Skip to content

Commit

Permalink
correctly extend cluster handlers in manufacturerspecific.py and remo…
Browse files Browse the repository at this point in the history
…ve workaround for illegal use of attribute updated signals in climate.py
  • Loading branch information
Caius-Bonus committed Oct 1, 2023
1 parent e49aed5 commit a33d14b
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 123 deletions.
8 changes: 4 additions & 4 deletions homeassistant/components/zha/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
CLUSTER_HANDLER_ON_OFF,
CLUSTER_HANDLER_ZONE,
SIGNAL_ADD_ENTITIES,
SIGNAL_ATTR_UPDATED,
SIGNAL_ATTR_UPDATED, CLUSTER_HANDLER_THERMOSTAT,
)
from .core.helpers import get_zha_data
from .core.registries import ZHA_ENTITIES
Expand Down Expand Up @@ -341,7 +341,7 @@ class AqaraLinkageAlarmState(BinarySensor, id_suffix="linkage_alarm_state"):


@MULTI_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossMountingModeActive(BinarySensor, id_suffix="mounting_mode_active"):
Expand All @@ -353,7 +353,7 @@ class DanfossMountingModeActive(BinarySensor, id_suffix="mounting_mode_active"):


@MULTI_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossHeatRequired(BinarySensor, id_suffix="heat_required"):
Expand All @@ -364,7 +364,7 @@ class DanfossHeatRequired(BinarySensor, id_suffix="heat_required"):


@MULTI_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossPreheatStatus(BinarySensor, id_suffix="preheat_status"):
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/zha/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,10 @@ async def async_added_to_hass(self) -> None:
self._thrm, SIGNAL_ATTR_UPDATED, self.async_attribute_updated
)

async def async_attribute_updated(self, record):
async def async_attribute_updated(self, attr_id, attr_name, value):
"""Handle attribute update from device."""
if (
record.attr_name in (ATTR_OCCP_COOL_SETPT, ATTR_OCCP_HEAT_SETPT)
attr_name in (ATTR_OCCP_COOL_SETPT, ATTR_OCCP_HEAT_SETPT)
and self.preset_mode == PRESET_AWAY
):
# occupancy attribute is an unreportable attribute, but if we get
Expand All @@ -379,7 +379,7 @@ async def async_attribute_updated(self, record):
if await self._thrm.get_occupancy() is True:
self._preset = PRESET_NONE

self.debug("Attribute '%s' = %s update", record.attr_name, record.value)
self.debug("Attribute '%s' = %s update", attr_name, value)
self.async_write_ha_state()

async def async_set_fan_mode(self, fan_mode: str) -> None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/zha/core/cluster_handlers/hvac.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def attribute_updated(self, attrid: int, value: Any, _: Any) -> None:
)
self.async_send_signal(
f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}",
AttributeUpdateRecord(attrid, attr_name, value),
attrid, attr_name, value,
)

async def async_set_operation_mode(self, mode) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Collection

from zhaquirks.inovelli.types import AllLEDEffectType, SingleLEDEffectType
import zigpy.zcl

from homeassistant.core import callback
from .homeautomation import Diagnostic
from .hvac import ThermostatClusterHandler, UserInterface

from .. import registries
from ..const import (
Expand Down Expand Up @@ -74,22 +76,22 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None:
super().__init__(cluster, endpoint)

if self.cluster.endpoint.manufacturer in (
"_TZE200_7tdtqgwv",
"_TZE200_amp6tsvy",
"_TZE200_oisqyl4o",
"_TZE200_vhy3iakz",
"_TZ3000_uim07oem",
"_TZE200_wfxuhoea",
"_TZE200_tviaymwx",
"_TZE200_g1ib5ldv",
"_TZE200_wunufsil",
"_TZE200_7deq70b8",
"_TZE200_tz32mtza",
"_TZE200_2hf7x9n3",
"_TZE200_aqnazj70",
"_TZE200_1ozguk6x",
"_TZE200_k6jhsr0q",
"_TZE200_9mahtqtg",
"_TZE200_7tdtqgwv",
"_TZE200_amp6tsvy",
"_TZE200_oisqyl4o",
"_TZE200_vhy3iakz",
"_TZ3000_uim07oem",
"_TZE200_wfxuhoea",
"_TZE200_tviaymwx",
"_TZE200_g1ib5ldv",
"_TZE200_wunufsil",
"_TZE200_7deq70b8",
"_TZE200_tz32mtza",
"_TZE200_2hf7x9n3",
"_TZE200_aqnazj70",
"_TZE200_1ozguk6x",
"_TZE200_k6jhsr0q",
"_TZE200_9mahtqtg",
):
self.ZCL_INIT_ATTRS = {
"backlight_mode": True,
Expand Down Expand Up @@ -286,12 +288,12 @@ class InovelliConfigEntityClusterHandler(ClusterHandler):
}

async def issue_all_led_effect(
self,
effect_type: AllLEDEffectType | int = AllLEDEffectType.Fast_Blink,
color: int = 200,
level: int = 100,
duration: int = 3,
**kwargs: Any,
self,
effect_type: AllLEDEffectType | int = AllLEDEffectType.Fast_Blink,
color: int = 200,
level: int = 100,
duration: int = 3,
**kwargs: Any,
) -> None:
"""Issue all LED effect command.
Expand All @@ -301,13 +303,13 @@ async def issue_all_led_effect(
await self.led_effect(effect_type, color, level, duration, expect_reply=False)

async def issue_individual_led_effect(
self,
led_number: int = 1,
effect_type: SingleLEDEffectType | int = SingleLEDEffectType.Fast_Blink,
color: int = 200,
level: int = 100,
duration: int = 3,
**kwargs: Any,
self,
led_number: int = 1,
effect_type: SingleLEDEffectType | int = SingleLEDEffectType.Fast_Blink,
color: int = 200,
level: int = 100,
duration: int = 3,
**kwargs: Any,
) -> None:
"""Issue individual LED effect command.
Expand Down Expand Up @@ -377,87 +379,91 @@ class IkeaRemote(ClusterHandler):
REPORT_CONFIG = ()


def compare_quirk_class(endpoint: Endpoint, name: str):
def compare_quirk_class(endpoint: Endpoint, names: str | Collection[str]):
"""Return True if the last two words separated by dots equal the words between the dots in name.
This function should probably be moved to the base class
"""
return endpoint.device.quirk_class.rsplit(".", 2)[1:] == name.split(".")
if isinstance(names, str):
names = {names}

return tuple(endpoint.device.quirk_class.rsplit(".", 2)[1:]) in {tuple(name.split(".")) for name in names}


@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(
zigpy.zcl.clusters.hvac.Thermostat.cluster_id
)
@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(
zigpy.zcl.clusters.hvac.Thermostat.cluster_id
)
class DanfossTRVChannel(ClusterHandler):
class DanfossTRVChannel(ThermostatClusterHandler):
"""TRV Channel class for the Danfoss TRV and derivatives."""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.REPORT_CONFIG = (*self.REPORT_CONFIG,
AttrReportConfig(attr="open_window_detection", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="heat_required", config=REPORT_CONFIG_ASAP),
AttrReportConfig(attr="mounting_mode_active", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="load_estimate", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="adaptation_run_status", config=REPORT_CONFIG_DEFAULT),
)

self.ZCL_INIT_ATTRS = {**self.ZCL_INIT_ATTRS,
"external_open_window_detected": True,
"window_open_feature": True,
"exercise_day_of_week": True,
"exercise_trigger_time": True,
"mounting_mode_control": True,
"orientation": True,
"external_measured_room_sensor": True,
"radiator_covered": True,
"heat_available": True,
"load_balancing_enable": True,
"load_room_mean": True,
"control_algorithm_scale_factor": True,
"regulation_setpoint_offset": True,
"adaptation_run_control": True,
"adaptation_run_settings": True,
}

@classmethod
def matches(cls, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> bool:
"""Filter the cluster match for specific devices."""
return compare_quirk_class(endpoint, "thermostat.DanfossThermostat")

REPORT_CONFIG = (
AttrReportConfig(attr="open_window_detection", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="heat_required", config=REPORT_CONFIG_ASAP),
AttrReportConfig(attr="mounting_mode_active", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="load_estimate", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="adaptation_run_status", config=REPORT_CONFIG_DEFAULT),
)

ZCL_INIT_ATTRS = {
"external_open_window_detected": True,
"window_open_feature": True,
"exercise_day_of_week": True,
"exercise_trigger_time": True,
"mounting_mode_control": True,
"orientation": True,
"external_measured_room_sensor": True,
"radiator_covered": True,
"heat_available": True,
"load_balancing_enable": True,
"load_room_mean": True,
"control_algorithm_scale_factor": True,
"regulation_setpoint_offset": True,
"adaptation_run_control": True,
"adaptation_run_settings": True,
}


@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(
zigpy.zcl.clusters.hvac.UserInterface.cluster_id
)
@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(
zigpy.zcl.clusters.hvac.UserInterface.cluster_id
)
class DanfossTRVInterfaceChannel(ClusterHandler):
class DanfossTRVInterfaceChannel(UserInterface):
"""Interface Channel class for the Danfoss TRV and derivatives."""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.ZCL_INIT_ATTRS = {**self.ZCL_INIT_ATTRS,
"viewing_direction": True}

@classmethod
def matches(cls, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> bool:
"""Filter the cluster match for specific devices."""
return compare_quirk_class(endpoint, "thermostat.DanfossThermostat")

ZCL_INIT_ATTRS = {"viewing_direction": True}


@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(
zigpy.zcl.clusters.homeautomation.Diagnostic.cluster_id
)
@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(
zigpy.zcl.clusters.homeautomation.Diagnostic.cluster_id
)
class DanfossTRVDiagnosticChannel(ClusterHandler):
class DanfossTRVDiagnosticChannel(Diagnostic):
"""Diagnostic Channel class for the Danfoss TRV and derivatives."""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.REPORT_CONFIG = (*self.ZCL_INIT_ATTRS,
AttrReportConfig(attr="sw_error_code", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="motor_step_counter", config=REPORT_CONFIG_DEFAULT),
)

@classmethod
def matches(cls, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> bool:
"""Filter the cluster match for specific devices."""
return compare_quirk_class(endpoint, "thermostat.DanfossThermostat")

REPORT_CONFIG = (
AttrReportConfig(attr="sw_error_code", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="motor_step_counter", config=REPORT_CONFIG_DEFAULT),
)
10 changes: 5 additions & 5 deletions homeassistant/components/zha/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ class MinHeatSetpointLimit(


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
Expand All @@ -1043,7 +1043,7 @@ class DanfossExerciseTriggerTime(


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
Expand All @@ -1060,7 +1060,7 @@ class DanfossExternalMeasuredRoomSensor(


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
Expand All @@ -1076,7 +1076,7 @@ class DanfossLoadRoomMean(ZHANumberConfigurationEntity, id_suffix="load_room_mea


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
Expand All @@ -1094,7 +1094,7 @@ class DanfossControlAlgorithmScaleFactor(


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/zha/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
CLUSTER_HANDLER_ON_OFF,
SIGNAL_ADD_ENTITIES,
SIGNAL_ATTR_UPDATED,
Strobe,
Strobe, CLUSTER_HANDLER_THERMOSTAT,
)
from .core.helpers import get_zha_data
from .core.registries import ZHA_ENTITIES
Expand Down Expand Up @@ -608,7 +608,7 @@ class DanfossExerciseDayOfTheWeekEnum(types.enum8):


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossExerciseDayOfTheWeek(
Expand All @@ -630,7 +630,7 @@ class DanfossOrientationEnum(types.enum8):


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossOrientation(ZCLEnumSelectEntity, id_suffix="orientation"):
Expand All @@ -653,7 +653,7 @@ class DanfossAdaptationRunControlEnum(types.enum8):


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="thermostat",
cluster_handler_names=CLUSTER_HANDLER_THERMOSTAT,
quirk_classes={"thermostat.DanfossThermostat"},
)
class DanfossAdaptationRunControl(
Expand Down
Loading

0 comments on commit a33d14b

Please sign in to comment.