diff --git a/custom_components/uponor/__init__.py b/custom_components/uponor/__init__.py old mode 100644 new mode 100755 index 53709a1..6ffbe79 --- a/custom_components/uponor/__init__.py +++ b/custom_components/uponor/__init__.py @@ -35,7 +35,7 @@ _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.CLIMATE, Platform.SWITCH] +PLATFORMS = [Platform.CLIMATE, Platform.SWITCH, Platform.SENSOR] async def async_setup(hass: HomeAssistant, config: dict): hass.data.setdefault(DOMAIN, {}) @@ -59,6 +59,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): def handle_set_variable(call): var_name = call.data.get('var_name') var_value = call.data.get('var_value') + _LOGGER.debug(f"Setting variable: {var_name} to: {var_value}") hass.data[DOMAIN]['state_proxy'].set_variable(var_name, var_value) hass.services.async_register(DOMAIN, "set_variable", handle_set_variable) @@ -79,7 +80,8 @@ async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None: async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Unload a config entry.""" _LOGGER.debug("Unloading setup entry: %s, data: %s, options: %s", config_entry.entry_id, config_entry.data, config_entry.options) - unload_ok = await hass.config_entries.async_unload_platforms(config_entry, [Platform.SWITCH, Platform.CLIMATE]) + unload_ok = await hass.config_entries.async_unload_platforms(config_entry, [Platform.SWITCH, Platform.CLIMATE, Platform.SENSOR]) + _LOGGER.debug(f"Unload status for Uponor platforms: {unload_ok}") return unload_ok class UponorStateProxy: @@ -143,12 +145,28 @@ def get_max_limit(self, thermostat): var = thermostat + '_maximum_setpoint' if var in self._data: return round((int(self._data[var]) - 320) / 18, 1) - + + def has_humidity_sensor(self, thermostat): + var = thermostat + '_rh' + return var in self._data and int(self._data[var]) != 0 + def get_humidity(self, thermostat): var = thermostat + '_rh' if var in self._data and int(self._data[var]) >= TOO_LOW_HUMIDITY_LIMIT: return int(self._data[var]) + def has_floor_temperature(self, thermostat): + var = thermostat + '_external_temperature' + return var in self._data and int(self._data[var]) != 32767 + + def get_floor_temperature(self, thermostat): + var = thermostat + '_external_temperature' + if var in self._data: + temp = int(self._data[var]) + if temp != 32767 and temp <= TOO_HIGH_TEMP_LIMIT: + return round((temp - 320) / 18, 1) + return None + # Temperature setpoint def get_setpoint(self, thermostat): @@ -294,8 +312,11 @@ def get_last_update(self): # Rest async def async_update(self,_=None): try: + _LOGGER.debug("Running async_update to get data from Uponor thermostat.") self.next_sp_from_dt = dt_util.now() self._data = await self._hass.async_add_executor_job(lambda: self._client.get_data()) + # For deep debug remove comment and you get the full json message. + # _LOGGER.debug(f"Data recieved from Uponor: {self._data}") self._hass.async_add_job(async_dispatcher_send, self._hass, SIGNAL_UPONOR_STATE_UPDATE) except Exception as ex: _LOGGER.error("Uponor thermostat was unable to update: %s", ex) diff --git a/custom_components/uponor/sensor.py b/custom_components/uponor/sensor.py new file mode 100755 index 0000000..16bb091 --- /dev/null +++ b/custom_components/uponor/sensor.py @@ -0,0 +1,143 @@ +import logging + +from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, SensorStateClass +from homeassistant.const import UnitOfTemperature, PERCENTAGE +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import DOMAIN, SIGNAL_UPONOR_STATE_UPDATE + +_LOGGER = logging.getLogger(__name__) # Lägg till detta +async def async_setup_entry(hass, entry, async_add_entities): + state_proxy = hass.data[DOMAIN]["state_proxy"] + + entities = [] + for thermostat in hass.data[DOMAIN]["thermostats"]: + room_name = state_proxy.get_room_name(thermostat) + _LOGGER.debug(f"Adding sensors for {room_name} (thermostat ID: {thermostat})") + entities.append(UponorRoomCurrentTemperatureSensor(state_proxy, thermostat)) + + if state_proxy.has_floor_temperature(thermostat): + entities.append(UponorFloorTemperatureSensor(state_proxy, thermostat)) + _LOGGER.debug(f"Added floor sensor for: {room_name}") + + if state_proxy.has_humidity_sensor(thermostat): + entities.append(UponorHumiditySensor(state_proxy, thermostat)) + _LOGGER.debug(f"Added humidity sensor for: {room_name}") + + _LOGGER.debug(f"Total number of sensors added: {len(entities)}") + async_add_entities(entities, update_before_add=False) + +class UponorFloorTemperatureSensor(SensorEntity): + + def __init__(self, state_proxy, thermostat): + self._state_proxy = state_proxy + self._thermostat = thermostat + self._attr_name = f"{state_proxy.get_room_name(thermostat)} Floor Temperature" + self._attr_unique_id = f"{state_proxy.get_thermostat_id(thermostat)}_floor_temp" + self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + self._attr_state_class = SensorStateClass.MEASUREMENT + + @property + def device_info(self): + return { + "identifiers": {(DOMAIN, self._state_proxy.get_thermostat_id(self._thermostat))}, + "name": self._state_proxy.get_room_name(self._thermostat), + "manufacturer": "Uponor", + "model": self._state_proxy.get_model(), + "sw_version": self._state_proxy.get_version(self._thermostat) + } + + @property + def native_value(self): + return self._state_proxy.get_floor_temperature(self._thermostat) + + async def async_added_to_hass(self): + self.async_on_remove( + async_dispatcher_connect( + self.hass, SIGNAL_UPONOR_STATE_UPDATE, self._update_callback + ) + ) + @callback + def _update_callback(self): + """Update sensor state. when data updates""" + _LOGGER.debug(f"Updating state for {self._attr_name} with ID {self._attr_unique_id}") + self.async_write_ha_state() + +class UponorRoomCurrentTemperatureSensor(SensorEntity): + + def __init__(self, state_proxy, thermostat): + self._state_proxy = state_proxy + self._thermostat = thermostat + self._attr_name = f"{state_proxy.get_room_name(thermostat)} Current Temperature" + self._attr_unique_id = f"{state_proxy.get_thermostat_id(thermostat)}_current_temp" + self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + self._attr_state_class = SensorStateClass.MEASUREMENT + + + @property + def device_info(self): + return { + "identifiers": {(DOMAIN, self._state_proxy.get_thermostat_id(self._thermostat))}, + "name": self._state_proxy.get_room_name(self._thermostat), + "manufacturer": "Uponor", + "model": self._state_proxy.get_model(), + "sw_version": self._state_proxy.get_version(self._thermostat) + } + + @property + def native_value(self): + return self._state_proxy.get_temperature(self._thermostat) + + async def async_added_to_hass(self): + self.async_on_remove( + async_dispatcher_connect( + self.hass, SIGNAL_UPONOR_STATE_UPDATE, self._update_callback + ) + ) + @callback + def _update_callback(self): + """Update sensor state. when data updates""" + _LOGGER.debug(f"Updating state for {self._attr_name} with ID {self._attr_unique_id}") + self.async_write_ha_state() + +class UponorHumiditySensor(SensorEntity): + def __init__(self, state_proxy, thermostat): + self._state_proxy = state_proxy + self._thermostat = thermostat + self._attr_name = f"{state_proxy.get_room_name(thermostat)} humidity" + self._attr_unique_id = f"{state_proxy.get_thermostat_id(thermostat)}_rh" + self._attr_device_class = SensorDeviceClass.HUMIDITY + self._attr_native_unit_of_measurement = PERCENTAGE + self._attr_state_class = SensorStateClass.MEASUREMENT + + @property + def device_info(self): + return { + "identifiers": {(DOMAIN, self._state_proxy.get_thermostat_id(self._thermostat))}, + "name": self._state_proxy.get_room_name(self._thermostat), + "manufacturer": "Uponor", + "model": self._state_proxy.get_model(), + "sw_version": self._state_proxy.get_version(self._thermostat) + } + + @property + def available(self): + """Return True if the sensor is available.""" + return self._state_proxy.has_humidity_sensor(self._thermostat) + + @property + def native_value(self): + return self._state_proxy.get_humidity(self._thermostat) + + async def async_added_to_hass(self): + self.async_on_remove( + async_dispatcher_connect( + self.hass, SIGNAL_UPONOR_STATE_UPDATE, self._update_callback + ) + ) + + @callback + def _update_callback(self): + _LOGGER.debug(f"Updating state for {self._attr_name} with ID {self._attr_unique_id}") + self.async_write_ha_state() \ No newline at end of file