homebridge-envoy-solar-sensor 0.1.3 → 0.1.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/config.schema.json +76 -70
- package/index.js +44 -13
- package/package.json +1 -1
package/config.schema.json
CHANGED
|
@@ -1,74 +1,80 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
2
|
+
"pluginAlias": "EnvoySolarSensor",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"schema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["host"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": {
|
|
10
|
+
"title": "Accessory Name",
|
|
11
|
+
"type": "string",
|
|
12
|
+
"default": "Solar Production",
|
|
13
|
+
"description": "Name shown in HomeKit"
|
|
14
|
+
},
|
|
15
|
+
"host": {
|
|
16
|
+
"title": "Envoy Address",
|
|
17
|
+
"type": "string",
|
|
18
|
+
"default": "envoy.local",
|
|
19
|
+
"description": "IP address or hostname of the Enphase Envoy on your local network"
|
|
20
|
+
},
|
|
21
|
+
"protocol": {
|
|
22
|
+
"title": "Protocol",
|
|
23
|
+
"type": "string",
|
|
24
|
+
"default": "https",
|
|
25
|
+
"oneOf": [
|
|
26
|
+
{ "title": "HTTPS", "enum": ["https"] },
|
|
27
|
+
{ "title": "HTTP", "enum": ["http"] }
|
|
28
|
+
],
|
|
29
|
+
"description": "Protocol used to connect to the Envoy"
|
|
30
|
+
},
|
|
31
|
+
"allowInsecureTLS": {
|
|
32
|
+
"title": "Allow Insecure TLS",
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": true,
|
|
35
|
+
"description": "Enable this if your Envoy uses a self-signed HTTPS certificate"
|
|
36
|
+
},
|
|
37
|
+
"mode": {
|
|
38
|
+
"title": "Envoy API Mode",
|
|
39
|
+
"type": "string",
|
|
40
|
+
"default": "productionJson",
|
|
41
|
+
"oneOf": [
|
|
42
|
+
{
|
|
43
|
+
"title": "production.json",
|
|
44
|
+
"enum": ["productionJson"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"title": "api/v1/production",
|
|
48
|
+
"enum": ["v1Production"]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"description": "Select the Envoy API endpoint to read production data"
|
|
52
|
+
},
|
|
53
|
+
"pollIntervalSeconds": {
|
|
54
|
+
"title": "Poll Interval (seconds)",
|
|
55
|
+
"type": "integer",
|
|
56
|
+
"default": 10,
|
|
57
|
+
"minimum": 5,
|
|
58
|
+
"description": "How often the Envoy is polled for production data"
|
|
59
|
+
},
|
|
60
|
+
"onThresholdW": {
|
|
61
|
+
"title": "On Threshold (W)",
|
|
62
|
+
"type": "integer",
|
|
63
|
+
"default": 80,
|
|
64
|
+
"description": "Production level above which the sensor becomes active"
|
|
65
|
+
},
|
|
66
|
+
"offThresholdW": {
|
|
67
|
+
"title": "Off Threshold (W)",
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"default": 30,
|
|
70
|
+
"description": "Production level below which the sensor becomes inactive"
|
|
71
|
+
},
|
|
72
|
+
"token": {
|
|
73
|
+
"title": "Authentication Token",
|
|
74
|
+
"type": "string",
|
|
75
|
+
"default": "",
|
|
76
|
+
"description": "Bearer token from Enphase Entrez, paste token only without the word Bearer"
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
|
-
|
|
80
|
+
}
|
package/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { Agent } = require('undici');
|
|
4
|
+
|
|
3
5
|
const PLUGIN_NAME = 'homebridge-envoy-solar-sensor';
|
|
4
6
|
const PLATFORM_NAME = 'EnvoySolarSensor';
|
|
5
7
|
|
|
@@ -31,10 +33,11 @@ class EnvoySolarPlatform {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
setupAccessory() {
|
|
34
|
-
const name = this.config.name || '
|
|
36
|
+
const name = this.config.name || 'Solar Production';
|
|
35
37
|
const host = this.config.host;
|
|
38
|
+
|
|
36
39
|
if (!host) {
|
|
37
|
-
this.log.error('Config mist host. Voorbeeld: "
|
|
40
|
+
this.log.error('Config mist host. Voorbeeld host: "envoy.local" of "192.168.1.50"');
|
|
38
41
|
return;
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -56,14 +59,18 @@ class EnvoySolarPlatform {
|
|
|
56
59
|
.setCharacteristic(this.Characteristic.SerialNumber, String(host));
|
|
57
60
|
|
|
58
61
|
const service = this.accessory.getService(this.Service.ContactSensor)
|
|
59
|
-
|| this.accessory.addService(this.Service.ContactSensor, '
|
|
62
|
+
|| this.accessory.addService(this.Service.ContactSensor, 'Production Active', 'production-active');
|
|
60
63
|
|
|
61
|
-
service.setCharacteristic(this.Characteristic.Name, '
|
|
64
|
+
service.setCharacteristic(this.Characteristic.Name, 'Production Active');
|
|
62
65
|
|
|
63
66
|
if (service.testCharacteristic(this.Characteristic.StatusActive)) {
|
|
64
67
|
service.updateCharacteristic(this.Characteristic.StatusActive, true);
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
if (service.testCharacteristic(this.Characteristic.StatusFault)) {
|
|
71
|
+
service.updateCharacteristic(this.Characteristic.StatusFault, this.Characteristic.StatusFault.NO_FAULT);
|
|
72
|
+
}
|
|
73
|
+
|
|
67
74
|
this.log.info(`Accessoire klaar: ${name} op host ${host}`);
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -78,7 +85,8 @@ class EnvoySolarPlatform {
|
|
|
78
85
|
const watts = await this.readProductionWatts();
|
|
79
86
|
this.updateState(watts);
|
|
80
87
|
} catch (err) {
|
|
81
|
-
|
|
88
|
+
const msg = err && err.message ? err.message : String(err);
|
|
89
|
+
this.log.warn(`Uitlezen mislukt: ${msg}`);
|
|
82
90
|
this.markFault();
|
|
83
91
|
}
|
|
84
92
|
};
|
|
@@ -102,12 +110,18 @@ class EnvoySolarPlatform {
|
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
getToken() {
|
|
105
|
-
|
|
113
|
+
const t = this.config.token;
|
|
114
|
+
if (!t) return '';
|
|
115
|
+
return String(t).trim();
|
|
106
116
|
}
|
|
107
117
|
|
|
108
118
|
getBaseUrl() {
|
|
109
|
-
const
|
|
110
|
-
return `${
|
|
119
|
+
const protocol = this.config.protocol || 'https';
|
|
120
|
+
return `${protocol}://${this.getHost()}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
isInsecureTLSEnabled() {
|
|
124
|
+
return Boolean(this.config.allowInsecureTLS);
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
markFault() {
|
|
@@ -180,10 +194,19 @@ class EnvoySolarPlatform {
|
|
|
180
194
|
const productionArray = data && data.production;
|
|
181
195
|
if (!Array.isArray(productionArray)) throw new Error('production.json mist production array');
|
|
182
196
|
|
|
183
|
-
const
|
|
184
|
-
|
|
197
|
+
const byType = (t) => productionArray.find((x) => x && x.type === t);
|
|
198
|
+
|
|
199
|
+
const eim = byType('eim');
|
|
200
|
+
const inverters = byType('inverters');
|
|
201
|
+
const production = byType('production');
|
|
202
|
+
|
|
203
|
+
const candidate = eim || production || inverters || productionArray[0];
|
|
204
|
+
const wNow = candidate && candidate.wNow;
|
|
205
|
+
|
|
206
|
+
if (typeof wNow !== 'number') {
|
|
207
|
+
throw new Error('production.json mist wNow in production items');
|
|
208
|
+
}
|
|
185
209
|
|
|
186
|
-
if (typeof wNow !== 'number') throw new Error('production.json mist wNow (type eim)');
|
|
187
210
|
return Math.max(0, wNow);
|
|
188
211
|
}
|
|
189
212
|
|
|
@@ -199,13 +222,21 @@ class EnvoySolarPlatform {
|
|
|
199
222
|
|
|
200
223
|
async fetchJson(url) {
|
|
201
224
|
const headers = { Accept: 'application/json' };
|
|
225
|
+
|
|
202
226
|
const token = this.getToken();
|
|
203
227
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
204
228
|
|
|
205
|
-
const
|
|
229
|
+
const allowInsecureTLS = this.isInsecureTLSEnabled();
|
|
230
|
+
|
|
231
|
+
const dispatcher = url.startsWith('https://')
|
|
232
|
+
? new Agent({ connect: { rejectUnauthorized: !allowInsecureTLS } })
|
|
233
|
+
: undefined;
|
|
234
|
+
|
|
235
|
+
const res = await fetch(url, { headers, dispatcher });
|
|
206
236
|
|
|
207
237
|
if (!res.ok) {
|
|
208
|
-
|
|
238
|
+
const text = await res.text().catch(() => '');
|
|
239
|
+
throw new Error(`HTTP ${res.status} op ${url} ${text}`);
|
|
209
240
|
}
|
|
210
241
|
|
|
211
242
|
return await res.json();
|
package/package.json
CHANGED