Skip to content

Commit

Permalink
NodOn: quirk for SIN-4-FP-21
Browse files Browse the repository at this point in the history
  • Loading branch information
ikruglov committed Sep 15, 2024
1 parent acecf70 commit 62418ea
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 0 deletions.
45 changes: 45 additions & 0 deletions tests/test_nodon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Tests for NodOn."""

from unittest import mock
from zigpy.zcl import foundation

import zhaquirks
from zhaquirks.nodon.pilot_wire import NodOnSIN4FP21

zhaquirks.setup()


def test_nodon_pilot_wire_signature_match(assert_signature_matches_quirk):
"""Test signature for NodOn SIN-4-FP-21 (Pilot Wire)."""

signature = {
"node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, manufacturer_code=4747, maximum_buffer_size=82, maximum_incoming_transfer_size=500, server_mask=11264, maximum_outgoing_transfer_size=500, descriptor_capability_field=<DescriptorCapability.NONE: 0>)",
"endpoints": {
"1": {
"profile_id": 260,
"device_type": "0x0051",
"in_clusters": [
"0x0000",
"0x0003",
"0x0004",
"0x0005",
"0x0006",
"0x0702",
"0x1000",
"0xfc00",
],
"out_clusters": ["0x0019"],
},
"242": {
"profile_id": 41440,
"device_type": "0x0066",
"in_clusters": ["0x0021"],
"out_clusters": ["0x0021"],
},
},
"manufacturer": "NodOn",
"model": "SIN-4-FP-21",
"class": "zhaquirks.nodon.pilot_wire.NodOnSIN4FP21",
}

assert_signature_matches_quirk(zhaquirks.nodon.pilot_wire.NodOnSIN4FP21, signature)
135 changes: 135 additions & 0 deletions zhaquirks/nodon/pilot_wire.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""NodOn pilot wire heating module."""

from zigpy.profiles import zgp, zha
from zigpy.quirks import CustomDevice, CustomCluster
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
OnOff,
Ota,
Scenes,
)

from zigpy.zcl.clusters.lightlink import LightLink
from zigpy.zcl.clusters.smartenergy import Metering

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

import zigpy.types as t


class NodOnPilotWireManufacturerSpecificCluster(CustomCluster):
"""NodOn manufacturer specific cluster to set Pilot Wire mode."""

# Codes taken from
# https://github.com/Koenkk/zigbee-herdsman-converters/blob/0f4833340a20db3dae625a61c41d9be0a6f952be/src/converters/fromZigbee.ts#L5285.
class PilotWireMode(t.enum8):
"""Pilot Wide Modes available in the device."""
Off = 0x00
Comfort = 0x01
Eco = 0x02
FrostProtection = 0x03
ComfortMinus1 = 0x04
ComfortMinus2 = 0x05

name: str = "NodOnPilotWireManufacturerSpecificCluster"
cluster_id: t.uint16_t = 0xFC00
ep_attribute: str = "nodon_pilot_wire_manufacturer_specific"

attributes = {
0x0000: ("mode", PilotWireMode, True),
}

server_commands = {
0x0000: foundation.ZCLCommandDef(
"setMode",
{"mode": PilotWireMode},
False,
is_manufacturer_specific=True,
),
}


class NodOnSIN4FP21(CustomDevice):
"""NodOn pilot wire heating module."""

signature = {
MODELS_INFO: [("NodOn", "SIN-4-FP-21")],
ENDPOINTS: {
# SimpleDescriptor(endpoint=1, profile=260, device_type=81, device_version=1,
# input_clusters=[0, 3, 4, 5, 6, 1794, 4096, 64512],
# output_clusters=[25]
1: {
PROFILE_ID: zha.PROFILE_ID,
# PilotWire is not really a smart plug, but this is what NodOn reports
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
Metering.cluster_id,
LightLink.cluster_id,
NodOnPilotWireManufacturerSpecificCluster.cluster_id,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=102
# input_clusters=[33]
# output_clusters=[33]
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.COMBO_BASIC,
INPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}

replacement = {
ENDPOINTS: {
# SimpleDescriptor(endpoint=1, profile=260, device_type=81, device_version=1,
# input_clusters=[0, 3, 4, 5, 6, 1794, 4096, 64512],
# output_clusters=[25]
1: {
PROFILE_ID: zha.PROFILE_ID,
# PilotWire is not really a smart plug, but this is what NodOn reports
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
Metering.cluster_id,
LightLink.cluster_id,
NodOnPilotWireManufacturerSpecificCluster,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=102
# input_clusters=[33]
# output_clusters=[33]
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.COMBO_BASIC,
INPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}

0 comments on commit 62418ea

Please sign in to comment.