Skip to content

Commit 88491c7

Browse files
authored
fix(alarm): address HA 2025.x deprecations (#171)
- Fix testing for Home Assistant > 2024.8 - Fix alarm panel deprecations - Fix `ConfigEntryState` deprecations - Fix `async_forward_entry_setup` deprecation (fixes #169) - Fix `alarm_state` deprecation for HA Core 2025.11
1 parent 83cb580 commit 88491c7

File tree

6 files changed

+38
-62
lines changed

6 files changed

+38
-62
lines changed

custom_components/econnect_metronet/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool:
132132
hass.services.async_register(DOMAIN, "disarm_sectors", partial(services.disarm_sectors, hass, config.entry_id))
133133
hass.services.async_register(DOMAIN, "update_state", partial(services.update_state, hass, config.entry_id))
134134

135-
for component in PLATFORMS:
136-
hass.async_create_task(hass.config_entries.async_forward_entry_setup(config, component))
135+
await hass.config_entries.async_forward_entry_setups(config, PLATFORMS)
137136

138137
return True
139138

custom_components/econnect_metronet/alarm_control_panel.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@
44

55
from homeassistant.components.alarm_control_panel import (
66
AlarmControlPanelEntity,
7+
AlarmControlPanelState,
78
CodeFormat,
89
)
910
from homeassistant.components.alarm_control_panel.const import (
1011
AlarmControlPanelEntityFeature as AlarmFeatures,
1112
)
1213
from homeassistant.config_entries import ConfigEntry
13-
from homeassistant.const import (
14-
CONF_USERNAME,
15-
STATE_ALARM_ARMED_AWAY,
16-
STATE_ALARM_ARMED_HOME,
17-
STATE_ALARM_ARMED_NIGHT,
18-
STATE_ALARM_ARMED_VACATION,
19-
STATE_ALARM_ARMING,
20-
STATE_ALARM_DISARMED,
21-
STATE_ALARM_DISARMING,
22-
)
14+
from homeassistant.const import CONF_USERNAME
2315
from homeassistant.core import HomeAssistant
2416
from homeassistant.helpers.update_coordinator import CoordinatorEntity
2517

@@ -77,7 +69,7 @@ def icon(self):
7769
return "hass:shield-home"
7870

7971
@property
80-
def state(self):
72+
def alarm_state(self):
8173
"""Return the state of the device."""
8274
return self._device.state
8375

@@ -91,19 +83,19 @@ def supported_features(self):
9183
"""Return the list of supported features."""
9284
return AlarmFeatures.ARM_HOME | AlarmFeatures.ARM_AWAY | AlarmFeatures.ARM_NIGHT | AlarmFeatures.ARM_VACATION
9385

94-
@set_device_state(STATE_ALARM_DISARMED, STATE_ALARM_DISARMING)
86+
@set_device_state(AlarmControlPanelState.DISARMED, AlarmControlPanelState.DISARMING)
9587
@retry_refresh_token
9688
async def async_alarm_disarm(self, code=None):
9789
"""Send disarm command."""
9890
await self.hass.async_add_executor_job(self._device.disarm, code)
9991

100-
@set_device_state(STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING)
92+
@set_device_state(AlarmControlPanelState.ARMED_AWAY, AlarmControlPanelState.ARMING)
10193
@retry_refresh_token
10294
async def async_alarm_arm_away(self, code=None):
10395
"""Send arm away command."""
10496
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_away)
10597

106-
@set_device_state(STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMING)
98+
@set_device_state(AlarmControlPanelState.ARMED_HOME, AlarmControlPanelState.ARMING)
10799
@retry_refresh_token
108100
async def async_alarm_arm_home(self, code=None):
109101
"""Send arm home command."""
@@ -113,7 +105,7 @@ async def async_alarm_arm_home(self, code=None):
113105

114106
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_home)
115107

116-
@set_device_state(STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMING)
108+
@set_device_state(AlarmControlPanelState.ARMED_NIGHT, AlarmControlPanelState.ARMING)
117109
@retry_refresh_token
118110
async def async_alarm_arm_night(self, code=None):
119111
"""Send arm night command."""
@@ -123,7 +115,7 @@ async def async_alarm_arm_night(self, code=None):
123115

124116
await self.hass.async_add_executor_job(self._device.arm, code, self._device._sectors_night)
125117

126-
@set_device_state(STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMING)
118+
@set_device_state(AlarmControlPanelState.ARMED_VACATION, AlarmControlPanelState.ARMING)
127119
@retry_refresh_token
128120
async def async_alarm_arm_vacation(self, code=None):
129121
"""Send arm vacation command."""

custom_components/econnect_metronet/devices.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,7 @@
1010
LockError,
1111
ParseError,
1212
)
13-
from homeassistant.const import (
14-
STATE_ALARM_ARMED_AWAY,
15-
STATE_ALARM_ARMED_HOME,
16-
STATE_ALARM_ARMED_NIGHT,
17-
STATE_ALARM_ARMED_VACATION,
18-
STATE_ALARM_ARMING,
19-
STATE_ALARM_DISARMED,
20-
STATE_ALARM_DISARMING,
21-
STATE_UNAVAILABLE,
22-
)
13+
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
2314
from requests.exceptions import HTTPError
2415

2516
from .const import (
@@ -72,7 +63,7 @@ def __init__(self, connection, config=None):
7263
self._sectors_vacation = config.get(CONF_AREAS_ARM_VACATION) or []
7364

7465
# Alarm state
75-
self.state = STATE_UNAVAILABLE
66+
self.state = None
7667

7768
def _register_sector(self, entity):
7869
"""Register a sector entity in the device's internal inventory."""
@@ -225,28 +216,28 @@ def get_state(self):
225216
"""
226217
# If the system is arming or disarming, return the current state
227218
# to prevent the state from being updated while the system is in transition.
228-
if self.state in [STATE_ALARM_ARMING, STATE_ALARM_DISARMING]:
219+
if self.state in [AlarmControlPanelState.ARMING, AlarmControlPanelState.DISARMING]:
229220
return self.state
230221

231222
sectors_armed = dict(self.items(q.SECTORS, status=True))
232223
if not sectors_armed:
233-
return STATE_ALARM_DISARMED
224+
return AlarmControlPanelState.DISARMED
234225

235226
# Note: `element` is the sector ID you use to arm/disarm the sector.
236227
sectors = [sectors["element"] for sectors in sectors_armed.values()]
237228
# Sort lists here for robustness, ensuring accurate comparisons
238229
# regardless of whether the input lists were pre-sorted or not.
239230
sectors_armed_sorted = sorted(sectors)
240231
if sectors_armed_sorted == sorted(self._sectors_home):
241-
return STATE_ALARM_ARMED_HOME
232+
return AlarmControlPanelState.ARMED_HOME
242233

243234
if sectors_armed_sorted == sorted(self._sectors_night):
244-
return STATE_ALARM_ARMED_NIGHT
235+
return AlarmControlPanelState.ARMED_NIGHT
245236

246237
if sectors_armed_sorted == sorted(self._sectors_vacation):
247-
return STATE_ALARM_ARMED_VACATION
238+
return AlarmControlPanelState.ARMED_VACATION
248239

249-
return STATE_ALARM_ARMED_AWAY
240+
return AlarmControlPanelState.ARMED_AWAY
250241

251242
def get_status(self, query: int, id: int) -> Union[bool, int]:
252243
"""Get the status of an item in the device inventory specified by query and id.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dev = [
5353
"pytest-socket",
5454
"requests-mock",
5555
"syrupy",
56+
"respx",
5657
]
5758

5859
lint = [

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
import responses
55
from elmo.api.client import ElmoClient
6+
from homeassistant.config_entries import ConfigEntryState
67

78
from custom_components.econnect_metronet import async_setup
89
from custom_components.econnect_metronet.alarm_control_panel import EconnectAlarm
@@ -164,6 +165,7 @@ def config_entry(hass):
164165
"domain": "econnect_metronet",
165166
"system_base_url": "https://example.com",
166167
},
168+
state=ConfigEntryState.SETUP_IN_PROGRESS,
167169
)
168170
config.add_to_hass(hass)
169171
return config

tests/test_devices.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,7 @@
22
import responses
33
from elmo import query as q
44
from elmo.api.exceptions import CodeError, CredentialError, LockError, ParseError
5-
from homeassistant.const import (
6-
STATE_ALARM_ARMED_AWAY,
7-
STATE_ALARM_ARMED_HOME,
8-
STATE_ALARM_ARMED_NIGHT,
9-
STATE_ALARM_ARMED_VACATION,
10-
STATE_ALARM_ARMING,
11-
STATE_ALARM_DISARMED,
12-
STATE_ALARM_DISARMING,
13-
STATE_UNAVAILABLE,
14-
)
5+
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
156
from requests.exceptions import HTTPError
167
from requests.models import Response
178

@@ -42,7 +33,7 @@ def test_device_constructor(client):
4233
assert device._sectors_home == []
4334
assert device._sectors_night == []
4435
assert device._sectors_vacation == []
45-
assert device.state == STATE_UNAVAILABLE
36+
assert device.state is None
4637

4738

4839
def test_device_constructor_with_config(client):
@@ -66,7 +57,7 @@ def test_device_constructor_with_config(client):
6657
assert device._sectors_home == [3, 4]
6758
assert device._sectors_night == [1, 2, 3]
6859
assert device._sectors_vacation == [5, 3]
69-
assert device.state == STATE_UNAVAILABLE
60+
assert device.state is None
7061

7162

7263
def test_device_constructor_with_config_empty(client):
@@ -90,7 +81,7 @@ def test_device_constructor_with_config_empty(client):
9081
assert device._sectors_home == []
9182
assert device._sectors_night == []
9283
assert device._sectors_vacation == []
93-
assert device.state == STATE_UNAVAILABLE
84+
assert device.state is None
9485

9586

9687
class TestItemInputs:
@@ -1468,7 +1459,7 @@ def test_get_state_no_sectors_armed(alarm_device):
14681459
alarm_device._sectors_night = []
14691460
alarm_device._inventory = {9: {}}
14701461
# Test
1471-
assert alarm_device.get_state() == STATE_ALARM_DISARMED
1462+
assert alarm_device.get_state() == AlarmControlPanelState.DISARMED
14721463

14731464

14741465
def test_get_state_armed_home(alarm_device):
@@ -1482,7 +1473,7 @@ def test_get_state_armed_home(alarm_device):
14821473
}
14831474
}
14841475
# Test
1485-
assert alarm_device.get_state() == STATE_ALARM_ARMED_HOME
1476+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_HOME
14861477

14871478

14881479
def test_get_state_armed_home_out_of_order(alarm_device):
@@ -1496,7 +1487,7 @@ def test_get_state_armed_home_out_of_order(alarm_device):
14961487
}
14971488
}
14981489
# Test
1499-
assert alarm_device.get_state() == STATE_ALARM_ARMED_HOME
1490+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_HOME
15001491

15011492

15021493
def test_get_state_armed_night(alarm_device):
@@ -1510,7 +1501,7 @@ def test_get_state_armed_night(alarm_device):
15101501
}
15111502
}
15121503
# Test (out of order keys to test sorting)
1513-
assert alarm_device.get_state() == STATE_ALARM_ARMED_NIGHT
1504+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_NIGHT
15141505

15151506

15161507
def test_get_state_armed_night_out_of_order(alarm_device):
@@ -1524,7 +1515,7 @@ def test_get_state_armed_night_out_of_order(alarm_device):
15241515
}
15251516
}
15261517
# Test
1527-
assert alarm_device.get_state() == STATE_ALARM_ARMED_NIGHT
1518+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_NIGHT
15281519

15291520

15301521
def test_get_state_armed_vacation(alarm_device):
@@ -1538,7 +1529,7 @@ def test_get_state_armed_vacation(alarm_device):
15381529
}
15391530
}
15401531
# Test (out of order keys to test sorting)
1541-
assert alarm_device.get_state() == STATE_ALARM_ARMED_VACATION
1532+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_VACATION
15421533

15431534

15441535
def test_get_state_armed_vacation_out_of_order(alarm_device):
@@ -1552,7 +1543,7 @@ def test_get_state_armed_vacation_out_of_order(alarm_device):
15521543
}
15531544
}
15541545
# Test
1555-
assert alarm_device.get_state() == STATE_ALARM_ARMED_VACATION
1546+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_VACATION
15561547

15571548

15581549
def test_get_state_armed_away(alarm_device):
@@ -1569,7 +1560,7 @@ def test_get_state_armed_away(alarm_device):
15691560
}
15701561
}
15711562
# Test
1572-
assert alarm_device.get_state() == STATE_ALARM_ARMED_AWAY
1563+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_AWAY
15731564

15741565

15751566
def test_get_state_armed_mixed(alarm_device):
@@ -1586,7 +1577,7 @@ def test_get_state_armed_mixed(alarm_device):
15861577
}
15871578
}
15881579
# Test
1589-
assert alarm_device.get_state() == STATE_ALARM_ARMED_AWAY
1580+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_AWAY
15901581

15911582

15921583
def test_get_state_armed_away_with_config(alarm_device):
@@ -1603,7 +1594,7 @@ def test_get_state_armed_away_with_config(alarm_device):
16031594
}
16041595
}
16051596
# Test
1606-
assert alarm_device.get_state() == STATE_ALARM_ARMED_AWAY
1597+
assert alarm_device.get_state() == AlarmControlPanelState.ARMED_AWAY
16071598

16081599

16091600
def test_get_state_while_disarming(alarm_device):
@@ -1612,9 +1603,9 @@ def test_get_state_while_disarming(alarm_device):
16121603
alarm_device._sectors_home = []
16131604
alarm_device._sectors_night = []
16141605
alarm_device._inventory = {9: {}}
1615-
alarm_device.state = STATE_ALARM_DISARMING
1606+
alarm_device.state = AlarmControlPanelState.DISARMING
16161607
# Test
1617-
assert alarm_device.get_state() == STATE_ALARM_DISARMING
1608+
assert alarm_device.get_state() == AlarmControlPanelState.DISARMING
16181609

16191610

16201611
def test_get_state_while_arming(alarm_device):
@@ -1623,9 +1614,9 @@ def test_get_state_while_arming(alarm_device):
16231614
alarm_device._sectors_home = []
16241615
alarm_device._sectors_night = []
16251616
alarm_device._inventory = {9: {}}
1626-
alarm_device.state = STATE_ALARM_ARMING
1617+
alarm_device.state = AlarmControlPanelState.ARMING
16271618
# Test
1628-
assert alarm_device.get_state() == STATE_ALARM_ARMING
1619+
assert alarm_device.get_state() == AlarmControlPanelState.ARMING
16291620

16301621

16311622
class TestTurnOff:

0 commit comments

Comments
 (0)