diff --git a/homeassistant/components/sensibo/binary_sensor.py b/homeassistant/components/sensibo/binary_sensor.py index fef81c6d7c1..02f86dbe009 100644 --- a/homeassistant/components/sensibo/binary_sensor.py +++ b/homeassistant/components/sensibo/binary_sensor.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import Any + +from pysensibo.model import MotionSensor, SensiboDevice from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -15,8 +16,8 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN, LOGGER -from .coordinator import MotionSensor, SensiboDataUpdateCoordinator +from .const import DOMAIN +from .coordinator import SensiboDataUpdateCoordinator from .entity import SensiboDeviceBaseEntity, SensiboMotionBaseEntity @@ -31,7 +32,7 @@ class MotionBaseEntityDescriptionMixin: class DeviceBaseEntityDescriptionMixin: """Mixin for required Sensibo base description keys.""" - value_fn: Callable[[dict[str, Any]], bool | None] + value_fn: Callable[[SensiboDevice], bool | None] @dataclass @@ -79,7 +80,7 @@ DEVICE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.MOTION, name="Room Occupied", icon="mdi:motion-sensor", - value_fn=lambda data: data["room_occupied"], + value_fn=lambda data: data.room_occupied, ), SensiboDeviceBinarySensorEntityDescription( key="update_available", @@ -87,7 +88,7 @@ DEVICE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, name="Update Available", icon="mdi:rocket-launch", - value_fn=lambda data: data["update_available"], + value_fn=lambda data: data.update_available, ), ) @@ -100,22 +101,19 @@ async def async_setup_entry( coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] entities: list[SensiboMotionSensor | SensiboDeviceSensor] = [] - LOGGER.debug("parsed data: %s", coordinator.data.parsed) entities.extend( SensiboMotionSensor(coordinator, device_id, sensor_id, sensor_data, description) for device_id, device_data in coordinator.data.parsed.items() - for sensor_id, sensor_data in device_data["motion_sensors"].items() + for sensor_id, sensor_data in device_data.motion_sensors.items() for description in MOTION_SENSOR_TYPES - if device_data["motion_sensors"] + if device_data.motion_sensors ) - LOGGER.debug("start device %s", entities) entities.extend( SensiboDeviceSensor(coordinator, device_id, description) for description in DEVICE_SENSOR_TYPES for device_id, device_data in coordinator.data.parsed.items() - if device_data[description.key] is not None + if getattr(device_data, description.key) is not None ) - LOGGER.debug("list: %s", entities) async_add_entities(entities) @@ -144,7 +142,7 @@ class SensiboMotionSensor(SensiboMotionBaseEntity, BinarySensorEntity): self.entity_description = entity_description self._attr_unique_id = f"{sensor_id}-{entity_description.key}" self._attr_name = ( - f"{self.device_data['name']} Motion Sensor {entity_description.name}" + f"{self.device_data.name} Motion Sensor {entity_description.name}" ) @property @@ -171,7 +169,7 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, BinarySensorEntity): ) self.entity_description = entity_description self._attr_unique_id = f"{device_id}-{entity_description.key}" - self._attr_name = f"{self.device_data['name']} {entity_description.name}" + self._attr_name = f"{self.device_data.name} {entity_description.name}" @property def is_on(self) -> bool | None: diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index cec25d2a918..6e9d2709b8b 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -126,11 +126,9 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): """Initiate SensiboClimate.""" super().__init__(coordinator, device_id) self._attr_unique_id = device_id - self._attr_name = coordinator.data.parsed[device_id]["name"] + self._attr_name = self.device_data.name self._attr_temperature_unit = ( - TEMP_CELSIUS - if coordinator.data.parsed[device_id]["temp_unit"] == "C" - else TEMP_FAHRENHEIT + TEMP_CELSIUS if self.device_data.temp_unit == "C" else TEMP_FAHRENHEIT ) self._attr_supported_features = self.get_features() self._attr_precision = PRECISION_TENTHS @@ -138,7 +136,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): def get_features(self) -> int: """Get supported features.""" features = 0 - for key in self.coordinator.data.parsed[self.unique_id]["full_features"]: + for key in self.device_data.full_features: if key in FIELD_TO_FLAG: features |= FIELD_TO_FLAG[key] return features @@ -146,30 +144,27 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): @property def current_humidity(self) -> int | None: """Return the current humidity.""" - return self.coordinator.data.parsed[self.unique_id]["humidity"] + return self.device_data.humidity @property def hvac_mode(self) -> str: """Return hvac operation.""" return ( - SENSIBO_TO_HA[self.coordinator.data.parsed[self.unique_id]["hvac_mode"]] - if self.coordinator.data.parsed[self.unique_id]["on"] + SENSIBO_TO_HA[self.device_data.hvac_mode] + if self.device_data.device_on else HVAC_MODE_OFF ) @property def hvac_modes(self) -> list[str]: """Return the list of available hvac operation modes.""" - return [ - SENSIBO_TO_HA[mode] - for mode in self.coordinator.data.parsed[self.unique_id]["hvac_modes"] - ] + return [SENSIBO_TO_HA[mode] for mode in self.device_data.hvac_modes] @property def current_temperature(self) -> float | None: """Return the current temperature.""" return convert_temperature( - self.coordinator.data.parsed[self.unique_id]["temp"], + self.device_data.temp, TEMP_CELSIUS, self.temperature_unit, ) @@ -177,57 +172,51 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): @property def target_temperature(self) -> float | None: """Return the temperature we try to reach.""" - return self.coordinator.data.parsed[self.unique_id]["target_temp"] + return self.device_data.target_temp @property def target_temperature_step(self) -> float | None: """Return the supported step of target temperature.""" - return self.coordinator.data.parsed[self.unique_id]["temp_step"] + return self.device_data.temp_step @property def fan_mode(self) -> str | None: """Return the fan setting.""" - return self.coordinator.data.parsed[self.unique_id]["fan_mode"] + return self.device_data.fan_mode @property def fan_modes(self) -> list[str] | None: """Return the list of available fan modes.""" - return self.coordinator.data.parsed[self.unique_id]["fan_modes"] + return self.device_data.fan_modes @property def swing_mode(self) -> str | None: """Return the swing setting.""" - return self.coordinator.data.parsed[self.unique_id]["swing_mode"] + return self.device_data.swing_mode @property def swing_modes(self) -> list[str] | None: """Return the list of available swing modes.""" - return self.coordinator.data.parsed[self.unique_id]["swing_modes"] + return self.device_data.swing_modes @property def min_temp(self) -> float: """Return the minimum temperature.""" - return self.coordinator.data.parsed[self.unique_id]["temp_list"][0] + return self.device_data.temp_list[0] @property def max_temp(self) -> float: """Return the maximum temperature.""" - return self.coordinator.data.parsed[self.unique_id]["temp_list"][-1] + return self.device_data.temp_list[-1] @property def available(self) -> bool: """Return True if entity is available.""" - return ( - self.coordinator.data.parsed[self.unique_id]["available"] - and super().available - ) + return self.device_data.available and super().available async def async_set_temperature(self, **kwargs) -> None: """Set new target temperature.""" - if ( - "targetTemperature" - not in self.coordinator.data.parsed[self.unique_id]["active_features"] - ): + if "targetTemperature" not in self.device_data.active_features: raise HomeAssistantError( "Current mode doesn't support setting Target Temperature" ) @@ -238,23 +227,13 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): if temperature == self.target_temperature: return - if temperature not in self.coordinator.data.parsed[self.unique_id]["temp_list"]: + if temperature not in self.device_data.temp_list: # Requested temperature is not supported. - if ( - temperature - > self.coordinator.data.parsed[self.unique_id]["temp_list"][-1] - ): - temperature = self.coordinator.data.parsed[self.unique_id]["temp_list"][ - -1 - ] + if temperature > self.device_data.temp_list[-1]: + temperature = self.device_data.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 - ] + elif temperature < self.device_data.temp_list[0]: + temperature = self.device_data.temp_list[0] else: return @@ -263,10 +242,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new target fan mode.""" - if ( - "fanLevel" - not in self.coordinator.data.parsed[self.unique_id]["active_features"] - ): + if "fanLevel" not in self.device_data.active_features: raise HomeAssistantError("Current mode doesn't support setting Fanlevel") await self._async_set_ac_state_property("fanLevel", fan_mode) @@ -278,7 +254,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): return # Turn on if not currently on. - if not self.coordinator.data.parsed[self.unique_id]["on"]: + if not self.device_data.device_on: await self._async_set_ac_state_property("on", True) await self._async_set_ac_state_property("mode", HA_TO_SENSIBO[hvac_mode]) @@ -286,10 +262,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): async def async_set_swing_mode(self, swing_mode: str) -> None: """Set new target swing operation.""" - if ( - "swing" - not in self.coordinator.data.parsed[self.unique_id]["active_features"] - ): + if "swing" not in self.device_data.active_features: raise HomeAssistantError("Current mode doesn't support setting Swing") await self._async_set_ac_state_property("swing", swing_mode) @@ -309,13 +282,13 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): params = { "name": name, "value": value, - "ac_states": self.coordinator.data.parsed[self.unique_id]["ac_states"], + "ac_states": self.device_data.ac_states, "assumed_state": assumed_state, } result = await self.async_send_command("set_ac_state", params) if result["result"]["status"] == "Success": - self.coordinator.data.parsed[self.unique_id][AC_STATE_TO_DATA[name]] = value + setattr(self.device_data, 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 6aaf53a0e73..a0321bf611e 100644 --- a/homeassistant/components/sensibo/coordinator.py +++ b/homeassistant/components/sensibo/coordinator.py @@ -1,12 +1,11 @@ """DataUpdateCoordinator for the Sensibo integration.""" from __future__ import annotations -from dataclasses import dataclass from datetime import timedelta -from typing import Any from pysensibo import SensiboClient from pysensibo.exceptions import AuthenticationError, SensiboError +from pysensibo.model import SensiboData from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY @@ -17,33 +16,6 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT -MAX_POSSIBLE_STEP = 1000 - - -@dataclass -class MotionSensor: - """Dataclass for motionsensors.""" - - id: str - alive: bool | None = None - motion: bool | None = None - fw_ver: str | None = None - fw_type: str | None = None - is_main_sensor: bool | None = None - battery_voltage: int | None = None - humidity: int | None = None - temperature: float | None = None - model: str | None = None - rssi: int | None = None - - -@dataclass -class SensiboData: - """Dataclass for Sensibo data.""" - - raw: dict - parsed: dict - class SensiboDataUpdateCoordinator(DataUpdateCoordinator): """A Sensibo Data Update Coordinator.""" @@ -67,156 +39,13 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> SensiboData: """Fetch data from Sensibo.""" - devices = [] try: - data = await self.client.async_get_devices() - for dev in data["result"]: - devices.append(dev) + data = await self.client.async_get_devices_data() except AuthenticationError as error: raise ConfigEntryAuthFailed from error except SensiboError as error: raise UpdateFailed from error - if not devices: + if not data.raw: raise UpdateFailed("No devices found") - - device_data: dict[str, Any] = {} - for dev in devices: - unique_id = dev["id"] - mac = dev["macAddress"] - name = dev["room"]["name"] - temperature = dev["measurements"].get("temperature") - humidity = dev["measurements"].get("humidity") - ac_states = dev["acState"] - target_temperature = ac_states.get("targetTemperature") - hvac_mode = ac_states.get("mode") - running = ac_states.get("on") - fan_mode = ac_states.get("fanLevel") - swing_mode = ac_states.get("swing") - horizontal_swing_mode = ac_states.get("horizontalSwing") - light_mode = ac_states.get("light") - available = dev["connectionStatus"].get("isAlive", True) - capabilities = dev["remoteCapabilities"] - hvac_modes = list(capabilities["modes"]) - if hvac_modes: - hvac_modes.append("off") - current_capabilities = capabilities["modes"][ac_states.get("mode")] - fan_modes = current_capabilities.get("fanLevels") - swing_modes = current_capabilities.get("swing") - horizontal_swing_modes = current_capabilities.get("horizontalSwing") - light_modes = current_capabilities.get("light") - temperature_unit_key = dev.get("temperatureUnit") or ac_states.get( - "temperatureUnit" - ) - temperatures_list = ( - current_capabilities["temperatures"] - .get(temperature_unit_key, {}) - .get("values", [0, 1]) - ) - if temperatures_list: - diff = MAX_POSSIBLE_STEP - for i in range(len(temperatures_list) - 1): - if temperatures_list[i + 1] - temperatures_list[i] < diff: - diff = temperatures_list[i + 1] - temperatures_list[i] - temperature_step = diff - - active_features = list(ac_states) - full_features = set() - for mode in capabilities["modes"]: - if "temperatures" in capabilities["modes"][mode]: - full_features.add("targetTemperature") - if "swing" in capabilities["modes"][mode]: - full_features.add("swing") - if "fanLevels" in capabilities["modes"][mode]: - full_features.add("fanLevel") - if "horizontalSwing" in capabilities["modes"][mode]: - full_features.add("horizontalSwing") - if "light" in capabilities["modes"][mode]: - full_features.add("light") - - state = hvac_mode if hvac_mode else "off" - - fw_ver = dev["firmwareVersion"] - fw_type = dev["firmwareType"] - model = dev["productModel"] - - calibration_temp = dev["sensorsCalibration"].get("temperature") - calibration_hum = dev["sensorsCalibration"].get("humidity") - - # Sky plus supports functionality to use motion sensor as sensor for temp and humidity - if main_sensor := dev["mainMeasurementsSensor"]: - measurements = main_sensor["measurements"] - temperature = measurements.get("temperature") - humidity = measurements.get("humidity") - - motion_sensors: dict[str, Any] = {} - if dev["motionSensors"]: - for sensor in dev["motionSensors"]: - measurement = sensor["measurements"] - motion_sensors[sensor["id"]] = MotionSensor( - id=sensor["id"], - alive=sensor["connectionStatus"].get("isAlive"), - motion=measurement.get("motion"), - fw_ver=sensor.get("firmwareVersion"), - fw_type=sensor.get("firmwareType"), - is_main_sensor=sensor.get("isMainSensor"), - battery_voltage=measurement.get("batteryVoltage"), - humidity=measurement.get("humidity"), - temperature=measurement.get("temperature"), - model=sensor.get("productModel"), - rssi=measurement.get("rssi"), - ) - - # Add information for pure devices - pure_conf = dev["pureBoostConfig"] - pure_sensitivity = pure_conf.get("sensitivity") if pure_conf else None - pure_boost_enabled = pure_conf.get("enabled") if pure_conf else None - pm25 = dev["measurements"].get("pm25") - - # Binary sensors for main device - room_occupied = dev["roomIsOccupied"] - update_available = bool( - dev["firmwareVersion"] != dev["currentlyAvailableFirmwareVersion"] - ) - - device_data[unique_id] = { - "id": unique_id, - "mac": mac, - "name": name, - "ac_states": ac_states, - "temp": temperature, - "humidity": humidity, - "target_temp": target_temperature, - "hvac_mode": hvac_mode, - "on": running, - "fan_mode": fan_mode, - "swing_mode": swing_mode, - "horizontal_swing_mode": horizontal_swing_mode, - "light_mode": light_mode, - "available": available, - "hvac_modes": hvac_modes, - "fan_modes": fan_modes, - "swing_modes": swing_modes, - "horizontal_swing_modes": horizontal_swing_modes, - "light_modes": light_modes, - "temp_unit": temperature_unit_key, - "temp_list": temperatures_list, - "temp_step": temperature_step, - "active_features": active_features, - "full_features": full_features, - "state": state, - "fw_ver": fw_ver, - "fw_type": fw_type, - "model": model, - "calibration_temp": calibration_temp, - "calibration_hum": calibration_hum, - "full_capabilities": capabilities, - "motion_sensors": motion_sensors, - "pure_sensitivity": pure_sensitivity, - "pure_boost_enabled": pure_boost_enabled, - "pm25": pm25, - "room_occupied": room_occupied, - "update_available": update_available, - } - - return SensiboData(raw=data, parsed=device_data) + return data diff --git a/homeassistant/components/sensibo/entity.py b/homeassistant/components/sensibo/entity.py index bc0e2f49a2a..ce85ecf2a38 100644 --- a/homeassistant/components/sensibo/entity.py +++ b/homeassistant/components/sensibo/entity.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any import async_timeout +from pysensibo.model import MotionSensor, SensiboDevice from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC @@ -11,7 +12,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN, LOGGER, SENSIBO_ERRORS, TIMEOUT -from .coordinator import MotionSensor, SensiboDataUpdateCoordinator +from .coordinator import SensiboDataUpdateCoordinator class SensiboBaseEntity(CoordinatorEntity[SensiboDataUpdateCoordinator]): @@ -28,7 +29,7 @@ class SensiboBaseEntity(CoordinatorEntity[SensiboDataUpdateCoordinator]): self._client = coordinator.client @property - def device_data(self) -> dict[str, Any]: + def device_data(self) -> SensiboDevice: """Return data for device.""" return self.coordinator.data.parsed[self._device_id] @@ -44,15 +45,15 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity): """Initiate Sensibo Number.""" super().__init__(coordinator, device_id) self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, self.device_data["id"])}, - name=self.device_data["name"], - connections={(CONNECTION_NETWORK_MAC, self.device_data["mac"])}, + identifiers={(DOMAIN, self.device_data.id)}, + name=self.device_data.name, + connections={(CONNECTION_NETWORK_MAC, self.device_data.mac)}, manufacturer="Sensibo", configuration_url="https://home.sensibo.com/", - model=self.device_data["model"], - sw_version=self.device_data["fw_ver"], - hw_version=self.device_data["fw_type"], - suggested_area=self.device_data["name"], + model=self.device_data.model, + sw_version=self.device_data.fw_ver, + hw_version=self.device_data.fw_type, + suggested_area=self.device_data.name, ) async def async_send_command( @@ -108,7 +109,7 @@ class SensiboMotionBaseEntity(SensiboBaseEntity): self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, sensor_id)}, - name=f"{self.device_data['name']} Motion Sensor {name}", + name=f"{self.device_data.name} Motion Sensor {name}", via_device=(DOMAIN, device_id), manufacturer="Sensibo", configuration_url="https://home.sensibo.com/", @@ -120,4 +121,4 @@ class SensiboMotionBaseEntity(SensiboBaseEntity): @property def sensor_data(self) -> MotionSensor: """Return data for device.""" - return self.device_data["motion_sensors"][self._sensor_id] + return self.device_data.motion_sensors[self._sensor_id] diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index f0132833d70..8313ddd4077 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.8"], + "requirements": ["pysensibo==1.0.9"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/homeassistant/components/sensibo/number.py b/homeassistant/components/sensibo/number.py index 001269ae168..69d9237da7a 100644 --- a/homeassistant/components/sensibo/number.py +++ b/homeassistant/components/sensibo/number.py @@ -84,25 +84,19 @@ class SensiboNumber(SensiboDeviceBaseEntity, NumberEntity): super().__init__(coordinator, device_id) self.entity_description = entity_description self._attr_unique_id = f"{device_id}-{entity_description.key}" - self._attr_name = ( - f"{coordinator.data.parsed[device_id]['name']} {entity_description.name}" - ) + self._attr_name = f"{self.device_data.name} {entity_description.name}" @property def value(self) -> float | None: """Return the value from coordinator data.""" - return self.coordinator.data.parsed[self._device_id][ - self.entity_description.key - ] + return getattr(self.device_data, 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.parsed[self._device_id][ - self.entity_description.key - ] = value + setattr(self.device_data, self.entity_description.key, value) self.async_write_ha_state() return raise HomeAssistantError(f"Could not set calibration for device {self.name}") diff --git a/homeassistant/components/sensibo/select.py b/homeassistant/components/sensibo/select.py index d211a9dd223..c442fbc1374 100644 --- a/homeassistant/components/sensibo/select.py +++ b/homeassistant/components/sensibo/select.py @@ -58,7 +58,7 @@ async def async_setup_entry( SensiboSelect(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() for description in SELECT_TYPES - if description.key in device_data["full_features"] + if description.key in device_data.full_features ) @@ -77,37 +77,35 @@ class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity): super().__init__(coordinator, device_id) self.entity_description = entity_description self._attr_unique_id = f"{device_id}-{entity_description.key}" - self._attr_name = ( - f"{coordinator.data.parsed[device_id]['name']} {entity_description.name}" - ) + self._attr_name = f"{self.device_data.name} {entity_description.name}" @property def current_option(self) -> str | None: """Return the current selected option.""" - return self.device_data[self.entity_description.remote_key] + return getattr(self.device_data, self.entity_description.remote_key) @property def options(self) -> list[str]: """Return possible options.""" - return self.device_data[self.entity_description.remote_options] or [] + return getattr(self.device_data, self.entity_description.remote_options) or [] async def async_select_option(self, option: str) -> None: """Set state to the selected option.""" - if self.entity_description.key not in self.device_data["active_features"]: + if self.entity_description.key not in self.device_data.active_features: raise HomeAssistantError( - f"Current mode {self.device_data['hvac_mode']} doesn't support setting {self.entity_description.name}" + f"Current mode {self.device_data.hvac_mode} doesn't support setting {self.entity_description.name}" ) params = { "name": self.entity_description.key, "value": option, - "ac_states": self.device_data["ac_states"], + "ac_states": self.device_data.ac_states, "assumed_state": False, } result = await self.async_send_command("set_ac_state", params) if result["result"]["status"] == "Success": - self.device_data[self.entity_description.remote_key] = option + setattr(self.device_data, self.entity_description.remote_key, option) self.async_write_ha_state() return diff --git a/homeassistant/components/sensibo/sensor.py b/homeassistant/components/sensibo/sensor.py index 21f2bf2b5da..307cfac6003 100644 --- a/homeassistant/components/sensibo/sensor.py +++ b/homeassistant/components/sensibo/sensor.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import Any + +from pysensibo.model import MotionSensor, SensiboDevice from homeassistant.components.sensor import ( SensorDeviceClass, @@ -25,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from .const import DOMAIN -from .coordinator import MotionSensor, SensiboDataUpdateCoordinator +from .coordinator import SensiboDataUpdateCoordinator from .entity import SensiboDeviceBaseEntity, SensiboMotionBaseEntity @@ -40,7 +41,7 @@ class MotionBaseEntityDescriptionMixin: class DeviceBaseEntityDescriptionMixin: """Mixin for required Sensibo base description keys.""" - value_fn: Callable[[dict[str, Any]], StateType] + value_fn: Callable[[SensiboDevice], StateType] @dataclass @@ -106,13 +107,13 @@ DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( state_class=SensorStateClass.MEASUREMENT, name="PM2.5", icon="mdi:air-filter", - value_fn=lambda data: data["pm25"], + value_fn=lambda data: data.pm25, ), SensiboDeviceSensorEntityDescription( key="pure_sensitivity", name="Pure Sensitivity", icon="mdi:air-filter", - value_fn=lambda data: data["pure_sensitivity"], + value_fn=lambda data: data.pure_sensitivity, ), ) @@ -129,15 +130,15 @@ async def async_setup_entry( entities.extend( SensiboMotionSensor(coordinator, device_id, sensor_id, sensor_data, description) for device_id, device_data in coordinator.data.parsed.items() - for sensor_id, sensor_data in device_data["motion_sensors"].items() + for sensor_id, sensor_data in device_data.motion_sensors.items() for description in MOTION_SENSOR_TYPES - if device_data["motion_sensors"] + if device_data.motion_sensors ) entities.extend( SensiboDeviceSensor(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() for description in DEVICE_SENSOR_TYPES - if device_data[description.key] is not None + if getattr(device_data, description.key) is not None ) async_add_entities(entities) @@ -166,7 +167,7 @@ class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity): self.entity_description = entity_description self._attr_unique_id = f"{sensor_id}-{entity_description.key}" self._attr_name = ( - f"{self.device_data['name']} Motion Sensor {entity_description.name}" + f"{self.device_data.name} Motion Sensor {entity_description.name}" ) @property @@ -193,7 +194,7 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, SensorEntity): ) self.entity_description = entity_description self._attr_unique_id = f"{device_id}-{entity_description.key}" - self._attr_name = f"{self.device_data['name']} {entity_description.name}" + self._attr_name = f"{self.device_data.name} {entity_description.name}" @property def native_value(self) -> StateType: diff --git a/requirements_all.txt b/requirements_all.txt index ad14a435446..6bbd9c38ce1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1767,7 +1767,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.8 +pysensibo==1.0.9 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c7cda0ac7de..014b1acdf81 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1169,7 +1169,7 @@ pyrituals==0.0.6 pyruckus==0.12 # homeassistant.components.sensibo -pysensibo==1.0.8 +pysensibo==1.0.9 # homeassistant.components.serial # homeassistant.components.zha