Skip to content

Commit e5665f2

Browse files
Merge pull request #37 from ApolloAutomation/bundle-card-and-autoview
feat: auto-create Lovelace view on first setup
2 parents 893e91d + 21dbb7e commit e5665f2

8 files changed

Lines changed: 292 additions & 31 deletions

File tree

‎.github/workflows/validate.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ on:
66
- cron: "0 0 * * *"
77
push:
88
branches:
9+
- Main
910
- main
1011
pull_request:
1112
branches:
13+
- Main
1214
- main
1315

1416
permissions: {}

‎README.md‎

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
Backend for the Zone Mapper Lovelace card. Persists zone definitions and exposes per‑zone occupancy sensors based on tracked X/Y entities.
66

7-
> [!WARNING]
8-
> This integration requires the Zone Mapper lovelace card for functionality. Install it from HACS or from [the repository.](https://github.com/ApolloAutomation/zone-mapper-card)
7+
Starting with 1.1.0, this integration seeds a "Zone Mapper" view with the card on your default dashboard the first time it's set up, so you don't have to drop the card in by hand. The companion [Zone Mapper card](https://github.com/ApolloAutomation/zone-mapper-card) still needs to be installed separately via HACS.
98

109
## Features
1110

@@ -14,11 +13,10 @@ Backend for the Zone Mapper Lovelace card. Persists zone definitions and exposes
1413
- Restores zones, tracked entities, and rotation after Home Assistant restarts
1514
- Listens for and processes updates from the card via a single service
1615
- Auto‑discovers and (re)loads platforms at startup based on existing entities
16+
- Seeds a "Zone Mapper" view with the card on the default dashboard the first time the integration is set up (storage‑mode dashboards only, opt‑out available in integration options)
1717

1818
## Installation
1919

20-
There are two ways to install this integration. Both this integration and [the lovelace card](https://github.com/ApolloAutomation/zone-mapper-card) **must** be installed
21-
2220
### With HACS (Recommended)
2321

2422
HACS is like an app store for Home Assistant. It makes installing and updating custom integrations much easier. Here's how to install using HACS:
@@ -28,27 +26,22 @@ HACS is like an app store for Home Assistant. It makes installing and updating c
2826
- If HACS is not installed yet, download it following the instructions on [https://hacs.xyz/docs/use/download/download/](https://hacs.xyz/docs/use/download/download/)
2927
- Follow the HACS initial configuration guide at [https://hacs.xyz/docs/configuration/basic](https://hacs.xyz/docs/configuration/basic)
3028

31-
- **Add this custom repository to HACS:**
29+
- **Add both custom repositories to HACS:**
3230

3331
- Go to `HACS` in your Home Assistant sidebar
34-
- CLick on the 3 dots in the upper right corner
32+
- Click on the 3 dots in the upper right corner
3533
- Click "Custom repositories"
36-
- Add this URL to the repository: [https://github.com/ApolloAutomation/zone-mapper](https://github.com/ApolloAutomation/zone-mapper)
37-
- Select `Integration` for the type
38-
- Click the `ADD` button
34+
- Add this URL as type `Integration`: [https://github.com/ApolloAutomation/zone-mapper](https://github.com/ApolloAutomation/zone-mapper)
35+
- Add this URL as type `Dashboard`: [https://github.com/ApolloAutomation/zone-mapper-card](https://github.com/ApolloAutomation/zone-mapper-card)
3936

4037
- **Install Zone Mapper:**
4138

42-
- Go to `HACS` in your Home Assistant sidebar
43-
- Search for `Zone Mapper` in HACS
44-
- Click on the card when you find it
45-
- Click the `Download` button at the bottom right
46-
- Repeat for lovelace card
47-
- Restart Home Assistant
48-
- Go to `Devices and Services`
39+
- In HACS, search for `Zone Mapper` and download both the integration and the card
40+
- Restart Home Assistant when prompted
41+
- Go to `Settings` → `Devices and Services`
4942
- Click `Add Integration`
50-
- Search `Zone Mapper`
51-
- Add `Zone Mapper`
43+
- Search `Zone Mapper` and add it
44+
- Open the new "Zone Mapper" view on your default dashboard, pick the device and target entities on the card, and start drawing
5245

5346
### Manual Installation
5447

@@ -58,15 +51,15 @@ HACS is like an app store for Home Assistant. It makes installing and updating c
5851
/config/custom_components/zone_mapper
5952
```
6053

61-
2. Add entry to your `configuration.yaml`:
54+
2. (Optional) add entry to your `configuration.yaml` if you want YAML-mode setup:
6255

6356
```yaml
6457
zone_mapper:
6558
```
6659
6760
3. Restart Home Assistant.
6861
69-
4. Companion card: download `zone-mapper-card.js` from [the card repo](https://github.com/ApolloAutomation/zone-mapper-card) under `/config/www` and add it as a Dashboard Resource.
62+
4. Install the companion [Zone Mapper card](https://github.com/ApolloAutomation/zone-mapper-card) (download `zone-mapper-card.js` to `/config/www` and add it as a Dashboard Resource).
7063

7164
## Troubleshooting
7265

‎custom_components/zone_mapper/__init__.py‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@
3434
ATTR_X_MIN,
3535
ATTR_Y_MAX,
3636
ATTR_Y_MIN,
37+
CONF_AUTO_CREATE_VIEW,
3738
COORD_SENSOR_UNIQUE_ID_FMT,
3839
DATA_LOCATIONS,
3940
DATA_PLATFORMS_LOADED,
41+
DEFAULT_AUTO_CREATE_VIEW,
4042
DOMAIN,
4143
EVENT_ZONE_UPDATED,
4244
POLYGON_MAX_POINTS,
4345
POLYGON_MIN_POINTS,
4446
PRESENCE_SENSOR_UNIQUE_ID_FMT,
47+
SEEDED_DEFAULT_VIEW_FLAG,
4548
SERVICE_UPDATE_ZONE,
4649
SHAPE_ELLIPSE,
4750
SHAPE_NONE,
@@ -57,6 +60,7 @@
5760
WARN_RECT_INVALID,
5861
WARN_RECT_NON_NUM,
5962
)
63+
from .frontend import async_seed_default_view
6064

6165
_LOGGER = logging.getLogger(__name__)
6266

@@ -488,15 +492,45 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
488492
return True
489493

490494

495+
def _auto_create_view_enabled(entry: ConfigEntry) -> bool:
496+
value = entry.options.get(CONF_AUTO_CREATE_VIEW, DEFAULT_AUTO_CREATE_VIEW)
497+
return bool(value)
498+
499+
500+
async def _schedule_view_seeding(hass: HomeAssistant, entry: ConfigEntry) -> None:
501+
"""Seed the default Lovelace view once HA is fully started."""
502+
if entry.data.get(SEEDED_DEFAULT_VIEW_FLAG):
503+
return
504+
if not _auto_create_view_enabled(entry):
505+
return
506+
507+
async def _run(_event: Event | None = None) -> None:
508+
if entry.data.get(SEEDED_DEFAULT_VIEW_FLAG):
509+
return
510+
if not _auto_create_view_enabled(entry):
511+
return
512+
seeded = await async_seed_default_view(hass)
513+
if seeded:
514+
hass.config_entries.async_update_entry(
515+
entry,
516+
data={**entry.data, SEEDED_DEFAULT_VIEW_FLAG: True},
517+
)
518+
519+
if getattr(hass, "is_running", False):
520+
hass.async_create_task(_run(None))
521+
else:
522+
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _run)
523+
524+
491525
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
492526
"""
493527
Set up Zone Mapper from a config entry.
494528
495-
This registers the service (if not already present) and bootstraps any
496-
restored entities from the registry so platforms load without requiring YAML.
529+
Registers the update service, bootstraps any restored entities, and (once)
530+
seeds a default Lovelace view so new users see the card immediately
531+
(assuming the zone-mapper-card frontend is installed via HACS).
497532
"""
498533
_get_integration_data(hass)
499-
_ = entry
500534

501535
# Ensure service is registered only once across YAML and UI setups.
502536
if not hass.services.has_service(DOMAIN, SERVICE_UPDATE_ZONE):
@@ -514,6 +548,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
514548
else:
515549
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, bootstrap_cb)
516550

551+
await _schedule_view_seeding(hass, entry)
552+
517553
return True
518554

519555

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
"""
2-
Config flow for Zone Mapper.
2+
Config and options flow for Zone Mapper.
33
4-
This integration is UI-configurable and requires no options. The flow simply
5-
creates a single entry so users don't need to add `zone_mapper:` to YAML.
4+
The integration is UI-configurable and has a single setup step that just
5+
creates the singleton entry. The options flow exposes a single toggle for the
6+
first-run auto-view seeding.
67
"""
78

89
from __future__ import annotations
910

11+
from typing import TYPE_CHECKING
12+
1013
import voluptuous as vol
1114
from homeassistant import config_entries
15+
from homeassistant.core import callback
16+
17+
from .const import (
18+
CONF_AUTO_CREATE_VIEW,
19+
DEFAULT_AUTO_CREATE_VIEW,
20+
DOMAIN,
21+
)
1222

13-
from .const import DOMAIN
23+
if TYPE_CHECKING:
24+
from homeassistant.config_entries import ConfigEntry
1425

1526

1627
class ZoneMapperConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@@ -22,7 +33,6 @@ async def async_step_user(
2233
self, user_input: dict | None = None
2334
) -> config_entries.ConfigFlowResult:
2435
"""Show a confirmation form and create the entry when submitted."""
25-
# Prevent multiple entries; this integration is singleton.
2636
if self._async_current_entries():
2737
return self.async_abort(reason="already_configured")
2838

@@ -31,5 +41,37 @@ async def async_step_user(
3141
self._abort_if_unique_id_configured()
3242
return self.async_create_entry(title="Zone Mapper", data={})
3343

34-
# Show a confirmation form with no fields
3544
return self.async_show_form(step_id="user", data_schema=vol.Schema({}))
45+
46+
@staticmethod
47+
@callback
48+
def async_get_options_flow(
49+
config_entry: ConfigEntry,
50+
) -> ZoneMapperOptionsFlow:
51+
"""Return the options flow for this entry."""
52+
return ZoneMapperOptionsFlow(config_entry)
53+
54+
55+
class ZoneMapperOptionsFlow(config_entries.OptionsFlow):
56+
"""Options flow with a single toggle for auto-view seeding."""
57+
58+
def __init__(self, config_entry: ConfigEntry) -> None:
59+
"""Store the entry the options apply to."""
60+
self._config_entry = config_entry
61+
62+
async def async_step_init(
63+
self, user_input: dict | None = None
64+
) -> config_entries.ConfigFlowResult:
65+
"""Prompt for the auto-view toggle and save it."""
66+
if user_input is not None:
67+
return self.async_create_entry(title="", data=user_input)
68+
69+
current = self._config_entry.options.get(
70+
CONF_AUTO_CREATE_VIEW, DEFAULT_AUTO_CREATE_VIEW
71+
)
72+
schema = vol.Schema(
73+
{
74+
vol.Required(CONF_AUTO_CREATE_VIEW, default=current): bool,
75+
}
76+
)
77+
return self.async_show_form(step_id="init", data_schema=schema)

‎custom_components/zone_mapper/const.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@
4747
COORD_SENSOR_UNIQUE_ID_FMT = "zone_mapper_{location}_zone_{zone_id}"
4848
PRESENCE_SENSOR_UNIQUE_ID_FMT = "zone_mapper_{location}_zone_{zone_id}_presence"
4949

50+
# Options flow / auto-view seeding
51+
CONF_AUTO_CREATE_VIEW = "auto_create_view"
52+
DEFAULT_AUTO_CREATE_VIEW = True
53+
SEEDED_DEFAULT_VIEW_FLAG = "seeded_default_view"
54+
AUTO_VIEW_TITLE = "Zone Mapper"
55+
AUTO_VIEW_PATH = "zone-mapper"
56+
AUTO_VIEW_ICON = "mdi:map-marker-radius"
57+
AUTO_VIEW_PLACEHOLDER_LOCATION = "Home"
58+
CARD_TYPE = "custom:zone-mapper-card"
59+
5060
# Log / warning templates
5161
WARN_POLY_INSUFFICIENT = (
5262
"Polygon zone %s in location '%s' has insufficient points (<3); clearing zone."

0 commit comments

Comments
 (0)