From b8e4780aa12a3b587971472be0cb52809670c660 Mon Sep 17 00:00:00 2001 From: G Johansson <goran.johansson@shiftit.se> Date: Mon, 7 Mar 2022 23:27:37 +0100 Subject: [PATCH] Modify diagnostics for Sensibo (#67764) --- homeassistant/components/sensibo/climate.py | 79 ++++++++++++------- .../components/sensibo/coordinator.py | 20 ++++- .../components/sensibo/diagnostics.py | 20 ++++- homeassistant/components/sensibo/entity.py | 2 +- homeassistant/components/sensibo/number.py | 12 ++- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 7c3282478ab..669fa7a9543 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -102,7 +102,7 @@ async def async_setup_entry( entities = [ SensiboClimate(coordinator, device_id) - for device_id, device_data in coordinator.data.items() + for device_id, device_data in coordinator.data.parsed.items() # Remove none climate devices if device_data["hvac_modes"] and device_data["temp"] ] @@ -128,10 +128,10 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): """Initiate SensiboClimate.""" super().__init__(coordinator, device_id) self._attr_unique_id = device_id - self._attr_name = coordinator.data[device_id]["name"] + self._attr_name = coordinator.data.parsed[device_id]["name"] self._attr_temperature_unit = ( TEMP_CELSIUS - if coordinator.data[device_id]["temp_unit"] == "C" + if coordinator.data.parsed[device_id]["temp_unit"] == "C" else TEMP_FAHRENHEIT ) self._attr_supported_features = self.get_features() @@ -140,7 +140,7 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): def get_features(self) -> int: """Get supported features.""" features = 0 - for key in self.coordinator.data[self.unique_id]["full_features"]: + for key in self.coordinator.data.parsed[self.unique_id]["full_features"]: if key in FIELD_TO_FLAG: features |= FIELD_TO_FLAG[key] return features @@ -148,14 +148,14 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): @property def current_humidity(self) -> int | None: """Return the current humidity.""" - return self.coordinator.data[self.unique_id]["humidity"] + return self.coordinator.data.parsed[self.unique_id]["humidity"] @property def hvac_mode(self) -> str: """Return hvac operation.""" return ( - SENSIBO_TO_HA[self.coordinator.data[self.unique_id]["hvac_mode"]] - if self.coordinator.data[self.unique_id]["on"] + SENSIBO_TO_HA[self.coordinator.data.parsed[self.unique_id]["hvac_mode"]] + if self.coordinator.data.parsed[self.unique_id]["on"] else HVAC_MODE_OFF ) @@ -164,14 +164,14 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): """Return the list of available hvac operation modes.""" return [ SENSIBO_TO_HA[mode] - for mode in self.coordinator.data[self.unique_id]["hvac_modes"] + for mode in self.coordinator.data.parsed[self.unique_id]["hvac_modes"] ] @property def current_temperature(self) -> float | None: """Return the current temperature.""" return convert_temperature( - self.coordinator.data[self.unique_id]["temp"], + self.coordinator.data.parsed[self.unique_id]["temp"], TEMP_CELSIUS, self.temperature_unit, ) @@ -179,53 +179,56 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): @property def target_temperature(self) -> float | None: """Return the temperature we try to reach.""" - return self.coordinator.data[self.unique_id]["target_temp"] + return self.coordinator.data.parsed[self.unique_id]["target_temp"] @property def target_temperature_step(self) -> float | None: """Return the supported step of target temperature.""" - return self.coordinator.data[self.unique_id]["temp_step"] + return self.coordinator.data.parsed[self.unique_id]["temp_step"] @property def fan_mode(self) -> str | None: """Return the fan setting.""" - return self.coordinator.data[self.unique_id]["fan_mode"] + return self.coordinator.data.parsed[self.unique_id]["fan_mode"] @property def fan_modes(self) -> list[str] | None: """Return the list of available fan modes.""" - return self.coordinator.data[self.unique_id]["fan_modes"] + return self.coordinator.data.parsed[self.unique_id]["fan_modes"] @property def swing_mode(self) -> str | None: """Return the swing setting.""" - return self.coordinator.data[self.unique_id]["swing_mode"] + return self.coordinator.data.parsed[self.unique_id]["swing_mode"] @property def swing_modes(self) -> list[str] | None: """Return the list of available swing modes.""" - return self.coordinator.data[self.unique_id]["swing_modes"] + return self.coordinator.data.parsed[self.unique_id]["swing_modes"] @property def min_temp(self) -> float: """Return the minimum temperature.""" - return self.coordinator.data[self.unique_id]["temp_list"][0] + return self.coordinator.data.parsed[self.unique_id]["temp_list"][0] @property def max_temp(self) -> float: """Return the maximum temperature.""" - return self.coordinator.data[self.unique_id]["temp_list"][-1] + return self.coordinator.data.parsed[self.unique_id]["temp_list"][-1] @property def available(self) -> bool: """Return True if entity is available.""" - return self.coordinator.data[self.unique_id]["available"] and super().available + return ( + self.coordinator.data.parsed[self.unique_id]["available"] + and super().available + ) async def async_set_temperature(self, **kwargs) -> None: """Set new target temperature.""" if ( "targetTemperature" - not in self.coordinator.data[self.unique_id]["active_features"] + not in self.coordinator.data.parsed[self.unique_id]["active_features"] ): raise HomeAssistantError( "Current mode doesn't support setting Target Temperature" @@ -237,13 +240,23 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): if temperature == self.target_temperature: return - if temperature not in self.coordinator.data[self.unique_id]["temp_list"]: + if temperature not in self.coordinator.data.parsed[self.unique_id]["temp_list"]: # Requested temperature is not supported. - if temperature > self.coordinator.data[self.unique_id]["temp_list"][-1]: - temperature = self.coordinator.data[self.unique_id]["temp_list"][-1] - - elif temperature < self.coordinator.data[self.unique_id]["temp_list"][0]: - temperature = self.coordinator.data[self.unique_id]["temp_list"][0] + if ( + temperature + > self.coordinator.data.parsed[self.unique_id]["temp_list"][-1] + ): + temperature = self.coordinator.data.parsed[self.unique_id]["temp_list"][ + -1 + ] + + elif ( + temperature + < self.coordinator.data.parsed[self.unique_id]["temp_list"][0] + ): + temperature = self.coordinator.data.parsed[self.unique_id]["temp_list"][ + 0 + ] else: return @@ -252,7 +265,10 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new target fan mode.""" - if "fanLevel" not in self.coordinator.data[self.unique_id]["active_features"]: + if ( + "fanLevel" + not in self.coordinator.data.parsed[self.unique_id]["active_features"] + ): raise HomeAssistantError("Current mode doesn't support setting Fanlevel") await self._async_set_ac_state_property("fanLevel", fan_mode) @@ -264,7 +280,7 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): return # Turn on if not currently on. - if not self.coordinator.data[self.unique_id]["on"]: + if not self.coordinator.data.parsed[self.unique_id]["on"]: await self._async_set_ac_state_property("on", True) await self._async_set_ac_state_property("mode", HA_TO_SENSIBO[hvac_mode]) @@ -272,7 +288,10 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): async def async_set_swing_mode(self, swing_mode: str) -> None: """Set new target swing operation.""" - if "swing" not in self.coordinator.data[self.unique_id]["active_features"]: + if ( + "swing" + not in self.coordinator.data.parsed[self.unique_id]["active_features"] + ): raise HomeAssistantError("Current mode doesn't support setting Swing") await self._async_set_ac_state_property("swing", swing_mode) @@ -292,13 +311,13 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity): params = { "name": name, "value": value, - "ac_states": self.coordinator.data[self.unique_id]["ac_states"], + "ac_states": self.coordinator.data.parsed[self.unique_id]["ac_states"], "assumed_state": assumed_state, } result = await self.async_send_command("set_ac_state", params) if result["result"]["status"] == "Success": - self.coordinator.data[self.unique_id][AC_STATE_TO_DATA[name]] = value + self.coordinator.data.parsed[self.unique_id][AC_STATE_TO_DATA[name]] = value self.async_write_ha_state() return diff --git a/homeassistant/components/sensibo/coordinator.py b/homeassistant/components/sensibo/coordinator.py index 4a03b532149..089cc0406bd 100644 --- a/homeassistant/components/sensibo/coordinator.py +++ b/homeassistant/components/sensibo/coordinator.py @@ -35,9 +35,19 @@ class MotionSensor: model: str | None = None +@dataclass +class SensiboData: + """Dataclass for Sensibo data.""" + + raw: dict + parsed: dict + + class SensiboDataUpdateCoordinator(DataUpdateCoordinator): """A Sensibo Data Update Coordinator.""" + data: SensiboData + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: """Initialize the Sensibo coordinator.""" self.client = SensiboClient( @@ -52,7 +62,7 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), ) - async def _async_update_data(self) -> dict[str, dict[str, Any]]: + async def _async_update_data(self) -> SensiboData: """Fetch data from Sensibo.""" devices = [] @@ -65,7 +75,10 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): except SensiboError as error: raise UpdateFailed from error - device_data: dict[str, dict[str, Any]] = {} + if not devices: + raise UpdateFailed("No devices found") + + device_data: dict[str, Any] = {} for dev in devices: unique_id = dev["id"] mac = dev["macAddress"] @@ -172,4 +185,5 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): "full_capabilities": capabilities, "motion_sensors": motion_sensors, } - return device_data + + return SensiboData(raw=data, parsed=device_data) diff --git a/homeassistant/components/sensibo/diagnostics.py b/homeassistant/components/sensibo/diagnostics.py index d3e2382c7a8..e4a4672bf64 100644 --- a/homeassistant/components/sensibo/diagnostics.py +++ b/homeassistant/components/sensibo/diagnostics.py @@ -3,16 +3,34 @@ from __future__ import annotations from typing import Any +from homeassistant.components.diagnostics.util import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from .const import DOMAIN from .coordinator import SensiboDataUpdateCoordinator +TO_REDACT = { + "location", + "ssid", + "id", + "macAddress", + "parentDeviceUid", + "qrId", + "serial", + "uid", + "email", + "firstName", + "lastName", + "username", + "podUid", + "deviceUid", +} + async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: ConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - return coordinator.data + return async_redact_data(coordinator.data.raw, TO_REDACT) diff --git a/homeassistant/components/sensibo/entity.py b/homeassistant/components/sensibo/entity.py index 22d29d37ead..ff68b0ebfd1 100644 --- a/homeassistant/components/sensibo/entity.py +++ b/homeassistant/components/sensibo/entity.py @@ -28,7 +28,7 @@ class SensiboBaseEntity(CoordinatorEntity): super().__init__(coordinator) self._device_id = device_id self._client = coordinator.client - device = coordinator.data[device_id] + device = coordinator.data.parsed[device_id] self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, device["id"])}, name=device["name"], diff --git a/homeassistant/components/sensibo/number.py b/homeassistant/components/sensibo/number.py index ec7a150ebe9..2aa38a41a7b 100644 --- a/homeassistant/components/sensibo/number.py +++ b/homeassistant/components/sensibo/number.py @@ -64,7 +64,7 @@ async def async_setup_entry( async_add_entities( SensiboNumber(coordinator, device_id, description) - for device_id, device_data in coordinator.data.items() + for device_id, device_data in coordinator.data.parsed.items() for description in NUMBER_TYPES if device_data["hvac_modes"] and device_data["temp"] ) @@ -86,20 +86,24 @@ class SensiboNumber(SensiboBaseEntity, NumberEntity): self.entity_description = entity_description self._attr_unique_id = f"{device_id}-{entity_description.key}" self._attr_name = ( - f"{coordinator.data[device_id]['name']} {entity_description.name}" + f"{coordinator.data.parsed[device_id]['name']} {entity_description.name}" ) @property def value(self) -> float | None: """Return the value from coordinator data.""" - return self.coordinator.data[self._device_id][self.entity_description.key] + return self.coordinator.data.parsed[self._device_id][ + self.entity_description.key + ] async def async_set_value(self, value: float) -> None: """Set value for calibration.""" data = {self.entity_description.remote_key: value} result = await self.async_send_command("set_calibration", {"data": data}) if result["status"] == "success": - self.coordinator.data[self._device_id][self.entity_description.key] = value + self.coordinator.data.parsed[self._device_id][ + self.entity_description.key + ] = value self.async_write_ha_state() return raise HomeAssistantError(f"Could not set calibration for device {self.name}") -- GitLab