Skip to content

Commit c2b284d

Browse files
vincentwolsinkNoRi2909joostlek
authored
Add humidity (steamer) control to Huum (#150330)
Co-authored-by: Norbert Rittel <norbert@rittel.de> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
1 parent b760bf3 commit c2b284d

File tree

6 files changed

+218
-1
lines changed

6 files changed

+218
-1
lines changed

homeassistant/components/huum/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
DOMAIN = "huum"
66

7-
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.LIGHT]
7+
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.LIGHT, Platform.NUMBER]
88

99
CONFIG_STEAMER = 1
1010
CONFIG_LIGHT = 2
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"entity": {
3+
"number": {
4+
"humidity": {
5+
"default": "mdi:water",
6+
"range": {
7+
"0": "mdi:water-off",
8+
"1": "mdi:water"
9+
}
10+
}
11+
}
12+
}
13+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Control for steamer."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from huum.const import SaunaStatus
8+
9+
from homeassistant.components.number import NumberEntity
10+
from homeassistant.core import HomeAssistant
11+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
12+
13+
from .const import CONFIG_STEAMER, CONFIG_STEAMER_AND_LIGHT
14+
from .coordinator import HuumConfigEntry, HuumDataUpdateCoordinator
15+
from .entity import HuumBaseEntity
16+
17+
_LOGGER = logging.getLogger(__name__)
18+
19+
20+
async def async_setup_entry(
21+
hass: HomeAssistant,
22+
config_entry: HuumConfigEntry,
23+
async_add_entities: AddConfigEntryEntitiesCallback,
24+
) -> None:
25+
"""Set up steamer if applicable."""
26+
coordinator = config_entry.runtime_data
27+
28+
# Light is configured for this sauna.
29+
if coordinator.data.config in [CONFIG_STEAMER, CONFIG_STEAMER_AND_LIGHT]:
30+
async_add_entities([HuumSteamer(coordinator)])
31+
32+
33+
class HuumSteamer(HuumBaseEntity, NumberEntity):
34+
"""Representation of a steamer."""
35+
36+
_attr_translation_key = "humidity"
37+
_attr_native_max_value = 10
38+
_attr_native_min_value = 0
39+
_attr_native_step = 1
40+
41+
def __init__(self, coordinator: HuumDataUpdateCoordinator) -> None:
42+
"""Initialize the steamer."""
43+
super().__init__(coordinator)
44+
45+
self._attr_unique_id = coordinator.config_entry.entry_id
46+
47+
@property
48+
def native_value(self) -> float:
49+
"""Return the current value."""
50+
return self.coordinator.data.humidity
51+
52+
async def async_set_native_value(self, value: float) -> None:
53+
"""Update the current value."""
54+
target_temperature = self.coordinator.data.target_temperature
55+
if (
56+
not target_temperature
57+
or self.coordinator.data.status != SaunaStatus.ONLINE_HEATING
58+
):
59+
return
60+
61+
await self.coordinator.huum.turn_on(
62+
temperature=target_temperature, humidity=int(value)
63+
)
64+
await self.coordinator.async_refresh()

homeassistant/components/huum/strings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
"light": {
2525
"name": "[%key:component::light::title%]"
2626
}
27+
},
28+
"number": {
29+
"humidity": {
30+
"name": "[%key:component::sensor::entity_component::humidity::name%]"
31+
}
2732
}
2833
}
2934
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# serializer version: 1
2+
# name: test_number_entity[number.huum_sauna_humidity-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': dict({
8+
'max': 10,
9+
'min': 0,
10+
'mode': <NumberMode.AUTO: 'auto'>,
11+
'step': 1,
12+
}),
13+
'config_entry_id': <ANY>,
14+
'config_subentry_id': <ANY>,
15+
'device_class': None,
16+
'device_id': <ANY>,
17+
'disabled_by': None,
18+
'domain': 'number',
19+
'entity_category': None,
20+
'entity_id': 'number.huum_sauna_humidity',
21+
'has_entity_name': True,
22+
'hidden_by': None,
23+
'icon': None,
24+
'id': <ANY>,
25+
'labels': set({
26+
}),
27+
'name': None,
28+
'options': dict({
29+
}),
30+
'original_device_class': None,
31+
'original_icon': None,
32+
'original_name': 'Humidity',
33+
'platform': 'huum',
34+
'previous_unique_id': None,
35+
'suggested_object_id': None,
36+
'supported_features': 0,
37+
'translation_key': 'humidity',
38+
'unique_id': 'AABBCC112233',
39+
'unit_of_measurement': None,
40+
})
41+
# ---
42+
# name: test_number_entity[number.huum_sauna_humidity-state]
43+
StateSnapshot({
44+
'attributes': ReadOnlyDict({
45+
'friendly_name': 'Huum sauna Humidity',
46+
'max': 10,
47+
'min': 0,
48+
'mode': <NumberMode.AUTO: 'auto'>,
49+
'step': 1,
50+
}),
51+
'context': <ANY>,
52+
'entity_id': 'number.huum_sauna_humidity',
53+
'last_changed': <ANY>,
54+
'last_reported': <ANY>,
55+
'last_updated': <ANY>,
56+
'state': '5',
57+
})
58+
# ---

tests/components/huum/test_number.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Tests for the Huum number entity."""
2+
3+
from unittest.mock import AsyncMock
4+
5+
from huum.const import SaunaStatus
6+
from syrupy.assertion import SnapshotAssertion
7+
8+
from homeassistant.components.number import (
9+
ATTR_VALUE,
10+
DOMAIN as NUMBER_DOMAIN,
11+
SERVICE_SET_VALUE,
12+
)
13+
from homeassistant.const import ATTR_ENTITY_ID, Platform
14+
from homeassistant.core import HomeAssistant
15+
from homeassistant.helpers import entity_registry as er
16+
17+
from . import setup_with_selected_platforms
18+
19+
from tests.common import MockConfigEntry, snapshot_platform
20+
21+
ENTITY_ID = "number.huum_sauna_humidity"
22+
23+
24+
async def test_number_entity(
25+
hass: HomeAssistant,
26+
mock_huum: AsyncMock,
27+
mock_config_entry: MockConfigEntry,
28+
snapshot: SnapshotAssertion,
29+
entity_registry: er.EntityRegistry,
30+
) -> None:
31+
"""Test the initial parameters."""
32+
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.NUMBER])
33+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
34+
35+
36+
async def test_set_humidity(
37+
hass: HomeAssistant,
38+
mock_huum: AsyncMock,
39+
mock_config_entry: MockConfigEntry,
40+
) -> None:
41+
"""Test setting the humidity."""
42+
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.NUMBER])
43+
44+
mock_huum.status = SaunaStatus.ONLINE_HEATING
45+
await hass.services.async_call(
46+
NUMBER_DOMAIN,
47+
SERVICE_SET_VALUE,
48+
{
49+
ATTR_ENTITY_ID: ENTITY_ID,
50+
ATTR_VALUE: 5,
51+
},
52+
blocking=True,
53+
)
54+
55+
mock_huum.turn_on.assert_called_once_with(temperature=80, humidity=5)
56+
57+
58+
async def test_dont_set_humidity_when_sauna_not_heating(
59+
hass: HomeAssistant,
60+
mock_huum: AsyncMock,
61+
mock_config_entry: MockConfigEntry,
62+
) -> None:
63+
"""Test setting the humidity."""
64+
await setup_with_selected_platforms(hass, mock_config_entry, [Platform.NUMBER])
65+
66+
mock_huum.status = SaunaStatus.ONLINE_NOT_HEATING
67+
await hass.services.async_call(
68+
NUMBER_DOMAIN,
69+
SERVICE_SET_VALUE,
70+
{
71+
ATTR_ENTITY_ID: ENTITY_ID,
72+
ATTR_VALUE: 5,
73+
},
74+
blocking=True,
75+
)
76+
77+
mock_huum.turn_on.assert_not_called()

0 commit comments

Comments
 (0)