Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set_profile service for Vallox integration #120225

Merged
merged 7 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions homeassistant/components/vallox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DEFAULT_FAN_SPEED_HOME,
DEFAULT_NAME,
DOMAIN,
I18N_KEY_TO_VALLOX_PROFILE,
)
from .coordinator import ValloxDataUpdateCoordinator

Expand Down Expand Up @@ -61,6 +62,18 @@
}
)

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."""
Expand All @@ -72,6 +85,7 @@
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(
Expand All @@ -86,6 +100,9 @@
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
),
}


Expand Down Expand Up @@ -183,6 +200,22 @@
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:
treetip marked this conversation as resolved.
Show resolved Hide resolved
_LOGGER.error(

Check warning on line 213 in homeassistant/components/vallox/__init__.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vallox/__init__.py#L212-L213

Added lines #L212 - L213 were not covered by tests
"Error setting profile %d for duration %s: %s", profile, duration, err
)
return False

Check warning on line 216 in homeassistant/components/vallox/__init__.py

View check run for this annotation

Codecov / codecov/patch

homeassistant/components/vallox/__init__.py#L216

Added line #L216 was not covered by tests
return True
Comment on lines +216 to +217
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the true false is something that is for vallox?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just imitated the other code in the vallox module, maybe someone else can answer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not 100% sure but might just be a leftover from #56966.


async def async_handle(self, call: ServiceCall) -> None:
"""Dispatch a service call."""
service_details = SERVICE_TO_METHOD.get(call.service)
Expand Down
17 changes: 9 additions & 8 deletions homeassistant/components/vallox/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,24 @@
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",
VALLOX_PROFILE.FIREPLACE: "Fireplace",
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 = {
Expand Down
12 changes: 6 additions & 6 deletions homeassistant/components/vallox/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
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

Expand Down Expand Up @@ -97,7 +97,7 @@ def __init__(
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:
Expand All @@ -108,7 +108,7 @@ def is_on(self) -> bool:
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:
Expand Down Expand Up @@ -204,7 +204,7 @@ async def _async_set_preset_mode_internal(self, preset_mode: str) -> bool:
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:
Expand All @@ -220,7 +220,7 @@ async def _async_set_percentage_internal(
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
)
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/vallox/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
},
"set_profile_fan_speed_boost": {
"service": "mdi:speedometer"
},
"set_profile": {
"service": "mdi:fan"
}
}
}
4 changes: 2 additions & 2 deletions homeassistant/components/vallox/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
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

Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions homeassistant/components/vallox/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
25 changes: 25 additions & 0 deletions homeassistant/components/vallox/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
48 changes: 47 additions & 1 deletion tests/components/vallox/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
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,
)
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

Expand Down Expand Up @@ -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 {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid if statements in test code

)

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
)