mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Reimplement PGLab sensor to use a coordinator (#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
This commit is contained in:
parent
cc5c8bf5e3
commit
cc30823726
@ -23,12 +23,14 @@ from homeassistant.helpers import config_validation as cv
|
|||||||
from .const import DOMAIN, LOGGER
|
from .const import DOMAIN, LOGGER
|
||||||
from .discovery import PGLabDiscovery
|
from .discovery import PGLabDiscovery
|
||||||
|
|
||||||
type PGLABConfigEntry = ConfigEntry[PGLabDiscovery]
|
type PGLabConfigEntry = ConfigEntry[PGLabDiscovery]
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: PGLABConfigEntry) -> bool:
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant, config_entry: PGLabConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Set up PG LAB Electronics integration from a config entry."""
|
"""Set up PG LAB Electronics integration from a config entry."""
|
||||||
|
|
||||||
async def mqtt_publish(topic: str, payload: str, qos: int, retain: bool) -> None:
|
async def mqtt_publish(topic: str, payload: str, qos: int, retain: bool) -> None:
|
||||||
@ -67,19 +69,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: PGLABConfigEntry) -> boo
|
|||||||
pglab_mqtt = PyPGLabMqttClient(mqtt_publish, mqtt_subscribe, mqtt_unsubscribe)
|
pglab_mqtt = PyPGLabMqttClient(mqtt_publish, mqtt_subscribe, mqtt_unsubscribe)
|
||||||
|
|
||||||
# Setup PGLab device discovery.
|
# Setup PGLab device discovery.
|
||||||
entry.runtime_data = PGLabDiscovery()
|
config_entry.runtime_data = PGLabDiscovery()
|
||||||
|
|
||||||
# Start to discovery PG Lab devices.
|
# Start to discovery PG Lab devices.
|
||||||
await entry.runtime_data.start(hass, pglab_mqtt, entry)
|
await config_entry.runtime_data.start(hass, pglab_mqtt, config_entry)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: PGLABConfigEntry) -> bool:
|
async def async_unload_entry(
|
||||||
|
hass: HomeAssistant, config_entry: PGLabConfigEntry
|
||||||
|
) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
|
|
||||||
# Stop PGLab device discovery.
|
# Stop PGLab device discovery.
|
||||||
pglab_discovery = entry.runtime_data
|
pglab_discovery = config_entry.runtime_data
|
||||||
await pglab_discovery.stop(hass, entry)
|
await pglab_discovery.stop(hass, config_entry)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
78
homeassistant/components/pglab/coordinator.py
Normal file
78
homeassistant/components/pglab/coordinator.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
"""Coordinator for PG LAB Electronics."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from pypglab.const import SENSOR_REBOOT_TIME, SENSOR_TEMPERATURE, SENSOR_VOLTAGE
|
||||||
|
from pypglab.device import Device as PyPGLabDevice
|
||||||
|
from pypglab.sensor import Sensor as PyPGLabSensors
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
|
from .const import DOMAIN, LOGGER
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import PGLabConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
class PGLabSensorsCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
|
"""Class to update Sensor Entities when receiving new data."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: PGLabConfigEntry,
|
||||||
|
pglab_device: PyPGLabDevice,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize."""
|
||||||
|
|
||||||
|
# get a reference of PG Lab device internal sensors state
|
||||||
|
self._sensors: PyPGLabSensors = pglab_device.sensors
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
LOGGER,
|
||||||
|
config_entry=config_entry,
|
||||||
|
name=DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _new_sensors_data(self, payload: str) -> None:
|
||||||
|
"""Handle new sensor data."""
|
||||||
|
|
||||||
|
# notify all listeners that new sensor values are available
|
||||||
|
self.async_set_updated_data(self._sensors.state)
|
||||||
|
|
||||||
|
async def subscribe_topics(self) -> None:
|
||||||
|
"""Subscribe the sensors state to be notifty from MQTT update messages."""
|
||||||
|
|
||||||
|
# subscribe to the pypglab sensors to receive updates from the mqtt broker
|
||||||
|
# when a new sensor values are available
|
||||||
|
await self._sensors.subscribe_topics()
|
||||||
|
|
||||||
|
# set the callback to be called when a new sensor values are available
|
||||||
|
self._sensors.set_on_state_callback(self._new_sensors_data)
|
||||||
|
|
||||||
|
def get_sensor_value(self, sensor_key: str) -> float | datetime | None:
|
||||||
|
"""Return the value of a sensor."""
|
||||||
|
|
||||||
|
if self.data:
|
||||||
|
value = self.data[sensor_key]
|
||||||
|
|
||||||
|
if (sensor_key == SENSOR_REBOOT_TIME) and value:
|
||||||
|
# convert the reboot time to a datetime object
|
||||||
|
return utcnow() - timedelta(seconds=value)
|
||||||
|
|
||||||
|
if (sensor_key == SENSOR_TEMPERATURE) and value:
|
||||||
|
# convert the temperature value to a float
|
||||||
|
return float(value)
|
||||||
|
|
||||||
|
if (sensor_key == SENSOR_VOLTAGE) and value:
|
||||||
|
# convert the voltage value to a float
|
||||||
|
return float(value)
|
||||||
|
|
||||||
|
return None
|
@ -1,56 +0,0 @@
|
|||||||
"""Device Sensor for PG LAB Electronics."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from pypglab.device import Device as PyPGLabDevice
|
|
||||||
from pypglab.sensor import Sensor as PyPGLabSensors
|
|
||||||
|
|
||||||
from homeassistant.core import callback
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .entity import PGLabEntity
|
|
||||||
|
|
||||||
|
|
||||||
class PGLabDeviceSensor:
|
|
||||||
"""Keeps PGLab device sensor update."""
|
|
||||||
|
|
||||||
def __init__(self, pglab_device: PyPGLabDevice) -> None:
|
|
||||||
"""Initialize the device sensor."""
|
|
||||||
|
|
||||||
# get a reference of PG Lab device internal sensors state
|
|
||||||
self._sensors: PyPGLabSensors = pglab_device.sensors
|
|
||||||
|
|
||||||
self._ha_sensors: list[PGLabEntity] = [] # list of HA entity sensors
|
|
||||||
|
|
||||||
async def subscribe_topics(self):
|
|
||||||
"""Subscribe to the device sensors topics."""
|
|
||||||
self._sensors.set_on_state_callback(self.state_updated)
|
|
||||||
await self._sensors.subscribe_topics()
|
|
||||||
|
|
||||||
def add_ha_sensor(self, entity: PGLabEntity) -> None:
|
|
||||||
"""Add a new HA sensor to the list."""
|
|
||||||
self._ha_sensors.append(entity)
|
|
||||||
|
|
||||||
def remove_ha_sensor(self, entity: PGLabEntity) -> None:
|
|
||||||
"""Remove a HA sensor from the list."""
|
|
||||||
self._ha_sensors.remove(entity)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def state_updated(self, payload: str) -> None:
|
|
||||||
"""Handle state updates."""
|
|
||||||
|
|
||||||
# notify all HA sensors that PG LAB device sensor fields have been updated
|
|
||||||
for s in self._ha_sensors:
|
|
||||||
s.state_updated(payload)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self) -> dict:
|
|
||||||
"""Return the device sensors state."""
|
|
||||||
return self._sensors.state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sensors(self) -> PyPGLabSensors:
|
|
||||||
"""Return the pypglab device sensors."""
|
|
||||||
return self._sensors
|
|
@ -25,13 +25,12 @@ from homeassistant.helpers.dispatcher import (
|
|||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
|
|
||||||
from .const import DISCOVERY_TOPIC, DOMAIN, LOGGER
|
from .const import DISCOVERY_TOPIC, DOMAIN, LOGGER
|
||||||
from .device_sensor import PGLabDeviceSensor
|
from .coordinator import PGLabSensorsCoordinator
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import PGLABConfigEntry
|
from . import PGLabConfigEntry
|
||||||
|
|
||||||
# Supported platforms.
|
# Supported platforms.
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
@ -69,7 +68,12 @@ def get_device_id_from_discovery_topic(topic: str) -> str | None:
|
|||||||
class DiscoverDeviceInfo:
|
class DiscoverDeviceInfo:
|
||||||
"""Keeps information of the PGLab discovered device."""
|
"""Keeps information of the PGLab discovered device."""
|
||||||
|
|
||||||
def __init__(self, pglab_device: PyPGLabDevice) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: PGLabConfigEntry,
|
||||||
|
pglab_device: PyPGLabDevice,
|
||||||
|
) -> None:
|
||||||
"""Initialize the device discovery info."""
|
"""Initialize the device discovery info."""
|
||||||
|
|
||||||
# Hash string represents the devices actual configuration,
|
# Hash string represents the devices actual configuration,
|
||||||
@ -77,15 +81,15 @@ class DiscoverDeviceInfo:
|
|||||||
# When the hash string changes the devices entities must be rebuilt.
|
# When the hash string changes the devices entities must be rebuilt.
|
||||||
self._hash = pglab_device.hash
|
self._hash = pglab_device.hash
|
||||||
self._entities: list[tuple[str, str]] = []
|
self._entities: list[tuple[str, str]] = []
|
||||||
self._sensors = PGLabDeviceSensor(pglab_device)
|
self.coordinator = PGLabSensorsCoordinator(hass, config_entry, pglab_device)
|
||||||
|
|
||||||
def add_entity(self, entity: Entity) -> None:
|
def add_entity(self, platform_domain: str, entity_unique_id: str | None) -> None:
|
||||||
"""Add an entity."""
|
"""Add an entity."""
|
||||||
|
|
||||||
# PGLabEntity always have unique IDs
|
# PGLabEntity always have unique IDs
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert entity.unique_id is not None
|
assert entity_unique_id is not None
|
||||||
self._entities.append((entity.platform.domain, entity.unique_id))
|
self._entities.append((platform_domain, entity_unique_id))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hash(self) -> int:
|
def hash(self) -> int:
|
||||||
@ -97,18 +101,15 @@ class DiscoverDeviceInfo:
|
|||||||
"""Return array of entities available."""
|
"""Return array of entities available."""
|
||||||
return self._entities
|
return self._entities
|
||||||
|
|
||||||
@property
|
|
||||||
def sensors(self) -> PGLabDeviceSensor:
|
|
||||||
"""Return the PGLab device sensor."""
|
|
||||||
return self._sensors
|
|
||||||
|
|
||||||
|
async def create_discover_device_info(
|
||||||
async def createDiscoverDeviceInfo(pglab_device: PyPGLabDevice) -> DiscoverDeviceInfo:
|
hass: HomeAssistant, config_entry: PGLabConfigEntry, pglab_device: PyPGLabDevice
|
||||||
|
) -> DiscoverDeviceInfo:
|
||||||
"""Create a new DiscoverDeviceInfo instance."""
|
"""Create a new DiscoverDeviceInfo instance."""
|
||||||
discovery_info = DiscoverDeviceInfo(pglab_device)
|
discovery_info = DiscoverDeviceInfo(hass, config_entry, pglab_device)
|
||||||
|
|
||||||
# Subscribe to sensor state changes.
|
# Subscribe to sensor state changes.
|
||||||
await discovery_info.sensors.subscribe_topics()
|
await discovery_info.coordinator.subscribe_topics()
|
||||||
return discovery_info
|
return discovery_info
|
||||||
|
|
||||||
|
|
||||||
@ -184,7 +185,10 @@ class PGLabDiscovery:
|
|||||||
del self._discovered[device_id]
|
del self._discovered[device_id]
|
||||||
|
|
||||||
async def start(
|
async def start(
|
||||||
self, hass: HomeAssistant, mqtt: PyPGLabMqttClient, entry: PGLABConfigEntry
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mqtt: PyPGLabMqttClient,
|
||||||
|
config_entry: PGLabConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Start discovering a PGLab devices."""
|
"""Start discovering a PGLab devices."""
|
||||||
|
|
||||||
@ -210,7 +214,7 @@ class PGLabDiscovery:
|
|||||||
# Create a new device.
|
# Create a new device.
|
||||||
device_registry = dr.async_get(hass)
|
device_registry = dr.async_get(hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry_id=entry.entry_id,
|
config_entry_id=config_entry.entry_id,
|
||||||
configuration_url=f"http://{pglab_device.ip}/",
|
configuration_url=f"http://{pglab_device.ip}/",
|
||||||
connections={(CONNECTION_NETWORK_MAC, pglab_device.mac)},
|
connections={(CONNECTION_NETWORK_MAC, pglab_device.mac)},
|
||||||
identifiers={(DOMAIN, pglab_device.id)},
|
identifiers={(DOMAIN, pglab_device.id)},
|
||||||
@ -241,7 +245,9 @@ class PGLabDiscovery:
|
|||||||
self.__clean_discovered_device(hass, pglab_device.id)
|
self.__clean_discovered_device(hass, pglab_device.id)
|
||||||
|
|
||||||
# Add a new device.
|
# Add a new device.
|
||||||
discovery_info = await createDiscoverDeviceInfo(pglab_device)
|
discovery_info = await create_discover_device_info(
|
||||||
|
hass, config_entry, pglab_device
|
||||||
|
)
|
||||||
self._discovered[pglab_device.id] = discovery_info
|
self._discovered[pglab_device.id] = discovery_info
|
||||||
|
|
||||||
# Create all new relay entities.
|
# Create all new relay entities.
|
||||||
@ -256,7 +262,7 @@ class PGLabDiscovery:
|
|||||||
hass,
|
hass,
|
||||||
CREATE_NEW_ENTITY[Platform.SENSOR],
|
CREATE_NEW_ENTITY[Platform.SENSOR],
|
||||||
pglab_device,
|
pglab_device,
|
||||||
discovery_info.sensors,
|
discovery_info.coordinator,
|
||||||
)
|
)
|
||||||
|
|
||||||
topics = {
|
topics = {
|
||||||
@ -267,7 +273,7 @@ class PGLabDiscovery:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Forward setup all HA supported platforms.
|
# Forward setup all HA supported platforms.
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||||
|
|
||||||
self._mqtt_client = mqtt
|
self._mqtt_client = mqtt
|
||||||
self._substate = async_prepare_subscribe_topics(hass, self._substate, topics)
|
self._substate = async_prepare_subscribe_topics(hass, self._substate, topics)
|
||||||
@ -282,9 +288,9 @@ class PGLabDiscovery:
|
|||||||
)
|
)
|
||||||
self._disconnect_platform.append(disconnect_callback)
|
self._disconnect_platform.append(disconnect_callback)
|
||||||
|
|
||||||
async def stop(self, hass: HomeAssistant, entry: PGLABConfigEntry) -> None:
|
async def stop(self, hass: HomeAssistant, config_entry: PGLabConfigEntry) -> None:
|
||||||
"""Stop to discovery PG LAB devices."""
|
"""Stop to discovery PG LAB devices."""
|
||||||
await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||||
|
|
||||||
# Disconnect all registered platforms.
|
# Disconnect all registered platforms.
|
||||||
for disconnect_callback in self._disconnect_platform:
|
for disconnect_callback in self._disconnect_platform:
|
||||||
@ -292,7 +298,9 @@ class PGLabDiscovery:
|
|||||||
|
|
||||||
async_unsubscribe_topics(hass, self._substate)
|
async_unsubscribe_topics(hass, self._substate)
|
||||||
|
|
||||||
async def add_entity(self, entity: Entity, device_id: str):
|
async def add_entity(
|
||||||
|
self, platform_domain: str, entity_unique_id: str | None, device_id: str
|
||||||
|
):
|
||||||
"""Save a new PG LAB device entity."""
|
"""Save a new PG LAB device entity."""
|
||||||
|
|
||||||
# Be sure that the device is been discovered.
|
# Be sure that the device is been discovered.
|
||||||
@ -300,4 +308,4 @@ class PGLabDiscovery:
|
|||||||
raise PGLabDiscoveryError("Unknown device, device_id not discovered")
|
raise PGLabDiscoveryError("Unknown device, device_id not discovered")
|
||||||
|
|
||||||
discovery_info = self._discovered[device_id]
|
discovery_info = self._discovered[device_id]
|
||||||
discovery_info.add_entity(entity)
|
discovery_info.add_entity(platform_domain, entity_unique_id)
|
||||||
|
@ -8,69 +8,105 @@ from pypglab.entity import Entity as PyPGLabEntity
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
from .coordinator import PGLabSensorsCoordinator
|
||||||
from .discovery import PGLabDiscovery
|
from .discovery import PGLabDiscovery
|
||||||
|
|
||||||
|
|
||||||
class PGLabEntity(Entity):
|
class PGLabBaseEntity(Entity):
|
||||||
"""Representation of a PGLab entity in Home Assistant."""
|
"""Base class of a PGLab entity in Home Assistant."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
discovery: PGLabDiscovery,
|
pglab_discovery: PGLabDiscovery,
|
||||||
device: PyPGLabDevice,
|
pglab_device: PyPGLabDevice,
|
||||||
entity: PyPGLabEntity,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
|
|
||||||
self._id = entity.id
|
self._device_id = pglab_device.id
|
||||||
self._device_id = device.id
|
self._discovery = pglab_discovery
|
||||||
self._entity = entity
|
|
||||||
self._discovery = discovery
|
|
||||||
|
|
||||||
# Information about the device that is partially visible in the UI.
|
# Information about the device that is partially visible in the UI.
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, device.id)},
|
identifiers={(DOMAIN, pglab_device.id)},
|
||||||
name=device.name,
|
name=pglab_device.name,
|
||||||
sw_version=device.firmware_version,
|
sw_version=pglab_device.firmware_version,
|
||||||
hw_version=device.hardware_version,
|
hw_version=pglab_device.hardware_version,
|
||||||
model=device.type,
|
model=pglab_device.type,
|
||||||
manufacturer=device.manufactor,
|
manufacturer=pglab_device.manufactor,
|
||||||
configuration_url=f"http://{device.ip}/",
|
configuration_url=f"http://{pglab_device.ip}/",
|
||||||
connections={(CONNECTION_NETWORK_MAC, device.mac)},
|
connections={(CONNECTION_NETWORK_MAC, pglab_device.mac)},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def subscribe_to_update(self):
|
|
||||||
"""Subscribe to the entity updates."""
|
|
||||||
self._entity.set_on_state_callback(self.state_updated)
|
|
||||||
await self._entity.subscribe_topics()
|
|
||||||
|
|
||||||
async def unsubscribe_to_update(self):
|
|
||||||
"""Unsubscribe to the entity updates."""
|
|
||||||
await self._entity.unsubscribe_topics()
|
|
||||||
self._entity.set_on_state_callback(None)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Update the device discovery info."""
|
"""Update the device discovery info."""
|
||||||
|
|
||||||
await self.subscribe_to_update()
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
|
|
||||||
# Inform PGLab discovery instance that a new entity is available.
|
# Inform PGLab discovery instance that a new entity is available.
|
||||||
# This is important to know in case the device needs to be reconfigured
|
# This is important to know in case the device needs to be reconfigured
|
||||||
# and the entity can be potentially destroyed.
|
# and the entity can be potentially destroyed.
|
||||||
await self._discovery.add_entity(self, self._device_id)
|
await self._discovery.add_entity(
|
||||||
|
self.platform.domain,
|
||||||
|
self.unique_id,
|
||||||
|
self._device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# propagate the async_added_to_hass to the super class
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
|
|
||||||
|
class PGLabEntity(PGLabBaseEntity):
|
||||||
|
"""Representation of a PGLab entity in Home Assistant."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pglab_discovery: PGLabDiscovery,
|
||||||
|
pglab_device: PyPGLabDevice,
|
||||||
|
pglab_entity: PyPGLabEntity,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the class."""
|
||||||
|
|
||||||
|
super().__init__(pglab_discovery, pglab_device)
|
||||||
|
|
||||||
|
self._id = pglab_entity.id
|
||||||
|
self._entity: PyPGLabEntity = pglab_entity
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe pypglab entity to be updated from mqtt when pypglab entity internal state change."""
|
||||||
|
|
||||||
|
# set the callback to be called when pypglab entity state is changed
|
||||||
|
self._entity.set_on_state_callback(self.state_updated)
|
||||||
|
|
||||||
|
# subscribe to the pypglab entity to receive updates from the mqtt broker
|
||||||
|
await self._entity.subscribe_topics()
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Unsubscribe when removed."""
|
"""Unsubscribe when removed."""
|
||||||
|
|
||||||
await super().async_will_remove_from_hass()
|
await super().async_will_remove_from_hass()
|
||||||
await self.unsubscribe_to_update()
|
await self._entity.unsubscribe_topics()
|
||||||
|
self._entity.set_on_state_callback(None)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def state_updated(self, payload: str) -> None:
|
def state_updated(self, payload: str) -> None:
|
||||||
"""Handle state updates."""
|
"""Handle state updates."""
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class PGLabSensorEntity(PGLabBaseEntity, CoordinatorEntity[PGLabSensorsCoordinator]):
|
||||||
|
"""Representation of a PGLab sensor entity in Home Assistant."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pglab_discovery: PGLabDiscovery,
|
||||||
|
pglab_device: PyPGLabDevice,
|
||||||
|
pglab_coordinator: PGLabSensorsCoordinator,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the class."""
|
||||||
|
|
||||||
|
PGLabBaseEntity.__init__(self, pglab_discovery, pglab_device)
|
||||||
|
CoordinatorEntity.__init__(self, pglab_coordinator)
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from pypglab.const import SENSOR_REBOOT_TIME, SENSOR_TEMPERATURE, SENSOR_VOLTAGE
|
from pypglab.const import SENSOR_REBOOT_TIME, SENSOR_TEMPERATURE, SENSOR_VOLTAGE
|
||||||
from pypglab.device import Device as PyPGLabDevice
|
from pypglab.device import Device as PyPGLabDevice
|
||||||
|
|
||||||
@ -16,12 +14,11 @@ from homeassistant.components.sensor import (
|
|||||||
from homeassistant.const import Platform, UnitOfElectricPotential, UnitOfTemperature
|
from homeassistant.const import Platform, UnitOfElectricPotential, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
|
|
||||||
from . import PGLABConfigEntry
|
from . import PGLabConfigEntry
|
||||||
from .device_sensor import PGLabDeviceSensor
|
from .coordinator import PGLabSensorsCoordinator
|
||||||
from .discovery import PGLabDiscovery
|
from .discovery import PGLabDiscovery
|
||||||
from .entity import PGLabEntity
|
from .entity import PGLabSensorEntity
|
||||||
|
|
||||||
PARALLEL_UPDATES = 0
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
@ -50,7 +47,7 @@ SENSOR_INFO: list[SensorEntityDescription] = [
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: PGLABConfigEntry,
|
config_entry: PGLabConfigEntry,
|
||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up sensor for device."""
|
"""Set up sensor for device."""
|
||||||
@ -58,62 +55,55 @@ async def async_setup_entry(
|
|||||||
@callback
|
@callback
|
||||||
def async_discover(
|
def async_discover(
|
||||||
pglab_device: PyPGLabDevice,
|
pglab_device: PyPGLabDevice,
|
||||||
pglab_device_sensor: PGLabDeviceSensor,
|
pglab_coordinator: PGLabSensorsCoordinator,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Discover and add a PG LAB Sensor."""
|
"""Discover and add a PG LAB Sensor."""
|
||||||
pglab_discovery = config_entry.runtime_data
|
pglab_discovery = config_entry.runtime_data
|
||||||
for description in SENSOR_INFO:
|
|
||||||
pglab_sensor = PGLabSensor(
|
sensors: list[PGLabSensor] = [
|
||||||
pglab_discovery, pglab_device, pglab_device_sensor, description
|
PGLabSensor(
|
||||||
|
description,
|
||||||
|
pglab_discovery,
|
||||||
|
pglab_device,
|
||||||
|
pglab_coordinator,
|
||||||
)
|
)
|
||||||
async_add_entities([pglab_sensor])
|
for description in SENSOR_INFO
|
||||||
|
]
|
||||||
|
|
||||||
|
async_add_entities(sensors)
|
||||||
|
|
||||||
# Register the callback to create the sensor entity when discovered.
|
# Register the callback to create the sensor entity when discovered.
|
||||||
pglab_discovery = config_entry.runtime_data
|
pglab_discovery = config_entry.runtime_data
|
||||||
await pglab_discovery.register_platform(hass, Platform.SENSOR, async_discover)
|
await pglab_discovery.register_platform(hass, Platform.SENSOR, async_discover)
|
||||||
|
|
||||||
|
|
||||||
class PGLabSensor(PGLabEntity, SensorEntity):
|
class PGLabSensor(PGLabSensorEntity, SensorEntity):
|
||||||
"""A PGLab sensor."""
|
"""A PGLab sensor."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
description: SensorEntityDescription,
|
||||||
pglab_discovery: PGLabDiscovery,
|
pglab_discovery: PGLabDiscovery,
|
||||||
pglab_device: PyPGLabDevice,
|
pglab_device: PyPGLabDevice,
|
||||||
pglab_device_sensor: PGLabDeviceSensor,
|
pglab_coordinator: PGLabSensorsCoordinator,
|
||||||
description: SensorEntityDescription,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Sensor class."""
|
"""Initialize the Sensor class."""
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(pglab_discovery, pglab_device, pglab_coordinator)
|
||||||
discovery=pglab_discovery,
|
|
||||||
device=pglab_device,
|
|
||||||
entity=pglab_device_sensor.sensors,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = description.key
|
|
||||||
self._pglab_device_sensor = pglab_device_sensor
|
|
||||||
self._attr_unique_id = f"{pglab_device.id}_{description.key}"
|
self._attr_unique_id = f"{pglab_device.id}_{description.key}"
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def state_updated(self, payload: str) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle state updates."""
|
"""Update attributes when the coordinator updates."""
|
||||||
|
|
||||||
# get the sensor value from pglab multi fields sensor
|
self._attr_native_value = self.coordinator.get_sensor_value(
|
||||||
value = self._pglab_device_sensor.state[self._type]
|
self.entity_description.key
|
||||||
|
)
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
if self.entity_description.device_class == SensorDeviceClass.TIMESTAMP:
|
@property
|
||||||
self._attr_native_value = utcnow() - timedelta(seconds=value)
|
def available(self) -> bool:
|
||||||
else:
|
"""Return PG LAB sensor availability."""
|
||||||
self._attr_native_value = value
|
return super().available and self.native_value is not None
|
||||||
|
|
||||||
super().state_updated(payload)
|
|
||||||
|
|
||||||
async def subscribe_to_update(self):
|
|
||||||
"""Register the HA sensor to be notify when the sensor status is changed."""
|
|
||||||
self._pglab_device_sensor.add_ha_sensor(self)
|
|
||||||
|
|
||||||
async def unsubscribe_to_update(self):
|
|
||||||
"""Unregister the HA sensor from sensor tatus updates."""
|
|
||||||
self._pglab_device_sensor.remove_ha_sensor(self)
|
|
||||||
|
@ -12,7 +12,7 @@ from homeassistant.const import Platform
|
|||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
from . import PGLABConfigEntry
|
from . import PGLabConfigEntry
|
||||||
from .discovery import PGLabDiscovery
|
from .discovery import PGLabDiscovery
|
||||||
from .entity import PGLabEntity
|
from .entity import PGLabEntity
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ PARALLEL_UPDATES = 0
|
|||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: PGLABConfigEntry,
|
config_entry: PGLabConfigEntry,
|
||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up switches for device."""
|
"""Set up switches for device."""
|
||||||
@ -52,9 +52,9 @@ class PGLabSwitch(PGLabEntity, SwitchEntity):
|
|||||||
"""Initialize the Switch class."""
|
"""Initialize the Switch class."""
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
discovery=pglab_discovery,
|
pglab_discovery,
|
||||||
device=pglab_device,
|
pglab_device,
|
||||||
entity=pglab_relay,
|
pglab_relay,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._attr_unique_id = f"{pglab_device.id}_relay{pglab_relay.id}"
|
self._attr_unique_id = f"{pglab_device.id}_relay{pglab_relay.id}"
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unknown',
|
'state': 'unavailable',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[mpu_voltage][updated_sensor_mpu_voltage]
|
# name: test_sensors[mpu_voltage][updated_sensor_mpu_voltage]
|
||||||
@ -43,7 +43,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unknown',
|
'state': 'unavailable',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[run_time][updated_sensor_run_time]
|
# name: test_sensors[run_time][updated_sensor_run_time]
|
||||||
@ -74,7 +74,7 @@
|
|||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_reported': <ANY>,
|
'last_reported': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': 'unknown',
|
'state': 'unavailable',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_sensors[temperature][updated_sensor_temperature]
|
# name: test_sensors[temperature][updated_sensor_temperature]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user