Skip to content

Commit f9d8a82

Browse files
authored
Merge pull request #19 from claudegel/sedna-flow-meeter
Sedna flow meeter
2 parents 7a19312 + 46b127b commit f9d8a82

File tree

2 files changed

+249
-4
lines changed

2 files changed

+249
-4
lines changed

README.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,62 @@ Following are the cluster/attributes set for reproting in Neviweb:
402402
|ActivePower|0x0B04|0x050B|0x29|30|600|0x64|
403403
|Energy reading|0x0702|0x0000|0x29|299|1799|int|
404404
|Safety water temp reporting|0xFF01|0x0076|DataType.UINT8|0|86400|null|
405-
405+
406+
# Setting the flow meter model for your VA422xZB valve 2n gen.
407+
To add your flow meter to your valve, you need to use the service ZHA Toolkit: Write Attribute. The data to set the flow meter is written in an attr_type array. The command is different for each type of flow meter:
408+
- FS4220: (3/4 inch)
409+
```
410+
service: zha_toolkit.attr_write
411+
data:
412+
ieee: 50:0b:91:40:00:03:ed:b0 <-- your valve ieee
413+
endpoint: 1
414+
cluster: 0xff01
415+
attribute: 0x0240
416+
attr_type: 0x48
417+
attr_val: [32, 12, 0, 194, 17, 0, 0, 136, 119, 0, 0, 1, 0, 0, 0]
418+
read_before_write: true
419+
read_after_write: true
420+
```
421+
- FS4221: (one inch)
422+
```
423+
service: zha_toolkit.attr_write
424+
data:
425+
ieee: 50:0b:91:40:00:03:ed:b0 <-- your valve ieee
426+
endpoint: 1
427+
cluster: 0xff01
428+
attribute: 0x0240
429+
attr_type: 0x48
430+
attr_val: [32, 12, 0, 159, 38, 0, 0, 76, 85, 1, 0, 1, 0, 0, 0]
431+
read_before_write: true
432+
read_after_write: true
433+
```
434+
- FS4222: (1.5 inch)
435+
```
436+
service: zha_toolkit.attr_write
437+
data:
438+
ieee: 50:0b:91:40:00:03:ed:b0 <-- your valve ieee
439+
endpoint: 1
440+
cluster: 0xff01
441+
attribute: 0x0240
442+
attr_type: 0x48
443+
attr_val: [32, 12, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
444+
read_before_write: true
445+
read_after_write: true
446+
```
447+
- No flow meter:
448+
```
449+
service: zha_toolkit.attr_write
450+
data:
451+
ieee: 50:0b:91:40:00:03:ed:b0 <-- your valve ieee
452+
endpoint: 1
453+
cluster: 0xff01
454+
attribute: 0x0240
455+
attr_type: 0x48
456+
attr_val: [32, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
457+
read_before_write: true
458+
read_after_write: true
459+
```
460+
406461
## Light switch and dimmer double tap, long press reporting :
407462
Sinopé light switches (SW2500ZB), dimmer (DM2500ZB and DM2550ZB) supports single, double and long click, but requires to enable device reporting on attribute 0x0054, cluster 0xff01 to get the action fired in ZHA. To proceed use zha_toolkit services and follow the example bellow :
408463

switch.py

Lines changed: 193 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Module to handle quirks of the Sinopé Technologies switches.
22
33
Supported devices, SP2600ZB, SP2610ZB, RM3250ZB, RM3500ZB,
4-
VA4200WZ, VA4201WZ, VA4200ZB, VA4201ZB, VA4220ZB, VA4221ZB and MC3100ZB.
4+
VA4200WZ, VA4201WZ, VA4200ZB, VA4201ZB, VA4220ZB, VA4221ZB and MC3100ZB,
5+
2nd gen VA4220ZB, VA4221ZB with flow meeter FS4220, FS4221.
56
"""
67

78
import zigpy.profiles.zha as zha_p
@@ -22,10 +23,12 @@
2223
)
2324
from zigpy.zcl.clusters.homeautomation import Diagnostic, ElectricalMeasurement
2425
from zigpy.zcl.clusters.lightlink import LightLink
25-
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement
26+
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement, FlowMeasurement
2627
from zigpy.zcl.clusters.security import IasZone
2728
from zigpy.zcl.clusters.smartenergy import Metering
2829

30+
from zigpy.zcl.foundation import Array
31+
2932
from zhaquirks.const import (
3033
DEVICE_TYPE,
3134
ENDPOINTS,
@@ -38,6 +41,7 @@
3841

3942
SINOPE_MANUFACTURER_CLUSTER_ID = 0xFF01
4043
CURTEMP = 0x0000
44+
FLOWMETER = 0x0000
4145

4246

4347
class SinopeManufacturerCluster(CustomCluster):
@@ -49,6 +53,41 @@ class KeypadLock(t.enum8):
4953
Unlocked = 0x00
5054
Locked = 0x01
5155

56+
class FlowAlarm(t.enum8):
57+
"""Alarm abnormal flow."""
58+
59+
Off = 0x00
60+
On = 0x01
61+
62+
class AlarmAction(t.enum8):
63+
"""Flow alarm action."""
64+
65+
Nothing = 0x00
66+
Notify = 0x01
67+
Close = 0x02
68+
Close_notify = 0x03
69+
70+
class PowerSource(t.uint32_t):
71+
"""Valve power souce types."""
72+
73+
Battery = 0x00000000
74+
ACUPS_01 = 0x00000001
75+
DC_power = 0x0001d4c0
76+
77+
class EmergencyPower(t.uint32_t):
78+
"""Valve emergency power souce types."""
79+
80+
Battery = 0x00000000
81+
ACUPS_01 = 0x00000001
82+
Battery_ACUPS_01 = 0x0000003c
83+
84+
class AbnormalAction(t.bitmap16):
85+
"""Action in case of abnormal flow detected."""
86+
87+
Nothing = 0x0000
88+
Notify = 0x0001
89+
Close_notify = 0x0003
90+
5291
class ColdStatus(t.enum8):
5392
"""cold_load_pickup_status values."""
5493

@@ -63,6 +102,20 @@ class TankSize(t.enum8):
63102
Gal_60 = 0x03
64103
Gal_80 = 0x04
65104

105+
class FlowDuration(t.uint32_t):
106+
"""tank_size values."""
107+
108+
M_15 = 0x0384
109+
M_30 = 0x0708
110+
M_45 = 0x0A8C
111+
M_60 = 0x0E10
112+
M_75 = 0x1194
113+
M_90 = 0x1518
114+
H_3 = 0x2A30
115+
H_6 = 0x5460
116+
H_12 = 0xA8C0
117+
H_24 = 0x15180
118+
66119
cluster_id = SINOPE_MANUFACTURER_CLUSTER_ID
67120
name = "Sinopé Manufacturer specific"
68121
ep_attribute = "sinope_manufacturer_specific"
@@ -71,27 +124,97 @@ class TankSize(t.enum8):
71124
0x0002: ("keypad_lockout", KeypadLock, True),
72125
0x0003: ("firmware_number", t.uint16_t, True),
73126
0x0004: ("firmware_version", t.CharacterString, True),
127+
0x0010: ("outdoor_temp", t.int16s, True),
74128
0x0013: ("tank_size", TankSize, True),
75129
0x0030: ("unknown_attr_2", t.uint8_t, True),
76130
0x0060: ("connected_load", t.uint16_t, True),
77-
0x0070: ("currentLoad", t.bitmap8, True),
131+
0x0070: ("current_load", t.bitmap8, True),
78132
0x0076: ("dr_config_water_temp_min", t.uint8_t, True),
79133
0x0077: ("dr_config_water_temp_time", t.uint8_t, True),
80134
0x0078: ("dr_wt_time_on", t.uint16_t, True),
135+
0x0080: ("unknown_attr_4", t.uint32_t, True),
136+
0x0090: ("current_summation_delivered", t.uint32_t, True),
81137
0x00A0: ("timer", t.uint32_t, True),
82138
0x00A1: ("timer_countdown", t.uint32_t, True),
139+
0x0101: ("unknown_attr_9", Array, True),
83140
0x0200: ("status", t.bitmap32, True),
141+
0x0221: ("unknown_attr_3", t.bitmap16, True),
142+
0x0230: ("alarm_flow_threshold", FlowAlarm, True),
143+
0x0231: ("alarm_options", AlarmAction, True),
144+
0x0240: ("flow_meter_config", Array, True),
145+
0x0241: ("countdown", t.uint32_t, True),
146+
0x0250: ("power_source", PowerSource, True),
147+
0x0251: ("emergency_power_source", EmergencyPower, True),
148+
0x0252: ("abnormal_flow_duration", FlowDuration, True),
149+
0x0253: ("abnormal_flow_action", AbnormalAction, True),
84150
0x0283: ("cold_load_pickup_status", ColdStatus, True),
85151
0xFFFD: ("cluster_revision", t.uint16_t, True),
86152
}
87153

88154

155+
class CustomBasicCluster(CustomCluster, Basic):
156+
"""Custom Basic Cluster."""
157+
158+
class PowerSource(t.enum8):
159+
"""Power source."""
160+
161+
Unknown = 0x0000
162+
Battery = 0x0003
163+
DC_source = 0x0004
164+
ACUPS_01 = 0x0081
165+
ACUPS01 = 0x0082
166+
167+
attributes = Basic.attributes.copy()
168+
attributes.update(
169+
{
170+
0x0007: ("power_source", PowerSource, True),
171+
}
172+
)
173+
174+
89175
class CustomMeteringCluster(CustomCluster, Metering):
90176
"""Custom Metering Cluster."""
91177

178+
class ValveStatus(t.bitmap8):
179+
"""valve_status."""
180+
181+
Off = 0x00
182+
Off_armed = 0x01
183+
On = 0x02
184+
185+
class UnitOfMeasure(t.enum8):
186+
"""unit_of_measure."""
187+
188+
KWh = 0x00
189+
Lh = 0x07
190+
92191
DIVISOR = 0x0302
93192
_CONSTANT_ATTRIBUTES = {DIVISOR: 1000}
94193

194+
attributes = Metering.attributes.copy()
195+
attributes.update(
196+
{
197+
0x0200: ("status", ValveStatus, True),
198+
0x0300: ("unit_of_measure", UnitOfMeasure, True),
199+
}
200+
)
201+
202+
203+
class CustomDeviceTemperatureCluster(CustomCluster, DeviceTemperature):
204+
"""Custom DeviceTemperature Cluster."""
205+
206+
def _update_attribute(self, attrid, value):
207+
if attrid == CURTEMP:
208+
super()._update_attribute(attrid, value*100)
209+
210+
211+
class CustomFlowMeasurementCluster(CustomCluster, FlowMeasurement):
212+
"""Custom FlowMeasurement Cluster."""
213+
214+
def _update_attribute(self, attrid, value):
215+
if attrid == FLOWMETER:
216+
super()._update_attribute(attrid, value/10)
217+
95218

96219
class CustomDeviceTemperatureCluster(CustomCluster, DeviceTemperature):
97220
"""Custom DeviceTemperature Cluster."""
@@ -248,6 +371,42 @@ class SinopeTechnologiesValve(CustomDevice):
248371
replacement = {
249372
ENDPOINTS: {
250373
1: {
374+
INPUT_CLUSTERS: [
375+
CustomBasicCluster,
376+
PowerConfiguration.cluster_id,
377+
Identify.cluster_id,
378+
Groups.cluster_id,
379+
Scenes.cluster_id,
380+
OnOff.cluster_id,
381+
LevelControl.cluster_id,
382+
Diagnostic.cluster_id,
383+
SinopeManufacturerCluster,
384+
],
385+
OUTPUT_CLUSTERS: [
386+
Identify.cluster_id,
387+
Ota.cluster_id,
388+
],
389+
}
390+
}
391+
}
392+
393+
394+
class SinopeTechnologiesValveG2(CustomDevice):
395+
"""SinopeTechnologiesValveG2 custom device."""
396+
397+
signature = {
398+
# <SimpleDescriptor(endpoint=1, profile=260,
399+
# device_type=3, device_version=0,
400+
# input_clusters=[0, 1, 3, 4, 5, 6, 8, 1026, 1280, 1794, 2821, 65281]
401+
# output_clusters=[3, 6, 25]>
402+
MODELS_INFO: [
403+
(SINOPE, "VA4220ZB"),
404+
(SINOPE, "VA4221ZB"),
405+
],
406+
ENDPOINTS: {
407+
1: {
408+
PROFILE_ID: zha_p.PROFILE_ID,
409+
DEVICE_TYPE: zha_p.DeviceType.LEVEL_CONTROLLABLE_OUTPUT,
251410
INPUT_CLUSTERS: [
252411
Basic.cluster_id,
253412
PowerConfiguration.cluster_id,
@@ -256,11 +415,42 @@ class SinopeTechnologiesValve(CustomDevice):
256415
Scenes.cluster_id,
257416
OnOff.cluster_id,
258417
LevelControl.cluster_id,
418+
TemperatureMeasurement.cluster_id,
419+
IasZone.cluster_id,
420+
Metering.cluster_id,
421+
Diagnostic.cluster_id,
422+
SINOPE_MANUFACTURER_CLUSTER_ID,
423+
],
424+
OUTPUT_CLUSTERS: [
425+
Identify.cluster_id,
426+
OnOff.cluster_id,
427+
Ota.cluster_id,
428+
],
429+
}
430+
},
431+
}
432+
433+
replacement = {
434+
ENDPOINTS: {
435+
1: {
436+
INPUT_CLUSTERS: [
437+
CustomBasicCluster,
438+
PowerConfiguration.cluster_id,
439+
Identify.cluster_id,
440+
Groups.cluster_id,
441+
Scenes.cluster_id,
442+
OnOff.cluster_id,
443+
LevelControl.cluster_id,
444+
TemperatureMeasurement.cluster_id,
445+
CustomFlowMeasurementCluster,
446+
IasZone.cluster_id,
447+
CustomMeteringCluster,
259448
Diagnostic.cluster_id,
260449
SinopeManufacturerCluster,
261450
],
262451
OUTPUT_CLUSTERS: [
263452
Identify.cluster_id,
453+
OnOff.cluster_id,
264454
Ota.cluster_id,
265455
],
266456
}

0 commit comments

Comments
 (0)