homebridge-easy-mqtt 1.4.1-beta.1 → 1.5.0-beta.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 (137) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +86 -5
  3. package/config.schema.json +76 -1
  4. package/dist/accessory/abstract/base.d.ts +2 -2
  5. package/dist/accessory/abstract/base.js +5 -25
  6. package/dist/accessory/abstract/base.js.map +1 -1
  7. package/dist/accessory/abstract/common.d.ts +17 -6
  8. package/dist/accessory/abstract/common.js +136 -28
  9. package/dist/accessory/abstract/common.js.map +1 -1
  10. package/dist/accessory/abstract/mqtt.d.ts +2 -0
  11. package/dist/accessory/abstract/mqtt.js +13 -12
  12. package/dist/accessory/abstract/mqtt.js.map +1 -1
  13. package/dist/accessory/addons/filter.d.ts +0 -1
  14. package/dist/accessory/addons/filter.js +1 -13
  15. package/dist/accessory/addons/filter.js.map +1 -1
  16. package/dist/accessory/characteristic/characteristic.d.ts +3 -0
  17. package/dist/accessory/characteristic/characteristic.js +23 -0
  18. package/dist/accessory/characteristic/characteristic.js.map +1 -0
  19. package/dist/accessory/characteristic/custom.d.ts +1 -0
  20. package/dist/accessory/characteristic/custom.js +3 -0
  21. package/dist/accessory/characteristic/custom.js.map +1 -1
  22. package/dist/accessory/characteristic/eve.d.ts +6 -0
  23. package/dist/accessory/characteristic/eve.js +24 -0
  24. package/dist/accessory/characteristic/eve.js.map +1 -0
  25. package/dist/accessory/climate/active.d.ts +0 -4
  26. package/dist/accessory/climate/active.js +4 -36
  27. package/dist/accessory/climate/active.js.map +1 -1
  28. package/dist/accessory/climate/fan2.d.ts +1 -12
  29. package/dist/accessory/climate/fan2.js +22 -101
  30. package/dist/accessory/climate/fan2.js.map +1 -1
  31. package/dist/accessory/climate/heaterCooler.d.ts +1 -11
  32. package/dist/accessory/climate/heaterCooler.js +24 -100
  33. package/dist/accessory/climate/heaterCooler.js.map +1 -1
  34. package/dist/accessory/climate/purifier.d.ts +1 -11
  35. package/dist/accessory/climate/purifier.js +20 -90
  36. package/dist/accessory/climate/purifier.js.map +1 -1
  37. package/dist/accessory/climate/temperatureControl.d.ts +2 -1
  38. package/dist/accessory/climate/temperatureControl.js +3 -3
  39. package/dist/accessory/climate/temperatureControl.js.map +1 -1
  40. package/dist/accessory/climate/thermostat.d.ts +1 -9
  41. package/dist/accessory/climate/thermostat.js +24 -77
  42. package/dist/accessory/climate/thermostat.js.map +1 -1
  43. package/dist/accessory/garage.d.ts +1 -9
  44. package/dist/accessory/garage.js +19 -85
  45. package/dist/accessory/garage.js.map +1 -1
  46. package/dist/accessory/lock.d.ts +1 -5
  47. package/dist/accessory/lock.js +23 -57
  48. package/dist/accessory/lock.js.map +1 -1
  49. package/dist/accessory/onoff/lightbulb.d.ts +1 -2
  50. package/dist/accessory/onoff/lightbulb.js +7 -13
  51. package/dist/accessory/onoff/lightbulb.js.map +1 -1
  52. package/dist/accessory/onoff/onoff.d.ts +2 -4
  53. package/dist/accessory/onoff/onoff.js +28 -32
  54. package/dist/accessory/onoff/onoff.js.map +1 -1
  55. package/dist/accessory/onoff/outlet.d.ts +1 -1
  56. package/dist/accessory/onoff/outlet.js +3 -3
  57. package/dist/accessory/onoff/outlet.js.map +1 -1
  58. package/dist/accessory/onoff/switch.d.ts +1 -2
  59. package/dist/accessory/onoff/switch.js +0 -3
  60. package/dist/accessory/onoff/switch.js.map +1 -1
  61. package/dist/accessory/position/blind.d.ts +1 -2
  62. package/dist/accessory/position/blind.js +5 -11
  63. package/dist/accessory/position/blind.js.map +1 -1
  64. package/dist/accessory/position/position.d.ts +0 -7
  65. package/dist/accessory/position/position.js +11 -62
  66. package/dist/accessory/position/position.js.map +1 -1
  67. package/dist/accessory/security.d.ts +3 -7
  68. package/dist/accessory/security.js +37 -69
  69. package/dist/accessory/security.js.map +1 -1
  70. package/dist/accessory/sensor/air.d.ts +1 -5
  71. package/dist/accessory/sensor/air.js +14 -46
  72. package/dist/accessory/sensor/air.js.map +1 -1
  73. package/dist/accessory/sensor/carbonDioxide.d.ts +1 -1
  74. package/dist/accessory/sensor/carbonDioxide.js +3 -3
  75. package/dist/accessory/sensor/carbonDioxide.js.map +1 -1
  76. package/dist/accessory/sensor/carbonMonoxide.d.ts +1 -1
  77. package/dist/accessory/sensor/carbonMonoxide.js +3 -3
  78. package/dist/accessory/sensor/carbonMonoxide.js.map +1 -1
  79. package/dist/accessory/sensor/contact.d.ts +1 -1
  80. package/dist/accessory/sensor/contact.js +18 -5
  81. package/dist/accessory/sensor/contact.js.map +1 -1
  82. package/dist/accessory/sensor/humidity.d.ts +1 -1
  83. package/dist/accessory/sensor/humidity.js +7 -4
  84. package/dist/accessory/sensor/humidity.js.map +1 -1
  85. package/dist/accessory/sensor/leak.d.ts +1 -1
  86. package/dist/accessory/sensor/leak.js +3 -3
  87. package/dist/accessory/sensor/leak.js.map +1 -1
  88. package/dist/accessory/sensor/light.d.ts +1 -1
  89. package/dist/accessory/sensor/light.js +3 -3
  90. package/dist/accessory/sensor/light.js.map +1 -1
  91. package/dist/accessory/sensor/motion.d.ts +1 -1
  92. package/dist/accessory/sensor/motion.js +7 -4
  93. package/dist/accessory/sensor/motion.js.map +1 -1
  94. package/dist/accessory/sensor/occupancy.d.ts +1 -1
  95. package/dist/accessory/sensor/occupancy.js +3 -3
  96. package/dist/accessory/sensor/occupancy.js.map +1 -1
  97. package/dist/accessory/sensor/sensor.d.ts +0 -2
  98. package/dist/accessory/sensor/sensor.js +2 -26
  99. package/dist/accessory/sensor/sensor.js.map +1 -1
  100. package/dist/accessory/sensor/smoke.d.ts +1 -1
  101. package/dist/accessory/sensor/smoke.js +3 -3
  102. package/dist/accessory/sensor/smoke.js.map +1 -1
  103. package/dist/accessory/sensor/temperature.d.ts +1 -1
  104. package/dist/accessory/sensor/temperature.js +7 -4
  105. package/dist/accessory/sensor/temperature.js.map +1 -1
  106. package/dist/accessory/valve.d.ts +1 -5
  107. package/dist/accessory/valve.js +7 -41
  108. package/dist/accessory/valve.js.map +1 -1
  109. package/dist/homebridge/platform.js +8 -4
  110. package/dist/homebridge/platform.js.map +1 -1
  111. package/dist/homebridge-ui/public/index.html +1 -1
  112. package/dist/homebridge-ui/public/ui.js +1 -1
  113. package/dist/i18n/en.d.ts +19 -5
  114. package/dist/i18n/en.js +20 -6
  115. package/dist/i18n/en.js.map +1 -1
  116. package/dist/i18n/fr.d.ts +19 -5
  117. package/dist/i18n/i18n.d.ts +19 -5
  118. package/dist/i18n/it.d.ts +19 -5
  119. package/dist/i18n/ro.d.ts +19 -5
  120. package/dist/i18n/template.d.ts +19 -5
  121. package/dist/i18n/vi.d.ts +19 -5
  122. package/dist/model/enums.d.ts +12 -1
  123. package/dist/model/enums.js +12 -0
  124. package/dist/model/enums.js.map +1 -1
  125. package/dist/model/history.d.ts +32 -0
  126. package/dist/model/history.js +128 -0
  127. package/dist/model/history.js.map +1 -0
  128. package/dist/model/mqtt.d.ts +1 -0
  129. package/dist/model/mqtt.js +37 -11
  130. package/dist/model/mqtt.js.map +1 -1
  131. package/dist/model/types.d.ts +15 -3
  132. package/lib/fakegato-history/fakegato-history.js +887 -0
  133. package/lib/fakegato-history/fakegato-storage.js +178 -0
  134. package/lib/fakegato-history/fakegato-timer.js +133 -0
  135. package/lib/fakegato-history/lib/uuid.js +25 -0
  136. package/lib/fakegato-history/package.json +5 -0
  137. package/package.json +2 -1
@@ -0,0 +1,887 @@
1
+ /*jshint esversion: 6,node: true,-W041: false */
2
+ 'use strict';
3
+
4
+ const Format = require('util').format;
5
+ const FakeGatoTimer = require('./fakegato-timer').FakeGatoTimer;
6
+ const FakeGatoStorage = require('./fakegato-storage').FakeGatoStorage;
7
+
8
+ const EPOCH_OFFSET = 978307200;
9
+
10
+ const TYPE_ENERGY = 'energy',
11
+ TYPE_ROOM = 'room',
12
+ TYPE_ROOM2 = 'room2',
13
+ TYPE_WEATHER = 'weather',
14
+ TYPE_DOOR = 'door',
15
+ TYPE_MOTION = 'motion',
16
+ TYPE_SWITCH = 'switch',
17
+ TYPE_THERMO = 'thermo',
18
+ TYPE_AQUA = 'aqua',
19
+ TYPE_CUSTOM = 'custom';
20
+
21
+ var homebridge;
22
+ var Characteristic, Service, Formats, Perms;
23
+
24
+ module.exports = function (pHomebridge) {
25
+ if (pHomebridge && !homebridge) {
26
+ homebridge = pHomebridge;
27
+ Characteristic = homebridge.hap.Characteristic;
28
+ Service = homebridge.hap.Service;
29
+ Formats = homebridge.hap.Formats;
30
+ Perms = homebridge.hap.Perms;
31
+ }
32
+
33
+
34
+ var hexToBase64 = function (val) {
35
+ return Buffer.from(('' + val).replace(/[^0-9A-F]/ig, ''), 'hex').toString('base64');
36
+ },
37
+ base64ToHex = function (val) {
38
+ if (!val)
39
+ return val;
40
+ return Buffer.from(val, 'base64').toString('hex');
41
+ },
42
+ swap16 = function (val) {
43
+ return ((val & 0xFF) << 8)
44
+ | ((val >>> 8) & 0xFF);
45
+ },
46
+ swap32 = function (val) {
47
+ return ((val & 0xFF) << 24)
48
+ | ((val & 0xFF00) << 8)
49
+ | ((val >>> 8) & 0xFF00)
50
+ | ((val >>> 24) & 0xFF);
51
+ },
52
+ hexToHPA = function (val) { //unused
53
+ return parseInt(swap16(val), 10);
54
+ },
55
+ hPAtoHex = function (val) { //unused
56
+ return swap16(Math.round(val)).toString(16);
57
+ },
58
+ numToHex = function (val, len) {
59
+ var s = Number(val >>> 0).toString(16);
60
+ if (s.length % 2 != 0) {
61
+ s = '0' + s;
62
+ }
63
+ if (len) {
64
+ return ('0000000000000' + s).slice(-1 * len);
65
+ }
66
+ return s;
67
+ },
68
+ ucfirst = function (val) {
69
+ return val.charAt(0).toUpperCase() + val.substr(1);
70
+ },
71
+ precisionRound = function (number, precision) {
72
+ var factor = Math.pow(10, precision);
73
+ return Math.round(number * factor) / factor;
74
+ };
75
+
76
+ class S2R1Characteristic extends Characteristic {
77
+ constructor() {
78
+ super('S2R1', S2R1Characteristic.UUID);
79
+ this.setProps({
80
+ format: Formats.DATA,
81
+ perms: [
82
+ Perms.PAIRED_READ, Perms.NOTIFY, Perms.HIDDEN
83
+ ]
84
+ });
85
+ }
86
+ }
87
+
88
+ S2R1Characteristic.UUID = 'E863F116-079E-48FF-8F27-9C2605A29F52';
89
+
90
+ class S2R2Characteristic extends Characteristic {
91
+ constructor() {
92
+ super('S2R2', S2R2Characteristic.UUID);
93
+ this.setProps({
94
+ format: Formats.DATA,
95
+ perms: [
96
+ Perms.PAIRED_READ, Perms.NOTIFY, Perms.HIDDEN
97
+ ]
98
+ });
99
+ }
100
+ }
101
+
102
+ S2R2Characteristic.UUID = 'E863F117-079E-48FF-8F27-9C2605A29F52';
103
+
104
+ class S2W1Characteristic extends Characteristic {
105
+ constructor() {
106
+ super('S2W1', S2W1Characteristic.UUID);
107
+ this.setProps({
108
+ format: Formats.DATA,
109
+ perms: [
110
+ Perms.PAIRED_WRITE, Perms.HIDDEN
111
+ ]
112
+ });
113
+ }
114
+ }
115
+
116
+ S2W1Characteristic.UUID = 'E863F11C-079E-48FF-8F27-9C2605A29F52';
117
+
118
+ class S2W2Characteristic extends Characteristic {
119
+ constructor() {
120
+ super('S2W2', S2W2Characteristic.UUID);
121
+ this.setProps({
122
+ format: Formats.DATA,
123
+ perms: [
124
+ Perms.PAIRED_WRITE, Perms.HIDDEN
125
+ ]
126
+ });
127
+ }
128
+ }
129
+
130
+ S2W2Characteristic.UUID = 'E863F121-079E-48FF-8F27-9C2605A29F52';
131
+
132
+ class FakeGatoHistoryService extends Service {
133
+ constructor(displayName, subtype) {
134
+ super(displayName, FakeGatoHistoryService.UUID, subtype);
135
+
136
+ this.addCharacteristic(S2R1Characteristic);
137
+ this.addCharacteristic(S2R2Characteristic);
138
+ this.addCharacteristic(S2W1Characteristic);
139
+ this.addCharacteristic(S2W2Characteristic);
140
+ }
141
+ }
142
+
143
+ FakeGatoHistoryService.UUID = 'E863F007-079E-48FF-8F27-9C2605A29F52';
144
+ var thisAccessory = {};
145
+ class FakeGatoHistory extends Service {
146
+ constructor(accessoryType, accessory, optionalParams) {
147
+
148
+ super(accessory.displayName + " History", FakeGatoHistoryService.UUID);
149
+
150
+ var entry2address = function (val) { // not used ?
151
+ var temp = val % this.memorySize;
152
+ return temp;
153
+ }.bind(this);
154
+
155
+ thisAccessory = accessory;
156
+ this.accessoryName = thisAccessory.displayName;
157
+ this.signatures = [];
158
+ this.uuid = require('./lib/uuid.js');
159
+
160
+ if (typeof (optionalParams) === 'object') {
161
+ this.size = optionalParams.size || 4032;
162
+ this.minutes = optionalParams.minutes || 10; // Optional timer length
163
+ this.storage = optionalParams.storage; // 'fs' or 'googleDrive'
164
+ this.path = optionalParams.path || optionalParams.folder || (this.storage == 'fs' ? homebridge.user.storagePath() : undefined);
165
+ this.filename = optionalParams.filename;
166
+ this.disableTimer = optionalParams.disableTimer || false;
167
+ this.disableRepeatLastData = optionalParams.disableRepeatLastData || false;
168
+ this.log = optionalParams.log || thisAccessory.log || {}; // workaround for typescript blocking of changing of accessory object definition
169
+ } else {
170
+ this.size = 4032;
171
+ this.minutes = 10;
172
+ this.disableTimer = false;
173
+ this.log = thisAccessory.log || {};
174
+ }
175
+
176
+ if (!this.log.debug) {
177
+ this.log.debug = function () { };
178
+ }
179
+
180
+ if (!this.disableTimer) {
181
+ if (homebridge.globalFakeGatoTimer === undefined)
182
+ homebridge.globalFakeGatoTimer = new FakeGatoTimer({
183
+ minutes: this.minutes,
184
+ log: this.log
185
+ });
186
+ }
187
+
188
+ if (this.storage !== undefined) {
189
+ this.loaded = false;
190
+ if (homebridge.globalFakeGatoStorage === undefined) {
191
+ homebridge.globalFakeGatoStorage = new FakeGatoStorage({
192
+ log: this.log
193
+ });
194
+ }
195
+ homebridge.globalFakeGatoStorage.addWriter(this, {
196
+ storage: this.storage,
197
+ path: this.path,
198
+ filename: this.filename,
199
+ keyPath: optionalParams.keyPath || homebridge.user.storagePath() || undefined,
200
+ onReady: function () {
201
+
202
+ this.load(function (err, loaded) {
203
+ //this.log.debug("**Fakegato-history Loaded",loaded);
204
+ //this.registerEvents();
205
+ if (err) this.log.debug('**Fakegato-history Load error :', err);
206
+ else {
207
+ if (loaded) this.log.debug('**Fakegato-history History Loaded from Persistant Storage');
208
+ this.loaded = true;
209
+ }
210
+ }.bind(this));
211
+ }.bind(this)
212
+ });
213
+ }
214
+
215
+
216
+ switch (accessoryType) {
217
+ case TYPE_WEATHER:
218
+ this.accessoryType116 = "03 0102 0202 0302";
219
+ this.accessoryType117 = "07";
220
+ if (!this.disableTimer) {
221
+ homebridge.globalFakeGatoTimer.subscribe(this, this.calculateAverage);
222
+ }
223
+ break;
224
+ case TYPE_ENERGY:
225
+ this.accessoryType116 = "04 0102 0202 0702 0f03";
226
+ this.accessoryType117 = "1f";
227
+ if (!this.disableTimer) {
228
+ homebridge.globalFakeGatoTimer.subscribe(this, this.calculateAverage);
229
+ }
230
+ break;
231
+ case TYPE_ROOM:
232
+ this.accessoryType116 = "04 0102 0202 0402 0f03";
233
+ this.accessoryType117 = "0f";
234
+ if (!this.disableTimer) {
235
+ homebridge.globalFakeGatoTimer.subscribe(this, this.calculateAverage);
236
+ }
237
+ break;
238
+ case TYPE_ROOM2:
239
+ this.accessoryType116 = "07 0102 0202 2202 2901 2501 2302 2801";
240
+ this.accessoryType117 = "7f";
241
+ if (!this.disableTimer) {
242
+ homebridge.globalFakeGatoTimer.subscribe(this, this.calculateAverage);
243
+ }
244
+ break;
245
+ case TYPE_DOOR:
246
+ this.accessoryType116 = "01 0601";
247
+ this.accessoryType117 = "01";
248
+ if (!this.disableTimer) {
249
+ homebridge.globalFakeGatoTimer.subscribe(this, function (params) { // callback
250
+ var backLog = params.backLog || [];
251
+ var immediate = params.immediate;
252
+
253
+ var fakegato = this.service;
254
+ var actualEntry = {};
255
+
256
+ if (backLog.length) {
257
+ if (!immediate) {
258
+ actualEntry.time = Math.round(new Date().valueOf() / 1000);
259
+ actualEntry.status = backLog[0].status;
260
+ }
261
+ else {
262
+ actualEntry.time = backLog[0].time;
263
+ actualEntry.status = backLog[0].status;
264
+ }
265
+ fakegato.log.debug('**Fakegato-timer callbackDoor: ', fakegato.accessoryName, ', immediate: ', immediate, ', entry: ', actualEntry);
266
+
267
+ fakegato._addEntry(actualEntry);
268
+ }
269
+ });
270
+ }
271
+ break;
272
+ case TYPE_MOTION:
273
+ this.accessoryType116 = "02 1301 1c01";
274
+ this.accessoryType117 = "02";
275
+ if (!this.disableTimer) {
276
+ homebridge.globalFakeGatoTimer.subscribe(this, function (params) { // callback
277
+ var backLog = params.backLog || [];
278
+ var immediate = params.immediate;
279
+
280
+ var fakegato = this.service;
281
+ var actualEntry = {};
282
+
283
+ if (backLog.length) {
284
+ if (!immediate) {
285
+ actualEntry.time = Math.round(new Date().valueOf() / 1000);
286
+ actualEntry.status = backLog[0].status;
287
+ }
288
+ else {
289
+ actualEntry.time = backLog[0].time;
290
+ actualEntry.status = backLog[0].status;
291
+ }
292
+ fakegato.log.debug('**Fakegato-timer callbackMotion: ', fakegato.accessoryName, ', immediate: ', immediate, ', entry: ', actualEntry);
293
+
294
+ fakegato._addEntry(actualEntry);
295
+ }
296
+ });
297
+ }
298
+ break;
299
+ case TYPE_SWITCH:
300
+ this.accessoryType116 = "01 0e01";
301
+ this.accessoryType117 = "01";
302
+ if (!this.disableTimer) {
303
+ homebridge.globalFakeGatoTimer.subscribe(this, function (params) { // callback
304
+ var backLog = params.backLog || [];
305
+ var immediate = params.immediate;
306
+
307
+ var fakegato = this.service;
308
+ var actualEntry = {};
309
+
310
+ if (backLog.length) {
311
+ if (!immediate) {
312
+ actualEntry.time = Math.round(new Date().valueOf() / 1000);
313
+ actualEntry.status = backLog[0].status;
314
+ }
315
+ else {
316
+ actualEntry.time = backLog[0].time;
317
+ actualEntry.status = backLog[0].status;
318
+ }
319
+ fakegato.log.debug('**Fakegato-timer callbackSwitch: ', fakegato.accessoryName, ', immediate: ', immediate, ', entry: ', actualEntry);
320
+
321
+ fakegato._addEntry(actualEntry);
322
+ }
323
+ });
324
+ }
325
+ break;
326
+ case TYPE_CUSTOM:
327
+ thisAccessory.services.forEach((service, i) => {
328
+ service.characteristics.forEach((characteristic, i) => {
329
+ // console.log('**Fakegato-history characteristics', characteristic.displayName, characteristic.UUID);
330
+ switch(this.uuid.toLongFormUUID(characteristic.UUID)) {
331
+ case Characteristic.CurrentTemperature.UUID: // Temperature
332
+ this.signatures.push({ signature: '0102', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 100, entry: "temp" });
333
+ break;
334
+ case Characteristic.VOCDensity.UUID: // VOC Density
335
+ this.signatures.push({ signature: '2202', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "voc" });
336
+ break;
337
+ case Characteristic.CurrentRelativeHumidity.UUID: // Humidity
338
+ this.signatures.push({ signature: '0202', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 100, entry: "humidity" });
339
+ break;
340
+ case 'E863F10F-079E-48FF-8F27-9C2605A29F52': // CustomCharacteristic.AtmosphericPressureLevel.UUID
341
+ this.signatures.push({ signature: '0302', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 10, entry: "pressure" });
342
+ break;
343
+ case 'E863F10B-079E-48FF-8F27-9C2605A29F52': // PPM
344
+ this.signatures.push({ signature: '0702', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 10, entry: "ppm" });
345
+ break;
346
+ case Characteristic.ContactSensorState.UUID: // Contact Sensor State
347
+ this.signatures.push({ signature: '0601', length: 2, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "contact" });
348
+ break;
349
+ case 'E863F10D-079E-48FF-8F27-9C2605A29F52': // Power
350
+ this.signatures.push({ signature: '0702', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 10, entry: "power" });
351
+ break;
352
+ case Characteristic.On.UUID: // Switch On
353
+ this.signatures.push({ signature: '0e01', length: 2, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "status" });
354
+ break;
355
+ case Characteristic.MotionDetected.UUID: // Motion Detected
356
+ this.signatures.push({ signature: '1c01', length: 2, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "motion" });
357
+ break;
358
+ case Characteristic.TargetTemperature.UUID: // Target Temperature
359
+ this.signatures.push({ signature: '1102', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 100, entry: "setTemp" });
360
+ break;
361
+ case 'E863F12E-079E-48FF-8F27-9C2605A29F52': // Valve Position
362
+ this.signatures.push({ signature: '1001', length: 2, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "valvePosition" });
363
+ break;
364
+ case '0000006B-0000-1000-8000-0026BB765291': // CurrentAmbiantLightLevel
365
+ this.signatures.push({ signature: '3002', length: 4, uuid: this.uuid.toShortFormUUID(characteristic.UUID), factor: 1, entry: "lux" });
366
+ break;
367
+ }
368
+ });
369
+ });
370
+ this.accessoryType116 = (' 0' + this.signatures.length.toString() + ' ' + this.signatures.sort((a, b) => (a.signature > b.signature) ? 1 : -1).map(a => a.signature).join(' ') + ' ');
371
+ if (!this.disableTimer) {
372
+ homebridge.globalFakeGatoTimer.subscribe(this, this.calculateAverage);
373
+ }
374
+ break;
375
+ case TYPE_AQUA:
376
+ this.accessoryType116 = "03 1f01 2a08 2302";
377
+ this.accessoryType117 = "05";
378
+ this.accessoryType117bis = "07";
379
+ break;
380
+ case TYPE_THERMO:
381
+ this.accessoryType116 = "05 0102 1102 1001 1201 1d01";
382
+ this.accessoryType117 = "1f";
383
+ break;
384
+ }
385
+
386
+ this.accessoryType = accessoryType;
387
+ this.firstEntry = 0;
388
+ this.lastEntry = 0;
389
+ this.history = ["noValue"];
390
+ this.memorySize = this.size;
391
+ this.usedMemory = 0;
392
+ this.currentEntry = 1;
393
+ this.transfer = false;
394
+ this.setTime = true;
395
+ this.restarted = true;
396
+ this.refTime = 0;
397
+ this.memoryAddress = 0;
398
+ this.dataStream = '';
399
+
400
+ this.saving = false;
401
+
402
+ this.registerEvents();
403
+ if (this.storage === undefined) {
404
+ this.loaded = true;
405
+ }
406
+ }
407
+
408
+ calculateAverage(params) { // callback
409
+ var backLog = params.backLog || [];
410
+ var previousAvrg = params.previousAvrg || {};
411
+ var timer = params.timer;
412
+
413
+ var fakegato = this.service;
414
+ var calc = {
415
+ sum: {},
416
+ num: {},
417
+ avrg: {}
418
+ };
419
+
420
+ for (var h in backLog) {
421
+ if (backLog.hasOwnProperty(h)) { // only valid keys
422
+ for (let key in backLog[h]) { // each record
423
+ if (backLog[h].hasOwnProperty(key) && key != 'time') { // except time
424
+ if (!calc.sum[key])
425
+ calc.sum[key] = 0;
426
+ if (!calc.num[key])
427
+ calc.num[key] = 0;
428
+ calc.sum[key] += backLog[h][key];
429
+ calc.num[key]++;
430
+ calc.avrg[key] = precisionRound(calc.sum[key] / calc.num[key], 2);
431
+ if (key == 'voc') // VOC expects integers
432
+ calc.avrg[key] = Math.round(calc.avrg[key]);
433
+ }
434
+ }
435
+ }
436
+ }
437
+ calc.avrg.time = Math.round(new Date().valueOf() / 1000); // set the time of the avrg
438
+
439
+ if(!fakegato.disableRepeatLastData) {
440
+ for (let key in previousAvrg) { // each record of previous average
441
+ if (previousAvrg.hasOwnProperty(key) && key != 'time') { // except time
442
+ if (!backLog.length ||//calc.avrg[key] == 0 || // zero value
443
+ calc.avrg[key] === undefined) // no key (meaning no value received for this key yet)
444
+ {
445
+ calc.avrg[key] = previousAvrg[key];
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ if (Object.keys(calc.avrg).length > 1) {
452
+ fakegato._addEntry(calc.avrg);
453
+ timer.emptyData(fakegato);
454
+ }
455
+ return calc.avrg;
456
+ }
457
+
458
+ registerEvents() {
459
+ this.log.debug('**Fakegato-history Registring Events', thisAccessory.displayName);
460
+ if (typeof thisAccessory.getService === "function") {
461
+ // Platform API
462
+ this.log.debug('**Fakegato-history Platform', thisAccessory.displayName);
463
+
464
+ this.service = thisAccessory.getService(FakeGatoHistoryService);
465
+ if (this.service === undefined) {
466
+ this.service = thisAccessory.addService(FakeGatoHistoryService, ucfirst(thisAccessory.displayName) + ' History', this.accessoryType);
467
+ }
468
+
469
+ this.service.getCharacteristic(S2R2Characteristic)
470
+ .on('get', this.getCurrentS2R2.bind(this));
471
+
472
+ this.service.getCharacteristic(S2W1Characteristic)
473
+ .on('set', this.setCurrentS2W1.bind(this));
474
+
475
+ this.service.getCharacteristic(S2W2Characteristic)
476
+ .on('set', this.setCurrentS2W2.bind(this));
477
+
478
+ }
479
+ else {
480
+ // Accessory API
481
+ this.log.debug('**Fakegato-history Accessory', thisAccessory.displayName);
482
+
483
+ this.addCharacteristic(S2R1Characteristic);
484
+
485
+ this.addCharacteristic(S2R2Characteristic)
486
+ .on('get', this.getCurrentS2R2.bind(this));
487
+
488
+ this.addCharacteristic(S2W1Characteristic)
489
+ .on('set', this.setCurrentS2W1.bind(this));
490
+
491
+ this.addCharacteristic(S2W2Characteristic)
492
+ .on('set', this.setCurrentS2W2.bind(this));
493
+ }
494
+ }
495
+
496
+ sendHistory(address) {
497
+ if (address != 0) {
498
+ this.currentEntry = address;
499
+ } else {
500
+ this.currentEntry = 1;
501
+ }
502
+ this.transfer = true;
503
+ }
504
+
505
+ addEntry(entry) {
506
+ switch (this.accessoryType) {
507
+ case TYPE_DOOR:
508
+ case TYPE_MOTION:
509
+ case TYPE_SWITCH:
510
+ if (!this.disableTimer)
511
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this, immediateCallback: true });
512
+ else
513
+ this._addEntry({ time: entry.time, status: entry.status });
514
+ break;
515
+ case TYPE_AQUA:
516
+ this._addEntry({ time: entry.time, status: entry.status, waterAmount: entry.waterAmount });
517
+ break;
518
+ case TYPE_WEATHER:
519
+ if (!this.disableTimer)
520
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this });
521
+ else
522
+ this._addEntry({ time: entry.time, temp: entry.temp, humidity: entry.humidity, pressure: entry.pressure });
523
+ break;
524
+ case TYPE_ROOM:
525
+ if (!this.disableTimer)
526
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this });
527
+ else
528
+ this._addEntry({ time: entry.time, temp: entry.temp, humidity: entry.humidity, ppm: entry.ppm });
529
+ break;
530
+ case TYPE_ROOM2:
531
+ if (!this.disableTimer)
532
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this });
533
+ else
534
+ this._addEntry({ time: entry.time, temp: entry.temp, humidity: entry.humidity, voc: entry.voc });
535
+ break;
536
+ case TYPE_ENERGY:
537
+ if (!this.disableTimer)
538
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this });
539
+ else
540
+ this._addEntry({ time: entry.time, power: entry.power });
541
+ break;
542
+ case TYPE_CUSTOM:
543
+ if (!this.disableTimer)
544
+ if ('power' in entry || 'temp' in entry || 'lux' in entry) { // Only put power, temperature or lux thru averager
545
+ homebridge.globalFakeGatoTimer.addData({ entry: entry, service: this });
546
+ } else {
547
+ this._addEntry(entry);
548
+ }
549
+ else
550
+ this._addEntry(entry);
551
+ break;
552
+ default:
553
+ this._addEntry(entry);
554
+ break;
555
+ }
556
+ }
557
+
558
+ //in order to be consistent with Eve, entry address start from 1
559
+ _addEntry(entry) {
560
+ if (this.loaded) {
561
+ var entry2address = function (val) {
562
+ return val % this.memorySize;
563
+ }
564
+ .bind(this);
565
+
566
+ var val;
567
+
568
+ if (this.usedMemory < this.memorySize) {
569
+ this.usedMemory++;
570
+ this.firstEntry = 0;
571
+ this.lastEntry = this.usedMemory;
572
+ } else {
573
+ this.firstEntry++;
574
+ this.lastEntry = this.firstEntry + this.usedMemory;
575
+ if (this.restarted == true) {
576
+ this.history[entry2address(this.lastEntry)] = {
577
+ time: entry.time,
578
+ setRefTime: 1
579
+ };
580
+ this.firstEntry++;
581
+ this.lastEntry = this.firstEntry + this.usedMemory;
582
+ this.restarted = false;
583
+ }
584
+ }
585
+
586
+ if (this.refTime == 0) {
587
+ this.refTime = entry.time - EPOCH_OFFSET;
588
+ this.history[this.lastEntry] = {
589
+ time: entry.time,
590
+ setRefTime: 1
591
+ };
592
+ this.initialTime = entry.time;
593
+ this.lastEntry++;
594
+ this.usedMemory++;
595
+ }
596
+
597
+ this.history[entry2address(this.lastEntry)] = (entry);
598
+
599
+ if (this.usedMemory < this.memorySize) {
600
+ val = Format(
601
+ '%s00000000%s%s%s%s%s000000000101',
602
+ numToHex(swap32(entry.time - this.refTime - EPOCH_OFFSET), 8),
603
+ numToHex(swap32(this.refTime), 8),
604
+ this.accessoryType116,
605
+ numToHex(swap16(this.usedMemory + 1), 4),
606
+ numToHex(swap16(this.memorySize), 4),
607
+ numToHex(swap32(this.firstEntry), 8));
608
+ } else {
609
+ val = Format(
610
+ '%s00000000%s%s%s%s%s000000000101',
611
+ numToHex(swap32(entry.time - this.refTime - EPOCH_OFFSET), 8),
612
+ numToHex(swap32(this.refTime), 8),
613
+ this.accessoryType116,
614
+ numToHex(swap16(this.usedMemory), 4),
615
+ numToHex(swap16(this.memorySize), 4),
616
+ numToHex(swap32(this.firstEntry + 1), 8));
617
+ }
618
+
619
+ if (this.service === undefined) { // Accessory API
620
+ this.getCharacteristic(S2R1Characteristic).setValue(hexToBase64(val));
621
+ }
622
+ else { // Platform API
623
+ this.service.getCharacteristic(S2R1Characteristic).setValue(hexToBase64(val));
624
+ }
625
+
626
+ this.log.debug("**Fakegato-history First entry %s: %s", this.accessoryName, this.firstEntry.toString(16));
627
+ this.log.debug("**Fakegato-history Last entry %s: %s", this.accessoryName, this.lastEntry.toString(16));
628
+ this.log.debug("**Fakegato-history Used memory %s: %s", this.accessoryName, this.usedMemory.toString(16));
629
+ this.log.debug("**Fakegato-history Val 116 %s: %s", this.accessoryName, val);
630
+
631
+ if (this.storage !== undefined) this.save();
632
+ } else {
633
+ setTimeout(function () { // retry in 100ms
634
+ this._addEntry(entry);
635
+ }.bind(this), 100);
636
+ }
637
+ }
638
+ getInitialTime() {
639
+ return this.initialTime;
640
+ }
641
+
642
+ setExtraPersistedData(extra) {
643
+ this.extra = extra;
644
+ }
645
+
646
+ getExtraPersistedData() {
647
+ return this.extra;
648
+ }
649
+
650
+ isHistoryLoaded() {
651
+ return this.loaded;
652
+ }
653
+
654
+ save() {
655
+ if (this.loaded) {
656
+
657
+ let data = {
658
+ firstEntry: this.firstEntry,
659
+ lastEntry: this.lastEntry,
660
+ usedMemory: this.usedMemory,
661
+ refTime: this.refTime,
662
+ initialTime: this.initialTime,
663
+ history: this.history,
664
+ extra: this.extra
665
+ };
666
+
667
+
668
+ homebridge.globalFakeGatoStorage.write({
669
+ service: this,
670
+ data: typeof (data) === "object" ? JSON.stringify(data) : data
671
+ });
672
+
673
+ } else {
674
+ setTimeout(function () { // retry in 100ms
675
+ this.save();
676
+ }.bind(this), 100);
677
+ }
678
+ }
679
+ load(cb) {
680
+ this.log.debug("**Fakegato-history Loading...");
681
+ homebridge.globalFakeGatoStorage.read({
682
+ service: this,
683
+ callback: function (err, data) {
684
+ if (!err) {
685
+ if (data) {
686
+ try {
687
+ this.log.debug("**Fakegato-history read data from", this.accessoryName, ":", data);
688
+ let jsonFile = typeof (data) === "object" ? data : JSON.parse(data);
689
+
690
+ this.firstEntry = jsonFile.firstEntry;
691
+ this.lastEntry = jsonFile.lastEntry;
692
+ this.usedMemory = jsonFile.usedMemory;
693
+ this.refTime = jsonFile.refTime;
694
+ this.initialTime = jsonFile.initialTime;
695
+ this.history = jsonFile.history;
696
+ this.extra = jsonFile.extra;
697
+ } catch (e) {
698
+ this.log.debug("**Fakegato-history ERROR fetching persisting data restart from zero - invalid JSON**", e);
699
+ cb(e, false);
700
+ }
701
+ cb(null, true);
702
+ }
703
+ } else {
704
+ // file don't exists
705
+ cb(null, false);
706
+ }
707
+ }.bind(this)
708
+ });
709
+ }
710
+ cleanPersist() {
711
+ this.log.debug("**Fakegato-history Cleaning...");
712
+ homebridge.globalFakeGatoStorage.remove({
713
+ service: this
714
+ });
715
+ }
716
+
717
+ getCurrentS2R2(callback) {
718
+ var entry2address = function (val) {
719
+ return val % this.memorySize;
720
+ }.bind(this);
721
+
722
+ if ((this.currentEntry <= this.lastEntry) && (this.transfer == true)) {
723
+ this.memoryAddress = entry2address(this.currentEntry);
724
+ for (var i = 0; i < 11; i++) {
725
+ if ((this.history[this.memoryAddress].setRefTime == 1) || (this.setTime == true) ||
726
+ (this.currentEntry == this.firstEntry + 1)) {
727
+ this.dataStream += Format(
728
+ ",15%s 0100 0000 81%s0000 0000 00 0000",
729
+ numToHex(swap32(this.currentEntry), 8),
730
+ numToHex(swap32(this.refTime), 8));
731
+ this.setTime = false;
732
+ }
733
+ else {
734
+ this.log.debug("**Fakegato-history %s Entry: %s, Address: %s", this.accessoryName, this.currentEntry, this.memoryAddress);
735
+ switch (this.accessoryType) {
736
+ case TYPE_WEATHER:
737
+ this.dataStream += Format(
738
+ ",10 %s%s-%s:%s %s %s",
739
+ numToHex(swap32(this.currentEntry), 8),
740
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
741
+ this.accessoryType117,
742
+ numToHex(swap16(this.history[this.memoryAddress].temp * 100), 4),
743
+ numToHex(swap16(this.history[this.memoryAddress].humidity * 100), 4),
744
+ numToHex(swap16(this.history[this.memoryAddress].pressure * 10), 4));
745
+ break;
746
+ case TYPE_ENERGY:
747
+ this.dataStream += Format(
748
+ ",14 %s%s-%s:0000 0000 %s 0000 0000",
749
+ numToHex(swap32(this.currentEntry), 8),
750
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
751
+ this.accessoryType117,
752
+ numToHex(swap16(this.history[this.memoryAddress].power * 10), 4));
753
+ break;
754
+ case TYPE_ROOM:
755
+ this.dataStream += Format(
756
+ ",13 %s%s%s%s%s%s0000 00",
757
+ numToHex(swap32(this.currentEntry), 8),
758
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
759
+ this.accessoryType117,
760
+ numToHex(swap16(this.history[this.memoryAddress].temp * 100), 4),
761
+ numToHex(swap16(this.history[this.memoryAddress].humidity * 100), 4),
762
+ numToHex(swap16(this.history[this.memoryAddress].ppm), 4));
763
+ break;
764
+ case TYPE_ROOM2:
765
+ this.dataStream += Format(
766
+ ",15 %s%s%s%s%s%s0054 a80f01",
767
+ numToHex(swap32(this.currentEntry), 8),
768
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
769
+ this.accessoryType117,
770
+ numToHex(swap16(this.history[this.memoryAddress].temp * 100), 4),
771
+ numToHex(swap16(this.history[this.memoryAddress].humidity * 100), 4),
772
+ numToHex(swap16(this.history[this.memoryAddress].voc), 4));
773
+ break;
774
+ case TYPE_DOOR:
775
+ case TYPE_MOTION:
776
+ case TYPE_SWITCH:
777
+ this.dataStream += Format(
778
+ ",0b %s%s%s%s",
779
+ numToHex(swap32(this.currentEntry), 8),
780
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
781
+ this.accessoryType117,
782
+ numToHex(this.history[this.memoryAddress].status, 2));
783
+ break;
784
+ case TYPE_AQUA:
785
+ if (this.history[this.memoryAddress].status == true)
786
+ this.dataStream += Format(
787
+ ",0d %s%s%s%s 300c",
788
+ numToHex(swap32(this.currentEntry), 8),
789
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
790
+ this.accessoryType117,
791
+ numToHex(this.history[this.memoryAddress].status, 2));
792
+ else
793
+ this.dataStream += Format(
794
+ ",15 %s%s%s%s%s 00000000 300c",
795
+ numToHex(swap32(this.currentEntry), 8),
796
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
797
+ this.accessoryType117bis,
798
+ numToHex(this.history[this.memoryAddress].status, 2),
799
+ numToHex(swap32(this.history[this.memoryAddress].waterAmount), 8));
800
+ break;
801
+ case TYPE_THERMO:
802
+ this.dataStream += Format(
803
+ ",11 %s%s%s%s%s%s 0000",
804
+ numToHex(swap32(this.currentEntry), 8),
805
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8),
806
+ this.accessoryType117,
807
+ numToHex(swap16(this.history[this.memoryAddress].currentTemp * 100), 4),
808
+ numToHex(swap16(this.history[this.memoryAddress].setTemp * 100), 4),
809
+ numToHex(this.history[this.memoryAddress].valvePosition, 2));
810
+ break;
811
+ case TYPE_CUSTOM:
812
+ var result = [];
813
+ var bitmask = 0;
814
+ var dataStream = Format("%s%s",
815
+ numToHex(swap32(this.currentEntry), 8),
816
+ numToHex(swap32(this.history[this.memoryAddress].time - this.refTime - EPOCH_OFFSET), 8));
817
+ for (const [key, value] of Object.entries(this.history[this.memoryAddress])) {
818
+ switch (key) {
819
+ case 'time':
820
+ break;
821
+ default:
822
+ for (var x = 0, iLen = this.signatures.length; x < iLen; x++) {
823
+ if (this.signatures[x].entry === key) {
824
+ // console.log('**Fakegato-history key', key, this.signatures[x].uuid, value, this.signatures[x].factor);
825
+ switch(this.signatures[x].length) {
826
+ case 8:
827
+ result[x] = Format('%s', numToHex(swap32(value * this.signatures[x].factor), this.signatures[x].length));
828
+ break;
829
+ case 4:
830
+ result[x] = Format('%s', numToHex(swap16(value * this.signatures[x].factor), this.signatures[x].length));
831
+ break;
832
+ case 2:
833
+ result[x] = Format('%s', numToHex(value * this.signatures[x].factor, this.signatures[x].length));
834
+ break;
835
+ }
836
+ bitmask += Math.pow(2, x);
837
+ }
838
+ }
839
+ }
840
+ }
841
+ var results = dataStream + ' ' + numToHex(bitmask, 2) + ' ' + result.map(a => a).join(' ');
842
+ // console.log('**Fakegato-history results', numToHex((results.replace(/[^0-9A-F]/ig, '').length) / 2 + 1) + ' ' + results);
843
+ this.dataStream += (' ' + numToHex((results.replace(/[^0-9A-F]/ig, '').length) / 2 + 1) + ' ' + results + ',');
844
+ break;
845
+ }
846
+ }
847
+ this.currentEntry++;
848
+ this.memoryAddress = entry2address(this.currentEntry);
849
+ if (this.currentEntry > this.lastEntry)
850
+ break;
851
+ }
852
+ this.log.debug("**Fakegato-history Data %s: %s", this.accessoryName, this.dataStream);
853
+ callback(null, hexToBase64(this.dataStream));
854
+ this.dataStream = '';
855
+ }
856
+ else {
857
+ this.transfer = false;
858
+ callback(null, hexToBase64('00'));
859
+ }
860
+ }
861
+
862
+
863
+ setCurrentS2W1(val, callback) {
864
+ callback(null);
865
+ this.log.debug("**Fakegato-history Data request %s: %s", this.accessoryName, base64ToHex(val));
866
+ var valHex = base64ToHex(val);
867
+ var substring = valHex.substring(4, 12);
868
+ var valInt = parseInt(substring, 16);
869
+ var address = swap32(valInt);
870
+ var hexAddress = address.toString('16');
871
+
872
+ this.log.debug("**Fakegato-history Address requested %s: %s", this.accessoryName, hexAddress);
873
+ this.sendHistory(address);
874
+
875
+ }
876
+
877
+ setCurrentS2W2(val, callback) {
878
+ this.log.debug("**Fakegato-history Clock adjust %s: %s", this.accessoryName, base64ToHex(val));
879
+ callback(null);
880
+ }
881
+
882
+ }
883
+
884
+ FakeGatoHistoryService.UUID = 'E863F007-079E-48FF-8F27-9C2605A29F52';
885
+
886
+ return FakeGatoHistory;
887
+ };