Add get device capabilities action call for Sensibo (#134596)

* Add get device capabilities action call for Sensibo

* Tests

* Mod

* Fix services

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
G Johansson 2025-01-06 11:09:08 +01:00 committed by GitHub
parent acd95975e4
commit eafbf1d1fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 115 additions and 3 deletions

View File

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.components.climate import (
ATTR_FAN_MODE,
ATTR_HVAC_MODE,
ATTR_SWING_MODE,
ClimateEntity,
ClimateEntityFeature,
@ -21,8 +22,8 @@ from homeassistant.const import (
PRECISION_TENTHS,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.core import HomeAssistant, SupportsResponse
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.unit_conversion import TemperatureConverter
@ -39,6 +40,7 @@ SERVICE_ENABLE_PURE_BOOST = "enable_pure_boost"
SERVICE_DISABLE_PURE_BOOST = "disable_pure_boost"
SERVICE_FULL_STATE = "full_state"
SERVICE_ENABLE_CLIMATE_REACT = "enable_climate_react"
SERVICE_GET_DEVICE_CAPABILITIES = "get_device_capabilities"
ATTR_HIGH_TEMPERATURE_THRESHOLD = "high_temperature_threshold"
ATTR_HIGH_TEMPERATURE_STATE = "high_temperature_state"
ATTR_LOW_TEMPERATURE_THRESHOLD = "low_temperature_threshold"
@ -172,7 +174,6 @@ async def async_setup_entry(
},
"async_full_ac_state",
)
platform.async_register_entity_service(
SERVICE_ENABLE_CLIMATE_REACT,
{
@ -186,6 +187,12 @@ async def async_setup_entry(
},
"async_enable_climate_react",
)
platform.async_register_entity_service(
SERVICE_GET_DEVICE_CAPABILITIES,
{vol.Required(ATTR_HVAC_MODE): vol.Coerce(HVACMode)},
"async_get_device_capabilities",
supports_response=SupportsResponse.ONLY,
)
class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
@ -390,6 +397,26 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
assumed_state=False,
)
async def async_get_device_capabilities(
self, hvac_mode: HVACMode
) -> dict[str, Any]:
"""Get capabilities from device."""
active_features = self.device_data.active_features
mode_capabilities: dict[str, Any] | None = self.device_data.full_capabilities[
"modes"
].get(hvac_mode.value)
if not mode_capabilities:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="mode_not_exist"
)
remote_capabilities: dict[str, Any] = {}
for active_feature in active_features:
if active_feature in mode_capabilities:
remote_capabilities[active_feature.lower()] = mode_capabilities[
active_feature
]
return remote_capabilities
async def async_assume_state(self, state: str) -> None:
"""Sync state with api."""
await self.async_send_api_call(

View File

@ -59,6 +59,9 @@
},
"enable_climate_react": {
"service": "mdi:wizard-hat"
},
"get_device_capabilities": {
"service": "mdi:shape-outline"
}
}
}

View File

@ -159,3 +159,21 @@ enable_climate_react:
- "feelslike"
- "humidity"
translation_key: smart_type
get_device_capabilities:
target:
entity:
integration: sensibo
domain: climate
fields:
hvac_mode:
required: true
example: "heat"
selector:
select:
options:
- "auto"
- "cool"
- "dry"
- "fan"
- "heat"
translation_key: hvac_mode

View File

@ -494,6 +494,16 @@
"description": "Choose between temperature/feels like/humidity."
}
}
},
"get_device_capabilities": {
"name": "Get device mode capabilities",
"description": "Retrieve the device capabilities for a specific device according to api requirements.",
"fields": {
"hvac_mode": {
"name": "[%key:component::climate::services::set_hvac_mode::fields::hvac_mode::name%]",
"description": "[%key:component::climate::services::set_hvac_mode::fields::hvac_mode::description%]"
}
}
}
},
"selector": {
@ -561,6 +571,9 @@
},
"no_data": {
"message": "[%key:component::sensibo::config::error::no_devices%]"
},
"mode_not_exist": {
"message": "The entity does not support the chosen mode"
}
}
}

View File

@ -227,3 +227,23 @@
'state': 'off',
})
# ---
# name: test_climate_get_device_capabilities
dict({
'climate.hallway': dict({
'horizontalswing': list([
'stopped',
'fixedLeft',
'fixedCenterLeft',
]),
'light': list([
'on',
'off',
]),
'swing': list([
'stopped',
'fixedTop',
'fixedMiddleTop',
]),
}),
})
# ---

View File

@ -43,6 +43,7 @@ from homeassistant.components.sensibo.climate import (
SERVICE_ENABLE_PURE_BOOST,
SERVICE_ENABLE_TIMER,
SERVICE_FULL_STATE,
SERVICE_GET_DEVICE_CAPABILITIES,
_find_valid_target_temp,
)
from homeassistant.components.sensibo.const import DOMAIN
@ -1187,3 +1188,33 @@ async def test_climate_fan_mode_and_swing_mode_not_supported(
state = hass.states.get("climate.hallway")
assert state.attributes["fan_mode"] == "high"
assert state.attributes["swing_mode"] == "stopped"
async def test_climate_get_device_capabilities(
hass: HomeAssistant,
load_int: ConfigEntry,
mock_client: MagicMock,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
) -> None:
"""Test the Sensibo climate Get device capabilitites service."""
response = await hass.services.async_call(
DOMAIN,
SERVICE_GET_DEVICE_CAPABILITIES,
{ATTR_ENTITY_ID: "climate.hallway", ATTR_HVAC_MODE: "heat"},
blocking=True,
return_response=True,
)
assert response == snapshot
with pytest.raises(
ServiceValidationError, match="The entity does not support the chosen mode"
):
await hass.services.async_call(
DOMAIN,
SERVICE_GET_DEVICE_CAPABILITIES,
{ATTR_ENTITY_ID: "climate.hallway", ATTR_HVAC_MODE: "heat_cool"},
blocking=True,
return_response=True,
)