diff --git a/custom_components/tech/const.py b/custom_components/tech/const.py index 59fcd49..3845fbb 100644 --- a/custom_components/tech/const.py +++ b/custom_components/tech/const.py @@ -35,7 +35,7 @@ # tile type TYPE_TEMPERATURE = 1 TYPE_FIRE_SENSOR = 2 -TYPE_TEMPERATURE_CH = 6 +TYPE_WIDGET = 6 TYPE_RELAY = 11 TYPE_ADDITIONAL_PUMP = 21 TYPE_FAN = 22 @@ -46,12 +46,18 @@ TYPE_SW_VERSION = 50 TYPE_OPEN_THERM = 252 +# widget type +WIDGET_DHW_PUMP = 1 +WIDGET_COLLECTOR_PUMP = 2 +WIDGET_TEMPERATURE_CH = 9 + # map iconId -> icon name ICON_BY_ID = { 3: "mdi:animation-play", # mode 17: "mdi:arrow-right-drop-circle-outline", # pump 50: "mdi:tune-vertical", # state 101: "mdi:cogs", # feeder + 167: "mdi:electric-switch", # contact } # map type -> icon name diff --git a/custom_components/tech/sensor.py b/custom_components/tech/sensor.py index df17094..12bfc38 100644 --- a/custom_components/tech/sensor.py +++ b/custom_components/tech/sensor.py @@ -48,18 +48,22 @@ OPENTHERM_SET_TEMP, OPENTHERM_SET_TEMP_DHW, SIGNAL_STRENGTH, + TYPE_ADDITIONAL_PUMP, TYPE_FAN, TYPE_FUEL_SUPPLY, TYPE_MIXING_VALVE, TYPE_OPEN_THERM, TYPE_TEMPERATURE, - TYPE_TEMPERATURE_CH, TYPE_TEXT, TYPE_VALVE, + TYPE_WIDGET, UDID, VALUE, VER, VISIBILITY, + WIDGET_COLLECTOR_PUMP, + WIDGET_DHW_PUMP, + WIDGET_TEMPERATURE_CH, WINDOW_SENSORS, WINDOW_STATE, WORKING_STATUS, @@ -113,8 +117,8 @@ async def async_setup_entry( entities.append( TileTemperatureSensor(tile, coordinator, config_entry, create_devices) ) - if tile[CONF_TYPE] == TYPE_TEMPERATURE_CH: - entities.append(TileWidgetSensor(tile, coordinator, config_entry)) + if tile[CONF_TYPE] == TYPE_WIDGET: + entities.extend(setup_tile_widget_sensors(tile, coordinator, config_entry)) if tile[CONF_TYPE] == TYPE_FAN: entities.append(TileFanSensor(tile, coordinator, config_entry)) if tile[CONF_TYPE] == TYPE_VALVE: @@ -178,6 +182,40 @@ async def async_setup_entry( ) +def setup_tile_widget_sensors(tile, coordinator, config_entry): + """Set up sensors for tile widgets.""" + entities = [] + + if tile[CONF_TYPE] == TYPE_WIDGET: + # Check bot widgets + for widget_key in ["widget1", "widget2"]: + widget = tile[CONF_PARAMS][widget_key] + + if widget["unit"] == -1 and widget[CONF_TYPE] == 0 and widget["txtId"] != 0: + # this is supposedly a binary sensor/contact + entities.append(TileWidgetContactSensor( + tile, coordinator, config_entry, widget_key=widget_key + ) + ) + + else: + if widget["type"] == WIDGET_DHW_PUMP or widget["type"] == WIDGET_TEMPERATURE_CH: + entities.append( + TileWidgetTemperatureSensor( + tile, coordinator, config_entry, widget_key=widget_key + ) + ) + + if widget["type"] == WIDGET_COLLECTOR_PUMP: + entities.append( + TileWidgetPumpSensor( + tile, coordinator, config_entry, widget_key=widget_key + ) + ) + + return entities + + def map_to_battery_sensors(zones, coordinator, config_entry): """Map the battery-operating devices in the zones to TechBatterySensor objects. @@ -1521,26 +1559,94 @@ def get_state(self, device) -> Any: return assets.get_text(device[CONF_PARAMS]["statusId"]) -class TileWidgetSensor(TileSensor, SensorEntity): +class TileWidgetTemperatureSensor(TileSensor, SensorEntity): """Representation of a Tile Widget Sensor.""" _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT - def __init__(self, device, coordinator, config_entry) -> None: + def __init__(self, device, coordinator, config_entry, widget_key) -> None: """Initialize the sensor.""" + self.widget_key = widget_key TileSensor.__init__(self, device, coordinator, config_entry) - self._name = ( + + txt_id = device[CONF_PARAMS][widget_key]["txtId"] + widget_type = device[CONF_PARAMS][widget_key][CONF_TYPE] + + # Determine the other widget key + other_widget_key = "widget2" if widget_key == "widget1" else "widget1" + + # Build the name + hub_name = ( + self._config_entry.title + " " + if self._config_entry.data[INCLUDE_HUB_IN_NAME] + else "" + ) + + if widget_type == WIDGET_DHW_PUMP: + temperature_type = ( + " Set Temperature" if widget_key == "widget1" else " Current Temperature" + ) + else: + temperature_type = "" + + # # If txt_id is 0, set it to the txt_id of the other widget + if txt_id == 0: + txt_id = device[CONF_PARAMS][other_widget_key]["txtId"] + + self._name = f"{hub_name}{assets.get_text(txt_id)}{temperature_type}" + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return f"{self._unique_id}_tile_temperature_{self.widget_key}" + + @property + def name(self) -> str | UndefinedType | None: + """Return the name of the sensor.""" + return self._name + + def get_state(self, device) -> Any: + """Get the state of the device.""" + if ( + device[CONF_PARAMS][self.widget_key]["unit"] == 7 + or device[CONF_PARAMS][self.widget_key]["unit"] == 4 + ): + return device[CONF_PARAMS][self.widget_key][VALUE] / 10 + if device[CONF_PARAMS][self.widget_key]["unit"] == 5: + return device[CONF_PARAMS][self.widget_key][VALUE] / 100 + return device[CONF_PARAMS][self.widget_key][VALUE] + + + +class TileWidgetPumpSensor(TileSensor, SensorEntity): + """Representation of a Tile Widget Pump Sensor.""" + + _attr_native_unit_of_measurement = PERCENTAGE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_icon = assets.get_icon_by_type(TYPE_ADDITIONAL_PUMP) + + def __init__(self, device, coordinator, config_entry, widget_key) -> None: + """Initialize the sensor.""" + self.widget_key = widget_key + TileSensor.__init__(self, device, coordinator, config_entry) + + # Determine which txtId to use + txt_id = device[CONF_PARAMS][widget_key]["txtId"] + + # Build the name + hub_name = ( self._config_entry.title + " " if self._config_entry.data[INCLUDE_HUB_IN_NAME] else "" - ) + assets.get_text(device[CONF_PARAMS]["widget1"]["txtId"]) + ) + self._name = f"{hub_name}{assets.get_text(txt_id)}" @property def unique_id(self) -> str: """Return a unique ID.""" - return f"{self._unique_id}_tile_widget" + return f"{self._unique_id}_tile_pump_{self.widget_key}" @property def name(self) -> str | UndefinedType | None: @@ -1549,8 +1655,56 @@ def name(self) -> str | UndefinedType | None: def get_state(self, device) -> Any: """Get the state of the device.""" - return device[CONF_PARAMS]["widget1"][VALUE] / 10 + return device[CONF_PARAMS][self.widget_key][VALUE] + +class TileWidgetContactSensor(BinarySensorEntity, TileEntity): + """Representation of a Tile Widget Contact Sensor.""" + + _attr_device_class = BinarySensorDeviceClass.OPENING + + def __init__(self, device, coordinator, config_entry, widget_key) -> None: + """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 _window_index already. + + """ + self.widget_key = widget_key + self._attr_is_on = ( + device[CONF_PARAMS][self.widget_key][VALUE] == 1 + ) + super().__init__(device, coordinator, config_entry) + self._attr_is_on = ( + device[CONF_PARAMS][self.widget_key][VALUE] == 1 + ) + + self._attr_icon = assets.get_icon(device[CONF_PARAMS]["iconId"]) + + # Determine which txtId to use + txt_id = device[CONF_PARAMS][widget_key]["txtId"] + + # Build the name + hub_name = ( + self._config_entry.title + " " + if self._config_entry.data[INCLUDE_HUB_IN_NAME] + else "" + ) + self._name = f"{hub_name}{assets.get_text(txt_id)}" + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return f"{self._unique_id}_tile_contact_{self.widget_key}" + + @property + def name(self) -> str | UndefinedType | None: + """Return the name of the sensor.""" + return self._name + + def get_state(self, device) -> Any: + """Get the state of the device.""" + return device[CONF_PARAMS][self.widget_key][VALUE] class TileValveSensor(TileSensor, SensorEntity): """Representation of a Tile Valve Sensor."""