mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Add climate platform to zwave_js (#45177)
* add zwave_js support for climate * fix * add fixture * rename fixture * fix variable name error * add tests * fix tests and handle set_temp properly based on unit * update call being tested * fix tests * improve coverage * fix docstring * address review comments * fix test * update enum class name * bump zwave-js-server-python version and assume primary_value is always set * add additional coverage * fix docstrings and move populating modes/presets into initialization * attempt to address comments * improve comment * move mode value into a variable so its easier to iterate in the future * dont assume mode as a discovery point * assume all values are available when node is ready * fix order of operations * switch to valueerror * use primary value * readd property and type to discovery schema
This commit is contained in:
parent
a1b0d6baad
commit
b2f914823d
300
homeassistant/components/zwave_js/climate.py
Normal file
300
homeassistant/components/zwave_js/climate.py
Normal file
@ -0,0 +1,300 @@
|
||||
"""Representation of Z-Wave thermostats."""
|
||||
import logging
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import (
|
||||
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||
THERMOSTAT_MODE_SETPOINT_MAP,
|
||||
THERMOSTAT_MODES,
|
||||
THERMOSTAT_OPERATING_STATE_PROPERTY,
|
||||
THERMOSTAT_SETPOINT_PROPERTY,
|
||||
CommandClass,
|
||||
ThermostatMode,
|
||||
ThermostatOperatingState,
|
||||
ThermostatSetpointType,
|
||||
)
|
||||
from zwave_js_server.model.value import Value as ZwaveValue
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
ATTR_HVAC_MODE,
|
||||
ATTR_TARGET_TEMP_HIGH,
|
||||
ATTR_TARGET_TEMP_LOW,
|
||||
CURRENT_HVAC_COOL,
|
||||
CURRENT_HVAC_FAN,
|
||||
CURRENT_HVAC_HEAT,
|
||||
CURRENT_HVAC_IDLE,
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_OFF,
|
||||
PRESET_NONE,
|
||||
SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import DATA_CLIENT, DATA_UNSUBSCRIBE, DOMAIN
|
||||
from .discovery import ZwaveDiscoveryInfo
|
||||
from .entity import ZWaveBaseEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Map Z-Wave HVAC Mode to Home Assistant value
|
||||
# Note: We treat "auto" as "heat_cool" as most Z-Wave devices
|
||||
# report auto_changeover as auto without schedule support.
|
||||
ZW_HVAC_MODE_MAP: Dict[int, str] = {
|
||||
ThermostatMode.OFF: HVAC_MODE_OFF,
|
||||
ThermostatMode.HEAT: HVAC_MODE_HEAT,
|
||||
ThermostatMode.COOL: HVAC_MODE_COOL,
|
||||
# Z-Wave auto mode is actually heat/cool in the hass world
|
||||
ThermostatMode.AUTO: HVAC_MODE_HEAT_COOL,
|
||||
ThermostatMode.AUXILIARY: HVAC_MODE_HEAT,
|
||||
ThermostatMode.FAN: HVAC_MODE_FAN_ONLY,
|
||||
ThermostatMode.FURNANCE: HVAC_MODE_HEAT,
|
||||
ThermostatMode.DRY: HVAC_MODE_DRY,
|
||||
ThermostatMode.AUTO_CHANGE_OVER: HVAC_MODE_HEAT_COOL,
|
||||
ThermostatMode.HEATING_ECON: HVAC_MODE_HEAT,
|
||||
ThermostatMode.COOLING_ECON: HVAC_MODE_COOL,
|
||||
ThermostatMode.AWAY: HVAC_MODE_HEAT_COOL,
|
||||
ThermostatMode.FULL_POWER: HVAC_MODE_HEAT,
|
||||
}
|
||||
|
||||
HVAC_CURRENT_MAP: Dict[int, str] = {
|
||||
ThermostatOperatingState.IDLE: CURRENT_HVAC_IDLE,
|
||||
ThermostatOperatingState.PENDING_HEAT: CURRENT_HVAC_IDLE,
|
||||
ThermostatOperatingState.HEATING: CURRENT_HVAC_HEAT,
|
||||
ThermostatOperatingState.PENDING_COOL: CURRENT_HVAC_IDLE,
|
||||
ThermostatOperatingState.COOLING: CURRENT_HVAC_COOL,
|
||||
ThermostatOperatingState.FAN_ONLY: CURRENT_HVAC_FAN,
|
||||
ThermostatOperatingState.VENT_ECONOMIZER: CURRENT_HVAC_FAN,
|
||||
ThermostatOperatingState.AUX_HEATING: CURRENT_HVAC_HEAT,
|
||||
ThermostatOperatingState.SECOND_STAGE_HEATING: CURRENT_HVAC_HEAT,
|
||||
ThermostatOperatingState.SECOND_STAGE_COOLING: CURRENT_HVAC_COOL,
|
||||
ThermostatOperatingState.SECOND_STAGE_AUX_HEAT: CURRENT_HVAC_HEAT,
|
||||
ThermostatOperatingState.THIRD_STAGE_AUX_HEAT: CURRENT_HVAC_HEAT,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable
|
||||
) -> None:
|
||||
"""Set up Z-Wave climate from config entry."""
|
||||
client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT]
|
||||
|
||||
@callback
|
||||
def async_add_climate(info: ZwaveDiscoveryInfo) -> None:
|
||||
"""Add Z-Wave Climate."""
|
||||
entities: List[ZWaveBaseEntity] = []
|
||||
entities.append(ZWaveClimate(client, info))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append(
|
||||
async_dispatcher_connect(
|
||||
hass, f"{DOMAIN}_add_{CLIMATE_DOMAIN}", async_add_climate
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
||||
"""Representation of a Z-Wave climate."""
|
||||
|
||||
def __init__(self, client: ZwaveClient, info: ZwaveDiscoveryInfo) -> None:
|
||||
"""Initialize lock."""
|
||||
super().__init__(client, info)
|
||||
self._hvac_modes: Dict[str, Optional[int]] = {}
|
||||
self._hvac_presets: Dict[str, Optional[int]] = {}
|
||||
|
||||
self._current_mode = self.info.primary_value
|
||||
self._setpoint_values: Dict[ThermostatSetpointType, ZwaveValue] = {}
|
||||
for enum in ThermostatSetpointType:
|
||||
self._setpoint_values[enum] = self.get_zwave_value(
|
||||
THERMOSTAT_SETPOINT_PROPERTY,
|
||||
command_class=CommandClass.THERMOSTAT_SETPOINT,
|
||||
value_property_key_name=enum.value,
|
||||
add_to_watched_value_ids=True,
|
||||
)
|
||||
self._operating_state = self.get_zwave_value(
|
||||
THERMOSTAT_OPERATING_STATE_PROPERTY,
|
||||
command_class=CommandClass.THERMOSTAT_OPERATING_STATE,
|
||||
add_to_watched_value_ids=True,
|
||||
)
|
||||
self._current_temp = self.get_zwave_value(
|
||||
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||
command_class=CommandClass.SENSOR_MULTILEVEL,
|
||||
add_to_watched_value_ids=True,
|
||||
)
|
||||
self._set_modes_and_presets()
|
||||
|
||||
def _setpoint_value(self, setpoint_type: ThermostatSetpointType) -> ZwaveValue:
|
||||
"""Optionally return a ZwaveValue for a setpoint."""
|
||||
val = self._setpoint_values[setpoint_type]
|
||||
if val is None:
|
||||
raise ValueError("Value requested is not available")
|
||||
|
||||
return val
|
||||
|
||||
def _set_modes_and_presets(self) -> None:
|
||||
"""Convert Z-Wave Thermostat modes into Home Assistant modes and presets."""
|
||||
all_modes: Dict[str, Optional[int]] = {}
|
||||
all_presets: Dict[str, Optional[int]] = {PRESET_NONE: None}
|
||||
|
||||
# Z-Wave uses one list for both modes and presets.
|
||||
# Iterate over all Z-Wave ThermostatModes and extract the hvac modes and presets.
|
||||
current_mode = self._current_mode
|
||||
if not current_mode:
|
||||
return
|
||||
for mode_id, mode_name in current_mode.metadata.states.items():
|
||||
mode_id = int(mode_id)
|
||||
if mode_id in THERMOSTAT_MODES:
|
||||
# treat value as hvac mode
|
||||
hass_mode = ZW_HVAC_MODE_MAP.get(mode_id)
|
||||
if hass_mode:
|
||||
all_modes[hass_mode] = mode_id
|
||||
else:
|
||||
# treat value as hvac preset
|
||||
all_presets[mode_name] = mode_id
|
||||
self._hvac_modes = all_modes
|
||||
self._hvac_presets = all_presets
|
||||
|
||||
@property
|
||||
def _current_mode_setpoint_enums(self) -> List[Optional[ThermostatSetpointType]]:
|
||||
"""Return the list of enums that are relevant to the current thermostat mode."""
|
||||
return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) # type: ignore
|
||||
|
||||
@property
|
||||
def temperature_unit(self) -> str:
|
||||
"""Return the unit of measurement used by the platform."""
|
||||
temp: Optional[ZwaveValue] = None
|
||||
if self._current_mode_setpoint_enums:
|
||||
temp = self._setpoint_value(self._current_mode_setpoint_enums[0])
|
||||
if temp is not None and temp.metadata.unit == "°F":
|
||||
return TEMP_FAHRENHEIT
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> str:
|
||||
"""Return hvac operation ie. heat, cool mode."""
|
||||
if self._current_mode is None:
|
||||
# Thermostat(valve) with no support for setting a mode is considered heating-only
|
||||
return HVAC_MODE_HEAT
|
||||
return ZW_HVAC_MODE_MAP.get(int(self._current_mode.value), HVAC_MODE_HEAT_COOL)
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> List[str]:
|
||||
"""Return the list of available hvac operation modes."""
|
||||
return list(self._hvac_modes)
|
||||
|
||||
@property
|
||||
def hvac_action(self) -> Optional[str]:
|
||||
"""Return the current running hvac operation if supported."""
|
||||
if not self._operating_state:
|
||||
return None
|
||||
return HVAC_CURRENT_MAP.get(int(self._operating_state.value))
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> Optional[float]:
|
||||
"""Return the current temperature."""
|
||||
return self._current_temp.value if self._current_temp else None
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> Optional[float]:
|
||||
"""Return the temperature we try to reach."""
|
||||
temp = self._setpoint_value(self._current_mode_setpoint_enums[0])
|
||||
return temp.value if temp else None
|
||||
|
||||
@property
|
||||
def target_temperature_high(self) -> Optional[float]:
|
||||
"""Return the highbound target temperature we try to reach."""
|
||||
temp = self._setpoint_value(self._current_mode_setpoint_enums[1])
|
||||
return temp.value if temp else None
|
||||
|
||||
@property
|
||||
def target_temperature_low(self) -> Optional[float]:
|
||||
"""Return the lowbound target temperature we try to reach."""
|
||||
return self.target_temperature
|
||||
|
||||
@property
|
||||
def preset_mode(self) -> Optional[str]:
|
||||
"""Return the current preset mode, e.g., home, away, temp."""
|
||||
if self._current_mode and int(self._current_mode.value) not in THERMOSTAT_MODES:
|
||||
return_val: str = self._current_mode.metadata.states.get(
|
||||
self._current_mode.value
|
||||
)
|
||||
return return_val
|
||||
return PRESET_NONE
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> Optional[List[str]]:
|
||||
"""Return a list of available preset modes."""
|
||||
return list(self._hvac_presets)
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return the list of supported features."""
|
||||
support = SUPPORT_PRESET_MODE
|
||||
if len(self._current_mode_setpoint_enums) == 1:
|
||||
support |= SUPPORT_TARGET_TEMPERATURE
|
||||
if len(self._current_mode_setpoint_enums) > 1:
|
||||
support |= SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||
return support
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
assert self.hass
|
||||
hvac_mode: Optional[str] = kwargs.get(ATTR_HVAC_MODE)
|
||||
|
||||
if hvac_mode is not None:
|
||||
await self.async_set_hvac_mode(hvac_mode)
|
||||
if len(self._current_mode_setpoint_enums) == 1:
|
||||
setpoint: ZwaveValue = self._setpoint_value(
|
||||
self._current_mode_setpoint_enums[0]
|
||||
)
|
||||
target_temp: Optional[float] = kwargs.get(ATTR_TEMPERATURE)
|
||||
if target_temp is not None:
|
||||
await self.info.node.async_set_value(setpoint, target_temp)
|
||||
elif len(self._current_mode_setpoint_enums) == 2:
|
||||
setpoint_low: ZwaveValue = self._setpoint_value(
|
||||
self._current_mode_setpoint_enums[0]
|
||||
)
|
||||
setpoint_high: ZwaveValue = self._setpoint_value(
|
||||
self._current_mode_setpoint_enums[1]
|
||||
)
|
||||
target_temp_low: Optional[float] = kwargs.get(ATTR_TARGET_TEMP_LOW)
|
||||
target_temp_high: Optional[float] = kwargs.get(ATTR_TARGET_TEMP_HIGH)
|
||||
if target_temp_low is not None:
|
||||
await self.info.node.async_set_value(setpoint_low, target_temp_low)
|
||||
if target_temp_high is not None:
|
||||
await self.info.node.async_set_value(setpoint_high, target_temp_high)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||
"""Set new target hvac mode."""
|
||||
if not self._current_mode:
|
||||
# Thermostat(valve) with no support for setting a mode
|
||||
raise ValueError(
|
||||
f"Thermostat {self.entity_id} does not support setting a mode"
|
||||
)
|
||||
hvac_mode_value = self._hvac_modes.get(hvac_mode)
|
||||
if hvac_mode_value is None:
|
||||
raise ValueError(f"Received an invalid hvac mode: {hvac_mode}")
|
||||
await self.info.node.async_set_value(self._current_mode, hvac_mode_value)
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new target preset mode."""
|
||||
if preset_mode == PRESET_NONE:
|
||||
# try to restore to the (translated) main hvac mode
|
||||
await self.async_set_hvac_mode(self.hvac_mode)
|
||||
return
|
||||
preset_mode_value = self._hvac_presets.get(preset_mode)
|
||||
if preset_mode_value is None:
|
||||
raise ValueError(f"Received an invalid preset mode: {preset_mode}")
|
||||
await self.info.node.async_set_value(self._current_mode, preset_mode_value)
|
@ -3,7 +3,7 @@
|
||||
|
||||
DOMAIN = "zwave_js"
|
||||
NAME = "Z-Wave JS"
|
||||
PLATFORMS = ["binary_sensor", "light", "lock", "sensor", "switch"]
|
||||
PLATFORMS = ["binary_sensor", "climate", "light", "lock", "sensor", "switch"]
|
||||
|
||||
DATA_CLIENT = "client"
|
||||
DATA_UNSUBSCRIBE = "unsubs"
|
||||
|
@ -74,6 +74,19 @@ DISCOVERY_SCHEMAS = [
|
||||
property={"currentMode", "locked"},
|
||||
type={"number", "boolean"},
|
||||
),
|
||||
# climate
|
||||
ZWaveDiscoverySchema(
|
||||
platform="climate",
|
||||
device_class_generic={"Thermostat"},
|
||||
device_class_specific={
|
||||
"Setback Thermostat",
|
||||
"Thermostat General",
|
||||
"Thermostat General V2",
|
||||
},
|
||||
command_class={CommandClass.THERMOSTAT_MODE},
|
||||
property={"mode"},
|
||||
type={"number"},
|
||||
),
|
||||
# lights
|
||||
# primary value is the currentValue (brightness)
|
||||
ZWaveDiscoverySchema(
|
||||
|
@ -67,6 +67,14 @@ def lock_schlage_be469_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/lock_schlage_be469_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="climate_radio_thermostat_ct100_plus_state", scope="session")
|
||||
def climate_radio_thermostat_ct100_plus_state_fixture():
|
||||
"""Load the climate radio thermostat ct100 plus node state fixture data."""
|
||||
return json.loads(
|
||||
load_fixture("zwave_js/climate_radio_thermostat_ct100_plus_state.json")
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def mock_client_fixture(controller_state, version_state):
|
||||
"""Mock a client."""
|
||||
@ -122,6 +130,16 @@ def lock_schlage_be469_fixture(client, lock_schlage_be469_state):
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="climate_radio_thermostat_ct100_plus")
|
||||
def climate_radio_thermostat_ct100_plus_fixture(
|
||||
client, climate_radio_thermostat_ct100_plus_state
|
||||
):
|
||||
"""Mock a climate radio thermostat ct100 plus node."""
|
||||
node = Node(client, climate_radio_thermostat_ct100_plus_state)
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="integration")
|
||||
async def integration_fixture(hass, client):
|
||||
"""Set up the zwave_js integration."""
|
||||
|
326
tests/components/zwave_js/test_climate.py
Normal file
326
tests/components/zwave_js/test_climate.py
Normal file
@ -0,0 +1,326 @@
|
||||
"""Test the Z-Wave JS climate platform."""
|
||||
import pytest
|
||||
from zwave_js_server.event import Event
|
||||
|
||||
from homeassistant.components.climate.const import (
|
||||
ATTR_CURRENT_TEMPERATURE,
|
||||
ATTR_HVAC_ACTION,
|
||||
ATTR_HVAC_MODE,
|
||||
ATTR_HVAC_MODES,
|
||||
ATTR_PRESET_MODE,
|
||||
ATTR_TARGET_TEMP_HIGH,
|
||||
ATTR_TARGET_TEMP_LOW,
|
||||
CURRENT_HVAC_IDLE,
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_OFF,
|
||||
PRESET_NONE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
|
||||
|
||||
CLIMATE_RADIO_THERMOSTAT_ENTITY = "climate.z_wave_thermostat_thermostat_mode"
|
||||
|
||||
|
||||
async def test_thermostat_v2(
|
||||
hass, client, climate_radio_thermostat_ct100_plus, integration
|
||||
):
|
||||
"""Test a thermostat v2 command class entity."""
|
||||
node = climate_radio_thermostat_ct100_plus
|
||||
state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == HVAC_MODE_HEAT
|
||||
assert state.attributes[ATTR_HVAC_MODES] == [
|
||||
HVAC_MODE_OFF,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
]
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.2
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 22.2
|
||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
||||
|
||||
# Test setting preset mode
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_PRESET_MODE: PRESET_NONE,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode",
|
||||
"states": {"0": "Off", "1": "Heat", "2": "Cool", "3": "Auto"},
|
||||
},
|
||||
"value": 1,
|
||||
}
|
||||
assert args["value"] == 1
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting hvac mode
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_HVAC_MODE: HVAC_MODE_COOL,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode",
|
||||
"states": {"0": "Off", "1": "Heat", "2": "Cool", "3": "Auto"},
|
||||
},
|
||||
"value": 1,
|
||||
}
|
||||
assert args["value"] == 2
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting temperature
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_HVAC_MODE: HVAC_MODE_COOL,
|
||||
ATTR_TEMPERATURE: 25,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 2
|
||||
args = client.async_send_command.call_args_list[0][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode",
|
||||
"states": {"0": "Off", "1": "Heat", "2": "Cool", "3": "Auto"},
|
||||
},
|
||||
"value": 1,
|
||||
}
|
||||
assert args["value"] == 2
|
||||
args = client.async_send_command.call_args_list[1][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 1,
|
||||
"propertyName": "setpoint",
|
||||
"propertyKeyName": "Heating",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"unit": "°F",
|
||||
"ccSpecific": {"setpointType": 1},
|
||||
},
|
||||
"value": 72,
|
||||
}
|
||||
assert args["value"] == 77
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test cool mode update from value updated event
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": 13,
|
||||
"args": {
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"newValue": 2,
|
||||
"prevValue": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY)
|
||||
assert state.state == HVAC_MODE_COOL
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 22.8
|
||||
|
||||
# Test heat_cool mode update from value updated event
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": 13,
|
||||
"args": {
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"newValue": 3,
|
||||
"prevValue": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY)
|
||||
assert state.state == HVAC_MODE_HEAT_COOL
|
||||
assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 22.8
|
||||
assert state.attributes[ATTR_TARGET_TEMP_LOW] == 22.2
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting temperature with heat_cool
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_TARGET_TEMP_HIGH: 30,
|
||||
ATTR_TARGET_TEMP_LOW: 25,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 2
|
||||
args = client.async_send_command.call_args_list[0][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 1,
|
||||
"propertyName": "setpoint",
|
||||
"propertyKeyName": "Heating",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"unit": "°F",
|
||||
"ccSpecific": {"setpointType": 1},
|
||||
},
|
||||
"value": 72,
|
||||
}
|
||||
assert args["value"] == 77
|
||||
|
||||
args = client.async_send_command.call_args_list[1][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 13
|
||||
assert args["valueId"] == {
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 2,
|
||||
"propertyName": "setpoint",
|
||||
"propertyKeyName": "Cooling",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"unit": "°F",
|
||||
"ccSpecific": {"setpointType": 2},
|
||||
},
|
||||
"value": 73,
|
||||
}
|
||||
assert args["value"] == 86
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
# Test setting unknown preset mode
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_PRESET_MODE: "unknown_preset",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
# Test setting invalid hvac mode
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_HVAC_MODE: HVAC_MODE_DRY,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test setting invalid preset mode
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY,
|
||||
ATTR_PRESET_MODE: "invalid_mode",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
693
tests/fixtures/zwave_js/climate_radio_thermostat_ct100_plus_state.json
vendored
Normal file
693
tests/fixtures/zwave_js/climate_radio_thermostat_ct100_plus_state.json
vendored
Normal file
@ -0,0 +1,693 @@
|
||||
{
|
||||
"nodeId": 13,
|
||||
"index": 0,
|
||||
"installerIcon": 4608,
|
||||
"userIcon": 4608,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"deviceClass": {
|
||||
"basic": "Static Controller",
|
||||
"generic": "Thermostat",
|
||||
"specific": "Thermostat General V2",
|
||||
"mandatorySupportedCCs": [
|
||||
"Basic",
|
||||
"Manufacturer Specific",
|
||||
"Thermostat Mode",
|
||||
"Thermostat Setpoint",
|
||||
"Version"
|
||||
],
|
||||
"mandatoryControlCCs": []
|
||||
},
|
||||
"isListening": true,
|
||||
"isFrequentListening": false,
|
||||
"isRouting": true,
|
||||
"maxBaudRate": 40000,
|
||||
"isSecure": false,
|
||||
"version": 4,
|
||||
"isBeaming": true,
|
||||
"manufacturerId": 152,
|
||||
"productId": 256,
|
||||
"productType": 25602,
|
||||
"firmwareVersion": "10.7",
|
||||
"zwavePlusVersion": 1,
|
||||
"nodeType": 0,
|
||||
"roleType": 5,
|
||||
"deviceConfig": {
|
||||
"manufacturerId": 152,
|
||||
"manufacturer": "Radio Thermostat Company of America (RTC)",
|
||||
"label": "CT100 Plus",
|
||||
"description": "Z-Wave Thermostat",
|
||||
"devices": [{ "productType": "0x6402", "productId": "0x0100" }],
|
||||
"firmwareVersion": { "min": "0.0", "max": "255.255" },
|
||||
"paramInformation": { "_map": {} }
|
||||
},
|
||||
"label": "CT100 Plus",
|
||||
"neighbors": [1, 2, 3, 4, 20],
|
||||
"endpointCountIsDynamic": false,
|
||||
"endpointsHaveIdenticalCapabilities": false,
|
||||
"individualEndpointCount": 2,
|
||||
"aggregatedEndpointCount": 0,
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 13,
|
||||
"index": 0,
|
||||
"installerIcon": 4608,
|
||||
"userIcon": 4608
|
||||
},
|
||||
{
|
||||
"nodeId": 13,
|
||||
"index": 1,
|
||||
"installerIcon": 4608,
|
||||
"userIcon": 4608
|
||||
},
|
||||
{ "nodeId": 13, "index": 2 }
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "manufacturerId",
|
||||
"propertyName": "manufacturerId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Manufacturer ID"
|
||||
},
|
||||
"value": 152
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productType",
|
||||
"propertyName": "productType",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product type"
|
||||
},
|
||||
"value": 25602
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productId",
|
||||
"propertyName": "productId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product ID"
|
||||
},
|
||||
"value": 256
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Library type"
|
||||
},
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version"
|
||||
},
|
||||
"value": "4.24"
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions"
|
||||
},
|
||||
"value": ["10.7"]
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "hardwareVersion",
|
||||
"propertyName": "hardwareVersion",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip hardware version"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Indicator",
|
||||
"commandClass": 135,
|
||||
"endpoint": 0,
|
||||
"property": "value",
|
||||
"propertyName": "value",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Indicator value",
|
||||
"ccSpecific": { "indicatorId": 0 }
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 1,
|
||||
"propertyName": "Temperature Reporting Threshold",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 4,
|
||||
"default": 2,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Disabled",
|
||||
"1": "0.5° F",
|
||||
"2": "1.0° F",
|
||||
"3": "1.5° F",
|
||||
"4": "2.0° F"
|
||||
},
|
||||
"label": "Temperature Reporting Threshold",
|
||||
"description": "Reporting threshold for changes in the ambient temperature",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 2,
|
||||
"propertyName": "HVAC Settings",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"valueSize": 4,
|
||||
"min": 0,
|
||||
"max": 0,
|
||||
"default": 0,
|
||||
"format": 0,
|
||||
"allowManualEntry": true,
|
||||
"label": "HVAC Settings",
|
||||
"description": "Configured HVAC settings",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 17891329
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 4,
|
||||
"propertyName": "Power Status",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 0,
|
||||
"default": 0,
|
||||
"format": 0,
|
||||
"allowManualEntry": true,
|
||||
"label": "Power Status",
|
||||
"description": "C-Wire / Battery Status",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 7,
|
||||
"propertyName": "Thermostat Swing Temperature",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 1,
|
||||
"max": 8,
|
||||
"default": 2,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"1": "0.5° F",
|
||||
"2": "1.0° F",
|
||||
"3": "1.5° F",
|
||||
"4": "2.0° F",
|
||||
"5": "2.5° F",
|
||||
"6": "3.0° F",
|
||||
"7": "3.5° F",
|
||||
"8": "4.0° F"
|
||||
},
|
||||
"label": "Thermostat Swing Temperature",
|
||||
"description": "Variance allowed from setpoint to engage HVAC",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 8,
|
||||
"propertyName": "Thermostat Diff Temperature",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 4,
|
||||
"max": 12,
|
||||
"default": 4,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": { "4": "2.0° F", "8": "4.0° F", "12": "6.0° F" },
|
||||
"label": "Thermostat Diff Temperature",
|
||||
"description": "Configures additional stages",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1028
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 9,
|
||||
"propertyName": "Thermostat Recovery Mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 1,
|
||||
"max": 2,
|
||||
"default": 2,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"1": "Fast recovery mode",
|
||||
"2": "Economy recovery mode"
|
||||
},
|
||||
"label": "Thermostat Recovery Mode",
|
||||
"description": "Fast or Economy recovery mode",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 10,
|
||||
"propertyName": "Temperature Reporting Filter",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 4,
|
||||
"min": 0,
|
||||
"max": 124,
|
||||
"default": 124,
|
||||
"format": 0,
|
||||
"allowManualEntry": true,
|
||||
"label": "Temperature Reporting Filter",
|
||||
"description": "Upper/Lower bounds for thermostat temperature reporting",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 11,
|
||||
"propertyName": "Simple UI Mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"default": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Normal mode enabled",
|
||||
"1": "Simple mode enabled"
|
||||
},
|
||||
"label": "Simple UI Mode",
|
||||
"description": "Simple mode enable/disable",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 12,
|
||||
"propertyName": "Multicast",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"default": 0,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Multicast disabled",
|
||||
"1": "Multicast enabled"
|
||||
},
|
||||
"label": "Multicast",
|
||||
"description": "Enable or disables Multicast",
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 3,
|
||||
"propertyName": "Utility Lock Enable/Disable",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": false,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"default": 0,
|
||||
"format": 1,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Utility lock disabled",
|
||||
"1": "Utility lock enabled"
|
||||
},
|
||||
"label": "Utility Lock Enable/Disable",
|
||||
"description": "Prevents setpoint changes at thermostat",
|
||||
"isFromConfig": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 5,
|
||||
"propertyName": "Humidity Reporting Threshold",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"default": 0,
|
||||
"format": 1,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Disabled",
|
||||
"1": "3% RH",
|
||||
"2": "5% RH",
|
||||
"3": "10% RH"
|
||||
},
|
||||
"label": "Humidity Reporting Threshold",
|
||||
"description": "Reporting threshold for changes in the relative humidity",
|
||||
"isFromConfig": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 6,
|
||||
"propertyName": "Auxiliary/Emergency",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"valueSize": 1,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"default": 0,
|
||||
"format": 1,
|
||||
"allowManualEntry": false,
|
||||
"states": {
|
||||
"0": "Auxiliary/Emergency heat disabled",
|
||||
"1": "Auxiliary/Emergency heat enabled"
|
||||
},
|
||||
"label": "Auxiliary/Emergency",
|
||||
"description": "Enables or disables auxiliary / emergency heating",
|
||||
"isFromConfig": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "level",
|
||||
"propertyName": "level",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "%",
|
||||
"label": "Battery level"
|
||||
},
|
||||
"value": 100
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "isLow",
|
||||
"propertyName": "isLow",
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Low battery level"
|
||||
},
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"commandClassName": "Indicator",
|
||||
"commandClass": 135,
|
||||
"endpoint": 1,
|
||||
"property": "value",
|
||||
"propertyName": "value",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Indicator value",
|
||||
"ccSpecific": { "indicatorId": 0 }
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"commandClassName": "Multilevel Sensor",
|
||||
"commandClass": 49,
|
||||
"endpoint": 1,
|
||||
"property": "Air temperature",
|
||||
"propertyName": "Air temperature",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"unit": "°F",
|
||||
"label": "Air temperature",
|
||||
"ccSpecific": { "sensorType": 1, "scale": 1 }
|
||||
},
|
||||
"value": 72
|
||||
},
|
||||
{
|
||||
"commandClassName": "Multilevel Sensor",
|
||||
"commandClass": 49,
|
||||
"endpoint": 1,
|
||||
"property": "Humidity",
|
||||
"propertyName": "Humidity",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"unit": "%",
|
||||
"label": "Humidity",
|
||||
"ccSpecific": { "sensorType": 5, "scale": 0 }
|
||||
},
|
||||
"value": 30
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode",
|
||||
"states": { "0": "Off", "1": "Heat", "2": "Cool", "3": "Auto" }
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 1,
|
||||
"property": "manufacturerData",
|
||||
"propertyName": "manufacturerData",
|
||||
"metadata": { "type": "any", "readable": true, "writeable": true }
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Operating State",
|
||||
"commandClass": 66,
|
||||
"endpoint": 1,
|
||||
"property": "state",
|
||||
"propertyName": "state",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Operating state",
|
||||
"states": {
|
||||
"0": "Idle",
|
||||
"1": "Heating",
|
||||
"2": "Cooling",
|
||||
"3": "Fan Only",
|
||||
"4": "Pending Heat",
|
||||
"5": "Pending Cool",
|
||||
"6": "Vent/Economizer",
|
||||
"7": "Aux Heating",
|
||||
"8": "2nd Stage Heating",
|
||||
"9": "2nd Stage Cooling",
|
||||
"10": "2nd Stage Aux Heat",
|
||||
"11": "3rd Stage Aux Heat"
|
||||
}
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 1,
|
||||
"propertyName": "setpoint",
|
||||
"propertyKeyName": "Heating",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"unit": "°F",
|
||||
"ccSpecific": { "setpointType": 1 }
|
||||
},
|
||||
"value": 72
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 2,
|
||||
"propertyName": "setpoint",
|
||||
"propertyKeyName": "Cooling",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"unit": "°F",
|
||||
"ccSpecific": { "setpointType": 2 }
|
||||
},
|
||||
"value": 73
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 1,
|
||||
"property": "level",
|
||||
"propertyName": "level",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "%",
|
||||
"label": "Battery level"
|
||||
},
|
||||
"value": 100
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 1,
|
||||
"property": "isLow",
|
||||
"propertyName": "isLow",
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Low battery level"
|
||||
},
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user