Skip to content

Commit 5e781c6

Browse files
jseidlJulien-DecoenLebe1ge
authored
Reload meta-areas on regular-area load (#511)
* added area load signal dispatcher and handler for auto-reloading meta-areas * MA entries wont wait for HA to load anymore, still pending adding reload logic when it actually starts * put the load notification under a check after hass is loaded * more lint * added throttling and delay to reload call * being more literal with timedelta argument * fix: Convert reload method to async and update related calls (#534) Co-authored-by: Juicy <julien.decoen@gmail.com> * removed await from async_schedule as it returns None which is not awaitable * added sleep to climate test to account for race conditions, linting * updated manifests and test shellscript * added listener to HA start to reload, modified the entity registry reload to also consider newly created entities * added logic on climate control test to account for async event propagation delays * removed unnecessary dependencies from tox and split github action for parallelization * adding version getter to lint github action * adding version getter to lint github action * updating tests and lint * mark beta test as continue-on-error as it is not a blocker * added tests for the reload logic * linter pass on area reload test file * added negative test cases for area reload --------- Co-authored-by: Juicy <julien.decoen.pro@gmail.com> Co-authored-by: Juicy <julien.decoen@gmail.com>
1 parent 80ac446 commit 5e781c6

File tree

15 files changed

+449
-116
lines changed

15 files changed

+449
-116
lines changed

.github/workflows/lint.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,11 @@ jobs:
2828
- name: "Install requirements"
2929
run: python3 -m pip install -r requirements-test.txt
3030

31+
- name: Get latest HA stable version from PyPI
32+
run: |
33+
VERSIONS=$(curl -s https://pypi.org/pypi/homeassistant/json | jq -r '.releases | keys | .[]')
34+
STABLE=$(echo "$VERSIONS" | grep -Ev '[a-zA-Z]' | sort -Vr | head -n1)
35+
echo "HA_STABLE_VERSION=$STABLE" >> $GITHUB_ENV
36+
3137
- name: "Run"
3238
run: tox -e lint

.github/workflows/pytest.yml

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,59 @@ permissions:
1212
contents: read
1313

1414
jobs:
15-
test:
15+
test-stable:
16+
name: "Test against stable release"
1617
runs-on: ubuntu-latest
17-
1818
steps:
1919
- uses: actions/checkout@v5
2020

2121
- name: Set up Python
2222
uses: actions/setup-python@v5
2323
with:
24-
python-version: '3.13'
24+
python-version: "3.13"
2525

2626
- name: Install tox and jq
2727
run: |
2828
python -m pip install --upgrade pip
2929
pip install -r requirements-test.txt
3030
sudo apt-get update && sudo apt-get install -y jq
3131
32-
- name: Get latest HA versions from PyPI
33-
id: ha_versions
32+
- name: Get latest HA stable version from PyPI
3433
run: |
3534
VERSIONS=$(curl -s https://pypi.org/pypi/homeassistant/json | jq -r '.releases | keys | .[]')
3635
STABLE=$(echo "$VERSIONS" | grep -Ev '[a-zA-Z]' | sort -Vr | head -n1)
37-
BETA=$(echo "$VERSIONS" | grep 'b' | sort -Vr | head -n1)
38-
3936
echo "HA_STABLE_VERSION=$STABLE" >> $GITHUB_ENV
40-
echo "HA_BETA_VERSION=$BETA" >> $GITHUB_ENV
4137
42-
- name: Run tox for both versions
43-
run: tox -e ha-stable,ha-beta
38+
- name: Run tox for stable
39+
run: tox -e ha-stable
4440
env:
4541
HA_STABLE_VERSION: ${{ env.HA_STABLE_VERSION }}
42+
43+
test-beta:
44+
name: "Test against beta release"
45+
runs-on: ubuntu-latest
46+
continue-on-error: true
47+
steps:
48+
- uses: actions/checkout@v5
49+
50+
- name: Set up Python
51+
uses: actions/setup-python@v5
52+
with:
53+
python-version: "3.13"
54+
55+
- name: Install tox and jq
56+
run: |
57+
python -m pip install --upgrade pip
58+
pip install -r requirements-test.txt
59+
sudo apt-get update && sudo apt-get install -y jq
60+
61+
- name: Get latest HA beta version from PyPI
62+
run: |
63+
VERSIONS=$(curl -s https://pypi.org/pypi/homeassistant/json | jq -r '.releases | keys | .[]')
64+
BETA=$(echo "$VERSIONS" | grep 'b' | sort -Vr | head -n1)
65+
echo "HA_BETA_VERSION=$BETA" >> $GITHUB_ENV
66+
67+
- name: Run tox for beta
68+
run: tox -e ha-beta
69+
env:
4670
HA_BETA_VERSION: ${{ env.HA_BETA_VERSION }}

custom_components/magic_areas/__init__.py

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
3535
"""Set up the component."""
3636

3737
@callback
38-
def _async_registry_updated(
38+
async def _async_reload_entry(*args, **kwargs) -> None:
39+
hass.config_entries.async_update_entry(
40+
config_entry,
41+
data={**config_entry.data, "entity_ts": datetime.now(UTC)},
42+
)
43+
44+
@callback
45+
async def _async_registry_updated(
3946
event: (
4047
Event[EventEntityRegistryUpdatedData]
4148
| Event[EventDeviceRegistryUpdatedData]
@@ -61,10 +68,7 @@ def _async_registry_updated(
6168
"%s: Reloading entry due entity registry change",
6269
config_entry.data[ATTR_NAME],
6370
)
64-
hass.config_entries.async_update_entry(
65-
config_entry,
66-
data={**config_entry.data, "entity_ts": datetime.now(UTC)},
67-
)
71+
await _async_reload_entry()
6872

6973
async def _async_setup_integration(*args, **kwargs) -> None:
7074
"""Load integration when Hass has finished starting."""
@@ -88,20 +92,23 @@ async def _async_setup_integration(*args, **kwargs) -> None:
8892
tracked_listeners.append(config_entry.add_update_listener(async_update_options))
8993

9094
# Watch for area changes.
91-
tracked_listeners.append(
92-
hass.bus.async_listen(
93-
EVENT_ENTITY_REGISTRY_UPDATED,
94-
_async_registry_updated,
95-
_entity_registry_filter,
95+
if not magic_area.is_meta():
96+
tracked_listeners.append(
97+
hass.bus.async_listen(
98+
EVENT_ENTITY_REGISTRY_UPDATED,
99+
_async_registry_updated,
100+
magic_area.make_entity_registry_filter(),
101+
)
96102
)
97-
)
98-
tracked_listeners.append(
99-
hass.bus.async_listen(
100-
EVENT_DEVICE_REGISTRY_UPDATED,
101-
_async_registry_updated,
102-
_device_registry_filter,
103+
tracked_listeners.append(
104+
hass.bus.async_listen(
105+
EVENT_DEVICE_REGISTRY_UPDATED,
106+
_async_registry_updated,
107+
magic_area.make_device_registry_filter(),
108+
)
103109
)
104-
)
110+
# Reload once Home Assistant has finished starting to make sure we have all entities.
111+
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_reload_entry)
105112

106113
hass.data[MODULE_DATA][config_entry.entry_id] = {
107114
DATA_AREA_OBJECT: magic_area,
@@ -115,13 +122,7 @@ async def _async_setup_integration(*args, **kwargs) -> None:
115122

116123
hass.data.setdefault(MODULE_DATA, {})
117124

118-
# Wait for Hass to have started before setting up.
119-
if hass.is_running:
120-
hass.create_task(_async_setup_integration())
121-
else:
122-
hass.bus.async_listen_once(
123-
EVENT_HOMEASSISTANT_STARTED, _async_setup_integration
124-
)
125+
await _async_setup_integration()
125126

126127
return True
127128

@@ -133,13 +134,10 @@ async def async_update_options(hass: HomeAssistant, config_entry: ConfigEntry) -
133134
)
134135
await hass.config_entries.async_reload(config_entry.entry_id)
135136

136-
# @TODO Reload corresponding meta areas (floor+interior/exterior+global)
137-
138137

139138
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
140139
"""Unload a config entry."""
141140

142-
platforms_unloaded = []
143141
if MODULE_DATA not in hass.data:
144142
_LOGGER.warning(
145143
"Module data object for Magic Areas not found, possibly already removed."
@@ -158,17 +156,13 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
158156
area_data = data[config_entry.entry_id]
159157
area = area_data[DATA_AREA_OBJECT]
160158

161-
for platform in area.available_platforms():
162-
unload_ok = await hass.config_entries.async_forward_entry_unload(
163-
config_entry, platform
164-
)
165-
platforms_unloaded.append(unload_ok)
159+
all_unloaded = await hass.config_entries.async_unload_platforms(
160+
config_entry, area.available_platforms()
161+
)
166162

167163
for tracked_listener in area_data[DATA_TRACKED_LISTENERS]:
168164
tracked_listener()
169165

170-
all_unloaded = all(platforms_unloaded)
171-
172166
if all_unloaded:
173167
data.pop(config_entry.entry_id)
174168

@@ -212,15 +206,3 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry):
212206
)
213207

214208
return True
215-
216-
217-
@callback
218-
def _entity_registry_filter(event_data: EventEntityRegistryUpdatedData) -> bool:
219-
"""Filter entity registry events."""
220-
return event_data["action"] == "update" and "area_id" in event_data["changes"]
221-
222-
223-
@callback
224-
def _device_registry_filter(event_data: EventDeviceRegistryUpdatedData) -> bool:
225-
"""Filter device registry events."""
226-
return event_data["action"] == "update" and "area_id" in event_data["changes"]

0 commit comments

Comments
 (0)