Skip to content

Commit cc30823

Browse files
Reimplement PGLab sensor to use a coordinator (home-assistant#139789)
* Reimplement PGLab sensor to use a coordinator * fix spelling mistake on coordinator name * rename createDiscoverDeviceInfo function in snake_case * adding suffix pglab_ to PGLabBaseEntity/PGLabEntity constructor parameters * Fix docs of PGLabEntity::async_added_to_hass * make coordinator able to return the sensor native value * renaming PGLABConfigEntry in PGLabConfigEntry to be consistent with the integration naming * renamed entry function arguments to config_entry to be less confusing * pass config_entry to constructor of base class of PGLabSensorsCoordinator * set the return value type of get_sensor_value * store coordinator as regular instance attribute * Avoid to access directly entity from discovery module * Rearrange get_sensor_value return types
1 parent cc5c8bf commit cc30823

File tree

8 files changed

+228
-168
lines changed

8 files changed

+228
-168
lines changed

homeassistant/components/pglab/__init__.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
from .const import DOMAIN, LOGGER
2424
from .discovery import PGLabDiscovery
2525

26-
type PGLABConfigEntry = ConfigEntry[PGLabDiscovery]
26+
type PGLabConfigEntry = ConfigEntry[PGLabDiscovery]
2727

2828
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
2929

3030

31-
async def async_setup_entry(hass: HomeAssistant, entry: PGLABConfigEntry) -> bool:
31+
async def async_setup_entry(
32+
hass: HomeAssistant, config_entry: PGLabConfigEntry
33+
) -> bool:
3234
"""Set up PG LAB Electronics integration from a config entry."""
3335

3436
async def mqtt_publish(topic: str, payload: str, qos: int, retain: bool) -> None:
@@ -67,19 +69,21 @@ async def mqtt_unsubscribe(sub_state: PyPGLabSubState) -> None:
6769
pglab_mqtt = PyPGLabMqttClient(mqtt_publish, mqtt_subscribe, mqtt_unsubscribe)
6870

6971
# Setup PGLab device discovery.
70-
entry.runtime_data = PGLabDiscovery()
72+
config_entry.runtime_data = PGLabDiscovery()
7173

7274
# Start to discovery PG Lab devices.
73-
await entry.runtime_data.start(hass, pglab_mqtt, entry)
75+
await config_entry.runtime_data.start(hass, pglab_mqtt, config_entry)
7476

7577
return True
7678

7779

78-
async def async_unload_entry(hass: HomeAssistant, entry: PGLABConfigEntry) -> bool:
80+
async def async_unload_entry(
81+
hass: HomeAssistant, config_entry: PGLabConfigEntry
82+
) -> bool:
7983
"""Unload a config entry."""
8084

8185
# Stop PGLab device discovery.
82-
pglab_discovery = entry.runtime_data
83-
await pglab_discovery.stop(hass, entry)
86+
pglab_discovery = config_entry.runtime_data
87+
await pglab_discovery.stop(hass, config_entry)
8488

8589
return True
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""Coordinator for PG LAB Electronics."""
2+
3+
from __future__ import annotations
4+
5+
from datetime import datetime, timedelta
6+
from typing import TYPE_CHECKING, Any
7+
8+
from pypglab.const import SENSOR_REBOOT_TIME, SENSOR_TEMPERATURE, SENSOR_VOLTAGE
9+
from pypglab.device import Device as PyPGLabDevice
10+
from pypglab.sensor import Sensor as PyPGLabSensors
11+
12+
from homeassistant.core import HomeAssistant, callback
13+
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
14+
from homeassistant.util.dt import utcnow
15+
16+
from .const import DOMAIN, LOGGER
17+
18+
if TYPE_CHECKING:
19+
from . import PGLabConfigEntry
20+
21+
22+
class PGLabSensorsCoordinator(DataUpdateCoordinator[dict[str, Any]]):
23+
"""Class to update Sensor Entities when receiving new data."""
24+
25+
def __init__(
26+
self,
27+
hass: HomeAssistant,
28+
config_entry: PGLabConfigEntry,
29+
pglab_device: PyPGLabDevice,
30+
) -> None:
31+
"""Initialize."""
32+
33+
# get a reference of PG Lab device internal sensors state
34+
self._sensors: PyPGLabSensors = pglab_device.sensors
35+
36+
super().__init__(
37+
hass,
38+
LOGGER,
39+
config_entry=config_entry,
40+
name=DOMAIN,
41+
)
42+
43+
@callback
44+
def _new_sensors_data(self, payload: str) -> None:
45+
"""Handle new sensor data."""
46+
47+
# notify all listeners that new sensor values are available
48+
self.async_set_updated_data(self._sensors.state)
49+
50+
async def subscribe_topics(self) -> None:
51+
"""Subscribe the sensors state to be notifty from MQTT update messages."""
52+
53+
# subscribe to the pypglab sensors to receive updates from the mqtt broker
54+
# when a new sensor values are available
55+
await self._sensors.subscribe_topics()
56+
57+
# set the callback to be called when a new sensor values are available
58+
self._sensors.set_on_state_callback(self._new_sensors_data)
59+
60+
def get_sensor_value(self, sensor_key: str) -> float | datetime | None:
61+
"""Return the value of a sensor."""
62+
63+
if self.data:
64+
value = self.data[sensor_key]
65+
66+
if (sensor_key == SENSOR_REBOOT_TIME) and value:
67+
# convert the reboot time to a datetime object
68+
return utcnow() - timedelta(seconds=value)
69+
70+
if (sensor_key == SENSOR_TEMPERATURE) and value:
71+
# convert the temperature value to a float
72+
return float(value)
73+
74+
if (sensor_key == SENSOR_VOLTAGE) and value:
75+
# convert the voltage value to a float
76+
return float(value)
77+
78+
return None

homeassistant/components/pglab/device_sensor.py

Lines changed: 0 additions & 56 deletions
This file was deleted.

homeassistant/components/pglab/discovery.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@
2525
async_dispatcher_connect,
2626
async_dispatcher_send,
2727
)
28-
from homeassistant.helpers.entity import Entity
2928

3029
from .const import DISCOVERY_TOPIC, DOMAIN, LOGGER
31-
from .device_sensor import PGLabDeviceSensor
30+
from .coordinator import PGLabSensorsCoordinator
3231

3332
if TYPE_CHECKING:
34-
from . import PGLABConfigEntry
33+
from . import PGLabConfigEntry
3534

3635
# Supported platforms.
3736
PLATFORMS = [
@@ -69,23 +68,28 @@ def get_device_id_from_discovery_topic(topic: str) -> str | None:
6968
class DiscoverDeviceInfo:
7069
"""Keeps information of the PGLab discovered device."""
7170

72-
def __init__(self, pglab_device: PyPGLabDevice) -> None:
71+
def __init__(
72+
self,
73+
hass: HomeAssistant,
74+
config_entry: PGLabConfigEntry,
75+
pglab_device: PyPGLabDevice,
76+
) -> None:
7377
"""Initialize the device discovery info."""
7478

7579
# Hash string represents the devices actual configuration,
7680
# it depends on the number of available relays and shutters.
7781
# When the hash string changes the devices entities must be rebuilt.
7882
self._hash = pglab_device.hash
7983
self._entities: list[tuple[str, str]] = []
80-
self._sensors = PGLabDeviceSensor(pglab_device)
84+
self.coordinator = PGLabSensorsCoordinator(hass, config_entry, pglab_device)
8185

82-
def add_entity(self, entity: Entity) -> None:
86+
def add_entity(self, platform_domain: str, entity_unique_id: str | None) -> None:
8387
"""Add an entity."""
8488

8589
# PGLabEntity always have unique IDs
8690
if TYPE_CHECKING:
87-
assert entity.unique_id is not None
88-
self._entities.append((entity.platform.domain, entity.unique_id))
91+
assert entity_unique_id is not None
92+
self._entities.append((platform_domain, entity_unique_id))
8993

9094
@property
9195
def hash(self) -> int:
@@ -97,18 +101,15 @@ def entities(self) -> list[tuple[str, str]]:
97101
"""Return array of entities available."""
98102
return self._entities
99103

100-
@property
101-
def sensors(self) -> PGLabDeviceSensor:
102-
"""Return the PGLab device sensor."""
103-
return self._sensors
104-
105104

106-
async def createDiscoverDeviceInfo(pglab_device: PyPGLabDevice) -> DiscoverDeviceInfo:
105+
async def create_discover_device_info(
106+
hass: HomeAssistant, config_entry: PGLabConfigEntry, pglab_device: PyPGLabDevice
107+
) -> DiscoverDeviceInfo:
107108
"""Create a new DiscoverDeviceInfo instance."""
108-
discovery_info = DiscoverDeviceInfo(pglab_device)
109+
discovery_info = DiscoverDeviceInfo(hass, config_entry, pglab_device)
109110

110111
# Subscribe to sensor state changes.
111-
await discovery_info.sensors.subscribe_topics()
112+
await discovery_info.coordinator.subscribe_topics()
112113
return discovery_info
113114

114115

@@ -184,7 +185,10 @@ def __clean_discovered_device(self, hass: HomeAssistant, device_id: str) -> None
184185
del self._discovered[device_id]
185186

186187
async def start(
187-
self, hass: HomeAssistant, mqtt: PyPGLabMqttClient, entry: PGLABConfigEntry
188+
self,
189+
hass: HomeAssistant,
190+
mqtt: PyPGLabMqttClient,
191+
config_entry: PGLabConfigEntry,
188192
) -> None:
189193
"""Start discovering a PGLab devices."""
190194

@@ -210,7 +214,7 @@ async def discovery_message_received(msg: ReceiveMessage) -> None:
210214
# Create a new device.
211215
device_registry = dr.async_get(hass)
212216
device_registry.async_get_or_create(
213-
config_entry_id=entry.entry_id,
217+
config_entry_id=config_entry.entry_id,
214218
configuration_url=f"http://{pglab_device.ip}/",
215219
connections={(CONNECTION_NETWORK_MAC, pglab_device.mac)},
216220
identifiers={(DOMAIN, pglab_device.id)},
@@ -241,7 +245,9 @@ async def discovery_message_received(msg: ReceiveMessage) -> None:
241245
self.__clean_discovered_device(hass, pglab_device.id)
242246

243247
# Add a new device.
244-
discovery_info = await createDiscoverDeviceInfo(pglab_device)
248+
discovery_info = await create_discover_device_info(
249+
hass, config_entry, pglab_device
250+
)
245251
self._discovered[pglab_device.id] = discovery_info
246252

247253
# Create all new relay entities.
@@ -256,7 +262,7 @@ async def discovery_message_received(msg: ReceiveMessage) -> None:
256262
hass,
257263
CREATE_NEW_ENTITY[Platform.SENSOR],
258264
pglab_device,
259-
discovery_info.sensors,
265+
discovery_info.coordinator,
260266
)
261267

262268
topics = {
@@ -267,7 +273,7 @@ async def discovery_message_received(msg: ReceiveMessage) -> None:
267273
}
268274

269275
# Forward setup all HA supported platforms.
270-
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
276+
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
271277

272278
self._mqtt_client = mqtt
273279
self._substate = async_prepare_subscribe_topics(hass, self._substate, topics)
@@ -282,22 +288,24 @@ async def register_platform(
282288
)
283289
self._disconnect_platform.append(disconnect_callback)
284290

285-
async def stop(self, hass: HomeAssistant, entry: PGLABConfigEntry) -> None:
291+
async def stop(self, hass: HomeAssistant, config_entry: PGLabConfigEntry) -> None:
286292
"""Stop to discovery PG LAB devices."""
287-
await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
293+
await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
288294

289295
# Disconnect all registered platforms.
290296
for disconnect_callback in self._disconnect_platform:
291297
disconnect_callback()
292298

293299
async_unsubscribe_topics(hass, self._substate)
294300

295-
async def add_entity(self, entity: Entity, device_id: str):
301+
async def add_entity(
302+
self, platform_domain: str, entity_unique_id: str | None, device_id: str
303+
):
296304
"""Save a new PG LAB device entity."""
297305

298306
# Be sure that the device is been discovered.
299307
if device_id not in self._discovered:
300308
raise PGLabDiscoveryError("Unknown device, device_id not discovered")
301309

302310
discovery_info = self._discovered[device_id]
303-
discovery_info.add_entity(entity)
311+
discovery_info.add_entity(platform_domain, entity_unique_id)

0 commit comments

Comments
 (0)