diff --git a/custom_components/tech/const.py b/custom_components/tech/const.py index 2714df9..0e74121 100644 --- a/custom_components/tech/const.py +++ b/custom_components/tech/const.py @@ -17,6 +17,10 @@ VALUE = "value" MANUFACTURER = "TechControllers" WORKING_STATUS = "workingStatus" +ACTUATORS = "actuators" +BATTERY_LEVEL = "batteryLevel" +SIGNAL_STRENGTH = "signalStrength" +ACTUATORS_OPEN = "actuatorsOpen" DEFAULT_ICON = "mdi:eye" diff --git a/custom_components/tech/sensor.py b/custom_components/tech/sensor.py index a494c62..73e142c 100644 --- a/custom_components/tech/sensor.py +++ b/custom_components/tech/sensor.py @@ -30,6 +30,9 @@ from . import TechCoordinator, assets from .const import ( + ACTUATORS, + ACTUATORS_OPEN, + BATTERY_LEVEL, CONTROLLER, DOMAIN, MANUFACTURER, @@ -103,6 +106,7 @@ async def async_setup_entry( battery_devices = map_to_battery_sensors(zones, coordinator, config_entry) temperature_sensors = map_to_temperature_sensors(zones, coordinator, config_entry) humidity_sensors = map_to_humidity_sensors(zones, coordinator, config_entry) + actuator_sensors = map_to_actuator_sensors(zones, coordinator, config_entry) signal_strength_sensors = map_to_signal_strength_sensors( zones, coordinator, config_entry ) @@ -113,6 +117,7 @@ async def async_setup_entry( battery_devices, temperature_sensors, humidity_sensors, # , tile_sensors + actuator_sensors, signal_strength_sensors, ), True, @@ -151,7 +156,7 @@ def is_battery_operating_device(device) -> bool: bool - True if the device is operating on battery, False otherwise. """ - return device[CONF_ZONE]["batteryLevel"] is not None + return device[CONF_ZONE][BATTERY_LEVEL] is not None def map_to_temperature_sensors(zones, coordinator, config_entry): @@ -228,6 +233,45 @@ def is_humidity_operating_device(device) -> bool: ) +def map_to_actuator_sensors(zones, coordinator, config_entry): + """Map zones to actuator sensors. + + Args: + zones: list of zones + coordinator: API to interact with actuators + config_entry: configuration entry for the sensors + + Returns: + list of ZoneActuatorSensor instances + + """ + # Filter devices that are actuator operating devices + devices = [ + deviceIndex + for deviceIndex in zones + if is_actuator_operating_device(zones[deviceIndex]) + ] + + return [ + ZoneActuatorSensor(zones[deviceIndex], coordinator, config_entry, idx) + for deviceIndex in devices + for idx in range(len(zones[deviceIndex][ACTUATORS])) + ] + + +def is_actuator_operating_device(device) -> bool: + """Check if the device has any actuators. + + Args: + device: dict - The device information containing the zone and humidity level. + + Returns: + bool - True if the device has any actuators, False otherwise. + + """ + return len(device[ACTUATORS]) > 0 + + def map_to_tile_sensors(tiles, coordinator, config_entry): """Map tiles to corresponding sensor objects based on the device type and create a list of sensor objects. @@ -341,7 +385,7 @@ def update_properties(self, device): """ self._name = device[CONF_DESCRIPTION][CONF_NAME] - self._attr_native_value = device[CONF_ZONE]["batteryLevel"] + self._attr_native_value = device[CONF_ZONE][BATTERY_LEVEL] @callback def _handle_coordinator_update(self, *args: Any) -> None: @@ -640,7 +684,6 @@ def __init__( self, device: Dict, coordinator: TechCoordinator, config_entry: ConfigEntry ): """Initialize the sensor.""" - _LOGGER.debug("Init ZoneSensor...") super().__init__(coordinator) self._config_entry = config_entry self._coordinator = coordinator @@ -782,7 +825,7 @@ def update_properties(self, device): """ self._name = device[CONF_DESCRIPTION][CONF_NAME] - self._attr_native_value = device[CONF_ZONE]["batteryLevel"] + self._attr_native_value = device[CONF_ZONE][BATTERY_LEVEL] class ZoneSignalStrengthSensor(ZoneSensor): @@ -792,11 +835,6 @@ class ZoneSignalStrengthSensor(ZoneSensor): _attr_state_class = SensorStateClass.MEASUREMENT _attr_icon = "mdi:signal" - def __init__(self, device, coordinator, controller_udid): - """Initialize the sensor.""" - self.attrs: Dict[str, Any] = {} - super().__init__(device, coordinator, controller_udid) - @property def name(self): """Return the name of the device.""" @@ -858,6 +896,73 @@ def update_properties(self, device): self._attr_native_value = None +class ZoneActuatorSensor(ZoneSensor): + """Representation of a Zone Actuator Sensor.""" + + _attr_native_unit_of_measurement = PERCENTAGE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_icon = assets.get_icon_by_type(TYPE_VALVE) + + def __init__(self, device, coordinator, config_entry, actuator_index): + """Initialize the sensor. + + These are needed before the call to super, as ZoneSensor class + calls update_properties in its init, which actually calls this class + update_properties, which does not know attrs and _actuator_index already. + + """ + self._actuator_index = actuator_index + self.attrs: Dict[str, Any] = {} + super().__init__(device, coordinator, config_entry) + self.attrs[BATTERY_LEVEL] = device[ACTUATORS][self._actuator_index][ + BATTERY_LEVEL + ] + self.attrs[SIGNAL_STRENGTH] = device[ACTUATORS][self._actuator_index][ + SIGNAL_STRENGTH + ] + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return f"{self._unique_id}_zone_actuator_{str(self._actuator_index + 1)}" + + @property + def name(self): + """Return the name of the entity.""" + return f"{self._name} actuator {str(self._actuator_index + 1)}" + + @property + def extra_state_attributes(self) -> Dict[str, Any]: + """Return the state attributes.""" + attributes = {} + attributes.update(self.attrs) + return attributes + + def update_properties(self, device): + """Update the properties of the ZoneActuatorSensor object. + + Args: + device (dict): The device information. + + Returns: + None + + """ + # Update the name of the device + self._name = device[CONF_DESCRIPTION][CONF_NAME] + + # Update the native value attribute + self._attr_native_value = device[CONF_ZONE][ACTUATORS_OPEN] + + # Update battery and signal strength + self.attrs[BATTERY_LEVEL] = device[ACTUATORS][self._actuator_index][ + BATTERY_LEVEL + ] + self.attrs[SIGNAL_STRENGTH] = device[ACTUATORS][self._actuator_index][ + SIGNAL_STRENGTH + ] + + class ZoneOutsideTempTile(ZoneSensor): """Representation of a Zone Temperature Sensor."""