Improve data handling for Sensibo (#68419)

This commit is contained in:
G Johansson 2022-03-24 22:15:08 +01:00 committed by GitHub
parent 5fffe9b22f
commit d23d19f9e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 81 additions and 287 deletions

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -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",

View file

@ -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}")

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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