Skip to content

Commit

Permalink
Correct range for nibe_heatpump numbers (#102553)
Browse files Browse the repository at this point in the history
  • Loading branch information
elupus committed Oct 22, 2023
1 parent e936ca0 commit 7d2fa5b
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 14 deletions.
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,6 @@ omit =
homeassistant/components/nibe_heatpump/__init__.py
homeassistant/components/nibe_heatpump/climate.py
homeassistant/components/nibe_heatpump/binary_sensor.py
homeassistant/components/nibe_heatpump/number.py
homeassistant/components/nibe_heatpump/select.py
homeassistant/components/nibe_heatpump/sensor.py
homeassistant/components/nibe_heatpump/switch.py
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/nibe_heatpump/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ def __init__(self, coordinator: Coordinator, coil: Coil) -> None:
self._attr_native_min_value,
self._attr_native_max_value,
) = _get_numeric_limits(coil.size)
self._attr_native_min_value /= coil.factor
self._attr_native_max_value /= coil.factor
else:
self._attr_native_min_value = float(coil.min)
self._attr_native_max_value = float(coil.max)
Expand Down
17 changes: 17 additions & 0 deletions tests/components/nibe_heatpump/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

from typing import Any

from nibe.heatpump import Model

from homeassistant.components.nibe_heatpump import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant

from tests.common import MockConfigEntry

MOCK_ENTRY_DATA = {
"model": None,
"ip_address": "127.0.0.1",
"listening_port": 9999,
"remote_read_port": 10000,
"remote_write_port": 10001,
"word_swap": True,
"connection_type": "nibegw",
}


async def async_add_entry(hass: HomeAssistant, data: dict[str, Any]) -> None:
"""Add entry and get the coordinator."""
Expand All @@ -17,3 +29,8 @@ async def async_add_entry(hass: HomeAssistant, data: dict[str, Any]) -> None:
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED


async def async_add_model(hass: HomeAssistant, model: Model):
"""Add entry of specific model."""
await async_add_entry(hass, {**MOCK_ENTRY_DATA, "model": model.name})
13 changes: 12 additions & 1 deletion tests/components/nibe_heatpump/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,15 @@ async def read_coils(

mock_connection.read_coil = read_coil
mock_connection.read_coils = read_coils
return coils

# pylint: disable-next=import-outside-toplevel
from homeassistant.components.nibe_heatpump import HeatPump

get_coils_original = HeatPump.get_coils

def get_coils(x):
coils_data = get_coils_original(x)
return [coil for coil in coils_data if coil.address in coils]

with patch.object(HeatPump, "get_coils", new=get_coils):
yield coils
135 changes: 135 additions & 0 deletions tests/components/nibe_heatpump/snapshots/test_number.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# serializer version: 1
# name: test_update[Model.F1155-47011-number.heat_offset_s1_47011--10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'F1155 Heat Offset S1',
'max': 10.0,
'min': -10.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1.0,
}),
'context': <ANY>,
'entity_id': 'number.heat_offset_s1_47011',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '-10.0',
})
# ---
# name: test_update[Model.F1155-47011-number.heat_offset_s1_47011-10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'F1155 Heat Offset S1',
'max': 10.0,
'min': -10.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1.0,
}),
'context': <ANY>,
'entity_id': 'number.heat_offset_s1_47011',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '10.0',
})
# ---
# name: test_update[Model.F1155-47062-number.heat_offset_s1_47011-None]
None
# ---
# name: test_update[Model.F750-47062-number.hw_charge_offset_47062--10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'F750 HW charge offset',
'max': 12.7,
'min': -12.8,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': '°C',
}),
'context': <ANY>,
'entity_id': 'number.hw_charge_offset_47062',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '-10.0',
})
# ---
# name: test_update[Model.F750-47062-number.hw_charge_offset_47062-10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'F750 HW charge offset',
'max': 12.7,
'min': -12.8,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': '°C',
}),
'context': <ANY>,
'entity_id': 'number.hw_charge_offset_47062',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '10.0',
})
# ---
# name: test_update[Model.F750-47062-number.hw_charge_offset_47062-None]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'F750 HW charge offset',
'max': 12.7,
'min': -12.8,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 0.1,
'unit_of_measurement': '°C',
}),
'context': <ANY>,
'entity_id': 'number.hw_charge_offset_47062',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_update[Model.S320-40031-number.heating_offset_climate_system_1_40031--10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'S320 Heating offset climate system 1',
'max': 10.0,
'min': -10.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1.0,
}),
'context': <ANY>,
'entity_id': 'number.heating_offset_climate_system_1_40031',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '-10.0',
})
# ---
# name: test_update[Model.S320-40031-number.heating_offset_climate_system_1_40031-10]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'S320 Heating offset climate system 1',
'max': 10.0,
'min': -10.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1.0,
}),
'context': <ANY>,
'entity_id': 'number.heating_offset_climate_system_1_40031',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '10.0',
})
# ---
# name: test_update[Model.S320-40031-number.heating_offset_climate_system_1_40031-None]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'S320 Heating offset climate system 1',
'max': 10.0,
'min': -10.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1.0,
}),
'context': <ANY>,
'entity_id': 'number.heating_offset_climate_system_1_40031',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
14 changes: 2 additions & 12 deletions tests/components/nibe_heatpump/test_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,10 @@
)
from homeassistant.core import HomeAssistant

from . import async_add_entry
from . import async_add_model

from tests.common import async_fire_time_changed

MOCK_ENTRY_DATA = {
"model": None,
"ip_address": "127.0.0.1",
"listening_port": 9999,
"remote_read_port": 10000,
"remote_write_port": 10001,
"word_swap": True,
"connection_type": "nibegw",
}


@pytest.fixture(autouse=True)
async def fixture_single_platform():
Expand Down Expand Up @@ -62,7 +52,7 @@ async def test_reset_button(
coils[unit.alarm_reset] = 0
coils[unit.alarm] = 0

await async_add_entry(hass, {**MOCK_ENTRY_DATA, "model": model.name})
await async_add_model(hass, model)

state = hass.states.get(entity_id)
assert state
Expand Down
109 changes: 109 additions & 0 deletions tests/components/nibe_heatpump/test_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""Test the Nibe Heat Pump config flow."""
from typing import Any
from unittest.mock import AsyncMock, patch

from nibe.coil import CoilData
from nibe.heatpump import Model
import pytest
from syrupy import SnapshotAssertion

from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as PLATFORM_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant

from . import async_add_model


@pytest.fixture(autouse=True)
async def fixture_single_platform():
"""Only allow this platform to load."""
with patch("homeassistant.components.nibe_heatpump.PLATFORMS", [Platform.NUMBER]):
yield


@pytest.mark.parametrize(
("model", "address", "entity_id", "value"),
[
# Tests for S series coils with min/max
(Model.S320, 40031, "number.heating_offset_climate_system_1_40031", 10),
(Model.S320, 40031, "number.heating_offset_climate_system_1_40031", -10),
(Model.S320, 40031, "number.heating_offset_climate_system_1_40031", None),
# Tests for F series coils with min/max
(Model.F1155, 47011, "number.heat_offset_s1_47011", 10),
(Model.F1155, 47011, "number.heat_offset_s1_47011", -10),
(Model.F1155, 47062, "number.heat_offset_s1_47011", None),
# Tests for F series coils without min/max
(Model.F750, 47062, "number.hw_charge_offset_47062", 10),
(Model.F750, 47062, "number.hw_charge_offset_47062", -10),
(Model.F750, 47062, "number.hw_charge_offset_47062", None),
],
)
async def test_update(
hass: HomeAssistant,
model: Model,
entity_id: str,
address: int,
value: Any,
coils: dict[int, Any],
entity_registry_enabled_by_default: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test setting of value."""
coils[address] = value

await async_add_model(hass, model)

await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state == snapshot


@pytest.mark.parametrize(
("model", "address", "entity_id", "value"),
[
(Model.S320, 40031, "number.heating_offset_climate_system_1_40031", 10),
(Model.S320, 40031, "number.heating_offset_climate_system_1_40031", -10),
(Model.F1155, 47011, "number.heat_offset_s1_47011", 10),
(Model.F1155, 47011, "number.heat_offset_s1_47011", -10),
(Model.F750, 47062, "number.hw_charge_offset_47062", 10),
],
)
async def test_set_value(
hass: HomeAssistant,
mock_connection: AsyncMock,
model: Model,
entity_id: str,
address: int,
value: Any,
coils: dict[int, Any],
entity_registry_enabled_by_default: None,
) -> None:
"""Test setting of value."""
coils[address] = 0

await async_add_model(hass, model)

await hass.async_block_till_done()
assert hass.states.get(entity_id)

# Write value
await hass.services.async_call(
PLATFORM_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: value},
blocking=True,
)

await hass.async_block_till_done()

# Verify written
args = mock_connection.write_coil.call_args
assert args
coil = args.args[0]
assert isinstance(coil, CoilData)
assert coil.coil.address == address
assert coil.value == value

0 comments on commit 7d2fa5b

Please sign in to comment.