@willieee802/zigbee-herdsman-converters 15.0.8-4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/converters/fromZigbee.js +8439 -0
  4. package/converters/toZigbee.js +7162 -0
  5. package/devices/ITCommander.js +37 -0
  6. package/devices/RMC002.js +20 -0
  7. package/devices/acova.js +101 -0
  8. package/devices/acuity_brands_lighting.js +11 -0
  9. package/devices/adeo.js +256 -0
  10. package/devices/adurosmart.js +130 -0
  11. package/devices/aeotec.js +13 -0
  12. package/devices/airam.js +59 -0
  13. package/devices/ajax_online.js +49 -0
  14. package/devices/akuvox.js +28 -0
  15. package/devices/alchemy.js +18 -0
  16. package/devices/aldi.js +53 -0
  17. package/devices/alecto.js +98 -0
  18. package/devices/anchor.js +17 -0
  19. package/devices/atlantic.js +110 -0
  20. package/devices/atsmart.js +28 -0
  21. package/devices/aubess.js +21 -0
  22. package/devices/aurora_lighting.js +292 -0
  23. package/devices/automaton.js +44 -0
  24. package/devices/awox.js +184 -0
  25. package/devices/axis.js +22 -0
  26. package/devices/bankamp.js +11 -0
  27. package/devices/bega.js +22 -0
  28. package/devices/belkin.js +11 -0
  29. package/devices/bitron.js +334 -0
  30. package/devices/blaupunkt.js +27 -0
  31. package/devices/blitzwolf.js +47 -0
  32. package/devices/bosch.js +702 -0
  33. package/devices/brimate.js +15 -0
  34. package/devices/bseed.js +17 -0
  35. package/devices/bticino.js +118 -0
  36. package/devices/busch-jaeger.js +140 -0
  37. package/devices/byun.js +24 -0
  38. package/devices/calex.js +37 -0
  39. package/devices/candeo.js +49 -0
  40. package/devices/casaia.js +52 -0
  41. package/devices/centralite.js +359 -0
  42. package/devices/cleode.js +20 -0
  43. package/devices/cleverio.js +36 -0
  44. package/devices/climax.js +138 -0
  45. package/devices/commercial_electric.js +16 -0
  46. package/devices/connecte.js +46 -0
  47. package/devices/cree.js +11 -0
  48. package/devices/ctm.js +999 -0
  49. package/devices/current_products_corp.js +24 -0
  50. package/devices/custom_devices_diy.js +850 -0
  51. package/devices/cy-lighting.js +11 -0
  52. package/devices/danalock.js +26 -0
  53. package/devices/danfoss.js +340 -0
  54. package/devices/databyte.ch.js +55 -0
  55. package/devices/datek.js +246 -0
  56. package/devices/dawon_dns.js +338 -0
  57. package/devices/develco.js +868 -0
  58. package/devices/digi.js +14 -0
  59. package/devices/diyruz.js +305 -0
  60. package/devices/dlink.js +37 -0
  61. package/devices/dnake.js +11 -0
  62. package/devices/dresden_elektronik.js +47 -0
  63. package/devices/easyaccess.js +27 -0
  64. package/devices/eatonhalo_led.js +11 -0
  65. package/devices/echostar.js +25 -0
  66. package/devices/ecodim.js +145 -0
  67. package/devices/ecolink.js +21 -0
  68. package/devices/ecosmart.js +64 -0
  69. package/devices/ecozy.js +31 -0
  70. package/devices/edp.js +39 -0
  71. package/devices/eglo.js +25 -0
  72. package/devices/elko.js +176 -0
  73. package/devices/enbrighten.js +163 -0
  74. package/devices/enocean.js +52 -0
  75. package/devices/envilar.js +73 -0
  76. package/devices/essentialb.js +87 -0
  77. package/devices/eurotronic.js +48 -0
  78. package/devices/evanell.js +29 -0
  79. package/devices/evn.js +31 -0
  80. package/devices/evology.js +24 -0
  81. package/devices/evvr.js +17 -0
  82. package/devices/ewelink.js +184 -0
  83. package/devices/ezex.js +24 -0
  84. package/devices/fantem.js +77 -0
  85. package/devices/feibit.js +172 -0
  86. package/devices/fireangel.js +15 -0
  87. package/devices/frankever.js +23 -0
  88. package/devices/garza.js +11 -0
  89. package/devices/ge.js +111 -0
  90. package/devices/gewiss.js +48 -0
  91. package/devices/gidealed.js +11 -0
  92. package/devices/giderwel.js +11 -0
  93. package/devices/giex.js +222 -0
  94. package/devices/girier.js +19 -0
  95. package/devices/gledopto.js +756 -0
  96. package/devices/gmy.js +11 -0
  97. package/devices/gs.js +29 -0
  98. package/devices/halemeier.js +18 -0
  99. package/devices/hampton_bay.js +37 -0
  100. package/devices/heiman.js +805 -0
  101. package/devices/hej.js +107 -0
  102. package/devices/hfh.js +11 -0
  103. package/devices/hgkg.js +54 -0
  104. package/devices/hilux.js +11 -0
  105. package/devices/hive.js +524 -0
  106. package/devices/home_control_as.js +27 -0
  107. package/devices/hommyn.js +24 -0
  108. package/devices/honyar.js +29 -0
  109. package/devices/hornbach.js +53 -0
  110. package/devices/hzc.js +21 -0
  111. package/devices/hzc_electric.js +42 -0
  112. package/devices/icasa.js +121 -0
  113. package/devices/idinio.js +19 -0
  114. package/devices/ihorn.js +61 -0
  115. package/devices/ikea.js +1168 -0
  116. package/devices/ilightsin.js +11 -0
  117. package/devices/iluminize.js +262 -0
  118. package/devices/ilux.js +11 -0
  119. package/devices/immax.js +203 -0
  120. package/devices/innr.js +705 -0
  121. package/devices/inovelli.js +1315 -0
  122. package/devices/insta.js +110 -0
  123. package/devices/iolloi.js +20 -0
  124. package/devices/iotperfect.js +20 -0
  125. package/devices/iris.js +180 -0
  126. package/devices/istar.js +20 -0
  127. package/devices/jasco.js +55 -0
  128. package/devices/javis.js +37 -0
  129. package/devices/jethome.js +48 -0
  130. package/devices/jiawen.js +18 -0
  131. package/devices/jumitech.js +18 -0
  132. package/devices/jxuan.js +49 -0
  133. package/devices/kami.js +15 -0
  134. package/devices/keen_home.js +80 -0
  135. package/devices/klikaanklikuit.js +17 -0
  136. package/devices/kmpcil.js +148 -0
  137. package/devices/konke.js +141 -0
  138. package/devices/ksentry.js +17 -0
  139. package/devices/kurvia.js +17 -0
  140. package/devices/kwikset.js +120 -0
  141. package/devices/lanesto.js +11 -0
  142. package/devices/lds.js +11 -0
  143. package/devices/led_trading.js +69 -0
  144. package/devices/ledvance.js +353 -0
  145. package/devices/leedarson.js +130 -0
  146. package/devices/legrand.js +554 -0
  147. package/devices/lellki.js +146 -0
  148. package/devices/letsled.js +11 -0
  149. package/devices/letv.js +18 -0
  150. package/devices/leviton.js +142 -0
  151. package/devices/lg.js +18 -0
  152. package/devices/lidl.js +1000 -0
  153. package/devices/lifecontrol.js +92 -0
  154. package/devices/lightsolutions.js +37 -0
  155. package/devices/linkind.js +205 -0
  156. package/devices/livingwise.js +59 -0
  157. package/devices/livolo.js +286 -0
  158. package/devices/lixee.js +779 -0
  159. package/devices/lonsonho.js +218 -0
  160. package/devices/lubeez.js +18 -0
  161. package/devices/lupus.js +74 -0
  162. package/devices/lutron.js +32 -0
  163. package/devices/lux.js +40 -0
  164. package/devices/m-elec.js +18 -0
  165. package/devices/makegood.js +51 -0
  166. package/devices/matcall_bv.js +18 -0
  167. package/devices/meazon.js +47 -0
  168. package/devices/mercator.js +225 -0
  169. package/devices/miboxer.js +72 -0
  170. package/devices/micromatic.js +29 -0
  171. package/devices/moes.js +400 -0
  172. package/devices/mycket.js +11 -0
  173. package/devices/m/303/274ller_licht.js +220 -0
  174. package/devices/namron.js +774 -0
  175. package/devices/nanoleaf.js +11 -0
  176. package/devices/neo.js +86 -0
  177. package/devices/net2grid.js +29 -0
  178. package/devices/netvox.js +35 -0
  179. package/devices/niko.js +314 -0
  180. package/devices/ninja_blocks.js +24 -0
  181. package/devices/niviss.js +11 -0
  182. package/devices/nodon.js +109 -0
  183. package/devices/nordtronic.js +30 -0
  184. package/devices/nous.js +88 -0
  185. package/devices/novo.js +17 -0
  186. package/devices/nue_3a.js +417 -0
  187. package/devices/nyce.js +67 -0
  188. package/devices/onesti.js +59 -0
  189. package/devices/openlumi.js +22 -0
  190. package/devices/orvibo.js +515 -0
  191. package/devices/osram.js +519 -0
  192. package/devices/oujiabao.js +15 -0
  193. package/devices/owon.js +352 -0
  194. package/devices/ozsmartthings.js +11 -0
  195. package/devices/paul_neuhaus.js +96 -0
  196. package/devices/paulmann.js +158 -0
  197. package/devices/peq.js +24 -0
  198. package/devices/perenio.js +413 -0
  199. package/devices/philips.js +3118 -0
  200. package/devices/plaid.js +25 -0
  201. package/devices/plugwise.js +173 -0
  202. package/devices/popp.js +10 -0
  203. package/devices/profalux.js +47 -0
  204. package/devices/prolight.js +54 -0
  205. package/devices/qmotion.js +33 -0
  206. package/devices/qoto.js +113 -0
  207. package/devices/quotra.js +18 -0
  208. package/devices/rademacher.js +18 -0
  209. package/devices/rgb_genie.js +119 -0
  210. package/devices/robb.js +335 -0
  211. package/devices/roome.js +15 -0
  212. package/devices/rtx.js +90 -0
  213. package/devices/salus_controls.js +137 -0
  214. package/devices/samotech.js +93 -0
  215. package/devices/saswell.js +58 -0
  216. package/devices/scanproducts.js +32 -0
  217. package/devices/schlage.js +24 -0
  218. package/devices/schneider_electric.js +1039 -0
  219. package/devices/schwaiger.js +62 -0
  220. package/devices/seastar_intelligence.js +16 -0
  221. package/devices/securifi.js +39 -0
  222. package/devices/sengled.js +332 -0
  223. package/devices/sercomm.js +140 -0
  224. package/devices/shenzhen_homa.js +53 -0
  225. package/devices/shinasystem.js +686 -0
  226. package/devices/siglis.js +440 -0
  227. package/devices/sinope.js +1257 -0
  228. package/devices/siterwell.js +46 -0
  229. package/devices/skydance.js +112 -0
  230. package/devices/slv.js +27 -0
  231. package/devices/smart9.js +27 -0
  232. package/devices/smart_home_pty.js +18 -0
  233. package/devices/smartenit.js +42 -0
  234. package/devices/smartthings.js +495 -0
  235. package/devices/smartwings.js +24 -0
  236. package/devices/sohan_electric.js +13 -0
  237. package/devices/solaredge.js +11 -0
  238. package/devices/somgoms.js +47 -0
  239. package/devices/sonoff.js +262 -0
  240. package/devices/spotmau.js +36 -0
  241. package/devices/sprut.js +317 -0
  242. package/devices/stelpro.js +216 -0
  243. package/devices/sunricher.js +625 -0
  244. package/devices/swann.js +33 -0
  245. package/devices/sylvania.js +200 -0
  246. package/devices/tci.js +25 -0
  247. package/devices/technicolor.js +47 -0
  248. package/devices/terncy.js +64 -0
  249. package/devices/the_light_group.js +70 -0
  250. package/devices/third_reality.js +195 -0
  251. package/devices/titan_products.js +22 -0
  252. package/devices/tplink.js +42 -0
  253. package/devices/trust.js +114 -0
  254. package/devices/tubeszb.js +20 -0
  255. package/devices/tuya.js +4215 -0
  256. package/devices/ubisys.js +938 -0
  257. package/devices/uhome.js +25 -0
  258. package/devices/universal_electronics_inc.js +119 -0
  259. package/devices/urlighting.js +12 -0
  260. package/devices/useelink.js +52 -0
  261. package/devices/vbled.js +18 -0
  262. package/devices/vesternet.js +188 -0
  263. package/devices/viessmann.js +51 -0
  264. package/devices/villeroy_boch.js +18 -0
  265. package/devices/vimar.js +78 -0
  266. package/devices/visonic.js +80 -0
  267. package/devices/vrey.js +18 -0
  268. package/devices/wally.js +25 -0
  269. package/devices/waxman.js +45 -0
  270. package/devices/weiser.js +67 -0
  271. package/devices/weten.js +13 -0
  272. package/devices/wisdom.js +11 -0
  273. package/devices/woox.js +177 -0
  274. package/devices/wyze.js +23 -0
  275. package/devices/xiaomi.js +3223 -0
  276. package/devices/xinghuoyuan.js +11 -0
  277. package/devices/yale.js +227 -0
  278. package/devices/ynoa.js +68 -0
  279. package/devices/yookee.js +23 -0
  280. package/devices/ysrsai.js +26 -0
  281. package/devices/zemismart.js +254 -0
  282. package/devices/zen.js +34 -0
  283. package/devices/zipato.js +11 -0
  284. package/index.js +242 -0
  285. package/lib/color.js +784 -0
  286. package/lib/configureKey.js +944 -0
  287. package/lib/constants.js +316 -0
  288. package/lib/exposes.js +677 -0
  289. package/lib/extend.js +180 -0
  290. package/lib/kelvinToXy.js +1912 -0
  291. package/lib/legacy.js +2223 -0
  292. package/lib/light.js +111 -0
  293. package/lib/ota/OTA_URLs.md +119 -0
  294. package/lib/ota/common.js +476 -0
  295. package/lib/ota/index.js +10 -0
  296. package/lib/ota/inovelli.js +72 -0
  297. package/lib/ota/ledvance.js +52 -0
  298. package/lib/ota/lixee.js +57 -0
  299. package/lib/ota/salus.js +82 -0
  300. package/lib/ota/securifi.js +25 -0
  301. package/lib/ota/tradfri.js +45 -0
  302. package/lib/ota/ubisys.js +61 -0
  303. package/lib/ota/zigbeeOTA.js +161 -0
  304. package/lib/philips.js +667 -0
  305. package/lib/reporting.js +234 -0
  306. package/lib/store.js +57 -0
  307. package/lib/tuya.js +2027 -0
  308. package/lib/utils.js +527 -0
  309. package/lib/xiaomi.d.ts +11 -0
  310. package/lib/xiaomi.js +1288 -0
  311. package/lib/zosung.js +243 -0
  312. package/package.json +39 -0
package/devices/ctm.js ADDED
@@ -0,0 +1,999 @@
1
+ const fz = require('../converters/fromZigbee');
2
+ const tz = require('../converters/toZigbee');
3
+ const exposes = require('../lib/exposes');
4
+ const reporting = require('../lib/reporting');
5
+ const constants = require('../lib/constants');
6
+ const utils = require('../lib/utils');
7
+ const e = exposes.presets;
8
+ const ea = exposes.access;
9
+
10
+ const dataType = {
11
+ boolean: 16,
12
+ uint8: 32,
13
+ uint16: 33,
14
+ int8: 40,
15
+ int16: 41,
16
+ enum8: 48,
17
+ charStr: 66,
18
+ ieeeAddr: 240,
19
+ };
20
+
21
+ const fzLocal = {
22
+ ctm_mbd_device_enabled: {
23
+ cluster: 'genOnOff',
24
+ type: ['attributeReport', 'readResponse'],
25
+ convert: (model, msg, publish, options, meta) => {
26
+ const result = {};
27
+ const data = msg.data;
28
+ if (data.hasOwnProperty('onOff')) {
29
+ result.device_enabled = data['onOff'] ? 'ON' : 'OFF';
30
+ }
31
+
32
+ return result;
33
+ },
34
+ },
35
+ ctm_device_mode: {
36
+ cluster: 'genOnOff',
37
+ type: ['attributeReport', 'readResponse'],
38
+ convert: (model, msg, publish, options, meta) => {
39
+ const result = {};
40
+ const data = msg.data;
41
+ if (data.hasOwnProperty(0x2200)) {
42
+ const deviceModeLookup = {0: 'astro_clock', 1: 'timer', 2: 'daily_timer', 3: 'weekly_timer'};
43
+ result.device_mode = deviceModeLookup[data[0x2200]];
44
+ }
45
+
46
+ return result;
47
+ },
48
+ },
49
+ ctm_device_enabled: {
50
+ cluster: 'genOnOff',
51
+ type: ['attributeReport', 'readResponse'],
52
+ convert: (model, msg, publish, options, meta) => {
53
+ const result = {};
54
+ const data = msg.data;
55
+ if (data.hasOwnProperty(0x2201)) {
56
+ result.device_enabled = data[0x2201] ? 'ON' : 'OFF';
57
+ }
58
+
59
+ return result;
60
+ },
61
+ },
62
+ ctm_child_lock: {
63
+ cluster: 'genOnOff',
64
+ type: ['attributeReport', 'readResponse'],
65
+ convert: (model, msg, publish, options, meta) => {
66
+ const result = {};
67
+ const data = msg.data;
68
+ if (data.hasOwnProperty(0x2202)) {
69
+ result.child_lock = data[0x2202] ? 'locked' : 'unlocked';
70
+ }
71
+
72
+ return result;
73
+ },
74
+ },
75
+ ctm_current_flag: {
76
+ cluster: 'genOnOff',
77
+ type: ['attributeReport', 'readResponse'],
78
+ convert: (model, msg, publish, options, meta) => {
79
+ const result = {};
80
+ const data = msg.data;
81
+ if (data.hasOwnProperty(0x5000)) {
82
+ result.current_flag = data[0x5000];
83
+ }
84
+
85
+ return result;
86
+ },
87
+ },
88
+ ctm_relay_state: {
89
+ cluster: 'genOnOff',
90
+ type: ['attributeReport', 'readResponse'],
91
+ convert: (model, msg, publish, options, meta) => {
92
+ const result = {};
93
+ const data = msg.data;
94
+ if (data.hasOwnProperty(0x5001)) {
95
+ result.state = data[0x5001] ? 'ON' : 'OFF';
96
+ }
97
+
98
+ return result;
99
+ },
100
+ },
101
+ ctm_temperature_offset: {
102
+ cluster: 'msTemperatureMeasurement',
103
+ type: ['attributeReport', 'readResponse'],
104
+ convert: (model, msg, publish, options, meta) => {
105
+ const result = {};
106
+ const data = msg.data;
107
+ if (data.hasOwnProperty(0x0400)) {
108
+ result.temperature_offset = data[0x0400];
109
+ }
110
+
111
+ return result;
112
+ },
113
+ },
114
+ ctm_thermostat: {
115
+ cluster: 'hvacThermostat',
116
+ type: ['attributeReport', 'readResponse'],
117
+ convert: (model, msg, publish, options, meta) => {
118
+ const result = {};
119
+ const data = msg.data;
120
+ if (data.hasOwnProperty(0x0401)) { // Load
121
+ result.load = data[0x0401];
122
+ }
123
+ if (data.hasOwnProperty(0x0402)) { // Display text
124
+ result.display_text = data[0x0402];
125
+ }
126
+ if (data.hasOwnProperty(0x0403)) { // Sensor
127
+ const sensorModeLookup = {
128
+ 0: 'air', 1: 'floor', 2: 'external', 3: 'regulator', 4: 'mv_air', 5: 'mv_external', 6: 'mv_regulator'};
129
+ result.sensor = sensorModeLookup[data[0x0403]];
130
+ }
131
+ if (data.hasOwnProperty(0x0405)) { // Regulator mode
132
+ result.regulator_mode = data[0x0405] ? 'regulator' : 'thermostat';
133
+ }
134
+ if (data.hasOwnProperty(0x0406)) { // Power status
135
+ result.power_status = data[0x0406] ? 'ON' : 'OFF';
136
+ }
137
+ if (data.hasOwnProperty(0x0408)) { // Mean power
138
+ result.mean_power = data[0x0408];
139
+ }
140
+ if (data.hasOwnProperty(0x0409)) { // Floor temp
141
+ result.floor_temp = utils.precisionRound(data[0x0409], 2) /100;
142
+ }
143
+ if (data.hasOwnProperty(0x0411)) { // Night switching
144
+ result.night_switching = data[0x0411] ? 'ON' : 'OFF';
145
+ }
146
+ if (data.hasOwnProperty(0x0412)) { // Frost guard
147
+ result.frost_guard = data[0x0412] ? 'ON' : 'OFF';
148
+ }
149
+ if (data.hasOwnProperty(0x0413)) { // Child lock
150
+ result.child_lock = data[0x0413] ? 'LOCK' : 'UNLOCK';
151
+ }
152
+ if (data.hasOwnProperty(0x0414)) { // Max floor temp
153
+ result.max_floor_temp = data[0x0414];
154
+ }
155
+ if (data.hasOwnProperty(0x0415)) { // Heating
156
+ result.heating = data[0x0415] ? 'heating' : 'idle';
157
+ }
158
+ if (data.hasOwnProperty(0x0420)) { // Regulator setpoint
159
+ result.regulator_setpoint = data[0x0420];
160
+ }
161
+ if (data.hasOwnProperty(0x0421)) { // Regulation mode
162
+ const regulationModeLookup = {0: 'thermostat', 1: 'regulator', 2: 'zzilent'};
163
+ result.regulation_mode= regulationModeLookup[data[0x0421]];
164
+ }
165
+ if (data.hasOwnProperty(0x0422)) { // Operation mode
166
+ const presetLookup = {0: 'off', 1: 'away', 2: 'sleep', 3: 'home'};
167
+ const systemModeLookup = {0: 'off', 1: 'off', 2: 'off', 3: 'heat'};
168
+ result.preset = presetLookup[data[0x0422]];
169
+ result.system_mode = systemModeLookup[data[0x0422]];
170
+ }
171
+ if (data.hasOwnProperty(0x0423)) { // Maximum floor temp guard
172
+ result.max_floor_guard = data[0x0423] ? 'ON' : 'OFF';
173
+ }
174
+ if (data.hasOwnProperty(0x0424)) { // Weekly timer enabled
175
+ result.weekly_timer = data[0x0424] ? 'ON' : 'OFF';
176
+ }
177
+ if (data.hasOwnProperty(0x0425)) { // Frost guard setpoint
178
+ result.frost_guard_setpoint = data[0x0425];
179
+ }
180
+ if (data.hasOwnProperty(0x0426)) { // External temperature
181
+ result.external_temp = utils.precisionRound(data[0x0426], 2) /100;
182
+ }
183
+ if (data.hasOwnProperty(0x0428)) { // Exteral sensor source
184
+ result.exteral_sensor_source = data[0x0428];
185
+ }
186
+ if (data.hasOwnProperty(0x0429)) { // Current air temperature
187
+ result.air_temp = utils.precisionRound(data[0x0429], 2) /100;
188
+ }
189
+ if (data.hasOwnProperty(0x0424)) { // Floor Sensor Error
190
+ result.floor_sensor_error = data[0x042B] ? 'error' : 'ok';
191
+ }
192
+ if (data.hasOwnProperty(0x0424)) { // External Air Sensor Error
193
+ result.exteral_sensor_error = data[0x042C] ? 'error' : 'ok';
194
+ }
195
+
196
+ return result;
197
+ },
198
+ },
199
+ ctm_group_config: {
200
+ cluster: '65191', // 0xFEA7 ctmGroupConfig
201
+ type: ['attributeReport', 'readResponse'],
202
+ convert: (model, msg, publish, options, meta) => {
203
+ const result = {};
204
+ const data = msg.data;
205
+ if (data.hasOwnProperty(0x0000)) {
206
+ result.group_id = data[0x0000];
207
+ }
208
+
209
+ return result;
210
+ },
211
+ },
212
+ ctm_sove_guard: {
213
+ cluster: '65481', // 0xFFC9 ctmSoveGuard
214
+ type: ['attributeReport', 'readResponse'],
215
+ convert: (model, msg, publish, options, meta) => {
216
+ const result = {};
217
+ const data = msg.data;
218
+ if (data.hasOwnProperty(0x0001)) { // Alarm status
219
+ const alarmStatusLookup = {
220
+ 0: 'ok', 1: 'tamper', 2: 'high_temperatur', 3: 'timer', 4: 'battery_alarm', 5: 'error', 0xFF: 'unknown'};
221
+ result.alarm_status = alarmStatusLookup[data[0x0001]];
222
+ }
223
+ if (data.hasOwnProperty(0x0002)) { // Change battery
224
+ result.battery_low = data[0x0002] ? true : false;
225
+ }
226
+ if (data.hasOwnProperty(0x0003)) { // Stove temperature
227
+ result.stove_temperature = data[0x0003];
228
+ }
229
+ if (data.hasOwnProperty(0x0004)) { // Ambient temperature
230
+ result.ambient_temperature = data[0x0004];
231
+ }
232
+ if (data.hasOwnProperty(0x0005)) { // Active
233
+ result.active = data[0x0005] ? true : false;
234
+ }
235
+ if (data.hasOwnProperty(0x0006)) { // Runtime
236
+ result.runtime = data[0x0006];
237
+ }
238
+ if (data.hasOwnProperty(0x0007)) { // Runtime timeout
239
+ result.runtime_timeout = data[0x0007];
240
+ }
241
+ if (data.hasOwnProperty(0x0008)) { // Reset reason
242
+ const resetReasonLookup = {
243
+ 0: 'unknown', 1: 'power_on', 2: 'external', 3: 'brown_out', 4: 'watchdog', 5: 'program_interface',
244
+ 6: 'software', 0xFF: 'unknown'};
245
+ result.reset_reason = resetReasonLookup[data[0x0008]];
246
+ }
247
+ if (data.hasOwnProperty(0x0009)) { // Dip switch
248
+ result.dip_switch = data[0x0009];
249
+ }
250
+ if (data.hasOwnProperty(0x000A)) { // Software version
251
+ result.sw_version = data[0x000A];
252
+ }
253
+ if (data.hasOwnProperty(0x000B)) { // Hardware version
254
+ result.hw_version = data[0x000B];
255
+ }
256
+ if (data.hasOwnProperty(0x000C)) { // Bootloader version
257
+ result.bootloader_version = data[0x000C];
258
+ }
259
+ if (data.hasOwnProperty(0x000D)) { // Model
260
+ const modelLookup = {0: 'unknown', 1: '1_8', 2: 'infinity', 3: 'hybrid', 4: 'tak', 0xFF: 'unknown'};
261
+ result.model = modelLookup[data[0x000D]];
262
+ }
263
+ if (data.hasOwnProperty(0x0010)) { // Relay address
264
+ result.relay_address = data[0x0010];
265
+ }
266
+ if (data.hasOwnProperty(0x0100)) { // Relay current flag
267
+ const currentFlagLookup = {0: 'false', 1: 'true', 0xFF: 'unknown'};
268
+ result.current_flag = currentFlagLookup[data[0x0100]];
269
+ }
270
+ if (data.hasOwnProperty(0x0101)) { // Relay current
271
+ result.relay_current = data[0x0101];
272
+ }
273
+ if (data.hasOwnProperty(0x0102)) { // Relay status
274
+ const relayStatusLookup = {0: 'off', 1: 'on', 2: 'not_present', 0xFF: 'unknown'};
275
+ result.relay_status = relayStatusLookup[data[0x0102]];
276
+ }
277
+ if (data.hasOwnProperty(0x0103)) { // Relay external button
278
+ const relayStatusLookup = {0: 'not_clicked', 1: 'clicked', 0xFF: 'unknown'};
279
+ result.external_button = relayStatusLookup[data[0x0103]];
280
+ }
281
+ if (data.hasOwnProperty(0x0104)) { // Relay alarm
282
+ const relayAlarmLookup = {0: 'ok', 1: 'no_communication', 2: 'over_current', 3: 'over_temperature', 0xFF: 'unknown'};
283
+ result.relay_alarm = relayAlarmLookup[data[0x0104]];
284
+ }
285
+ if (data.hasOwnProperty(0x0105)) { // Alarm status (from relay)
286
+ const relayAlarmStatusLookup = {
287
+ 0: 'ok', 1: 'tamper', 2: 'high_temperatur', 3: 'timer', 4: 'battery_alarm', 5: 'error', 0xFF: 'unknown'};
288
+ result.relay_alarm_status = relayAlarmStatusLookup[data[0x0105]];
289
+ }
290
+
291
+ return result;
292
+ },
293
+ },
294
+ ctm_water_leak_alarm: {
295
+ cluster: 'ssIasZone',
296
+ type: ['commandStatusChangeNotification', 'attributeReport'],
297
+ convert: (model, msg, publish, options, meta) => {
298
+ const zoneStatus = msg.data.zonestatus;
299
+ return {
300
+ active_water_leak: (zoneStatus & 1) > 0,
301
+ water_leak: (zoneStatus & 1<<1) > 0,
302
+ battery_low: (zoneStatus & 1<<3) > 0,
303
+ };
304
+ },
305
+ },
306
+ };
307
+
308
+
309
+ const tzLocal = {
310
+ ctm_mbd_device_enabled: {
311
+ key: ['device_enabled'],
312
+ convertSet: async (entity, key, value, meta) => {
313
+ await entity.command('genOnOff', value.toLowerCase(), {}, utils.getOptions(meta.mapped, entity));
314
+ },
315
+ convertGet: async (entity, key, meta) => {
316
+ await entity.read('genOnOff', ['onOff']);
317
+ },
318
+ },
319
+ ctm_device_mode: {
320
+ key: ['device_mode'],
321
+ convertGet: async (entity, key, meta) => {
322
+ await entity.read('genOnOff', [0x2200]);
323
+ },
324
+ },
325
+ ctm_device_enabled: {
326
+ key: ['device_enabled'],
327
+ convertSet: async (entity, key, value, meta) => {
328
+ await entity.write('genOnOff', {0x2201: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
329
+ },
330
+ convertGet: async (entity, key, meta) => {
331
+ await entity.read('genOnOff', [0x2201]);
332
+ },
333
+ },
334
+ ctm_child_lock: {
335
+ key: ['child_lock'],
336
+ convertGet: async (entity, key, meta) => {
337
+ await entity.read('genOnOff', [0x2202]);
338
+ },
339
+ },
340
+ ctm_current_flag: {
341
+ key: ['current_flag'],
342
+ convertGet: async (entity, key, meta) => {
343
+ await entity.read('genOnOff', [0x5000], {manufacturerCode: 0x1337});
344
+ },
345
+ },
346
+ ctm_relay_state: {
347
+ key: ['state'],
348
+ convertSet: async (entity, key, value, meta) => {
349
+ await entity.write('genOnOff',
350
+ {0x5001: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}}, {manufacturerCode: 0x1337});
351
+ },
352
+ convertGet: async (entity, key, meta) => {
353
+ await entity.read('genOnOff', [0x5001], {manufacturerCode: 0x1337});
354
+ },
355
+ },
356
+ ctm_temperature_offset: {
357
+ key: ['temperature_offset'],
358
+ convertSet: async (entity, key, value, meta) => {
359
+ await entity.write('msTemperatureMeasurement',
360
+ {0x0400: {value: value, type: dataType.int8}}, {manufacturerCode: 0x1337, sendWhen: 'active'});
361
+ },
362
+ convertGet: async (entity, key, meta) => {
363
+ await entity.read('msTemperatureMeasurement', [0x0400], {manufacturerCode: 0x1337, sendWhen: 'active'});
364
+ await entity.read('msTemperatureMeasurement', ['measuredValue'], {sendWhen: 'active'});
365
+ },
366
+ },
367
+ ctm_thermostat: {
368
+ key: ['load', 'display_text', 'sensor', 'regulator_mode', 'power_status', 'system_mode', 'night_switching', 'frost_guard',
369
+ 'max_floor_temp', 'regulator_setpoint', 'regulation_mode', 'max_floor_guard', 'weekly_timer', 'exteral_sensor_source',
370
+ ],
371
+ convertSet: async (entity, key, value, meta) => {
372
+ switch (key) {
373
+ case 'load':
374
+ await entity.write('hvacThermostat', {0x0401: {value: value, type: dataType.uint16}});
375
+ break;
376
+ case 'display_text':
377
+ await entity.write('hvacThermostat', {0x0402: {value: value, type: dataType.charStr}});
378
+ break;
379
+ case 'sensor':
380
+ await entity.write('hvacThermostat', {0x0403: {
381
+ value: {'air': 0, 'floor': 1, 'external': 2, 'regulator': 3, 'mv_air': 4, 'mv_external': 5, 'mv_regulator': 6}[value],
382
+ type: dataType.enum8}});
383
+ break;
384
+ case 'regulator_mode':
385
+ await entity.write('hvacThermostat', {0x0405: {value: {'thermostat': 0, 'regulator': 1}[value], type: dataType.boolean}});
386
+ break;
387
+ case 'power_status':
388
+ await entity.write('hvacThermostat', {0x0406: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
389
+ break;
390
+ case 'system_mode':
391
+ if (value === 'off') {
392
+ await entity.write('hvacThermostat', {0x0406: {value: 0, type: dataType.boolean}});
393
+ } else if (value === 'heat') {
394
+ await entity.write('hvacThermostat', {0x0422: {value: 3, type: dataType.uint8}});
395
+ }
396
+ break;
397
+ case 'night_switching':
398
+ await entity.write('hvacThermostat', {0x0411: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
399
+ break;
400
+ case 'frost_guard':
401
+ await entity.write('hvacThermostat', {0x0412: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
402
+ break;
403
+ case 'max_floor_temp':
404
+ await entity.write('hvacThermostat', {0x0414: {value: value, type: dataType.uint8}});
405
+ break;
406
+ case 'regulator_setpoint':
407
+ await entity.write('hvacThermostat', {0x0420: {value: value, type: dataType.uint8}});
408
+ break;
409
+ case 'regulation_mode':
410
+ await entity.write('hvacThermostat', {0x0421: {
411
+ value: {'thermostat': 0, 'regulator': 1, 'zzilent': 2}[value],
412
+ type: dataType.uint8}});
413
+ break;
414
+ case 'max_floor_guard':
415
+ await entity.write('hvacThermostat', {0x0423: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
416
+ break;
417
+ case 'weekly_timer':
418
+ await entity.write('hvacThermostat', {0x0424: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
419
+ break;
420
+ case 'exteral_sensor_source':
421
+ await entity.write('hvacThermostat', {0x0428: {value: value, type: dataType.uint16}});
422
+ break;
423
+
424
+ default: // Unknown key
425
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertSet ${key}`);
426
+ }
427
+ },
428
+ convertGet: async (entity, key, meta) => {
429
+ switch (key) {
430
+ case 'load':
431
+ await entity.read('hvacThermostat', [0x0401]);
432
+ break;
433
+ case 'display_text':
434
+ await entity.read('hvacThermostat', [0x0402]);
435
+ break;
436
+ case 'sensor':
437
+ await entity.read('hvacThermostat', [0x0403]);
438
+ break;
439
+ case 'regulator_mode':
440
+ await entity.read('hvacThermostat', [0x0405]);
441
+ break;
442
+ case 'power_status':
443
+ await entity.read('hvacThermostat', [0x0406]);
444
+ break;
445
+ case 'night_switching':
446
+ await entity.read('hvacThermostat', [0x0411]);
447
+ break;
448
+ case 'frost_guard':
449
+ await entity.read('hvacThermostat', [0x0412]);
450
+ break;
451
+ case 'max_floor_temp':
452
+ await entity.read('hvacThermostat', [0x0414]);
453
+ break;
454
+ case 'regulator_setpoint':
455
+ await entity.read('hvacThermostat', [0x0420]);
456
+ break;
457
+ case 'regulation_mode':
458
+ await entity.read('hvacThermostat', [0x0421]);
459
+ break;
460
+ case 'system_mode':
461
+ await entity.read('hvacThermostat', [0x0422]);
462
+ break;
463
+ case 'max_floor_guard':
464
+ await entity.read('hvacThermostat', [0x0423]);
465
+ break;
466
+ case 'weekly_timer':
467
+ await entity.read('hvacThermostat', [0x0424]);
468
+ break;
469
+ case 'exteral_sensor_source':
470
+ await entity.read('hvacThermostat', [0x0428]);
471
+ break;
472
+
473
+ default: // Unknown key
474
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertGet ${key}`);
475
+ }
476
+ },
477
+ },
478
+ ctm_thermostat_preset: {
479
+ key: ['preset'],
480
+ convertSet: async (entity, key, value, meta) => {
481
+ const presetLookup = {'off': 0, 'away': 1, 'sleep': 2, 'home': 3};
482
+ await entity.write('hvacThermostat', {0x0422: {value: presetLookup[value], type: dataType.uint8}});
483
+ },
484
+ },
485
+ ctm_thermostat_child_lock: {
486
+ key: ['child_lock'],
487
+ convertSet: async (entity, key, value, meta) => {
488
+ await entity.write('hvacThermostat', {0x0413: {value: {'UNLOCK': 0, 'LOCK': 1}[value], type: dataType.boolean}});
489
+ },
490
+ },
491
+ ctm_thermostat_gets: {
492
+ key: ['mean_power', 'floor_temp', 'heating', 'frost_guard_setpoint', 'external_temp',
493
+ 'air_temp', 'floor_sensor_error', 'exteral_sensor_error',
494
+ ],
495
+ convertGet: async (entity, key, meta) => {
496
+ switch (key) {
497
+ case 'mean_power':
498
+ await entity.read('hvacThermostat', [0x0408]);
499
+ break;
500
+ case 'floor_temp':
501
+ await entity.read('hvacThermostat', [0x0409]);
502
+ break;
503
+ case 'heating':
504
+ await entity.read('hvacThermostat', [0x0415]);
505
+ break;
506
+ case 'frost_guard_setpoint':
507
+ await entity.read('hvacThermostat', [0x0425]);
508
+ break;
509
+ case 'external_temp':
510
+ await entity.read('hvacThermostat', [0x0426]);
511
+ break;
512
+ case 'air_temp':
513
+ await entity.read('hvacThermostat', [0x0429]);
514
+ break;
515
+ case 'floor_sensor_error':
516
+ await entity.read('hvacThermostat', [0x042B]);
517
+ break;
518
+ case 'exteral_sensor_error':
519
+ await entity.read('hvacThermostat', [0x042C]);
520
+ break;
521
+
522
+ default: // Unknown key
523
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertGet ${key}`);
524
+ }
525
+ },
526
+ },
527
+ ctm_group_config: {
528
+ key: ['group_id'],
529
+ convertGet: async (entity, key, meta) => {
530
+ await entity.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337, sendWhen: 'active'});
531
+ },
532
+ },
533
+ ctm_sove_guard: {
534
+ key: [
535
+ 'alarm_status', 'change_battery', 'stove_temperature', 'ambient_temperature', 'active', 'runtime', 'runtime_timeout',
536
+ 'reset_reason', 'dip_switch', 'sw_version', 'hw_version', 'bootloader_version', 'model', 'relay_address',
537
+ 'current_flag', 'relay_current', 'relay_status', 'external_button', 'relay_alarm', 'relay_alarm_status',
538
+ ],
539
+ convertGet: async (entity, key, meta) => {
540
+ switch (key) {
541
+ case 'alarm_status':
542
+ await entity.read(0xFFC9, [0x0001], {manufacturerCode: 0x1337});
543
+ break;
544
+ case 'battery_low':
545
+ await entity.read(0xFFC9, [0x0002], {manufacturerCode: 0x1337});
546
+ break;
547
+ case 'stove_temperature':
548
+ await entity.read(0xFFC9, [0x0003], {manufacturerCode: 0x1337});
549
+ break;
550
+ case 'ambient_temperature':
551
+ await entity.read(0xFFC9, [0x0004], {manufacturerCode: 0x1337});
552
+ break;
553
+ case 'active':
554
+ await entity.read(0xFFC9, [0x0005], {manufacturerCode: 0x1337});
555
+ break;
556
+ case 'runtime':
557
+ await entity.read(0xFFC9, [0x0006], {manufacturerCode: 0x1337});
558
+ break;
559
+ case 'runtime_timeout':
560
+ await entity.read(0xFFC9, [0x0007], {manufacturerCode: 0x1337});
561
+ break;
562
+ case 'reset_reason':
563
+ await entity.read(0xFFC9, [0x0008], {manufacturerCode: 0x1337});
564
+ break;
565
+ case 'dip_switch':
566
+ await entity.read(0xFFC9, [0x0009], {manufacturerCode: 0x1337});
567
+ break;
568
+ case 'sw_version':
569
+ await entity.read(0xFFC9, [0x000A], {manufacturerCode: 0x1337});
570
+ break;
571
+ case 'hw_version':
572
+ await entity.read(0xFFC9, [0x000B], {manufacturerCode: 0x1337});
573
+ break;
574
+ case 'bootloader_version':
575
+ await entity.read(0xFFC9, [0x000C], {manufacturerCode: 0x1337});
576
+ break;
577
+ case 'model':
578
+ await entity.read(0xFFC9, [0x000D], {manufacturerCode: 0x1337});
579
+ break;
580
+ case 'relay_address':
581
+ await entity.read(0xFFC9, [0x0010], {manufacturerCode: 0x1337});
582
+ break;
583
+ case 'current_flag':
584
+ await entity.read(0xFFC9, [0x0100], {manufacturerCode: 0x1337});
585
+ break;
586
+ case 'relay_current':
587
+ await entity.read(0xFFC9, [0x0101], {manufacturerCode: 0x1337});
588
+ break;
589
+ case 'relay_status':
590
+ await entity.read(0xFFC9, [0x0102], {manufacturerCode: 0x1337});
591
+ break;
592
+ case 'external_button':
593
+ await entity.read(0xFFC9, [0x0103], {manufacturerCode: 0x1337});
594
+ break;
595
+ case 'relay_alarm':
596
+ await entity.read(0xFFC9, [0x0104], {manufacturerCode: 0x1337});
597
+ break;
598
+ case 'relay_alarm_status':
599
+ await entity.read(0xFFC9, [0x0105], {manufacturerCode: 0x1337});
600
+ break;
601
+
602
+ default: // Unknown key
603
+ throw new Error(`Unhandled key tzLocal.ctm_sove_guard.convertGet ${key}`);
604
+ }
605
+ },
606
+ },
607
+ };
608
+
609
+ module.exports = [
610
+ {
611
+ zigbeeModel: ['mTouch Dim'],
612
+ model: 'mTouch_Dim',
613
+ vendor: 'CTM Lyng',
614
+ description: 'mTouch Dim OP, touch dimmer',
615
+ fromZigbee: [fz.on_off, fz.brightness, fz.lighting_ballast_configuration],
616
+ toZigbee: [tz.on_off, tz.light_onoff_brightness, tz.light_brightness_move, tz.ballast_config],
617
+ meta: {disableDefaultResponse: true},
618
+ configure: async (device, coordinatorEndpoint, logger) => {
619
+ const endpoint = device.getEndpoint(1);
620
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'lightingBallastCfg']);
621
+ await endpoint.read('genOnOff', ['onOff']);
622
+ await reporting.onOff(endpoint);
623
+ await endpoint.read('genLevelCtrl', ['currentLevel']);
624
+ await reporting.brightness(endpoint);
625
+ await endpoint.read('lightingBallastCfg', ['minLevel', 'maxLevel', 'powerOnLevel']);
626
+ await endpoint.configureReporting('lightingBallastCfg', [{
627
+ attribute: 'minLevel',
628
+ minimumReportInterval: 0,
629
+ maximumReportInterval: constants.repInterval.HOUR,
630
+ reportableChange: null}]);
631
+ await endpoint.configureReporting('lightingBallastCfg', [{
632
+ attribute: 'maxLevel',
633
+ minimumReportInterval: 0,
634
+ maximumReportInterval: constants.repInterval.HOUR,
635
+ reportableChange: null}]);
636
+ await endpoint.configureReporting('lightingBallastCfg', [{
637
+ attribute: 'powerOnLevel',
638
+ minimumReportInterval: 0,
639
+ maximumReportInterval: constants.repInterval.HOUR,
640
+ reportableChange: null}]);
641
+ },
642
+ exposes: [e.light_brightness(),
643
+ exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(99)
644
+ .withDescription('Specifies the minimum brightness value'),
645
+ exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(99)
646
+ .withDescription('Specifies the maximum brightness value'),
647
+ exposes.numeric('ballast_power_on_level', ea.ALL).withValueMin(1).withValueMax(99)
648
+ .withDescription('Specifies the initialisation light level. Can not be set lower than "ballast_minimum_level"')],
649
+ },
650
+ {
651
+ zigbeeModel: ['mTouch Bryter'],
652
+ model: 'mTouch_Bryter',
653
+ vendor: 'CTM Lyng',
654
+ description: 'mTouch Bryter OP, 3 channel switch',
655
+ fromZigbee: [fz.temperature, fz.battery, fz.command_recall, fz.command_on, fz.command_off, fz.command_toggle,
656
+ fz.command_move, fz.command_stop, fzLocal.ctm_group_config],
657
+ toZigbee: [],
658
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
659
+ configure: async (device, coordinatorEndpoint, logger) => {
660
+ const endpoint = device.getEndpoint(1);
661
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement']);
662
+ await reporting.batteryVoltage(endpoint);
663
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
664
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
665
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
666
+ },
667
+ exposes: [e.battery(), e.temperature(),
668
+ e.action(['recall_1', 'recall_2', 'recall_3', 'on', 'off', 'toggle',
669
+ 'brightness_move_down', 'brightness_move_up', 'brightness_stop']),
670
+ exposes.numeric('group_id', ea.STATE)
671
+ .withDescription('The device sends commands with this group ID. Put dvices in this group to control them.')],
672
+ },
673
+ {
674
+ zigbeeModel: ['mTouch One'],
675
+ model: 'mTouch_One',
676
+ vendor: 'CTM Lyng',
677
+ description: 'mTouch One OP, touch thermostat',
678
+ fromZigbee: [fz.thermostat, fzLocal.ctm_thermostat],
679
+ toZigbee: [tz.thermostat_occupied_heating_setpoint, tz.thermostat_local_temperature, tzLocal.ctm_thermostat,
680
+ tzLocal.ctm_thermostat_preset, tzLocal.ctm_thermostat_child_lock, tzLocal.ctm_thermostat_gets],
681
+ configure: async (device, coordinatorEndpoint, logger) => {
682
+ const endpoint = device.getEndpoint(1);
683
+ await reporting.bind(endpoint, coordinatorEndpoint, ['hvacThermostat']);
684
+ await endpoint.read('hvacThermostat', ['localTemp', 'occupiedHeatingSetpoint']);
685
+ await reporting.thermostatTemperature(endpoint);
686
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
687
+ await endpoint.read('hvacThermostat', [0x0401]);
688
+ await endpoint.read('hvacThermostat', [0x0402]);
689
+ // Regulator mode
690
+ await endpoint.read('hvacThermostat', [0x0405]);
691
+ await endpoint.configureReporting('hvacThermostat', [{
692
+ attribute: {ID: 0x0405, type: dataType.boolean},
693
+ minimumReportInterval: 1,
694
+ maximumReportInterval: constants.repInterval.MAX,
695
+ reportableChange: null}]);
696
+ // Power consumption
697
+ await endpoint.read('hvacThermostat', [0x0408]);
698
+ await endpoint.configureReporting('hvacThermostat', [{
699
+ attribute: {ID: 0x0408, type: dataType.uint16},
700
+ minimumReportInterval: 0,
701
+ maximumReportInterval: constants.repInterval.HOUR,
702
+ reportableChange: 5}]);
703
+ // Floor temp sensor
704
+ await endpoint.read('hvacThermostat', [0x0409]);
705
+ await endpoint.configureReporting('hvacThermostat', [{
706
+ attribute: {ID: 0x0409, type: dataType.int16},
707
+ minimumReportInterval: 0,
708
+ maximumReportInterval: constants.repInterval.HOUR,
709
+ reportableChange: 10}]);
710
+ // Frost guard
711
+ await endpoint.read('hvacThermostat', [0x0412]);
712
+ await endpoint.configureReporting('hvacThermostat', [{
713
+ attribute: {ID: 0x0412, type: dataType.boolean},
714
+ minimumReportInterval: 0,
715
+ maximumReportInterval: constants.repInterval.MAX,
716
+ reportableChange: null}]);
717
+ // Child lock active/inactive
718
+ await endpoint.read('hvacThermostat', [0x0413]);
719
+ await endpoint.configureReporting('hvacThermostat', [{
720
+ attribute: {ID: 0x0413, type: dataType.boolean},
721
+ minimumReportInterval: 0,
722
+ maximumReportInterval: constants.repInterval.MAX,
723
+ reportableChange: null}]);
724
+ // Regulator setpoint
725
+ await endpoint.read('hvacThermostat', [0x0420]);
726
+ await endpoint.configureReporting('hvacThermostat', [{
727
+ attribute: {ID: 0x0420, type: dataType.uint8},
728
+ minimumReportInterval: 0,
729
+ maximumReportInterval: constants.repInterval.HOUR,
730
+ reportableChange: 1}]);
731
+ // Operation mode
732
+ await endpoint.read('hvacThermostat', [0x0422]);
733
+ await endpoint.configureReporting('hvacThermostat', [{
734
+ attribute: {ID: 0x0422, type: dataType.uint8},
735
+ minimumReportInterval: 0,
736
+ maximumReportInterval: constants.repInterval.HOUR,
737
+ reportableChange: 1}]);
738
+ // Air temp sensor
739
+ await endpoint.read('hvacThermostat', [0x0429]);
740
+ await endpoint.configureReporting('hvacThermostat', [{
741
+ attribute: {ID: 0x0429, type: dataType.int16},
742
+ minimumReportInterval: 0,
743
+ maximumReportInterval: constants.repInterval.HOUR,
744
+ reportableChange: 10}]);
745
+ },
746
+ exposes: [e.child_lock(),
747
+ exposes.climate()
748
+ .withSetpoint('occupied_heating_setpoint', 5, 40, 1)
749
+ .withLocalTemperature()
750
+ .withSystemMode(['off', 'heat'])
751
+ .withPreset(['off', 'away', 'sleep', 'home']),
752
+ exposes.numeric('load', ea.ALL).withUnit('W')
753
+ .withDescription('Load in W when heating is on (between 0-3600 W). The thermostat uses the value as input to the ' +
754
+ 'mean_power calculation.')
755
+ .withValueMin(0).withValueMax(3600),
756
+ exposes.text('display_text', ea.ALL)
757
+ .withDescription('Displayed text on thermostat display (zone). Max 19 characters'),
758
+ exposes.binary('regulator_mode', ea.ALL, 'regulator', 'thermostat')
759
+ .withDescription('Device in regulator or thermostat mode.'),
760
+ exposes.numeric('mean_power', ea.STATE_GET).withUnit('W')
761
+ .withDescription('Reports average power usage last 10 minutes'),
762
+ exposes.numeric('floor_temp', ea.STATE_GET).withUnit('°C')
763
+ .withDescription('Current temperature measured from the floor sensor'),
764
+ exposes.binary('frost_guard', ea.ALL, 'ON', 'OFF')
765
+ .withDescription('When frost guard is ON, it is activated when the thermostat is switched OFF with the ON/OFF button.' +
766
+ 'At the same time, the display will fade and the text "Frostsikring x °C" appears in the display and remains until the ' +
767
+ 'thermostat is switched on again.'),
768
+ exposes.numeric('regulator_setpoint', ea.ALL).withUnit('%')
769
+ .withDescription('Setpoint in %, use only when the thermostat is in regulator mode.')
770
+ .withValueMin(1).withValueMax(99),
771
+ exposes.numeric('air_temp', ea.STATE_GET).withUnit('°C')
772
+ .withDescription('Current temperature measured from the air sensor'),
773
+ ],
774
+ },
775
+ {
776
+ zigbeeModel: ['mStikk Outlet', 'mStikk 16A', 'mStikk 25A', 'Tavlerele 25A'],
777
+ model: 'mStikk_Outlet',
778
+ vendor: 'CTM Lyng',
779
+ description: 'mStikk OP, wall socket',
780
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
781
+ toZigbee: [tz.on_off],
782
+ configure: async (device, coordinatorEndpoint, logger) => {
783
+ const endpoint = device.getEndpoint(1);
784
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
785
+ await endpoint.read('haElectricalMeasurement', ['acVoltageMultiplier', 'acVoltageDivisor']);
786
+ await endpoint.read('haElectricalMeasurement', ['acCurrentMultiplier', 'acCurrentDivisor']);
787
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
788
+ await reporting.readMeteringMultiplierDivisor(endpoint);
789
+ await endpoint.read('genOnOff', ['onOff']);
790
+ await reporting.onOff(endpoint);
791
+ await reporting.rmsVoltage(endpoint, {change: 100});
792
+ await reporting.rmsCurrent(endpoint);
793
+ await reporting.activePower(endpoint);
794
+ await reporting.currentSummDelivered(endpoint);
795
+ },
796
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy()],
797
+ },
798
+ {
799
+ zigbeeModel: ['mKomfy'],
800
+ model: 'mKomfy_Sensor',
801
+ vendor: 'CTM Lyng',
802
+ description: 'mKomfy, stove guard',
803
+ fromZigbee: [fz.temperature, fz.battery, fzLocal.ctm_sove_guard],
804
+ toZigbee: [],
805
+ configure: async (device, coordinatorEndpoint, logger) => {
806
+ const endpoint = device.getEndpoint(1);
807
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement', 0xFFC9]);
808
+ await reporting.batteryPercentageRemaining(endpoint);
809
+ // await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
810
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
811
+ // Alarm status
812
+ // await endpoint.read(0xFFC9, [0x0001], {manufacturerCode: 0x1337});
813
+ await endpoint.configureReporting(0xFFC9, [{
814
+ attribute: {ID: 0x0001, type: dataType.uint8},
815
+ minimumReportInterval: 0,
816
+ maximumReportInterval: constants.repInterval.HOUR,
817
+ reportableChange: 0}], {manufacturerCode: 0x1337});
818
+ // Change battery
819
+ // await endpoint.read(0xFFC9, [0x0002], {manufacturerCode: 0x1337});
820
+ await endpoint.configureReporting(0xFFC9, [{
821
+ attribute: {ID: 0x0002, type: dataType.uint8},
822
+ minimumReportInterval: 0,
823
+ maximumReportInterval: constants.repInterval.MAX,
824
+ reportableChange: 0}], {manufacturerCode: 0x1337});
825
+ // Active
826
+ // await endpoint.read(0xFFC9, [0x0005], {manufacturerCode: 0x1337});
827
+ await endpoint.configureReporting(0xFFC9, [{
828
+ attribute: {ID: 0x0005, type: dataType.uint8},
829
+ minimumReportInterval: 0,
830
+ maximumReportInterval: constants.repInterval.HOUR,
831
+ reportableChange: 0}], {manufacturerCode: 0x1337});
832
+ },
833
+ exposes: [e.battery(), e.battery_low(), e.temperature(),
834
+ exposes.enum('alarm_status', ea.STATE, ['ok', 'tamper', 'high_temperatur', 'timer', 'battery_alarm', 'error', 'unknown'])
835
+ .withDescription('Alarm status.'),
836
+ exposes.binary('active', ea.STATE, true, false)
837
+ .withDescription('Stove guard active/inactive (Stove in use)')],
838
+ },
839
+ {
840
+ zigbeeModel: ['mTouch Astro'],
841
+ model: 'mTouch_Astro',
842
+ vendor: 'CTM Lyng',
843
+ description: 'mTouch Astro OP, astro clock',
844
+ fromZigbee: [fz.on_off, fz.command_on, fz.command_off, fzLocal.ctm_device_mode, fzLocal.ctm_device_enabled,
845
+ fzLocal.ctm_child_lock, fzLocal.ctm_group_config],
846
+ toZigbee: [tz.on_off, tzLocal.ctm_device_enabled],
847
+ meta: {disableDefaultResponse: true},
848
+ configure: async (device, coordinatorEndpoint, logger) => {
849
+ const endpoint = device.getEndpoint(1);
850
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
851
+ await endpoint.read('genOnOff', ['onOff']);
852
+ await reporting.onOff(endpoint);
853
+ // Device mode
854
+ await endpoint.read('genOnOff', [0x2200]);
855
+ await endpoint.configureReporting('genOnOff', [{
856
+ attribute: {ID: 0x2200, type: dataType.uint8},
857
+ minimumReportInterval: 0,
858
+ maximumReportInterval: constants.repInterval.HOUR,
859
+ reportableChange: 0}]);
860
+ await endpoint.read('genOnOff', [0x2201]);
861
+ await endpoint.configureReporting('genOnOff', [{
862
+ attribute: {ID: 0x2201, type: dataType.boolean},
863
+ minimumReportInterval: 0,
864
+ maximumReportInterval: constants.repInterval.HOUR,
865
+ reportableChange: null}]);
866
+ await endpoint.read('genOnOff', [0x2202]);
867
+ await endpoint.configureReporting('genOnOff', [{
868
+ attribute: {ID: 0x2202, type: dataType.boolean},
869
+ minimumReportInterval: 0,
870
+ maximumReportInterval: constants.repInterval.HOUR,
871
+ reportableChange: null}]);
872
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
873
+ },
874
+ exposes: [e.switch(), e.action(['on', 'off']),
875
+ exposes.enum('device_mode', ea.STATE, ['astro_clock', 'timer', 'daily_timer', 'weekly_timer'])
876
+ .withDescription('Device mode.'),
877
+ exposes.binary('device_enabled', ea.ALL, 'ON', 'OFF')
878
+ .withDescription('Turn the device on or off'),
879
+ exposes.binary('child_lock', ea.STATE, 'locked', 'unlocked')
880
+ .withDescription('Physical input on the device enabled/disabled'),
881
+ exposes.numeric('group_id', ea.STATE)
882
+ .withDescription('The device sends commands with this group ID. Put devices in this group to control them.'),
883
+ ],
884
+ },
885
+ {
886
+ zigbeeModel: ['AX Water Sensor'],
887
+ model: 'AX_Water_Sensor',
888
+ vendor: 'CTM Lyng',
889
+ description: 'AX Water Sensor, water leakage detector',
890
+ fromZigbee: [fz.battery, fz.ias_enroll, fzLocal.ctm_water_leak_alarm],
891
+ toZigbee: [],
892
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
893
+ configure: async (device, coordinatorEndpoint, logger) => {
894
+ const endpoint = device.getEndpoint(1);
895
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone']);
896
+ await reporting.batteryVoltage(endpoint);
897
+ await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
898
+ },
899
+ exposes: [e.battery(), e.battery_low(), e.water_leak(),
900
+ exposes.binary('active_water_leak', ea.STATE, true, false)
901
+ .withDescription('Indicates whether there is an active water leak'),
902
+ ],
903
+ },
904
+ {
905
+ zigbeeModel: ['AX Valve Controller'],
906
+ model: 'AX_Valve_Controller',
907
+ vendor: 'CTM Lyng',
908
+ description: 'AX Valve Controller, water shutoff valve controller',
909
+ fromZigbee: [fz.on_off, fz.ias_enroll, fzLocal.ctm_water_leak_alarm],
910
+ toZigbee: [tz.on_off],
911
+ meta: {disableDefaultResponse: true},
912
+ configure: async (device, coordinatorEndpoint, logger) => {
913
+ const endpoint = device.getEndpoint(1);
914
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
915
+ await endpoint.read('genOnOff', ['onOff']);
916
+ await reporting.onOff(endpoint);
917
+ const endpoint2 = device.getEndpoint(2);
918
+ await reporting.bind(endpoint2, coordinatorEndpoint, ['ssIasZone']);
919
+ await endpoint2.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
920
+ },
921
+ exposes: [e.switch(), e.water_leak(),
922
+ exposes.binary('active_water_leak', ea.STATE, true, false)
923
+ .withDescription('Indicates whether there is an active water leak'),
924
+ ],
925
+ },
926
+ {
927
+ zigbeeModel: ['Mikrofon'],
928
+ model: 'mSwitch_Mic',
929
+ vendor: 'CTM Lyng',
930
+ description: 'Mikrofon, alarm detection microphone',
931
+ fromZigbee: [fz.temperature, fz.battery, fz.command_on, fz.command_off, fz.ias_enroll, fz.ias_smoke_alarm_1,
932
+ fzLocal.ctm_group_config],
933
+ toZigbee: [],
934
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
935
+ configure: async (device, coordinatorEndpoint, logger) => {
936
+ const endpoint = device.getEndpoint(1);
937
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'msTemperatureMeasurement']);
938
+ await reporting.batteryVoltage(endpoint);
939
+ await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
940
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
941
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
942
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
943
+ },
944
+ exposes: [e.temperature(), e.battery(), e.battery_low(), e.smoke(),
945
+ e.action(['on', 'off']),
946
+ exposes.numeric('group_id', ea.STATE)
947
+ .withDescription('The device sends commands with this group ID. Put devices in this group to control them.'),
948
+ ],
949
+ },
950
+ {
951
+ zigbeeModel: ['Air Sensor'],
952
+ model: 'mTouch_Air_Sensor',
953
+ vendor: 'CTM Lyng',
954
+ description: 'Air Sensor, temperature & humidity sensor',
955
+ fromZigbee: [fz.battery, fz.temperature, fz.humidity],
956
+ toZigbee: [],
957
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
958
+ configure: async (device, coordinatorEndpoint, logger) => {
959
+ const endpoint = device.getEndpoint(1);
960
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity']);
961
+ await reporting.batteryVoltage(endpoint);
962
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
963
+ await reporting.temperature(endpoint);
964
+ await endpoint.read('msRelativeHumidity', ['measuredValue']);
965
+ await reporting.humidity(endpoint);
966
+ },
967
+ exposes: [e.battery(), e.temperature(), e.humidity()],
968
+ },
969
+ {
970
+ zigbeeModel: ['MBD-S'],
971
+ model: 'MBD-S',
972
+ vendor: 'CTM Lyng',
973
+ description: 'MBD-S, motion detector with 16A relay',
974
+ fromZigbee: [fz.illuminance, fz.occupancy, fzLocal.ctm_mbd_device_enabled, fzLocal.ctm_relay_state],
975
+ toZigbee: [tzLocal.ctm_mbd_device_enabled, tzLocal.ctm_relay_state],
976
+ meta: {disableDefaultResponse: true},
977
+ configure: async (device, coordinatorEndpoint, logger) => {
978
+ const endpoint = device.getEndpoint(1);
979
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'msIlluminanceMeasurement', 'msOccupancySensing']);
980
+ await endpoint.read('genOnOff', ['onOff']);
981
+ await reporting.onOff(endpoint);
982
+ await endpoint.read('msIlluminanceMeasurement', ['measuredValue']);
983
+ await reporting.illuminance(endpoint);
984
+ await endpoint.read('msOccupancySensing', ['occupancy']);
985
+ await reporting.occupancy(endpoint);
986
+ // Relay State
987
+ await endpoint.read('genOnOff', [0x5001], {manufacturerCode: 0x1337});
988
+ await endpoint.configureReporting('genOnOff', [{
989
+ attribute: {ID: 0x5001, type: dataType.boolean},
990
+ minimumReportInterval: 1,
991
+ maximumReportInterval: constants.repInterval.HOUR,
992
+ reportableChange: 0}], {manufacturerCode: 0x1337});
993
+ },
994
+ exposes: [e.switch(), e.illuminance(), e.illuminance_lux(), e.occupancy(),
995
+ exposes.binary('device_enabled', ea.ALL, 'ON', 'OFF')
996
+ .withDescription('Turn the device on or off'),
997
+ ],
998
+ },
999
+ ];