diff --git a/README.md b/README.md index 1c4741e..7121522 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This will enable monitor electric circuits in great detail. * **Power**: active, apparent and power factor * **Energy**: partial (resettable) and total * **Demand**: active power, maximum active power (resettable) and timestamp of maximum active power +* **Environment**: Temperature, humidity and CO2 * **Alarm**: current state and its reasons * **Diagnostics**: gateway status, LQI, RSSI, packet loss, connectivity status @@ -39,7 +40,7 @@ This will enable monitor electric circuits in great detail. * All PowerTags * Acti9 Active -* ZBRTT1 (soon) +* All environment sensors (not HeatTag) #### Further integrations diff --git a/custom_components/powertag_gateway/binary_sensor.py b/custom_components/powertag_gateway/binary_sensor.py index 4029311..2bd2eee 100644 --- a/custom_components/powertag_gateway/binary_sensor.py +++ b/custom_components/powertag_gateway/binary_sensor.py @@ -8,19 +8,19 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import CONF_CLIENT, DOMAIN -from .entity_base import PowerTagEntity, GatewayEntity, setup_entities, gateway_device_info -from .powertag_features import FeatureClass +from .entity_base import WirelessDeviceEntity, GatewayEntity, setup_entities, gateway_device_info +from .device_features import FeatureClass from .schneider_modbus import SchneiderModbus, LinkStatus, PanelHealth, TypeOfGateway _LOGGER = logging.getLogger(__name__) -def list_binary_sensors() -> list[type[PowerTagEntity]]: +def list_binary_sensors() -> list[type[WirelessDeviceEntity]]: return [ PowerTagWirelessCommunicationValid, PowerTagRadioCommunicationValid, - PowerTagAlarmValid, - PowerTagGetAlarm + PowerTagAlarm, + AmbientTagAlarm ] @@ -45,7 +45,7 @@ async def async_setup_entry( async_add_entities(entities, update_before_add=False) -class PowerTagWirelessCommunicationValid(PowerTagEntity, BinarySensorEntity): +class PowerTagWirelessCommunicationValid(WirelessDeviceEntity, BinarySensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_device_class = BinarySensorDeviceClass.CONNECTIVITY @@ -59,14 +59,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagRadioCommunicationValid(PowerTagEntity, BinarySensorEntity): +class PowerTagRadioCommunicationValid(WirelessDeviceEntity, BinarySensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_device_class = BinarySensorDeviceClass.CONNECTIVITY @@ -80,21 +81,36 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagAlarmValid(PowerTagEntity, BinarySensorEntity): +class PowerTagAlarm(WirelessDeviceEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.PROBLEM def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): - super().__init__(client, modbus_index, tag_device, "alarm valid") + super().__init__(client, modbus_index, tag_device, "alarm info") async def async_update(self): - self._attr_is_on = not self._client.tag_is_alarm_valid(self._modbus_index) + alarm = self._client.tag_get_alarm(self._modbus_index) + + if alarm.has_alarm != self._attr_is_on: + self._attr_is_on = alarm.has_alarm + self._attr_extra_state_attributes = { + "Voltage loss": alarm.voltage_loss, + "Current overload when voltage loss": alarm.current_overload_when_voltage_loss, + "Current short-circuit": alarm.current_short_circuit, + "Overload 45%": alarm.current_overload_45_percent, + "Load current loss": alarm.load_current_loss, + "Overvoltage 120%": alarm.overvoltage_120_percent, + "Undervoltage 80%": alarm.undervoltage_80_percent, + "Current 50%": alarm.current_50_percent, + "Current 80%": alarm.current_80_percent + } @staticmethod def supports_feature_set(feature_class: FeatureClass) -> bool: @@ -107,42 +123,24 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagGetAlarm(PowerTagEntity, BinarySensorEntity): +class AmbientTagAlarm(WirelessDeviceEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.PROBLEM def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): - super().__init__(client, modbus_index, tag_device, "alarm info") + super().__init__(client, modbus_index, tag_device, "battery") self.__product_range = self._client.tag_product_range(self._modbus_index) async def async_update(self): alarm = self._client.tag_get_alarm(self._modbus_index) self._attr_is_on = alarm.has_alarm - if self.__product_range == "PowerTag": - self._attr_extra_state_attributes = { - "Voltage loss": alarm.alarm_voltage_loss, - "Overcurrent at voltage loss": alarm.alarm_current_overload, - "Overload 45%": alarm.alarm_overload_45_percent, - "Loadcurrent loss": alarm.alarm_load_current_loss, - "Overvoltage 120%": alarm.alarm_overvoltage, - "Undervoltage 80%": alarm.alarm_undervoltage - } - elif self.__product_range == "HeatTag": - self._attr_extra_state_attributes = { - "Alarm": alarm.alarm_heattag_alarm, - "Preventive maintenance on device": alarm.alarm_heattag_maintenance, - "Device replacement": alarm.alarm_heattag_replacement - } - @staticmethod def supports_feature_set(feature_class: FeatureClass) -> bool: - return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, - FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + return feature_class in [FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): - return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] + return type_of_gateway in [TypeOfGateway.PANEL_SERVER] class GatewayStatus(GatewayEntity, BinarySensorEntity): diff --git a/custom_components/powertag_gateway/button.py b/custom_components/powertag_gateway/button.py index 35cf201..d0c36ae 100644 --- a/custom_components/powertag_gateway/button.py +++ b/custom_components/powertag_gateway/button.py @@ -6,14 +6,14 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .entity_base import PowerTagEntity, setup_entities -from .powertag_features import FeatureClass +from .entity_base import WirelessDeviceEntity, setup_entities +from .device_features import FeatureClass from .schneider_modbus import SchneiderModbus, TypeOfGateway _LOGGER = logging.getLogger(__name__) -def list_buttons() -> list[type[PowerTagEntity]]: +def list_buttons() -> list[type[WirelessDeviceEntity]]: return [ PowerTagResetPeakDemand, PowerTagResetActiveEnergy, @@ -34,7 +34,7 @@ async def async_setup_entry( async_add_entities(entities, update_before_add=False) -class PowerTagResetPeakDemand(PowerTagEntity, ButtonEntity): +class PowerTagResetPeakDemand(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset peak demand") @@ -58,7 +58,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagResetActiveEnergy(PowerTagEntity, ButtonEntity): +class PowerTagResetActiveEnergy(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset active energy") @@ -80,7 +80,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagResetActiveEnergyDelivered(PowerTagEntity, ButtonEntity): +class PowerTagResetActiveEnergyDelivered(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset active energy delivered") @@ -102,7 +102,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagResetActiveEnergyReceived(PowerTagEntity, ButtonEntity): +class PowerTagResetActiveEnergyReceived(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset active energy received") @@ -124,7 +124,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagResetReactiveEnergyDelivered(PowerTagEntity, ButtonEntity): +class PowerTagResetReactiveEnergyDelivered(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset reactive energy delivered") @@ -148,7 +148,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagResetReactiveEnergyReceived(PowerTagEntity, ButtonEntity): +class PowerTagResetReactiveEnergyReceived(WirelessDeviceEntity, ButtonEntity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): super().__init__(client, modbus_index, tag_device, "reset reactive energy received") diff --git a/custom_components/powertag_gateway/powertag_features.py b/custom_components/powertag_gateway/device_features.py similarity index 90% rename from custom_components/powertag_gateway/powertag_features.py rename to custom_components/powertag_gateway/device_features.py index 59a9138..b24b401 100644 --- a/custom_components/powertag_gateway/powertag_features.py +++ b/custom_components/powertag_gateway/device_features.py @@ -18,6 +18,9 @@ class FeatureClass(Enum): M3 = auto() R1 = auto() C = auto() + TEMP0 = auto + TEMP1 = auto() + CO2 = auto() class UnknownDevice(IntegrationError): @@ -38,7 +41,10 @@ def from_commercial_reference(commercial_reference: str) -> FeatureClass: '^LV434022$': FeatureClass.M2, '^LV434023$': FeatureClass.M3, '^A9MEM1590|A9MEM1591|A9MEM1592|A9MEM1593|PLTR.$': FeatureClass.R1, - '^A9TAA....|A9TAB....|A9TDEC...|A9TDFC...|A9TDFD...|A9TPDD...|A9TPED...|A9TYAE...|A9TYBE...$': FeatureClass.C + '^A9TAA....|A9TAB....|A9TDEC...|A9TDFC...|A9TDFD...|A9TPDD...|A9TPED...|A9TYAE...|A9TYBE...$': FeatureClass.C, + '^EMS59440$': FeatureClass.TEMP0, + '^SED-TRH-G-5045|ZBRTT1|ESST010B0400|A9XST114|EMS59443$': FeatureClass.TEMP1, + '^SED-CO2-G-5045$': FeatureClass.CO2 }.items(): if re.match(regex, commercial_reference): return result diff --git a/custom_components/powertag_gateway/entity_base.py b/custom_components/powertag_gateway/entity_base.py index f86ddd2..4ddabdf 100644 --- a/custom_components/powertag_gateway/entity_base.py +++ b/custom_components/powertag_gateway/entity_base.py @@ -8,22 +8,23 @@ from .const import CONF_CLIENT, DOMAIN from .const import GATEWAY_DOMAIN, TAG_DOMAIN -from .powertag_features import FeatureClass, from_commercial_reference, UnknownDevice, from_wireless_device_type_code +from .device_features import FeatureClass, from_commercial_reference, UnknownDevice, from_wireless_device_type_code from .schneider_modbus import SchneiderModbus, Phase, LineVoltage, PhaseSequence, TypeOfGateway _LOGGER = logging.getLogger(__name__) def gateway_device_info(client: SchneiderModbus, presentation_url: str) -> DeviceInfo: + serial = client.serial_number() return DeviceInfo( configuration_url=presentation_url, - identifiers={(GATEWAY_DOMAIN, client.serial_number())}, + identifiers={(GATEWAY_DOMAIN, serial)}, hw_version=client.hardware_version(), sw_version=client.firmware_version(), manufacturer=client.manufacturer(), model=client.product_code(), name=client.name(), - serial_number=client.serial_number() + serial_number=serial ) @@ -113,7 +114,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): raise NotImplementedError() -class PowerTagEntity(Entity): +class WirelessDeviceEntity(Entity): def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo, entity_name: str): self._client = client self._modbus_index = modbus_index @@ -134,7 +135,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): def collect_entities(client: SchneiderModbus, entities: list[Entity], feature_class: FeatureClass, modbus_address: int, - powertag_entity: type[PowerTagEntity], tag_device: DeviceInfo, tag_phase_sequence: PhaseSequence): + powertag_entity: type[WirelessDeviceEntity], tag_device: DeviceInfo, tag_phase_sequence: PhaseSequence): params_raw = inspect.signature(powertag_entity.__init__).parameters params = [name for name in params_raw.items() if name[0] != "self" and name[0] != "kwargs"] args = [] @@ -171,7 +172,7 @@ def collect_entities(client: SchneiderModbus, entities: list[Entity], feature_cl entities.append(powertag_entity(*args)) -def setup_entities(hass: HomeAssistant, config_entry: ConfigEntry, powertag_entities: list[type[PowerTagEntity]]): +def setup_entities(hass: HomeAssistant, config_entry: ConfigEntry, powertag_entities: list[type[WirelessDeviceEntity]]): data = hass.data[DOMAIN][config_entry.entry_id] client = data[CONF_CLIENT] presentation_url = data[CONF_INTERNAL_URL] diff --git a/custom_components/powertag_gateway/manifest.json b/custom_components/powertag_gateway/manifest.json index 63e7350..cee8b7c 100644 --- a/custom_components/powertag_gateway/manifest.json +++ b/custom_components/powertag_gateway/manifest.json @@ -7,5 +7,5 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/Breina/PowerTagGateway/issues", "requirements": ["pymodbus==3.5.4"], - "version": "0.1.1" + "version": "0.4.0" } diff --git a/custom_components/powertag_gateway/schneider_modbus.py b/custom_components/powertag_gateway/schneider_modbus.py index a3b1b26..4969b8a 100644 --- a/custom_components/powertag_gateway/schneider_modbus.py +++ b/custom_components/powertag_gateway/schneider_modbus.py @@ -45,21 +45,24 @@ class PanelHealth(enum.Enum): OUT_OF_ORDER = 2 -class AlarmStatus: +class AlarmDetails: def __init__(self, bitmask: int): - lower_mask = bitmask & 0b1111_1111 - - self.has_alarm = lower_mask != 0 - self.alarm_voltage_loss = (lower_mask & 0b0000_0001) != 0 - self.alarm_current_overload = (lower_mask & 0b0000_0010) != 0 - # self.alarm_reserved = (lower_mask & 0b0000_0100) != 0 - self.alarm_overload_45_percent = (lower_mask & 0b0000_1000) != 0 - self.alarm_load_current_loss = (lower_mask & 0b0001_0000) != 0 - self.alarm_overvoltage = (lower_mask & 0b0010_0000) != 0 - self.alarm_undervoltage = (lower_mask & 0b0100_0000) != 0 - self.alarm_heattag_alarm = (lower_mask & 0b0001_0000_0000) != 0 - self.alarm_heattag_maintenance = (lower_mask & 0b0100_0000_0000) != 0 - self.alarm_heattag_replacement = (lower_mask & 0b1000_0000_0000) != 0 + self.has_alarm = bitmask != 0 + + self.voltage_loss = 0b1 & bitmask != 0 + self.current_overload_when_voltage_loss = 0b1 << 1 & bitmask != 0 + self.current_short_circuit = 0b1 << 2 & bitmask != 0 + self.current_overload_45_percent = 0b1 << 3 & bitmask != 0 + self.load_current_loss = 0b1 << 4 & bitmask != 0 + self.overvoltage_120_percent = 0b1 << 5 & bitmask != 0 + self.undervoltage_80_percent = 0b1 << 6 & bitmask != 0 + self.battery_almost_low = 0b1 << 7 & bitmask != 0 + self.heat = 0b1 << 8 & bitmask != 0 + self.battery_low = 0b1 << 9 & bitmask != 0 + self.preventive_maintenance = 0b1 << 10 & bitmask != 0 + self.device_replacement = 0b1 << 11 & bitmask != 0 + self.current_50_percent = 0b1 << 12 & bitmask != 0 + self.current_80_percent = 0b1 << 13 & bitmask != 0 class DeviceUsage(enum.Enum): @@ -249,298 +252,301 @@ def date_time(self) -> datetime | None: # Current Metering Data - def tag_current(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_current(self, tag_index: int, phase: Phase) -> float | None: """RMS current on phase""" - return self.__read_float_32(0xBB7 + phase.value, power_tag_index) + return self.__read_float_32(0xBB7 + phase.value, tag_index) - def tag_current_neutral(self, power_tag_index: int) -> float | None: + def tag_current_neutral(self, tag_index: int) -> float | None: """RMS current on Neutral""" - return self.__read_float_32(0xBBD, power_tag_index) + return self.__read_float_32(0xBBD, tag_index) # Voltage Metering Data - def tag_voltage(self, power_tag_index: int, line_voltage: LineVoltage) -> float | None: + def tag_voltage(self, tag_index: int, line_voltage: LineVoltage) -> float | None: """RMS phase-to-phase voltage""" - return self.__read_float_32(0xBCB + line_voltage.value, power_tag_index) + return self.__read_float_32(0xBCB + line_voltage.value, tag_index) # Power Metering Data - def tag_power_active(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_power_active(self, tag_index: int, phase: Phase) -> float | None: """Active power on phase""" - return self.__read_float_32(0xBED + phase.value, power_tag_index) + return self.__read_float_32(0xBED + phase.value, tag_index) - def tag_power_active_total(self, power_tag_index: int) -> float | None: + def tag_power_active_total(self, tag_index: int) -> float | None: """Total active power""" - return self.__read_float_32(0xBF3, power_tag_index) + return self.__read_float_32(0xBF3, tag_index) - def tag_power_reactive(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_power_reactive(self, tag_index: int, phase: Phase) -> float | None: """Reactive power on phase""" - return self.__read_float_32(0xBF5 + phase.value, power_tag_index) + return self.__read_float_32(0xBF5 + phase.value, tag_index) - def tag_power_reactive_total(self, power_tag_index: int) -> float | None: + def tag_power_reactive_total(self, tag_index: int) -> float | None: """Total reactive power""" - return self.__read_float_32(0xBFB, power_tag_index) + return self.__read_float_32(0xBFB, tag_index) - def tag_power_apparent(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_power_apparent(self, tag_index: int, phase: Phase) -> float | None: """Apparent power on phase""" - return self.__read_float_32(0xBFD + phase.value, power_tag_index) + return self.__read_float_32(0xBFD + phase.value, tag_index) - def tag_power_apparent_total(self, power_tag_index: int) -> float | None: + def tag_power_apparent_total(self, tag_index: int) -> float | None: """Total apparent power (arithmetric)""" - return self.__read_float_32(0xC03, power_tag_index) + return self.__read_float_32(0xC03, tag_index) # Power Factor Metering Data - def tag_power_factor(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_power_factor(self, tag_index: int, phase: Phase) -> float | None: """Power factor on phase""" - return self.__read_float_32(0xC05 + phase.value, power_tag_index) + return self.__read_float_32(0xC05 + phase.value, tag_index) - def tag_power_factor_total(self, power_tag_index: int) -> float | None: + def tag_power_factor_total(self, tag_index: int) -> float | None: """Total power factor""" - return self.__read_float_32(0xC0B, power_tag_index) + return self.__read_float_32(0xC0B, tag_index) - def tag_power_factor_sign_convention(self, power_tag_index: int) -> PowerFactorSignConvention: + def tag_power_factor_sign_convention(self, tag_index: int) -> PowerFactorSignConvention: """Power factor sign convention""" - return PowerFactorSignConvention(self.__read_int_16(0xC0D, power_tag_index)) + return PowerFactorSignConvention(self.__read_int_16(0xC0D, tag_index)) # Frequency Metering Data - def tag_ac_frequency(self, power_tag_index: int) -> float | None: + def tag_ac_frequency(self, tag_index: int) -> float | None: """AC frequency""" - return self.__read_float_32(0xC25, power_tag_index) + return self.__read_float_32(0xC25, tag_index) # Device Temperature Metering Data - def tag_device_temperature(self, power_tag_index: int) -> float | None: + def tag_device_temperature(self, tag_index: int) -> float | None: """Device internal temperature""" - return self.__read_float_32(0xC3B, power_tag_index) + return self.__read_float_32(0xC3B, tag_index) # Energy Data – Legacy Zone - def tag_energy_active_delivered_plus_received_total(self, power_tag_index: int) -> int | None: + def tag_energy_active_delivered_plus_received_total(self, tag_index: int) -> int | None: """Total active energy delivered + received (not resettable)""" - return self.__read_int_64(0xC83, power_tag_index) + return self.__read_int_64(0xC83, tag_index) - def tag_energy_active_delta(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_active_delta(self, tag_index: int, phase: Phase) -> int | None: """Active energy on phase delivered - received (not resettable)""" - return self.__read_int_64(0xC8F + phase.value * 2, power_tag_index) + return self.__read_int_64(0xC8F + phase.value * 2, tag_index) - def tag_energy_active_delivered_plus_received_partial(self, power_tag_index: int) -> int | None: + def tag_energy_active_delivered_plus_received_partial(self, tag_index: int) -> int | None: """Partial active energy delivered + received (resettable)""" - return self.__read_int_64(0xCB7, power_tag_index) + return self.__read_int_64(0xCB7, tag_index) - def tag_reset_energy_active_partial(self, power_tag_index: int): + def tag_reset_energy_active_partial(self, tag_index: int): """Set partial active energy counter. The value returns to zero by PowerTag Link gateway""" - self.__write_int_64(0xCBB, power_tag_index, 1) + self.__write_int_64(0xCBB, tag_index, 1) - def tag_reset_energy_active_delivered_partial(self, power_tag_index: int): + def tag_reset_energy_active_delivered_partial(self, tag_index: int): """Set partial active energy delivered counter. The value returns to zero by PowerTag Link gateway""" - self.__write_int_64(0xCC3, power_tag_index, 1) + self.__write_int_64(0xCC3, tag_index, 1) - def tag_reset_energy_active_received_partial(self, power_tag_index: int): + def tag_reset_energy_active_received_partial(self, tag_index: int): """Set partial active energy received counter. The value returns to zero by PowerTag Link gateway.""" - self.__write_int_64(0xCCB, power_tag_index, 1) + self.__write_int_64(0xCCB, tag_index, 1) - def tag_reset_energy_reactive_delivered_partial(self, power_tag_index: int): + def tag_reset_energy_reactive_delivered_partial(self, tag_index: int): """Set partial reactive energy delivered counter. The value returns to zero by PowerTag Link gateway.""" - self.__write_int_64(0xCD3, power_tag_index, 1) + self.__write_int_64(0xCD3, tag_index, 1) - def tag_reset_energy_reactive_received_partial(self, power_tag_index: int): + def tag_reset_energy_reactive_received_partial(self, tag_index: int): """Set partial reactive energy received counter. The value returns to zero by PowerTag Link gateway.""" - self.__write_int_64(0xCDB, power_tag_index, 1) + self.__write_int_64(0xCDB, tag_index, 1) # Energy Data – New Zone - def tag_energy_active_delivered_partial(self, power_tag_index: int) -> int | None: + def tag_energy_active_delivered_partial(self, tag_index: int) -> int | None: """Active energy delivered (resettable)""" - return self.__read_int_64(0x1390, power_tag_index) + return self.__read_int_64(0x1390, tag_index) - def tag_energy_active_delivered_total(self, power_tag_index: int) -> int | None: + def tag_energy_active_delivered_total(self, tag_index: int) -> int | None: """Active energy delivered count positively (not resettable)""" - return self.__read_int_64(0x1394, power_tag_index) + return self.__read_int_64(0x1394, tag_index) - def tag_energy_active_received_partial(self, power_tag_index: int) -> int | None: + def tag_energy_active_received_partial(self, tag_index: int) -> int | None: """Active energy received (resettable)""" - return self.__read_int_64(0x1398, power_tag_index) + return self.__read_int_64(0x1398, tag_index) - def tag_energy_active_received_total(self, power_tag_index: int) -> int | None: + def tag_energy_active_received_total(self, tag_index: int) -> int | None: """Active energy received count negatively (not resettable)""" - return self.__read_int_64(0x139C, power_tag_index) + return self.__read_int_64(0x139C, tag_index) - def tag_energy_active_delivered_partial_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_active_delivered_partial_phase(self, tag_index: int, phase: Phase) -> int | None: """Active energy on phase delivered (resettable)""" - return self.__read_int_64(0x13B8 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x13B8 + phase.value * 0x14, tag_index) - def tag_energy_active_delivered_total_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_active_delivered_total_phase(self, tag_index: int, phase: Phase) -> int | None: """Active energy on phase delivered (not resettable)""" - return self.__read_int_64(0x13BC + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x13BC + phase.value * 0x14, tag_index) - def tag_energy_active_received_partial_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_active_received_partial_phase(self, tag_index: int, phase: Phase) -> int | None: """Active energy on phase received (resettable)""" - return self.__read_int_64(0x13C0 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x13C0 + phase.value * 0x14, tag_index) - def tag_energy_active_received_total_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_active_received_total_phase(self, tag_index: int, phase: Phase) -> int | None: """Active energy on phase received (not resettable)""" - return self.__read_int_64(0x13C4 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x13C4 + phase.value * 0x14, tag_index) - def tag_energy_reactive_delivered_partial(self, power_tag_index: int) -> int | None: + def tag_energy_reactive_delivered_partial(self, tag_index: int) -> int | None: """Reactive energy delivered (resettable)""" - return self.__read_int_64(0x1438, power_tag_index) + return self.__read_int_64(0x1438, tag_index) - def tag_energy_reactive_delivered_total(self, power_tag_index: int) -> int | None: + def tag_energy_reactive_delivered_total(self, tag_index: int) -> int | None: """Reactive energy delivered count positively (not resettable)""" - return self.__read_int_64(0x143C, power_tag_index) + return self.__read_int_64(0x143C, tag_index) - def tag_energy_reactive_received_partial(self, power_tag_index: int) -> int | None: + def tag_energy_reactive_received_partial(self, tag_index: int) -> int | None: """Reactive energy received (resettable)""" - return self.__read_int_64(0x1488, power_tag_index) + return self.__read_int_64(0x1488, tag_index) - def tag_energy_reactive_received_total(self, power_tag_index: int) -> int | None: + def tag_energy_reactive_received_total(self, tag_index: int) -> int | None: """Reactive energy received count negatively (not resettable)""" - return self.__read_int_64(0x144C, power_tag_index) + return self.__read_int_64(0x144C, tag_index) - def tag_energy_reactive_delivered_partial_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_reactive_delivered_partial_phase(self, tag_index: int, phase: Phase) -> int | None: """Reactive energy on phase delivered (resettable)""" - return self.__read_int_64(0x1470 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x1470 + phase.value * 0x14, tag_index) - def tag_energy_reactive_delivered_total_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_reactive_delivered_total_phase(self, tag_index: int, phase: Phase) -> int | None: """Reactive energy on phase delivered (not resettable)""" - return self.__read_int_64(0x1474 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x1474 + phase.value * 0x14, tag_index) - def tag_energy_reactive_received_partial_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_reactive_received_partial_phase(self, tag_index: int, phase: Phase) -> int | None: """Reactive energy on phase received (resettable)""" - return self.__read_int_64(0x1478 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x1478 + phase.value * 0x14, tag_index) - def tag_energy_reactive_received_total_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_reactive_received_total_phase(self, tag_index: int, phase: Phase) -> int | None: """Reactive energy on phase received (not resettable)""" - return self.__read_int_64(0x147C + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x147C + phase.value * 0x14, tag_index) - def tag_energy_apparent_partial(self, power_tag_index: int) -> int | None: + def tag_energy_apparent_partial(self, tag_index: int) -> int | None: """Apparent energy delivered + received (resettable)""" - return self.__read_int_64(0x14F4, power_tag_index) + return self.__read_int_64(0x14F4, tag_index) - def tag_energy_apparent_total(self, power_tag_index: int) -> int | None: + def tag_energy_apparent_total(self, tag_index: int) -> int | None: """Apparent energy delivered + received (not resettable)""" - return self.__read_int_64(0x14F8, power_tag_index) + return self.__read_int_64(0x14F8, tag_index) - def tag_energy_apparent_partial_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_apparent_partial_phase(self, tag_index: int, phase: Phase) -> int | None: """Apparent energy on phase (resettable)""" - return self.__read_int_64(0x150C + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x150C + phase.value * 0x14, tag_index) - def tag_energy_apparent_total_phase(self, power_tag_index: int, phase: Phase) -> int | None: + def tag_energy_apparent_total_phase(self, tag_index: int, phase: Phase) -> int | None: """Apparent energy on phase A (not resettable)""" - return self.__read_int_64(0x1510 + phase.value * 0x14, power_tag_index) + return self.__read_int_64(0x1510 + phase.value * 0x14, tag_index) # Power Demand Data - def tag_power_active_demand_total(self, power_tag_index: int) -> float | None: + def tag_power_active_demand_total(self, tag_index: int) -> float | None: """Demand total active power""" - return self.__read_float_32(0x0EB5, power_tag_index) + return self.__read_float_32(0x0EB5, tag_index) - def tag_power_active_power_demand_total_maximum(self, power_tag_index: int) -> float | None: + def tag_power_active_power_demand_total_maximum(self, tag_index: int) -> float | None: """Maximum Demand total active power""" - return self.__read_float_32(0x0EB9, power_tag_index) + return self.__read_float_32(0x0EB9, tag_index) - def tag_power_active_demand_total_maximum_timestamp(self, power_tag_index: int) -> datetime | None: + def tag_power_active_demand_total_maximum_timestamp(self, tag_index: int) -> datetime | None: """Maximum Demand total active power""" - return self.__read_date_time(0x0EBB, power_tag_index) + return self.__read_date_time(0x0EBB, tag_index) # Alarm - def tag_is_alarm_valid(self, power_tag_index: int) -> bool | None: + def tag_is_alarm_valid(self, tag_index: int) -> AlarmDetails | bool | None: """Validity of the alarm bitmap""" - return (self.__read_int_16(0xCE1, power_tag_index) & 0b1) != 0 + if self.type_of_gateway is TypeOfGateway.PANEL_SERVER: + return AlarmDetails(self.__read_int_16(0xCE1, tag_index)) + else: + return (self.__read_int_16(0xCE1, tag_index) & 0b1) != 0 - def tag_get_alarm(self, power_tag_index: int) -> AlarmStatus: + def tag_get_alarm(self, tag_index: int) -> AlarmDetails: """Alarms""" - return AlarmStatus(self.__read_int_16(0xCE3, power_tag_index)) + return AlarmDetails(self.__read_int_16(0xCE3, tag_index)) - def tag_current_at_voltage_loss(self, power_tag_index: int, phase: Phase) -> float | None: + def tag_current_at_voltage_loss(self, tag_index: int, phase: Phase) -> float | None: """RMS current on phase at voltage loss (last RMS current measured when voltage loss occurred)""" - return self.__read_float_32(0xCE5 + phase.value, power_tag_index) + return self.__read_float_32(0xCE5 + phase.value, tag_index) # Load Operating Time - def tag_load_operating_time(self, power_tag_index: int) -> int | None: + def tag_load_operating_time(self, tag_index: int) -> int | None: """Load operating time counter.""" - return self.__read_int_32(0xCEB, power_tag_index) + return self.__read_int_32(0xCEB, tag_index) - def tag_load_operating_time_active_power_threshold(self, power_tag_index: int) -> float | None: + def tag_load_operating_time_active_power_threshold(self, tag_index: int) -> float | None: """Active power threshold for Load operating time counter. Counter starts above the threshold value.""" - return self.__read_float_32(0xCED, power_tag_index) + return self.__read_float_32(0xCED, tag_index) - def tag_load_operating_time_start(self, power_tag_index: int) -> datetime | None: + def tag_load_operating_time_start(self, tag_index: int) -> datetime | None: """Date and time stamp of last Set or reset of Load operating time counter.""" - return self.__read_date_time(0xCEF, power_tag_index) + return self.__read_date_time(0xCEF, tag_index) # Configuration Registers - def tag_name(self, power_tag_index: int) -> str | None: + def tag_name(self, tag_index: int) -> str | None: """User application name of the wireless device. The user can enter maximum 20 characters.""" - return self.__read_string(0x7918, 10, power_tag_index, 20) + return self.__read_string(0x7918, 10, tag_index, 20) - def tag_circuit(self, power_tag_index: int) -> str: + def tag_circuit(self, tag_index: int) -> str: """Circuit identifier of the wireless device. The user can enter maximum five characters.""" - return self.__read_string(0x7922, 3, power_tag_index, 5) + return self.__read_string(0x7922, 3, tag_index, 5) - def tag_usage(self, power_tag_index: int) -> DeviceUsage: + def tag_usage(self, tag_index: int) -> DeviceUsage: """Indicates the usage of the wireless device.""" - return DeviceUsage(self.__read_int_16(0x7925, power_tag_index)) + return DeviceUsage(self.__read_int_16(0x7925, tag_index)) - def tag_phase_sequence(self, power_tag_index: int) -> PhaseSequence: + def tag_phase_sequence(self, tag_index: int) -> PhaseSequence: """Phase sequence.""" - return PhaseSequence(self.__read_int_16(0x7926, power_tag_index)) + return PhaseSequence(self.__read_int_16(0x7926, tag_index)) - def tag_position(self, power_tag_index: int) -> Position: + def tag_position(self, tag_index: int) -> Position: """Mounting position""" - return Position(self.__read_int_16(0x7927, power_tag_index)) + return Position(self.__read_int_16(0x7927, tag_index)) - def tag_circuit_diagnostic(self, power_tag_index: int) -> Position: + def tag_circuit_diagnostic(self, tag_index: int) -> Position: """Circuit diagnostics""" - return Position(self.__read_int_16(0x7928, power_tag_index)) + return Position(self.__read_int_16(0x7928, tag_index)) - def tag_rated_current(self, power_tag_index: int) -> int | None: + def tag_rated_current(self, tag_index: int) -> int | None: """Rated current of the protective device to the wireless device""" - return self.__read_int_16(0x7929, power_tag_index) + return self.__read_int_16(0x7929, tag_index) - def tag_electrical_network_system_type(self, power_tag_index: int) -> ElectricalNetworkSystemType: - code = self.__read_int_16(0x792A, power_tag_index) + def tag_electrical_network_system_type(self, tag_index: int) -> ElectricalNetworkSystemType: + code = self.__read_int_16(0x792A, tag_index) system_type = [e for e in ElectricalNetworkSystemType if e.value[0] == code] return system_type[0] if system_type else None - def tag_rated_voltage(self, power_tag_index: int) -> float | None: + def tag_rated_voltage(self, tag_index: int) -> float | None: """Rated voltage""" if self.type_of_gateway == TypeOfGateway.SMARTLINK: return None - return self.__read_float_32(0x792B, power_tag_index) + return self.__read_float_32(0x792B, tag_index) - def tag_reset_peak_demands(self, power_tag_index: int): + def tag_reset_peak_demands(self, tag_index: int): """Reset All Peak Demands""" - self.__write_int_16(0x792E, power_tag_index, 1) + self.__write_int_16(0x792E, tag_index, 1) - def tag_power_supply_type(self, power_tag_index: int) -> Position: + def tag_power_supply_type(self, tag_index: int) -> Position: """Power supply type""" if self.type_of_gateway == TypeOfGateway.SMARTLINK: return Position.INVALID - return Position(self.__read_int_16(0x792F, power_tag_index)) + return Position(self.__read_int_16(0x792F, tag_index)) # Device identification - def tag_device_identification(self, power_tag_index: int): - return self.__identify(power_tag_index) + def tag_device_identification(self, tag_index: int): + return self.__identify(tag_index) - def tag_product_identifier(self, power_tag_index: int) -> int | None: + def tag_product_identifier(self, tag_index: int) -> int | None: """Wireless device code type""" if self.type_of_gateway == TypeOfGateway.SMARTLINK: - return self.__read_int_16(0x7930, power_tag_index) + return self.__read_int_16(0x7930, tag_index) raise NotImplementedError() - def tag_product_type(self, power_tag_index: int) -> ProductType | None: + def tag_product_type(self, tag_index: int) -> ProductType | None: """Wireless device code type""" if self.type_of_gateway == TypeOfGateway.SMARTLINK: try: - identifier = self.__read_int_16(0x7930, power_tag_index) + identifier = self.__read_int_16(0x7930, tag_index) if not identifier: _LOGGER.error("The powertag returned an error while requesting its product type") return None @@ -554,13 +560,13 @@ def tag_product_type(self, power_tag_index: int) -> ProductType | None: except ConnectionError as e: _LOGGER.warning( - f"Could not read product type of device on slave ID {power_tag_index}: {str(e)}. " + f"Could not read product type of device on slave ID {tag_index}: {str(e)}. " f"Might be because there's device, or an actual error. Either way we're stopping the search.", exc_info=True ) return None else: - identifier = self.__read_int_16(0x7937, power_tag_index) + identifier = self.__read_int_16(0x7937, tag_index) product_type = [p for p in ProductType if p.value[1] == identifier] if not product_type: _LOGGER.warning( @@ -570,95 +576,128 @@ def tag_product_type(self, power_tag_index: int) -> ProductType | None: return product_type[0] if product_type else None - def tag_slave_address(self, power_tag_index: int) -> int | None: + def tag_slave_address(self, tag_index: int) -> int | None: """Virtual Modbus server address""" - return self.__read_int_16(0x7931, power_tag_index) + return self.__read_int_16(0x7931, tag_index) - def tag_rf_id(self, power_tag_index: int) -> int | None: + def tag_rf_id(self, tag_index: int) -> int | None: """Wireless device Radio Frequency Identifier""" - return self.__read_int_64(0x7932, power_tag_index) + return self.__read_int_64(0x7932, tag_index) - def tag_vendor_name(self, power_tag_index: int) -> str | None: + def tag_vendor_name(self, tag_index: int) -> str | None: """Vendor name""" - return self.__read_string(0x7944, 16, power_tag_index, 32) + return self.__read_string(0x7944, 16, tag_index, 32) - def tag_product_code(self, power_tag_index: int) -> str | None: + def tag_product_code(self, tag_index: int) -> str | None: """Wireless device commercial reference""" if self.type_of_gateway is TypeOfGateway.SMARTLINK: return None - return self.__read_string(0x7954, 16, power_tag_index, 32) + return self.__read_string(0x7954, 16, tag_index, 32) - def tag_firmware_revision(self, power_tag_index: int) -> str | None: + def tag_firmware_revision(self, tag_index: int) -> str | None: """Firmware revision""" - return self.__read_string(0x7964, 6, power_tag_index, 12) + return self.__read_string(0x7964, 6, tag_index, 12) - def tag_hardware_revision(self, power_tag_index: int) -> str | None: + def tag_hardware_revision(self, tag_index: int) -> str | None: """Hardware revision""" - return self.__read_string(0x796A, 6, power_tag_index, 12) + return self.__read_string(0x796A, 6, tag_index, 12) - def tag_serial_number(self, power_tag_index: int) -> str | None: + def tag_serial_number(self, tag_index: int) -> str | None: """Serial number""" - return self.__read_string(0x7970, 10, power_tag_index, 20) + return self.__read_string(0x7970, 10, tag_index, 20) - def tag_product_range(self, power_tag_index: int) -> str | None: + def tag_product_range(self, tag_index: int) -> str | None: """Product range""" - return self.__read_string(0x797A, 8, power_tag_index, 16) + return self.__read_string(0x797A, 8, tag_index, 16) - def tag_product_model(self, power_tag_index: int) -> str | None: + def tag_product_model(self, tag_index: int) -> str | None: """Product model""" - return self.__read_string(0x7982, 8, power_tag_index, 16) + return self.__read_string(0x7982, 8, tag_index, 16) - def tag_product_family(self, power_tag_index: int) -> str | None: + def tag_product_family(self, tag_index: int) -> str | None: """Product family""" if self.type_of_gateway is TypeOfGateway.SMARTLINK: return None - return self.__read_string(0x798A, 8, power_tag_index, 16) + return self.__read_string(0x798A, 8, tag_index, 16) # Diagnostic Data Registers - def tag_radio_communication_valid(self, power_tag_index: int) -> bool: + def tag_radio_communication_valid(self, tag_index: int) -> bool: """Validity of the RF communication between PowerTag system and PowerTag Link gateway status.""" - return self.__read_int_16(0x79A8, power_tag_index) != 0 + return self.__read_int_16(0x79A8, tag_index) != 0 - def tag_wireless_communication_valid(self, power_tag_index: int) -> bool: + def tag_wireless_communication_valid(self, tag_index: int) -> bool: """Communication status between PowerTag Link gateway and wireless devices.""" - return self.__read_int_16(0x79A9, power_tag_index) != 0 + return self.__read_int_16(0x79A9, tag_index) != 0 - def tag_radio_per_tag(self, power_tag_index: int) -> float | None: + def tag_radio_per_tag(self, tag_index: int) -> float | None: """Packet Error Rate (PER) of the device, received by PowerTag Link gateway""" - return self.__read_float_32(0x79B4, power_tag_index) + return self.__read_float_32(0x79B4, tag_index) - def tag_radio_rssi_inside_tag(self, power_tag_index: int) -> float | None: + def tag_radio_rssi_inside_tag(self, tag_index: int) -> float | None: """RSSI of the device, received by PowerTag Link gateway""" - return self.__read_float_32(0x79B6, power_tag_index) + return self.__read_float_32(0x79B6, tag_index) - def tag_radio_lqi_tag(self, power_tag_index: int) -> int | None: + def tag_radio_lqi_tag(self, tag_index: int) -> int | None: """Link Quality Indicator (LQI) of the device, received by PowerTag Link gateway""" - return self.__read_int_16(0x79B8, power_tag_index) + return self.__read_int_16(0x79B8, tag_index) - def tag_radio_per_gateway(self, power_tag_index: int) -> float | None: + def tag_radio_per_gateway(self, tag_index: int) -> float | None: """PER of gateway, calculated inside the PowerTag Link gateway""" - return self.__read_float_32(0x79AF, power_tag_index) + return self.__read_float_32(0x79AF, tag_index) - def tag_radio_rssi_inside_gateway(self, power_tag_index: int) -> float | None: + def tag_radio_rssi_inside_gateway(self, tag_index: int) -> float | None: """Radio Signal Strength Indicator (RSSI) of gateway, calculated inside the PowerTag Link gateway""" - return self.__read_float_32(0x79B1, power_tag_index) + return self.__read_float_32(0x79B1, tag_index) - def tag_radio_lqi_gateway(self, power_tag_index: int) -> float | None: + def tag_radio_lqi_gateway(self, tag_index: int) -> float | None: """LQI of gateway, calculated insider the PowerTag Link gateway""" - return self.__read_int_16(0x79B3, power_tag_index) + return self.__read_int_16(0x79B3, tag_index) - def tag_radio_per_maximum(self, power_tag_index: int) -> float | None: + def tag_radio_per_maximum(self, tag_index: int) -> float | None: """PER–Maximum value between device and gateway""" - return self.__read_float_32(0x79B4, power_tag_index) + return self.__read_float_32(0x79B4, tag_index) - def tag_radio_rssi_minimum(self, power_tag_index: int) -> float | None: + def tag_radio_rssi_minimum(self, tag_index: int) -> float | None: """RSSI–Minimal value between device and gateway""" - return self.__read_float_32(0x79B6, power_tag_index) + return self.__read_float_32(0x79B6, tag_index) - def tag_radio_lqi_minimum(self, power_tag_index: int) -> float | None: + def tag_radio_lqi_minimum(self, tag_index: int) -> float | None: """LQI–Minimal value between device and gateway""" - return self.__read_int_16(0x79B8, power_tag_index) + return self.__read_int_16(0x79B8, tag_index) + + # Environmental sensors + def env_battery_voltage(self, tag_index: int) -> float | None: + """Battery voltage""" + return self.__read_float_32(0x0CF3, tag_index) + + def env_temperature(self, tag_index: int) -> float | None: + """Temperature value""" + return self.__read_float_32(0x0FA0, tag_index) + + def env_temperature_maximum(self, tag_index: int) -> float | None: + """Maximum value that the device is able to read (maximum measurable temperature).""" + return self.__read_float_32(0x0FA2, tag_index) + + def env_temperature_minimum(self, tag_index: int) -> float | None: + """Minimum value that the device is able to read (minimum measurable temperature).""" + return self.__read_float_32(0x0FA4, tag_index) + + def env_humidity(self, tag_index: int) -> float | None: + """"Relative humidity value Example: 50% represented as 0.50""" + return self.__read_float_32(0x0FA6, tag_index) + + def env_humidity_maximum(self, tag_index: int) -> float | None: + """Maximum value that the device is able to read (maximum measurable humidity).""" + return self.__read_float_32(0x0FA8, tag_index) + + def env_humidity_minimum(self, tag_index: int) -> float | None: + """Minimum value that the device is able to read (minimum measurable humidity).""" + return self.__read_float_32(0x0FAA, tag_index) + + def env_co2(self, tag_index: int) -> float | None: + """CO2 (Example:5000 ppm represented as 0,005) """ + return self.__read_float_32(0x0FAE, tag_index) # Identification and Status Register diff --git a/custom_components/powertag_gateway/sensor.py b/custom_components/powertag_gateway/sensor.py index 6c747bc..2027b0f 100644 --- a/custom_components/powertag_gateway/sensor.py +++ b/custom_components/powertag_gateway/sensor.py @@ -9,8 +9,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import CONF_CLIENT, DOMAIN -from .entity_base import GatewayEntity, PowerTagEntity, setup_entities, gateway_device_info -from .powertag_features import FeatureClass +from .device_features import FeatureClass +from .entity_base import GatewayEntity, WirelessDeviceEntity, setup_entities, gateway_device_info from .schneider_modbus import SchneiderModbus, Phase, LineVoltage, PowerFactorSignConvention, \ TypeOfGateway @@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__) -def list_sensors() -> list[type[PowerTagEntity]]: +def list_sensors() -> list[type[WirelessDeviceEntity]]: return [ PowerTagTotalActiveEnergy, PowerTagTotalActiveEnergyPerPhase, @@ -60,12 +60,16 @@ def list_sensors() -> list[type[PowerTagEntity]]: PowerTagActivePower, PowerTagActivePowerPerPhase, PowerTagDemandActivePower, - PowerTagRssiTag, - PowerTagRssiGateway, - PowerTagLqiTag, - PowerTagLqiGateway, - PowerTagPerTag, - PowerTagPerGateway + EnvTagBatteryVoltage, + EnvTagTemperature, + EnvTagHumidity, + EnvTagCO2, + DeviceRssiTag, + DeviceRssiGateway, + DeviceLqiTag, + DeviceLqiGateway, + DevicePerTag, + DevicePerGateway, ] @@ -102,7 +106,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergy(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergy(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -124,7 +128,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -147,7 +151,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagReactivePower(PowerTagEntity, SensorEntity): +class PowerTagReactivePower(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.REACTIVE_POWER _attr_native_unit_of_measurement = "var" _attr_state_class = SensorStateClass.MEASUREMENT @@ -168,7 +172,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagReactivePowerPerPhase(PowerTagEntity, SensorEntity): +class PowerTagReactivePowerPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.REACTIVE_POWER _attr_native_unit_of_measurement = "var" _attr_state_class = SensorStateClass.MEASUREMENT @@ -189,7 +193,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagApparentPower(PowerTagEntity, SensorEntity): +class PowerTagApparentPower(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.APPARENT_POWER _attr_native_unit_of_measurement = "VA" _attr_state_class = SensorStateClass.MEASUREMENT @@ -211,7 +215,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagApparentPowerPerPhase(PowerTagEntity, SensorEntity): +class PowerTagApparentPowerPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.APPARENT_POWER _attr_native_unit_of_measurement = "VA" _attr_state_class = SensorStateClass.MEASUREMENT @@ -232,7 +236,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPowerFactor(PowerTagEntity, SensorEntity): +class PowerTagPowerFactor(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.POWER_FACTOR _attr_native_unit_of_measurement = "%" _attr_state_class = SensorStateClass.MEASUREMENT @@ -261,7 +265,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPowerFactorPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPowerFactorPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.POWER_FACTOR _attr_native_unit_of_measurement = "%" _attr_state_class = SensorStateClass.MEASUREMENT @@ -288,7 +292,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialActiveEnergyDelivered(PowerTagEntity, SensorEntity): +class PowerTagPartialActiveEnergyDelivered(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -311,7 +315,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyDelivered(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyDelivered(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -333,7 +337,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialActiveEnergyDeliveredPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPartialActiveEnergyDeliveredPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -357,7 +361,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyDeliveredPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyDeliveredPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -378,7 +382,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialActiveEnergyReceived(PowerTagEntity, SensorEntity): +class PowerTagPartialActiveEnergyReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -401,7 +405,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyReceived(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -423,7 +427,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialActiveEnergyReceivedPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPartialActiveEnergyReceivedPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -447,7 +451,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyReceivedPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyReceivedPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -468,7 +472,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialActiveEnergyDeliveredAndReceived(PowerTagEntity, SensorEntity): +class PowerTagPartialActiveEnergyDeliveredAndReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -490,7 +494,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalActiveEnergyDeliveredAndReceived(PowerTagEntity, SensorEntity): +class PowerTagTotalActiveEnergyDeliveredAndReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "Wh" _attr_state_class = SensorStateClass.TOTAL @@ -536,7 +540,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): # return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialReactiveEnergyDelivered(PowerTagEntity, SensorEntity): +class PowerTagPartialReactiveEnergyDelivered(WirelessDeviceEntity, SensorEntity): # TODO lobby for Reactive-energy _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" @@ -558,7 +562,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalReactiveEnergyDelivered(PowerTagEntity, SensorEntity): +class PowerTagTotalReactiveEnergyDelivered(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL @@ -578,7 +582,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialReactiveEnergyDeliveredPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPartialReactiveEnergyDeliveredPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -601,7 +605,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalReactiveEnergyDeliveredPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalReactiveEnergyDeliveredPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL @@ -624,7 +628,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialReactiveEnergyReceived(PowerTagEntity, SensorEntity): +class PowerTagPartialReactiveEnergyReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -645,7 +649,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalReactiveEnergyReceived(PowerTagEntity, SensorEntity): +class PowerTagTotalReactiveEnergyReceived(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL @@ -665,7 +669,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialReactiveEnergyReceivedPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPartialReactiveEnergyReceivedPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -688,7 +692,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalReactiveEnergyReceivedPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalReactiveEnergyReceivedPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VARh" _attr_state_class = SensorStateClass.TOTAL @@ -711,7 +715,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialApparentEnergy(PowerTagEntity, SensorEntity): +class PowerTagPartialApparentEnergy(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VAh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -731,7 +735,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalApparentEnergy(PowerTagEntity, SensorEntity): +class PowerTagTotalApparentEnergy(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VAh" _attr_state_class = SensorStateClass.TOTAL @@ -751,7 +755,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPartialApparentEnergyPerPhase(PowerTagEntity, SensorEntity): +class PowerTagPartialApparentEnergyPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VAh" _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -772,7 +776,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTotalApparentEnergyPerPhase(PowerTagEntity, SensorEntity): +class PowerTagTotalApparentEnergyPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_native_unit_of_measurement = "VAh" _attr_state_class = SensorStateClass.TOTAL @@ -793,7 +797,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagCurrent(PowerTagEntity, SensorEntity): +class PowerTagCurrent(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.CURRENT _attr_native_unit_of_measurement = "A" _attr_state_class = SensorStateClass.MEASUREMENT @@ -820,7 +824,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagCurrentNeutral(PowerTagEntity, SensorEntity): +class PowerTagCurrentNeutral(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.CURRENT _attr_native_unit_of_measurement = "A" _attr_state_class = SensorStateClass.MEASUREMENT @@ -844,7 +848,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagVoltage(PowerTagEntity, SensorEntity): +class PowerTagVoltage(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.VOLTAGE _attr_native_unit_of_measurement = "V" _attr_state_class = SensorStateClass.MEASUREMENT @@ -874,7 +878,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagFrequency(PowerTagEntity, SensorEntity): +class PowerTagFrequency(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.FREQUENCY _attr_native_unit_of_measurement = "Hz" _attr_state_class = SensorStateClass.MEASUREMENT @@ -895,7 +899,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagTemperature(PowerTagEntity, SensorEntity): +class PowerTagTemperature(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_native_unit_of_measurement = "C" _attr_state_class = SensorStateClass.MEASUREMENT @@ -921,7 +925,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagActivePower(PowerTagEntity, SensorEntity): +class PowerTagActivePower(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.POWER _attr_native_unit_of_measurement = "W" _attr_state_class = SensorStateClass.MEASUREMENT @@ -943,7 +947,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagActivePowerPerPhase(PowerTagEntity, SensorEntity): +class PowerTagActivePowerPerPhase(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.POWER _attr_native_unit_of_measurement = "W" _attr_state_class = SensorStateClass.MEASUREMENT @@ -966,7 +970,7 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagDemandActivePower(PowerTagEntity, SensorEntity): +class PowerTagDemandActivePower(WirelessDeviceEntity, SensorEntity): _attr_device_class = SensorDeviceClass.POWER _attr_native_unit_of_measurement = "W" _attr_state_class = SensorStateClass.MEASUREMENT @@ -992,7 +996,96 @@ def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.SMARTLINK, TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagRssiTag(PowerTagEntity, SensorEntity): +class EnvTagBatteryVoltage(WirelessDeviceEntity, SensorEntity): + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_device_class = SensorDeviceClass.VOLTAGE + _attr_native_unit_of_measurement = "V" + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): + super().__init__(client, modbus_index, tag_device, "battery voltage") + + async def async_update(self): + self._attr_native_value = self._client.env_battery_voltage(self._modbus_index) + + @staticmethod + def supports_feature_set(feature_class: FeatureClass) -> bool: + return feature_class in [FeatureClass.TEMP1, FeatureClass.CO2] + + @staticmethod + def supports_gateway(type_of_gateway: TypeOfGateway): + return type_of_gateway in [TypeOfGateway.PANEL_SERVER] + + +class EnvTagTemperature(WirelessDeviceEntity, SensorEntity): + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = "°C" + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): + super().__init__(client, modbus_index, tag_device, "temperature") + self._attr_extra_state_attributes = { + "Minimum measurable temperature (°C)": client.env_temperature_minimum(modbus_index), + "Maximum measurable temperature (°C)": client.env_temperature_maximum(modbus_index) + } + + async def async_update(self): + self._attr_native_value = self._client.env_temperature(self._modbus_index) + + @staticmethod + def supports_feature_set(feature_class: FeatureClass) -> bool: + return feature_class in [FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] + + @staticmethod + def supports_gateway(type_of_gateway: TypeOfGateway): + return type_of_gateway in [TypeOfGateway.PANEL_SERVER] + + +class EnvTagHumidity(WirelessDeviceEntity, SensorEntity): + _attr_device_class = SensorDeviceClass.HUMIDITY + _attr_native_unit_of_measurement = "%" + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): + super().__init__(client, modbus_index, tag_device, "humidity") + self._attr_extra_state_attributes = { + "Minimum measurable humidity (%)": client.env_humidity_minimum(modbus_index), + "Maximum measurable humidity (%)": client.env_humidity_maximum(modbus_index) + } + + async def async_update(self): + self._attr_native_value = self._client.env_humidity(self._modbus_index) + + @staticmethod + def supports_feature_set(feature_class: FeatureClass) -> bool: + return feature_class in [FeatureClass.TEMP1, FeatureClass.CO2] + + @staticmethod + def supports_gateway(type_of_gateway: TypeOfGateway): + return type_of_gateway in [TypeOfGateway.PANEL_SERVER] + + +class EnvTagCO2(WirelessDeviceEntity, SensorEntity): + _attr_device_class = SensorDeviceClass.CO2 + _attr_native_unit_of_measurement = "ppm" + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, client: SchneiderModbus, modbus_index: int, tag_device: DeviceInfo): + super().__init__(client, modbus_index, tag_device, "CO2") + + async def async_update(self): + self._attr_native_value = self._client.env_co2(self._modbus_index) * 1000 + + @staticmethod + def supports_feature_set(feature_class: FeatureClass) -> bool: + return feature_class in [FeatureClass.CO2] + + @staticmethod + def supports_gateway(type_of_gateway: TypeOfGateway): + return type_of_gateway in [TypeOfGateway.PANEL_SERVER] + + +class DeviceRssiTag(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH _attr_native_unit_of_measurement = "dBm" @@ -1011,14 +1104,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagRssiGateway(PowerTagEntity, SensorEntity): +class DeviceRssiGateway(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH _attr_native_unit_of_measurement = "dBm" @@ -1037,14 +1131,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagLqiTag(PowerTagEntity, SensorEntity): +class DeviceLqiTag(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_state_class = SensorStateClass.MEASUREMENT @@ -1061,14 +1156,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagLqiGateway(PowerTagEntity, SensorEntity): +class DeviceLqiGateway(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_state_class = SensorStateClass.MEASUREMENT @@ -1085,14 +1181,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPerTag(PowerTagEntity, SensorEntity): +class DevicePerTag(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_state_class = SensorStateClass.MEASUREMENT @@ -1109,14 +1206,15 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): return type_of_gateway in [TypeOfGateway.POWERTAG_LINK, TypeOfGateway.PANEL_SERVER] -class PowerTagPerGateway(PowerTagEntity, SensorEntity): +class DevicePerGateway(WirelessDeviceEntity, SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_state_class = SensorStateClass.MEASUREMENT @@ -1133,7 +1231,8 @@ async def async_update(self): def supports_feature_set(feature_class: FeatureClass) -> bool: return feature_class in [FeatureClass.A1, FeatureClass.A2, FeatureClass.P1, FeatureClass.F1, FeatureClass.F2, FeatureClass.F3, FeatureClass.FL, FeatureClass.M0, FeatureClass.M1, FeatureClass.M2, - FeatureClass.M3, FeatureClass.R1, FeatureClass.C] + FeatureClass.M3, FeatureClass.R1, FeatureClass.C, + FeatureClass.TEMP0, FeatureClass.TEMP1, FeatureClass.CO2] @staticmethod def supports_gateway(type_of_gateway: TypeOfGateway): diff --git a/doc/DOCA0241EN-06.xlsx b/doc/DOCA0241EN-06.xlsx index fddfb53..779e1b0 100644 Binary files a/doc/DOCA0241EN-06.xlsx and b/doc/DOCA0241EN-06.xlsx differ