Skip to content

Commit 10c1262

Browse files
authored
Switch LCN integration to local polling (home-assistant#152601)
1 parent 2fe2055 commit 10c1262

File tree

20 files changed

+226
-230
lines changed

20 files changed

+226
-230
lines changed

homeassistant/components/lcn/binary_sensor.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Support for LCN binary sensors."""
22

33
from collections.abc import Iterable
4+
from datetime import timedelta
45
from functools import partial
56

67
import pypck
@@ -19,6 +20,7 @@
1920
from .helpers import InputType, LcnConfigEntry
2021

2122
PARALLEL_UPDATES = 0
23+
SCAN_INTERVAL = timedelta(minutes=1)
2224

2325

2426
def add_lcn_entities(
@@ -69,21 +71,11 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
6971
config[CONF_DOMAIN_DATA][CONF_SOURCE]
7072
]
7173

72-
async def async_added_to_hass(self) -> None:
73-
"""Run when entity about to be added to hass."""
74-
await super().async_added_to_hass()
75-
if not self.device_connection.is_group:
76-
await self.device_connection.activate_status_request_handler(
77-
self.bin_sensor_port
78-
)
79-
80-
async def async_will_remove_from_hass(self) -> None:
81-
"""Run when entity will be removed from hass."""
82-
await super().async_will_remove_from_hass()
83-
if not self.device_connection.is_group:
84-
await self.device_connection.cancel_status_request_handler(
85-
self.bin_sensor_port
86-
)
74+
async def async_update(self) -> None:
75+
"""Update the state of the entity."""
76+
await self.device_connection.request_status_binary_sensors(
77+
SCAN_INTERVAL.seconds
78+
)
8779

8880
def input_received(self, input_obj: InputType) -> None:
8981
"""Set sensor value when LCN input object (command) is received."""

homeassistant/components/lcn/climate.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Support for LCN climate control."""
22

3+
import asyncio
34
from collections.abc import Iterable
5+
from datetime import timedelta
46
from functools import partial
57
from typing import Any, cast
68

@@ -36,6 +38,7 @@
3638
from .helpers import InputType, LcnConfigEntry
3739

3840
PARALLEL_UPDATES = 0
41+
SCAN_INTERVAL = timedelta(minutes=1)
3942

4043

4144
def add_lcn_entities(
@@ -110,20 +113,6 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
110113
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
111114
)
112115

113-
async def async_added_to_hass(self) -> None:
114-
"""Run when entity about to be added to hass."""
115-
await super().async_added_to_hass()
116-
if not self.device_connection.is_group:
117-
await self.device_connection.activate_status_request_handler(self.variable)
118-
await self.device_connection.activate_status_request_handler(self.setpoint)
119-
120-
async def async_will_remove_from_hass(self) -> None:
121-
"""Run when entity will be removed from hass."""
122-
await super().async_will_remove_from_hass()
123-
if not self.device_connection.is_group:
124-
await self.device_connection.cancel_status_request_handler(self.variable)
125-
await self.device_connection.cancel_status_request_handler(self.setpoint)
126-
127116
@property
128117
def temperature_unit(self) -> str:
129118
"""Return the unit of measurement."""
@@ -192,6 +181,17 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
192181
self._target_temperature = temperature
193182
self.async_write_ha_state()
194183

184+
async def async_update(self) -> None:
185+
"""Update the state of the entity."""
186+
await asyncio.gather(
187+
self.device_connection.request_status_variable(
188+
self.variable, SCAN_INTERVAL.seconds
189+
),
190+
self.device_connection.request_status_variable(
191+
self.setpoint, SCAN_INTERVAL.seconds
192+
),
193+
)
194+
195195
def input_received(self, input_obj: InputType) -> None:
196196
"""Set temperature value when LCN input object is received."""
197197
if not isinstance(input_obj, pypck.inputs.ModStatusVar):

homeassistant/components/lcn/cover.py

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Support for LCN covers."""
22

3+
import asyncio
34
from collections.abc import Iterable
5+
from datetime import timedelta
46
from functools import partial
57
from typing import Any
68

@@ -27,6 +29,7 @@
2729
from .helpers import InputType, LcnConfigEntry
2830

2931
PARALLEL_UPDATES = 0
32+
SCAN_INTERVAL = timedelta(minutes=1)
3033

3134

3235
def add_lcn_entities(
@@ -73,7 +76,7 @@ async def async_setup_entry(
7376
class LcnOutputsCover(LcnEntity, CoverEntity):
7477
"""Representation of a LCN cover connected to output ports."""
7578

76-
_attr_is_closed = False
79+
_attr_is_closed = True
7780
_attr_is_closing = False
7881
_attr_is_opening = False
7982
_attr_assumed_state = True
@@ -93,28 +96,6 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
9396
else:
9497
self.reverse_time = None
9598

96-
async def async_added_to_hass(self) -> None:
97-
"""Run when entity about to be added to hass."""
98-
await super().async_added_to_hass()
99-
if not self.device_connection.is_group:
100-
await self.device_connection.activate_status_request_handler(
101-
pypck.lcn_defs.OutputPort["OUTPUTUP"]
102-
)
103-
await self.device_connection.activate_status_request_handler(
104-
pypck.lcn_defs.OutputPort["OUTPUTDOWN"]
105-
)
106-
107-
async def async_will_remove_from_hass(self) -> None:
108-
"""Run when entity will be removed from hass."""
109-
await super().async_will_remove_from_hass()
110-
if not self.device_connection.is_group:
111-
await self.device_connection.cancel_status_request_handler(
112-
pypck.lcn_defs.OutputPort["OUTPUTUP"]
113-
)
114-
await self.device_connection.cancel_status_request_handler(
115-
pypck.lcn_defs.OutputPort["OUTPUTDOWN"]
116-
)
117-
11899
async def async_close_cover(self, **kwargs: Any) -> None:
119100
"""Close the cover."""
120101
state = pypck.lcn_defs.MotorStateModifier.DOWN
@@ -147,6 +128,18 @@ async def async_stop_cover(self, **kwargs: Any) -> None:
147128
self._attr_is_opening = False
148129
self.async_write_ha_state()
149130

131+
async def async_update(self) -> None:
132+
"""Update the state of the entity."""
133+
if not self.device_connection.is_group:
134+
await asyncio.gather(
135+
self.device_connection.request_status_output(
136+
pypck.lcn_defs.OutputPort["OUTPUTUP"], SCAN_INTERVAL.seconds
137+
),
138+
self.device_connection.request_status_output(
139+
pypck.lcn_defs.OutputPort["OUTPUTDOWN"], SCAN_INTERVAL.seconds
140+
),
141+
)
142+
150143
def input_received(self, input_obj: InputType) -> None:
151144
"""Set cover states when LCN input object (command) is received."""
152145
if (
@@ -175,7 +168,7 @@ def input_received(self, input_obj: InputType) -> None:
175168
class LcnRelayCover(LcnEntity, CoverEntity):
176169
"""Representation of a LCN cover connected to relays."""
177170

178-
_attr_is_closed = False
171+
_attr_is_closed = True
179172
_attr_is_closing = False
180173
_attr_is_opening = False
181174
_attr_assumed_state = True
@@ -206,20 +199,6 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
206199
self._is_closing = False
207200
self._is_opening = False
208201

209-
async def async_added_to_hass(self) -> None:
210-
"""Run when entity about to be added to hass."""
211-
await super().async_added_to_hass()
212-
if not self.device_connection.is_group:
213-
await self.device_connection.activate_status_request_handler(
214-
self.motor, self.positioning_mode
215-
)
216-
217-
async def async_will_remove_from_hass(self) -> None:
218-
"""Run when entity will be removed from hass."""
219-
await super().async_will_remove_from_hass()
220-
if not self.device_connection.is_group:
221-
await self.device_connection.cancel_status_request_handler(self.motor)
222-
223202
async def async_close_cover(self, **kwargs: Any) -> None:
224203
"""Close the cover."""
225204
if not await self.device_connection.control_motor_relays(
@@ -274,6 +253,17 @@ async def async_set_cover_position(self, **kwargs: Any) -> None:
274253

275254
self.async_write_ha_state()
276255

256+
async def async_update(self) -> None:
257+
"""Update the state of the entity."""
258+
coros = [self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)]
259+
if self.positioning_mode == pypck.lcn_defs.MotorPositioningMode.BS4:
260+
coros.append(
261+
self.device_connection.request_status_motor_position(
262+
self.motor, self.positioning_mode, SCAN_INTERVAL.seconds
263+
)
264+
)
265+
await asyncio.gather(*coros)
266+
277267
def input_received(self, input_obj: InputType) -> None:
278268
"""Set cover states when LCN input object (command) is received."""
279269
if isinstance(input_obj, pypck.inputs.ModStatusRelays):

homeassistant/components/lcn/entity.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
class LcnEntity(Entity):
2323
"""Parent class for all entities associated with the LCN component."""
2424

25-
_attr_should_poll = False
2625
_attr_has_entity_name = True
2726
device_connection: DeviceConnectionType
2827

@@ -57,15 +56,24 @@ def unique_id(self) -> str:
5756
).lower(),
5857
)
5958

59+
@property
60+
def should_poll(self) -> bool:
61+
"""Groups may not poll for a status."""
62+
return not self.device_connection.is_group
63+
6064
async def async_added_to_hass(self) -> None:
6165
"""Run when entity about to be added to hass."""
6266
self.device_connection = get_device_connection(
6367
self.hass, self.config[CONF_ADDRESS], self.config_entry
6468
)
65-
if not self.device_connection.is_group:
66-
self._unregister_for_inputs = self.device_connection.register_for_inputs(
67-
self.input_received
68-
)
69+
if self.device_connection.is_group:
70+
return
71+
72+
self._unregister_for_inputs = self.device_connection.register_for_inputs(
73+
self.input_received
74+
)
75+
76+
self.schedule_update_ha_state(force_refresh=True)
6977

7078
async def async_will_remove_from_hass(self) -> None:
7179
"""Run when entity will be removed from hass."""

homeassistant/components/lcn/helpers.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,19 @@ async def async_update_device_config(
251251
"""Fill missing values in device_config with infos from LCN bus."""
252252
# fetch serial info if device is module
253253
if not (is_group := device_config[CONF_ADDRESS][2]): # is module
254-
await device_connection.serial_known
254+
await device_connection.serials_known()
255255
if device_config[CONF_HARDWARE_SERIAL] == -1:
256-
device_config[CONF_HARDWARE_SERIAL] = device_connection.hardware_serial
256+
device_config[CONF_HARDWARE_SERIAL] = (
257+
device_connection.serials.hardware_serial
258+
)
257259
if device_config[CONF_SOFTWARE_SERIAL] == -1:
258-
device_config[CONF_SOFTWARE_SERIAL] = device_connection.software_serial
260+
device_config[CONF_SOFTWARE_SERIAL] = (
261+
device_connection.serials.software_serial
262+
)
259263
if device_config[CONF_HARDWARE_TYPE] == -1:
260-
device_config[CONF_HARDWARE_TYPE] = device_connection.hardware_type.value
264+
device_config[CONF_HARDWARE_TYPE] = (
265+
device_connection.serials.hardware_type.value
266+
)
261267

262268
# fetch name if device is module
263269
if device_config[CONF_NAME] != "":

homeassistant/components/lcn/light.py

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Support for LCN lights."""
22

33
from collections.abc import Iterable
4+
from datetime import timedelta
45
from functools import partial
56
from typing import Any
67

@@ -33,6 +34,7 @@
3334
BRIGHTNESS_SCALE = (1, 100)
3435

3536
PARALLEL_UPDATES = 0
37+
SCAN_INTERVAL = timedelta(minutes=1)
3638

3739

3840
def add_lcn_entities(
@@ -100,18 +102,6 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
100102
self._attr_color_mode = ColorMode.ONOFF
101103
self._attr_supported_color_modes = {self._attr_color_mode}
102104

103-
async def async_added_to_hass(self) -> None:
104-
"""Run when entity about to be added to hass."""
105-
await super().async_added_to_hass()
106-
if not self.device_connection.is_group:
107-
await self.device_connection.activate_status_request_handler(self.output)
108-
109-
async def async_will_remove_from_hass(self) -> None:
110-
"""Run when entity will be removed from hass."""
111-
await super().async_will_remove_from_hass()
112-
if not self.device_connection.is_group:
113-
await self.device_connection.cancel_status_request_handler(self.output)
114-
115105
async def async_turn_on(self, **kwargs: Any) -> None:
116106
"""Turn the entity on."""
117107
if ATTR_TRANSITION in kwargs:
@@ -157,6 +147,12 @@ async def async_turn_off(self, **kwargs: Any) -> None:
157147
self._attr_is_on = False
158148
self.async_write_ha_state()
159149

150+
async def async_update(self) -> None:
151+
"""Update the state of the entity."""
152+
await self.device_connection.request_status_output(
153+
self.output, SCAN_INTERVAL.seconds
154+
)
155+
160156
def input_received(self, input_obj: InputType) -> None:
161157
"""Set light state when LCN input object (command) is received."""
162158
if (
@@ -184,18 +180,6 @@ def __init__(self, config: ConfigType, config_entry: LcnConfigEntry) -> None:
184180

185181
self.output = pypck.lcn_defs.RelayPort[config[CONF_DOMAIN_DATA][CONF_OUTPUT]]
186182

187-
async def async_added_to_hass(self) -> None:
188-
"""Run when entity about to be added to hass."""
189-
await super().async_added_to_hass()
190-
if not self.device_connection.is_group:
191-
await self.device_connection.activate_status_request_handler(self.output)
192-
193-
async def async_will_remove_from_hass(self) -> None:
194-
"""Run when entity will be removed from hass."""
195-
await super().async_will_remove_from_hass()
196-
if not self.device_connection.is_group:
197-
await self.device_connection.cancel_status_request_handler(self.output)
198-
199183
async def async_turn_on(self, **kwargs: Any) -> None:
200184
"""Turn the entity on."""
201185
states = [pypck.lcn_defs.RelayStateModifier.NOCHANGE] * 8
@@ -214,6 +198,10 @@ async def async_turn_off(self, **kwargs: Any) -> None:
214198
self._attr_is_on = False
215199
self.async_write_ha_state()
216200

201+
async def async_update(self) -> None:
202+
"""Update the state of the entity."""
203+
await self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)
204+
217205
def input_received(self, input_obj: InputType) -> None:
218206
"""Set light state when LCN input object (command) is received."""
219207
if not isinstance(input_obj, pypck.inputs.ModStatusRelays):

homeassistant/components/lcn/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"config_flow": true,
77
"dependencies": ["http", "websocket_api"],
88
"documentation": "https://www.home-assistant.io/integrations/lcn",
9-
"iot_class": "local_push",
9+
"iot_class": "local_polling",
1010
"loggers": ["pypck"],
1111
"quality_scale": "bronze",
12-
"requirements": ["pypck==0.8.12", "lcn-frontend==0.2.7"]
12+
"requirements": ["pypck==0.9.2", "lcn-frontend==0.2.7"]
1313
}

0 commit comments

Comments
 (0)