mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Add Sensibo Climate React (#78221)
This commit is contained in:
parent
746bdb44ac
commit
b04165b495
@ -38,6 +38,12 @@ ATTR_MINUTES = "minutes"
|
|||||||
SERVICE_ENABLE_PURE_BOOST = "enable_pure_boost"
|
SERVICE_ENABLE_PURE_BOOST = "enable_pure_boost"
|
||||||
SERVICE_DISABLE_PURE_BOOST = "disable_pure_boost"
|
SERVICE_DISABLE_PURE_BOOST = "disable_pure_boost"
|
||||||
SERVICE_FULL_STATE = "full_state"
|
SERVICE_FULL_STATE = "full_state"
|
||||||
|
SERVICE_ENABLE_CLIMATE_REACT = "enable_climate_react"
|
||||||
|
ATTR_HIGH_TEMPERATURE_THRESHOLD = "high_temperature_threshold"
|
||||||
|
ATTR_HIGH_TEMPERATURE_STATE = "high_temperature_state"
|
||||||
|
ATTR_LOW_TEMPERATURE_THRESHOLD = "low_temperature_threshold"
|
||||||
|
ATTR_LOW_TEMPERATURE_STATE = "low_temperature_state"
|
||||||
|
ATTR_SMART_TYPE = "smart_type"
|
||||||
|
|
||||||
ATTR_AC_INTEGRATION = "ac_integration"
|
ATTR_AC_INTEGRATION = "ac_integration"
|
||||||
ATTR_GEO_INTEGRATION = "geo_integration"
|
ATTR_GEO_INTEGRATION = "geo_integration"
|
||||||
@ -140,6 +146,20 @@ async def async_setup_entry(
|
|||||||
"async_full_ac_state",
|
"async_full_ac_state",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_ENABLE_CLIMATE_REACT,
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_HIGH_TEMPERATURE_THRESHOLD): float,
|
||||||
|
vol.Required(ATTR_HIGH_TEMPERATURE_STATE): dict,
|
||||||
|
vol.Required(ATTR_LOW_TEMPERATURE_THRESHOLD): float,
|
||||||
|
vol.Required(ATTR_LOW_TEMPERATURE_STATE): dict,
|
||||||
|
vol.Required(ATTR_SMART_TYPE): vol.In(
|
||||||
|
["temperature", "feelsLike", "humidity"]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"async_enable_climate_react",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
||||||
"""Representation of a Sensibo device."""
|
"""Representation of a Sensibo device."""
|
||||||
@ -430,6 +450,42 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||||||
data=params,
|
data=params,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_enable_climate_react(
|
||||||
|
self,
|
||||||
|
high_temperature_threshold: float,
|
||||||
|
high_temperature_state: dict[str, Any],
|
||||||
|
low_temperature_threshold: float,
|
||||||
|
low_temperature_state: dict[str, Any],
|
||||||
|
smart_type: str,
|
||||||
|
) -> None:
|
||||||
|
"""Enable Climate React Configuration."""
|
||||||
|
high_temp = high_temperature_threshold
|
||||||
|
low_temp = low_temperature_threshold
|
||||||
|
|
||||||
|
if high_temperature_state.get("temperatureUnit") == "F":
|
||||||
|
high_temp = TemperatureConverter.convert(
|
||||||
|
high_temperature_threshold, TEMP_FAHRENHEIT, TEMP_CELSIUS
|
||||||
|
)
|
||||||
|
low_temp = TemperatureConverter.convert(
|
||||||
|
low_temperature_threshold, TEMP_FAHRENHEIT, TEMP_CELSIUS
|
||||||
|
)
|
||||||
|
|
||||||
|
params: dict[str, str | bool | float | dict] = {
|
||||||
|
"enabled": True,
|
||||||
|
"deviceUid": self._device_id,
|
||||||
|
"highTemperatureState": high_temperature_state,
|
||||||
|
"highTemperatureThreshold": high_temp,
|
||||||
|
"lowTemperatureState": low_temperature_state,
|
||||||
|
"lowTemperatureThreshold": low_temp,
|
||||||
|
"type": smart_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
await self.api_call_custom_service_climate_react(
|
||||||
|
key="smart_on",
|
||||||
|
value=True,
|
||||||
|
data=params,
|
||||||
|
)
|
||||||
|
|
||||||
@async_handle_api_call
|
@async_handle_api_call
|
||||||
async def async_send_api_call(
|
async def async_send_api_call(
|
||||||
self,
|
self,
|
||||||
@ -470,6 +526,17 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||||||
result = await self._client.async_set_pureboost(self._device_id, data)
|
result = await self._client.async_set_pureboost(self._device_id, data)
|
||||||
return bool(result.get("status") == "success")
|
return bool(result.get("status") == "success")
|
||||||
|
|
||||||
|
@async_handle_api_call
|
||||||
|
async def api_call_custom_service_climate_react(
|
||||||
|
self,
|
||||||
|
key: str,
|
||||||
|
value: Any,
|
||||||
|
data: dict,
|
||||||
|
) -> bool:
|
||||||
|
"""Make service call to api."""
|
||||||
|
result = await self._client.async_set_climate_react(self._device_id, data)
|
||||||
|
return bool(result.get("status") == "success")
|
||||||
|
|
||||||
@async_handle_api_call
|
@async_handle_api_call
|
||||||
async def api_call_custom_service_full_ac_state(
|
async def api_call_custom_service_full_ac_state(
|
||||||
self,
|
self,
|
||||||
|
@ -155,6 +155,32 @@ DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = (
|
|||||||
extra_fn=None,
|
extra_fn=None,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
),
|
),
|
||||||
|
SensiboDeviceSensorEntityDescription(
|
||||||
|
key="climate_react_low",
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
name="Climate React low temperature threshold",
|
||||||
|
value_fn=lambda data: data.smart_low_temp_threshold,
|
||||||
|
extra_fn=lambda data: data.smart_low_state,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
),
|
||||||
|
SensiboDeviceSensorEntityDescription(
|
||||||
|
key="climate_react_high",
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
name="Climate React high temperature threshold",
|
||||||
|
value_fn=lambda data: data.smart_high_temp_threshold,
|
||||||
|
extra_fn=lambda data: data.smart_high_state,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
),
|
||||||
|
SensiboDeviceSensorEntityDescription(
|
||||||
|
key="climate_react_type",
|
||||||
|
device_class="sensibo__smart_type",
|
||||||
|
name="Climate React type",
|
||||||
|
value_fn=lambda data: data.smart_type,
|
||||||
|
extra_fn=None,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
),
|
||||||
FILTER_LAST_RESET_DESCRIPTION,
|
FILTER_LAST_RESET_DESCRIPTION,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -281,7 +307,10 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, SensorEntity):
|
|||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType | datetime:
|
def native_value(self) -> StateType | datetime:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.entity_description.value_fn(self.device_data)
|
state = self.entity_description.value_fn(self.device_data)
|
||||||
|
if isinstance(state, str):
|
||||||
|
return state.lower()
|
||||||
|
return state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||||
|
@ -146,3 +146,56 @@ full_state:
|
|||||||
options:
|
options:
|
||||||
- "on"
|
- "on"
|
||||||
- "off"
|
- "off"
|
||||||
|
enable_climate_react:
|
||||||
|
name: Enable Climate React
|
||||||
|
description: Enable and configure Climate React
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
integration: sensibo
|
||||||
|
domain: climate
|
||||||
|
fields:
|
||||||
|
high_temperature_threshold:
|
||||||
|
name: Threshold high
|
||||||
|
description: When temp/humidity goes above
|
||||||
|
required: true
|
||||||
|
example: 24
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 150
|
||||||
|
step: 0.1
|
||||||
|
mode: box
|
||||||
|
high_temperature_state:
|
||||||
|
name: State high threshold
|
||||||
|
description: What should happen at high threshold. Requires full state
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
object:
|
||||||
|
low_temperature_threshold:
|
||||||
|
name: Threshold low
|
||||||
|
description: When temp/humidity goes below
|
||||||
|
required: true
|
||||||
|
example: 19
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 150
|
||||||
|
step: 0.1
|
||||||
|
mode: box
|
||||||
|
low_temperature_state:
|
||||||
|
name: State low threshold
|
||||||
|
description: What should happen at low threshold. Requires full state
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
object:
|
||||||
|
smart_type:
|
||||||
|
name: Trigger type
|
||||||
|
description: Choose between temperature/feels like/humidity
|
||||||
|
required: true
|
||||||
|
example: "temperature"
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "temperature"
|
||||||
|
- "feelsLike"
|
||||||
|
- "humidity"
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
"sensibo__sensitivity": {
|
"sensibo__sensitivity": {
|
||||||
"n": "Normal",
|
"n": "Normal",
|
||||||
"s": "Sensitive"
|
"s": "Sensitive"
|
||||||
|
},
|
||||||
|
"sensibo__smart_type": {
|
||||||
|
"temperature": "Temperature",
|
||||||
|
"feelslike": "Feels like",
|
||||||
|
"humidity": "Humidity"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.components.switch import (
|
|||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
@ -53,6 +54,17 @@ DEVICE_SWITCH_TYPES: tuple[SensiboDeviceSwitchEntityDescription, ...] = (
|
|||||||
command_off="async_turn_off_timer",
|
command_off="async_turn_off_timer",
|
||||||
data_key="timer_on",
|
data_key="timer_on",
|
||||||
),
|
),
|
||||||
|
SensiboDeviceSwitchEntityDescription(
|
||||||
|
key="climate_react_switch",
|
||||||
|
device_class=SwitchDeviceClass.SWITCH,
|
||||||
|
name="Climate React",
|
||||||
|
icon="mdi:wizard-hat",
|
||||||
|
value_fn=lambda data: data.smart_on,
|
||||||
|
extra_fn=lambda data: {"type": data.smart_type},
|
||||||
|
command_on="async_turn_on_off_smart",
|
||||||
|
command_off="async_turn_on_off_smart",
|
||||||
|
data_key="smart_on",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
PURE_SWITCH_TYPES: tuple[SensiboDeviceSwitchEntityDescription, ...] = (
|
PURE_SWITCH_TYPES: tuple[SensiboDeviceSwitchEntityDescription, ...] = (
|
||||||
@ -166,3 +178,15 @@ class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
|||||||
data["primeIntegration"] = False
|
data["primeIntegration"] = False
|
||||||
result = await self._client.async_set_pureboost(self._device_id, data)
|
result = await self._client.async_set_pureboost(self._device_id, data)
|
||||||
return bool(result.get("status") == "success")
|
return bool(result.get("status") == "success")
|
||||||
|
|
||||||
|
@async_handle_api_call
|
||||||
|
async def async_turn_on_off_smart(self, key: str, value: Any) -> bool:
|
||||||
|
"""Make service call to api for setting Climate React."""
|
||||||
|
if self.device_data.smart_type is None:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
"Use Sensibo Enable Climate React Service once to enable switch or the Sensibo app"
|
||||||
|
)
|
||||||
|
new_state = bool(self.device_data.smart_on is False)
|
||||||
|
data: dict[str, Any] = {"enabled": new_state}
|
||||||
|
result = await self._client.async_enable_climate_react(self._device_id, data)
|
||||||
|
return bool(result.get("status") == "success")
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
"sensibo__sensitivity": {
|
"sensibo__sensitivity": {
|
||||||
"n": "Normal",
|
"n": "Normal",
|
||||||
"s": "Sensitive"
|
"s": "Sensitive"
|
||||||
|
},
|
||||||
|
"sensibo__smart_type": {
|
||||||
|
"feelslike": "Feels like",
|
||||||
|
"humidity": "Humidity",
|
||||||
|
"temperature": "Temperature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,14 +23,20 @@ from homeassistant.components.climate import (
|
|||||||
from homeassistant.components.sensibo.climate import (
|
from homeassistant.components.sensibo.climate import (
|
||||||
ATTR_AC_INTEGRATION,
|
ATTR_AC_INTEGRATION,
|
||||||
ATTR_GEO_INTEGRATION,
|
ATTR_GEO_INTEGRATION,
|
||||||
|
ATTR_HIGH_TEMPERATURE_STATE,
|
||||||
|
ATTR_HIGH_TEMPERATURE_THRESHOLD,
|
||||||
ATTR_HORIZONTAL_SWING_MODE,
|
ATTR_HORIZONTAL_SWING_MODE,
|
||||||
ATTR_INDOOR_INTEGRATION,
|
ATTR_INDOOR_INTEGRATION,
|
||||||
ATTR_LIGHT,
|
ATTR_LIGHT,
|
||||||
|
ATTR_LOW_TEMPERATURE_STATE,
|
||||||
|
ATTR_LOW_TEMPERATURE_THRESHOLD,
|
||||||
ATTR_MINUTES,
|
ATTR_MINUTES,
|
||||||
ATTR_OUTDOOR_INTEGRATION,
|
ATTR_OUTDOOR_INTEGRATION,
|
||||||
ATTR_SENSITIVITY,
|
ATTR_SENSITIVITY,
|
||||||
|
ATTR_SMART_TYPE,
|
||||||
ATTR_TARGET_TEMPERATURE,
|
ATTR_TARGET_TEMPERATURE,
|
||||||
SERVICE_ASSUME_STATE,
|
SERVICE_ASSUME_STATE,
|
||||||
|
SERVICE_ENABLE_CLIMATE_REACT,
|
||||||
SERVICE_ENABLE_PURE_BOOST,
|
SERVICE_ENABLE_PURE_BOOST,
|
||||||
SERVICE_ENABLE_TIMER,
|
SERVICE_ENABLE_TIMER,
|
||||||
SERVICE_FULL_STATE,
|
SERVICE_FULL_STATE,
|
||||||
@ -923,6 +929,311 @@ async def test_climate_pure_boost(
|
|||||||
assert state4.state == "s"
|
assert state4.state == "s"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_climate_climate_react(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry_enabled_by_default: AsyncMock,
|
||||||
|
load_int: ConfigEntry,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
get_data: SensiboData,
|
||||||
|
) -> None:
|
||||||
|
"""Test the Sensibo climate react custom service."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state_climate = hass.states.get("climate.hallway")
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react",
|
||||||
|
):
|
||||||
|
with pytest.raises(MultipleInvalid):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ENABLE_PURE_BOOST,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state_climate.entity_id,
|
||||||
|
ATTR_LOW_TEMPERATURE_THRESHOLD: 0.2,
|
||||||
|
ATTR_HIGH_TEMPERATURE_THRESHOLD: 30.3,
|
||||||
|
ATTR_SMART_TYPE: "temperature",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react",
|
||||||
|
return_value={
|
||||||
|
"status": "success",
|
||||||
|
"result": {
|
||||||
|
"enabled": True,
|
||||||
|
"deviceUid": "ABC999111",
|
||||||
|
"highTemperatureState": {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 15,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
"highTemperatureThreshold": 30.5,
|
||||||
|
"lowTemperatureState": {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 25,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
"lowTemperatureThreshold": 5.5,
|
||||||
|
"type": "temperature",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ENABLE_CLIMATE_REACT,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state_climate.entity_id,
|
||||||
|
ATTR_LOW_TEMPERATURE_THRESHOLD: 5.5,
|
||||||
|
ATTR_HIGH_TEMPERATURE_THRESHOLD: 30.5,
|
||||||
|
ATTR_LOW_TEMPERATURE_STATE: {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 25,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
ATTR_HIGH_TEMPERATURE_STATE: {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 15,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
ATTR_SMART_TYPE: "temperature",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True)
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", "temperature")
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_low_temp_threshold", 5.5)
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_high_temp_threshold", 30.5)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
get_data.parsed["ABC999111"],
|
||||||
|
"smart_low_state",
|
||||||
|
{
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 25,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
get_data.parsed["ABC999111"],
|
||||||
|
"smart_high_state",
|
||||||
|
{
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 15,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
state2 = hass.states.get("sensor.hallway_climate_react_low_temperature_threshold")
|
||||||
|
state3 = hass.states.get("sensor.hallway_climate_react_high_temperature_threshold")
|
||||||
|
state4 = hass.states.get("sensor.hallway_climate_react_type")
|
||||||
|
assert state1.state == "on"
|
||||||
|
assert state2.state == "5.5"
|
||||||
|
assert state3.state == "30.5"
|
||||||
|
assert state4.state == "temperature"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_climate_climate_react_fahrenheit(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry_enabled_by_default: AsyncMock,
|
||||||
|
load_int: ConfigEntry,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
get_data: SensiboData,
|
||||||
|
) -> None:
|
||||||
|
"""Test the Sensibo climate react custom service with fahrenheit."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state_climate = hass.states.get("climate.hallway")
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react",
|
||||||
|
return_value={
|
||||||
|
"status": "success",
|
||||||
|
"result": {
|
||||||
|
"enabled": True,
|
||||||
|
"deviceUid": "ABC999111",
|
||||||
|
"highTemperatureState": {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 65,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
"highTemperatureThreshold": 77,
|
||||||
|
"lowTemperatureState": {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 85,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
"lowTemperatureThreshold": 32,
|
||||||
|
"type": "temperature",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ENABLE_CLIMATE_REACT,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state_climate.entity_id,
|
||||||
|
ATTR_LOW_TEMPERATURE_THRESHOLD: 32.0,
|
||||||
|
ATTR_HIGH_TEMPERATURE_THRESHOLD: 77.0,
|
||||||
|
ATTR_LOW_TEMPERATURE_STATE: {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 85,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
ATTR_HIGH_TEMPERATURE_STATE: {
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 65,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
ATTR_SMART_TYPE: "temperature",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True)
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", "temperature")
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_low_temp_threshold", 0)
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_high_temp_threshold", 25)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
get_data.parsed["ABC999111"],
|
||||||
|
"smart_low_state",
|
||||||
|
{
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 85,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
get_data.parsed["ABC999111"],
|
||||||
|
"smart_high_state",
|
||||||
|
{
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 65,
|
||||||
|
"temperatureUnit": "F",
|
||||||
|
"mode": "cool",
|
||||||
|
"fanLevel": "high",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
state2 = hass.states.get("sensor.hallway_climate_react_low_temperature_threshold")
|
||||||
|
state3 = hass.states.get("sensor.hallway_climate_react_high_temperature_threshold")
|
||||||
|
state4 = hass.states.get("sensor.hallway_climate_react_type")
|
||||||
|
assert state1.state == "on"
|
||||||
|
assert state2.state == "0"
|
||||||
|
assert state3.state == "25"
|
||||||
|
assert state4.state == "temperature"
|
||||||
|
|
||||||
|
|
||||||
async def test_climate_full_ac_state(
|
async def test_climate_full_ac_state(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry_enabled_by_default: AsyncMock,
|
entity_registry_enabled_by_default: AsyncMock,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from pysensibo.model import SensiboData
|
from pysensibo.model import SensiboData
|
||||||
from pytest import MonkeyPatch
|
from pytest import MonkeyPatch
|
||||||
@ -16,6 +16,7 @@ from tests.common import async_fire_time_changed
|
|||||||
|
|
||||||
async def test_sensor(
|
async def test_sensor(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
entity_registry_enabled_by_default: AsyncMock,
|
||||||
load_int: ConfigEntry,
|
load_int: ConfigEntry,
|
||||||
monkeypatch: MonkeyPatch,
|
monkeypatch: MonkeyPatch,
|
||||||
get_data: SensiboData,
|
get_data: SensiboData,
|
||||||
@ -25,9 +26,11 @@ async def test_sensor(
|
|||||||
state1 = hass.states.get("sensor.hallway_motion_sensor_battery_voltage")
|
state1 = hass.states.get("sensor.hallway_motion_sensor_battery_voltage")
|
||||||
state2 = hass.states.get("sensor.kitchen_pm2_5")
|
state2 = hass.states.get("sensor.kitchen_pm2_5")
|
||||||
state3 = hass.states.get("sensor.kitchen_pure_sensitivity")
|
state3 = hass.states.get("sensor.kitchen_pure_sensitivity")
|
||||||
|
state4 = hass.states.get("sensor.hallway_climate_react_low_temperature_threshold")
|
||||||
assert state1.state == "3000"
|
assert state1.state == "3000"
|
||||||
assert state2.state == "1"
|
assert state2.state == "1"
|
||||||
assert state3.state == "n"
|
assert state3.state == "n"
|
||||||
|
assert state4.state == "0.0"
|
||||||
assert state2.attributes == {
|
assert state2.attributes == {
|
||||||
"state_class": "measurement",
|
"state_class": "measurement",
|
||||||
"unit_of_measurement": "µg/m³",
|
"unit_of_measurement": "µg/m³",
|
||||||
@ -35,6 +38,20 @@ async def test_sensor(
|
|||||||
"icon": "mdi:air-filter",
|
"icon": "mdi:air-filter",
|
||||||
"friendly_name": "Kitchen PM2.5",
|
"friendly_name": "Kitchen PM2.5",
|
||||||
}
|
}
|
||||||
|
assert state4.attributes == {
|
||||||
|
"device_class": "temperature",
|
||||||
|
"friendly_name": "Hallway Climate React low temperature threshold",
|
||||||
|
"state_class": "measurement",
|
||||||
|
"unit_of_measurement": "°C",
|
||||||
|
"on": True,
|
||||||
|
"targetTemperature": 21,
|
||||||
|
"temperatureUnit": "C",
|
||||||
|
"mode": "heat",
|
||||||
|
"fanLevel": "low",
|
||||||
|
"swing": "stopped",
|
||||||
|
"horizontalSwing": "stopped",
|
||||||
|
"light": "on",
|
||||||
|
}
|
||||||
|
|
||||||
monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pm25", 2)
|
monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pm25", 2)
|
||||||
|
|
||||||
|
@ -223,3 +223,113 @@ async def test_switch_command_failure(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_climate_react(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: ConfigEntry,
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
get_data: SensiboData,
|
||||||
|
) -> None:
|
||||||
|
"""Test the Sensibo switch for climate react."""
|
||||||
|
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
assert state1.state == STATE_OFF
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_enable_climate_react",
|
||||||
|
return_value={"status": "success"},
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state1.entity_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
assert state1.state == STATE_ON
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.sensibo.util.SensiboClient.async_enable_climate_react",
|
||||||
|
return_value={"status": "success"},
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state1.entity_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", False)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
assert state1.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_climate_react_no_data(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: ConfigEntry,
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
get_data: SensiboData,
|
||||||
|
) -> None:
|
||||||
|
"""Test the Sensibo switch for climate react."""
|
||||||
|
|
||||||
|
monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", None)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||||
|
return_value=get_data,
|
||||||
|
):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass,
|
||||||
|
dt.utcnow() + timedelta(minutes=5),
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state1 = hass.states.get("switch.hallway_climate_react")
|
||||||
|
assert state1.state == STATE_OFF
|
||||||
|
|
||||||
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: state1.entity_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user