@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
@@ -0,0 +1,1257 @@
1
+ const exposes = require('../lib/exposes');
2
+ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
3
+ const tz = require('../converters/toZigbee');
4
+ const constants = require('../lib/constants');
5
+ const utils = require('../lib/utils');
6
+ const reporting = require('../lib/reporting');
7
+ const extend = require('../lib/extend');
8
+ const e = exposes.presets;
9
+ const ea = exposes.access;
10
+ const {precisionRound} = require('../lib/utils');
11
+
12
+ const manuSinope = {manufacturerCode: 0x119C};
13
+
14
+ const fzLocal = {
15
+ ias_water_leak_alarm: {
16
+ // RM3500ZB specific
17
+ cluster: 'ssIasZone',
18
+ type: ['commandStatusChangeNotification', 'attributeReport', 'readResponse'],
19
+ convert: (model, msg, publish, options, meta) => {
20
+ const zoneStatus = msg.data.zoneStatus;
21
+ return {
22
+ water_leak: (zoneStatus & 1) > 0,
23
+ tamper: (zoneStatus & 1<<2) > 0,
24
+ };
25
+ },
26
+ },
27
+ thermostat: {
28
+ cluster: 'hvacThermostat',
29
+ type: ['attributeReport', 'readResponse'],
30
+ options: [exposes.options.legacy()],
31
+ convert: (model, msg, publish, options, meta) => {
32
+ delete msg['running_state'];
33
+ const result = {};
34
+ const occupancyLookup = {0: 'unoccupied', 1: 'occupied'};
35
+ const cycleOutputLookup = {15: '15_sec', 300: '5_min', 600: '10_min',
36
+ 900: '15_min', 1200: '20_min', 1800: '30_min', 65535: 'off'};
37
+
38
+ if (msg.data.hasOwnProperty('1024')) {
39
+ result.thermostat_occupancy = occupancyLookup[msg.data['1024']];
40
+ }
41
+ if (msg.data.hasOwnProperty('SinopeOccupancy')) {
42
+ result.thermostat_occupancy = occupancyLookup[msg.data['SinopeOccupancy']];
43
+ }
44
+ if (msg.data.hasOwnProperty('1025')) {
45
+ result.main_cycle_output = cycleOutputLookup[msg.data['1025']];
46
+ }
47
+ if (msg.data.hasOwnProperty('SinopeMainCycleOutput')) {
48
+ result.main_cycle_output = cycleOutputLookup[msg.data['SinopeMainCycleOutput']];
49
+ }
50
+ if (msg.data.hasOwnProperty('1026')) {
51
+ const lookup = {0: 'on_demand', 1: 'sensing'};
52
+ result.backlight_auto_dim = lookup[msg.data['1026']];
53
+ }
54
+ if (msg.data.hasOwnProperty('SinopeBacklight')) {
55
+ const lookup = {0: 'on_demand', 1: 'sensing'};
56
+ result.backlight_auto_dim = lookup[msg.data['SinopeBacklight']];
57
+ }
58
+ if (msg.data.hasOwnProperty('1028')) {
59
+ result.aux_cycle_output = cycleOutputLookup[msg.data['1028']];
60
+ }
61
+ if (msg.data.hasOwnProperty('localTemp')) {
62
+ result.local_temperature = precisionRound(msg.data['localTemp'], 2) / 100;
63
+ }
64
+ if (msg.data.hasOwnProperty('localTemperatureCalibration')) {
65
+ result.local_temperature_calibration = precisionRound(msg.data['localTemperatureCalibration'], 2) / 10;
66
+ }
67
+ if (msg.data.hasOwnProperty('outdoorTemp')) {
68
+ result.outdoor_temperature = precisionRound(msg.data['outdoorTemp'], 2) / 100;
69
+ }
70
+ if (msg.data.hasOwnProperty('occupiedHeatingSetpoint')) {
71
+ result.occupied_heating_setpoint = precisionRound(msg.data['occupiedHeatingSetpoint'], 2) / 100;
72
+ }
73
+ if (msg.data.hasOwnProperty('unoccupiedHeatingSetpoint')) {
74
+ result.unoccupied_heating_setpoint = precisionRound(msg.data['unoccupiedHeatingSetpoint'], 2) / 100;
75
+ }
76
+ if (msg.data.hasOwnProperty('occupiedCoolingSetpoint')) {
77
+ result.occupied_cooling_setpoint = precisionRound(msg.data['occupiedCoolingSetpoint'], 2) / 100;
78
+ }
79
+ if (msg.data.hasOwnProperty('unoccupiedCoolingSetpoint')) {
80
+ result.unoccupied_cooling_setpoint = precisionRound(msg.data['unoccupiedCoolingSetpoint'], 2) / 100;
81
+ }
82
+ if (msg.data.hasOwnProperty('ctrlSeqeOfOper')) {
83
+ result.control_sequence_of_operation = constants.thermostatControlSequenceOfOperations[msg.data['ctrlSeqeOfOper']];
84
+ }
85
+ if (msg.data.hasOwnProperty('systemMode')) {
86
+ result.system_mode = constants.thermostatSystemModes[msg.data['systemMode']];
87
+ }
88
+ if (msg.data.hasOwnProperty('pIHeatingDemand')) {
89
+ result.pi_heating_demand = precisionRound(msg.data['pIHeatingDemand'], 0);
90
+ }
91
+ if (msg.data.hasOwnProperty('minHeatSetpointLimit')) {
92
+ result.min_heat_setpoint_limit = precisionRound(msg.data['minHeatSetpointLimit'], 2) / 100;
93
+ }
94
+ if (msg.data.hasOwnProperty('maxHeatSetpointLimit')) {
95
+ result.max_heat_setpoint_limit = precisionRound(msg.data['maxHeatSetpointLimit'], 2) / 100;
96
+ }
97
+ if (msg.data.hasOwnProperty('absMinHeatSetpointLimit')) {
98
+ result.abs_min_heat_setpoint_limit = precisionRound(msg.data['absMinHeatSetpointLimit'], 2) / 100;
99
+ }
100
+ if (msg.data.hasOwnProperty('absMaxHeatSetpointLimit')) {
101
+ result.abs_max_heat_setpoint_limit = precisionRound(msg.data['absMaxHeatSetpointLimit'], 2) / 100;
102
+ }
103
+ if (msg.data.hasOwnProperty('pIHeatingDemand')) {
104
+ result.running_state = msg.data['pIHeatingDemand'] >= 10 ? 'heat' : 'idle';
105
+ }
106
+ return result;
107
+ },
108
+ },
109
+ sinope: {
110
+ cluster: 'manuSpecificSinope',
111
+ type: ['attributeReport', 'readResponse'],
112
+ convert: (model, msg, publish, options, meta) => {
113
+ const result = {};
114
+ if (msg.data.hasOwnProperty('GFCiStatus')) {
115
+ const lookup = {0: 'off', 1: 'on'};
116
+ result.gfci_status = lookup[msg.data['GFCiStatus']];
117
+ }
118
+ if (msg.data.hasOwnProperty('floorLimitStatus')) {
119
+ const lookup = {0: 'off', 1: 'on'};
120
+ result.floor_limit_status = lookup[msg.data['floorLimitStatus']];
121
+ }
122
+ if (msg.data.hasOwnProperty('outdoorTempToDisplayTimeout')) {
123
+ result.enable_outdoor_temperature = msg.data['outdoorTempToDisplayTimeout'] == 10800 ? 'ON' : 'OFF';
124
+ }
125
+ if (msg.data.hasOwnProperty('currentTimeToDisplay')) {
126
+ result.current_time_to_display = msg.data['currentTimeToDisplay'];
127
+ }
128
+ if (msg.data.hasOwnProperty('floorControlMode')) {
129
+ const lookup = {1: 'ambiant', 2: 'floor'};
130
+ result.floor_control_mode = lookup[msg.data['floorControlMode']];
131
+ }
132
+ if (msg.data.hasOwnProperty('ambiantMaxHeatSetpointLimit')) {
133
+ result.ambiant_max_heat_setpoint = msg.data['ambiantMaxHeatSetpointLimit'] / 100.0;
134
+ if (result.ambiant_max_heat_setpoint === -327.68) {
135
+ result.ambiant_max_heat_setpoint = 'off';
136
+ }
137
+ }
138
+ if (msg.data.hasOwnProperty('floorMinHeatSetpointLimit')) {
139
+ result.floor_min_heat_setpoint = msg.data['floorMinHeatSetpointLimit'] / 100.0;
140
+ if (result.floor_min_heat_setpoint === -327.68) {
141
+ result.floor_min_heat_setpoint = 'off';
142
+ }
143
+ }
144
+ if (msg.data.hasOwnProperty('floorMaxHeatSetpointLimit')) {
145
+ result.floor_max_heat_setpoint = msg.data['floorMaxHeatSetpointLimit'] / 100.0;
146
+ if (result.floor_max_heat_setpoint === -327.68) {
147
+ result.floor_max_heat_setpoint = 'off';
148
+ }
149
+ }
150
+ if (msg.data.hasOwnProperty('temperatureSensor')) {
151
+ const lookup = {0: '10k', 1: '12k'};
152
+ result.floor_temperature_sensor = lookup[msg.data['temperatureSensor']];
153
+ }
154
+ if (msg.data.hasOwnProperty('timeFormatToDisplay')) {
155
+ const lookup = {0: '24h', 1: '12h'};
156
+ result.time_format = lookup[msg.data['timeFormatToDisplay']];
157
+ }
158
+ if (msg.data.hasOwnProperty('connectedLoad')) {
159
+ result.connected_load = msg.data['connectedLoad'];
160
+ }
161
+ if (msg.data.hasOwnProperty('auxConnectedLoad')) {
162
+ result.aux_connected_load = msg.data['auxConnectedLoad'];
163
+ if (result.aux_connected_load == 65535) {
164
+ result.aux_connected_load = 'disabled';
165
+ }
166
+ }
167
+ if (msg.data.hasOwnProperty('pumpProtection')) {
168
+ result.pump_protection = msg.data['pumpProtection'] == 1 ? 'ON' : 'OFF';
169
+ }
170
+ if (msg.data.hasOwnProperty('dimmerTimmer')) {
171
+ result.timer_seconds = msg.data['dimmerTimmer'];
172
+ }
173
+ if (msg.data.hasOwnProperty('ledIntensityOn')) {
174
+ result.led_intensity_on = msg.data['ledIntensityOn'];
175
+ }
176
+ if (msg.data.hasOwnProperty('ledIntensityOff')) {
177
+ result.led_intensity_off = msg.data['ledIntensityOff'];
178
+ }
179
+ if (msg.data.hasOwnProperty('minimumBrightness')) {
180
+ result.minimum_brightness = msg.data['minimumBrightness'];
181
+ }
182
+ return result;
183
+ },
184
+ },
185
+ };
186
+ const tzLocal = {
187
+ thermostat_occupancy: {
188
+ key: ['thermostat_occupancy'],
189
+ convertSet: async (entity, key, value, meta) => {
190
+ const sinopeOccupancy = {0: 'unoccupied', 1: 'occupied'};
191
+ const SinopeOccupancy = utils.getKey(sinopeOccupancy, value, value, Number);
192
+ await entity.write('hvacThermostat', {SinopeOccupancy}, manuSinope);
193
+ return {state: {'thermostat_occupancy': value}};
194
+ },
195
+ convertGet: async (entity, key, meta) => {
196
+ await entity.read('hvacThermostat', ['SinopeOccupancy'], manuSinope);
197
+ },
198
+ },
199
+ backlight_autodim: {
200
+ key: ['backlight_auto_dim'],
201
+ convertSet: async (entity, key, value, meta) => {
202
+ const sinopeBacklightParam = {0: 'on_demand', 1: 'sensing'};
203
+ const SinopeBacklight = utils.getKey(sinopeBacklightParam, value, value, Number);
204
+ await entity.write('hvacThermostat', {SinopeBacklight}, manuSinope);
205
+ return {state: {'backlight_auto_dim': value}};
206
+ },
207
+ convertGet: async (entity, key, meta) => {
208
+ await entity.read('hvacThermostat', ['SinopeBacklight'], manuSinope);
209
+ },
210
+ },
211
+ main_cycle_output: {
212
+ key: ['main_cycle_output'],
213
+ convertSet: async (entity, key, value, meta) => {
214
+ const lookup = {'15_sec': 15, '5_min': 300, '10_min': 600, '15_min': 900, '20_min': 1200, '30_min': 1800};
215
+ await entity.write('hvacThermostat', {SinopeMainCycleOutput: lookup[value]}, manuSinope);
216
+ return {state: {'main_cycle_output': value}};
217
+ },
218
+ convertGet: async (entity, key, meta) => {
219
+ await entity.read('hvacThermostat', ['SinopeMainCycleOutput'], manuSinope);
220
+ },
221
+ },
222
+ aux_cycle_output: {
223
+ // TH1400ZB specific
224
+ key: ['aux_cycle_output'],
225
+ convertSet: async (entity, key, value, meta) => {
226
+ const lookup = {'off': 65535, '15_sec': 15, '5_min': 300, '10_min': 600, '15_min': 900, '20_min': 1200, '30_min': 1800};
227
+ await entity.write('hvacThermostat', {SinopeAuxCycleOutput: lookup[value]});
228
+ return {state: {'aux_cycle_output': value}};
229
+ },
230
+ convertGet: async (entity, key, meta) => {
231
+ await entity.read('hvacThermostat', ['SinopeAuxCycleOutput']);
232
+ },
233
+ },
234
+ enable_outdoor_temperature: {
235
+ key: ['enable_outdoor_temperature'],
236
+ convertSet: async (entity, key, value, meta) => {
237
+ if (value.toLowerCase() == 'on') {
238
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 10800}, manuSinope);
239
+ } else if (value.toLowerCase() == 'off') {
240
+ // set timer to 12 sec in order to disable outdoor temperature
241
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplayTimeout: 12}, manuSinope);
242
+ }
243
+ return {readAfterWriteTime: 250, state: {enable_outdoor_temperature: value}};
244
+ },
245
+ convertGet: async (entity, key, meta) => {
246
+ await entity.read('manuSpecificSinope', ['outdoorTempToDisplayTimeout'], manuSinope);
247
+ },
248
+ },
249
+ outdoor_temperature: {
250
+ key: ['thermostat_outdoor_temperature'],
251
+ convertSet: async (entity, key, value, meta) => {
252
+ if (value > -100 && value < 100) {
253
+ await entity.write('manuSpecificSinope', {outdoorTempToDisplay: value * 100}, manuSinope);
254
+ }
255
+ },
256
+ },
257
+ thermostat_time: {
258
+ key: ['thermostat_time'],
259
+ convertSet: async (entity, key, value, meta) => {
260
+ if (value === '') {
261
+ const thermostatDate = new Date();
262
+ const thermostatTimeSec = thermostatDate.getTime() / 1000;
263
+ const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
264
+ const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
265
+ await entity.write('manuSpecificSinope', {currentTimeToDisplay}, manuSinope);
266
+ } else if (value !== '') {
267
+ await entity.write('manuSpecificSinope', {currentTimeToDisplay: value}, manuSinope);
268
+ }
269
+ },
270
+ },
271
+ floor_control_mode: {
272
+ // TH1300ZB and TH1400ZB specific
273
+ key: ['floor_control_mode'],
274
+ convertSet: async (entity, key, value, meta) => {
275
+ if (typeof value !== 'string') {
276
+ return;
277
+ }
278
+ const lookup = {'ambiant': 1, 'floor': 2};
279
+ value = value.toLowerCase();
280
+ if (lookup.hasOwnProperty(value)) {
281
+ await entity.write('manuSpecificSinope', {floorControlMode: lookup[value]});
282
+ }
283
+ return {readAfterWriteTime: 250, state: {floor_control_mode: value}};
284
+ },
285
+ convertGet: async (entity, key, meta) => {
286
+ await entity.read('manuSpecificSinope', ['floorControlMode']);
287
+ },
288
+ },
289
+ ambiant_max_heat_setpoint: {
290
+ // TH1300ZB and TH1400ZBspecific
291
+ key: ['ambiant_max_heat_setpoint'],
292
+ convertSet: async (entity, key, value, meta) => {
293
+ if ((value >= 5 && value <= 36) || value == 'off') {
294
+ await entity.write('manuSpecificSinope', {ambiantMaxHeatSetpointLimit: (value == 'off' ? -32768 : value * 100)});
295
+ return {readAfterWriteTime: 250, state: {ambiant_max_heat_setpoint: value}};
296
+ }
297
+ },
298
+ convertGet: async (entity, key, meta) => {
299
+ await entity.read('manuSpecificSinope', ['ambiantMaxHeatSetpointLimit']);
300
+ },
301
+ },
302
+ floor_min_heat_setpoint: {
303
+ // TH1300ZB and TH1400ZB specific
304
+ key: ['floor_min_heat_setpoint'],
305
+ convertSet: async (entity, key, value, meta) => {
306
+ if ((value >= 5 && value <= 34) || value == 'off') {
307
+ await entity.write('manuSpecificSinope', {floorMinHeatSetpointLimit: (value == 'off' ? -32768 : value * 100)});
308
+ return {readAfterWriteTime: 250, state: {floor_min_heat_setpoint: value}};
309
+ }
310
+ },
311
+ convertGet: async (entity, key, meta) => {
312
+ await entity.read('manuSpecificSinope', ['floorMinHeatSetpointLimit']);
313
+ },
314
+ },
315
+ floor_max_heat_setpoint: {
316
+ // TH1300ZB and TH1400ZB specific
317
+ key: ['floor_max_heat_setpoint'],
318
+ convertSet: async (entity, key, value, meta) => {
319
+ if ((value >= 7 && value <= 36) || value == 'off') {
320
+ await entity.write('manuSpecificSinope', {floorMaxHeatSetpointLimit: (value == 'off' ? -32768 : value * 100)});
321
+ return {readAfterWriteTime: 250, state: {floor_max_heat_setpoint: value}};
322
+ }
323
+ },
324
+ convertGet: async (entity, key, meta) => {
325
+ await entity.read('manuSpecificSinope', ['floorMaxHeatSetpointLimit']);
326
+ },
327
+ },
328
+ temperature_sensor: {
329
+ // TH1300ZB and TH1400ZB specific
330
+ key: ['floor_temperature_sensor'],
331
+ convertSet: async (entity, key, value, meta) => {
332
+ if (typeof value !== 'string') {
333
+ return;
334
+ }
335
+ const lookup = {'10k': 0, '12k': 1};
336
+ value = value.toLowerCase();
337
+ if (lookup.hasOwnProperty(value)) {
338
+ await entity.write('manuSpecificSinope', {temperatureSensor: lookup[value]});
339
+ }
340
+ return {readAfterWriteTime: 250, state: {floor_temperature_sensor: value}};
341
+ },
342
+ convertGet: async (entity, key, meta) => {
343
+ await entity.read('manuSpecificSinope', ['temperatureSensor']);
344
+ },
345
+ },
346
+ time_format: {
347
+ key: ['time_format'],
348
+ convertSet: async (entity, key, value, meta) => {
349
+ if (typeof value !== 'string') {
350
+ return;
351
+ }
352
+ const lookup = {'24h': 0, '12h': 1};
353
+ value = value.toLowerCase();
354
+ if (lookup.hasOwnProperty(value)) {
355
+ await entity.write('manuSpecificSinope', {timeFormatToDisplay: lookup[value]}, manuSinope);
356
+ return {readAfterWriteTime: 250, state: {time_format: value}};
357
+ }
358
+ },
359
+ convertGet: async (entity, key, meta) => {
360
+ await entity.read('manuSpecificSinope', ['timeFormatToDisplay'], manuSinope);
361
+ },
362
+ },
363
+ connected_load: {
364
+ // TH1400ZB specific
365
+ key: ['connected_load'],
366
+ convertSet: async (entity, key, value, meta) => {
367
+ await entity.write('manuSpecificSinope', {connectedLoad: value});
368
+ return {state: {connected_load: value}};
369
+ },
370
+ convertGet: async (entity, key, meta) => {
371
+ await entity.read('manuSpecificSinope', ['connectedLoad']);
372
+ },
373
+ },
374
+ aux_connected_load: {
375
+ // TH1400ZB specific
376
+ key: ['aux_connected_load'],
377
+ convertSet: async (entity, key, value, meta) => {
378
+ await entity.write('manuSpecificSinope', {auxConnectedLoad: value});
379
+ return {readAfterWriteTime: 250, state: {aux_connected_load: value}};
380
+ },
381
+ convertGet: async (entity, key, meta) => {
382
+ await entity.read('manuSpecificSinope', ['auxConnectedLoad']);
383
+ },
384
+ },
385
+ pump_protection: {
386
+ // TH1400ZB specific
387
+ key: ['pump_protection'],
388
+ convertSet: async (entity, key, value, meta) => {
389
+ if (value.toLowerCase() == 'on') {
390
+ await entity.write('manuSpecificSinope', {pumpProtection: 1});
391
+ } else if (value.toLowerCase() == 'off') {
392
+ await entity.write('manuSpecificSinope', {pumpProtection: 255});
393
+ }
394
+ return {readAfterWriteTime: 250, state: {pump_protection: value}};
395
+ },
396
+ convertGet: async (entity, key, meta) => {
397
+ await entity.read('manuSpecificSinope', ['pumpProtection']);
398
+ },
399
+ },
400
+ led_intensity_on: {
401
+ // DM25x0ZB and SW2500ZB
402
+ key: ['led_intensity_on'],
403
+ convertSet: async (entity, key, value, meta) => {
404
+ if (value >= 0 && value <= 100) {
405
+ await entity.write('manuSpecificSinope', {ledIntensityOn: value});
406
+ }
407
+ return {readAfterWriteTime: 250, state: {ledIntensityOn: value}};
408
+ },
409
+ convertGet: async (entity, key, meta) => {
410
+ await entity.read('manuSpecificSinope', ['ledIntensityOn']);
411
+ },
412
+ },
413
+ led_intensity_off: {
414
+ // DM2500ZB and SW2500ZB
415
+ key: ['led_intensity_off'],
416
+ convertSet: async (entity, key, value, meta) => {
417
+ if (value >= 0 && value <= 100) {
418
+ await entity.write('manuSpecificSinope', {ledIntensityOff: value});
419
+ }
420
+ return {readAfterWriteTime: 250, state: {ledIntensityOff: value}};
421
+ },
422
+ convertGet: async (entity, key, meta) => {
423
+ await entity.read('manuSpecificSinope', ['ledIntensityOff']);
424
+ },
425
+ },
426
+ led_color_on: {
427
+ // DM25x0ZB and SW2500ZB
428
+ key: ['led_color_on'],
429
+ convertSet: async (entity, key, value, meta) => {
430
+ const r = (value.r >= 0 && value.r <= 255) ? value.r : 0;
431
+ const g = (value.g >= 0 && value.g <= 255) ? value.g : 0;
432
+ const b = (value.b >= 0 && value.b <= 255) ? value.b : 0;
433
+
434
+ const valueHex = r + g * 256 + (b * 256 ** 2);
435
+ await entity.write('manuSpecificSinope', {ledColorOn: valueHex});
436
+ },
437
+ },
438
+ led_color_off: {
439
+ // DM25x0ZB and SW2500ZB
440
+ key: ['led_color_off'],
441
+ convertSet: async (entity, key, value, meta) => {
442
+ const r = (value.r >= 0 && value.r <= 255) ? value.r : 0;
443
+ const g = (value.g >= 0 && value.g <= 255) ? value.g : 0;
444
+ const b = (value.b >= 0 && value.b <= 255) ? value.b : 0;
445
+
446
+ const valueHex = r + g * 256 + b * 256 ** 2;
447
+ await entity.write('manuSpecificSinope', {ledColorOff: valueHex});
448
+ },
449
+ },
450
+ minimum_brightness: {
451
+ // DM2x0ZB
452
+ key: ['minimum_brightness'],
453
+ convertSet: async (entity, key, value, meta) => {
454
+ if (value >= 0 && value <= 3000) {
455
+ await entity.write('manuSpecificSinope', {minimumBrightness: value});
456
+ }
457
+ return {readAfterWriteTime: 250, state: {minimumBrightness: value}};
458
+ },
459
+ convertGet: async (entity, key, meta) => {
460
+ await entity.read('manuSpecificSinope', ['minimumBrightness']);
461
+ },
462
+ },
463
+ timer_seconds: {
464
+ // DM25x0ZB and SW2500ZB
465
+ key: ['timer_seconds'],
466
+ convertSet: async (entity, key, value, meta) => {
467
+ if (value >= 0 && value <= 10800) {
468
+ await entity.write('manuSpecificSinope', {dimmerTimmer: value});
469
+ }
470
+ return {readAfterWriteTime: 250, state: {dimmerTimmer: value}};
471
+ },
472
+ convertGet: async (entity, key, meta) => {
473
+ await entity.read('manuSpecificSinope', ['dimmerTimmer']);
474
+ },
475
+ },
476
+ };
477
+ module.exports = [
478
+ {
479
+ zigbeeModel: ['TH1123ZB'],
480
+ model: 'TH1123ZB',
481
+ vendor: 'Sinopé',
482
+ description: 'Zigbee line volt thermostat',
483
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
484
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
485
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
486
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
487
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
488
+ tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, tz.electrical_measurement_power],
489
+ exposes: [
490
+ exposes.climate()
491
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
492
+ .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
493
+ .withLocalTemperature()
494
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
495
+ .withPiHeatingDemand()
496
+ .withRunningState(['idle', 'heat'], ea.STATE),
497
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
498
+ .withDescription('Occupancy state of the thermostat'),
499
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
500
+ .withDescription('Showing outdoor temperature on secondary display'),
501
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
502
+ .withDescription('The temperature format displayed on the thermostat screen'),
503
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
504
+ .withDescription('The time format featured on the thermostat display'),
505
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
506
+ .withDescription('Control backlight dimming behavior'),
507
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
508
+ .withDescription('Enables or disables the device’s buttons'),
509
+ exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '15_min'])
510
+ .withDescription('The length of the control cycle: 15_sec=normal 15_min=fan'),
511
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
512
+ ],
513
+
514
+ configure: async (device, coordinatorEndpoint, logger) => {
515
+ const endpoint = device.getEndpoint(1);
516
+ const binds = [
517
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
518
+ 'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
519
+ 'manuSpecificSinope'];
520
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
521
+ await reporting.thermostatTemperature(endpoint);
522
+ await reporting.thermostatPIHeatingDemand(endpoint);
523
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
524
+
525
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
526
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
527
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
528
+ try {
529
+ await reporting.thermostatSystemMode(endpoint);
530
+ } catch (error) {/* Not all support this */}
531
+
532
+ await reporting.readMeteringMultiplierDivisor(endpoint);
533
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
534
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
535
+ try {
536
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
537
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
538
+ } catch (error) {
539
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {'acPowerMultiplier': 1, 'acPowerDivisor': 1});
540
+ }
541
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
542
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
543
+ },
544
+ },
545
+ {
546
+ zigbeeModel: ['TH1124ZB'],
547
+ model: 'TH1124ZB',
548
+ vendor: 'Sinopé',
549
+ description: 'Zigbee line volt thermostat',
550
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
551
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
552
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
553
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
554
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
555
+ tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, tz.electrical_measurement_power],
556
+ exposes: [
557
+ exposes.climate()
558
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
559
+ .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
560
+ .withLocalTemperature()
561
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
562
+ .withPiHeatingDemand()
563
+ .withRunningState(['idle', 'heat'], ea.STATE),
564
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
565
+ .withDescription('Occupancy state of the thermostat'),
566
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
567
+ .withDescription('Showing outdoor temperature on secondary display'),
568
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
569
+ .withDescription('The temperature format displayed on the thermostat screen'),
570
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
571
+ .withDescription('The time format featured on the thermostat display'),
572
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
573
+ .withDescription('Control backlight dimming behavior'),
574
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
575
+ .withDescription('Enables or disables the device’s buttons'),
576
+ exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '15_min'])
577
+ .withDescription('The length of the control cycle: 15_sec=normal 15_min=fan'),
578
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
579
+ ],
580
+
581
+ configure: async (device, coordinatorEndpoint, logger) => {
582
+ const endpoint = device.getEndpoint(1);
583
+ const binds = [
584
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
585
+ 'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
586
+ 'manuSpecificSinope'];
587
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
588
+ await reporting.thermostatTemperature(endpoint);
589
+ await reporting.thermostatPIHeatingDemand(endpoint);
590
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
591
+
592
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
593
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
594
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
595
+ try {
596
+ await reporting.thermostatSystemMode(endpoint);
597
+ } catch (error) {/* Not all support this */}
598
+
599
+ await reporting.readMeteringMultiplierDivisor(endpoint);
600
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
601
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
602
+ try {
603
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
604
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
605
+ } catch (error) {
606
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {'acPowerMultiplier': 1, 'acPowerDivisor': 1});
607
+ }
608
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
609
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
610
+ },
611
+ },
612
+ {
613
+ zigbeeModel: ['TH1123ZB-G2'],
614
+ model: 'TH1123ZB-G2',
615
+ vendor: 'Sinopé',
616
+ description: 'Zigbee line volt thermostat',
617
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
618
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
619
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
620
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
621
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
622
+ tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, tz.electrical_measurement_power],
623
+ exposes: [
624
+ exposes.climate()
625
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
626
+ .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
627
+ .withLocalTemperature()
628
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
629
+ .withPiHeatingDemand()
630
+ .withRunningState(['idle', 'heat'], ea.STATE),
631
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
632
+ .withDescription('Occupancy state of the thermostat'),
633
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
634
+ .withDescription('Showing outdoor temperature on secondary display'),
635
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
636
+ .withDescription('The temperature format displayed on the thermostat screen'),
637
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
638
+ .withDescription('The time format featured on the thermostat display'),
639
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
640
+ .withDescription('Control backlight dimming behavior'),
641
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
642
+ .withDescription('Enables or disables the device’s buttons'),
643
+ exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '15_min'])
644
+ .withDescription('The length of the control cycle: 15_sec=normal 15_min=fan'),
645
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
646
+ ],
647
+
648
+ configure: async (device, coordinatorEndpoint, logger) => {
649
+ const endpoint = device.getEndpoint(1);
650
+ const binds = [
651
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
652
+ 'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
653
+ 'manuSpecificSinope'];
654
+ await reporting.bind(endpoint, coordinatorEndpoint, binds); // This G2 version has limited memory space
655
+ const thermostatDate = new Date();
656
+ const thermostatTimeSec = thermostatDate.getTime() / 1000;
657
+ const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
658
+ const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
659
+ await endpoint.write('manuSpecificSinope', {currentTimeToDisplay}, manuSinope);
660
+ await endpoint.write('manuSpecificSinope', {'secondScreenBehavior': 0}, manuSinope); // Mode auto
661
+
662
+ await reporting.thermostatTemperature(endpoint);
663
+ await reporting.thermostatPIHeatingDemand(endpoint);
664
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
665
+ await reporting.thermostatSystemMode(endpoint);
666
+
667
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
668
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
669
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
670
+
671
+ await reporting.readMeteringMultiplierDivisor(endpoint);
672
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
673
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
674
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
675
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
676
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
677
+
678
+ // Disable default reporting (not used by Sinope)
679
+ await reporting.thermostatRunningState(endpoint, {min: 1, max: 0xFFFF});
680
+ try {
681
+ await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
682
+ } catch (error) {/* Do nothing */}
683
+ },
684
+ },
685
+ {
686
+ zigbeeModel: ['TH1124ZB-G2'],
687
+ model: 'TH1124ZB-G2',
688
+ vendor: 'Sinopé',
689
+ description: 'Zigbee line volt thermostat',
690
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
691
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
692
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
693
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
694
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
695
+ tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, tz.electrical_measurement_power],
696
+ exposes: [
697
+ exposes.climate()
698
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
699
+ .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
700
+ .withLocalTemperature()
701
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
702
+ .withPiHeatingDemand()
703
+ .withRunningState(['idle', 'heat'], ea.STATE),
704
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
705
+ .withDescription('Occupancy state of the thermostat'),
706
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
707
+ .withDescription('Showing outdoor temperature on secondary display'),
708
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
709
+ .withDescription('The temperature format displayed on the thermostat screen'),
710
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
711
+ .withDescription('The time format featured on the thermostat display'),
712
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
713
+ .withDescription('Control backlight dimming behavior'),
714
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
715
+ .withDescription('Enables or disables the device’s buttons'),
716
+ exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '15_min'])
717
+ .withDescription('The length of the control cycle: 15_sec=normal 15_min=fan'),
718
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
719
+ ],
720
+
721
+ configure: async (device, coordinatorEndpoint, logger) => {
722
+ const endpoint = device.getEndpoint(1);
723
+ const binds = [
724
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
725
+ 'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
726
+ 'manuSpecificSinope'];
727
+ await reporting.bind(endpoint, coordinatorEndpoint, binds); // This G2 version has limited memory space
728
+ const thermostatDate = new Date();
729
+ const thermostatTimeSec = thermostatDate.getTime() / 1000;
730
+ const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
731
+ const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
732
+ await endpoint.write('manuSpecificSinope', {currentTimeToDisplay}, manuSinope);
733
+ await endpoint.write('manuSpecificSinope', {'secondScreenBehavior': 0}, manuSinope); // Mode auto
734
+
735
+ await reporting.thermostatTemperature(endpoint);
736
+ await reporting.thermostatPIHeatingDemand(endpoint);
737
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
738
+ await reporting.thermostatSystemMode(endpoint);
739
+
740
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
741
+ await endpoint.configureReporting('msTemperatureMeasurement', [{
742
+ attribute: 'tolerance', minimumReportInterval: 1, maximumReportInterval: 0xFFFF, reportableChange: 1}]);
743
+
744
+ await reporting.readMeteringMultiplierDivisor(endpoint);
745
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
746
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
747
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
748
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
749
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
750
+
751
+ // Disable default reporting (not used by Sinope)
752
+ await reporting.thermostatRunningState(endpoint, {min: 1, max: 0xFFFF});
753
+ try {
754
+ await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
755
+ } catch (error) {/* Do nothing */}
756
+ },
757
+ },
758
+ {
759
+ zigbeeModel: ['TH1300ZB'],
760
+ model: 'TH1300ZB',
761
+ vendor: 'Sinopé',
762
+ description: 'Zigbee smart floor heating thermostat',
763
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
764
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
765
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
766
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
767
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
768
+ tzLocal.thermostat_occupancy, tzLocal.floor_control_mode, tzLocal.ambiant_max_heat_setpoint, tzLocal.floor_min_heat_setpoint,
769
+ tzLocal.floor_max_heat_setpoint, tzLocal.temperature_sensor, tz.electrical_measurement_power],
770
+ exposes: [
771
+ exposes.climate()
772
+ .withSetpoint('occupied_heating_setpoint', 5, 36, 0.5)
773
+ .withSetpoint('unoccupied_heating_setpoint', 5, 36, 0.5)
774
+ .withLocalTemperature()
775
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
776
+ .withPiHeatingDemand()
777
+ .withRunningState(['idle', 'heat'], ea.STATE),
778
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
779
+ .withDescription('Occupancy state of the thermostat'),
780
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
781
+ .withDescription('Showing outdoor temperature on secondary display'),
782
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
783
+ .withDescription('The temperature format displayed on the thermostat screen'),
784
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
785
+ .withDescription('The time format featured on the thermostat display'),
786
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
787
+ .withDescription('Control backlight dimming behavior'),
788
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
789
+ .withDescription('Enables or disables the device’s buttons'),
790
+ e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy()],
791
+ configure: async (device, coordinatorEndpoint, logger) => {
792
+ const endpoint = device.getEndpoint(1);
793
+ const binds = [
794
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
795
+ 'haElectricalMeasurement', 'msTemperatureMeasurement', 'seMetering', 'manuSpecificSinope'];
796
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
797
+ await reporting.thermostatTemperature(endpoint);
798
+ await reporting.thermostatPIHeatingDemand(endpoint);
799
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
800
+ try {
801
+ await reporting.readMeteringMultiplierDivisor(endpoint);
802
+ } catch (error) {/* Do nothing*/}
803
+ try {
804
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
805
+ } catch (error) {/* Do nothing*/}
806
+ try {
807
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
808
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
809
+ } catch (error) {
810
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {'acPowerMultiplier': 1, 'acPowerDivisor': 1});
811
+ }
812
+ try {
813
+ await endpoint.read('haElectricalMeasurement', ['acCurrentMultiplier', 'acCurrentDivisor']);
814
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
815
+ } catch (error) {/* Do nothing*/}
816
+ try {
817
+ await endpoint.read('haElectricalMeasurement', ['acVoltageMultiplier', 'acVoltageDivisor']);
818
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
819
+ } catch (error) {/* Do nothing*/}
820
+
821
+ try {
822
+ await reporting.thermostatKeypadLockMode(endpoint);
823
+ } catch (error) {
824
+ // Not all support this: https://github.com/Koenkk/zigbee2mqtt/issues/3760
825
+ }
826
+
827
+ await endpoint.configureReporting('manuSpecificSinope', [{attribute: 'GFCiStatus', minimumReportInterval: 1,
828
+ maximumReportInterval: constants.repInterval.HOUR, reportableChange: 1}]);
829
+ await endpoint.configureReporting('manuSpecificSinope', [{attribute: 'floorLimitStatus', minimumReportInterval: 1,
830
+ maximumReportInterval: constants.repInterval.HOUR, reportableChange: 1}]);
831
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // disable reporting
832
+ },
833
+ },
834
+ {
835
+ zigbeeModel: ['TH1400ZB'],
836
+ model: 'TH1400ZB',
837
+ vendor: 'Sinopé',
838
+ description: 'Zigbee low volt thermostat',
839
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
840
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
841
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
842
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
843
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
844
+ tzLocal.thermostat_occupancy, tzLocal.floor_control_mode, tzLocal.ambiant_max_heat_setpoint, tzLocal.floor_min_heat_setpoint,
845
+ tzLocal.floor_max_heat_setpoint, tzLocal.temperature_sensor, tz.thermostat_min_heat_setpoint_limit,
846
+ tz.thermostat_max_heat_setpoint_limit, tzLocal.connected_load, tzLocal.aux_connected_load, tzLocal.main_cycle_output,
847
+ tzLocal.aux_cycle_output, tzLocal.pump_protection],
848
+ exposes: [
849
+ exposes.climate()
850
+ .withSetpoint('occupied_heating_setpoint', 5, 36, 0.5)
851
+ .withSetpoint('unoccupied_heating_setpoint', 5, 36, 0.5)
852
+ .withLocalTemperature()
853
+ .withSystemMode(['off', 'heat'])
854
+ .withPiHeatingDemand()
855
+ .withRunningState(['idle', 'heat'], ea.STATE),
856
+ e.max_heat_setpoint_limit(5, 36, 0.5),
857
+ e.min_heat_setpoint_limit(5, 36, 0.5),
858
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
859
+ .withDescription('Occupancy state of the thermostat'),
860
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
861
+ .withDescription('Showing outdoor temperature on secondary display'),
862
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
863
+ .withDescription('The temperature format displayed on the thermostat screen'),
864
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
865
+ .withDescription('The time format featured on the thermostat display'),
866
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
867
+ .withDescription('The display backlight behavior'),
868
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
869
+ .withDescription('Enables or disables the device’s buttons'),
870
+ exposes.numeric('connected_load', ea.ALL)
871
+ .withUnit('W').withValueMin(1).withValueMax(20000)
872
+ .withDescription('The power in watts of the electrical load connected to the device'),
873
+ exposes.enum('floor_control_mode', ea.ALL, ['ambiant', 'floor'])
874
+ .withDescription('Control mode using floor or ambient temperature'),
875
+ exposes.numeric('floor_max_heat_setpoint', ea.ALL)
876
+ .withUnit('°C').withValueMin(7).withValueMax(36).withValueStep(0.5)
877
+ .withPreset('off', 'off', 'Use minimum permitted value')
878
+ .withDescription('The maximum floor temperature limit of the floor when in ambient control mode'),
879
+ exposes.numeric('floor_min_heat_setpoint', ea.ALL)
880
+ .withUnit('°C').withValueMin(5).withValueMax(34).withValueStep(0.5)
881
+ .withPreset('off', 'off', 'Use minimum permitted value')
882
+ .withDescription('The minimum floor temperature limit of the floor when in ambient control mode'),
883
+ exposes.numeric('ambiant_max_heat_setpoint', ea.ALL)
884
+ .withUnit('°C').withValueMin(5).withValueMax(36).withValueStep(0.5)
885
+ .withPreset('off', 'off', 'Use minimum permitted value')
886
+ .withDescription('The maximum ambient temperature limit when in floor control mode'),
887
+ exposes.enum('floor_temperature_sensor', ea.ALL, ['10k', '12k'])
888
+ .withDescription('The floor sensor'),
889
+ exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '5_min', '10_min', '15_min', '20_min', '30_min'])
890
+ .withDescription('The length of the control cycle according to the type of load connected to the thermostats'),
891
+ exposes.enum('aux_cycle_output', ea.ALL, ['off', '15_sec', '5_min', '10_min', '15_min', '20_min', '30_min'])
892
+ .withDescription('The length of the control cycle according to the type of auxiliary load connected to the thermostats'),
893
+ exposes.binary('pump_protection', ea.ALL, 'ON', 'OFF')
894
+ .withDescription('This function prevents the seizure of the pump'),
895
+ exposes.numeric('aux_connected_load', ea.ALL)
896
+ .withUnit('W').withValueMin(0).withValueMax(20000)
897
+ .withDescription('The power in watts of the heater connected to the auxiliary output of the thermostat'),
898
+ ],
899
+
900
+ configure: async (device, coordinatorEndpoint, logger) => {
901
+ const endpoint = device.getEndpoint(1);
902
+ const binds = [
903
+ 'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat',
904
+ 'hvacUserInterfaceCfg', 'msTemperatureMeasurement', 'manuSpecificSinope'];
905
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
906
+ await reporting.thermostatTemperature(endpoint);
907
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
908
+ await reporting.thermostatPIHeatingDemand(endpoint);
909
+
910
+ try {
911
+ await reporting.thermostatSystemMode(endpoint);
912
+ } catch (error) {/* Not all support this */}
913
+
914
+ await endpoint.read('hvacThermostat', ['occupiedHeatingSetpoint', 'localTemp', 'systemMode', 'pIHeatingDemand',
915
+ 'SinopeBacklight', 'maxHeatSetpointLimit', 'minHeatSetpointLimit', 'SinopeMainCycleOutput', 'SinopeAuxCycleOutput']);
916
+ await endpoint.read('hvacUserInterfaceCfg', ['keypadLockout', 'tempDisplayMode']);
917
+ await endpoint.read('manuSpecificSinope', ['timeFormatToDisplay', 'connectedLoad', 'auxConnectedLoad', 'floorControlMode',
918
+ 'floorMinHeatSetpointLimit', 'floorMaxHeatSetpointLimit', 'ambiantMaxHeatSetpointLimit', 'outdoorTempToDisplayTimeout',
919
+ 'temperatureSensor', 'pumpProtection']);
920
+
921
+ await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // disable reporting
922
+ },
923
+ },
924
+ {
925
+ zigbeeModel: ['TH1500ZB'],
926
+ model: 'TH1500ZB',
927
+ vendor: 'Sinopé',
928
+ description: 'Zigbee dual pole line volt thermostat',
929
+ fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.legacy.hvac_user_interface,
930
+ fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
931
+ toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
932
+ tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim,
933
+ tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.outdoor_temperature,
934
+ tzLocal.thermostat_occupancy],
935
+ exposes: [
936
+ exposes.climate()
937
+ .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
938
+ .withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
939
+ .withLocalTemperature()
940
+ .withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
941
+ .withPiHeatingDemand()
942
+ .withRunningState(['idle', 'heat'], ea.STATE),
943
+ exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
944
+ .withDescription('Occupancy state of the thermostat'),
945
+ exposes.binary('enable_outdoor_temperature', ea.ALL, 'ON', 'OFF')
946
+ .withDescription('Showing outdoor temperature on secondary display'),
947
+ exposes.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
948
+ .withDescription('The temperature format displayed on the thermostat screen'),
949
+ exposes.enum('time_format', ea.ALL, ['24h', '12h'])
950
+ .withDescription('The time format featured on the thermostat display'),
951
+ exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
952
+ .withDescription('Control backlight dimming behavior'),
953
+ exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
954
+ .withDescription('Enables or disables the device’s buttons'),
955
+ ],
956
+
957
+ configure: async (device, coordinatorEndpoint, logger) => {
958
+ const endpoint = device.getEndpoint(1);
959
+ const binds = [
960
+ 'genBasic', 'genIdentify', 'genGroups',
961
+ 'hvacThermostat', 'hvacUserInterfaceCfg', 'msTemperatureMeasurement'];
962
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
963
+ await reporting.thermostatTemperature(endpoint);
964
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
965
+ await reporting.thermostatPIHeatingDemand(endpoint);
966
+ },
967
+ },
968
+ {
969
+ zigbeeModel: ['SW2500ZB'],
970
+ model: 'SW2500ZB',
971
+ vendor: 'Sinopé',
972
+ description: 'Zigbee smart light switch',
973
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fzLocal.sinope],
974
+ toZigbee: [tz.on_off, tzLocal.timer_seconds, tzLocal.led_intensity_on, tzLocal.led_intensity_off,
975
+ tzLocal.led_color_on, tzLocal.led_color_off],
976
+ exposes: [e.switch(),
977
+ exposes.numeric('timer_seconds', ea.ALL).withValueMin(0).withValueMax(10800)
978
+ .withDescription('Automatically turn off load after x seconds'),
979
+ exposes.numeric('led_intensity_on', ea.ALL).withValueMin(0).withValueMax(100)
980
+ .withDescription('Control status LED intensity when load ON'),
981
+ exposes.numeric('led_intensity_off', ea.ALL).withValueMin(0).withValueMax(100)
982
+ .withDescription('Control status LED intensity when load OFF'),
983
+ exposes.composite('led_color_on', 'led_color_on', ea.SET)
984
+ .withFeature(exposes.numeric('r', ea.SET))
985
+ .withFeature(exposes.numeric('g', ea.SET))
986
+ .withFeature(exposes.numeric('b', ea.SET))
987
+ .withDescription('Control status LED color when load ON'),
988
+ exposes.composite('led_color_off', 'led_color_off', ea.SET)
989
+ .withFeature(exposes.numeric('r', ea.SET))
990
+ .withFeature(exposes.numeric('g', ea.SET))
991
+ .withFeature(exposes.numeric('b', ea.SET))
992
+ .withDescription('Control status LED color when load OFF')],
993
+ configure: async (device, coordinatorEndpoint, logger) => {
994
+ const endpoint = device.getEndpoint(1);
995
+ const binds = ['genOnOff'];
996
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
997
+ await reporting.onOff(endpoint);
998
+ },
999
+ },
1000
+ {
1001
+ zigbeeModel: ['DM2500ZB'],
1002
+ model: 'DM2500ZB',
1003
+ vendor: 'Sinopé',
1004
+ description: 'Zigbee smart dimmer',
1005
+ fromZigbee: [fz.on_off, fz.brightness, fz.electrical_measurement, fzLocal.sinope],
1006
+ toZigbee: [tz.light_onoff_brightness, tzLocal.timer_seconds, tzLocal.led_intensity_on, tzLocal.led_intensity_off,
1007
+ tzLocal.minimum_brightness, tzLocal.led_color_on, tzLocal.led_color_off],
1008
+ exposes: [e.light_brightness(),
1009
+ exposes.numeric('timer_seconds', ea.ALL).withValueMin(0).withValueMax(10800)
1010
+ .withDescription('Automatically turn off load after x seconds'),
1011
+ exposes.numeric('led_intensity_on', ea.ALL).withValueMin(0).withValueMax(100)
1012
+ .withDescription('Control status LED when load ON'),
1013
+ exposes.numeric('led_intensity_off', ea.ALL).withValueMin(0).withValueMax(100)
1014
+ .withDescription('Control status LED when load OFF'),
1015
+ exposes.numeric('minimum_brightness', ea.ALL).withValueMin(0).withValueMax(3000)
1016
+ .withDescription('Control minimum dimmer brightness'),
1017
+ exposes.composite('led_color_on', 'led_color_on', ea.SET)
1018
+ .withFeature(exposes.numeric('r', ea.SET))
1019
+ .withFeature(exposes.numeric('g', ea.SET))
1020
+ .withFeature(exposes.numeric('b', ea.SET))
1021
+ .withDescription('Control status LED color when load ON'),
1022
+ exposes.composite('led_color_off', 'led_color_off', ea.SET)
1023
+ .withFeature(exposes.numeric('r', ea.SET))
1024
+ .withFeature(exposes.numeric('g', ea.SET))
1025
+ .withFeature(exposes.numeric('b', ea.SET))
1026
+ .withDescription('Control status LED color when load OFF')],
1027
+ configure: async (device, coordinatorEndpoint, logger) => {
1028
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
1029
+ const endpoint = device.getEndpoint(1);
1030
+ const binds = ['genBasic', 'genLevelCtrl'];
1031
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1032
+ await reporting.onOff(endpoint);
1033
+ await reporting.brightness(endpoint);
1034
+ },
1035
+ },
1036
+ {
1037
+ zigbeeModel: ['DM2550ZB'],
1038
+ model: 'DM2550ZB',
1039
+ vendor: 'Sinopé',
1040
+ description: 'Zigbee Adaptive phase smart dimmer',
1041
+ fromZigbee: [fz.on_off, fz.brightness, fz.electrical_measurement, fzLocal.sinope],
1042
+ toZigbee: [tz.light_onoff_brightness, tzLocal.timer_seconds, tzLocal.led_intensity_on, tzLocal.led_intensity_off,
1043
+ tzLocal.minimum_brightness, tzLocal.led_color_on, tzLocal.led_color_off],
1044
+ exposes: [e.light_brightness(),
1045
+ exposes.numeric('timer_seconds', ea.ALL).withValueMin(0).withValueMax(10800)
1046
+ .withDescription('Automatically turn off load after x seconds'),
1047
+ exposes.numeric('led_intensity_on', ea.ALL).withValueMin(0).withValueMax(100)
1048
+ .withDescription('Control status LED when load ON'),
1049
+ exposes.numeric('led_intensity_off', ea.ALL).withValueMin(0).withValueMax(100)
1050
+ .withDescription('Control status LED when load OFF'),
1051
+ exposes.numeric('minimum_brightness', ea.ALL).withValueMin(0).withValueMax(3000)
1052
+ .withDescription('Control minimum dimmer brightness'),
1053
+ exposes.composite('led_color_on', 'led_color_on', ea.SET)
1054
+ .withFeature(exposes.numeric('r', ea.SET))
1055
+ .withFeature(exposes.numeric('g', ea.SET))
1056
+ .withFeature(exposes.numeric('b', ea.SET))
1057
+ .withDescription('Control status LED color when load ON'),
1058
+ exposes.composite('led_color_off', 'led_color_off', ea.SET)
1059
+ .withFeature(exposes.numeric('r', ea.SET))
1060
+ .withFeature(exposes.numeric('g', ea.SET))
1061
+ .withFeature(exposes.numeric('b', ea.SET))
1062
+ .withDescription('Control status LED color when load OFF')],
1063
+ configure: async (device, coordinatorEndpoint, logger) => {
1064
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
1065
+ const endpoint = device.getEndpoint(1);
1066
+ const binds = ['genBasic', 'genLevelCtrl'];
1067
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1068
+ await reporting.onOff(endpoint);
1069
+ await reporting.brightness(endpoint);
1070
+ },
1071
+ },
1072
+ {
1073
+ zigbeeModel: ['SP2600ZB'],
1074
+ model: 'SP2600ZB',
1075
+ vendor: 'Sinopé',
1076
+ description: 'Zigbee smart plug',
1077
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
1078
+ toZigbee: [tz.on_off, tz.frequency],
1079
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy()],
1080
+ configure: async (device, coordinatorEndpoint, logger) => {
1081
+ const endpoint = device.getEndpoint(1);
1082
+ const binds = ['genBasic', 'genIdentify', 'genOnOff', 'haElectricalMeasurement', 'seMetering'];
1083
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1084
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
1085
+ await reporting.onOff(endpoint);
1086
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 10 : 0.1W
1087
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 10}); // divider 100: 0.1Arms
1088
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 10}); // divider 100: 0.1Vrms
1089
+ endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 1000, multiplier: 1});
1090
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [0, 1]}); // divider 1
1091
+ },
1092
+ },
1093
+ {
1094
+ zigbeeModel: ['SP2610ZB'],
1095
+ model: 'SP2610ZB',
1096
+ vendor: 'Sinopé',
1097
+ description: 'Zigbee smart plug',
1098
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
1099
+ toZigbee: [tz.on_off, tz.frequency],
1100
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy()],
1101
+ configure: async (device, coordinatorEndpoint, logger) => {
1102
+ const endpoint = device.getEndpoint(1);
1103
+ const binds = ['genBasic', 'genIdentify', 'genOnOff', 'haElectricalMeasurement', 'seMetering'];
1104
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1105
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
1106
+ await reporting.onOff(endpoint);
1107
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 10 : 0.1W
1108
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 10}); // divider 100: 0.1Arms
1109
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 10}); // divider 100: 0.1Vrms
1110
+ endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 1000, multiplier: 1});
1111
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [0, 1]}); // divider 1
1112
+ },
1113
+ },
1114
+ {
1115
+ zigbeeModel: ['RM3250ZB'],
1116
+ model: 'RM3250ZB',
1117
+ vendor: 'Sinopé',
1118
+ description: '50A Smart electrical load controller',
1119
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
1120
+ toZigbee: [tz.on_off],
1121
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy()],
1122
+ configure: async (device, coordinatorEndpoint) => {
1123
+ const endpoint = device.getEndpoint(1);
1124
+ const binds = ['genOnOff', 'haElectricalMeasurement', 'seMetering'];
1125
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1126
+ await reporting.onOff(endpoint);
1127
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
1128
+ await reporting.activePower(endpoint);
1129
+ await reporting.rmsCurrent(endpoint);
1130
+ await reporting.rmsVoltage(endpoint);
1131
+ await reporting.readMeteringMultiplierDivisor(endpoint);
1132
+ await reporting.currentSummDelivered(endpoint);
1133
+ },
1134
+ },
1135
+ {
1136
+ zigbeeModel: ['WL4200'],
1137
+ model: 'WL4200',
1138
+ vendor: 'Sinopé',
1139
+ description: 'Zigbee smart water leak detector',
1140
+ fromZigbee: [fz.ias_water_leak_alarm_1, fz.temperature, fz.battery],
1141
+ exposes: [e.water_leak(), e.battery_low(), e.temperature(), e.battery()],
1142
+ toZigbee: [],
1143
+ configure: async (device, coordinatorEndpoint) => {
1144
+ const endpoint = device.getEndpoint(1);
1145
+ const binds = ['genPowerCfg', 'msTemperatureMeasurement'];
1146
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1147
+ await reporting.temperature(endpoint, {min: 600, max: constants.repInterval.MAX, change: 100});
1148
+ await reporting.batteryPercentageRemaining(endpoint);
1149
+ await reporting.batteryAlarmState(endpoint);
1150
+ },
1151
+ },
1152
+ {
1153
+ zigbeeModel: ['WL4200S'],
1154
+ model: 'WL4200S',
1155
+ vendor: 'Sinopé',
1156
+ description: 'Zigbee smart water leak detector with external sensor',
1157
+ fromZigbee: [fz.ias_water_leak_alarm_1, fz.temperature, fz.battery],
1158
+ toZigbee: [],
1159
+ exposes: [e.water_leak(), e.battery_low(), e.temperature(), e.battery()],
1160
+ configure: async (device, coordinatorEndpoint) => {
1161
+ const endpoint = device.getEndpoint(1);
1162
+ const binds = ['genPowerCfg', 'msTemperatureMeasurement'];
1163
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1164
+ await reporting.temperature(endpoint, {min: 600, max: constants.repInterval.MAX, change: 100});
1165
+ await reporting.batteryPercentageRemaining(endpoint);
1166
+ await reporting.batteryAlarmState(endpoint);
1167
+ },
1168
+ },
1169
+ {
1170
+ zigbeeModel: ['VA4200WZ'],
1171
+ model: 'VA4200WZ',
1172
+ vendor: 'Sinopé',
1173
+ description: 'Zigbee smart water valve (3/4")',
1174
+ fromZigbee: [fz.cover_position_via_brightness, fz.cover_state_via_onoff, fz.battery],
1175
+ toZigbee: [tz.cover_via_brightness],
1176
+ exposes: [e.valve_switch(), e.valve_position(), e.battery_low(), e.battery()],
1177
+ configure: async (device, coordinatorEndpoint) => {
1178
+ const endpoint = device.getEndpoint(1);
1179
+ const binds = ['genOnOff', 'genLevelCtrl', 'genPowerCfg'];
1180
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1181
+ await reporting.batteryPercentageRemaining(endpoint);
1182
+ await reporting.onOff(endpoint);
1183
+ await reporting.brightness(endpoint); // valve position
1184
+ },
1185
+ },
1186
+ {
1187
+ zigbeeModel: ['VA4201WZ'],
1188
+ model: 'VA4201WZ',
1189
+ vendor: 'Sinopé',
1190
+ description: 'Zigbee smart water valve (1")',
1191
+ fromZigbee: [fz.cover_position_via_brightness, fz.cover_state_via_onoff, fz.battery],
1192
+ toZigbee: [tz.cover_via_brightness],
1193
+ exposes: [e.valve_switch(), e.valve_position(), e.battery_low(), e.battery()],
1194
+ configure: async (device, coordinatorEndpoint) => {
1195
+ const endpoint = device.getEndpoint(1);
1196
+ const binds = ['genOnOff', 'genLevelCtrl', 'genPowerCfg'];
1197
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1198
+ await reporting.batteryPercentageRemaining(endpoint);
1199
+ await reporting.onOff(endpoint);
1200
+ await reporting.brightness(endpoint); // valve position
1201
+ },
1202
+ },
1203
+ {
1204
+ zigbeeModel: ['VA4220ZB'],
1205
+ model: 'VA4220ZB',
1206
+ vendor: 'Sinopé',
1207
+ description: 'Sedna smart water valve',
1208
+ fromZigbee: [fz.ignore_iaszone_statuschange, fz.cover_position_via_brightness, fz.cover_state_via_onoff,
1209
+ fz.battery, fz.metering],
1210
+ toZigbee: [tz.cover_via_brightness],
1211
+ meta: {battery: {voltageToPercentage: {min: 5400, max: 6800}}},
1212
+ exposes: [e.valve_switch(), e.valve_position(), e.battery_low(), e.battery(), e.battery_voltage()],
1213
+ configure: async (device, coordinatorEndpoint) => {
1214
+ const endpoint = device.getEndpoint(1);
1215
+ const binds = [
1216
+ 'genBasic', 'genGroups', 'genOnOff', 'ssIasZone', 'genLevelCtrl',
1217
+ 'genPowerCfg', 'seMetering', 'manuSpecificSinope'];
1218
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1219
+ await reporting.batteryPercentageRemaining(endpoint);
1220
+ await reporting.onOff(endpoint);
1221
+ await reporting.brightness(endpoint); // valve position
1222
+ try {
1223
+ await reporting.batteryVoltage(endpoint);
1224
+ } catch (error) {/* Do Nothing */}
1225
+ try {
1226
+ await reporting.batteryAlarmState(endpoint);
1227
+ } catch (error) {/* Do Nothing */}
1228
+ },
1229
+ },
1230
+ {
1231
+ zigbeeModel: ['RM3500ZB'],
1232
+ model: 'RM3500ZB',
1233
+ vendor: 'Sinopé',
1234
+ description: 'Calypso smart water heater controller',
1235
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fzLocal.ias_water_leak_alarm,
1236
+ fzLocal.sinope, fz.temperature],
1237
+ toZigbee: [tz.on_off],
1238
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.water_leak(), e.temperature()],
1239
+ configure: async (device, coordinatorEndpoint) => {
1240
+ const endpoint = device.getEndpoint(1);
1241
+ const binds = ['genOnOff', 'haElectricalMeasurement', 'seMetering', 'msTemperatureMeasurement', 'ssIasZone',
1242
+ 'manuSpecificSinope'];
1243
+ await reporting.bind(endpoint, coordinatorEndpoint, binds);
1244
+ await reporting.onOff(endpoint);
1245
+ await reporting.temperature(endpoint, {min: 10, max: 301, change: 10}); // divider 100: 0.1C
1246
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
1247
+ await reporting.activePower(endpoint, {min: 10, max: 305, change: 2}); // divider 1 : 2W
1248
+ await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 10}); // divider 1000: 0.01Arms
1249
+ await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 1}); // divider 1: 1Vrms
1250
+ await reporting.readMeteringMultiplierDivisor(endpoint);
1251
+ await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [10, 10]}); // divider 1000: 0,01kWh
1252
+
1253
+ await endpoint.configureReporting('ssIasZone', [{attribute: 'zoneStatus', minimumReportInterval: 1,
1254
+ maximumReportInterval: constants.repInterval.HOUR, reportableChange: 1}]);
1255
+ },
1256
+ },
1257
+ ];