Skip to content

Commit 84a975b

Browse files
authored
Add Balboa spa temperature range state control (high/low) (#115285)
* Add temperature range switch (high/low) to Balboa spa integration. * Change Balboa spa integration temperature range control from switch to select * Balboa spa integration: Fix ruff formatting * Balboa spa integration: increase test coverage * Balboa spa integration review fixes: Move instance attributes as class attributes. Fix code comments.
1 parent 127c27c commit 84a975b

File tree

6 files changed

+160
-2
lines changed

6 files changed

+160
-2
lines changed

homeassistant/components/balboa/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818

1919
_LOGGER = logging.getLogger(__name__)
2020

21-
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.FAN, Platform.LIGHT]
21+
PLATFORMS = [
22+
Platform.BINARY_SENSOR,
23+
Platform.CLIMATE,
24+
Platform.FAN,
25+
Platform.LIGHT,
26+
Platform.SELECT,
27+
]
2228

2329

2430
KEEP_ALIVE_INTERVAL = timedelta(minutes=1)

homeassistant/components/balboa/icons.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
"off": "mdi:pump-off"
2828
}
2929
}
30+
},
31+
"select": {
32+
"temperature_range": {
33+
"default": "mdi:thermometer-lines"
34+
}
3035
}
3136
}
3237
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Support for Spa Client selects."""
2+
3+
from pybalboa import SpaClient, SpaControl
4+
from pybalboa.enums import LowHighRange
5+
6+
from homeassistant.components.select import SelectEntity
7+
from homeassistant.config_entries import ConfigEntry
8+
from homeassistant.core import HomeAssistant
9+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
10+
11+
from .const import DOMAIN
12+
from .entity import BalboaEntity
13+
14+
15+
async def async_setup_entry(
16+
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
17+
) -> None:
18+
"""Set up the spa select entity."""
19+
spa: SpaClient = hass.data[DOMAIN][entry.entry_id]
20+
async_add_entities([BalboaTempRangeSelectEntity(spa.temperature_range)])
21+
22+
23+
class BalboaTempRangeSelectEntity(BalboaEntity, SelectEntity):
24+
"""Representation of a Temperature Range select."""
25+
26+
_attr_icon = "mdi:thermometer-lines"
27+
_attr_name = "Temperature range"
28+
_attr_unique_id = "temperature_range"
29+
_attr_translation_key = "temperature_range"
30+
_attr_options = [
31+
LowHighRange.LOW.name.lower(),
32+
LowHighRange.HIGH.name.lower(),
33+
]
34+
35+
def __init__(self, control: SpaControl) -> None:
36+
"""Initialise the select."""
37+
super().__init__(control.client, "TempHiLow")
38+
self._control = control
39+
40+
@property
41+
def current_option(self) -> str | None:
42+
"""Return current select option."""
43+
if self._control.state == LowHighRange.HIGH:
44+
return LowHighRange.HIGH.name.lower()
45+
return LowHighRange.LOW.name.lower()
46+
47+
async def async_select_option(self, option: str) -> None:
48+
"""Select temperature range high/low mode."""
49+
if option == LowHighRange.HIGH.name.lower():
50+
await self._client.set_temperature_range(LowHighRange.HIGH)
51+
else:
52+
await self._client.set_temperature_range(LowHighRange.LOW)

homeassistant/components/balboa/strings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@
6565
"only_light": {
6666
"name": "Light"
6767
}
68+
},
69+
"select": {
70+
"temperature_range": {
71+
"name": "Temperature range",
72+
"state": {
73+
"low": "Low",
74+
"high": "High"
75+
}
76+
}
6877
}
6978
}
7079
}

tests/components/balboa/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections.abc import Callable, Generator
66
from unittest.mock import AsyncMock, MagicMock, patch
77

8-
from pybalboa.enums import HeatMode
8+
from pybalboa.enums import HeatMode, LowHighRange
99
import pytest
1010

1111
from homeassistant.core import HomeAssistant
@@ -60,5 +60,6 @@ def emit(_):
6060
client.heat_state = 2
6161
client.lights = []
6262
client.pumps = []
63+
client.temperature_range.state = LowHighRange.LOW
6364

6465
yield client
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""Tests of the select entity of the balboa integration."""
2+
3+
from __future__ import annotations
4+
5+
from unittest.mock import MagicMock, call
6+
7+
from pybalboa import SpaControl
8+
from pybalboa.enums import LowHighRange
9+
import pytest
10+
11+
from homeassistant.components.select import (
12+
ATTR_OPTION,
13+
DOMAIN as SELECT_DOMAIN,
14+
SERVICE_SELECT_OPTION,
15+
)
16+
from homeassistant.const import ATTR_ENTITY_ID
17+
from homeassistant.core import HomeAssistant
18+
19+
from . import client_update, init_integration
20+
21+
ENTITY_SELECT = "select.fakespa_temperature_range"
22+
23+
24+
@pytest.fixture
25+
def mock_select(client: MagicMock):
26+
"""Return a mock switch."""
27+
select = MagicMock(SpaControl)
28+
29+
async def set_state(state: LowHighRange):
30+
select.state = state # mock the spacontrol state
31+
32+
select.client = client
33+
select.state = LowHighRange.LOW
34+
select.set_state = set_state
35+
client.temperature_range = select
36+
return select
37+
38+
39+
async def test_select(hass: HomeAssistant, client: MagicMock, mock_select) -> None:
40+
"""Test spa temperature range select."""
41+
await init_integration(hass)
42+
43+
# check if the initial state is off
44+
state = hass.states.get(ENTITY_SELECT)
45+
assert state.state == LowHighRange.LOW.name.lower()
46+
47+
# test high state
48+
await _select_option_and_wait(hass, ENTITY_SELECT, LowHighRange.HIGH.name.lower())
49+
assert client.set_temperature_range.call_count == 1
50+
assert client.set_temperature_range.call_args == call(LowHighRange.HIGH)
51+
52+
# test back to low state
53+
await _select_option_and_wait(hass, ENTITY_SELECT, LowHighRange.LOW.name.lower())
54+
assert client.set_temperature_range.call_count == 2 # total call count
55+
assert client.set_temperature_range.call_args == call(LowHighRange.LOW)
56+
57+
58+
async def test_selected_option(
59+
hass: HomeAssistant, client: MagicMock, mock_select
60+
) -> None:
61+
"""Test spa temperature range selected option."""
62+
63+
await init_integration(hass)
64+
65+
# ensure initial low state
66+
state = hass.states.get(ENTITY_SELECT)
67+
assert state.state == LowHighRange.LOW.name.lower()
68+
69+
# ensure high state
70+
mock_select.state = LowHighRange.HIGH
71+
state = await client_update(hass, client, ENTITY_SELECT)
72+
assert state.state == LowHighRange.HIGH.name.lower()
73+
74+
75+
async def _select_option_and_wait(hass: HomeAssistant | None, entity, option):
76+
await hass.services.async_call(
77+
SELECT_DOMAIN,
78+
SERVICE_SELECT_OPTION,
79+
{
80+
ATTR_ENTITY_ID: entity,
81+
ATTR_OPTION: option,
82+
},
83+
blocking=True,
84+
)
85+
await hass.async_block_till_done()

0 commit comments

Comments
 (0)