mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Add support for Shelly BLU TRV (#128439)
* feat: add support for Shelly BLU TRV * chore: apply some fixes * make BLUTRV a separate device * apply review comment * review comments and small optimization * add HVACMode.OFF * a couple of fixes * 2 more fixes * better approach * cleanup * small optimization * remove cooling as not supported by firmware * tweaks * humidity and entity name * fix naming * allign async_set_hvac_mode * align settings * restore temp * fix * remove OFF * cleanup * hvac_mode * add tests * typo * more tests * bump aioshelly
This commit is contained in:
parent
ee865d2f0f
commit
6e1a13f878
@ -7,7 +7,7 @@ from dataclasses import asdict, dataclass
|
|||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from aioshelly.block_device import Block
|
from aioshelly.block_device import Block
|
||||||
from aioshelly.const import RPC_GENERATIONS
|
from aioshelly.const import BLU_TRV_IDENTIFIER, BLU_TRV_MODEL_NAME, RPC_GENERATIONS
|
||||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
||||||
|
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
@ -22,7 +22,11 @@ from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
|||||||
from homeassistant.core import HomeAssistant, State, callback
|
from homeassistant.core import HomeAssistant, State, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
from homeassistant.helpers.device_registry import (
|
||||||
|
CONNECTION_BLUETOOTH,
|
||||||
|
CONNECTION_NETWORK_MAC,
|
||||||
|
DeviceInfo,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||||
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
|
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
|
||||||
@ -31,6 +35,7 @@ from homeassistant.util.unit_conversion import TemperatureConverter
|
|||||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
BLU_TRV_TEMPERATURE_SETTINGS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
NOT_CALIBRATED_ISSUE_ID,
|
NOT_CALIBRATED_ISSUE_ID,
|
||||||
@ -124,6 +129,7 @@ def async_setup_rpc_entry(
|
|||||||
coordinator = config_entry.runtime_data.rpc
|
coordinator = config_entry.runtime_data.rpc
|
||||||
assert coordinator
|
assert coordinator
|
||||||
climate_key_ids = get_rpc_key_ids(coordinator.device.status, "thermostat")
|
climate_key_ids = get_rpc_key_ids(coordinator.device.status, "thermostat")
|
||||||
|
blutrv_key_ids = get_rpc_key_ids(coordinator.device.status, BLU_TRV_IDENTIFIER)
|
||||||
|
|
||||||
climate_ids = []
|
climate_ids = []
|
||||||
for id_ in climate_key_ids:
|
for id_ in climate_key_ids:
|
||||||
@ -139,10 +145,11 @@ def async_setup_rpc_entry(
|
|||||||
unique_id = f"{coordinator.mac}-switch:{id_}"
|
unique_id = f"{coordinator.mac}-switch:{id_}"
|
||||||
async_remove_shelly_entity(hass, "switch", unique_id)
|
async_remove_shelly_entity(hass, "switch", unique_id)
|
||||||
|
|
||||||
if not climate_ids:
|
if climate_ids:
|
||||||
return
|
async_add_entities(RpcClimate(coordinator, id_) for id_ in climate_ids)
|
||||||
|
|
||||||
async_add_entities(RpcClimate(coordinator, id_) for id_ in climate_ids)
|
if blutrv_key_ids:
|
||||||
|
async_add_entities(RpcBluTrvClimate(coordinator, id_) for id_ in blutrv_key_ids)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -526,3 +533,75 @@ class RpcClimate(ShellyRpcEntity, ClimateEntity):
|
|||||||
await self.call_rpc(
|
await self.call_rpc(
|
||||||
"Thermostat.SetConfig", {"config": {"id": self._id, "enable": mode}}
|
"Thermostat.SetConfig", {"config": {"id": self._id, "enable": mode}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RpcBluTrvClimate(ShellyRpcEntity, ClimateEntity):
|
||||||
|
"""Entity that controls a thermostat on RPC based Shelly devices."""
|
||||||
|
|
||||||
|
_attr_max_temp = BLU_TRV_TEMPERATURE_SETTINGS["max"]
|
||||||
|
_attr_min_temp = BLU_TRV_TEMPERATURE_SETTINGS["min"]
|
||||||
|
_attr_supported_features = (
|
||||||
|
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_attr_hvac_modes = [HVACMode.HEAT]
|
||||||
|
_attr_hvac_mode = HVACMode.HEAT
|
||||||
|
_attr_target_temperature_step = BLU_TRV_TEMPERATURE_SETTINGS["step"]
|
||||||
|
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
|
def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None:
|
||||||
|
"""Initialize."""
|
||||||
|
|
||||||
|
super().__init__(coordinator, f"{BLU_TRV_IDENTIFIER}:{id_}")
|
||||||
|
self._id = id_
|
||||||
|
self._config = coordinator.device.config[f"{BLU_TRV_IDENTIFIER}:{id_}"]
|
||||||
|
ble_addr: str = self._config["addr"]
|
||||||
|
self._attr_unique_id = f"{ble_addr}-{self.key}"
|
||||||
|
name = self._config["name"] or f"shellyblutrv-{ble_addr.replace(":", "")}"
|
||||||
|
model_id = self._config.get("local_name")
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
connections={(CONNECTION_BLUETOOTH, ble_addr)},
|
||||||
|
identifiers={(DOMAIN, ble_addr)},
|
||||||
|
via_device=(DOMAIN, self.coordinator.mac),
|
||||||
|
manufacturer="Shelly",
|
||||||
|
model=BLU_TRV_MODEL_NAME.get(model_id),
|
||||||
|
model_id=model_id,
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
|
# Added intentionally to the constructor to avoid double name from base class
|
||||||
|
self._attr_name = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_temperature(self) -> float | None:
|
||||||
|
"""Set target temperature."""
|
||||||
|
if not self._config["enable"]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return cast(float, self.status["target_C"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_temperature(self) -> float | None:
|
||||||
|
"""Return current temperature."""
|
||||||
|
return cast(float, self.status["current_C"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hvac_action(self) -> HVACAction:
|
||||||
|
"""HVAC current action."""
|
||||||
|
if not self.status["pos"]:
|
||||||
|
return HVACAction.IDLE
|
||||||
|
|
||||||
|
return HVACAction.HEATING
|
||||||
|
|
||||||
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||||
|
"""Set new target temperature."""
|
||||||
|
if (target_temp := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.call_rpc(
|
||||||
|
"BluTRV.Call",
|
||||||
|
{
|
||||||
|
"id": self._id,
|
||||||
|
"method": "Trv.SetTarget",
|
||||||
|
"params": {"id": 0, "target_C": target_temp},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@ -187,6 +187,13 @@ RPC_THERMOSTAT_SETTINGS: Final = {
|
|||||||
"step": 0.5,
|
"step": 0.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BLU_TRV_TEMPERATURE_SETTINGS: Final = {
|
||||||
|
"min": 4,
|
||||||
|
"max": 30,
|
||||||
|
"step": 0.1,
|
||||||
|
"default": 20.0,
|
||||||
|
}
|
||||||
|
|
||||||
# Kelvin value for colorTemp
|
# Kelvin value for colorTemp
|
||||||
KELVIN_MAX_VALUE: Final = 6500
|
KELVIN_MAX_VALUE: Final = 6500
|
||||||
KELVIN_MIN_VALUE_WHITE: Final = 2700
|
KELVIN_MIN_VALUE_WHITE: Final = 2700
|
||||||
|
@ -154,6 +154,7 @@ class ShellyCoordinatorBase[_DeviceT: BlockDevice | RpcDevice](
|
|||||||
config_entry_id=self.entry.entry_id,
|
config_entry_id=self.entry.entry_id,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
connections={(CONNECTION_NETWORK_MAC, self.mac)},
|
connections={(CONNECTION_NETWORK_MAC, self.mac)},
|
||||||
|
identifiers={(DOMAIN, self.mac)},
|
||||||
manufacturer="Shelly",
|
manufacturer="Shelly",
|
||||||
model=MODEL_NAMES.get(self.model),
|
model=MODEL_NAMES.get(self.model),
|
||||||
model_id=self.model,
|
model_id=self.model,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aioshelly"],
|
"loggers": ["aioshelly"],
|
||||||
"requirements": ["aioshelly==12.2.0"],
|
"requirements": ["aioshelly==12.3.0"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_http._tcp.local.",
|
"type": "_http._tcp.local.",
|
||||||
|
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@ -368,7 +368,7 @@ aioruuvigateway==0.1.0
|
|||||||
aiosenz==1.0.0
|
aiosenz==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==12.2.0
|
aioshelly==12.3.0
|
||||||
|
|
||||||
# homeassistant.components.skybell
|
# homeassistant.components.skybell
|
||||||
aioskybell==22.7.0
|
aioskybell==22.7.0
|
||||||
|
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@ -350,7 +350,7 @@ aioruuvigateway==0.1.0
|
|||||||
aiosenz==1.0.0
|
aiosenz==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==12.2.0
|
aioshelly==12.3.0
|
||||||
|
|
||||||
# homeassistant.components.skybell
|
# homeassistant.components.skybell
|
||||||
aioskybell==22.7.0
|
aioskybell==22.7.0
|
||||||
|
@ -148,6 +148,13 @@ def get_entity_state(hass: HomeAssistant, entity_id: str) -> str:
|
|||||||
return entity.state
|
return entity.state
|
||||||
|
|
||||||
|
|
||||||
|
def get_entity_attribute(hass: HomeAssistant, entity_id: str, attribute: str) -> str:
|
||||||
|
"""Return entity attribute."""
|
||||||
|
entity = hass.states.get(entity_id)
|
||||||
|
assert entity
|
||||||
|
return entity.attributes[attribute]
|
||||||
|
|
||||||
|
|
||||||
def register_device(
|
def register_device(
|
||||||
device_registry: DeviceRegistry, config_entry: ConfigEntry
|
device_registry: DeviceRegistry, config_entry: ConfigEntry
|
||||||
) -> DeviceEntry:
|
) -> DeviceEntry:
|
||||||
|
@ -202,6 +202,64 @@ MOCK_CONFIG = {
|
|||||||
"voltmeter:100": {"xvoltage": {"unit": "ppm"}},
|
"voltmeter:100": {"xvoltage": {"unit": "ppm"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MOCK_BLU_TRV_REMOTE_CONFIG = {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"key": "blutrv:200",
|
||||||
|
"status": {
|
||||||
|
"id": 200,
|
||||||
|
"target_C": 17.1,
|
||||||
|
"current_C": 17.1,
|
||||||
|
"pos": 0,
|
||||||
|
"rssi": -60,
|
||||||
|
"battery": 100,
|
||||||
|
"packet_id": 58,
|
||||||
|
"last_updated_ts": 1734967725,
|
||||||
|
"paired": True,
|
||||||
|
"rpc": True,
|
||||||
|
"rsv": 61,
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"id": 200,
|
||||||
|
"addr": "f8:44:77:25:f0:dd",
|
||||||
|
"name": "TRV-Name",
|
||||||
|
"key": None,
|
||||||
|
"trv": "bthomedevice:200",
|
||||||
|
"temp_sensors": [],
|
||||||
|
"dw_sensors": [],
|
||||||
|
"override_delay": 30,
|
||||||
|
"meta": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"blutrv:200": {
|
||||||
|
"id": 0,
|
||||||
|
"enable": True,
|
||||||
|
"min_valve_position": 0,
|
||||||
|
"default_boost_duration": 1800,
|
||||||
|
"default_override_duration": 2147483647,
|
||||||
|
"default_override_target_C": 8,
|
||||||
|
"addr": "f8:44:77:25:f0:dd",
|
||||||
|
"name": "TRV-Name",
|
||||||
|
"local_name": "SBTR-001AEU",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MOCK_BLU_TRV_REMOTE_STATUS = {
|
||||||
|
"blutrv:200": {
|
||||||
|
"id": 0,
|
||||||
|
"pos": 0,
|
||||||
|
"steps": 0,
|
||||||
|
"current_C": 15.2,
|
||||||
|
"target_C": 17.1,
|
||||||
|
"schedule_rev": 0,
|
||||||
|
"errors": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MOCK_SHELLY_COAP = {
|
MOCK_SHELLY_COAP = {
|
||||||
"mac": MOCK_MAC,
|
"mac": MOCK_MAC,
|
||||||
"auth": False,
|
"auth": False,
|
||||||
@ -373,6 +431,24 @@ def _mock_rpc_device(version: str | None = None):
|
|||||||
return device
|
return device
|
||||||
|
|
||||||
|
|
||||||
|
def _mock_blu_rtv_device(version: str | None = None):
|
||||||
|
"""Mock rpc (Gen2, Websocket) device."""
|
||||||
|
device = Mock(
|
||||||
|
spec=RpcDevice,
|
||||||
|
config=MOCK_CONFIG | MOCK_BLU_TRV_REMOTE_CONFIG,
|
||||||
|
event={},
|
||||||
|
shelly=MOCK_SHELLY_RPC,
|
||||||
|
version=version or "1.0.0",
|
||||||
|
hostname="test-host",
|
||||||
|
status=MOCK_STATUS_RPC | MOCK_BLU_TRV_REMOTE_STATUS,
|
||||||
|
firmware_version="some fw string",
|
||||||
|
initialized=True,
|
||||||
|
connected=True,
|
||||||
|
)
|
||||||
|
type(device).name = PropertyMock(return_value="Test name")
|
||||||
|
return device
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def mock_rpc_device():
|
async def mock_rpc_device():
|
||||||
"""Mock rpc (Gen2, Websocket) device with BLE support."""
|
"""Mock rpc (Gen2, Websocket) device with BLE support."""
|
||||||
@ -420,3 +496,24 @@ async def mock_rpc_device():
|
|||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_bluetooth(enable_bluetooth: None) -> None:
|
def mock_bluetooth(enable_bluetooth: None) -> None:
|
||||||
"""Auto mock bluetooth."""
|
"""Auto mock bluetooth."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
async def mock_blu_trv():
|
||||||
|
"""Mock BLU TRV."""
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch("aioshelly.rpc_device.RpcDevice.create") as blu_trv_device_mock,
|
||||||
|
patch("homeassistant.components.shelly.bluetooth.async_start_scanner"),
|
||||||
|
):
|
||||||
|
|
||||||
|
def update():
|
||||||
|
blu_trv_device_mock.return_value.subscribe_updates.call_args[0][0](
|
||||||
|
{}, RpcUpdateType.STATUS
|
||||||
|
)
|
||||||
|
|
||||||
|
device = _mock_blu_rtv_device()
|
||||||
|
blu_trv_device_mock.return_value = device
|
||||||
|
blu_trv_device_mock.return_value.mock_update = Mock(side_effect=update)
|
||||||
|
|
||||||
|
yield blu_trv_device_mock.return_value
|
||||||
|
@ -3,7 +3,12 @@
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from unittest.mock import AsyncMock, Mock, PropertyMock
|
from unittest.mock import AsyncMock, Mock, PropertyMock
|
||||||
|
|
||||||
from aioshelly.const import MODEL_VALVE, MODEL_WALL_DISPLAY
|
from aioshelly.const import (
|
||||||
|
BLU_TRV_IDENTIFIER,
|
||||||
|
MODEL_BLU_GATEWAY_GEN3,
|
||||||
|
MODEL_VALVE,
|
||||||
|
MODEL_WALL_DISPLAY,
|
||||||
|
)
|
||||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -37,7 +42,13 @@ from homeassistant.helpers.device_registry import DeviceRegistry
|
|||||||
from homeassistant.helpers.entity_registry import EntityRegistry
|
from homeassistant.helpers.entity_registry import EntityRegistry
|
||||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
||||||
|
|
||||||
from . import MOCK_MAC, init_integration, register_device, register_entity
|
from . import (
|
||||||
|
MOCK_MAC,
|
||||||
|
get_entity_attribute,
|
||||||
|
init_integration,
|
||||||
|
register_device,
|
||||||
|
register_entity,
|
||||||
|
)
|
||||||
from .conftest import MOCK_STATUS_COAP
|
from .conftest import MOCK_STATUS_COAP
|
||||||
|
|
||||||
from tests.common import mock_restore_cache, mock_restore_cache_with_extra_data
|
from tests.common import mock_restore_cache, mock_restore_cache_with_extra_data
|
||||||
@ -294,13 +305,13 @@ async def test_block_restored_climate(
|
|||||||
assert hass.states.get(entity_id).attributes.get("temperature") == 22.0
|
assert hass.states.get(entity_id).attributes.get("temperature") == 22.0
|
||||||
|
|
||||||
|
|
||||||
async def test_block_restored_climate_us_customery(
|
async def test_block_restored_climate_us_customary(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_block_device: Mock,
|
mock_block_device: Mock,
|
||||||
device_registry: DeviceRegistry,
|
device_registry: DeviceRegistry,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test block restored climate with US CUSTOMATY unit system."""
|
"""Test block restored climate with US CUSTOMARY unit system."""
|
||||||
hass.config.units = US_CUSTOMARY_SYSTEM
|
hass.config.units = US_CUSTOMARY_SYSTEM
|
||||||
monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp")
|
monkeypatch.delattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "targetTemp")
|
||||||
monkeypatch.delattr(mock_block_device.blocks[GAS_VALVE_BLOCK_ID], "targetTemp")
|
monkeypatch.delattr(mock_block_device.blocks[GAS_VALVE_BLOCK_ID], "targetTemp")
|
||||||
@ -759,3 +770,82 @@ async def test_wall_display_thermostat_mode_external_actuator(
|
|||||||
entry = entity_registry.async_get(climate_entity_id)
|
entry = entity_registry.async_get(climate_entity_id)
|
||||||
assert entry
|
assert entry
|
||||||
assert entry.unique_id == "123456789ABC-thermostat:0"
|
assert entry.unique_id == "123456789ABC-thermostat:0"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_blu_trv_climate_set_temperature(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_blu_trv: Mock,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test BLU TRV set target temperature."""
|
||||||
|
|
||||||
|
entity_id = "climate.trv_name"
|
||||||
|
monkeypatch.delitem(mock_blu_trv.status, "thermostat:0")
|
||||||
|
|
||||||
|
await init_integration(hass, 3, model=MODEL_BLU_GATEWAY_GEN3)
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_TEMPERATURE) == 17.1
|
||||||
|
|
||||||
|
monkeypatch.setitem(
|
||||||
|
mock_blu_trv.status[f"{BLU_TRV_IDENTIFIER}:200"], "target_C", 28
|
||||||
|
)
|
||||||
|
await hass.services.async_call(
|
||||||
|
CLIMATE_DOMAIN,
|
||||||
|
SERVICE_SET_TEMPERATURE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 28},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_blu_trv.mock_update()
|
||||||
|
|
||||||
|
mock_blu_trv.call_rpc.assert_called_once_with(
|
||||||
|
"BluTRV.Call",
|
||||||
|
{
|
||||||
|
"id": 200,
|
||||||
|
"method": "Trv.SetTarget",
|
||||||
|
"params": {"id": 0, "target_C": 28.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_TEMPERATURE) == 28
|
||||||
|
|
||||||
|
|
||||||
|
async def test_blu_trv_climate_disabled(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_blu_trv: Mock,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test BLU TRV disabled."""
|
||||||
|
|
||||||
|
entity_id = "climate.trv_name"
|
||||||
|
monkeypatch.delitem(mock_blu_trv.status, "thermostat:0")
|
||||||
|
|
||||||
|
await init_integration(hass, 3, model=MODEL_BLU_GATEWAY_GEN3)
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_TEMPERATURE) == 17.1
|
||||||
|
|
||||||
|
monkeypatch.setitem(
|
||||||
|
mock_blu_trv.config[f"{BLU_TRV_IDENTIFIER}:200"], "enable", False
|
||||||
|
)
|
||||||
|
mock_blu_trv.mock_update()
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_TEMPERATURE) is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_blu_trv_climate_hvac_action(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_blu_trv: Mock,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Test BLU TRV is heating."""
|
||||||
|
|
||||||
|
entity_id = "climate.trv_name"
|
||||||
|
monkeypatch.delitem(mock_blu_trv.status, "thermostat:0")
|
||||||
|
|
||||||
|
await init_integration(hass, 3, model=MODEL_BLU_GATEWAY_GEN3)
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_HVAC_ACTION) == HVACAction.IDLE
|
||||||
|
|
||||||
|
monkeypatch.setitem(mock_blu_trv.status[f"{BLU_TRV_IDENTIFIER}:200"], "pos", 10)
|
||||||
|
mock_blu_trv.mock_update()
|
||||||
|
|
||||||
|
assert get_entity_attribute(hass, entity_id, ATTR_HVAC_ACTION) == HVACAction.HEATING
|
||||||
|
Loading…
x
Reference in New Issue
Block a user