home-assistant/homeassistant/components/zha/core/channels/smartenergy.py

182 lines
5.1 KiB
Python

"""
Smart energy channels module for Zigbee Home Automation.
For more details about this component, please refer to the documentation at
https://home-assistant.io/integrations/zha/
"""
import logging
import zigpy.zcl.clusters.smartenergy as smartenergy
from homeassistant.core import callback
from .. import registries
from ..channels import AttributeListeningChannel, ZigbeeChannel
from ..const import REPORT_CONFIG_DEFAULT
_LOGGER = logging.getLogger(__name__)
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Calendar.cluster_id)
class Calendar(ZigbeeChannel):
"""Calendar channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.DeviceManagement.cluster_id)
class DeviceManagement(ZigbeeChannel):
"""Device Management channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Drlc.cluster_id)
class Drlc(ZigbeeChannel):
"""Demand Response and Load Control channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.EnergyManagement.cluster_id)
class EnergyManagement(ZigbeeChannel):
"""Energy Management channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Events.cluster_id)
class Events(ZigbeeChannel):
"""Event channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.KeyEstablishment.cluster_id)
class KeyEstablishment(ZigbeeChannel):
"""Key Establishment channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.MduPairing.cluster_id)
class MduPairing(ZigbeeChannel):
"""Pairing channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Messaging.cluster_id)
class Messaging(ZigbeeChannel):
"""Messaging channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Metering.cluster_id)
class Metering(AttributeListeningChannel):
"""Metering channel."""
REPORT_CONFIG = [{"attr": "instantaneous_demand", "config": REPORT_CONFIG_DEFAULT}]
unit_of_measure_map = {
0x00: "kW",
0x01: "m³/h",
0x02: "ft³/h",
0x03: "ccf/h",
0x04: "US gal/h",
0x05: "IMP gal/h",
0x06: "BTU/h",
0x07: "l/h",
0x08: "kPa",
0x09: "kPa",
0x0A: "mcf/h",
0x0B: "unitless",
0x0C: "MJ/s",
}
def __init__(self, cluster, device):
"""Initialize Metering."""
super().__init__(cluster, device)
self._divisor = None
self._multiplier = None
self._unit_enum = None
self._format_spec = None
async def async_configure(self):
"""Configure channel."""
await self.fetch_config(False)
await super().async_configure()
async def async_initialize(self, from_cache):
"""Initialize channel."""
await self.fetch_config(True)
await super().async_initialize(from_cache)
@callback
def attribute_updated(self, attrid, value):
"""Handle attribute update from Metering cluster."""
super().attribute_updated(attrid, value * self._multiplier / self._divisor)
@property
def unit_of_measurement(self):
"""Return unit of measurement."""
return self.unit_of_measure_map.get(self._unit_enum & 0x7F, "unknown")
async def fetch_config(self, from_cache):
"""Fetch config from device and updates format specifier."""
self._divisor = await self.get_attribute_value("divisor", from_cache=from_cache)
self._multiplier = await self.get_attribute_value(
"multiplier", from_cache=from_cache
)
self._unit_enum = await self.get_attribute_value(
"unit_of_measure", from_cache=from_cache
)
fmting = await self.get_attribute_value(
"demand_formatting", from_cache=from_cache
)
if self._divisor is None or self._divisor == 0:
self._divisor = 1
if self._multiplier is None or self._multiplier == 0:
self._multiplier = 1
if self._unit_enum is None:
self._unit_enum = 0x7F # unknown
if fmting is None:
fmting = 0xF9 # 1 digit to the right, 15 digits to the left
r_digits = fmting & 0x07 # digits to the right of decimal point
l_digits = (fmting >> 3) & 0x0F # digits to the left of decimal point
if l_digits == 0:
l_digits = 15
width = r_digits + l_digits + (1 if r_digits > 0 else 0)
if fmting & 0x80:
self._format_spec = "{:" + str(width) + "." + str(r_digits) + "f}"
else:
self._format_spec = "{:0" + str(width) + "." + str(r_digits) + "f}"
def formatter_function(self, value):
"""Return formatted value for display."""
return self._format_spec.format(value).lstrip()
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Prepayment.cluster_id)
class Prepayment(ZigbeeChannel):
"""Prepayment channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Price.cluster_id)
class Price(ZigbeeChannel):
"""Price channel."""
pass
@registries.ZIGBEE_CHANNEL_REGISTRY.register(smartenergy.Tunneling.cluster_id)
class Tunneling(ZigbeeChannel):
"""Tunneling channel."""
pass