diff --git a/custom_components/rointe/climate.py b/custom_components/rointe/climate.py index 550aa38..6466898 100644 --- a/custom_components/rointe/climate.py +++ b/custom_components/rointe/climate.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Any + from rointesdk.device import RointeDevice from homeassistant.components.climate import ( @@ -19,16 +21,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ( - DOMAIN, - LOGGER, - RADIATOR_TEMP_MAX, - RADIATOR_TEMP_MIN, - RADIATOR_TEMP_STEP, - RointeCommand, - RointeOperationMode, - RointePreset, -) +from .const import DOMAIN, LOGGER, RointeCommand, RointeOperationMode, RointePreset from .coordinator import RointeDataUpdateCoordinator from .entity import RointeRadiatorEntity @@ -45,6 +38,10 @@ RointePreset.ICE: RointePreset.ICE, } +RADIATOR_TEMP_STEP = 0.5 +RADIATOR_TEMP_MIN = 7.0 +RADIATOR_TEMP_MAX = 30.0 + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -171,14 +168,16 @@ def preset_mode(self) -> str | None: # Also captures "none" (man mode, temperature outside presets) return ROINTE_HASS_MAP.get(self._radiator.preset, None) - async def async_set_temperature(self, **kwargs): + async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" target_temperature = kwargs["temperature"] + LOGGER.debug("Setting temperature to %s", target_temperature) + if not RADIATOR_TEMP_MIN <= target_temperature <= RADIATOR_TEMP_MAX: raise ValueError( - f"Invalid set_humidity value (must be in range 7.0, 30.0): {target_temperature}" + f"Invalid set_temperature value (must be in range {RADIATOR_TEMP_MIN}, {RADIATOR_TEMP_MAX}): {target_temperature}" ) # Round to the nearest half value. @@ -188,12 +187,12 @@ async def async_set_temperature(self, **kwargs): self._radiator, RointeCommand.SET_TEMP, rounded_temp ): raise HomeAssistantError( - f"Failed to set HVAC mode for {self._radiator.name}" + f"Failed to set temperature for {self._radiator.name}" ) await self._signal_thermostat_update() - async def async_set_hvac_mode(self, hvac_mode): + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new target hvac mode.""" LOGGER.debug("Setting HVAC mode to %s", hvac_mode) @@ -207,7 +206,7 @@ async def async_set_hvac_mode(self, hvac_mode): await self._signal_thermostat_update() - async def async_set_preset_mode(self, preset_mode): + async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new target preset mode.""" LOGGER.debug("Setting preset mode: %s", preset_mode) @@ -215,7 +214,7 @@ async def async_set_preset_mode(self, preset_mode): self._radiator, RointeCommand.SET_PRESET, preset_mode ): raise HomeAssistantError( - f"Failed to set HVAC mode for {self._radiator.name}" + f"Failed to set HVAC preset for {self._radiator.name}" ) await self._signal_thermostat_update() diff --git a/custom_components/rointe/const.py b/custom_components/rointe/const.py index 0de7880..323f645 100644 --- a/custom_components/rointe/const.py +++ b/custom_components/rointe/const.py @@ -22,10 +22,6 @@ PRESET_ROINTE_ICE = "ice" -RADIATOR_TEMP_STEP = 0.5 -RADIATOR_TEMP_MIN = 7.0 -RADIATOR_TEMP_MAX = 30.0 - class RointePreset(StrEnum): """Rointe radiators preset modes.""" diff --git a/custom_components/rointe/device_manager.py b/custom_components/rointe/device_manager.py index 8934754..f3cd645 100644 --- a/custom_components/rointe/device_manager.py +++ b/custom_components/rointe/device_manager.py @@ -291,7 +291,12 @@ async def send_command( ) -> bool: """Send command to the device.""" - LOGGER.debug("Sending command [%s] to device ID [%s]", command, device.name) + LOGGER.debug( + "Sending command [%s] to device ID [%s]. Args: %s", + command, + device.name, + arg, + ) if command == RointeCommand.SET_TEMP: return await self._set_device_temp(device, arg) @@ -313,8 +318,11 @@ async def _set_device_temp(self, device: RointeDevice, new_temp: float) -> bool: ) if not result.success: + LOGGER.debug("_set_device_temp failed: %s", result.error_message) + # Set the device as unavailable. device.hass_available = False + return False # Update the device internal status @@ -341,6 +349,7 @@ async def _set_device_mode(self, device: RointeDevice, hvac_mode: str) -> bool: ) if not result.success: + LOGGER.debug("_set_device_mode failed: %s", result.error_message) # Set the device as unavailable. device.hass_available = False return False @@ -388,6 +397,8 @@ async def _set_device_preset(self, device: RointeDevice, preset: str) -> bool: ) if not result.success: + LOGGER.debug("_set_device_preset failed: %s", result.error_message) + # Set the device as unavailable. device.hass_available = False return False diff --git a/custom_components/rointe/manifest.json b/custom_components/rointe/manifest.json index e018ebc..8d5e9e3 100644 --- a/custom_components/rointe/manifest.json +++ b/custom_components/rointe/manifest.json @@ -5,6 +5,6 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rointe", "iot_class": "cloud_polling", - "version": "1.5.0", - "requirements": ["rointe-sdk==1.5.0"] + "requirements": ["rointe-sdk==1.5.0"], + "version": "1.5.1" } diff --git a/custom_components/rointe/sensor.py b/custom_components/rointe/sensor.py deleted file mode 100644 index 80c328e..0000000 --- a/custom_components/rointe/sensor.py +++ /dev/null @@ -1,62 +0,0 @@ -"""A sensor for the current Rointe radiator temperature.""" -from __future__ import annotations - -from datetime import datetime - -from rointesdk.device import RointeDevice - -from homeassistant.components.sensor import SensorEntity -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import StateType - -from .const import DOMAIN -from .coordinator import RointeDataUpdateCoordinator -from .entity import RointeRadiatorEntity -from .sensor_descriptions import SENSOR_DESCRIPTIONS, RointeSensorEntityDescription - - -async def async_setup_entry( - hass: HomeAssistant, - entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up the radiator sensors from the config entry.""" - coordinator: RointeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - - coordinator.add_sensor_entities_for_seen_keys( - async_add_entities, SENSOR_DESCRIPTIONS, RointeGenericSensor - ) - - -class RointeGenericSensor(RointeRadiatorEntity, SensorEntity): - """Generic radiator sensor.""" - - entity_description: RointeSensorEntityDescription - - def __init__( - self, - radiator: RointeDevice, - coordinator: RointeDataUpdateCoordinator, - description: RointeSensorEntityDescription, - ) -> None: - """Initialize a generic sensor.""" - super().__init__( - coordinator, - radiator, - name=f"{radiator.name} {description.name}", - unique_id=f"{radiator.id}-{description.key}", - ) - - self.entity_description = description - - @property - def native_value(self) -> StateType: - """Return the sensor value.""" - return self.entity_description.value_fn(self._radiator) - - @property - def last_reset(self) -> datetime | None: - """Return the last time the sensor was initialized, if relevant.""" - return self.entity_description.last_reset_fn(self._radiator) diff --git a/custom_components/rointe/sensor_descriptions.py b/custom_components/rointe/sensor_descriptions.py deleted file mode 100644 index 3791f2a..0000000 --- a/custom_components/rointe/sensor_descriptions.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Sensor descriptions for Rointe.""" -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass -from datetime import datetime - -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT, TEMP_CELSIUS -from homeassistant.helpers.entity import EntityCategory -from homeassistant.helpers.typing import StateType - -from .device_manager import RointeDevice - - -@dataclass -class RointeSensorEntityDescriptionMixin: - """Define a description mixin for Rointe sensor entities.""" - - value_fn: Callable[[RointeDevice], StateType] - last_reset_fn: Callable[[RointeDevice], datetime | None] - - -@dataclass -class RointeSensorEntityDescription( - SensorEntityDescription, RointeSensorEntityDescriptionMixin -): - """Define an object to describe Rointe sensor entities.""" - - -SENSOR_DESCRIPTIONS = [ - # Current room temperature sensor (probe value). - RointeSensorEntityDescription( - key="current_temperature", - name="Current Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, - state_class=SensorStateClass.MEASUREMENT, - value_fn=lambda radiator: radiator.temp_probe, - last_reset_fn=lambda radiator: None, - ), - # Energy usage in Kw/h. - RointeSensorEntityDescription( - key="energy", - name="Energy Consumption", - device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, - state_class=SensorStateClass.TOTAL, - entity_category=EntityCategory.DIAGNOSTIC, - value_fn=lambda radiator: radiator.energy_data.kwh - if radiator.energy_data - else None, - last_reset_fn=lambda radiator: radiator.energy_data.start - if radiator.energy_data - else None, - ), - # Effective power usage in W. - RointeSensorEntityDescription( - key="power", - name="Effective Power", - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - value_fn=lambda radiator: radiator.energy_data.effective_power - if radiator.energy_data - else None, - last_reset_fn=lambda radiator: None, - ), -] diff --git a/custom_components/rointe/translations/en.json b/custom_components/rointe/translations/en.json deleted file mode 100644 index b86bb4a..0000000 --- a/custom_components/rointe/translations/en.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "Device is already configured" - }, - "error": { - "invalid_auth": "Invalid authentication", - "unable_get_installations": "An error occurred while retrieving installations" - }, - "step": { - "user": { - "data": { - "rointe_password": "Password", - "rointe_username": "Username" - }, - "title": "Connect to Rointe Connect" - - }, - "installation": { - "data": { - "rointe_installation": "Installations" - }, - "title":"Select installation" - } - } - } -} \ No newline at end of file diff --git a/custom_components/rointe/update.py b/custom_components/rointe/update.py deleted file mode 100644 index 7455b76..0000000 --- a/custom_components/rointe/update.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Update entity platform for Rointe devices.""" -from __future__ import annotations - -from abc import ABC - -from rointesdk.device import RointeDevice - -from homeassistant.components.update import ( - UpdateDeviceClass, - UpdateEntity, - UpdateEntityDescription, -) -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import EntityCategory -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from .const import DOMAIN -from .coordinator import RointeDataUpdateCoordinator -from .entity import RointeRadiatorEntity - - -async def async_setup_entry( - hass: HomeAssistant, - entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up the radiator sensors from the config entry.""" - coordinator: RointeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - - # Register the Entity classes and platform on the coordinator. - coordinator.add_entities_for_seen_keys( - async_add_entities, - [RointeUpdateEntity], - "update", - ) - - -class RointeUpdateEntity(RointeRadiatorEntity, UpdateEntity, ABC): - """Update entity.""" - - def __init__( - self, - radiator: RointeDevice, - coordinator: RointeDataUpdateCoordinator, - ) -> None: - """Init the update entity.""" - - self.entity_description = UpdateEntityDescription( - key="fw_update_available", - name="Update Available", - device_class=UpdateDeviceClass.FIRMWARE, - entity_category=EntityCategory.DIAGNOSTIC, - ) - - # Set the name and ID of this entity to be the radiator name/id and a prefix. - super().__init__( - coordinator, - radiator, - name=f"{radiator.name} Update Available", - unique_id=f"{radiator.id}-fw_update_available", - ) - - @property - def installed_version(self) -> str | None: - """Version installed and in use.""" - return self._radiator.firmware_version - - @property - def latest_version(self) -> str | None: - """Latest version available for install.""" - return self._radiator.latest_firmware_version