Skip to content

Commit

Permalink
Add ThirdReality multifunction night light (#2540)
Browse files Browse the repository at this point in the history
* Add ThirdReality multifunction night light

* Improve comment for `ThirdRealitySpecificCluster`

* Use `AttributeDefs` to get `zone_type` ID

* Only forward when `attrid=0x0002`

* Rename constant

* Add basic test checking if motion attr is forwarded

---------

Co-authored-by: TheJulianJES <[email protected]>
  • Loading branch information
llhappier and TheJulianJES authored Aug 29, 2023
1 parent cd908ce commit efa4828
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
40 changes: 40 additions & 0 deletions tests/test_thirdreality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Tests for Third Reality quirks."""


import pytest
from zigpy.zcl.clusters.security import IasZone

import zhaquirks
import zhaquirks.thirdreality.night_light

from tests.common import ClusterListener

zhaquirks.setup()


@pytest.mark.parametrize("quirk", (zhaquirks.thirdreality.night_light.Nightlight,))
async def test_third_reality_nightlight(zigpy_device_from_quirk, quirk):
"""Test Third Reality night light forwarding motion attribute to IasZone cluster."""

device = zigpy_device_from_quirk(quirk)

ias_zone_cluster = device.endpoints[1].ias_zone
ias_zone_listener = ClusterListener(ias_zone_cluster)

ias_zone_status_id = IasZone.AttributeDefs.zone_status.id

third_reality_cluster = device.endpoints[1].in_clusters[0xFC00]

# 0x0002 is also used on manufacturer specific cluster for motion events
third_reality_cluster.update_attribute(0x0002, IasZone.ZoneStatus.Alarm_1)

assert len(ias_zone_listener.attribute_updates) == 1
assert ias_zone_listener.attribute_updates[0][0] == ias_zone_status_id
assert ias_zone_listener.attribute_updates[0][1] == IasZone.ZoneStatus.Alarm_1

# turn off motion alarm
third_reality_cluster.update_attribute(0x0002, 0)

assert len(ias_zone_listener.attribute_updates) == 2
assert ias_zone_listener.attribute_updates[1][0] == ias_zone_status_id
assert ias_zone_listener.attribute_updates[1][1] == 0
106 changes: 106 additions & 0 deletions zhaquirks/thirdreality/night_light.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Third Reality night light zigbee devices."""
from zigpy.profiles import zha
from zigpy.quirks import CustomCluster, CustomDevice
from zigpy.zcl.clusters.general import (
Basic,
Groups,
Identify,
LevelControl,
MultistateInput,
OnOff,
Ota,
Scenes,
)
from zigpy.zcl.clusters.lighting import Color
from zigpy.zcl.clusters.lightlink import LightLink
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement
from zigpy.zcl.clusters.security import IasZone

from zhaquirks import LocalDataCluster
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.thirdreality import THIRD_REALITY

THIRD_REALITY_CLUSTER_ID = 0xFC00
THIRD_REALITY_MOTION_EVENT_ATTR_ID = 0x0002


class ThirdRealitySpecificCluster(CustomCluster):
"""Manufacturer specific cluster to relay motion event to IAS Zone cluster."""

cluster_id = THIRD_REALITY_CLUSTER_ID

def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
if attrid == THIRD_REALITY_MOTION_EVENT_ATTR_ID:
self.endpoint.ias_zone.update_attribute(
IasZone.AttributeDefs.zone_status.id, value
)


class LocalIasZone(LocalDataCluster, IasZone):
"""Local IAS Zone cluster."""

_CONSTANT_ATTRIBUTES = {
IasZone.AttributeDefs.zone_type.id: IasZone.ZoneType.Motion_Sensor
}


class Nightlight(CustomDevice):
"""Custom device for 3RSNL02043Z."""

signature = {
MODELS_INFO: [(THIRD_REALITY, "3RSNL02043Z")],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
LevelControl.cluster_id,
MultistateInput.cluster_id,
Color.cluster_id,
IlluminanceMeasurement.cluster_id,
LightLink.cluster_id,
ThirdRealitySpecificCluster.cluster_id,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
],
}
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.COLOR_DIMMABLE_LIGHT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
LevelControl.cluster_id,
MultistateInput.cluster_id,
Color.cluster_id,
IlluminanceMeasurement.cluster_id,
LightLink.cluster_id,
LocalIasZone,
ThirdRealitySpecificCluster,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
],
}
}
}

0 comments on commit efa4828

Please sign in to comment.