mirror of
https://github.com/home-assistant/core.git
synced 2025-07-30 00:27:19 +00:00
Add device specific discovery for Heatit Z-TRM3 thermostat (#49804)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
37dad92bf7
commit
fdc29d6a80
@ -44,6 +44,9 @@ from homeassistant.components.climate.const import (
|
|||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.zwave_js.discovery_data_template import (
|
||||||
|
DynamicCurrentTempClimateDataTemplate,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
@ -59,6 +62,7 @@ from homeassistant.helpers.temperature import convert_temperature
|
|||||||
from .const import DATA_CLIENT, DATA_UNSUBSCRIBE, DOMAIN
|
from .const import DATA_CLIENT, DATA_UNSUBSCRIBE, DOMAIN
|
||||||
from .discovery import ZwaveDiscoveryInfo
|
from .discovery import ZwaveDiscoveryInfo
|
||||||
from .entity import ZWaveBaseEntity
|
from .entity import ZWaveBaseEntity
|
||||||
|
from .helpers import get_value_of_zwave_value
|
||||||
|
|
||||||
# Map Z-Wave HVAC Mode to Home Assistant value
|
# Map Z-Wave HVAC Mode to Home Assistant value
|
||||||
# Note: We treat "auto" as "heat_cool" as most Z-Wave devices
|
# Note: We treat "auto" as "heat_cool" as most Z-Wave devices
|
||||||
@ -110,6 +114,9 @@ async def async_setup_entry(
|
|||||||
def async_add_climate(info: ZwaveDiscoveryInfo) -> None:
|
def async_add_climate(info: ZwaveDiscoveryInfo) -> None:
|
||||||
"""Add Z-Wave Climate."""
|
"""Add Z-Wave Climate."""
|
||||||
entities: list[ZWaveBaseEntity] = []
|
entities: list[ZWaveBaseEntity] = []
|
||||||
|
if info.platform_hint == "dynamic_current_temp":
|
||||||
|
entities.append(DynamicCurrentTempClimate(config_entry, client, info))
|
||||||
|
else:
|
||||||
entities.append(ZWaveClimate(config_entry, client, info))
|
entities.append(ZWaveClimate(config_entry, client, info))
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
@ -129,7 +136,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
|
self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize lock."""
|
"""Initialize thermostat."""
|
||||||
super().__init__(config_entry, client, info)
|
super().__init__(config_entry, client, info)
|
||||||
self._hvac_modes: dict[str, int | None] = {}
|
self._hvac_modes: dict[str, int | None] = {}
|
||||||
self._hvac_presets: dict[str, int | None] = {}
|
self._hvac_presets: dict[str, int | None] = {}
|
||||||
@ -285,12 +292,12 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
@property
|
@property
|
||||||
def current_humidity(self) -> int | None:
|
def current_humidity(self) -> int | None:
|
||||||
"""Return the current humidity level."""
|
"""Return the current humidity level."""
|
||||||
return self._current_humidity.value if self._current_humidity else None
|
return get_value_of_zwave_value(self._current_humidity)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_temperature(self) -> float | None:
|
def current_temperature(self) -> float | None:
|
||||||
"""Return the current temperature."""
|
"""Return the current temperature."""
|
||||||
return self._current_temp.value if self._current_temp else None
|
return get_value_of_zwave_value(self._current_temp)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature(self) -> float | None:
|
def target_temperature(self) -> float | None:
|
||||||
@ -302,7 +309,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
temp = self._setpoint_value(self._current_mode_setpoint_enums[0])
|
temp = self._setpoint_value(self._current_mode_setpoint_enums[0])
|
||||||
except (IndexError, ValueError):
|
except (IndexError, ValueError):
|
||||||
return None
|
return None
|
||||||
return temp.value if temp else None
|
return get_value_of_zwave_value(temp)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature_high(self) -> float | None:
|
def target_temperature_high(self) -> float | None:
|
||||||
@ -314,7 +321,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
temp = self._setpoint_value(self._current_mode_setpoint_enums[1])
|
temp = self._setpoint_value(self._current_mode_setpoint_enums[1])
|
||||||
except (IndexError, ValueError):
|
except (IndexError, ValueError):
|
||||||
return None
|
return None
|
||||||
return temp.value if temp else None
|
return get_value_of_zwave_value(temp)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature_low(self) -> float | None:
|
def target_temperature_low(self) -> float | None:
|
||||||
@ -482,3 +489,25 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
if preset_mode_value is None:
|
if preset_mode_value is None:
|
||||||
raise ValueError(f"Received an invalid preset mode: {preset_mode}")
|
raise ValueError(f"Received an invalid preset mode: {preset_mode}")
|
||||||
await self.info.node.async_set_value(self._current_mode, preset_mode_value)
|
await self.info.node.async_set_value(self._current_mode, preset_mode_value)
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicCurrentTempClimate(ZWaveClimate):
|
||||||
|
"""Representation of a thermostat that can dynamically use a different Zwave Value for current temp."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
|
||||||
|
) -> None:
|
||||||
|
"""Initialize thermostat."""
|
||||||
|
super().__init__(config_entry, client, info)
|
||||||
|
self.data_template = cast(
|
||||||
|
DynamicCurrentTempClimateDataTemplate, self.info.platform_data_template
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_temperature(self) -> float | None:
|
||||||
|
"""Return the current temperature."""
|
||||||
|
assert self.info.platform_data
|
||||||
|
val = get_value_of_zwave_value(
|
||||||
|
self.data_template.current_temperature_value(self.info.platform_data)
|
||||||
|
)
|
||||||
|
return val if val is not None else super().current_temperature
|
||||||
|
@ -5,13 +5,19 @@ from collections.abc import Generator
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from zwave_js_server.const import CommandClass
|
from zwave_js_server.const import THERMOSTAT_CURRENT_TEMP_PROPERTY, CommandClass
|
||||||
from zwave_js_server.model.device_class import DeviceClassItem
|
from zwave_js_server.model.device_class import DeviceClassItem
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
from zwave_js_server.model.value import Value as ZwaveValue
|
from zwave_js_server.model.value import Value as ZwaveValue
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
from .discovery_data_template import (
|
||||||
|
BaseDiscoverySchemaDataTemplate,
|
||||||
|
DynamicCurrentTempClimateDataTemplate,
|
||||||
|
ZwaveValueID,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ZwaveDiscoveryInfo:
|
class ZwaveDiscoveryInfo:
|
||||||
@ -27,6 +33,12 @@ class ZwaveDiscoveryInfo:
|
|||||||
platform: str
|
platform: str
|
||||||
# hint for the platform about this discovered entity
|
# hint for the platform about this discovered entity
|
||||||
platform_hint: str | None = ""
|
platform_hint: str | None = ""
|
||||||
|
# data template to use in platform logic
|
||||||
|
platform_data_template: BaseDiscoverySchemaDataTemplate | None = None
|
||||||
|
# helper data to use in platform setup
|
||||||
|
platform_data: dict[str, Any] | None = None
|
||||||
|
# additional values that need to be watched by entity
|
||||||
|
additional_value_ids_to_watch: set[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -69,6 +81,8 @@ class ZWaveDiscoverySchema:
|
|||||||
primary_value: ZWaveValueDiscoverySchema
|
primary_value: ZWaveValueDiscoverySchema
|
||||||
# [optional] hint for platform
|
# [optional] hint for platform
|
||||||
hint: str | None = None
|
hint: str | None = None
|
||||||
|
# [optional] template to generate platform specific data to use in setup
|
||||||
|
data_template: BaseDiscoverySchemaDataTemplate | None = None
|
||||||
# [optional] the node's manufacturer_id must match ANY of these values
|
# [optional] the node's manufacturer_id must match ANY of these values
|
||||||
manufacturer_id: set[int] | None = None
|
manufacturer_id: set[int] | None = None
|
||||||
# [optional] the node's product_id must match ANY of these values
|
# [optional] the node's product_id must match ANY of these values
|
||||||
@ -214,6 +228,52 @@ DISCOVERY_SCHEMAS = [
|
|||||||
primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
||||||
assumed_state=True,
|
assumed_state=True,
|
||||||
),
|
),
|
||||||
|
# Heatit Z-TRM3
|
||||||
|
ZWaveDiscoverySchema(
|
||||||
|
platform="climate",
|
||||||
|
hint="dynamic_current_temp",
|
||||||
|
manufacturer_id={0x019B},
|
||||||
|
product_id={0x0203},
|
||||||
|
product_type={0x0003},
|
||||||
|
primary_value=ZWaveValueDiscoverySchema(
|
||||||
|
command_class={CommandClass.THERMOSTAT_MODE},
|
||||||
|
property={"mode"},
|
||||||
|
type={"number"},
|
||||||
|
),
|
||||||
|
data_template=DynamicCurrentTempClimateDataTemplate(
|
||||||
|
{
|
||||||
|
# Internal Sensor
|
||||||
|
"A": ZwaveValueID(
|
||||||
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
CommandClass.SENSOR_MULTILEVEL,
|
||||||
|
endpoint=2,
|
||||||
|
),
|
||||||
|
"AF": ZwaveValueID(
|
||||||
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
CommandClass.SENSOR_MULTILEVEL,
|
||||||
|
endpoint=2,
|
||||||
|
),
|
||||||
|
# External Sensor
|
||||||
|
"A2": ZwaveValueID(
|
||||||
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
CommandClass.SENSOR_MULTILEVEL,
|
||||||
|
endpoint=3,
|
||||||
|
),
|
||||||
|
"A2F": ZwaveValueID(
|
||||||
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
CommandClass.SENSOR_MULTILEVEL,
|
||||||
|
endpoint=3,
|
||||||
|
),
|
||||||
|
# Floor sensor
|
||||||
|
"F": ZwaveValueID(
|
||||||
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
CommandClass.SENSOR_MULTILEVEL,
|
||||||
|
endpoint=4,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ZwaveValueID(2, CommandClass.CONFIGURATION, endpoint=0),
|
||||||
|
),
|
||||||
|
),
|
||||||
# ====== START OF CONFIG PARAMETER SPECIFIC MAPPING SCHEMAS =======
|
# ====== START OF CONFIG PARAMETER SPECIFIC MAPPING SCHEMAS =======
|
||||||
# Door lock mode config parameter. Functionality equivalent to Notification CC
|
# Door lock mode config parameter. Functionality equivalent to Notification CC
|
||||||
# list sensors.
|
# list sensors.
|
||||||
@ -524,6 +584,15 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# resolve helper data from template
|
||||||
|
resolved_data = None
|
||||||
|
additional_value_ids_to_watch = None
|
||||||
|
if schema.data_template:
|
||||||
|
resolved_data = schema.data_template.resolve_data(value)
|
||||||
|
additional_value_ids_to_watch = schema.data_template.value_ids_to_watch(
|
||||||
|
resolved_data
|
||||||
|
)
|
||||||
|
|
||||||
# all checks passed, this value belongs to an entity
|
# all checks passed, this value belongs to an entity
|
||||||
yield ZwaveDiscoveryInfo(
|
yield ZwaveDiscoveryInfo(
|
||||||
node=value.node,
|
node=value.node,
|
||||||
@ -531,6 +600,9 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None
|
|||||||
assumed_state=schema.assumed_state,
|
assumed_state=schema.assumed_state,
|
||||||
platform=schema.platform,
|
platform=schema.platform,
|
||||||
platform_hint=schema.hint,
|
platform_hint=schema.hint,
|
||||||
|
platform_data_template=schema.data_template,
|
||||||
|
platform_data=resolved_data,
|
||||||
|
additional_value_ids_to_watch=additional_value_ids_to_watch,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not schema.allow_multi:
|
if not schema.allow_multi:
|
||||||
|
109
homeassistant/components/zwave_js/discovery_data_template.py
Normal file
109
homeassistant/components/zwave_js/discovery_data_template.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""Data template classes for discovery used to generate device specific data for setup."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ZwaveValueID:
|
||||||
|
"""Class to represent a value ID."""
|
||||||
|
|
||||||
|
property_: str | int
|
||||||
|
command_class: int
|
||||||
|
endpoint: int | None = None
|
||||||
|
property_key: str | int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BaseDiscoverySchemaDataTemplate:
|
||||||
|
"""Base class for discovery schema data templates."""
|
||||||
|
|
||||||
|
def resolve_data(self, value: ZwaveValue) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Resolve helper class data for a discovered value.
|
||||||
|
|
||||||
|
Can optionally be implemented by subclasses if input data needs to be
|
||||||
|
transformed once discovered Value is available.
|
||||||
|
"""
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]:
|
||||||
|
"""
|
||||||
|
Return list of all ZwaveValues resolved by helper that should be watched.
|
||||||
|
|
||||||
|
Should be implemented by subclasses only if there are values to watch.
|
||||||
|
"""
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
return []
|
||||||
|
|
||||||
|
def value_ids_to_watch(self, resolved_data: dict[str, Any]) -> set[str]:
|
||||||
|
"""
|
||||||
|
Return list of all Value IDs resolved by helper that should be watched.
|
||||||
|
|
||||||
|
Not to be overwritten by subclasses.
|
||||||
|
"""
|
||||||
|
return {val.value_id for val in self.values_to_watch(resolved_data) if val}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_value_from_id(
|
||||||
|
node: ZwaveNode, value_id_obj: ZwaveValueID
|
||||||
|
) -> ZwaveValue | None:
|
||||||
|
"""Get a ZwaveValue from a node using a ZwaveValueDict."""
|
||||||
|
value_id = get_value_id(
|
||||||
|
node,
|
||||||
|
value_id_obj.command_class,
|
||||||
|
value_id_obj.property_,
|
||||||
|
endpoint=value_id_obj.endpoint,
|
||||||
|
property_key=value_id_obj.property_key,
|
||||||
|
)
|
||||||
|
return node.values.get(value_id)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||||
|
"""Data template class for Z-Wave JS Climate entities with dynamic current temps."""
|
||||||
|
|
||||||
|
lookup_table: dict[str | int, ZwaveValueID]
|
||||||
|
dependent_value: ZwaveValueID
|
||||||
|
|
||||||
|
def resolve_data(self, value: ZwaveValue) -> dict[str, Any]:
|
||||||
|
"""Resolve helper class data for a discovered value."""
|
||||||
|
data: dict[str, Any] = {
|
||||||
|
"lookup_table": {},
|
||||||
|
"dependent_value": self._get_value_from_id(
|
||||||
|
value.node, self.dependent_value
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for key in self.lookup_table:
|
||||||
|
data["lookup_table"][key] = self._get_value_from_id(
|
||||||
|
value.node, self.lookup_table[key]
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]:
|
||||||
|
"""Return list of all ZwaveValues resolved by helper that should be watched."""
|
||||||
|
return [
|
||||||
|
*resolved_data["lookup_table"].values(),
|
||||||
|
resolved_data["dependent_value"],
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def current_temperature_value(resolved_data: dict[str, Any]) -> ZwaveValue | None:
|
||||||
|
"""Get current temperature ZwaveValue from resolved data."""
|
||||||
|
lookup_table: dict[str | int, ZwaveValue | None] = resolved_data["lookup_table"]
|
||||||
|
dependent_value: ZwaveValue | None = resolved_data["dependent_value"]
|
||||||
|
|
||||||
|
if dependent_value:
|
||||||
|
lookup_key = dependent_value.metadata.states[
|
||||||
|
str(dependent_value.value)
|
||||||
|
].split("-")[0]
|
||||||
|
return lookup_table.get(lookup_key)
|
||||||
|
|
||||||
|
return None
|
@ -37,6 +37,11 @@ class ZWaveBaseEntity(Entity):
|
|||||||
# entities requiring additional values, can add extra ids to this list
|
# entities requiring additional values, can add extra ids to this list
|
||||||
self.watched_value_ids = {self.info.primary_value.value_id}
|
self.watched_value_ids = {self.info.primary_value.value_id}
|
||||||
|
|
||||||
|
if self.info.additional_value_ids_to_watch:
|
||||||
|
self.watched_value_ids = self.watched_value_ids.union(
|
||||||
|
self.info.additional_value_ids_to_watch
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def on_value_update(self) -> None:
|
def on_value_update(self) -> None:
|
||||||
"""Call when one of the watched values change.
|
"""Call when one of the watched values change.
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
"""Helper functions for Z-Wave JS integration."""
|
"""Helper functions for Z-Wave JS integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from zwave_js_server.client import Client as ZwaveClient
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
from zwave_js_server.model.value import Value as ZwaveValue
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import __version__ as HA_VERSION
|
from homeassistant.const import __version__ as HA_VERSION
|
||||||
@ -15,6 +16,12 @@ from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
|
|||||||
from .const import CONF_DATA_COLLECTION_OPTED_IN, DATA_CLIENT, DOMAIN
|
from .const import CONF_DATA_COLLECTION_OPTED_IN, DATA_CLIENT, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get_value_of_zwave_value(value: ZwaveValue | None) -> Any | None:
|
||||||
|
"""Return the value of a ZwaveValue."""
|
||||||
|
return value.value if value else None
|
||||||
|
|
||||||
|
|
||||||
async def async_enable_statistics(client: ZwaveClient) -> None:
|
async def async_enable_statistics(client: ZwaveClient) -> None:
|
||||||
"""Enable statistics on the driver."""
|
"""Enable statistics on the driver."""
|
||||||
await client.driver.async_enable_statistics("Home Assistant", HA_VERSION)
|
await client.driver.async_enable_statistics("Home Assistant", HA_VERSION)
|
||||||
|
@ -438,6 +438,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat
|
|||||||
|
|
||||||
async def test_thermostat_heatit(hass, client, climate_heatit_z_trm3, integration):
|
async def test_thermostat_heatit(hass, client, climate_heatit_z_trm3, integration):
|
||||||
"""Test a thermostat v2 command class entity."""
|
"""Test a thermostat v2 command class entity."""
|
||||||
|
node = climate_heatit_z_trm3
|
||||||
state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY)
|
state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY)
|
||||||
|
|
||||||
assert state
|
assert state
|
||||||
@ -453,6 +454,52 @@ async def test_thermostat_heatit(hass, client, climate_heatit_z_trm3, integratio
|
|||||||
assert state.attributes[ATTR_MIN_TEMP] == 5
|
assert state.attributes[ATTR_MIN_TEMP] == 5
|
||||||
assert state.attributes[ATTR_MAX_TEMP] == 35
|
assert state.attributes[ATTR_MAX_TEMP] == 35
|
||||||
|
|
||||||
|
# Try switching to external sensor
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": 24,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Configuration",
|
||||||
|
"commandClass": 112,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": 2,
|
||||||
|
"propertyName": "Sensor mode",
|
||||||
|
"newValue": 4,
|
||||||
|
"prevValue": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY)
|
||||||
|
assert state
|
||||||
|
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 0
|
||||||
|
|
||||||
|
# Try switching to floor sensor
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": 24,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Configuration",
|
||||||
|
"commandClass": 112,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": 2,
|
||||||
|
"propertyName": "Sensor mode",
|
||||||
|
"newValue": 0,
|
||||||
|
"prevValue": 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY)
|
||||||
|
assert state
|
||||||
|
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 25.5
|
||||||
|
|
||||||
|
|
||||||
async def test_thermostat_srt321_hrt4_zw(hass, client, srt321_hrt4_zw, integration):
|
async def test_thermostat_srt321_hrt4_zw(hass, client, srt321_hrt4_zw, integration):
|
||||||
"""Test a climate entity from a HRT4-ZW / SRT321 thermostat device.
|
"""Test a climate entity from a HRT4-ZW / SRT321 thermostat device.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user