mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add support for polled Smart Energy Metering sensors to ZHA (#71527)
* Add framework for polled se metering sensors * add model * find attr * type info
This commit is contained in:
parent
5ca82b2d33
commit
60387a417f
@ -102,7 +102,7 @@ class ElectricalMeasurementChannel(ZigbeeChannel):
|
|||||||
for attr, value in result.items():
|
for attr, value in result.items():
|
||||||
self.async_send_signal(
|
self.async_send_signal(
|
||||||
f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}",
|
f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}",
|
||||||
self.cluster.attridx.get(attr, attr),
|
self.cluster.find_attribute(attr).id,
|
||||||
attr,
|
attr,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,12 @@ from functools import partialmethod
|
|||||||
from zigpy.zcl.clusters import smartenergy
|
from zigpy.zcl.clusters import smartenergy
|
||||||
|
|
||||||
from .. import registries, typing as zha_typing
|
from .. import registries, typing as zha_typing
|
||||||
from ..const import REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_OP
|
from ..const import (
|
||||||
|
REPORT_CONFIG_ASAP,
|
||||||
|
REPORT_CONFIG_DEFAULT,
|
||||||
|
REPORT_CONFIG_OP,
|
||||||
|
SIGNAL_ATTR_UPDATED,
|
||||||
|
)
|
||||||
from .base import ZigbeeChannel
|
from .base import ZigbeeChannel
|
||||||
|
|
||||||
|
|
||||||
@ -163,6 +168,25 @@ class Metering(ZigbeeChannel):
|
|||||||
) # 1 digit to the right, 15 digits to the left
|
) # 1 digit to the right, 15 digits to the left
|
||||||
self._summa_format = self.get_formatting(fmting)
|
self._summa_format = self.get_formatting(fmting)
|
||||||
|
|
||||||
|
async def async_force_update(self) -> None:
|
||||||
|
"""Retrieve latest state."""
|
||||||
|
self.debug("async_force_update")
|
||||||
|
|
||||||
|
attrs = [
|
||||||
|
a["attr"]
|
||||||
|
for a in self.REPORT_CONFIG
|
||||||
|
if a["attr"] not in self.cluster.unsupported_attributes
|
||||||
|
]
|
||||||
|
result = await self.get_attributes(attrs, from_cache=False, only_cache=False)
|
||||||
|
if result:
|
||||||
|
for attr, value in result.items():
|
||||||
|
self.async_send_signal(
|
||||||
|
f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}",
|
||||||
|
self.cluster.find_attribute(attr).id,
|
||||||
|
attr,
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_formatting(formatting: int) -> str:
|
def get_formatting(formatting: int) -> str:
|
||||||
"""Return a formatting string, given the formatting value.
|
"""Return a formatting string, given the formatting value.
|
||||||
|
@ -420,7 +420,10 @@ class Illuminance(Sensor):
|
|||||||
return round(pow(10, ((value - 1) / 10000)), 1)
|
return round(pow(10, ((value - 1) / 10000)), 1)
|
||||||
|
|
||||||
|
|
||||||
@MULTI_MATCH(channel_names=CHANNEL_SMARTENERGY_METERING)
|
@MULTI_MATCH(
|
||||||
|
channel_names=CHANNEL_SMARTENERGY_METERING,
|
||||||
|
stop_on_match_group=CHANNEL_SMARTENERGY_METERING,
|
||||||
|
)
|
||||||
class SmartEnergyMetering(Sensor):
|
class SmartEnergyMetering(Sensor):
|
||||||
"""Metering sensor."""
|
"""Metering sensor."""
|
||||||
|
|
||||||
@ -464,6 +467,26 @@ class SmartEnergyMetering(Sensor):
|
|||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
@MULTI_MATCH(
|
||||||
|
channel_names=CHANNEL_SMARTENERGY_METERING,
|
||||||
|
models={"TS011F"},
|
||||||
|
stop_on_match_group=CHANNEL_SMARTENERGY_METERING,
|
||||||
|
)
|
||||||
|
class PolledSmartEnergyMetering(SmartEnergyMetering):
|
||||||
|
"""Polled metering sensor."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self) -> bool:
|
||||||
|
"""Poll the entity for current state."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_update(self) -> None:
|
||||||
|
"""Retrieve latest state."""
|
||||||
|
if not self.available:
|
||||||
|
return
|
||||||
|
await self._channel.async_force_update()
|
||||||
|
|
||||||
|
|
||||||
@MULTI_MATCH(channel_names=CHANNEL_SMARTENERGY_METERING)
|
@MULTI_MATCH(channel_names=CHANNEL_SMARTENERGY_METERING)
|
||||||
class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered"):
|
class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered"):
|
||||||
"""Smart Energy Metering summation sensor."""
|
"""Smart Energy Metering summation sensor."""
|
||||||
|
@ -29,6 +29,7 @@ from .entity import ZhaEntity, ZhaGroupEntity
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .core.channels.base import ZigbeeChannel
|
from .core.channels.base import ZigbeeChannel
|
||||||
|
from .core.channels.general import OnOffChannel
|
||||||
from .core.device import ZHADevice
|
from .core.device import ZHADevice
|
||||||
|
|
||||||
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.SWITCH)
|
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.SWITCH)
|
||||||
@ -62,10 +63,16 @@ async def async_setup_entry(
|
|||||||
class Switch(ZhaEntity, SwitchEntity):
|
class Switch(ZhaEntity, SwitchEntity):
|
||||||
"""ZHA switch."""
|
"""ZHA switch."""
|
||||||
|
|
||||||
def __init__(self, unique_id, zha_device, channels, **kwargs):
|
def __init__(
|
||||||
|
self,
|
||||||
|
unique_id: str,
|
||||||
|
zha_device: ZHADevice,
|
||||||
|
channels: list[ZigbeeChannel],
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
"""Initialize the ZHA switch."""
|
"""Initialize the ZHA switch."""
|
||||||
super().__init__(unique_id, zha_device, channels, **kwargs)
|
super().__init__(unique_id, zha_device, channels, **kwargs)
|
||||||
self._on_off_channel = self.cluster_channels.get(CHANNEL_ON_OFF)
|
self._on_off_channel: OnOffChannel = self.cluster_channels.get(CHANNEL_ON_OFF)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
@ -74,14 +81,14 @@ class Switch(ZhaEntity, SwitchEntity):
|
|||||||
return False
|
return False
|
||||||
return self._on_off_channel.on_off
|
return self._on_off_channel.on_off
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
result = await self._on_off_channel.turn_on()
|
result = await self._on_off_channel.turn_on()
|
||||||
if not result:
|
if not result:
|
||||||
return
|
return
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
result = await self._on_off_channel.turn_off()
|
result = await self._on_off_channel.turn_off()
|
||||||
if not result:
|
if not result:
|
||||||
@ -112,7 +119,12 @@ class SwitchGroup(ZhaGroupEntity, SwitchEntity):
|
|||||||
"""Representation of a switch group."""
|
"""Representation of a switch group."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, entity_ids: list[str], unique_id: str, group_id: int, zha_device, **kwargs
|
self,
|
||||||
|
entity_ids: list[str],
|
||||||
|
unique_id: str,
|
||||||
|
group_id: int,
|
||||||
|
zha_device: ZHADevice,
|
||||||
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a switch group."""
|
"""Initialize a switch group."""
|
||||||
super().__init__(entity_ids, unique_id, group_id, zha_device, **kwargs)
|
super().__init__(entity_ids, unique_id, group_id, zha_device, **kwargs)
|
||||||
@ -126,7 +138,7 @@ class SwitchGroup(ZhaGroupEntity, SwitchEntity):
|
|||||||
"""Return if the switch is on based on the statemachine."""
|
"""Return if the switch is on based on the statemachine."""
|
||||||
return bool(self._state)
|
return bool(self._state)
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
result = await self._on_off_channel.on()
|
result = await self._on_off_channel.on()
|
||||||
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
||||||
@ -134,7 +146,7 @@ class SwitchGroup(ZhaGroupEntity, SwitchEntity):
|
|||||||
self._state = True
|
self._state = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
result = await self._on_off_channel.off()
|
result = await self._on_off_channel.off()
|
||||||
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
||||||
@ -165,7 +177,7 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity):
|
|||||||
unique_id: str,
|
unique_id: str,
|
||||||
zha_device: ZHADevice,
|
zha_device: ZHADevice,
|
||||||
channels: list[ZigbeeChannel],
|
channels: list[ZigbeeChannel],
|
||||||
**kwargs,
|
**kwargs: Any,
|
||||||
) -> ZhaEntity | None:
|
) -> ZhaEntity | None:
|
||||||
"""Entity Factory.
|
"""Entity Factory.
|
||||||
|
|
||||||
@ -190,7 +202,7 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity):
|
|||||||
unique_id: str,
|
unique_id: str,
|
||||||
zha_device: ZHADevice,
|
zha_device: ZHADevice,
|
||||||
channels: list[ZigbeeChannel],
|
channels: list[ZigbeeChannel],
|
||||||
**kwargs,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init this number configuration entity."""
|
"""Init this number configuration entity."""
|
||||||
self._channel: ZigbeeChannel = channels[0]
|
self._channel: ZigbeeChannel = channels[0]
|
||||||
@ -215,7 +227,7 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity):
|
|||||||
invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute))
|
invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute))
|
||||||
return (not val) if invert else val
|
return (not val) if invert else val
|
||||||
|
|
||||||
async def async_turn_on_off(self, state) -> None:
|
async def async_turn_on_off(self, state: bool) -> None:
|
||||||
"""Turn the entity on or off."""
|
"""Turn the entity on or off."""
|
||||||
try:
|
try:
|
||||||
invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute))
|
invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute))
|
||||||
@ -230,11 +242,11 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity):
|
|||||||
):
|
):
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
await self.async_turn_on_off(True)
|
await self.async_turn_on_off(True)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
await self.async_turn_on_off(False)
|
await self.async_turn_on_off(False)
|
||||||
|
|
||||||
@ -263,8 +275,8 @@ class OnOffWindowDetectionFunctionConfigurationEntity(
|
|||||||
):
|
):
|
||||||
"""Representation of a ZHA window detection configuration entity."""
|
"""Representation of a ZHA window detection configuration entity."""
|
||||||
|
|
||||||
_zcl_attribute = "window_detection_function"
|
_zcl_attribute: str = "window_detection_function"
|
||||||
_zcl_inverter_attribute = "window_detection_function_inverter"
|
_zcl_inverter_attribute: str = "window_detection_function_inverter"
|
||||||
|
|
||||||
|
|
||||||
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"})
|
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"})
|
||||||
@ -273,4 +285,4 @@ class P1MotionTriggerIndicatorSwitch(
|
|||||||
):
|
):
|
||||||
"""Representation of a ZHA motion triggering configuration entity."""
|
"""Representation of a ZHA motion triggering configuration entity."""
|
||||||
|
|
||||||
_zcl_attribute = "trigger_indicator"
|
_zcl_attribute: str = "trigger_indicator"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user