Modify diagnostics for Sensibo (#67764)

This commit is contained in:
G Johansson 2022-03-07 23:27:37 +01:00 committed by GitHub
parent 63957787c4
commit b8e4780aa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 38 deletions

View File

@ -102,7 +102,7 @@ async def async_setup_entry(
entities = [ entities = [
SensiboClimate(coordinator, device_id) 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 # Remove none climate devices
if device_data["hvac_modes"] and device_data["temp"] if device_data["hvac_modes"] and device_data["temp"]
] ]
@ -128,10 +128,10 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
"""Initiate SensiboClimate.""" """Initiate SensiboClimate."""
super().__init__(coordinator, device_id) super().__init__(coordinator, device_id)
self._attr_unique_id = 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 = ( self._attr_temperature_unit = (
TEMP_CELSIUS TEMP_CELSIUS
if coordinator.data[device_id]["temp_unit"] == "C" if coordinator.data.parsed[device_id]["temp_unit"] == "C"
else TEMP_FAHRENHEIT else TEMP_FAHRENHEIT
) )
self._attr_supported_features = self.get_features() self._attr_supported_features = self.get_features()
@ -140,7 +140,7 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
def get_features(self) -> int: def get_features(self) -> int:
"""Get supported features.""" """Get supported features."""
features = 0 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: if key in FIELD_TO_FLAG:
features |= FIELD_TO_FLAG[key] features |= FIELD_TO_FLAG[key]
return features return features
@ -148,14 +148,14 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
@property @property
def current_humidity(self) -> int | None: def current_humidity(self) -> int | None:
"""Return the current humidity.""" """Return the current humidity."""
return self.coordinator.data[self.unique_id]["humidity"] return self.coordinator.data.parsed[self.unique_id]["humidity"]
@property @property
def hvac_mode(self) -> str: def hvac_mode(self) -> str:
"""Return hvac operation.""" """Return hvac operation."""
return ( return (
SENSIBO_TO_HA[self.coordinator.data[self.unique_id]["hvac_mode"]] SENSIBO_TO_HA[self.coordinator.data.parsed[self.unique_id]["hvac_mode"]]
if self.coordinator.data[self.unique_id]["on"] if self.coordinator.data.parsed[self.unique_id]["on"]
else HVAC_MODE_OFF else HVAC_MODE_OFF
) )
@ -164,14 +164,14 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
"""Return the list of available hvac operation modes.""" """Return the list of available hvac operation modes."""
return [ return [
SENSIBO_TO_HA[mode] 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 @property
def current_temperature(self) -> float | None: def current_temperature(self) -> float | None:
"""Return the current temperature.""" """Return the current temperature."""
return convert_temperature( return convert_temperature(
self.coordinator.data[self.unique_id]["temp"], self.coordinator.data.parsed[self.unique_id]["temp"],
TEMP_CELSIUS, TEMP_CELSIUS,
self.temperature_unit, self.temperature_unit,
) )
@ -179,53 +179,56 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
@property @property
def target_temperature(self) -> float | None: def target_temperature(self) -> float | None:
"""Return the temperature we try to reach.""" """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 @property
def target_temperature_step(self) -> float | None: def target_temperature_step(self) -> float | None:
"""Return the supported step of target temperature.""" """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 @property
def fan_mode(self) -> str | None: def fan_mode(self) -> str | None:
"""Return the fan setting.""" """Return the fan setting."""
return self.coordinator.data[self.unique_id]["fan_mode"] return self.coordinator.data.parsed[self.unique_id]["fan_mode"]
@property @property
def fan_modes(self) -> list[str] | None: def fan_modes(self) -> list[str] | None:
"""Return the list of available fan modes.""" """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 @property
def swing_mode(self) -> str | None: def swing_mode(self) -> str | None:
"""Return the swing setting.""" """Return the swing setting."""
return self.coordinator.data[self.unique_id]["swing_mode"] return self.coordinator.data.parsed[self.unique_id]["swing_mode"]
@property @property
def swing_modes(self) -> list[str] | None: def swing_modes(self) -> list[str] | None:
"""Return the list of available swing modes.""" """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 @property
def min_temp(self) -> float: def min_temp(self) -> float:
"""Return the minimum temperature.""" """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 @property
def max_temp(self) -> float: def max_temp(self) -> float:
"""Return the maximum temperature.""" """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 @property
def available(self) -> bool: def available(self) -> bool:
"""Return True if entity is available.""" """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: async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature.""" """Set new target temperature."""
if ( if (
"targetTemperature" "targetTemperature"
not in self.coordinator.data[self.unique_id]["active_features"] not in self.coordinator.data.parsed[self.unique_id]["active_features"]
): ):
raise HomeAssistantError( raise HomeAssistantError(
"Current mode doesn't support setting Target Temperature" "Current mode doesn't support setting Target Temperature"
@ -237,13 +240,23 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
if temperature == self.target_temperature: if temperature == self.target_temperature:
return 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. # Requested temperature is not supported.
if temperature > self.coordinator.data[self.unique_id]["temp_list"][-1]: if (
temperature = self.coordinator.data[self.unique_id]["temp_list"][-1] 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[self.unique_id]["temp_list"][0]: elif (
temperature = self.coordinator.data[self.unique_id]["temp_list"][0] temperature
< self.coordinator.data.parsed[self.unique_id]["temp_list"][0]
):
temperature = self.coordinator.data.parsed[self.unique_id]["temp_list"][
0
]
else: else:
return return
@ -252,7 +265,10 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
async def async_set_fan_mode(self, fan_mode: str) -> None: async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode.""" """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") raise HomeAssistantError("Current mode doesn't support setting Fanlevel")
await self._async_set_ac_state_property("fanLevel", fan_mode) await self._async_set_ac_state_property("fanLevel", fan_mode)
@ -264,7 +280,7 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
return return
# Turn on if not currently on. # 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("on", True)
await self._async_set_ac_state_property("mode", HA_TO_SENSIBO[hvac_mode]) 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: async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set new target swing operation.""" """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") raise HomeAssistantError("Current mode doesn't support setting Swing")
await self._async_set_ac_state_property("swing", swing_mode) await self._async_set_ac_state_property("swing", swing_mode)
@ -292,13 +311,13 @@ class SensiboClimate(SensiboBaseEntity, ClimateEntity):
params = { params = {
"name": name, "name": name,
"value": value, "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, "assumed_state": assumed_state,
} }
result = await self.async_send_command("set_ac_state", params) result = await self.async_send_command("set_ac_state", params)
if result["result"]["status"] == "Success": 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() self.async_write_ha_state()
return return

View File

@ -35,9 +35,19 @@ class MotionSensor:
model: str | None = None model: str | None = None
@dataclass
class SensiboData:
"""Dataclass for Sensibo data."""
raw: dict
parsed: dict
class SensiboDataUpdateCoordinator(DataUpdateCoordinator): class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
"""A Sensibo Data Update Coordinator.""" """A Sensibo Data Update Coordinator."""
data: SensiboData
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the Sensibo coordinator.""" """Initialize the Sensibo coordinator."""
self.client = SensiboClient( self.client = SensiboClient(
@ -52,7 +62,7 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL), 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.""" """Fetch data from Sensibo."""
devices = [] devices = []
@ -65,7 +75,10 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
except SensiboError as error: except SensiboError as error:
raise UpdateFailed from 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: for dev in devices:
unique_id = dev["id"] unique_id = dev["id"]
mac = dev["macAddress"] mac = dev["macAddress"]
@ -172,4 +185,5 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
"full_capabilities": capabilities, "full_capabilities": capabilities,
"motion_sensors": motion_sensors, "motion_sensors": motion_sensors,
} }
return device_data
return SensiboData(raw=data, parsed=device_data)

View File

@ -3,16 +3,34 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.components.diagnostics.util import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DOMAIN from .const import DOMAIN
from .coordinator import SensiboDataUpdateCoordinator 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( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
return coordinator.data return async_redact_data(coordinator.data.raw, TO_REDACT)

View File

@ -28,7 +28,7 @@ class SensiboBaseEntity(CoordinatorEntity):
super().__init__(coordinator) super().__init__(coordinator)
self._device_id = device_id self._device_id = device_id
self._client = coordinator.client self._client = coordinator.client
device = coordinator.data[device_id] device = coordinator.data.parsed[device_id]
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device["id"])}, identifiers={(DOMAIN, device["id"])},
name=device["name"], name=device["name"],

View File

@ -64,7 +64,7 @@ async def async_setup_entry(
async_add_entities( async_add_entities(
SensiboNumber(coordinator, device_id, description) 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 for description in NUMBER_TYPES
if device_data["hvac_modes"] and device_data["temp"] if device_data["hvac_modes"] and device_data["temp"]
) )
@ -86,20 +86,24 @@ class SensiboNumber(SensiboBaseEntity, NumberEntity):
self.entity_description = entity_description self.entity_description = entity_description
self._attr_unique_id = f"{device_id}-{entity_description.key}" self._attr_unique_id = f"{device_id}-{entity_description.key}"
self._attr_name = ( self._attr_name = (
f"{coordinator.data[device_id]['name']} {entity_description.name}" f"{coordinator.data.parsed[device_id]['name']} {entity_description.name}"
) )
@property @property
def value(self) -> float | None: def value(self) -> float | None:
"""Return the value from coordinator data.""" """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: async def async_set_value(self, value: float) -> None:
"""Set value for calibration.""" """Set value for calibration."""
data = {self.entity_description.remote_key: value} data = {self.entity_description.remote_key: value}
result = await self.async_send_command("set_calibration", {"data": data}) result = await self.async_send_command("set_calibration", {"data": data})
if result["status"] == "success": 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() self.async_write_ha_state()
return return
raise HomeAssistantError(f"Could not set calibration for device {self.name}") raise HomeAssistantError(f"Could not set calibration for device {self.name}")