mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 07:07:28 +00:00
Add ZHA HVAC Action sensor (#57021)
* WIP * Refactor multi-entity matching Eliminate the notion on primary channel. * Cleanup climate tests * Refactor multi-entity match Remove the "primary channel" in multiple entity matches * Cleanup * Add HVAC Action sensor * Add a "stop_on_match" option for multi entities matches Nominally working HVAC state sensors * Add id_suffix for HVAC action sensor * Fix Zen HVAC action sensor * Pylint
This commit is contained in:
parent
69875cbd11
commit
723596076d
@ -60,8 +60,6 @@ from .core.const import (
|
|||||||
from .core.registries import ZHA_ENTITIES
|
from .core.registries import ZHA_ENTITIES
|
||||||
from .entity import ZhaEntity
|
from .entity import ZhaEntity
|
||||||
|
|
||||||
DEPENDENCIES = ["zha"]
|
|
||||||
|
|
||||||
ATTR_SYS_MODE = "system_mode"
|
ATTR_SYS_MODE = "system_mode"
|
||||||
ATTR_RUNNING_MODE = "running_mode"
|
ATTR_RUNNING_MODE = "running_mode"
|
||||||
ATTR_SETPT_CHANGE_SRC = "setpoint_change_source"
|
ATTR_SETPT_CHANGE_SRC = "setpoint_change_source"
|
||||||
@ -76,6 +74,7 @@ ATTR_UNOCCP_COOL_SETPT = "unoccupied_cooling_setpoint"
|
|||||||
|
|
||||||
|
|
||||||
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
|
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
|
||||||
|
MULTI_MATCH = functools.partial(ZHA_ENTITIES.multipass_match, DOMAIN)
|
||||||
RUNNING_MODE = {0x00: HVAC_MODE_OFF, 0x03: HVAC_MODE_COOL, 0x04: HVAC_MODE_HEAT}
|
RUNNING_MODE = {0x00: HVAC_MODE_OFF, 0x03: HVAC_MODE_COOL, 0x04: HVAC_MODE_HEAT}
|
||||||
|
|
||||||
|
|
||||||
@ -164,16 +163,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS].append(unsub)
|
hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS].append(unsub)
|
||||||
|
|
||||||
|
|
||||||
@STRICT_MATCH(channel_names=CHANNEL_THERMOSTAT, aux_channels=CHANNEL_FAN)
|
@MULTI_MATCH(channel_names=CHANNEL_THERMOSTAT, aux_channels=CHANNEL_FAN)
|
||||||
class Thermostat(ZhaEntity, ClimateEntity):
|
class Thermostat(ZhaEntity, ClimateEntity):
|
||||||
"""Representation of a ZHA Thermostat device."""
|
"""Representation of a ZHA Thermostat device."""
|
||||||
|
|
||||||
DEFAULT_MAX_TEMP = 35
|
DEFAULT_MAX_TEMP = 35
|
||||||
DEFAULT_MIN_TEMP = 7
|
DEFAULT_MIN_TEMP = 7
|
||||||
|
|
||||||
_domain = DOMAIN
|
|
||||||
value_attribute = 0x0000
|
|
||||||
|
|
||||||
def __init__(self, unique_id, zha_device, channels, **kwargs):
|
def __init__(self, unique_id, zha_device, channels, **kwargs):
|
||||||
"""Initialize ZHA Thermostat instance."""
|
"""Initialize ZHA Thermostat instance."""
|
||||||
super().__init__(unique_id, zha_device, channels, **kwargs)
|
super().__init__(unique_id, zha_device, channels, **kwargs)
|
||||||
@ -519,9 +515,10 @@ class Thermostat(ZhaEntity, ClimateEntity):
|
|||||||
return await handler(enable)
|
return await handler(enable)
|
||||||
|
|
||||||
|
|
||||||
@STRICT_MATCH(
|
@MULTI_MATCH(
|
||||||
channel_names={CHANNEL_THERMOSTAT, "sinope_manufacturer_specific"},
|
channel_names={CHANNEL_THERMOSTAT, "sinope_manufacturer_specific"},
|
||||||
manufacturers="Sinope Technologies",
|
manufacturers="Sinope Technologies",
|
||||||
|
stop_on_match=True,
|
||||||
)
|
)
|
||||||
class SinopeTechnologiesThermostat(Thermostat):
|
class SinopeTechnologiesThermostat(Thermostat):
|
||||||
"""Sinope Technologies Thermostat."""
|
"""Sinope Technologies Thermostat."""
|
||||||
@ -570,10 +567,11 @@ class SinopeTechnologiesThermostat(Thermostat):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@STRICT_MATCH(
|
@MULTI_MATCH(
|
||||||
channel_names=CHANNEL_THERMOSTAT,
|
channel_names=CHANNEL_THERMOSTAT,
|
||||||
aux_channels=CHANNEL_FAN,
|
aux_channels=CHANNEL_FAN,
|
||||||
manufacturers="Zen Within",
|
manufacturers="Zen Within",
|
||||||
|
stop_on_match=True,
|
||||||
)
|
)
|
||||||
class ZenWithinThermostat(Thermostat):
|
class ZenWithinThermostat(Thermostat):
|
||||||
"""Zen Within Thermostat implementation."""
|
"""Zen Within Thermostat implementation."""
|
||||||
@ -599,11 +597,12 @@ class ZenWithinThermostat(Thermostat):
|
|||||||
return CURRENT_HVAC_OFF
|
return CURRENT_HVAC_OFF
|
||||||
|
|
||||||
|
|
||||||
@STRICT_MATCH(
|
@MULTI_MATCH(
|
||||||
channel_names=CHANNEL_THERMOSTAT,
|
channel_names=CHANNEL_THERMOSTAT,
|
||||||
aux_channels=CHANNEL_FAN,
|
aux_channels=CHANNEL_FAN,
|
||||||
manufacturers="Centralite",
|
manufacturers="Centralite",
|
||||||
models="3157100",
|
models="3157100",
|
||||||
|
stop_on_match=True,
|
||||||
)
|
)
|
||||||
class CentralitePearl(ZenWithinThermostat):
|
class CentralitePearl(ZenWithinThermostat):
|
||||||
"""Centralite Pearl Thermostat implementation."""
|
"""Centralite Pearl Thermostat implementation."""
|
||||||
|
@ -63,8 +63,8 @@ class ProbeEndpoint:
|
|||||||
def discover_entities(self, channel_pool: zha_typing.ChannelPoolType) -> None:
|
def discover_entities(self, channel_pool: zha_typing.ChannelPoolType) -> None:
|
||||||
"""Process an endpoint on a zigpy device."""
|
"""Process an endpoint on a zigpy device."""
|
||||||
self.discover_by_device_type(channel_pool)
|
self.discover_by_device_type(channel_pool)
|
||||||
self.discover_by_cluster_id(channel_pool)
|
|
||||||
self.discover_multi_entities(channel_pool)
|
self.discover_multi_entities(channel_pool)
|
||||||
|
self.discover_by_cluster_id(channel_pool)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def discover_by_device_type(self, channel_pool: zha_typing.ChannelPoolType) -> None:
|
def discover_by_device_type(self, channel_pool: zha_typing.ChannelPoolType) -> None:
|
||||||
@ -166,24 +166,41 @@ class ProbeEndpoint:
|
|||||||
def discover_multi_entities(channel_pool: zha_typing.ChannelPoolType) -> None:
|
def discover_multi_entities(channel_pool: zha_typing.ChannelPoolType) -> None:
|
||||||
"""Process an endpoint on and discover multiple entities."""
|
"""Process an endpoint on and discover multiple entities."""
|
||||||
|
|
||||||
|
ep_profile_id = channel_pool.endpoint.profile_id
|
||||||
|
ep_device_type = channel_pool.endpoint.device_type
|
||||||
|
cmpt_by_dev_type = zha_regs.DEVICE_CLASS[ep_profile_id].get(ep_device_type)
|
||||||
remaining_channels = channel_pool.unclaimed_channels()
|
remaining_channels = channel_pool.unclaimed_channels()
|
||||||
for channel in remaining_channels:
|
|
||||||
unique_id = f"{channel_pool.unique_id}-{channel.cluster.cluster_id}"
|
|
||||||
|
|
||||||
matches, claimed = zha_regs.ZHA_ENTITIES.get_multi_entity(
|
matches, claimed = zha_regs.ZHA_ENTITIES.get_multi_entity(
|
||||||
channel_pool.manufacturer,
|
channel_pool.manufacturer, channel_pool.model, remaining_channels
|
||||||
channel_pool.model,
|
|
||||||
channel,
|
|
||||||
remaining_channels,
|
|
||||||
)
|
)
|
||||||
if not claimed:
|
|
||||||
continue
|
|
||||||
|
|
||||||
channel_pool.claim_channels(claimed)
|
channel_pool.claim_channels(claimed)
|
||||||
for component, ent_classes_list in matches.items():
|
for component, ent_n_chan_list in matches.items():
|
||||||
for entity_class in ent_classes_list:
|
for entity_and_channel in ent_n_chan_list:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"'%s' component -> '%s' using %s",
|
||||||
|
component,
|
||||||
|
entity_and_channel.entity_class.__name__,
|
||||||
|
[ch.name for ch in entity_and_channel.claimed_channel],
|
||||||
|
)
|
||||||
|
for component, ent_n_chan_list in matches.items():
|
||||||
|
for entity_and_channel in ent_n_chan_list:
|
||||||
|
if component == cmpt_by_dev_type:
|
||||||
|
# for well known device types, like thermostats we'll take only 1st class
|
||||||
channel_pool.async_new_entity(
|
channel_pool.async_new_entity(
|
||||||
component, entity_class, unique_id, claimed
|
component,
|
||||||
|
entity_and_channel.entity_class,
|
||||||
|
channel_pool.unique_id,
|
||||||
|
entity_and_channel.claimed_channel,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
first_ch = entity_and_channel.claimed_channel[0]
|
||||||
|
channel_pool.async_new_entity(
|
||||||
|
component,
|
||||||
|
entity_and_channel.entity_class,
|
||||||
|
f"{channel_pool.unique_id}-{first_ch.cluster.cluster_id}",
|
||||||
|
entity_and_channel.claimed_channel,
|
||||||
)
|
)
|
||||||
|
|
||||||
def initialize(self, hass: HomeAssistant) -> None:
|
def initialize(self, hass: HomeAssistant) -> None:
|
||||||
|
@ -3,7 +3,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Dict
|
import dataclasses
|
||||||
|
import logging
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
from zigpy import zcl
|
from zigpy import zcl
|
||||||
@ -27,6 +29,7 @@ from . import channels as zha_channels # noqa: F401 pylint: disable=unused-impo
|
|||||||
from .decorators import CALLABLE_T, DictRegistry, SetRegistry
|
from .decorators import CALLABLE_T, DictRegistry, SetRegistry
|
||||||
from .typing import ChannelType
|
from .typing import ChannelType
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
GROUP_ENTITY_DOMAINS = [LIGHT, SWITCH, FAN]
|
GROUP_ENTITY_DOMAINS = [LIGHT, SWITCH, FAN]
|
||||||
|
|
||||||
PHILLIPS_REMOTE_CLUSTER = 0xFC00
|
PHILLIPS_REMOTE_CLUSTER = 0xFC00
|
||||||
@ -157,6 +160,8 @@ class MatchRule:
|
|||||||
aux_channels: Callable | set[str] | str = attr.ib(
|
aux_channels: Callable | set[str] | str = attr.ib(
|
||||||
factory=frozenset, converter=set_or_callable
|
factory=frozenset, converter=set_or_callable
|
||||||
)
|
)
|
||||||
|
# for multi entities, stop further processing on a match for a component
|
||||||
|
stop_on_match: bool = attr.ib(default=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def weight(self) -> int:
|
def weight(self) -> int:
|
||||||
@ -234,8 +239,16 @@ class MatchRule:
|
|||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
RegistryDictType = Dict[str, Dict[MatchRule, CALLABLE_T]]
|
@dataclasses.dataclass
|
||||||
|
class EntityClassAndChannels:
|
||||||
|
"""Container for entity class and corresponding channels."""
|
||||||
|
|
||||||
|
entity_class: CALLABLE_T
|
||||||
|
claimed_channel: list[ChannelType]
|
||||||
|
|
||||||
|
|
||||||
|
RegistryDictType = Dict[str, Dict[MatchRule, CALLABLE_T]]
|
||||||
|
MultiRegistryDictType = Dict[str, Dict[MatchRule, List[CALLABLE_T]]]
|
||||||
GroupRegistryDictType = Dict[str, CALLABLE_T]
|
GroupRegistryDictType = Dict[str, CALLABLE_T]
|
||||||
|
|
||||||
|
|
||||||
@ -245,7 +258,7 @@ class ZHAEntityRegistry:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize Registry instance."""
|
"""Initialize Registry instance."""
|
||||||
self._strict_registry: RegistryDictType = collections.defaultdict(dict)
|
self._strict_registry: RegistryDictType = collections.defaultdict(dict)
|
||||||
self._multi_entity_registry: RegistryDictType = collections.defaultdict(
|
self._multi_entity_registry: MultiRegistryDictType = collections.defaultdict(
|
||||||
lambda: collections.defaultdict(list)
|
lambda: collections.defaultdict(list)
|
||||||
)
|
)
|
||||||
self._group_registry: GroupRegistryDictType = {}
|
self._group_registry: GroupRegistryDictType = {}
|
||||||
@ -271,22 +284,26 @@ class ZHAEntityRegistry:
|
|||||||
self,
|
self,
|
||||||
manufacturer: str,
|
manufacturer: str,
|
||||||
model: str,
|
model: str,
|
||||||
primary_channel: ChannelType,
|
channels: list[ChannelType],
|
||||||
aux_channels: list[ChannelType],
|
|
||||||
components: set | None = None,
|
components: set | None = None,
|
||||||
) -> tuple[dict[str, list[CALLABLE_T]], list[ChannelType]]:
|
) -> tuple[dict[str, list[EntityClassAndChannels]], list[ChannelType]]:
|
||||||
"""Match ZHA Channels to potentially multiple ZHA Entity classes."""
|
"""Match ZHA Channels to potentially multiple ZHA Entity classes."""
|
||||||
result: dict[str, list[CALLABLE_T]] = collections.defaultdict(list)
|
result: dict[str, list[EntityClassAndChannels]] = collections.defaultdict(list)
|
||||||
claimed: set[ChannelType] = set()
|
all_claimed: set[ChannelType] = set()
|
||||||
for component in components or self._multi_entity_registry:
|
for component in components or self._multi_entity_registry:
|
||||||
matches = self._multi_entity_registry[component]
|
matches = self._multi_entity_registry[component]
|
||||||
for match in sorted(matches, key=lambda x: x.weight, reverse=True):
|
sorted_matches = sorted(matches, key=lambda x: x.weight, reverse=True)
|
||||||
if match.strict_matched(manufacturer, model, [primary_channel]):
|
for match in sorted_matches:
|
||||||
claimed |= set(match.claim_channels(aux_channels))
|
if match.strict_matched(manufacturer, model, channels):
|
||||||
ent_classes = self._multi_entity_registry[component][match]
|
claimed = match.claim_channels(channels)
|
||||||
result[component].extend(ent_classes)
|
for ent_class in self._multi_entity_registry[component][match]:
|
||||||
|
ent_n_channels = EntityClassAndChannels(ent_class, claimed)
|
||||||
|
result[component].append(ent_n_channels)
|
||||||
|
all_claimed |= set(claimed)
|
||||||
|
if match.stop_on_match:
|
||||||
|
break
|
||||||
|
|
||||||
return result, list(claimed)
|
return result, list(all_claimed)
|
||||||
|
|
||||||
def get_group_entity(self, component: str) -> CALLABLE_T:
|
def get_group_entity(self, component: str) -> CALLABLE_T:
|
||||||
"""Match a ZHA group to a ZHA Entity class."""
|
"""Match a ZHA group to a ZHA Entity class."""
|
||||||
@ -325,11 +342,17 @@ class ZHAEntityRegistry:
|
|||||||
manufacturers: Callable | set[str] | str = None,
|
manufacturers: Callable | set[str] | str = None,
|
||||||
models: Callable | set[str] | str = None,
|
models: Callable | set[str] | str = None,
|
||||||
aux_channels: Callable | set[str] | str = None,
|
aux_channels: Callable | set[str] | str = None,
|
||||||
|
stop_on_match: bool = False,
|
||||||
) -> Callable[[CALLABLE_T], CALLABLE_T]:
|
) -> Callable[[CALLABLE_T], CALLABLE_T]:
|
||||||
"""Decorate a loose match rule."""
|
"""Decorate a loose match rule."""
|
||||||
|
|
||||||
rule = MatchRule(
|
rule = MatchRule(
|
||||||
channel_names, generic_ids, manufacturers, models, aux_channels
|
channel_names,
|
||||||
|
generic_ids,
|
||||||
|
manufacturers,
|
||||||
|
models,
|
||||||
|
aux_channels,
|
||||||
|
stop_on_match,
|
||||||
)
|
)
|
||||||
|
|
||||||
def decorator(zha_entity: CALLABLE_T) -> CALLABLE_T:
|
def decorator(zha_entity: CALLABLE_T) -> CALLABLE_T:
|
||||||
|
@ -5,6 +5,13 @@ import functools
|
|||||||
import numbers
|
import numbers
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from homeassistant.components.climate.const import (
|
||||||
|
CURRENT_HVAC_COOL,
|
||||||
|
CURRENT_HVAC_FAN,
|
||||||
|
CURRENT_HVAC_HEAT,
|
||||||
|
CURRENT_HVAC_IDLE,
|
||||||
|
CURRENT_HVAC_OFF,
|
||||||
|
)
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
DEVICE_CLASS_CO,
|
DEVICE_CLASS_CO,
|
||||||
@ -57,6 +64,7 @@ from .core.const import (
|
|||||||
CHANNEL_PRESSURE,
|
CHANNEL_PRESSURE,
|
||||||
CHANNEL_SMARTENERGY_METERING,
|
CHANNEL_SMARTENERGY_METERING,
|
||||||
CHANNEL_TEMPERATURE,
|
CHANNEL_TEMPERATURE,
|
||||||
|
CHANNEL_THERMOSTAT,
|
||||||
DATA_ZHA,
|
DATA_ZHA,
|
||||||
DATA_ZHA_DISPATCHERS,
|
DATA_ZHA_DISPATCHERS,
|
||||||
SIGNAL_ADD_ENTITIES,
|
SIGNAL_ADD_ENTITIES,
|
||||||
@ -482,3 +490,120 @@ class FormaldehydeConcentration(Sensor):
|
|||||||
_decimals = 0
|
_decimals = 0
|
||||||
_multiplier = 1e6
|
_multiplier = 1e6
|
||||||
_unit = CONCENTRATION_PARTS_PER_MILLION
|
_unit = CONCENTRATION_PARTS_PER_MILLION
|
||||||
|
|
||||||
|
|
||||||
|
@MULTI_MATCH(channel_names=CHANNEL_THERMOSTAT)
|
||||||
|
class ThermostatHVACAction(Sensor, id_suffix="hvac_action"):
|
||||||
|
"""Thermostat HVAC action sensor."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_entity(
|
||||||
|
cls,
|
||||||
|
unique_id: str,
|
||||||
|
zha_device: ZhaDeviceType,
|
||||||
|
channels: list[ChannelType],
|
||||||
|
**kwargs,
|
||||||
|
) -> ZhaEntity | None:
|
||||||
|
"""Entity Factory.
|
||||||
|
|
||||||
|
Return entity if it is a supported configuration, otherwise return None
|
||||||
|
"""
|
||||||
|
|
||||||
|
return cls(unique_id, zha_device, channels, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> str | None:
|
||||||
|
"""Return the current HVAC action."""
|
||||||
|
if (
|
||||||
|
self._channel.pi_heating_demand is None
|
||||||
|
and self._channel.pi_cooling_demand is None
|
||||||
|
):
|
||||||
|
return self._rm_rs_action
|
||||||
|
return self._pi_demand_action
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _rm_rs_action(self) -> str | None:
|
||||||
|
"""Return the current HVAC action based on running mode and running state."""
|
||||||
|
|
||||||
|
running_mode = self._channel.running_mode
|
||||||
|
if running_mode == self._channel.RunningMode.Heat:
|
||||||
|
return CURRENT_HVAC_HEAT
|
||||||
|
if running_mode == self._channel.RunningMode.Cool:
|
||||||
|
return CURRENT_HVAC_COOL
|
||||||
|
|
||||||
|
running_state = self._channel.running_state
|
||||||
|
if running_state and running_state & (
|
||||||
|
self._channel.RunningState.Fan_State_On
|
||||||
|
| self._channel.RunningState.Fan_2nd_Stage_On
|
||||||
|
| self._channel.RunningState.Fan_3rd_Stage_On
|
||||||
|
):
|
||||||
|
return CURRENT_HVAC_FAN
|
||||||
|
if (
|
||||||
|
self._channel.system_mode != self._channel.SystemMode.Off
|
||||||
|
and running_mode == self._channel.SystemMode.Off
|
||||||
|
):
|
||||||
|
return CURRENT_HVAC_IDLE
|
||||||
|
return CURRENT_HVAC_OFF
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _pi_demand_action(self) -> str | None:
|
||||||
|
"""Return the current HVAC action based on pi_demands."""
|
||||||
|
|
||||||
|
heating_demand = self._channel.pi_heating_demand
|
||||||
|
if heating_demand is not None and heating_demand > 0:
|
||||||
|
return CURRENT_HVAC_HEAT
|
||||||
|
cooling_demand = self._channel.pi_cooling_demand
|
||||||
|
if cooling_demand is not None and cooling_demand > 0:
|
||||||
|
return CURRENT_HVAC_COOL
|
||||||
|
|
||||||
|
if self._channel.system_mode != self._channel.SystemMode.Off:
|
||||||
|
return CURRENT_HVAC_IDLE
|
||||||
|
return CURRENT_HVAC_OFF
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_set_state(self, *args, **kwargs) -> None:
|
||||||
|
"""Handle state update from channel."""
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
@MULTI_MATCH(
|
||||||
|
channel_names=CHANNEL_THERMOSTAT,
|
||||||
|
manufacturers="Zen Within",
|
||||||
|
stop_on_match=True,
|
||||||
|
)
|
||||||
|
class ZenHVACAction(ThermostatHVACAction):
|
||||||
|
"""Zen Within Thermostat HVAC Action."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _rm_rs_action(self) -> str | None:
|
||||||
|
"""Return the current HVAC action based on running mode and running state."""
|
||||||
|
|
||||||
|
running_state = self._channel.running_state
|
||||||
|
if running_state is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
rs_heat = (
|
||||||
|
self._channel.RunningState.Heat_State_On
|
||||||
|
| self._channel.RunningState.Heat_2nd_Stage_On
|
||||||
|
)
|
||||||
|
if running_state & rs_heat:
|
||||||
|
return CURRENT_HVAC_HEAT
|
||||||
|
|
||||||
|
rs_cool = (
|
||||||
|
self._channel.RunningState.Cool_State_On
|
||||||
|
| self._channel.RunningState.Cool_2nd_Stage_On
|
||||||
|
)
|
||||||
|
if running_state & rs_cool:
|
||||||
|
return CURRENT_HVAC_COOL
|
||||||
|
|
||||||
|
running_state = self._channel.running_state
|
||||||
|
if running_state and running_state & (
|
||||||
|
self._channel.RunningState.Fan_State_On
|
||||||
|
| self._channel.RunningState.Fan_2nd_Stage_On
|
||||||
|
| self._channel.RunningState.Fan_3rd_Stage_On
|
||||||
|
):
|
||||||
|
return CURRENT_HVAC_FAN
|
||||||
|
|
||||||
|
if self._channel.system_mode != self._channel.SystemMode.Off:
|
||||||
|
return CURRENT_HVAC_IDLE
|
||||||
|
return CURRENT_HVAC_OFF
|
||||||
|
@ -45,6 +45,7 @@ from homeassistant.components.climate.const import (
|
|||||||
SERVICE_SET_PRESET_MODE,
|
SERVICE_SET_PRESET_MODE,
|
||||||
SERVICE_SET_TEMPERATURE,
|
SERVICE_SET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.components.zha.climate import (
|
from homeassistant.components.zha.climate import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
HVAC_MODE_2_SYSTEM,
|
HVAC_MODE_2_SYSTEM,
|
||||||
@ -174,6 +175,7 @@ def device_climate_mock(hass, zigpy_device_mock, zha_device_joined):
|
|||||||
plugged_attrs = {**ZCL_ATTR_PLUG, **plug}
|
plugged_attrs = {**ZCL_ATTR_PLUG, **plug}
|
||||||
|
|
||||||
zigpy_device = zigpy_device_mock(clusters, manufacturer=manuf, quirk=quirk)
|
zigpy_device = zigpy_device_mock(clusters, manufacturer=manuf, quirk=quirk)
|
||||||
|
zigpy_device.node_desc.mac_capability_flags |= 0b_0000_0100
|
||||||
zigpy_device.endpoints[1].thermostat.PLUGGED_ATTR_READS = plugged_attrs
|
zigpy_device.endpoints[1].thermostat.PLUGGED_ATTR_READS = plugged_attrs
|
||||||
zha_device = await zha_device_joined(zigpy_device)
|
zha_device = await zha_device_joined(zigpy_device)
|
||||||
await async_enable_traffic(hass, [zha_device])
|
await async_enable_traffic(hass, [zha_device])
|
||||||
@ -257,45 +259,60 @@ async def test_climate_hvac_action_running_state(hass, device_climate):
|
|||||||
|
|
||||||
thrm_cluster = device_climate.device.endpoints[1].thermostat
|
thrm_cluster = device_climate.device.endpoints[1].thermostat
|
||||||
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
|
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
|
||||||
|
sensor_entity_id = await find_entity_id(SENSOR_DOMAIN, device_climate, hass)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_OFF
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
|
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_OFF
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001C: Thermostat.SystemMode.Auto}
|
hass, thrm_cluster, {0x001C: Thermostat.SystemMode.Auto}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_IDLE
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Cool}
|
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Cool}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_COOL
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Heat}
|
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Heat}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_HEAT
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
|
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_IDLE
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_State_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_State_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_FAN
|
||||||
|
|
||||||
|
|
||||||
async def test_climate_hvac_action_running_state_zen(hass, device_climate_zen):
|
async def test_climate_hvac_action_running_state_zen(hass, device_climate_zen):
|
||||||
@ -303,63 +320,84 @@ async def test_climate_hvac_action_running_state_zen(hass, device_climate_zen):
|
|||||||
|
|
||||||
thrm_cluster = device_climate_zen.device.endpoints[1].thermostat
|
thrm_cluster = device_climate_zen.device.endpoints[1].thermostat
|
||||||
entity_id = await find_entity_id(DOMAIN, device_climate_zen, hass)
|
entity_id = await find_entity_id(DOMAIN, device_climate_zen, hass)
|
||||||
|
sensor_entity_id = await find_entity_id(SENSOR_DOMAIN, device_climate_zen, hass)
|
||||||
|
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert ATTR_HVAC_ACTION not in state.attributes
|
assert ATTR_HVAC_ACTION not in state.attributes
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == "unknown"
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Cool_2nd_Stage_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Cool_2nd_Stage_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_COOL
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_State_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_State_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_FAN
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Heat_2nd_Stage_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Heat_2nd_Stage_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_HEAT
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_2nd_Stage_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_2nd_Stage_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_FAN
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Cool_State_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Cool_State_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_COOL
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_3rd_Stage_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_3rd_Stage_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_FAN
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Heat_State_On}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Heat_State_On}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_HEAT
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Idle}
|
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Idle}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_OFF
|
||||||
|
|
||||||
await send_attributes_report(
|
await send_attributes_report(
|
||||||
hass, thrm_cluster, {0x001C: Thermostat.SystemMode.Heat}
|
hass, thrm_cluster, {0x001C: Thermostat.SystemMode.Heat}
|
||||||
)
|
)
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
|
||||||
|
hvac_sensor_state = hass.states.get(sensor_entity_id)
|
||||||
|
assert hvac_sensor_state.state == CURRENT_HVAC_IDLE
|
||||||
|
|
||||||
|
|
||||||
async def test_climate_hvac_action_pi_demand(hass, device_climate):
|
async def test_climate_hvac_action_pi_demand(hass, device_climate):
|
||||||
|
@ -332,27 +332,13 @@ def test_multi_sensor_match(channel, entity_registry):
|
|||||||
ch_illuminati = channel("illuminance", 0x0401)
|
ch_illuminati = channel("illuminance", 0x0401)
|
||||||
|
|
||||||
match, claimed = entity_registry.get_multi_entity(
|
match, claimed = entity_registry.get_multi_entity(
|
||||||
"manufacturer",
|
"manufacturer", "model", channels=[ch_se, ch_illuminati]
|
||||||
"model",
|
|
||||||
primary_channel=ch_illuminati,
|
|
||||||
aux_channels=[ch_se, ch_illuminati],
|
|
||||||
)
|
|
||||||
|
|
||||||
assert s.binary_sensor not in match
|
|
||||||
assert s.component not in match
|
|
||||||
assert set(claimed) == set()
|
|
||||||
|
|
||||||
match, claimed = entity_registry.get_multi_entity(
|
|
||||||
"manufacturer",
|
|
||||||
"model",
|
|
||||||
primary_channel=ch_se,
|
|
||||||
aux_channels=[ch_se, ch_illuminati],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert s.binary_sensor in match
|
assert s.binary_sensor in match
|
||||||
assert s.component not in match
|
assert s.component not in match
|
||||||
assert set(claimed) == {ch_se}
|
assert set(claimed) == {ch_se}
|
||||||
assert {cls.__name__ for cls in match[s.binary_sensor]} == {
|
assert {cls.entity_class.__name__ for cls in match[s.binary_sensor]} == {
|
||||||
SmartEnergySensor2.__name__
|
SmartEnergySensor2.__name__
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,17 +357,16 @@ def test_multi_sensor_match(channel, entity_registry):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
match, claimed = entity_registry.get_multi_entity(
|
match, claimed = entity_registry.get_multi_entity(
|
||||||
"manufacturer",
|
"manufacturer", "model", channels={ch_se, ch_illuminati}
|
||||||
"model",
|
|
||||||
primary_channel=ch_se,
|
|
||||||
aux_channels={ch_se, ch_illuminati},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert s.binary_sensor in match
|
assert s.binary_sensor in match
|
||||||
assert s.component in match
|
assert s.component in match
|
||||||
assert set(claimed) == {ch_se, ch_illuminati}
|
assert set(claimed) == {ch_se, ch_illuminati}
|
||||||
assert {cls.__name__ for cls in match[s.binary_sensor]} == {
|
assert {cls.entity_class.__name__ for cls in match[s.binary_sensor]} == {
|
||||||
SmartEnergySensor2.__name__,
|
SmartEnergySensor2.__name__,
|
||||||
SmartEnergySensor3.__name__,
|
SmartEnergySensor3.__name__,
|
||||||
}
|
}
|
||||||
assert {cls.__name__ for cls in match[s.component]} == {SmartEnergySensor1.__name__}
|
assert {cls.entity_class.__name__ for cls in match[s.component]} == {
|
||||||
|
SmartEnergySensor1.__name__
|
||||||
|
}
|
||||||
|
@ -3174,6 +3174,7 @@ DEVICES = [
|
|||||||
"sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_current",
|
"sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_current",
|
||||||
"sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_voltage",
|
"sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_voltage",
|
||||||
"sensor.sinope_technologies_th1123zb_77665544_temperature",
|
"sensor.sinope_technologies_th1123zb_77665544_temperature",
|
||||||
|
"sensor.sinope_technologies_th1123zb_77665544_thermostat_hvac_action",
|
||||||
],
|
],
|
||||||
DEV_SIG_ENT_MAP: {
|
DEV_SIG_ENT_MAP: {
|
||||||
("climate", "00:11:22:33:44:55:66:77-1"): {
|
("climate", "00:11:22:33:44:55:66:77-1"): {
|
||||||
@ -3201,6 +3202,11 @@ DEVICES = [
|
|||||||
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementRMSVoltage",
|
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementRMSVoltage",
|
||||||
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_voltage",
|
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1123zb_77665544_electrical_measurement_rms_voltage",
|
||||||
},
|
},
|
||||||
|
("sensor", "00:11:22:33:44:55:66:77-1-513-hvac_action"): {
|
||||||
|
DEV_SIG_CHANNELS: ["thermostat"],
|
||||||
|
DEV_SIG_ENT_MAP_CLASS: "ThermostatHVACAction",
|
||||||
|
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1123zb_77665544_thermostat_hvac_action",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
DEV_SIG_EVT_CHANNELS: ["1:0x0019"],
|
DEV_SIG_EVT_CHANNELS: ["1:0x0019"],
|
||||||
SIG_MANUFACTURER: "Sinope Technologies",
|
SIG_MANUFACTURER: "Sinope Technologies",
|
||||||
@ -3231,6 +3237,7 @@ DEVICES = [
|
|||||||
"sensor.sinope_technologies_th1124zb_77665544_electrical_measurement_rms_current",
|
"sensor.sinope_technologies_th1124zb_77665544_electrical_measurement_rms_current",
|
||||||
"sensor.sinope_technologies_th1124zb_77665544_electrical_measurement_rms_voltage",
|
"sensor.sinope_technologies_th1124zb_77665544_electrical_measurement_rms_voltage",
|
||||||
"sensor.sinope_technologies_th1124zb_77665544_temperature",
|
"sensor.sinope_technologies_th1124zb_77665544_temperature",
|
||||||
|
"sensor.sinope_technologies_th1124zb_77665544_thermostat_hvac_action",
|
||||||
"climate.sinope_technologies_th1124zb_77665544_thermostat",
|
"climate.sinope_technologies_th1124zb_77665544_thermostat",
|
||||||
],
|
],
|
||||||
DEV_SIG_ENT_MAP: {
|
DEV_SIG_ENT_MAP: {
|
||||||
@ -3239,6 +3246,11 @@ DEVICES = [
|
|||||||
DEV_SIG_ENT_MAP_CLASS: "Thermostat",
|
DEV_SIG_ENT_MAP_CLASS: "Thermostat",
|
||||||
DEV_SIG_ENT_MAP_ID: "climate.sinope_technologies_th1124zb_77665544_thermostat",
|
DEV_SIG_ENT_MAP_ID: "climate.sinope_technologies_th1124zb_77665544_thermostat",
|
||||||
},
|
},
|
||||||
|
("sensor", "00:11:22:33:44:55:66:77-1-513-hvac_action"): {
|
||||||
|
DEV_SIG_CHANNELS: ["thermostat"],
|
||||||
|
DEV_SIG_ENT_MAP_CLASS: "ThermostatHVACAction",
|
||||||
|
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_th1124zb_77665544_thermostat_hvac_action",
|
||||||
|
},
|
||||||
("sensor", "00:11:22:33:44:55:66:77-1-1026"): {
|
("sensor", "00:11:22:33:44:55:66:77-1-1026"): {
|
||||||
DEV_SIG_CHANNELS: ["temperature"],
|
DEV_SIG_CHANNELS: ["temperature"],
|
||||||
DEV_SIG_ENT_MAP_CLASS: "Temperature",
|
DEV_SIG_ENT_MAP_CLASS: "Temperature",
|
||||||
@ -3454,6 +3466,7 @@ DEVICES = [
|
|||||||
},
|
},
|
||||||
DEV_SIG_ENTITIES: [
|
DEV_SIG_ENTITIES: [
|
||||||
"climate.zen_within_zen_01_77665544_fan_thermostat",
|
"climate.zen_within_zen_01_77665544_fan_thermostat",
|
||||||
|
"sensor.zen_within_zen_01_77665544_thermostat_hvac_action",
|
||||||
"sensor.zen_within_zen_01_77665544_power",
|
"sensor.zen_within_zen_01_77665544_power",
|
||||||
],
|
],
|
||||||
DEV_SIG_ENT_MAP: {
|
DEV_SIG_ENT_MAP: {
|
||||||
@ -3467,6 +3480,11 @@ DEVICES = [
|
|||||||
DEV_SIG_ENT_MAP_CLASS: "ZenWithinThermostat",
|
DEV_SIG_ENT_MAP_CLASS: "ZenWithinThermostat",
|
||||||
DEV_SIG_ENT_MAP_ID: "climate.zen_within_zen_01_77665544_fan_thermostat",
|
DEV_SIG_ENT_MAP_ID: "climate.zen_within_zen_01_77665544_fan_thermostat",
|
||||||
},
|
},
|
||||||
|
("sensor", "00:11:22:33:44:55:66:77-1-513-hvac_action"): {
|
||||||
|
DEV_SIG_CHANNELS: ["thermostat"],
|
||||||
|
DEV_SIG_ENT_MAP_CLASS: "ZenHVACAction",
|
||||||
|
DEV_SIG_ENT_MAP_ID: "sensor.zen_within_zen_01_77665544_thermostat_hvac_action",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
DEV_SIG_EVT_CHANNELS: ["1:0x0019"],
|
DEV_SIG_EVT_CHANNELS: ["1:0x0019"],
|
||||||
SIG_MANUFACTURER: "Zen Within",
|
SIG_MANUFACTURER: "Zen Within",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user