Add set_profile service for Vallox integration (#120225)
* Add set_profile service for Vallox integration * Merge profile constants, use str input for service * add service test and some related refactoring * Change service uom to 'minutes' Co-authored-by: Sebastian Lövdahl <slovdahl@hibox.fi> * Update icons.js format after rebase * Translate profile names for service * Fix test using wrong dict --------- Co-authored-by: Sebastian Lövdahl <slovdahl@hibox.fi>
This commit is contained in:
parent
45ab6e9b06
commit
af62e8267f
8 changed files with 146 additions and 17 deletions
|
@ -22,6 +22,7 @@ from .const import (
|
|||
DEFAULT_FAN_SPEED_HOME,
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
I18N_KEY_TO_VALLOX_PROFILE,
|
||||
)
|
||||
from .coordinator import ValloxDataUpdateCoordinator
|
||||
|
||||
|
@ -61,6 +62,18 @@ SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED = vol.Schema(
|
|||
}
|
||||
)
|
||||
|
||||
ATTR_PROFILE = "profile"
|
||||
ATTR_DURATION = "duration"
|
||||
|
||||
SERVICE_SCHEMA_SET_PROFILE = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_PROFILE): vol.In(I18N_KEY_TO_VALLOX_PROFILE),
|
||||
vol.Optional(ATTR_DURATION): vol.All(
|
||||
vol.Coerce(int), vol.Clamp(min=1, max=65535)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ServiceMethodDetails(NamedTuple):
|
||||
"""Details for SERVICE_TO_METHOD mapping."""
|
||||
|
@ -72,6 +85,7 @@ class ServiceMethodDetails(NamedTuple):
|
|||
SERVICE_SET_PROFILE_FAN_SPEED_HOME = "set_profile_fan_speed_home"
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_AWAY = "set_profile_fan_speed_away"
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_BOOST = "set_profile_fan_speed_boost"
|
||||
SERVICE_SET_PROFILE = "set_profile"
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_HOME: ServiceMethodDetails(
|
||||
|
@ -86,6 +100,9 @@ SERVICE_TO_METHOD = {
|
|||
method="async_set_profile_fan_speed_boost",
|
||||
schema=SERVICE_SCHEMA_SET_PROFILE_FAN_SPEED,
|
||||
),
|
||||
SERVICE_SET_PROFILE: ServiceMethodDetails(
|
||||
method="async_set_profile", schema=SERVICE_SCHEMA_SET_PROFILE
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,6 +200,22 @@ class ValloxServiceHandler:
|
|||
return False
|
||||
return True
|
||||
|
||||
async def async_set_profile(
|
||||
self, profile: str, duration: int | None = None
|
||||
) -> bool:
|
||||
"""Activate profile for given duration."""
|
||||
_LOGGER.debug("Activating profile %s for %s min", profile, duration)
|
||||
try:
|
||||
await self._client.set_profile(
|
||||
I18N_KEY_TO_VALLOX_PROFILE[profile], duration
|
||||
)
|
||||
except ValloxApiException as err:
|
||||
_LOGGER.error(
|
||||
"Error setting profile %d for duration %s: %s", profile, duration, err
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def async_handle(self, call: ServiceCall) -> None:
|
||||
"""Dispatch a service call."""
|
||||
service_details = SERVICE_TO_METHOD.get(call.service)
|
||||
|
|
|
@ -22,14 +22,15 @@ DEFAULT_FAN_SPEED_HOME = 50
|
|||
DEFAULT_FAN_SPEED_AWAY = 25
|
||||
DEFAULT_FAN_SPEED_BOOST = 65
|
||||
|
||||
VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE = {
|
||||
VALLOX_PROFILE.HOME: "Home",
|
||||
VALLOX_PROFILE.AWAY: "Away",
|
||||
VALLOX_PROFILE.BOOST: "Boost",
|
||||
VALLOX_PROFILE.FIREPLACE: "Fireplace",
|
||||
I18N_KEY_TO_VALLOX_PROFILE = {
|
||||
"home": VALLOX_PROFILE.HOME,
|
||||
"away": VALLOX_PROFILE.AWAY,
|
||||
"boost": VALLOX_PROFILE.BOOST,
|
||||
"fireplace": VALLOX_PROFILE.FIREPLACE,
|
||||
"extra": VALLOX_PROFILE.EXTRA,
|
||||
}
|
||||
|
||||
VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE = {
|
||||
VALLOX_PROFILE_TO_PRESET_MODE = {
|
||||
VALLOX_PROFILE.HOME: "Home",
|
||||
VALLOX_PROFILE.AWAY: "Away",
|
||||
VALLOX_PROFILE.BOOST: "Boost",
|
||||
|
@ -37,8 +38,8 @@ VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE = {
|
|||
VALLOX_PROFILE.EXTRA: "Extra",
|
||||
}
|
||||
|
||||
PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE = {
|
||||
value: key for (key, value) in VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE.items()
|
||||
PRESET_MODE_TO_VALLOX_PROFILE = {
|
||||
value: key for (key, value) in VALLOX_PROFILE_TO_PRESET_MODE.items()
|
||||
}
|
||||
|
||||
VALLOX_CELL_STATE_TO_STR = {
|
||||
|
|
|
@ -23,8 +23,8 @@ from .const import (
|
|||
METRIC_KEY_PROFILE_FAN_SPEED_HOME,
|
||||
MODE_OFF,
|
||||
MODE_ON,
|
||||
PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE,
|
||||
VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE,
|
||||
PRESET_MODE_TO_VALLOX_PROFILE,
|
||||
VALLOX_PROFILE_TO_PRESET_MODE,
|
||||
)
|
||||
from .coordinator import ValloxDataUpdateCoordinator
|
||||
|
||||
|
@ -97,7 +97,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
|
|||
self._client = client
|
||||
|
||||
self._attr_unique_id = str(self._device_uuid)
|
||||
self._attr_preset_modes = list(PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE)
|
||||
self._attr_preset_modes = list(PRESET_MODE_TO_VALLOX_PROFILE)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
|
@ -108,7 +108,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
|
|||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode."""
|
||||
vallox_profile = self.coordinator.data.profile
|
||||
return VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE.get(vallox_profile)
|
||||
return VALLOX_PROFILE_TO_PRESET_MODE.get(vallox_profile)
|
||||
|
||||
@property
|
||||
def percentage(self) -> int | None:
|
||||
|
@ -204,7 +204,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
|
|||
return False
|
||||
|
||||
try:
|
||||
profile = PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode]
|
||||
profile = PRESET_MODE_TO_VALLOX_PROFILE[preset_mode]
|
||||
await self._client.set_profile(profile)
|
||||
|
||||
except ValloxApiException as err:
|
||||
|
@ -220,7 +220,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
|
|||
Returns true if speed has been changed, false otherwise.
|
||||
"""
|
||||
vallox_profile = (
|
||||
PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode]
|
||||
PRESET_MODE_TO_VALLOX_PROFILE[preset_mode]
|
||||
if preset_mode is not None
|
||||
else self.coordinator.data.profile
|
||||
)
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
},
|
||||
"set_profile_fan_speed_boost": {
|
||||
"service": "mdi:speedometer"
|
||||
},
|
||||
"set_profile": {
|
||||
"service": "mdi:fan"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ from .const import (
|
|||
METRIC_KEY_MODE,
|
||||
MODE_ON,
|
||||
VALLOX_CELL_STATE_TO_STR,
|
||||
VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE,
|
||||
VALLOX_PROFILE_TO_PRESET_MODE,
|
||||
)
|
||||
from .coordinator import ValloxDataUpdateCoordinator
|
||||
|
||||
|
@ -78,7 +78,7 @@ class ValloxProfileSensor(ValloxSensorEntity):
|
|||
def native_value(self) -> StateType:
|
||||
"""Return the value reported by the sensor."""
|
||||
vallox_profile = self.coordinator.data.profile
|
||||
return VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE.get(vallox_profile)
|
||||
return VALLOX_PROFILE_TO_PRESET_MODE.get(vallox_profile)
|
||||
|
||||
|
||||
# There is a quirk with respect to the fan speed reporting. The device keeps on reporting the last
|
||||
|
|
|
@ -27,3 +27,24 @@ set_profile_fan_speed_boost:
|
|||
min: 0
|
||||
max: 100
|
||||
unit_of_measurement: "%"
|
||||
|
||||
set_profile:
|
||||
fields:
|
||||
profile:
|
||||
required: true
|
||||
selector:
|
||||
select:
|
||||
translation_key: "profile"
|
||||
options:
|
||||
- "home"
|
||||
- "away"
|
||||
- "boost"
|
||||
- "fireplace"
|
||||
- "extra"
|
||||
duration:
|
||||
required: false
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 65535
|
||||
unit_of_measurement: "minutes"
|
||||
|
|
|
@ -133,6 +133,31 @@
|
|||
"description": "[%key:component::vallox::services::set_profile_fan_speed_home::fields::fan_speed::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_profile": {
|
||||
"name": "Activate profile for duration",
|
||||
"description": "Activate a profile and optionally set duration.",
|
||||
"fields": {
|
||||
"profile": {
|
||||
"name": "Profile",
|
||||
"description": "Profile to activate"
|
||||
},
|
||||
"duration": {
|
||||
"name": "Duration",
|
||||
"description": "Activation duration, if omitted device uses stored duration. Duration of 65535 activates profile without timeout. Duration only applies to Boost, Fireplace and Extra profiles."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"profile": {
|
||||
"options": {
|
||||
"home": "Home",
|
||||
"away": "Away",
|
||||
"boost": "Boost",
|
||||
"fireplace": "Fireplace",
|
||||
"extra": "Extra"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@ import pytest
|
|||
from vallox_websocket_api import Profile
|
||||
|
||||
from homeassistant.components.vallox import (
|
||||
ATTR_DURATION,
|
||||
ATTR_PROFILE,
|
||||
ATTR_PROFILE_FAN_SPEED,
|
||||
I18N_KEY_TO_VALLOX_PROFILE,
|
||||
SERVICE_SET_PROFILE,
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_AWAY,
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_BOOST,
|
||||
SERVICE_SET_PROFILE_FAN_SPEED_HOME,
|
||||
|
@ -12,7 +16,7 @@ from homeassistant.components.vallox import (
|
|||
from homeassistant.components.vallox.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import patch_set_fan_speed
|
||||
from .conftest import patch_set_fan_speed, patch_set_profile
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -47,3 +51,45 @@ async def test_create_service(
|
|||
|
||||
# Assert
|
||||
set_fan_speed.assert_called_once_with(profile, 30)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("profile", "duration"),
|
||||
[
|
||||
("home", None),
|
||||
("home", 15),
|
||||
("away", None),
|
||||
("away", 15),
|
||||
("boost", None),
|
||||
("boost", 15),
|
||||
("fireplace", None),
|
||||
("fireplace", 15),
|
||||
("extra", None),
|
||||
("extra", 15),
|
||||
],
|
||||
)
|
||||
async def test_set_profile_service(
|
||||
hass: HomeAssistant, mock_entry: MockConfigEntry, profile: str, duration: int | None
|
||||
) -> None:
|
||||
"""Test service for setting profile and duration."""
|
||||
# Act
|
||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch_set_profile() as set_profile:
|
||||
service_data = {ATTR_PROFILE: profile} | (
|
||||
{ATTR_DURATION: duration} if duration is not None else {}
|
||||
)
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_PROFILE,
|
||||
service_data=service_data,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Assert
|
||||
set_profile.assert_called_once_with(
|
||||
I18N_KEY_TO_VALLOX_PROFILE[profile], duration
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue