Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for QUAD-ZIG-SW from smarthjemmet.dk #3400

Merged
merged 25 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b05cfbe
Add support for QUAD-ZIG-SW from smarthjemmet.dk
mortenmoulder Oct 6, 2024
312e1d6
Apply pre-commit auto fixes
pre-commit-ci[bot] Oct 6, 2024
9bd1af7
Fix docstrings
mortenmoulder Oct 6, 2024
d4eb649
Merge branch 'dev' of https://github.com/mortenmoulder/zha-device-han…
mortenmoulder Oct 6, 2024
04baa50
Apply pre-commit auto fixes
pre-commit-ci[bot] Oct 6, 2024
745b05d
Update file name and add tests
mortenmoulder Oct 6, 2024
1170d36
Apply pre-commit auto fixes
pre-commit-ci[bot] Oct 6, 2024
df836e2
Fix docstring
mortenmoulder Oct 6, 2024
367ea10
Merge branch 'dev' of https://github.com/mortenmoulder/zha-device-han…
mortenmoulder Oct 6, 2024
65d32f6
Remove empty `__init__`
TheJulianJES Oct 17, 2024
e4fd04f
Remove unused `_current_state`
TheJulianJES Oct 17, 2024
c303d4e
Use walrus operator for `action`
TheJulianJES Oct 17, 2024
8619f67
Refactor quirk `CustomDevice` classes
TheJulianJES Oct 17, 2024
d14e974
Replace with v2 quirk
TheJulianJES Oct 18, 2024
a246d9c
Remove test
TheJulianJES Oct 18, 2024
1568a8a
Remove copied code from `LocalDataCluster`
TheJulianJES Oct 21, 2024
c34f6f2
Adjust device trigger range to range(1, 5)
TheJulianJES Oct 21, 2024
5143bc1
Remove output clusters, re-add as input clusters
TheJulianJES Oct 21, 2024
544880b
Use `AttributeDefs` in `_update_attribute`
TheJulianJES Oct 21, 2024
2667f9e
Merge branch 'dev' into mortenmoulder-dev
TheJulianJES Feb 1, 2025
c4d03d0
Fix walrus (thanks @bangert)
TheJulianJES Feb 1, 2025
00eb2bf
Remove updating attribute `0` on press
TheJulianJES Feb 1, 2025
1443e59
Also apply to `MULTI-ZIG-SW` model
TheJulianJES Feb 1, 2025
b12b59c
Add tests
TheJulianJES Feb 1, 2025
453ea0f
Combine if statements
TheJulianJES Feb 1, 2025
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
57 changes: 57 additions & 0 deletions tests/test_smarthjemmet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Tests for SmartHjemmet quirks."""

from unittest import mock

import pytest
from zigpy.zcl.clusters.general import MultistateInput

import zhaquirks

zhaquirks.setup()


@pytest.mark.parametrize("endpoint", (2, 3, 4, 5))
def test_quadzigsw(zigpy_device_from_v2_quirk, endpoint):
"""Test the SmartHjemmet QUAD-ZIG-SW."""
device = zigpy_device_from_v2_quirk(
"smarthjemmet.dk", "QUAD-ZIG-SW", endpoint_ids=[1, 2, 3, 4, 5]
)

cluster = device.endpoints[endpoint].multistate_input
listener = mock.MagicMock()
cluster.add_listener(listener)

multistate_value = MultistateInput.AttributeDefs.present_value.id
multistate_text = MultistateInput.AttributeDefs.state_text.id

# test that attribute writes are passed through with no events
cluster.update_attribute(MultistateInput.AttributeDefs.state_text.id, "test")
assert listener.zha_send_event.call_count == 0
assert listener.attribute_updated.call_count == 1
assert listener.attribute_updated.call_args[0][0] == multistate_text
assert listener.attribute_updated.call_args[0][1] == "test"

# test that the cluster does not send events for unknown values
cluster.update_attribute(multistate_value, 5)
assert listener.zha_send_event.call_count == 0

# test that the cluster sends the correct events
cluster.update_attribute(multistate_value, 0)
assert listener.zha_send_event.call_count == 1
listener.zha_send_event.assert_called_with("release", {"value": 0})

cluster.update_attribute(multistate_value, 1)
assert listener.zha_send_event.call_count == 2
listener.zha_send_event.assert_called_with("single", {"value": 1})

cluster.update_attribute(multistate_value, 2)
assert listener.zha_send_event.call_count == 3
listener.zha_send_event.assert_called_with("double", {"value": 2})

cluster.update_attribute(multistate_value, 3)
assert listener.zha_send_event.call_count == 4
listener.zha_send_event.assert_called_with("triple", {"value": 3})

cluster.update_attribute(multistate_value, 4)
assert listener.zha_send_event.call_count == 5
listener.zha_send_event.assert_called_with("hold", {"value": 4})
1 change: 1 addition & 0 deletions zhaquirks/smarthjemmet/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""smarthjemmet.dk devices."""
87 changes: 87 additions & 0 deletions zhaquirks/smarthjemmet/quadzigsw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Device handler for smarthjemmet.dk QUAD-ZIG-SW."""

from zigpy.quirks import CustomCluster
from zigpy.quirks.v2 import QuirkBuilder
from zigpy.zcl import ClusterType
from zigpy.zcl.clusters.general import MultistateInput

from zhaquirks import PowerConfigurationCluster
from zhaquirks.const import (
COMMAND,
COMMAND_DOUBLE,
COMMAND_HOLD,
COMMAND_RELEASE,
COMMAND_SINGLE,
COMMAND_TRIPLE,
DOUBLE_PRESS,
ENDPOINT_ID,
LONG_PRESS,
LONG_RELEASE,
SHORT_PRESS,
TRIPLE_PRESS,
VALUE,
ZHA_SEND_EVENT,
)

SMARTHJEMMET = "smarthjemmet.dk"

ACTION_TYPE = {
0: COMMAND_RELEASE,
1: COMMAND_SINGLE,
2: COMMAND_DOUBLE,
3: COMMAND_TRIPLE,
4: COMMAND_HOLD,
}


class CR2032PowerConfigurationCluster(PowerConfigurationCluster):
"""CR2032 Power Configuration Cluster."""

MIN_VOLTS = 2.2
MAX_VOLTS = 3.0


class CustomMultistateInputCluster(CustomCluster, MultistateInput):
"""Multistate input cluster."""

def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
if (
attrid == MultistateInput.AttributeDefs.present_value.id
and (action := ACTION_TYPE.get(value)) is not None
):
event_args = {VALUE: value}
self.listener_event(ZHA_SEND_EVENT, action, event_args)


(
QuirkBuilder(SMARTHJEMMET, "QUAD-ZIG-SW")
.applies_to(SMARTHJEMMET, "MULTI-ZIG-SW")
.skip_configuration()
.replaces(CR2032PowerConfigurationCluster)
.removes(MultistateInput.cluster_id, cluster_type=ClusterType.Client, endpoint_id=2)
.removes(MultistateInput.cluster_id, cluster_type=ClusterType.Client, endpoint_id=3)
.removes(MultistateInput.cluster_id, cluster_type=ClusterType.Client, endpoint_id=4)
.removes(MultistateInput.cluster_id, cluster_type=ClusterType.Client, endpoint_id=5)
.adds(CustomMultistateInputCluster, endpoint_id=2)
.adds(CustomMultistateInputCluster, endpoint_id=3)
.adds(CustomMultistateInputCluster, endpoint_id=4)
.adds(CustomMultistateInputCluster, endpoint_id=5)
.device_automation_triggers(
{
(press_type, f"button_{i}"): {
COMMAND: command,
ENDPOINT_ID: i + 1,
}
for i in range(1, 5)
for press_type, command in {
(SHORT_PRESS, COMMAND_SINGLE),
(DOUBLE_PRESS, COMMAND_DOUBLE),
(TRIPLE_PRESS, COMMAND_TRIPLE),
(LONG_PRESS, COMMAND_HOLD),
(LONG_RELEASE, COMMAND_RELEASE),
}
}
)
.add_to_registry()
)
Loading