homebridge-edomoticz 2.1.49 → 3.0.4

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.
package/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
4
4
 
5
5
  # Homebridge-eDomoticz
6
- This is a plugin for [Homebridge](https://github.com/nfarina/homebridge) and [Homebridge Config UI X Support](https://github.com/oznu/homebridge-config-ui-x) and [Domoticz](https://github.com/domoticz/domoticz).
6
+ This is a plugin for [Homebridge](https://github.com/nfarina/homebridge) v1.x and [Homebridge Config UI X Support](https://github.com/oznu/homebridge-config-ui-x) and [Domoticz](https://github.com/domoticz/domoticz).
7
+
8
+ It doesn't work with Homebridge v2.x
7
9
 
8
10
  ## Supports:
9
11
  <details>
package/index.js CHANGED
@@ -14,7 +14,9 @@ var Mqtt = require('./lib/mqtt.js').Mqtt;
14
14
  var eDomoticzAccessory = require('./lib/domoticz_accessory.js');
15
15
  var Constants = require('./lib/constants.js');
16
16
  var Helper = require('./lib/helper.js').Helper;
17
- var eDomoticzServices = require('./lib/services.js').eDomoticzServices;
17
+ var Services = require('./lib/services.js');
18
+ var eDomoticzServices = Services.eDomoticzServices;
19
+ var initServices = Services.initServices;
18
20
  const util = require('util');
19
21
 
20
22
  module.exports = function(homebridge) {
@@ -24,40 +26,11 @@ module.exports = function(homebridge) {
24
26
  Types = homebridge.hapLegacyTypes;
25
27
  UUID = homebridge.hap.uuid;
26
28
 
27
- util.inherits(eDomoticzServices.TotalConsumption, Characteristic);
28
- util.inherits(eDomoticzServices.CurrentConsumption, Characteristic);
29
- util.inherits(eDomoticzServices.GasConsumption, Characteristic);
30
- util.inherits(eDomoticzServices.TempOverride, Characteristic);
31
- util.inherits(eDomoticzServices.MeterDeviceService, Service);
32
- util.inherits(eDomoticzServices.GasDeviceService, Service);
33
- util.inherits(eDomoticzServices.Ampere, Characteristic);
34
- util.inherits(eDomoticzServices.AMPDeviceService, Service);
35
- util.inherits(eDomoticzServices.Volt, Characteristic);
36
- util.inherits(eDomoticzServices.VOLTDeviceService, Service);
37
- util.inherits(eDomoticzServices.CurrentUsage, Characteristic);
38
- util.inherits(eDomoticzServices.UsageDeviceService, Service);
39
- util.inherits(eDomoticzServices.TodayConsumption, Characteristic);
40
- util.inherits(eDomoticzServices.Barometer, Characteristic);
41
- util.inherits(eDomoticzServices.WaterFlow, Characteristic);
42
- util.inherits(eDomoticzServices.TotalWaterFlow, Characteristic);
43
- util.inherits(eDomoticzServices.WaterDeviceService, Service);
44
- util.inherits(eDomoticzServices.WeatherService, Service);
45
- util.inherits(eDomoticzServices.WindSpeed, Characteristic);
46
- util.inherits(eDomoticzServices.WindChill, Characteristic);
47
- util.inherits(eDomoticzServices.WindDirection, Characteristic);
48
- util.inherits(eDomoticzServices.WindDeviceService, Service);
49
- util.inherits(eDomoticzServices.Rainfall, Characteristic);
50
- util.inherits(eDomoticzServices.RainDeviceService, Service);
51
- util.inherits(eDomoticzServices.Visibility, Characteristic);
52
- util.inherits(eDomoticzServices.VisibilityDeviceService, Service);
53
- util.inherits(eDomoticzServices.SolRad, Characteristic);
54
- util.inherits(eDomoticzServices.SolRadDeviceService, Service);
55
- util.inherits(eDomoticzServices.LocationService, Service);
56
- util.inherits(eDomoticzServices.Location, Characteristic);
57
- util.inherits(eDomoticzServices.InfotextDeviceService, Service);
58
- util.inherits(eDomoticzServices.Infotext, Characteristic);
59
- util.inherits(eDomoticzServices.UVDeviceService, Service);
60
- util.inherits(eDomoticzServices.UVIndex, Characteristic);
29
+ // Populate eDomoticzServices with ES6 class definitions now that HAP refs
30
+ // (Service / Characteristic) are available. Replaces the previous pattern
31
+ // of pre-ES6 constructors + util.inherits, which crashes under HAP-NodeJS
32
+ // v2 ("Class constructor Service cannot be invoked without 'new'").
33
+ initServices(Service, Characteristic, UUID, homebridge.hap);
61
34
 
62
35
  //homebridge.registerAccessory("homebridge-edomoticz", "eDomoticz", eDomoticzAccessory);
63
36
  homebridge.registerPlatform("homebridge-edomoticz", "eDomoticz", eDomoticzPlatform, true);
@@ -243,6 +216,18 @@ eDomoticzPlatform.prototype = {
243
216
  var uuid = platformAccessory.context.uuid;
244
217
  var eve = platformAccessory.context.eve;
245
218
 
219
+ // Cached characteristics come back with the perms that were persisted
220
+ // when the cache was written. A cache produced by 3.0.0-3.0.2 under
221
+ // HAP v2 carries perms [null, "ev"] on every custom characteristic,
222
+ // which makes iOS refuse to pair. Re-apply the canonical perms.
223
+ var pairedRead = (this.api.hap.Perms && this.api.hap.Perms.PAIRED_READ)
224
+ || (Characteristic.Perms && Characteristic.Perms.PAIRED_READ)
225
+ || 'pr';
226
+ var healed = Helper.healCustomCharacteristicPerms(platformAccessory, eDomoticzServices, Characteristic, pairedRead);
227
+ if (healed > 0) {
228
+ this.log("Repaired " + healed + " cached custom characteristic perms on " + device.Name);
229
+ }
230
+
246
231
  // Generate the already cached accessory again
247
232
  var accessory = new eDomoticzAccessory(this, platformAccessory, false, device.Used, device.idx, device.Name, uuid, device.HaveDimmer, device.MaxDimLevel, device.SubType, device.Type, device.BatteryLevel, device.SwitchType, device.SwitchTypeVal, device.HardwareID, device.HardwareTypeVal, device.Image, eve, device.HaveTimeout, device.Description);
248
233
  this.accessories.push(accessory);
package/lib/domoticz.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var request = require("request");
2
2
  var Helper = require('./helper.js').Helper;
3
- var extend = require('util')._extend;
3
+ var extend = Object.assign;
4
4
 
5
5
  module.exports = {
6
6
  Domoticz: Domoticz
@@ -48,7 +48,6 @@ Domoticz.settings = function(accessory, completion, error) {
48
48
  Helper.LogConnectionError(this.platform, response, err);
49
49
  if (typeof error !== 'undefined' && error !== false) {
50
50
  error();
51
- callback();
52
51
  }
53
52
  }
54
53
  }.bind(accessory));
@@ -208,7 +207,6 @@ Domoticz.updateWithURL = function(accessory, url, completion) {
208
207
  }
209
208
  else {
210
209
  Helper.LogConnectionError(this.platform, response, err);
211
- callback();
212
210
  }
213
211
 
214
212
  if (typeof completion !== 'undefined' && completion !== false) {
@@ -86,7 +86,7 @@ function eDomoticzAccessory(platform, platformAccessory, IsScene, status, idx, n
86
86
  }
87
87
  this.platformAccessory.reachable = true;
88
88
  if (this.swTypeVal == Constants.DeviceTypeMedia) {
89
- this.platformAccessory.category = Categories.TELEVISION;
89
+ this.platformAccessory.category = platform.api.hap.Categories.TELEVISION;
90
90
  }
91
91
  this.publishServices();
92
92
  }
package/lib/helper.js CHANGED
@@ -157,4 +157,63 @@ Helper.LogConnectionError = function(platform, response, err)
157
157
  }
158
158
 
159
159
  platform.forceLog(errorMessage);
160
- }
160
+ }
161
+ /*
162
+ * Re-apply canonical perms to custom eDomoticz Characteristics restored from
163
+ * Homebridge's accessory cache.
164
+ *
165
+ * Homebridge persists every Characteristic's `props` (perms, format, unit...)
166
+ * in cachedAccessories and HAP-NodeJS restores them verbatim on startup.
167
+ * Custom characteristics are not known HAP classes, so they come back as
168
+ * generic Characteristic instances carrying whatever props were cached.
169
+ * A cache written by a plugin build that published perms [null, "ev"]
170
+ * (see CHANGELOG 3.0.3) keeps poisoning the bridge after upgrading, and
171
+ * iOS keeps refusing to pair, until the cache is cleared by hand.
172
+ *
173
+ * This walks a restored PlatformAccessory and, for every characteristic
174
+ * whose UUID matches a custom eDomoticz Characteristic class, replaces the
175
+ * perms with the ones the class defines today when the cached ones are
176
+ * missing, contain null/undefined, or lack PAIRED_READ. Only `perms` is
177
+ * touched: format/unit/min/max are left alone on purpose because two
178
+ * custom characteristics share a UUID with different units
179
+ * (CurrentConsumption 'W' vs GasConsumption 'm3').
180
+ *
181
+ * Returns the number of characteristics healed.
182
+ */
183
+ Helper.healCustomCharacteristicPerms = function (platformAccessory, eDomoticzServices, Characteristic, pairedReadPerm) {
184
+ if (!platformAccessory || !Array.isArray(platformAccessory.services)) {
185
+ return 0;
186
+ }
187
+
188
+ var canonicalPerms = {};
189
+ Object.keys(eDomoticzServices).forEach(function (key) {
190
+ var Ctor = eDomoticzServices[key];
191
+ if (typeof Ctor !== 'function' || !(Ctor.prototype instanceof Characteristic)) {
192
+ return;
193
+ }
194
+ var probe = new Ctor();
195
+ if (!canonicalPerms[probe.UUID] && probe.props && Array.isArray(probe.props.perms)) {
196
+ canonicalPerms[probe.UUID] = probe.props.perms.slice();
197
+ }
198
+ });
199
+
200
+ var healed = 0;
201
+ platformAccessory.services.forEach(function (service) {
202
+ (service.characteristics || []).forEach(function (characteristic) {
203
+ var perms = canonicalPerms[characteristic.UUID];
204
+ if (!perms) {
205
+ return;
206
+ }
207
+ var current = (characteristic.props && characteristic.props.perms) || [];
208
+ var broken = current.length === 0
209
+ || current.some(function (p) { return p === null || p === undefined; })
210
+ || current.indexOf(pairedReadPerm) === -1;
211
+ if (broken) {
212
+ characteristic.setProps({ perms: perms });
213
+ healed++;
214
+ }
215
+ });
216
+ });
217
+
218
+ return healed;
219
+ };
package/lib/services.js CHANGED
@@ -1,367 +1,498 @@
1
1
  var Constants = require('./constants.js');
2
2
 
3
+ var eDomoticzServices = {};
4
+
3
5
  module.exports = {
4
- eDomoticzServices: eDomoticzServices
5
- }
6
+ eDomoticzServices: eDomoticzServices,
7
+ initServices: initServices
8
+ };
6
9
 
10
+ /*
11
+ * initServices is called from index.js once homebridge has provided its HAP
12
+ * references. It populates the (already exported) eDomoticzServices object
13
+ * with ES6 class definitions. The exported object reference does not change,
14
+ * so consumers that captured it via `require('./services.js').eDomoticzServices`
15
+ * (e.g. lib/domoticz_accessory.js) keep working transparently.
16
+ *
17
+ * Order matters per the migration plan: define the 20 custom Characteristics
18
+ * first, then the 14 custom Services that compose them.
19
+ */
20
+ function initServices(Service, Characteristic, UUID, hap) {
7
21
 
8
- function eDomoticzServices() {
22
+ // HAP-NodeJS v1 exposed Formats / Perms / Units as static enums on the
23
+ // Characteristic class. HAP-NodeJS v2 moved them to the module root (and
24
+ // Characteristic.Formats / .Perms / .Units became undefined). Resolve
25
+ // them via fallback so this plugin works on both Homebridge 1.x and 2.x.
26
+ var Formats = (Characteristic && Characteristic.Formats) || (hap && hap.Formats);
27
+ var Perms = (Characteristic && Characteristic.Perms) || (hap && hap.Perms);
28
+ var Units = (Characteristic && Characteristic.Units) || (hap && hap.Units);
9
29
 
10
- }
30
+ if (!Formats || !Perms || !Units) {
31
+ throw new Error(
32
+ 'homebridge-edomoticz: cannot resolve HAP Formats/Perms/Units. ' +
33
+ 'Neither Characteristic.* (HAP v1) nor hap.* (HAP v2) provided ' +
34
+ 'them. This indicates an unsupported HAP-NodeJS version.'
35
+ );
36
+ }
11
37
 
12
- /* Define Custom Services & Characteristics */
13
- // PowerMeter Characteristics
14
- eDomoticzServices.TotalConsumption = function() {
15
- var charUUID = 'E863F10C-079E-48FF-8F27-9C2605A29F52'; //UUID.generate('eDomoticz:customchar:TotalConsumption');
16
- Characteristic.call(this, 'Total Consumption', charUUID);
17
- this.setProps({
18
- format: Characteristic.Formats.FLOAT,
19
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
20
- unit: 'kWh'
21
- });
22
- this.value = this.getDefaultValue();
23
- this.UUID = charUUID;
24
- };
25
- eDomoticzServices.TodayConsumption = function() {
26
- var charUUID = UUID.generate('eDomoticz:customchar:TodayConsumption');
27
- Characteristic.call(this, 'Today', charUUID);
28
- this.setProps({
29
- format: Characteristic.Formats.FLOAT,
30
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
31
- unit: 'kWh'
32
- });
33
- this.value = this.getDefaultValue();
34
- this.UUID = charUUID;
35
- };
36
- eDomoticzServices.CurrentConsumption = function() {
37
- var charUUID = 'E863F10D-079E-48FF-8F27-9C2605A29F52'; //UUID.generate('eDomoticz:customchar:CurrentConsumption');
38
- Characteristic.call(this, 'Consumption', charUUID);
39
- this.setProps({
40
- format: Characteristic.Formats.FLOAT,
41
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
42
- unit: 'W'
43
- });
44
- this.value = this.getDefaultValue();
45
- this.UUID = charUUID;
46
- };
47
- eDomoticzServices.Ampere = function() {
48
- var charUUID = 'E863F126-079E-48FF-8F27-9C2605A29F52'; //AMPERE
49
- Characteristic.call(this, 'Amps', charUUID);
50
- this.setProps({
51
- format: Characteristic.Formats.FLOAT,
52
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
53
- unit: 'A'
54
- });
55
- this.value = this.getDefaultValue();
56
- this.UUID = charUUID;
57
- };
58
- eDomoticzServices.Volt = function() {
59
- var charUUID = 'E863F10A-079E-48FF-8F27-9C2605A29F52'; //VOLT
60
- Characteristic.call(this, 'Volts', charUUID);
61
- this.setProps({
62
- format: Characteristic.Formats.FLOAT,
63
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
64
- unit: 'V'
65
- });
66
- this.value = this.getDefaultValue();
67
- this.UUID = charUUID;
68
- };
69
- eDomoticzServices.GasConsumption = function() {
70
- var charUUID = UUID.generate('eDomoticz:customchar:CurrentConsumption');
71
- Characteristic.call(this, 'Meter Total', charUUID);
72
- this.setProps({
73
- format: Characteristic.Formats.FLOAT,
74
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]
75
- });
76
- this.value = this.getDefaultValue();
77
- this.UUID = charUUID;
78
- };
79
- eDomoticzServices.WaterFlow = function() {
80
- var charUUID = UUID.generate('eDomoticz:customchar:WaterFlow');
81
- Characteristic.call(this, 'Flow Rate', charUUID);
82
- this.setProps({
83
- format: Characteristic.Formats.FLOAT,
84
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
85
- unit: 'm3'
86
- });
87
- this.value = this.getDefaultValue();
88
- this.UUID = charUUID;
89
- };
90
- eDomoticzServices.TotalWaterFlow = function() {
91
- var charUUID = UUID.generate('eDomoticz:customchar:TotalWaterFlow');
92
- Characteristic.call(this, 'Flow Total', charUUID);
93
- this.setProps({
94
- format: Characteristic.Formats.FLOAT,
95
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
96
- unit: 'l'
97
- });
98
- this.value = this.getDefaultValue();
99
- this.UUID = charUUID;
100
- };
101
- // Custom SetPoint Minutes characteristic for TempOverride modes
102
- eDomoticzServices.TempOverride = function() {
103
- var charUUID = UUID.generate('eDomoticz:customchar:OverrideTime');
104
- Characteristic.call(this, 'Override (Mins, 0 = Auto, 481 = Permanent)', charUUID);
105
- this.setProps({
106
- format: Characteristic.Formats.FLOAT,
107
- maxValue: 481,
108
- minValue: 0,
109
- minStep: 1,
110
- unit: 'mins',
111
- perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]
112
- });
113
- this.value = this.getDefaultValue();
114
- this.UUID = charUUID;
115
- };
116
- // Ampere Meter
117
- eDomoticzServices.AMPDeviceService = function(displayName, subtype) {
118
- var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
119
- Service.call(this, displayName, serviceUUID, subtype);
120
- this.addCharacteristic(new eDomoticzServices.Ampere());
121
- };
122
- // Voltage Meter
123
- eDomoticzServices.VOLTDeviceService = function(displayName, subtype) {
124
- var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
125
- Service.call(this, displayName, serviceUUID, subtype);
126
- this.addCharacteristic(new eDomoticzServices.Volt());
127
- };
38
+ /* ===== Custom Characteristics (20) ===== */
128
39
 
129
- // The PowerMeter itself
130
- eDomoticzServices.MeterDeviceService = function(displayName, subtype) {
40
+ // PowerMeter Characteristics
41
+ eDomoticzServices.TotalConsumption = class extends Characteristic {
42
+ constructor() {
43
+ var charUUID = 'E863F10C-079E-48FF-8F27-9C2605A29F52'; //UUID.generate('eDomoticz:customchar:TotalConsumption');
44
+ super('Total Consumption', charUUID);
45
+ this.setProps({
46
+ format: Formats.FLOAT,
47
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
48
+ unit: 'kWh'
49
+ });
50
+ this.value = this.getDefaultValue();
51
+ this.UUID = charUUID;
52
+ }
53
+ };
131
54
 
132
- var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
133
- Service.call(this, displayName, serviceUUID, subtype);
134
- this.addCharacteristic(new eDomoticzServices.CurrentConsumption());
135
- this.addOptionalCharacteristic(new eDomoticzServices.TotalConsumption());
136
- this.addOptionalCharacteristic(new eDomoticzServices.TodayConsumption());
137
- };
138
- // Waterflow Meter
139
- eDomoticzServices.WaterDeviceService = function(displayName, subtype) {
140
- var serviceUUID = UUID.generate('eDomoticz:watermeter:customservice');
141
- Service.call(this, displayName, serviceUUID, subtype);
142
- this.addCharacteristic(new eDomoticzServices.WaterFlow());
143
- this.addOptionalCharacteristic(new eDomoticzServices.TotalWaterFlow());
144
- };
145
- // P1 Smart Meter -> Gas
146
- eDomoticzServices.GasDeviceService = function(displayName, subtype) {
147
- var serviceUUID = UUID.generate('eDomoticz:gasmeter:customservice');
148
- Service.call(this, displayName, serviceUUID, subtype);
149
- this.addCharacteristic(new eDomoticzServices.GasConsumption());
150
- };
151
- // Usage Meter Characteristics
152
- eDomoticzServices.CurrentUsage = function() {
153
- var charUUID = UUID.generate('eDomoticz:customchar:CurrentUsage');
154
- Characteristic.call(this, 'Current Usage', charUUID);
155
- this.setProps({
156
- format: Characteristic.Formats.FLOAT,
157
- unit: Characteristic.Units.PERCENTAGE,
158
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
159
- minValue:0,
160
- maxValue:100,
161
- minStep:0.1
162
- });
163
- this.value = this.getDefaultValue();
164
- this.UUID = charUUID;
165
- };
166
- // The Usage Meter itself
167
- eDomoticzServices.UsageDeviceService = function(displayName, subtype) {
168
- var serviceUUID = UUID.generate('eDomoticz:usagedevice:customservice');
169
- Service.call(this, displayName, serviceUUID, subtype);
170
- this.addCharacteristic(new eDomoticzServices.CurrentUsage());
171
- };
172
- // Location Meter (sensor should have 'Location' in title)
173
- eDomoticzServices.Location = function() {
174
- var charUUID = UUID.generate('eDomoticz:customchar:Location');
175
- Characteristic.call(this, 'Location', charUUID);
176
- this.setProps({
177
- format: Characteristic.Formats.STRING,
178
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]
179
- });
180
- this.value = this.getDefaultValue();
181
- this.UUID = charUUID;
182
- };
183
- eDomoticzServices.LocationService = function(displayName, subtype) {
184
- var serviceUUID = UUID.generate('eDomoticz:location:customservice');
185
- Service.call(this, displayName, serviceUUID, subtype);
186
- this.addCharacteristic(new eDomoticzServices.Location());
187
- };
188
- // DarkSkies WindSpeed Characteristic
189
- eDomoticzServices.WindSpeed = function() {
190
- var charUUID = '49C8AE5A-A3A5-41AB-BF1F-12D5654F9F41';//'9331096F-E49E-4D98-B57B-57803498FA36'; //UUID.generate('eDomoticz:customchar:WindSpeed');
191
- Characteristic.call(this, 'Wind Speed', charUUID);
192
- this.setProps({
193
- format: Characteristic.Formats.FLOAT,
194
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
195
- unit:'m/s',
196
- minValue:0,
197
- maxValue:360,
198
- minStep:0.1
199
- });
200
- this.value = this.getDefaultValue();
201
- this.UUID = charUUID;
202
- };
203
- // DarkSkies WindChill Characteristic
204
- eDomoticzServices.WindChill = function() {
205
- var charUUID = UUID.generate('eDomoticz:customchar:WindChill');
206
- Characteristic.call(this, 'Wind Chill', charUUID);
207
- this.setProps({
208
- format: Characteristic.Formats.FLOAT,
209
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
210
- unit: Characteristic.Units.CELSIUS,
211
- minValue:-50,
212
- maxValue:100,
213
- minStep:0.1
214
- });
215
- this.value = this.getDefaultValue();
216
- this.UUID = charUUID;
217
- };
218
- // DarkSkies WindDirection Characteristic
219
- eDomoticzServices.WindDirection = function() {
220
- var charUUID = '46f1284c-1912-421b-82f5-eb75008b167e';//'6C3F6DFA-7340-4ED4-AFFD-0E0310ECCD9E'; //UUID.generate('eDomoticz:customchar:WindDirection');
221
- Characteristic.call(this, 'Wind Direction', charUUID);
222
- this.setProps({
223
- format: Characteristic.Formats.INT,
224
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
225
- unit: Characteristic.Units.ARC_DEGREE,
226
- minValue:0,
227
- maxValue:360,
228
- minStep:1
229
- });
230
- this.value = this.getDefaultValue();
231
- this.UUID = charUUID;
232
- };
233
- // DarkSkies Virtual Wind Sensor
234
- eDomoticzServices.WindDeviceService = function(displayName, subtype) {
235
- var serviceUUID = '2AFB775E-79E5-4399-B3CD-398474CAE86C'; //UUID.generate('eDomoticz:winddevice:customservice');
236
- Service.call(this, displayName, serviceUUID, subtype);
237
- this.addCharacteristic(new eDomoticzServices.WindSpeed());
238
- this.addOptionalCharacteristic(new eDomoticzServices.WindChill());
239
- this.addOptionalCharacteristic(new eDomoticzServices.WindDirection());
240
- this.addOptionalCharacteristic(new Characteristic.CurrentTemperature());
241
- };
242
- // DarkSkies Rain Characteristics
243
- eDomoticzServices.Rainfall = function() {
244
- var charUUID = 'ccc04890-565b-4376-b39a-3113341d9e0f';//'C53F35CE-C615-4AA4-9112-EBF679C5EB14'; //UUID.generate('eDomoticz:customchar:Rainfall');
245
- Characteristic.call(this, 'Amount today', charUUID);
246
- this.setProps({
247
- format: Characteristic.Formats.FLOAT,
248
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
249
- unit: 'mm',
250
- minValue:0,
251
- maxValue:360,
252
- minStep:0.1
253
- });
254
- this.value = this.getDefaultValue();
255
- this.UUID = charUUID;
256
- };
257
- // DarkSkies Rain Meter itself
258
- eDomoticzServices.RainDeviceService = function(displayName, subtype) {
259
- var serviceUUID = 'D92D5391-92AF-4824-AF4A-356F25F25EA1'; //UUID.generate('eDomoticz:raindevice:customservice');
260
- Service.call(this, displayName, serviceUUID, subtype);
261
- this.addCharacteristic(new eDomoticzServices.Rainfall());
262
- };
263
- // DarkSkies Visibility Characteristics
264
- eDomoticzServices.Visibility = function() {
265
- var charUUID = 'd24ecc1e-6fad-4fb5-8137-5af88bd5e857';//UUID.generate('eDomoticz:customchar:Visibility');
266
- Characteristic.call(this, 'Distance', charUUID);
267
- this.setProps({
268
- format: Characteristic.Formats.FLOAT,
269
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
270
- unit: 'miles',
271
- minValue:0,
272
- maxValue:20,
273
- minStep:0.1
274
- });
275
- this.value = this.getDefaultValue();
276
- this.UUID = charUUID;
277
- };
278
- // DarkSkies Visibility Meter itself
279
- eDomoticzServices.VisibilityDeviceService = function(displayName, subtype) {
280
- var serviceUUID = UUID.generate('eDomoticz:visibilitydevice:customservice');
281
- Service.call(this, displayName, serviceUUID, subtype);
282
- this.addCharacteristic(new eDomoticzServices.Visibility());
283
- };
284
- // DarkSkies UVIndex Characteristics
285
- eDomoticzServices.UVIndex = function() {
286
- var charUUID = '05ba0fe0-b848-4226-906d-5b64272e05ce';//UUID.generate('eDomoticz:customchar:Visibility');
287
- Characteristic.call(this, 'UVIndex', charUUID);
288
- this.setProps({
289
- format: Characteristic.Formats.FLOAT,
290
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
291
- unit: 'UVI',
292
- minValue:0,
293
- maxValue:20,
294
- minStep:0.1
295
- });
296
- this.value = this.getDefaultValue();
297
- this.UUID = charUUID;
298
- };
55
+ eDomoticzServices.TodayConsumption = class extends Characteristic {
56
+ constructor() {
57
+ var charUUID = UUID.generate('eDomoticz:customchar:TodayConsumption');
58
+ super('Today', charUUID);
59
+ this.setProps({
60
+ format: Formats.FLOAT,
61
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
62
+ unit: 'kWh'
63
+ });
64
+ this.value = this.getDefaultValue();
65
+ this.UUID = charUUID;
66
+ }
67
+ };
299
68
 
300
- // DarkSkies UV Index Meter itself
301
- eDomoticzServices.UVDeviceService = function(displayName, subtype) {
302
- var serviceUUID = UUID.generate('eDomoticz:uvdevice:customservice');
303
- Service.call(this, displayName, serviceUUID, subtype);
304
- this.addCharacteristic(new eDomoticzServices.UVIndex());
305
- };
306
- // DarkSkies Solar Radiation Characteristics
307
- eDomoticzServices.SolRad = function() {
308
- var charUUID = UUID.generate('eDomoticz:customchar:SolRad');
309
- Characteristic.call(this, 'Radiation', charUUID);
310
- this.setProps({
311
- format: Characteristic.Formats.FLOAT,
312
- unit: 'W/m2',
313
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
314
- minValue:0,
315
- maxValue:10000,
316
- minStep:0.1
317
- });
318
- this.value = this.getDefaultValue();
319
- this.UUID = charUUID;
320
- };
321
- // DarkSkies Solar Radiation Meter itself
322
- eDomoticzServices.SolRadDeviceService = function(displayName, subtype) {
323
- var serviceUUID = UUID.generate('eDomoticz:solraddevice:customservice');
324
- Service.call(this, displayName, serviceUUID, subtype);
325
- this.addCharacteristic(new eDomoticzServices.SolRad());
326
- };
327
- // Barometer Characteristic
328
- eDomoticzServices.Barometer = function() {
329
- var charUUID = 'E863F10F-079E-48FF-8F27-9C2605A29F52';
330
- Characteristic.call(this, 'Pressure', charUUID);
331
- this.setProps({
332
- format: Characteristic.Formats.FLOAT,
333
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY],
334
- unit: 'hPA',
335
- minValue: 500,
336
- maxValue: 2000,
337
- minStep: 0.1
338
- });
339
- this.value = this.getDefaultValue();
340
- this.UUID = charUUID;
341
- };
342
- // Weather Service
343
- eDomoticzServices.WeatherService = function(displayName, subtype) {
344
- var serviceUUID = 'debf1b79-312e-47f7-bf82-993d9950f3a2';//'E863F001-079E-48FF-8F27-9C2605A29F52';
345
- Service.call(this, displayName, serviceUUID, subtype);
346
- this.addCharacteristic(new Characteristic.CurrentTemperature());
347
- this.addOptionalCharacteristic(new Characteristic.CurrentRelativeHumidity());
348
- this.addOptionalCharacteristic(new eDomoticzServices.Barometer());
349
- };
350
- // DarkSkies Visibility Characteristics
351
- eDomoticzServices.Infotext = function() {
352
- var charUUID = UUID.generate('eDomoticz:customchar:Infotext');
353
- Characteristic.call(this, 'Infotext', charUUID);
354
- this.setProps({
355
- format: 'string',
356
- perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]
357
- });
358
- this.value = this.getDefaultValue();
359
- this.UUID = charUUID;
360
- };
361
- // DarkSkies Visibility Meter itself
362
- eDomoticzServices.InfotextDeviceService = function(displayName, subtype) {
363
- var serviceUUID = UUID.generate('eDomoticz:infotextdevice:customservice');
364
- Service.call(this, displayName, serviceUUID, subtype);
365
- this.addCharacteristic(new eDomoticzServices.Infotext());
366
- };
367
- /* End of Custom Services & Characteristics */
69
+ eDomoticzServices.CurrentConsumption = class extends Characteristic {
70
+ constructor() {
71
+ var charUUID = 'E863F10D-079E-48FF-8F27-9C2605A29F52'; //UUID.generate('eDomoticz:customchar:CurrentConsumption');
72
+ super('Consumption', charUUID);
73
+ this.setProps({
74
+ format: Formats.FLOAT,
75
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
76
+ unit: 'W'
77
+ });
78
+ this.value = this.getDefaultValue();
79
+ this.UUID = charUUID;
80
+ }
81
+ };
82
+
83
+ eDomoticzServices.Ampere = class extends Characteristic {
84
+ constructor() {
85
+ var charUUID = 'E863F126-079E-48FF-8F27-9C2605A29F52'; //AMPERE
86
+ super('Amps', charUUID);
87
+ this.setProps({
88
+ format: Formats.FLOAT,
89
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
90
+ unit: 'A'
91
+ });
92
+ this.value = this.getDefaultValue();
93
+ this.UUID = charUUID;
94
+ }
95
+ };
96
+
97
+ eDomoticzServices.Volt = class extends Characteristic {
98
+ constructor() {
99
+ var charUUID = 'E863F10A-079E-48FF-8F27-9C2605A29F52'; //VOLT
100
+ super('Volts', charUUID);
101
+ this.setProps({
102
+ format: Formats.FLOAT,
103
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
104
+ unit: 'V'
105
+ });
106
+ this.value = this.getDefaultValue();
107
+ this.UUID = charUUID;
108
+ }
109
+ };
110
+
111
+ // NOTE: UUID key intentionally reuses 'CurrentConsumption' string — preserved
112
+ // verbatim from upstream (historical collision, do not "fix").
113
+ eDomoticzServices.GasConsumption = class extends Characteristic {
114
+ constructor() {
115
+ var charUUID = UUID.generate('eDomoticz:customchar:CurrentConsumption');
116
+ super('Meter Total', charUUID);
117
+ this.setProps({
118
+ format: Formats.FLOAT,
119
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY]
120
+ });
121
+ this.value = this.getDefaultValue();
122
+ this.UUID = charUUID;
123
+ }
124
+ };
125
+
126
+ eDomoticzServices.WaterFlow = class extends Characteristic {
127
+ constructor() {
128
+ var charUUID = UUID.generate('eDomoticz:customchar:WaterFlow');
129
+ super('Flow Rate', charUUID);
130
+ this.setProps({
131
+ format: Formats.FLOAT,
132
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
133
+ unit: 'm3'
134
+ });
135
+ this.value = this.getDefaultValue();
136
+ this.UUID = charUUID;
137
+ }
138
+ };
139
+
140
+ eDomoticzServices.TotalWaterFlow = class extends Characteristic {
141
+ constructor() {
142
+ var charUUID = UUID.generate('eDomoticz:customchar:TotalWaterFlow');
143
+ super('Flow Total', charUUID);
144
+ this.setProps({
145
+ format: Formats.FLOAT,
146
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
147
+ unit: 'l'
148
+ });
149
+ this.value = this.getDefaultValue();
150
+ this.UUID = charUUID;
151
+ }
152
+ };
153
+
154
+ // Custom SetPoint Minutes characteristic for TempOverride modes
155
+ eDomoticzServices.TempOverride = class extends Characteristic {
156
+ constructor() {
157
+ var charUUID = UUID.generate('eDomoticz:customchar:OverrideTime');
158
+ super('Override (Mins, 0 = Auto, 481 = Permanent)', charUUID);
159
+ this.setProps({
160
+ format: Formats.FLOAT,
161
+ maxValue: 481,
162
+ minValue: 0,
163
+ minStep: 1,
164
+ unit: 'mins',
165
+ perms: [Perms.PAIRED_READ, Perms.PAIRED_WRITE, Perms.NOTIFY]
166
+ });
167
+ this.value = this.getDefaultValue();
168
+ this.UUID = charUUID;
169
+ }
170
+ };
171
+
172
+ // Usage Meter Characteristics
173
+ eDomoticzServices.CurrentUsage = class extends Characteristic {
174
+ constructor() {
175
+ var charUUID = UUID.generate('eDomoticz:customchar:CurrentUsage');
176
+ super('Current Usage', charUUID);
177
+ this.setProps({
178
+ format: Formats.FLOAT,
179
+ unit: Units.PERCENTAGE,
180
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
181
+ minValue: 0,
182
+ maxValue: 100,
183
+ minStep: 0.1
184
+ });
185
+ this.value = this.getDefaultValue();
186
+ this.UUID = charUUID;
187
+ }
188
+ };
189
+
190
+ // Location Characteristic (sensor should have 'Location' in title)
191
+ eDomoticzServices.Location = class extends Characteristic {
192
+ constructor() {
193
+ var charUUID = UUID.generate('eDomoticz:customchar:Location');
194
+ super('Location', charUUID);
195
+ this.setProps({
196
+ format: Formats.STRING,
197
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY]
198
+ });
199
+ this.value = this.getDefaultValue();
200
+ this.UUID = charUUID;
201
+ }
202
+ };
203
+
204
+ // DarkSkies WindSpeed Characteristic
205
+ eDomoticzServices.WindSpeed = class extends Characteristic {
206
+ constructor() {
207
+ var charUUID = '49C8AE5A-A3A5-41AB-BF1F-12D5654F9F41';//'9331096F-E49E-4D98-B57B-57803498FA36'; //UUID.generate('eDomoticz:customchar:WindSpeed');
208
+ super('Wind Speed', charUUID);
209
+ this.setProps({
210
+ format: Formats.FLOAT,
211
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
212
+ unit: 'm/s',
213
+ minValue: 0,
214
+ maxValue: 360,
215
+ minStep: 0.1
216
+ });
217
+ this.value = this.getDefaultValue();
218
+ this.UUID = charUUID;
219
+ }
220
+ };
221
+
222
+ // DarkSkies WindChill Characteristic
223
+ eDomoticzServices.WindChill = class extends Characteristic {
224
+ constructor() {
225
+ var charUUID = UUID.generate('eDomoticz:customchar:WindChill');
226
+ super('Wind Chill', charUUID);
227
+ this.setProps({
228
+ format: Formats.FLOAT,
229
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
230
+ unit: Units.CELSIUS,
231
+ minValue: -50,
232
+ maxValue: 100,
233
+ minStep: 0.1
234
+ });
235
+ this.value = this.getDefaultValue();
236
+ this.UUID = charUUID;
237
+ }
238
+ };
239
+
240
+ // DarkSkies WindDirection Characteristic
241
+ eDomoticzServices.WindDirection = class extends Characteristic {
242
+ constructor() {
243
+ var charUUID = '46f1284c-1912-421b-82f5-eb75008b167e';//'6C3F6DFA-7340-4ED4-AFFD-0E0310ECCD9E'; //UUID.generate('eDomoticz:customchar:WindDirection');
244
+ super('Wind Direction', charUUID);
245
+ this.setProps({
246
+ format: Formats.INT,
247
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
248
+ unit: Units.ARC_DEGREE,
249
+ minValue: 0,
250
+ maxValue: 360,
251
+ minStep: 1
252
+ });
253
+ this.value = this.getDefaultValue();
254
+ this.UUID = charUUID;
255
+ }
256
+ };
257
+
258
+ // DarkSkies Rain Characteristic
259
+ eDomoticzServices.Rainfall = class extends Characteristic {
260
+ constructor() {
261
+ var charUUID = 'ccc04890-565b-4376-b39a-3113341d9e0f';//'C53F35CE-C615-4AA4-9112-EBF679C5EB14'; //UUID.generate('eDomoticz:customchar:Rainfall');
262
+ super('Amount today', charUUID);
263
+ this.setProps({
264
+ format: Formats.FLOAT,
265
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
266
+ unit: 'mm',
267
+ minValue: 0,
268
+ maxValue: 360,
269
+ minStep: 0.1
270
+ });
271
+ this.value = this.getDefaultValue();
272
+ this.UUID = charUUID;
273
+ }
274
+ };
275
+
276
+ // DarkSkies Visibility Characteristic
277
+ eDomoticzServices.Visibility = class extends Characteristic {
278
+ constructor() {
279
+ var charUUID = 'd24ecc1e-6fad-4fb5-8137-5af88bd5e857';//UUID.generate('eDomoticz:customchar:Visibility');
280
+ super('Distance', charUUID);
281
+ this.setProps({
282
+ format: Formats.FLOAT,
283
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
284
+ unit: 'miles',
285
+ minValue: 0,
286
+ maxValue: 20,
287
+ minStep: 0.1
288
+ });
289
+ this.value = this.getDefaultValue();
290
+ this.UUID = charUUID;
291
+ }
292
+ };
293
+
294
+ // DarkSkies UVIndex Characteristic
295
+ eDomoticzServices.UVIndex = class extends Characteristic {
296
+ constructor() {
297
+ var charUUID = '05ba0fe0-b848-4226-906d-5b64272e05ce';//UUID.generate('eDomoticz:customchar:Visibility');
298
+ super('UVIndex', charUUID);
299
+ this.setProps({
300
+ format: Formats.FLOAT,
301
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
302
+ unit: 'UVI',
303
+ minValue: 0,
304
+ maxValue: 20,
305
+ minStep: 0.1
306
+ });
307
+ this.value = this.getDefaultValue();
308
+ this.UUID = charUUID;
309
+ }
310
+ };
311
+
312
+ // DarkSkies Solar Radiation Characteristic
313
+ eDomoticzServices.SolRad = class extends Characteristic {
314
+ constructor() {
315
+ var charUUID = UUID.generate('eDomoticz:customchar:SolRad');
316
+ super('Radiation', charUUID);
317
+ this.setProps({
318
+ format: Formats.FLOAT,
319
+ unit: 'W/m2',
320
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
321
+ minValue: 0,
322
+ maxValue: 10000,
323
+ minStep: 0.1
324
+ });
325
+ this.value = this.getDefaultValue();
326
+ this.UUID = charUUID;
327
+ }
328
+ };
329
+
330
+ // Barometer Characteristic
331
+ eDomoticzServices.Barometer = class extends Characteristic {
332
+ constructor() {
333
+ var charUUID = 'E863F10F-079E-48FF-8F27-9C2605A29F52';
334
+ super('Pressure', charUUID);
335
+ this.setProps({
336
+ format: Formats.FLOAT,
337
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY],
338
+ unit: 'hPA',
339
+ minValue: 500,
340
+ maxValue: 2000,
341
+ minStep: 0.1
342
+ });
343
+ this.value = this.getDefaultValue();
344
+ this.UUID = charUUID;
345
+ }
346
+ };
347
+
348
+ // Infotext Characteristic (free-form string)
349
+ eDomoticzServices.Infotext = class extends Characteristic {
350
+ constructor() {
351
+ var charUUID = UUID.generate('eDomoticz:customchar:Infotext');
352
+ super('Infotext', charUUID);
353
+ this.setProps({
354
+ format: 'string',
355
+ perms: [Perms.PAIRED_READ, Perms.NOTIFY]
356
+ });
357
+ this.value = this.getDefaultValue();
358
+ this.UUID = charUUID;
359
+ }
360
+ };
361
+
362
+ /* ===== Custom Services (14) ===== */
363
+
364
+ // Ampere Meter
365
+ // NOTE: serviceUUID key intentionally shared with VOLT and Meter device
366
+ // services (historical upstream behaviour, differentiated by subtype).
367
+ eDomoticzServices.AMPDeviceService = class extends Service {
368
+ constructor(displayName, subtype) {
369
+ var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
370
+ super(displayName, serviceUUID, subtype);
371
+ this.addCharacteristic(new eDomoticzServices.Ampere());
372
+ }
373
+ };
374
+
375
+ // Voltage Meter
376
+ eDomoticzServices.VOLTDeviceService = class extends Service {
377
+ constructor(displayName, subtype) {
378
+ var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
379
+ super(displayName, serviceUUID, subtype);
380
+ this.addCharacteristic(new eDomoticzServices.Volt());
381
+ }
382
+ };
383
+
384
+ // The PowerMeter itself
385
+ eDomoticzServices.MeterDeviceService = class extends Service {
386
+ constructor(displayName, subtype) {
387
+ var serviceUUID = UUID.generate('eDomoticz:powermeter:customservice');
388
+ super(displayName, serviceUUID, subtype);
389
+ this.addCharacteristic(new eDomoticzServices.CurrentConsumption());
390
+ this.addOptionalCharacteristic(new eDomoticzServices.TotalConsumption());
391
+ this.addOptionalCharacteristic(new eDomoticzServices.TodayConsumption());
392
+ }
393
+ };
394
+
395
+ // Waterflow Meter
396
+ eDomoticzServices.WaterDeviceService = class extends Service {
397
+ constructor(displayName, subtype) {
398
+ var serviceUUID = UUID.generate('eDomoticz:watermeter:customservice');
399
+ super(displayName, serviceUUID, subtype);
400
+ this.addCharacteristic(new eDomoticzServices.WaterFlow());
401
+ this.addOptionalCharacteristic(new eDomoticzServices.TotalWaterFlow());
402
+ }
403
+ };
404
+
405
+ // P1 Smart Meter -> Gas
406
+ eDomoticzServices.GasDeviceService = class extends Service {
407
+ constructor(displayName, subtype) {
408
+ var serviceUUID = UUID.generate('eDomoticz:gasmeter:customservice');
409
+ super(displayName, serviceUUID, subtype);
410
+ this.addCharacteristic(new eDomoticzServices.GasConsumption());
411
+ }
412
+ };
413
+
414
+ // The Usage Meter itself
415
+ eDomoticzServices.UsageDeviceService = class extends Service {
416
+ constructor(displayName, subtype) {
417
+ var serviceUUID = UUID.generate('eDomoticz:usagedevice:customservice');
418
+ super(displayName, serviceUUID, subtype);
419
+ this.addCharacteristic(new eDomoticzServices.CurrentUsage());
420
+ }
421
+ };
422
+
423
+ eDomoticzServices.LocationService = class extends Service {
424
+ constructor(displayName, subtype) {
425
+ var serviceUUID = UUID.generate('eDomoticz:location:customservice');
426
+ super(displayName, serviceUUID, subtype);
427
+ this.addCharacteristic(new eDomoticzServices.Location());
428
+ }
429
+ };
430
+
431
+ // DarkSkies Virtual Wind Sensor
432
+ eDomoticzServices.WindDeviceService = class extends Service {
433
+ constructor(displayName, subtype) {
434
+ var serviceUUID = '2AFB775E-79E5-4399-B3CD-398474CAE86C'; //UUID.generate('eDomoticz:winddevice:customservice');
435
+ super(displayName, serviceUUID, subtype);
436
+ this.addCharacteristic(new eDomoticzServices.WindSpeed());
437
+ this.addOptionalCharacteristic(new eDomoticzServices.WindChill());
438
+ this.addOptionalCharacteristic(new eDomoticzServices.WindDirection());
439
+ this.addOptionalCharacteristic(new Characteristic.CurrentTemperature());
440
+ }
441
+ };
442
+
443
+ // DarkSkies Rain Meter itself
444
+ eDomoticzServices.RainDeviceService = class extends Service {
445
+ constructor(displayName, subtype) {
446
+ var serviceUUID = 'D92D5391-92AF-4824-AF4A-356F25F25EA1'; //UUID.generate('eDomoticz:raindevice:customservice');
447
+ super(displayName, serviceUUID, subtype);
448
+ this.addCharacteristic(new eDomoticzServices.Rainfall());
449
+ }
450
+ };
451
+
452
+ // DarkSkies Visibility Meter itself
453
+ eDomoticzServices.VisibilityDeviceService = class extends Service {
454
+ constructor(displayName, subtype) {
455
+ var serviceUUID = UUID.generate('eDomoticz:visibilitydevice:customservice');
456
+ super(displayName, serviceUUID, subtype);
457
+ this.addCharacteristic(new eDomoticzServices.Visibility());
458
+ }
459
+ };
460
+
461
+ // DarkSkies UV Index Meter itself
462
+ eDomoticzServices.UVDeviceService = class extends Service {
463
+ constructor(displayName, subtype) {
464
+ var serviceUUID = UUID.generate('eDomoticz:uvdevice:customservice');
465
+ super(displayName, serviceUUID, subtype);
466
+ this.addCharacteristic(new eDomoticzServices.UVIndex());
467
+ }
468
+ };
469
+
470
+ // DarkSkies Solar Radiation Meter itself
471
+ eDomoticzServices.SolRadDeviceService = class extends Service {
472
+ constructor(displayName, subtype) {
473
+ var serviceUUID = UUID.generate('eDomoticz:solraddevice:customservice');
474
+ super(displayName, serviceUUID, subtype);
475
+ this.addCharacteristic(new eDomoticzServices.SolRad());
476
+ }
477
+ };
478
+
479
+ // Weather Service (Temp + Humidity + Baro composite, surfaced as Eve Weather)
480
+ eDomoticzServices.WeatherService = class extends Service {
481
+ constructor(displayName, subtype) {
482
+ var serviceUUID = 'debf1b79-312e-47f7-bf82-993d9950f3a2';//'E863F001-079E-48FF-8F27-9C2605A29F52';
483
+ super(displayName, serviceUUID, subtype);
484
+ this.addCharacteristic(new Characteristic.CurrentTemperature());
485
+ this.addOptionalCharacteristic(new Characteristic.CurrentRelativeHumidity());
486
+ this.addOptionalCharacteristic(new eDomoticzServices.Barometer());
487
+ }
488
+ };
489
+
490
+ // Infotext Device Service
491
+ eDomoticzServices.InfotextDeviceService = class extends Service {
492
+ constructor(displayName, subtype) {
493
+ var serviceUUID = UUID.generate('eDomoticz:infotextdevice:customservice');
494
+ super(displayName, serviceUUID, subtype);
495
+ this.addCharacteristic(new eDomoticzServices.Infotext());
496
+ }
497
+ };
498
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "homebridge-edomoticz",
3
- "version": "2.1.49",
3
+ "version": "3.0.4",
4
4
  "description": "homebridge-plugin for Domoticz https://github.com/nfarina/homebridge",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "node test/load-plugin.js"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
@@ -17,14 +17,18 @@
17
17
  "Domoticz"
18
18
  ],
19
19
  "engines": {
20
- "node": ">=0.12.0",
21
- "homebridge": ">=0.2.5"
20
+ "node": ">=18.15.0",
21
+ "homebridge": ">=1.8.0 || >=2.0.0"
22
22
  },
23
23
  "dependencies": {
24
24
  "inherits": "^2.0.1",
25
25
  "mqtt": "^2.15.0",
26
26
  "request": "^2.81.0"
27
27
  },
28
+ "devDependencies": {
29
+ "@homebridge/hap-nodejs": "^2.1.0",
30
+ "homebridge": "^2.0.0"
31
+ },
28
32
  "author": {
29
33
  "name": "PatchworkBoy",
30
34
  "url": "aka Marci"
@@ -36,7 +40,6 @@
36
40
  "homepage": "https://github.com/patchworkboy/homebridge-eDomoticz#readme",
37
41
  "readme": "# homebridge-eDomoticz\nA fully-fledged up-to-date Homebridge-Plugin\nfor use with [Homebridge](https://github.com/nfarina/homebridge) v0.2.1+\nand [Domoticz](https://github.com/domoticz/domoticz)\n\n##Supports:\n###Standard HomeKit Types:\n- Sockets (on/off) - Domoticz SwitchTypeVal: 0\n- Lamps (on/off) - Domoticz SwitchTypeVal: 0\n- Contact Sensors - Domoticz SwitchTypeVal: 2\n- Blinds - Domoticz SwitchTypeVal: 3\n- Smoke Detectors - Domoticz SwitchTypeVal: 5\n- Blinds (inverted) - Domoticz SwitchTypeVal: 6\n- Lamps (dimmer) - Domoticz SwitchTypeVal: 7\n- Motion Sensors - Domoticz SwitchTypeVal: 8\n- Push Switches - Domoticz SwitchTypeVal: 9\n- Lock Mechanisms - Domoticz SwitchTypeVal: 11\n- Blinds (%) - Domoticz SwitchTypeVal: 13\n- Blinds (& inverted) - Domoticz SwitchTypeVal: 16\n\n##Provides:\n###Custom HomeKit Types:\n- General kWh power meters - Types: General, Current; SubType: kWh, mapped to Eve chars where possible\n- CurrentCost USB power meter - Type: Usage, SubType: Electric, mapped to Eve chars where possible\n- P1 Smart Meter (Electric & Gas), mapped to Eve chars where possible\n- EvoHome** / OpenTherm Thermostat support - Types: Heating, Thermostat; SubTypes: Zone, SetPoint\n- YouLess Meter (Current, Total and Today Total Consumption) - Type: YouLess Meter; SubType: YouLess counter, mapped to Eve chars where possible\n- General Usage % meters (eg: Motherboard Sensors Hardware Device - CPU %, Mem %, HDD % etc) - Type: General; SubType: Percentage\n- Temperature, Temp + Humidity, Temp + Humidity + Baro (Current Temperature, Current Humidity, Current Pressure in hPA) - Type: Temp, Temp + Humidty, Temp + Humidity + Baro [id'd as Eve Weather]\n- DarkSkies Virtual Weather Station Sensors (Wind, Solar Radiation, Rainfall, Visibility, Barometer [id'd as Eve Weather])\n\n** assumes the EvoHome has been setup according to [this script method](https://www.domoticz.com/wiki/Evohome#Scripting_for_RFG100).\n\n###Todo:\n- [ ] homebridge [plugin 2.0](https://github.com/nfarina/homebridge/pull/497) support\n- [x] MQTT-based realtime updates\n- [x] Hue/RGB\n- [x] Blinds\n- [x] m3 (gas usage)\n- [x] Motion sensors\n- [x] Smoke Detectors\n- [x] Brightness/Dimming\n- [x] kWh (electricity usage)\n- [x] General % usage\n- [x] Humidity\n- [x] Pressure\n- [x] YouLess Meter\n- [x] Open/Closed contact sensors\n- [ ] ...more sensors (ongoing)!\n\n## Installation\n```\nsudo npm install -g homebridge-edomoticz\n```\n\n## Update\n```\nsudo npm update -g homebridge-edomoticz\n```\n\n## Configuration\n\n~/.homebridge/config.json example:\n```\n{\n \"bridge\": {\n \"name\": \"Homebridge\",\n \"username\": \"CC:21:3E:E4:DE:33\",\n \"port\": 51826,\n \"pin\": \"031-45-154\"\n },\n \"description\": \"Configuration file for (e)xtended Domoticz platform.\",\n \"platforms\": [\n {\n \"platform\": \"eDomoticz\",\n \"name\": \"eDomoticz\",\n \"server\": \"127.0.0.1\",\n \"port\": \"8080\",\n \"ssl\": 0,\n \"roomid\": 0,\n \"mqttenable\": 1,\n \"mqttserver\": \"127.0.0.1\",\n \"mqttport\": \"1883\",\n \"mqttauth\": 0,\n \"mqttuser\": \"\",\n \"mqttpass\": \"\"\n }\n ],\n \"accessories\": []\n}\n```\n\n## Tips\n\n### Authentication\nIf Domoticz is set up to use basic or form login authentication, set \"server\":\"user:pass@ip\" within config.json. The plugin will internally extract the username and password, base64 encode it and send it as a http authorization header whenever it talks to your Domoticz server.\n\n### SSL\nSet \"ssl\":1 in config.json to turn on SSL (ie: server connects with https:// rather than http://). You will need to specify your SSL port - usually \"port\":\"443\" by default.\n\n### Issues pairing to Homebridge when you have a lot of Domoticz sensors...\nIf you have more than 100 devices in Domoticz, you need to limit the number of devices exposed to HomeKit (HomeKit only supports 100 Accessories on a single bridge - whilst we could combine multiple sensors into a single homekit accessory within the plugin, the possible combinations out there are endless, so we won't).\n\nTherefore, to reduce the number of devices exposed from Domoticz, create a roomplan within Domoticz via Setup > More Options > Plans > roomplan. Add only the devices you wish to be exposed to HomeKit to this new roomplan within Domoticz, and then get it's roomidx number. Set \"roomid\" in your config.json file to this room number.\n\n### Is my <<some accessory>> supported??\nSee [Domoticz API Reference](https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Retrieve_status_of_specific_device) - query your device as per the instructions there, and if your device’s SwitchTypeVal isn't in the 'Supports:' list or Type/SubType aren’t in the ’Provides:' list above then it'll just appear as an On/Off switch. [Open a new issue](https://github.com/PatchworkBoy/homebridge-eDomoticz/issues/new) including the output from the json api and I’ll get look into supporting that particular device more fully!\n\n### What does the Override slider represent on the EvoHome Thermostat?\nOverride-Until time in minutes from the current time. Allows setting an override-until time upto 8 hours in the future. Setting this slider to 0 will set the heating mode to Auto. Setting it to 481 will set the override as a PermanentOverride.\n\n### Logging\nComplies with Homebridge's native logging & debugging methodology - see https://github.com/nfarina/homebridge/wiki/Basic-Trouble-Shooting\n",
38
42
  "readmeFilename": "README.md",
39
- "_id": "homebridge-edomoticz@2.1.8",
40
43
  "_from": "homebridge-edomoticz",
41
44
  "_resolved": "file:homebridge-edomoticz"
42
45
  }
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+ /*
3
+ * test/load-plugin.js
4
+ *
5
+ * Smoke test for the Homebridge 2.x / HAP-NodeJS v2 compatibility migration.
6
+ *
7
+ * It does NOT load the full plugin (index.js) on purpose: index.js pulls
8
+ * domoticz_accessory.js + request + mqtt etc., and we want a focused check
9
+ * on the migration itself. Instead it replicates what index.js does on
10
+ * startup -- read Service/Characteristic/uuid from HAP, call initServices --
11
+ * and then instantiates a representative battery of subclasses.
12
+ *
13
+ * The original v2.1.50 plugin crashed under HAP-NodeJS v2 with:
14
+ * TypeError: Class constructor Service cannot be invoked without 'new'
15
+ * at new eDomoticzServices.MeterDeviceService (lib/services.js:133:13)
16
+ * This test will catch any regression of that bug.
17
+ *
18
+ * Coverage:
19
+ * - Characteristic with fixed Eve UUID -> CurrentConsumption
20
+ * - Characteristic with UUID.generate -> TodayConsumption
21
+ * - Characteristic with historical UUID collision -> GasConsumption
22
+ * - The 3 Services that share 'eDomoticz:powermeter:customservice'
23
+ * (differentiated only by subtype) -> AMP / VOLT / Meter
24
+ * - Service with fixed UUID -> WindDeviceService
25
+ * - Service that composes a HAP stock Characteristic
26
+ * (Characteristic.CurrentTemperature) -> WeatherService
27
+ *
28
+ * Exit codes:
29
+ * 0 -> all instantiations succeeded
30
+ * 2 -> @homebridge/hap-nodejs not installed (run `npm install` first)
31
+ * 3 -> initServices threw
32
+ * 1 -> at least one instantiation threw
33
+ */
34
+
35
+ let hap;
36
+ try {
37
+ hap = require('@homebridge/hap-nodejs');
38
+ } catch (e) {
39
+ console.error('FAIL: @homebridge/hap-nodejs is not installed. Run `npm install` first.');
40
+ console.error(e && e.stack ? e.stack : e);
41
+ process.exit(2);
42
+ }
43
+
44
+ // 1. Load the migrated services module
45
+ const Services = require('../lib/services.js');
46
+
47
+ // 2. Replicate what index.js does on startup: hand HAP refs to the factory.
48
+ // The 4th argument (the hap module itself) lets initServices resolve the
49
+ // Formats/Perms/Units enums under HAP v2, where they live on the module
50
+ // root rather than on the Characteristic class.
51
+ try {
52
+ Services.initServices(hap.Service, hap.Characteristic, hap.uuid, hap);
53
+ } catch (e) {
54
+ console.error('FAIL: Services.initServices threw.');
55
+ console.error(e && e.stack ? e.stack : e);
56
+ process.exit(3);
57
+ }
58
+
59
+ const eDomoticzServices = Services.eDomoticzServices;
60
+
61
+ // 3. Instantiate representatives
62
+ const cases = [
63
+ // --- Characteristics ---
64
+ {
65
+ label: 'Characteristic, fixed Eve UUID (CurrentConsumption)',
66
+ run: function () { return new eDomoticzServices.CurrentConsumption(); },
67
+ },
68
+ {
69
+ label: 'Characteristic, UUID.generate (TodayConsumption)',
70
+ run: function () { return new eDomoticzServices.TodayConsumption(); },
71
+ },
72
+ {
73
+ label: 'Characteristic, historical UUID collision (GasConsumption)',
74
+ run: function () { return new eDomoticzServices.GasConsumption(); },
75
+ },
76
+
77
+ // --- The 3 Services sharing UUID 'eDomoticz:powermeter:customservice' ---
78
+ {
79
+ label: "Service, shared UUID key, subtype='sub-amp' (AMPDeviceService)",
80
+ run: function () { return new eDomoticzServices.AMPDeviceService('AMP', 'sub-amp'); },
81
+ },
82
+ {
83
+ label: "Service, shared UUID key, subtype='sub-volt' (VOLTDeviceService)",
84
+ run: function () { return new eDomoticzServices.VOLTDeviceService('VOLT', 'sub-volt'); },
85
+ },
86
+ {
87
+ label: "Service, shared UUID key, subtype='sub-meter' (MeterDeviceService) -- original crash site",
88
+ run: function () { return new eDomoticzServices.MeterDeviceService('Meter', 'sub-meter'); },
89
+ },
90
+
91
+ // --- Other shapes ---
92
+ {
93
+ label: 'Service, fixed UUID (WindDeviceService)',
94
+ run: function () { return new eDomoticzServices.WindDeviceService('Wind', 'sub-wind'); },
95
+ },
96
+ {
97
+ label: 'Service composing HAP stock + custom Characteristic (WeatherService)',
98
+ run: function () { return new eDomoticzServices.WeatherService('Weather', 'sub-weather'); },
99
+ },
100
+ ];
101
+
102
+ let ok = 0;
103
+ let failed = 0;
104
+ const collisionUUIDs = [];
105
+
106
+ for (const c of cases) {
107
+ try {
108
+ const inst = c.run();
109
+ if (!inst) throw new Error('instance is falsy');
110
+ ok++;
111
+ // Capture the UUID of the three colliding services so we can assert
112
+ // afterwards that they really do share the same UUID and differ only
113
+ // in subtype (the historical upstream invariant).
114
+ if (/AMPDeviceService|VOLTDeviceService|MeterDeviceService/.test(c.label)) {
115
+ collisionUUIDs.push({ label: c.label.split(' (')[1].replace(')', ''), uuid: inst.UUID, subtype: inst.subtype });
116
+ }
117
+ console.log(' OK ' + c.label + ' -> UUID=' + inst.UUID + (inst.subtype ? ', subtype=' + inst.subtype : ''));
118
+ } catch (e) {
119
+ failed++;
120
+ const stack = e && e.stack ? e.stack.split('\n').slice(0, 4).join('\n ') : String(e);
121
+ console.error(' FAIL ' + c.label);
122
+ console.error(' ' + stack);
123
+ }
124
+ }
125
+
126
+ // Assert every custom Characteristic is readable on the wire.
127
+ // HAP-NodeJS v2 removed the deprecated Perms.READ / Perms.WRITE aliases
128
+ // (only PAIRED_READ / PAIRED_WRITE remain), so a stale alias silently
129
+ // resolves to undefined and the characteristic is published with
130
+ // perms [null, "ev"]. iOS rejects the whole bridge at pairing with
131
+ // "Accessory out of compliance" (upstream PR #297 report), and HAP-NodeJS
132
+ // itself answers reads with WRITE_ONLY_CHARACTERISTIC. Regression guard.
133
+ {
134
+ const hapPerms = hap.Perms || (hap.Characteristic && hap.Characteristic.Perms);
135
+ const PR = hapPerms.PAIRED_READ;
136
+ let checked = 0;
137
+ let bad = [];
138
+ for (const name of Object.keys(eDomoticzServices)) {
139
+ const Ctor = eDomoticzServices[name];
140
+ if (typeof Ctor !== 'function' || !(Ctor.prototype instanceof hap.Characteristic)) continue;
141
+ const inst = new Ctor();
142
+ checked++;
143
+ const perms = (inst.props && inst.props.perms) || [];
144
+ if (perms.some(function (x) { return x == null; }) || perms.indexOf(PR) === -1) {
145
+ bad.push(name + ' perms=' + JSON.stringify(perms));
146
+ }
147
+ }
148
+ if (bad.length) {
149
+ failed++;
150
+ console.error(' FAIL perms invariant: ' + bad.length + ' custom Characteristic(s) not readable / have null perms:');
151
+ bad.forEach(function (b) { console.error(' ' + b); });
152
+ } else {
153
+ console.log(' OK perms invariant: ' + checked + ' custom Characteristics all carry PAIRED_READ and no null perms');
154
+ }
155
+ }
156
+
157
+ // Assert cached characteristics with poisoned perms get healed on restore.
158
+ // Simulates what Homebridge hands to configureAccessory: a PlatformAccessory
159
+ // whose services were deserialized from cachedAccessories, where custom
160
+ // characteristics are generic hap.Characteristic instances carrying the
161
+ // persisted props. A cache written by 3.0.0-3.0.2 under HAP v2 has
162
+ // perms [null, "ev"]; Helper.healCustomCharacteristicPerms must fix exactly
163
+ // those and leave healthy ones (and non-perms props) alone.
164
+ {
165
+ const Helper = require('../lib/helper.js').Helper;
166
+ const hapPerms = hap.Perms || (hap.Characteristic && hap.Characteristic.Perms);
167
+ const PR = hapPerms.PAIRED_READ;
168
+ const good = new eDomoticzServices.Barometer();
169
+ const poisoned = hap.Characteristic.deserialize({
170
+ displayName: good.displayName,
171
+ UUID: good.UUID,
172
+ props: { format: good.props.format, unit: good.props.unit, perms: [null, 'ev'] },
173
+ value: 1013,
174
+ });
175
+ const healthy = hap.Characteristic.deserialize({
176
+ displayName: 'Consumption',
177
+ UUID: new eDomoticzServices.CurrentConsumption().UUID,
178
+ props: { format: 'float', unit: 'W', perms: [PR, 'ev'] },
179
+ value: 12,
180
+ });
181
+ const svc = new hap.Service('Cached Weather', new eDomoticzServices.WeatherService('x').UUID, 'sub');
182
+ svc.addCharacteristic(poisoned);
183
+ svc.addCharacteristic(healthy);
184
+ const fakePlatformAccessory = { services: [svc] };
185
+
186
+ const healed = Helper.healCustomCharacteristicPerms(fakePlatformAccessory, eDomoticzServices, hap.Characteristic, PR);
187
+ const after = poisoned.props.perms;
188
+ const problems = [];
189
+ if (healed !== 1) problems.push('expected exactly 1 healed characteristic, got ' + healed);
190
+ if (after.indexOf(PR) === -1 || after.some(function (x) { return x == null; })) problems.push('poisoned perms not repaired: ' + JSON.stringify(after));
191
+ if (poisoned.props.unit !== good.props.unit) problems.push('unit was altered: ' + poisoned.props.unit);
192
+ if (JSON.stringify(healthy.props.perms) !== JSON.stringify([PR, 'ev'])) problems.push('healthy perms were altered: ' + JSON.stringify(healthy.props.perms));
193
+ const second = Helper.healCustomCharacteristicPerms(fakePlatformAccessory, eDomoticzServices, hap.Characteristic, PR);
194
+ if (second !== 0) problems.push('heal is not idempotent, second pass healed ' + second);
195
+ if (problems.length) {
196
+ failed++;
197
+ console.error(' FAIL cache heal:');
198
+ problems.forEach(function (m) { console.error(' ' + m); });
199
+ } else {
200
+ console.log(' OK cache heal: poisoned [null,"ev"] -> ' + JSON.stringify(after) + ', healthy untouched, idempotent');
201
+ }
202
+ }
203
+
204
+ // Assert the historical collision is preserved: same UUID across the 3
205
+ // services, distinct subtypes.
206
+ if (failed === 0 && collisionUUIDs.length === 3) {
207
+ const uniqueUUIDs = new Set(collisionUUIDs.map(function (x) { return x.uuid; }));
208
+ const uniqueSubtypes = new Set(collisionUUIDs.map(function (x) { return x.subtype; }));
209
+ if (uniqueUUIDs.size !== 1) {
210
+ failed++;
211
+ console.error(' FAIL collision invariant: the 3 powermeter services should share one UUID, got ' + uniqueUUIDs.size + ' distinct UUIDs.');
212
+ } else if (uniqueSubtypes.size !== 3) {
213
+ failed++;
214
+ console.error(' FAIL collision invariant: subtypes should be distinct, got ' + uniqueSubtypes.size + '.');
215
+ } else {
216
+ console.log(' OK collision invariant: 3 services share UUID ' + Array.from(uniqueUUIDs)[0] + ' with 3 distinct subtypes');
217
+ }
218
+ }
219
+
220
+ if (failed) {
221
+ console.error('\n' + failed + ' check(s) failed.');
222
+ process.exit(1);
223
+ }
224
+
225
+ console.log('\nOK: ' + ok + ' instanciaciones completadas');
226
+ console.log('Carga + instanciacion OK');
227
+ process.exit(0);
package/.npmignore DELETED
@@ -1,3 +0,0 @@
1
-
2
- *.log
3
- node_modules