Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions tests/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
)
from zigpy.exceptions import ZigbeeException
from zigpy.profiles import zha
from zigpy.quirks import DEVICE_REGISTRY, CustomCluster, CustomDevice
from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry
from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder
import zigpy.types as t
from zigpy.typing import UNDEFINED
Expand Down Expand Up @@ -495,8 +495,9 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) ->
model="model",
)

registry = DeviceRegistry()
(
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model)
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry)
.adds(WindowDetectionFunctionQuirk.TuyaManufCluster)
.switch(
"window_detection_function",
Expand All @@ -509,7 +510,7 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) ->
.add_to_registry()
)

zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev)
zigpy_device_ = registry.get_device(zigpy_dev)

assert isinstance(zigpy_device_, CustomDeviceV2)
cluster = zigpy_device_.endpoints[1].tuya_manufacturer
Expand Down Expand Up @@ -575,8 +576,9 @@ async def test_switch_configurable_custom_on_off_values_force_inverted(
model="model2",
)

registry = DeviceRegistry()
(
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model)
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry)
.adds(WindowDetectionFunctionQuirk.TuyaManufCluster)
.switch(
"window_detection_function",
Expand All @@ -590,7 +592,7 @@ async def test_switch_configurable_custom_on_off_values_force_inverted(
.add_to_registry()
)

zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev)
zigpy_device_ = registry.get_device(zigpy_dev)

assert isinstance(zigpy_device_, CustomDeviceV2)
cluster = zigpy_device_.endpoints[1].tuya_manufacturer
Expand Down Expand Up @@ -656,8 +658,9 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute(
model="model3",
)

registry = DeviceRegistry()
(
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model)
QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry)
.adds(WindowDetectionFunctionQuirk.TuyaManufCluster)
.switch(
"window_detection_function",
Expand All @@ -671,7 +674,7 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute(
.add_to_registry()
)

zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev)
zigpy_device_ = registry.get_device(zigpy_dev)

assert isinstance(zigpy_device_, CustomDeviceV2)
cluster = zigpy_device_.endpoints[1].tuya_manufacturer
Expand Down
101 changes: 77 additions & 24 deletions zha/application/platforms/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,24 +208,6 @@ class Accelerometer(BinarySensor):
min_interval=1, max_interval=900, reportable_change=1
),
),
"x_axis": AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
min_interval=1, max_interval=900, reportable_change=1
),
),
"y_axis": AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
min_interval=1, max_interval=900, reportable_change=1
),
),
"z_axis": AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
min_interval=1, max_interval=900, reportable_change=1
),
),
},
),
}
Expand Down Expand Up @@ -254,12 +236,6 @@ class Occupancy(BinarySensor):
min_interval=0, max_interval=900, reportable_change=1
),
),
OccupancySensing.AttributeDefs.pir_o_to_u_delay: AttrConfig(
read_on_startup=False,
),
OccupancySensing.AttributeDefs.pir_u_to_o_delay: AttrConfig(
read_on_startup=False,
),
},
),
}
Expand Down Expand Up @@ -523,6 +499,20 @@ class ReplaceFilter(BinarySensor):
server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}),
)

_server_cluster_config = {
IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig(
bind=True,
attributes={
"replace_filter": AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
min_interval=0, max_interval=900, reportable_change=1
),
),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraPetFeederErrorDetected(BinarySensor):
Expand All @@ -538,6 +528,15 @@ class AqaraPetFeederErrorDetected(BinarySensor):
models=frozenset({"aqara.feeder.acn001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"error_detected": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class XiaomiPlugConsumerConnected(BinarySensor):
Expand All @@ -554,6 +553,15 @@ class XiaomiPlugConsumerConnected(BinarySensor):
models=frozenset({"lumi.plug.mmeu01", "lumi.plug.maeu01"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"consumer_connected": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraThermostatWindowOpen(BinarySensor):
Expand All @@ -569,6 +577,15 @@ class AqaraThermostatWindowOpen(BinarySensor):
models=frozenset({"lumi.airrtc.agl001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"window_open": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraThermostatValveAlarm(BinarySensor):
Expand All @@ -585,6 +602,15 @@ class AqaraThermostatValveAlarm(BinarySensor):
models=frozenset({"lumi.airrtc.agl001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"valve_alarm": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraThermostatCalibrated(BinarySensor):
Expand All @@ -601,6 +627,15 @@ class AqaraThermostatCalibrated(BinarySensor):
models=frozenset({"lumi.airrtc.agl001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"calibrated": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraThermostatExternalSensor(BinarySensor):
Expand All @@ -617,6 +652,15 @@ class AqaraThermostatExternalSensor(BinarySensor):
models=frozenset({"lumi.airrtc.agl001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"sensor": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(AQARA_OPPLE_CLUSTER)
class AqaraLinkageAlarmState(BinarySensor):
Expand Down Expand Up @@ -649,6 +693,15 @@ class AqaraE1CurtainMotorOpenedByHandBinarySensor(BinarySensor):
models=frozenset({"lumi.curtain.agl001"}),
)

_server_cluster_config = {
AQARA_OPPLE_CLUSTER: ClusterConfig(
bind=True,
attributes={
"hand_open": AttrConfig(read_on_startup=False),
},
),
}


@register_entity(Thermostat.cluster_id)
class DanfossMountingModeActive(BinarySensor):
Expand Down
60 changes: 34 additions & 26 deletions zha/application/platforms/climate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@
register_entity,
)
from zha.application.platforms.climate.const import (
ATTR_OCCP_COOL_SETPT,
ATTR_OCCP_HEAT_SETPT,
ATTR_OCCUPANCY,
ATTR_PI_COOLING_DEMAND,
ATTR_PI_HEATING_DEMAND,
ATTR_SYS_MODE,
ATTR_UNOCCP_COOL_SETPT,
ATTR_UNOCCP_HEAT_SETPT,
FAN_AUTO,
FAN_ON,
HVAC_MODE_2_SYSTEM,
Expand Down Expand Up @@ -208,14 +200,14 @@ class Thermostat(BaseThermostat):
_attr_translation_key: str = "thermostat"
_enable_turn_on_off_backwards_compatibility = False
_attr_extra_state_attribute_names: set[str] = {
ATTR_SYS_MODE,
ATTR_OCCUPANCY,
ATTR_OCCP_COOL_SETPT,
ATTR_OCCP_HEAT_SETPT,
ATTR_PI_HEATING_DEMAND,
ATTR_PI_COOLING_DEMAND,
ATTR_UNOCCP_COOL_SETPT,
ATTR_UNOCCP_HEAT_SETPT,
ThermostatCluster.AttributeDefs.system_mode.name,
ThermostatCluster.AttributeDefs.occupancy.name,
ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name,
ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name,
ThermostatCluster.AttributeDefs.pi_heating_demand.name,
ThermostatCluster.AttributeDefs.pi_cooling_demand.name,
ThermostatCluster.AttributeDefs.unoccupied_cooling_setpoint.name,
ThermostatCluster.AttributeDefs.unoccupied_heating_setpoint.name,
}

_cluster_match = ClusterMatch(
Expand Down Expand Up @@ -564,18 +556,30 @@ def state(self) -> dict[str, Any]:

response = super().state

response[ATTR_SYS_MODE] = (
response[ThermostatCluster.AttributeDefs.system_mode.name] = (
f"[{self._system_mode}]/{system_mode}"
if self.hvac_mode is not None
else None
)
response[ATTR_OCCUPANCY] = self._occupancy
response[ATTR_OCCP_COOL_SETPT] = self._occupied_cooling_setpoint
response[ATTR_OCCP_HEAT_SETPT] = self._occupied_heating_setpoint
response[ATTR_PI_HEATING_DEMAND] = self._pi_heating_demand
response[ATTR_PI_COOLING_DEMAND] = self._pi_cooling_demand
response[ATTR_UNOCCP_COOL_SETPT] = self._unoccupied_cooling_setpoint
response[ATTR_UNOCCP_HEAT_SETPT] = self._unoccupied_heating_setpoint
response[ThermostatCluster.AttributeDefs.occupancy.name] = self._occupancy
response[ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name] = (
self._occupied_cooling_setpoint
)
response[ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name] = (
self._occupied_heating_setpoint
)
response[ThermostatCluster.AttributeDefs.pi_heating_demand.name] = (
self._pi_heating_demand
)
response[ThermostatCluster.AttributeDefs.pi_cooling_demand.name] = (
self._pi_cooling_demand
)
response[ThermostatCluster.AttributeDefs.unoccupied_cooling_setpoint.name] = (
self._unoccupied_cooling_setpoint
)
response[ThermostatCluster.AttributeDefs.unoccupied_heating_setpoint.name] = (
self._unoccupied_heating_setpoint
)
return response

@property
Expand Down Expand Up @@ -779,7 +783,11 @@ async def _handle_attribute_updated(
) -> None:
"""Handle attribute update from device."""
if (
event.attribute_name in (ATTR_OCCP_COOL_SETPT, ATTR_OCCP_HEAT_SETPT)
event.attribute_name
in (
ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name,
ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name,
)
and self.preset_mode == Preset.AWAY
and await self._async_get_occupancy() is True
):
Expand Down Expand Up @@ -1104,7 +1112,7 @@ def state(self) -> dict[str, Any]:

response = super().state

response[ATTR_SYS_MODE] = (
response[ThermostatCluster.AttributeDefs.system_mode.name] = (
f"[{self._system_mode}]/{system_mode}"
if self.hvac_mode is not None
else None
Expand Down
17 changes: 0 additions & 17 deletions zha/application/platforms/climate/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,6 @@

from zigpy.zcl.clusters.hvac import ControlSequenceOfOperation, RunningMode, SystemMode

ATTR_SYS_MODE: Final[str] = "system_mode"
ATTR_FAN_MODE: Final[str] = "fan_mode"
ATTR_RUNNING_MODE: Final[str] = "running_mode"
ATTR_SETPT_CHANGE_SRC: Final[str] = "setpoint_change_source"
ATTR_SETPT_CHANGE_AMT: Final[str] = "setpoint_change_amount"
ATTR_OCCUPANCY: Final[str] = "occupancy"
ATTR_PI_COOLING_DEMAND: Final[str] = "pi_cooling_demand"
ATTR_PI_HEATING_DEMAND: Final[str] = "pi_heating_demand"
ATTR_OCCP_COOL_SETPT: Final[str] = "occupied_cooling_setpoint"
ATTR_OCCP_HEAT_SETPT: Final[str] = "occupied_heating_setpoint"
ATTR_UNOCCP_HEAT_SETPT: Final[str] = "unoccupied_heating_setpoint"
ATTR_UNOCCP_COOL_SETPT: Final[str] = "unoccupied_cooling_setpoint"
ATTR_HVAC_MODE: Final[str] = "hvac_mode"
ATTR_TARGET_TEMP_HIGH: Final[str] = "target_temp_high"
ATTR_TARGET_TEMP_LOW: Final[str] = "target_temp_low"
ATTR_TEMPERATURE: Final[str] = "temperature"

PRECISION_TENTHS: Final[float] = 0.1

# Possible fan state
Expand Down
6 changes: 0 additions & 6 deletions zha/application/platforms/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ class DeviceScannerEntity(BaseDeviceTracker):
PowerConfiguration.cluster_id: ClusterConfig(
bind=True,
attributes={
PowerConfiguration.AttributeDefs.battery_voltage: AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
min_interval=3600, max_interval=10800, reportable_change=1
),
),
PowerConfiguration.AttributeDefs.battery_percentage_remaining: AttrConfig(
read_on_startup=False,
reporting=ReportingConfig(
Expand Down
Loading
Loading