Sensibo clean code (#74437)
This commit is contained in:
parent
b3596fdea1
commit
03d804123a
19 changed files with 425 additions and 319 deletions
|
@ -1,4 +1,4 @@
|
|||
"""The sensibo component."""
|
||||
"""The Sensibo component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from pysensibo.exceptions import AuthenticationError
|
||||
|
|
|
@ -65,7 +65,6 @@ MOTION_SENSOR_TYPES: tuple[SensiboMotionBinarySensorEntityDescription, ...] = (
|
|||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="Alive",
|
||||
icon="mdi:wifi",
|
||||
value_fn=lambda data: data.alive,
|
||||
),
|
||||
SensiboMotionBinarySensorEntityDescription(
|
||||
|
@ -104,7 +103,6 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
name="Pure Boost linked with AC",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_ac_integration,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
|
@ -112,7 +110,6 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
name="Pure Boost linked with presence",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_geo_integration,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
|
@ -120,7 +117,6 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
name="Pure Boost linked with indoor air quality",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_measure_integration,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
|
@ -128,12 +124,13 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
name="Pure Boost linked with outdoor air quality",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_prime_integration,
|
||||
),
|
||||
FILTER_CLEAN_REQUIRED_DESCRIPTION,
|
||||
)
|
||||
|
||||
DESCRIPTION_BY_MODELS = {"pure": PURE_SENSOR_TYPES}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
|
@ -161,15 +158,10 @@ async def async_setup_entry(
|
|||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for description in PURE_SENSOR_TYPES
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
if device_data.model == "pure"
|
||||
)
|
||||
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.model != "pure"
|
||||
for description in DESCRIPTION_BY_MODELS.get(
|
||||
device_data.model, DEVICE_SENSOR_TYPES
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
|
|
@ -1,24 +1,44 @@
|
|||
"""Button platform for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from pysensibo.model import SensiboDevice
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
from .entity import SensiboDeviceBaseEntity
|
||||
from .entity import SensiboDeviceBaseEntity, async_handle_api_call
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
DEVICE_BUTTON_TYPES: ButtonEntityDescription = ButtonEntityDescription(
|
||||
|
||||
@dataclass
|
||||
class SensiboEntityDescriptionMixin:
|
||||
"""Mixin values for Sensibo entities."""
|
||||
|
||||
data_key: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class SensiboButtonEntityDescription(
|
||||
ButtonEntityDescription, SensiboEntityDescriptionMixin
|
||||
):
|
||||
"""Class describing Sensibo Button entities."""
|
||||
|
||||
|
||||
DEVICE_BUTTON_TYPES = SensiboButtonEntityDescription(
|
||||
key="reset_filter",
|
||||
name="Reset filter",
|
||||
icon="mdi:air-filter",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
data_key="filter_clean",
|
||||
)
|
||||
|
||||
|
||||
|
@ -29,26 +49,22 @@ async def async_setup_entry(
|
|||
|
||||
coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities: list[SensiboDeviceButton] = []
|
||||
|
||||
entities.extend(
|
||||
async_add_entities(
|
||||
SensiboDeviceButton(coordinator, device_id, DEVICE_BUTTON_TYPES)
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SensiboDeviceButton(SensiboDeviceBaseEntity, ButtonEntity):
|
||||
"""Representation of a Sensibo Device Binary Sensor."""
|
||||
|
||||
entity_description: ButtonEntityDescription
|
||||
entity_description: SensiboButtonEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SensiboDataUpdateCoordinator,
|
||||
device_id: str,
|
||||
entity_description: ButtonEntityDescription,
|
||||
entity_description: SensiboButtonEntityDescription,
|
||||
) -> None:
|
||||
"""Initiate Sensibo Device Button."""
|
||||
super().__init__(
|
||||
|
@ -60,8 +76,18 @@ class SensiboDeviceButton(SensiboDeviceBaseEntity, ButtonEntity):
|
|||
|
||||
async def async_press(self) -> None:
|
||||
"""Press the button."""
|
||||
result = await self.async_send_command("reset_filter")
|
||||
if result["status"] == "success":
|
||||
await self.coordinator.async_request_refresh()
|
||||
return
|
||||
raise HomeAssistantError(f"Could not set calibration for device {self.name}")
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=False,
|
||||
)
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_send_api_call(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
result = await self._client.async_reset_filter(
|
||||
self._device_id,
|
||||
)
|
||||
return bool(result.get("status") == "success")
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
from bisect import bisect_left
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pysensibo.model import SensiboDevice
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
|
@ -24,7 +25,7 @@ from homeassistant.util.temperature import convert as convert_temperature
|
|||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
from .entity import SensiboDeviceBaseEntity
|
||||
from .entity import SensiboDeviceBaseEntity, async_handle_api_call
|
||||
|
||||
SERVICE_ASSUME_STATE = "assume_state"
|
||||
SERVICE_ENABLE_TIMER = "enable_timer"
|
||||
|
@ -123,7 +124,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
def __init__(
|
||||
self, coordinator: SensiboDataUpdateCoordinator, device_id: str
|
||||
) -> None:
|
||||
"""Initiate SensiboClimate."""
|
||||
"""Initiate Sensibo Climate."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self._attr_unique_id = device_id
|
||||
self._attr_temperature_unit = (
|
||||
|
@ -173,6 +174,11 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def temperature_unit(self) -> str:
|
||||
"""Return temperature unit."""
|
||||
return TEMP_CELSIUS if self.device_data.temp_unit == "C" else TEMP_FAHRENHEIT
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
|
@ -242,69 +248,99 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
return
|
||||
|
||||
new_temp = _find_valid_target_temp(temperature, self.device_data.temp_list)
|
||||
await self._async_set_ac_state_property("targetTemperature", new_temp)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["targetTemperature"],
|
||||
value=new_temp,
|
||||
name="targetTemperature",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set new target fan mode."""
|
||||
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)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["fanLevel"],
|
||||
value=fan_mode,
|
||||
name="fanLevel",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set new target operation mode."""
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
await self._async_set_ac_state_property("on", False)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["on"],
|
||||
value=False,
|
||||
name="on",
|
||||
assumed_state=False,
|
||||
)
|
||||
return
|
||||
|
||||
# Turn on if not currently on.
|
||||
if not self.device_data.device_on:
|
||||
await self._async_set_ac_state_property("on", True)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["on"],
|
||||
value=True,
|
||||
name="on",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
await self._async_set_ac_state_property("mode", HA_TO_SENSIBO[hvac_mode])
|
||||
await self.coordinator.async_request_refresh()
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["mode"],
|
||||
value=HA_TO_SENSIBO[hvac_mode],
|
||||
name="mode",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_set_swing_mode(self, swing_mode: str) -> None:
|
||||
"""Set new target swing operation."""
|
||||
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)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["swing"],
|
||||
value=swing_mode,
|
||||
name="swing",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn Sensibo unit on."""
|
||||
await self._async_set_ac_state_property("on", True)
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["on"],
|
||||
value=True,
|
||||
name="on",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn Sensibo unit on."""
|
||||
await self._async_set_ac_state_property("on", False)
|
||||
|
||||
async def _async_set_ac_state_property(
|
||||
self, name: str, value: str | int | bool, assumed_state: bool = False
|
||||
) -> None:
|
||||
"""Set AC state."""
|
||||
params = {
|
||||
"name": name,
|
||||
"value": value,
|
||||
"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":
|
||||
setattr(self.device_data, AC_STATE_TO_DATA[name], value)
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
failure = result["result"]["failureReason"]
|
||||
raise HomeAssistantError(
|
||||
f"Could not set state for device {self.name} due to reason {failure}"
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["on"],
|
||||
value=False,
|
||||
name="on",
|
||||
assumed_state=False,
|
||||
)
|
||||
|
||||
async def async_assume_state(self, state: str) -> None:
|
||||
"""Sync state with api."""
|
||||
await self._async_set_ac_state_property("on", state != HVACMode.OFF, True)
|
||||
await self.coordinator.async_refresh()
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=AC_STATE_TO_DATA["on"],
|
||||
value=state != HVACMode.OFF,
|
||||
name="on",
|
||||
assumed_state=True,
|
||||
)
|
||||
|
||||
async def async_enable_timer(self, minutes: int) -> None:
|
||||
"""Enable the timer."""
|
||||
|
@ -313,11 +349,13 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
"minutesFromNow": minutes,
|
||||
"acState": {**self.device_data.ac_states, "on": new_state},
|
||||
}
|
||||
result = await self.async_send_command("set_timer", params)
|
||||
|
||||
if result["status"] == "success":
|
||||
return await self.coordinator.async_request_refresh()
|
||||
raise HomeAssistantError(f"Could not enable timer for device {self.name}")
|
||||
await self.api_call_custom_service_timer(
|
||||
device_data=self.device_data,
|
||||
key="timer_on",
|
||||
value=True,
|
||||
command="set_timer",
|
||||
data=params,
|
||||
)
|
||||
|
||||
async def async_enable_pure_boost(
|
||||
self,
|
||||
|
@ -343,5 +381,57 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
if outdoor_integration is not None:
|
||||
params["primeIntegration"] = outdoor_integration
|
||||
|
||||
await self.async_send_command("set_pure_boost", params)
|
||||
await self.coordinator.async_refresh()
|
||||
await self.api_call_custom_service_pure_boost(
|
||||
device_data=self.device_data,
|
||||
key="pure_boost_enabled",
|
||||
value=True,
|
||||
command="set_pure_boost",
|
||||
data=params,
|
||||
)
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_send_api_call(
|
||||
self,
|
||||
device_data: SensiboDevice,
|
||||
key: Any,
|
||||
value: Any,
|
||||
name: str,
|
||||
assumed_state: bool = False,
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
result = await self._client.async_set_ac_state_property(
|
||||
self._device_id,
|
||||
name,
|
||||
value,
|
||||
self.device_data.ac_states,
|
||||
assumed_state,
|
||||
)
|
||||
return bool(result.get("result", {}).get("status") == "Success")
|
||||
|
||||
@async_handle_api_call
|
||||
async def api_call_custom_service_timer(
|
||||
self,
|
||||
device_data: SensiboDevice,
|
||||
key: Any,
|
||||
value: Any,
|
||||
command: str,
|
||||
data: dict,
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
result = {}
|
||||
result = await self._client.async_set_timer(self._device_id, data)
|
||||
return bool(result.get("status") == "success")
|
||||
|
||||
@async_handle_api_call
|
||||
async def api_call_custom_service_pure_boost(
|
||||
self,
|
||||
device_data: SensiboDevice,
|
||||
key: Any,
|
||||
value: Any,
|
||||
command: str,
|
||||
data: dict,
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
result = {}
|
||||
result = await self._client.async_set_pureboost(self._device_id, data)
|
||||
return bool(result.get("status") == "success")
|
||||
|
|
|
@ -10,14 +10,14 @@ import voluptuous as vol
|
|||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.selector import TextSelector
|
||||
|
||||
from .const import DEFAULT_NAME, DOMAIN
|
||||
from .util import NoDevicesError, NoUsernameError, async_validate_api
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Required(CONF_API_KEY): TextSelector(),
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -12,10 +12,13 @@ from homeassistant.const import CONF_API_KEY
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT
|
||||
|
||||
REQUEST_REFRESH_DELAY = 0.35
|
||||
|
||||
|
||||
class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""A Sensibo Data Update Coordinator."""
|
||||
|
@ -34,6 +37,11 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
|
||||
# We don't want an immediate refresh since the device
|
||||
# takes a moment to reflect the state change
|
||||
request_refresh_debouncer=Debouncer(
|
||||
hass, LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
|
||||
),
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> SensiboData:
|
||||
|
|
|
@ -31,6 +31,6 @@ TO_REDACT = {
|
|||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
"""Return diagnostics for Sensibo config entry."""
|
||||
coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
return async_redact_data(coordinator.data.raw, TO_REDACT)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
"""Base entity for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import TYPE_CHECKING, Any, TypeVar
|
||||
|
||||
import async_timeout
|
||||
from pysensibo.model import MotionSensor, SensiboDevice
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
|
@ -14,9 +16,39 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|||
from .const import DOMAIN, LOGGER, SENSIBO_ERRORS, TIMEOUT
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
|
||||
_T = TypeVar("_T", bound="SensiboDeviceBaseEntity")
|
||||
_P = ParamSpec("_P")
|
||||
|
||||
|
||||
def async_handle_api_call(
|
||||
function: Callable[Concatenate[_T, _P], Coroutine[Any, Any, Any]]
|
||||
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, Any]]:
|
||||
"""Decorate api calls."""
|
||||
|
||||
async def wrap_api_call(*args: Any, **kwargs: Any) -> None:
|
||||
"""Wrap services for api calls."""
|
||||
res: bool = False
|
||||
try:
|
||||
async with async_timeout.timeout(TIMEOUT):
|
||||
res = await function(*args, **kwargs)
|
||||
except SENSIBO_ERRORS as err:
|
||||
raise HomeAssistantError from err
|
||||
|
||||
LOGGER.debug("Result %s for entity %s with arguments %s", res, args[0], kwargs)
|
||||
entity: SensiboDeviceBaseEntity = args[0]
|
||||
if res is not True:
|
||||
raise HomeAssistantError(f"Could not execute service for {entity.name}")
|
||||
if kwargs.get("key") is not None and kwargs.get("value") is not None:
|
||||
setattr(entity.device_data, kwargs["key"], kwargs["value"])
|
||||
LOGGER.debug("Debug check key %s is now %s", kwargs["key"], kwargs["value"])
|
||||
entity.async_write_ha_state()
|
||||
await entity.coordinator.async_request_refresh()
|
||||
|
||||
return wrap_api_call
|
||||
|
||||
|
||||
class SensiboBaseEntity(CoordinatorEntity[SensiboDataUpdateCoordinator]):
|
||||
"""Representation of a Sensibo entity."""
|
||||
"""Representation of a Sensibo Base Entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -35,7 +67,7 @@ class SensiboBaseEntity(CoordinatorEntity[SensiboDataUpdateCoordinator]):
|
|||
|
||||
|
||||
class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
||||
"""Representation of a Sensibo device."""
|
||||
"""Representation of a Sensibo Device."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
|
@ -44,7 +76,7 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
|||
coordinator: SensiboDataUpdateCoordinator,
|
||||
device_id: str,
|
||||
) -> None:
|
||||
"""Initiate Sensibo Number."""
|
||||
"""Initiate Sensibo Device."""
|
||||
super().__init__(coordinator, device_id)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self.device_data.id)},
|
||||
|
@ -58,63 +90,9 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
|||
suggested_area=self.device_data.name,
|
||||
)
|
||||
|
||||
async def async_send_command(
|
||||
self, command: str, params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Send command to Sensibo api."""
|
||||
try:
|
||||
async with async_timeout.timeout(TIMEOUT):
|
||||
result = await self.async_send_api_call(command, params)
|
||||
except SENSIBO_ERRORS as err:
|
||||
raise HomeAssistantError(
|
||||
f"Failed to send command {command} for device {self.name} to Sensibo servers: {err}"
|
||||
) from err
|
||||
|
||||
LOGGER.debug("Result: %s", result)
|
||||
return result
|
||||
|
||||
async def async_send_api_call(
|
||||
self, command: str, params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Send api call."""
|
||||
result: dict[str, Any] = {"status": None}
|
||||
if command == "set_calibration":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_calibration(
|
||||
self._device_id,
|
||||
params["data"],
|
||||
)
|
||||
if command == "set_ac_state":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_ac_state_property(
|
||||
self._device_id,
|
||||
params["name"],
|
||||
params["value"],
|
||||
params["ac_states"],
|
||||
params["assumed_state"],
|
||||
)
|
||||
if command == "set_timer":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_timer(self._device_id, params)
|
||||
if command == "del_timer":
|
||||
result = await self._client.async_del_timer(self._device_id)
|
||||
if command == "set_pure_boost":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_pureboost(
|
||||
self._device_id,
|
||||
params,
|
||||
)
|
||||
if command == "reset_filter":
|
||||
result = await self._client.async_reset_filter(self._device_id)
|
||||
return result
|
||||
|
||||
|
||||
class SensiboMotionBaseEntity(SensiboBaseEntity):
|
||||
"""Representation of a Sensibo motion entity."""
|
||||
"""Representation of a Sensibo Motion Entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
|
@ -141,7 +119,7 @@ class SensiboMotionBaseEntity(SensiboBaseEntity):
|
|||
|
||||
@property
|
||||
def sensor_data(self) -> MotionSensor | None:
|
||||
"""Return data for device."""
|
||||
"""Return data for Motion Sensor."""
|
||||
if TYPE_CHECKING:
|
||||
assert self.device_data.motion_sensors
|
||||
return self.device_data.motion_sensors[self._sensor_id]
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
"""Number platform for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from pysensibo.model import SensiboDevice
|
||||
|
||||
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
from .entity import SensiboDeviceBaseEntity
|
||||
from .entity import SensiboDeviceBaseEntity, async_handle_api_call
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
@ -22,6 +25,7 @@ class SensiboEntityDescriptionMixin:
|
|||
"""Mixin values for Sensibo entities."""
|
||||
|
||||
remote_key: str
|
||||
value_fn: Callable[[SensiboDevice], float | None]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -42,6 +46,7 @@ DEVICE_NUMBER_TYPES = (
|
|||
native_min_value=-10,
|
||||
native_max_value=10,
|
||||
native_step=0.1,
|
||||
value_fn=lambda data: data.calibration_temp,
|
||||
),
|
||||
SensiboNumberEntityDescription(
|
||||
key="calibration_hum",
|
||||
|
@ -53,6 +58,7 @@ DEVICE_NUMBER_TYPES = (
|
|||
native_min_value=-10,
|
||||
native_max_value=10,
|
||||
native_step=0.1,
|
||||
value_fn=lambda data: data.calibration_hum,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -90,15 +96,22 @@ class SensiboNumber(SensiboDeviceBaseEntity, NumberEntity):
|
|||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the value from coordinator data."""
|
||||
value: float | None = getattr(self.device_data, self.entity_description.key)
|
||||
return value
|
||||
return self.entity_description.value_fn(self.device_data)
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set value for calibration."""
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data, key=self.entity_description.key, value=value
|
||||
)
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_send_api_call(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
data = {self.entity_description.remote_key: value}
|
||||
result = await self.async_send_command("set_calibration", {"data": data})
|
||||
if result["status"] == "success":
|
||||
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}")
|
||||
result = await self._client.async_set_calibration(
|
||||
self._device_id,
|
||||
data,
|
||||
)
|
||||
return bool(result.get("status") == "success")
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
"""Number platform for Sensibo integration."""
|
||||
"""Select platform for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pysensibo.model import SensiboDevice
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -11,7 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
from .entity import SensiboDeviceBaseEntity
|
||||
from .entity import SensiboDeviceBaseEntity, async_handle_api_call
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
@ -20,31 +24,34 @@ PARALLEL_UPDATES = 0
|
|||
class SensiboSelectDescriptionMixin:
|
||||
"""Mixin values for Sensibo entities."""
|
||||
|
||||
remote_key: str
|
||||
remote_options: str
|
||||
data_key: str
|
||||
value_fn: Callable[[SensiboDevice], str | None]
|
||||
options_fn: Callable[[SensiboDevice], list[str] | None]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SensiboSelectEntityDescription(
|
||||
SelectEntityDescription, SensiboSelectDescriptionMixin
|
||||
):
|
||||
"""Class describing Sensibo Number entities."""
|
||||
"""Class describing Sensibo Select entities."""
|
||||
|
||||
|
||||
DEVICE_SELECT_TYPES = (
|
||||
SensiboSelectEntityDescription(
|
||||
key="horizontalSwing",
|
||||
remote_key="horizontal_swing_mode",
|
||||
remote_options="horizontal_swing_modes",
|
||||
data_key="horizontal_swing_mode",
|
||||
name="Horizontal swing",
|
||||
icon="mdi:air-conditioner",
|
||||
value_fn=lambda data: data.horizontal_swing_mode,
|
||||
options_fn=lambda data: data.horizontal_swing_modes,
|
||||
),
|
||||
SensiboSelectEntityDescription(
|
||||
key="light",
|
||||
remote_key="light_mode",
|
||||
remote_options="light_modes",
|
||||
data_key="light_mode",
|
||||
name="Light",
|
||||
icon="mdi:flashlight",
|
||||
value_fn=lambda data: data.light_mode,
|
||||
options_fn=lambda data: data.light_modes,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -83,15 +90,15 @@ class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity):
|
|||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the current selected option."""
|
||||
option: str | None = getattr(
|
||||
self.device_data, self.entity_description.remote_key
|
||||
)
|
||||
return option
|
||||
return self.entity_description.value_fn(self.device_data)
|
||||
|
||||
@property
|
||||
def options(self) -> list[str]:
|
||||
"""Return possible options."""
|
||||
return getattr(self.device_data, self.entity_description.remote_options) or []
|
||||
options = self.entity_description.options_fn(self.device_data)
|
||||
if TYPE_CHECKING:
|
||||
assert options is not None
|
||||
return options
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Set state to the selected option."""
|
||||
|
@ -100,20 +107,28 @@ class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity):
|
|||
f"Current mode {self.device_data.hvac_mode} doesn't support setting {self.entity_description.name}"
|
||||
)
|
||||
|
||||
params = {
|
||||
await self.async_send_api_call(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=option,
|
||||
)
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_send_api_call(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api."""
|
||||
data = {
|
||||
"name": self.entity_description.key,
|
||||
"value": option,
|
||||
"value": value,
|
||||
"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":
|
||||
setattr(self.device_data, self.entity_description.remote_key, option)
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
failure = result["result"]["failureReason"]
|
||||
raise HomeAssistantError(
|
||||
f"Could not set state for device {self.name} due to reason {failure}"
|
||||
result = await self._client.async_set_ac_state_property(
|
||||
self._device_id,
|
||||
data["name"],
|
||||
data["value"],
|
||||
data["ac_states"],
|
||||
data["assumed_state"],
|
||||
)
|
||||
return bool(result.get("result", {}).get("status") == "Success")
|
||||
|
|
|
@ -63,7 +63,7 @@ class SensiboMotionSensorEntityDescription(
|
|||
class SensiboDeviceSensorEntityDescription(
|
||||
SensorEntityDescription, DeviceBaseEntityDescriptionMixin
|
||||
):
|
||||
"""Describes Sensibo Motion sensor entity."""
|
||||
"""Describes Sensibo Device sensor entity."""
|
||||
|
||||
|
||||
FILTER_LAST_RESET_DESCRIPTION = SensiboDeviceSensorEntityDescription(
|
||||
|
@ -178,6 +178,8 @@ AIRQ_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = (
|
|||
),
|
||||
)
|
||||
|
||||
DESCRIPTION_BY_MODELS = {"pure": PURE_SENSOR_TYPES, "airq": AIRQ_SENSOR_TYPES}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
|
@ -200,20 +202,9 @@ async def async_setup_entry(
|
|||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
for description in PURE_SENSOR_TYPES
|
||||
if device_data.model == "pure"
|
||||
)
|
||||
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.model != "pure"
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
for description in AIRQ_SENSOR_TYPES
|
||||
if device_data.model == "airq"
|
||||
for description in DESCRIPTION_BY_MODELS.get(
|
||||
device_data.model, DEVICE_SENSOR_TYPES
|
||||
)
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
|
|
@ -15,11 +15,17 @@
|
|||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "Follow the documentation to get your api key."
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "Follow the documentation to get a new api key."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,25 +14,24 @@ from homeassistant.components.switch import (
|
|||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SensiboDataUpdateCoordinator
|
||||
from .entity import SensiboDeviceBaseEntity
|
||||
from .entity import SensiboDeviceBaseEntity, async_handle_api_call
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeviceBaseEntityDescriptionMixin:
|
||||
"""Mixin for required Sensibo base description keys."""
|
||||
"""Mixin for required Sensibo Device description keys."""
|
||||
|
||||
value_fn: Callable[[SensiboDevice], bool | None]
|
||||
extra_fn: Callable[[SensiboDevice], dict[str, str | bool | None]] | None
|
||||
command_on: str
|
||||
command_off: str
|
||||
remote_key: str
|
||||
data_key: str
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -52,7 +51,7 @@ DEVICE_SWITCH_TYPES: tuple[SensiboDeviceSwitchEntityDescription, ...] = (
|
|||
extra_fn=lambda data: {"id": data.timer_id, "turn_on": data.timer_state_on},
|
||||
command_on="set_timer",
|
||||
command_off="del_timer",
|
||||
remote_key="timer_on",
|
||||
data_key="timer_on",
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -65,56 +64,27 @@ PURE_SWITCH_TYPES: tuple[SensiboDeviceSwitchEntityDescription, ...] = (
|
|||
extra_fn=None,
|
||||
command_on="set_pure_boost",
|
||||
command_off="set_pure_boost",
|
||||
remote_key="pure_boost_enabled",
|
||||
data_key="pure_boost_enabled",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def build_params(command: str, device_data: SensiboDevice) -> dict[str, Any] | None:
|
||||
"""Build params for turning on switch."""
|
||||
if command == "set_timer":
|
||||
new_state = bool(device_data.ac_states["on"] is False)
|
||||
params = {
|
||||
"minutesFromNow": 60,
|
||||
"acState": {**device_data.ac_states, "on": new_state},
|
||||
}
|
||||
return params
|
||||
if command == "set_pure_boost":
|
||||
new_state = bool(device_data.pure_boost_enabled is False)
|
||||
params = {"enabled": new_state}
|
||||
if device_data.pure_measure_integration is None:
|
||||
params["sensitivity"] = "N"
|
||||
params["measurementsIntegration"] = True
|
||||
params["acIntegration"] = False
|
||||
params["geoIntegration"] = False
|
||||
params["primeIntegration"] = False
|
||||
return params
|
||||
return None
|
||||
DESCRIPTION_BY_MODELS = {"pure": PURE_SWITCH_TYPES}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Sensibo binary sensor platform."""
|
||||
"""Set up Sensibo Switch platform."""
|
||||
|
||||
coordinator: SensiboDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities: list[SensiboDeviceSwitch] = []
|
||||
|
||||
entities.extend(
|
||||
async_add_entities(
|
||||
SensiboDeviceSwitch(coordinator, device_id, description)
|
||||
for description in DEVICE_SWITCH_TYPES
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
if device_data.model != "pure"
|
||||
for description in DESCRIPTION_BY_MODELS.get(
|
||||
device_data.model, DEVICE_SWITCH_TYPES
|
||||
)
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSwitch(coordinator, device_id, description)
|
||||
for description in PURE_SWITCH_TYPES
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
if device_data.model == "pure"
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
||||
|
@ -143,33 +113,33 @@ class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
|||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity on."""
|
||||
params = build_params(self.entity_description.command_on, self.device_data)
|
||||
result = await self.async_send_command(
|
||||
self.entity_description.command_on, params
|
||||
)
|
||||
|
||||
if result["status"] == "success":
|
||||
setattr(self.device_data, self.entity_description.remote_key, True)
|
||||
self.async_write_ha_state()
|
||||
return await self.coordinator.async_request_refresh()
|
||||
raise HomeAssistantError(
|
||||
f"Could not execute {self.entity_description.command_on} for device {self.name}"
|
||||
)
|
||||
if self.entity_description.key == "timer_on_switch":
|
||||
await self.async_turn_on_timer(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=True,
|
||||
)
|
||||
if self.entity_description.key == "pure_boost_switch":
|
||||
await self.async_turn_on_off_pure_boost(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=True,
|
||||
)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity off."""
|
||||
params = build_params(self.entity_description.command_on, self.device_data)
|
||||
result = await self.async_send_command(
|
||||
self.entity_description.command_off, params
|
||||
)
|
||||
|
||||
if result["status"] == "success":
|
||||
setattr(self.device_data, self.entity_description.remote_key, False)
|
||||
self.async_write_ha_state()
|
||||
return await self.coordinator.async_request_refresh()
|
||||
raise HomeAssistantError(
|
||||
f"Could not execute {self.entity_description.command_off} for device {self.name}"
|
||||
)
|
||||
if self.entity_description.key == "timer_on_switch":
|
||||
await self.async_turn_off_timer(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=False,
|
||||
)
|
||||
if self.entity_description.key == "pure_boost_switch":
|
||||
await self.async_turn_on_off_pure_boost(
|
||||
device_data=self.device_data,
|
||||
key=self.entity_description.data_key,
|
||||
value=False,
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
|
@ -177,3 +147,43 @@ class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
|||
if self.entity_description.extra_fn:
|
||||
return self.entity_description.extra_fn(self.device_data)
|
||||
return None
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_turn_on_timer(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api for setting timer."""
|
||||
result = {}
|
||||
new_state = bool(device_data.ac_states["on"] is False)
|
||||
data = {
|
||||
"minutesFromNow": 60,
|
||||
"acState": {**device_data.ac_states, "on": new_state},
|
||||
}
|
||||
result = await self._client.async_set_timer(self._device_id, data)
|
||||
return bool(result.get("status") == "success")
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_turn_off_timer(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api for deleting timer."""
|
||||
result = {}
|
||||
result = await self._client.async_del_timer(self._device_id)
|
||||
return bool(result.get("status") == "success")
|
||||
|
||||
@async_handle_api_call
|
||||
async def async_turn_on_off_pure_boost(
|
||||
self, device_data: SensiboDevice, key: Any, value: Any
|
||||
) -> bool:
|
||||
"""Make service call to api for setting Pure Boost."""
|
||||
result = {}
|
||||
new_state = bool(device_data.pure_boost_enabled is False)
|
||||
data: dict[str, Any] = {"enabled": new_state}
|
||||
if device_data.pure_measure_integration is None:
|
||||
data["sensitivity"] = "N"
|
||||
data["measurementsIntegration"] = True
|
||||
data["acIntegration"] = False
|
||||
data["geoIntegration"] = False
|
||||
data["primeIntegration"] = False
|
||||
result = await self._client.async_set_pureboost(self._device_id, data)
|
||||
return bool(result.get("status") == "success")
|
||||
|
|
|
@ -15,11 +15,17 @@
|
|||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "API Key"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "Follow the documentation to get a new api key."
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "Follow the documentation to get your api key."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ from .const import LOGGER, SENSIBO_ERRORS, TIMEOUT
|
|||
|
||||
|
||||
async def async_validate_api(hass: HomeAssistant, api_key: str) -> str:
|
||||
"""Get data from API."""
|
||||
"""Validate the api and return username."""
|
||||
client = SensiboClient(
|
||||
api_key,
|
||||
session=async_get_clientsession(hass),
|
||||
|
|
|
@ -36,7 +36,9 @@ async def test_button(
|
|||
assert state_filter_clean.state is STATE_ON
|
||||
assert state_filter_last_reset.state == "2022-03-12T15:24:26+00:00"
|
||||
|
||||
freezer.move_to(datetime(2022, 6, 19, 20, 0, 0))
|
||||
today = datetime(datetime.now().year + 1, 6, 19, 20, 0, 0).replace(tzinfo=dt.UTC)
|
||||
today_str = today.isoformat()
|
||||
freezer.move_to(today)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||
|
@ -53,13 +55,13 @@ async def test_button(
|
|||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "filter_clean", False)
|
||||
monkeypatch.setattr(
|
||||
get_data.parsed["ABC999111"],
|
||||
"filter_last_reset",
|
||||
datetime(2022, 6, 19, 20, 0, 0, tzinfo=dt.UTC),
|
||||
today,
|
||||
)
|
||||
|
||||
with patch(
|
||||
|
@ -75,11 +77,9 @@ async def test_button(
|
|||
state_button = hass.states.get("button.hallway_reset_filter")
|
||||
state_filter_clean = hass.states.get("binary_sensor.hallway_filter_clean_required")
|
||||
state_filter_last_reset = hass.states.get("sensor.hallway_filter_last_reset")
|
||||
assert (
|
||||
state_button.state == datetime(2022, 6, 19, 20, 0, 0, tzinfo=dt.UTC).isoformat()
|
||||
)
|
||||
assert state_button.state == today_str
|
||||
assert state_filter_clean.state is STATE_OFF
|
||||
assert state_filter_last_reset.state == "2022-06-19T20:00:00+00:00"
|
||||
assert state_filter_last_reset.state == today_str
|
||||
|
||||
|
||||
async def test_button_failure(
|
||||
|
|
|
@ -115,6 +115,9 @@ async def test_climate_fan(
|
|||
assert state1.attributes["fan_mode"] == "high"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -180,6 +183,9 @@ async def test_climate_swing(
|
|||
assert state1.attributes["swing_mode"] == "stopped"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -189,7 +195,7 @@ async def test_climate_swing(
|
|||
{ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "fixedTop"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state2 = hass.states.get("climate.hallway")
|
||||
assert state2.attributes["swing_mode"] == "fixedTop"
|
||||
|
@ -244,6 +250,9 @@ async def test_climate_temperatures(
|
|||
assert state1.attributes["temperature"] == 25
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -259,6 +268,9 @@ async def test_climate_temperatures(
|
|||
assert state2.attributes["temperature"] == 20
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -274,6 +286,9 @@ async def test_climate_temperatures(
|
|||
assert state2.attributes["temperature"] == 16
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -289,6 +304,9 @@ async def test_climate_temperatures(
|
|||
assert state2.attributes["temperature"] == 19
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -304,6 +322,9 @@ async def test_climate_temperatures(
|
|||
assert state2.attributes["temperature"] == 20
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -481,7 +502,7 @@ async def test_climate_hvac_mode(
|
|||
{ATTR_ENTITY_ID: state1.entity_id, ATTR_HVAC_MODE: "off"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state2 = hass.states.get("climate.hallway")
|
||||
assert state2.state == "off"
|
||||
|
@ -540,6 +561,9 @@ async def test_climate_on_off(
|
|||
assert state1.state == "heat"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
@ -549,12 +573,15 @@ async def test_climate_on_off(
|
|||
{ATTR_ENTITY_ID: state1.entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state2 = hass.states.get("climate.hallway")
|
||||
assert state2.state == "off"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
|
||||
return_value={"result": {"status": "Success"}},
|
||||
):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""The test for the sensibo entity."""
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from pysensibo.model import SensiboData
|
||||
import pytest
|
||||
|
@ -11,11 +11,6 @@ from homeassistant.components.climate.const import (
|
|||
DOMAIN as CLIMATE_DOMAIN,
|
||||
SERVICE_SET_FAN_MODE,
|
||||
)
|
||||
from homeassistant.components.number.const import (
|
||||
ATTR_VALUE,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
)
|
||||
from homeassistant.components.sensibo.const import SENSIBO_ERRORS
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
|
@ -51,7 +46,7 @@ async def test_entity(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("p_error", SENSIBO_ERRORS)
|
||||
async def test_entity_send_command(
|
||||
async def test_entity_failed_service_calls(
|
||||
hass: HomeAssistant,
|
||||
p_error: Exception,
|
||||
load_int: ConfigEntry,
|
||||
|
@ -91,29 +86,3 @@ async def test_entity_send_command(
|
|||
|
||||
state = hass.states.get("climate.hallway")
|
||||
assert state.attributes["fan_mode"] == "low"
|
||||
|
||||
|
||||
async def test_entity_send_command_calibration(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: AsyncMock,
|
||||
load_int: ConfigEntry,
|
||||
get_data: SensiboData,
|
||||
) -> None:
|
||||
"""Test the Sensibo send command for calibration."""
|
||||
|
||||
state = hass.states.get("number.hallway_temperature_calibration")
|
||||
assert state.state == "0.1"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_calibration",
|
||||
return_value={"status": "success"},
|
||||
):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: state.entity_id, ATTR_VALUE: 0.2},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get("number.hallway_temperature_calibration")
|
||||
assert state.state == "0.2"
|
||||
|
|
|
@ -8,7 +8,6 @@ from pysensibo.model import SensiboData
|
|||
import pytest
|
||||
from pytest import MonkeyPatch
|
||||
|
||||
from homeassistant.components.sensibo.switch import build_params
|
||||
from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
|
@ -134,6 +133,7 @@ async def test_switch_pure_boost(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", True)
|
||||
monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_measure_integration", None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
|
@ -223,28 +223,3 @@ async def test_switch_command_failure(
|
|||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_build_params(
|
||||
hass: HomeAssistant,
|
||||
load_int: ConfigEntry,
|
||||
monkeypatch: MonkeyPatch,
|
||||
get_data: SensiboData,
|
||||
) -> None:
|
||||
"""Test the build params method."""
|
||||
|
||||
assert build_params("set_timer", get_data.parsed["ABC999111"]) == {
|
||||
"minutesFromNow": 60,
|
||||
"acState": {**get_data.parsed["ABC999111"].ac_states, "on": False},
|
||||
}
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_measure_integration", None)
|
||||
assert build_params("set_pure_boost", get_data.parsed["AAZZAAZZ"]) == {
|
||||
"enabled": True,
|
||||
"sensitivity": "N",
|
||||
"measurementsIntegration": True,
|
||||
"acIntegration": False,
|
||||
"geoIntegration": False,
|
||||
"primeIntegration": False,
|
||||
}
|
||||
assert build_params("incorrect_command", get_data.parsed["ABC999111"]) is None
|
||||
|
|
Loading…
Add table
Reference in a new issue