homebridge-enphase-envoy 10.3.1-beta.9 → 10.3.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.
- package/README.md +3 -3
- package/config.schema.json +1 -3
- package/package.json +2 -2
- package/src/constants.js +0 -4
- package/src/customcharacteristics.js +16 -0
- package/src/envoydata.js +48 -52
- package/src/envoydevice.js +42 -48
- package/src/functions.js +24 -3
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ The `homebridge-enphase-envoy` plugin integrates Enphase Envoy solar energy moni
|
|
|
23
23
|
|
|
24
24
|
| Package | Installation | Role | Required |
|
|
25
25
|
| --- | --- | --- | --- |
|
|
26
|
-
| [Homebridge](https://github.com/homebridge/homebridge) | [Homebridge Wiki](https://github.com/homebridge/homebridge/wiki) | HomeKit Bridge | Required |
|
|
27
|
-
| [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) | [Homebridge UI Wiki](https://github.com/homebridge/homebridge-config-ui-x/wiki) | Homebridge Web User Interface |
|
|
26
|
+
| [Homebridge v2.0.0](https://github.com/homebridge/homebridge) | [Homebridge Wiki](https://github.com/homebridge/homebridge/wiki) | HomeKit Bridge | Required |
|
|
27
|
+
| [Homebridge UI <= v5.5.0](https://github.com/homebridge/homebridge-config-ui-x) | [Homebridge UI Wiki](https://github.com/homebridge/homebridge-config-ui-x/wiki) | Homebridge Web User Interface | Required |
|
|
28
28
|
| [Enphase Envoy](https://www.npmjs.com/package/homebridge-enphase-envoy) | [Plug-In Wiki](https://github.com/grzegorz914/homebridge-enphase-envoy/wiki) | Homebridge Plug-In | Required |
|
|
29
29
|
|
|
30
30
|
## Supported hardware
|
|
@@ -90,7 +90,7 @@ The `homebridge-enphase-envoy` plugin integrates Enphase Envoy solar energy moni
|
|
|
90
90
|
### Configuration
|
|
91
91
|
|
|
92
92
|
* Running this plugin as a [Child Bridge](https://github.com/homebridge/homebridge/wiki/Child-Bridges) is **highly recommended**. This prevents Homebridge from crashing if the plugin crashes.
|
|
93
|
-
* Installation and use of [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) to configure this plugin
|
|
93
|
+
* Installation and use of [Homebridge UI <= v5.5.0](https://github.com/homebridge/homebridge-config-ui-x) to configure this plugin.
|
|
94
94
|
* The `sample-config.json` can be edited and used as an alternative for advanced users.
|
|
95
95
|
|
|
96
96
|
<p align="center">
|
package/config.schema.json
CHANGED
|
@@ -1432,8 +1432,7 @@
|
|
|
1432
1432
|
]
|
|
1433
1433
|
}
|
|
1434
1434
|
],
|
|
1435
|
-
"description": "Accessory type for Home app"
|
|
1436
|
-
"required": true
|
|
1435
|
+
"description": "Accessory type for Home app"
|
|
1437
1436
|
},
|
|
1438
1437
|
"name": {
|
|
1439
1438
|
"title": "Accessory name",
|
|
@@ -3066,7 +3065,6 @@
|
|
|
3066
3065
|
]
|
|
3067
3066
|
}
|
|
3068
3067
|
],
|
|
3069
|
-
"required": true,
|
|
3070
3068
|
"condition": {
|
|
3071
3069
|
"functionBody": "return model.devices[arrayIndices[0]].generatorModeControls[arrayIndices[1]].displayType > 0;"
|
|
3072
3070
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"private": false,
|
|
3
3
|
"displayName": "Enphase Envoy",
|
|
4
4
|
"name": "homebridge-enphase-envoy",
|
|
5
|
-
"version": "10.3.1
|
|
5
|
+
"version": "10.3.1",
|
|
6
6
|
"description": "Homebridge p7ugin for Photovoltaic Energy System manufactured by Enphase.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "grzegorz914",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"async-mqtt": "^2.6.3",
|
|
39
|
-
"axios": "^1.
|
|
39
|
+
"axios": "^1.13.1",
|
|
40
40
|
"express": "^5.1.0",
|
|
41
41
|
"fast-xml-parser": "^5.3.0",
|
|
42
42
|
"fakegato-history": "^0.6.7"
|
package/src/constants.js
CHANGED
|
@@ -174,13 +174,10 @@ export const ApiCodes = {
|
|
|
174
174
|
"ENCHG_STATE_DISCHARGING": "Encharge state discharging",
|
|
175
175
|
"ENCHG_STATE_IDLE": "Encharge state idle",
|
|
176
176
|
"ENCHG_STATE_READY": "Encharge state ready",
|
|
177
|
-
"ENCMN_MDE_BMU_READY": "Encharge BMU ready",
|
|
178
177
|
"ENCMN_MDE_ENCHARGE_READY": "Encharge mode ready",
|
|
179
178
|
"ENCMN_MDE_ON_GRID": "Encharge mode on grid",
|
|
180
179
|
"ENCMN_MDE_OFF_GRID": "Encharge mode off grid",
|
|
181
|
-
"ENCMN_MDE_PCU_READY": "Encharge Microinverter ready",
|
|
182
180
|
"ENCMN_C6_CC_READY": "C6 Combiner Controller ready",
|
|
183
|
-
"ENCMN_C6_RGM_DEV_CONNECTED": "C6 Revenue Grade Meter connected",
|
|
184
181
|
"ENPOWER": "Enpower",
|
|
185
182
|
"ENS_DEVICE_STATE_READY": "Ensemble state ready",
|
|
186
183
|
"ENPWR_STATE_GRIDMODE_CONFIRM": "Enpower state grid mode confirm",
|
|
@@ -205,7 +202,6 @@ export const ApiCodes = {
|
|
|
205
202
|
"check-wiring": "Check Wiring",
|
|
206
203
|
"close": "Close",
|
|
207
204
|
"closed": "Closed",
|
|
208
|
-
"configured": "Configured",
|
|
209
205
|
"connected": "Connected",
|
|
210
206
|
"consumption": "Consumption Net",
|
|
211
207
|
"discharging": "Discharging",
|
|
@@ -1554,6 +1554,22 @@ export default (api) => {
|
|
|
1554
1554
|
}
|
|
1555
1555
|
Characteristic.EncAggSoc = EncAggSoc;
|
|
1556
1556
|
|
|
1557
|
+
class EncAggRatedPower extends Characteristic {
|
|
1558
|
+
constructor() {
|
|
1559
|
+
super('ENC rated power', '00000208-000B-1000-8000-0026BB765291');
|
|
1560
|
+
this.setProps({
|
|
1561
|
+
format: Formats.FLOAT,
|
|
1562
|
+
unit: 'kW',
|
|
1563
|
+
maxValue: 1000,
|
|
1564
|
+
minValue: -1000,
|
|
1565
|
+
minStep: 0.001,
|
|
1566
|
+
perms: [Perms.PAIRED_READ, Perms.NOTIFY]
|
|
1567
|
+
});
|
|
1568
|
+
this.value = this.getDefaultValue();
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
Characteristic.EncAggRatedPower = EncAggRatedPower;
|
|
1572
|
+
|
|
1557
1573
|
class EncAggBackupEnergy extends Characteristic {
|
|
1558
1574
|
constructor() {
|
|
1559
1575
|
super('ENC backup energy', '00000209-000B-1000-8000-0026BB765291');
|
package/src/envoydata.js
CHANGED
|
@@ -338,6 +338,7 @@ class EnvoyData extends EventEmitter {
|
|
|
338
338
|
devices: [],
|
|
339
339
|
settings: {},
|
|
340
340
|
tariff: {},
|
|
341
|
+
tariffRaw: {},
|
|
341
342
|
ratedPowerSumKw: null,
|
|
342
343
|
realPowerSumKw: null,
|
|
343
344
|
phaseA: false,
|
|
@@ -412,7 +413,7 @@ class EnvoyData extends EventEmitter {
|
|
|
412
413
|
.on('updateEnsemble', () => this.handleWithLock('updateEnsemble', async () => {
|
|
413
414
|
const updateEnsemble = this.feature.ensemble.inventory.supported ? await this.updateEnsembleInventory() : false;
|
|
414
415
|
if (updateEnsemble && this.feature.ensemble.status.supported) await this.updateEnsembleStatus();
|
|
415
|
-
if (updateEnsemble && this.feature.
|
|
416
|
+
if (updateEnsemble && this.feature.inventory.esubs.encharges.power.supported) await this.updateEnsemblePower();
|
|
416
417
|
|
|
417
418
|
const updateEnchargeSettings = updateEnsemble && this.feature.inventory.esubs.encharges.settings.supported ? await this.updateEnchargesSettings() : false;
|
|
418
419
|
if (updateEnchargeSettings && this.feature.inventory.esubs.encharges.tariff.supported) await this.updateTariff();
|
|
@@ -587,13 +588,12 @@ class EnvoyData extends EventEmitter {
|
|
|
587
588
|
// Load token from file on startup, only if mode is 1
|
|
588
589
|
if (this.envoyFirmware7xxTokenGenerationMode === 1 && start) {
|
|
589
590
|
try {
|
|
590
|
-
const data = await this.functions.readData(this.envoyTokenFile);
|
|
591
|
+
const data = await this.functions.readData(this.envoyTokenFile, true);
|
|
591
592
|
try {
|
|
592
|
-
const
|
|
593
|
-
const fileTokenExist = parsedData.token ? 'Exist' : 'Missing';
|
|
593
|
+
const fileTokenExist = data.token ? 'Exist' : 'Missing';
|
|
594
594
|
if (this.logDebug) this.emit('debug', `Token from file: ${fileTokenExist}`);
|
|
595
|
-
if (
|
|
596
|
-
this.feature.info.jwtToken =
|
|
595
|
+
if (data.token) {
|
|
596
|
+
this.feature.info.jwtToken = data;
|
|
597
597
|
}
|
|
598
598
|
} catch (error) {
|
|
599
599
|
if (this.logWarn) this.emit('warn', `Token parse error: ${error}`);
|
|
@@ -906,36 +906,33 @@ class EnvoyData extends EventEmitter {
|
|
|
906
906
|
const home = response.data;
|
|
907
907
|
if (this.logDebug) this.emit('debug', 'Home:', home);
|
|
908
908
|
|
|
909
|
+
const comm = home.comm ?? {};
|
|
910
|
+
const network = home.network ?? {};
|
|
911
|
+
const wirelessConnections = home.wireless_connection ?? [];
|
|
912
|
+
const networkInterfaces = network.interfaces ?? [];
|
|
913
|
+
|
|
909
914
|
// Communication device support flags
|
|
910
|
-
const microinvertersSupported = 'pcu' in
|
|
911
|
-
const acBatteriesSupported = 'acb' in
|
|
912
|
-
const qRelaysSupported = 'nsrb' in
|
|
913
|
-
const ensemblesSupported = 'esub' in
|
|
914
|
-
const enchargesSupported = 'encharge' in
|
|
915
|
+
const microinvertersSupported = 'pcu' in comm;
|
|
916
|
+
const acBatteriesSupported = 'acb' in comm;
|
|
917
|
+
const qRelaysSupported = 'nsrb' in comm;
|
|
918
|
+
const ensemblesSupported = 'esub' in comm;
|
|
919
|
+
const enchargesSupported = 'encharge' in comm;
|
|
920
|
+
this.pv.home = home;
|
|
915
921
|
|
|
922
|
+
// Update feature flags
|
|
916
923
|
this.feature.inventory.pcus.supported = microinvertersSupported;
|
|
917
924
|
this.feature.inventory.acbs.supported = acBatteriesSupported;
|
|
918
925
|
this.feature.inventory.nsrbs.supported = qRelaysSupported;
|
|
919
926
|
this.feature.inventory.esubs.supported = ensemblesSupported;
|
|
920
927
|
this.feature.inventory.esubs.encharges.supported = enchargesSupported;
|
|
921
928
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
this.feature.home.networkInterfaces.supported = networkInterfacesSupported;
|
|
926
|
-
this.feature.home.networkInterfaces.installed = home.network.interfaces.length > 0;
|
|
927
|
-
this.feature.home.networkInterfaces.count = home.network.interfaces.length;
|
|
928
|
-
}
|
|
929
|
+
this.feature.home.networkInterfaces.supported = networkInterfaces.length > 0;
|
|
930
|
+
this.feature.home.networkInterfaces.installed = networkInterfaces.some(i => i.carrier);
|
|
931
|
+
this.feature.home.networkInterfaces.count = networkInterfaces.length;
|
|
929
932
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
this.feature.home.wirelessConnections.supported = wirelessConnectionsSupported;
|
|
934
|
-
this.feature.home.wirelessConnections.installed = home.wireless_connection.length > 0;
|
|
935
|
-
this.feature.home.wirelessConnections.count = home.wireless_connection.length;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
this.pv.home = home;
|
|
933
|
+
this.feature.home.wirelessConnections.supported = wirelessConnections.length > 0;
|
|
934
|
+
this.feature.home.wirelessConnections.installed = wirelessConnections.some(w => w.connected);
|
|
935
|
+
this.feature.home.wirelessConnections.count = wirelessConnections.length;
|
|
939
936
|
this.feature.home.supported = true;
|
|
940
937
|
|
|
941
938
|
// RESTful + MQTT
|
|
@@ -1965,7 +1962,7 @@ class EnvoyData extends EventEmitter {
|
|
|
1965
1962
|
};
|
|
1966
1963
|
|
|
1967
1964
|
// Calculate encharges rated power summary in kW
|
|
1968
|
-
this.pv.inventory.esubs.encharges.ratedPowerSumKw = enchargesRatedPowerSummary.length > 0 ? enchargesRatedPowerSummary.reduce((total, num) => total + num, 0) / 1000 : null;
|
|
1965
|
+
this.pv.inventory.esubs.encharges.ratedPowerSumKw = enchargesRatedPowerSummary.length > 0 ? (enchargesRatedPowerSummary.reduce((total, num) => total + num, 0) / enchargesRatedPowerSummary.length) / 1000 : null;
|
|
1969
1966
|
}
|
|
1970
1967
|
|
|
1971
1968
|
// Update enpowers statuses if installed
|
|
@@ -2041,17 +2038,18 @@ class EnvoyData extends EventEmitter {
|
|
|
2041
2038
|
|
|
2042
2039
|
try {
|
|
2043
2040
|
const response = await this.axiosInstance.get(ApiUrls.EnsemblePower);
|
|
2044
|
-
const responseData = response.data
|
|
2041
|
+
const responseData = response.data;
|
|
2045
2042
|
if (this.logDebug) this.emit('debug', `Ensemble power response:`, responseData);
|
|
2046
2043
|
|
|
2047
|
-
const devices = responseData.devices
|
|
2048
|
-
if (devices.length === 0) return false;
|
|
2044
|
+
const devices = responseData.devices ?? [];
|
|
2045
|
+
if (!devices.length === 0) return false;
|
|
2049
2046
|
|
|
2050
2047
|
// update encharges
|
|
2051
2048
|
const enchargesRealPowerSummary = [];
|
|
2052
2049
|
const encharges = this.pv.inventory.esubs.encharges.devices || [];
|
|
2053
|
-
for (const
|
|
2054
|
-
const
|
|
2050
|
+
for (const device of devices) {
|
|
2051
|
+
const serialNumber = device.serial_num;
|
|
2052
|
+
const encharge = encharges.find(device => device.serialNumber === serialNumber);
|
|
2055
2053
|
if (this.logDebug) this.emit('debug', `Ensemble device power:`, device);
|
|
2056
2054
|
if (!device) continue;
|
|
2057
2055
|
|
|
@@ -2062,12 +2060,12 @@ class EnvoyData extends EventEmitter {
|
|
|
2062
2060
|
soc: device.soc,
|
|
2063
2061
|
};
|
|
2064
2062
|
|
|
2065
|
-
this.feature.inventory.esubs.encharges.power.supported = true;
|
|
2066
2063
|
if (this.functions.isValidValue(encharge.power.realPower)) enchargesRealPowerSummary.push(encharge.power.realPower);
|
|
2064
|
+
this.feature.inventory.esubs.encharges.power.supported = true;
|
|
2067
2065
|
}
|
|
2068
2066
|
|
|
2069
2067
|
// Calculate encharges real power summary in kW
|
|
2070
|
-
this.pv.inventory.esubs.encharges.realPowerSumKw = enchargesRealPowerSummary.length > 0 ? enchargesRealPowerSummary.reduce((total, num) => total + num, 0) /
|
|
2068
|
+
this.pv.inventory.esubs.encharges.realPowerSumKw = enchargesRealPowerSummary.length > 0 ? (enchargesRealPowerSummary.reduce((total, num) => total + num, 0) / enchargesRealPowerSummary.length) / 1000 : null;
|
|
2071
2069
|
|
|
2072
2070
|
// ensemble power supported
|
|
2073
2071
|
this.feature.ensemble.power.supported = true;
|
|
@@ -2093,14 +2091,15 @@ class EnvoyData extends EventEmitter {
|
|
|
2093
2091
|
const enchargesSettingsSupported = 'enc_settings' in enchargesSettings;
|
|
2094
2092
|
if (!enchargesSettingsSupported) return false;
|
|
2095
2093
|
|
|
2096
|
-
const settings =
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2094
|
+
const settings = enchargesSettings.enc_settings;
|
|
2095
|
+
const encharges = this.pv.inventory.esubs.encharges;
|
|
2096
|
+
encharges.settings = {
|
|
2097
|
+
enable: settings.enable, // boolean
|
|
2098
|
+
country: settings.country, // string
|
|
2099
|
+
currentLimit: settings.current_limit, // float
|
|
2100
|
+
perPhase: settings.per_phase // boolean
|
|
2101
2101
|
};
|
|
2102
2102
|
|
|
2103
|
-
this.pv.inventory.esubs.encharges.settings = settings;
|
|
2104
2103
|
this.feature.inventory.esubs.encharges.settings.supported = true;
|
|
2105
2104
|
|
|
2106
2105
|
// RESTFul and MQTT update
|
|
@@ -2119,11 +2118,12 @@ class EnvoyData extends EventEmitter {
|
|
|
2119
2118
|
try {
|
|
2120
2119
|
const response = await this.axiosInstance.get(ApiUrls.TariffSettingsGetPut);
|
|
2121
2120
|
const tariffSettings = response.data;
|
|
2121
|
+
|
|
2122
2122
|
if (this.logDebug) this.emit('debug', 'Tariff:', tariffSettings);
|
|
2123
2123
|
|
|
2124
2124
|
const enchargesTariffSupported = 'tariff' in tariffSettings;
|
|
2125
2125
|
if (!enchargesTariffSupported) return false;
|
|
2126
|
-
|
|
2126
|
+
this.pv.inventory.esubs.encharges.tariffRaw = tariffSettings;
|
|
2127
2127
|
this.pv.inventory.esubs.encharges.tariff = tariffSettings;
|
|
2128
2128
|
this.feature.inventory.esubs.encharges.tariff.supported = true;
|
|
2129
2129
|
|
|
@@ -2548,7 +2548,7 @@ class EnvoyData extends EventEmitter {
|
|
|
2548
2548
|
if (this.logDebug) this.emit('debug', `Requesting set encharge settings`);
|
|
2549
2549
|
|
|
2550
2550
|
try {
|
|
2551
|
-
const tariff = this.pv.inventory.esubs.encharges.
|
|
2551
|
+
const tariff = this.pv.inventory.esubs.encharges.tariffRaw.tariff;
|
|
2552
2552
|
tariff.storage_settings.mode = profile;
|
|
2553
2553
|
tariff.storage_settings.reserved_soc = reservedSoc;
|
|
2554
2554
|
tariff.storage_settings.charge_from_grid = chargeFromGrid;
|
|
@@ -2774,16 +2774,12 @@ class EnvoyData extends EventEmitter {
|
|
|
2774
2774
|
const getEnsemble = tokenRequired && this.feature.inventory.esubs.supported ? await this.updateEnsembleInventory() : false;
|
|
2775
2775
|
if (getEnsemble) {
|
|
2776
2776
|
await this.updateEnsembleStatus();
|
|
2777
|
-
if (this.feature.inventory.esubs.encharges.installed)
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
await this.updateTariff();
|
|
2781
|
-
}
|
|
2777
|
+
if (this.feature.inventory.esubs.encharges.installed) await this.updateEnsemblePower();
|
|
2778
|
+
const getEnchargeSettings = this.feature.inventory.esubs.encharges.installed ? await this.updateEnchargesSettings() : false;
|
|
2779
|
+
if (getEnchargeSettings) await this.updateTariff();
|
|
2782
2780
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
await this.updateDryContactsSettings();
|
|
2786
|
-
}
|
|
2781
|
+
const getDryContacts = this.feature.inventory.esubs.enpowers.installed ? await this.updateDryContacts() : false;
|
|
2782
|
+
if (getDryContacts) await this.updateDryContactsSettings();
|
|
2787
2783
|
|
|
2788
2784
|
const getGenerator = await this.updateGenerator();
|
|
2789
2785
|
if (getGenerator && this.feature.inventory.esubs.generator.installed) await this.updateGeneratorSettings();
|
package/src/envoydevice.js
CHANGED
|
@@ -1988,8 +1988,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
1988
1988
|
}
|
|
1989
1989
|
|
|
1990
1990
|
if (ensemblesCountersSupported) characteristics.push({ type: Characteristic.RestPower, label: 'rest power', value: counters.restPowerKw, unit: 'kW' });
|
|
1991
|
-
if (enchargesStatusSupported) characteristics.push({ type: Characteristic.RatedPower, label: 'rated power', value: this.pv.inventoryData.esubs.
|
|
1992
|
-
if (enchargesPowerSupported) characteristics.push({ type: Characteristic.RealPower, label: 'real power', value: this.pv.inventoryData.esubs.
|
|
1991
|
+
if (enchargesStatusSupported) characteristics.push({ type: Characteristic.RatedPower, label: 'rated power', value: this.pv.inventoryData.esubs.ratedPowerSumKw, unit: 'kW' });
|
|
1992
|
+
if (enchargesPowerSupported) characteristics.push({ type: Characteristic.RealPower, label: 'real power', value: this.pv.inventoryData.esubs.realPowerSumKw, unit: 'kW' });
|
|
1993
1993
|
|
|
1994
1994
|
for (const { type, label, value, unit = '', postfix = '' } of characteristics) {
|
|
1995
1995
|
if (!this.functions.isValidValue(value)) continue;
|
|
@@ -2257,14 +2257,22 @@ class EnvoyDevice extends EventEmitter {
|
|
|
2257
2257
|
{ type: Characteristic.ReadingTime, label: 'reading time', value: encharge.readingTime },
|
|
2258
2258
|
];
|
|
2259
2259
|
|
|
2260
|
-
if (gridProfileSupported)
|
|
2260
|
+
if (gridProfileSupported) {
|
|
2261
|
+
characteristics.push(
|
|
2262
|
+
{ type: Characteristic.GridProfile, label: 'grid profile', value: encharge.gridProfile }
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2261
2266
|
if (enchargesStatusSupported && encharge.status) {
|
|
2262
2267
|
characteristics.push(
|
|
2263
2268
|
{ type: Characteristic.CommInterface, label: 'comm interface', value: encharge.status.commInterfaceStr },
|
|
2264
2269
|
{ type: Characteristic.RatedPower, label: 'rated power', value: encharge.status.ratedPowerKw, unit: 'kW' }
|
|
2265
2270
|
);
|
|
2266
2271
|
}
|
|
2267
|
-
|
|
2272
|
+
|
|
2273
|
+
if (enchargesPowerSupported && encharge.power) {
|
|
2274
|
+
characteristics.push({ type: Characteristic.RealPower, label: 'real power', value: encharge.power.realPowerKw, unit: 'kW' });
|
|
2275
|
+
}
|
|
2268
2276
|
|
|
2269
2277
|
for (const { type, label, value, unit = '', postfix = '' } of characteristics) {
|
|
2270
2278
|
if (!this.functions.isValidValue(value)) continue;
|
|
@@ -2391,23 +2399,24 @@ class EnvoyDevice extends EventEmitter {
|
|
|
2391
2399
|
if (this.logDebug) this.emit('debug', `Prepare ${enchargeName} Profile Sensor Services`);
|
|
2392
2400
|
|
|
2393
2401
|
this.enchargeProfileSensorsServices = [];
|
|
2402
|
+
|
|
2394
2403
|
for (let i = 0; i < this.enchargeProfileSensors.length; i++) {
|
|
2395
2404
|
const sensor = this.enchargeProfileSensors[i];
|
|
2396
2405
|
const { namePrefix, name, serviceType, characteristicType } = sensor;
|
|
2397
2406
|
const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
|
|
2398
2407
|
|
|
2399
|
-
const
|
|
2400
|
-
|
|
2401
|
-
|
|
2408
|
+
const service = accessory.addService(serviceType, serviceName, `enchargeProfileSensorService${i}`);
|
|
2409
|
+
service.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
2410
|
+
service.setCharacteristic(Characteristic.ConfiguredName, serviceName);
|
|
2402
2411
|
|
|
2403
|
-
|
|
2412
|
+
service.getCharacteristic(characteristicType)
|
|
2404
2413
|
.onGet(async () => {
|
|
2405
2414
|
const currentState = sensor.state;
|
|
2406
2415
|
if (this.logInfo) this.emit('info', `${enchargeName} profile: ${name}, state: ${currentState ? 'Active' : 'Not Active'}`);
|
|
2407
2416
|
return currentState;
|
|
2408
2417
|
});
|
|
2409
2418
|
|
|
2410
|
-
this.enchargeProfileSensorsServices.push(
|
|
2419
|
+
this.enchargeProfileSensorsServices.push(service);
|
|
2411
2420
|
}
|
|
2412
2421
|
}
|
|
2413
2422
|
}
|
|
@@ -2686,7 +2695,7 @@ class EnvoyDevice extends EventEmitter {
|
|
|
2686
2695
|
{ type: Characteristic.AdminState, label: 'admin state', value: collar.adminStateStr },
|
|
2687
2696
|
{ type: Characteristic.Status, label: 'status', value: collar.deviceStatus },
|
|
2688
2697
|
{ type: Characteristic.MidState, label: 'mid state', value: collar.midState },
|
|
2689
|
-
{ type: Characteristic.GridState, label: '
|
|
2698
|
+
{ type: Characteristic.GridState, label: 'mid state', value: collar.gridState },
|
|
2690
2699
|
{ type: Characteristic.Communicating, label: 'communicating', value: collar.communicating, postfix: collar.communicating ? 'Yes' : 'No' },
|
|
2691
2700
|
{ type: Characteristic.Temperature, label: 'temperature', value: collar.temperature, unit: '°C' },
|
|
2692
2701
|
{ type: Characteristic.ReadingTime, label: 'reading time', value: collar.readingTime }
|
|
@@ -3108,6 +3117,7 @@ class EnvoyDevice extends EventEmitter {
|
|
|
3108
3117
|
this.emit('devInfo', `Firmware: ${info.software}`);
|
|
3109
3118
|
this.emit('devInfo', `SerialNr: ${info.serialNumber}`);
|
|
3110
3119
|
this.emit('devInfo', `Time: ${this.functions.formatTimestamp(info.time, timeZone)}`);
|
|
3120
|
+
this.emit('devInfo', `Energy Meter: ${this.energyMeter ? 'Enabled' : 'Disabled'}`);
|
|
3111
3121
|
this.emit('devInfo', `------------------------------`);
|
|
3112
3122
|
|
|
3113
3123
|
// Inventory
|
|
@@ -3165,15 +3175,6 @@ class EnvoyDevice extends EventEmitter {
|
|
|
3165
3175
|
|
|
3166
3176
|
this.emit('devInfo', `--------------------------------`);
|
|
3167
3177
|
}
|
|
3168
|
-
|
|
3169
|
-
// Eve Energy Meter
|
|
3170
|
-
if (this.energyMeter) {
|
|
3171
|
-
this.emit('devInfo', `EVE Meter: Yes`);
|
|
3172
|
-
this.emit('devInfo', `Production: Enabled`);
|
|
3173
|
-
if (feature.meters.consumptionNet.supported) this.emit('devInfo', `Consumption Net: ${feature.meters.consumptionNet.enabled ? 'Enabled' : 'Disabled'}`);
|
|
3174
|
-
if (feature.meters.consumptionTotal.supported) this.emit('devInfo', `Consumption Total: ${feature.meters.consumptionTotal.enabled ? 'Enabled' : 'Disabled'}`);
|
|
3175
|
-
this.emit('devInfo', `------------------------------`);
|
|
3176
|
-
}
|
|
3177
3178
|
})
|
|
3178
3179
|
.on('updateDataSampling', (state) => {
|
|
3179
3180
|
if (this.logDebug) this.emit('debug', `Update data sampling`);
|
|
@@ -3204,9 +3205,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
3204
3205
|
try {
|
|
3205
3206
|
const comm = home.comm ?? {};
|
|
3206
3207
|
const network = home.network ?? {};
|
|
3207
|
-
const wirelessConnections = home.wireless_connection ?? [];
|
|
3208
|
-
const networkInterfaces = network.interfaces ?? []
|
|
3209
|
-
|
|
3208
|
+
const wirelessConnections = (home.wireless_connection ?? []).filter(kit => kit.signal_strength_max > 0);
|
|
3209
|
+
const networkInterfaces = (network.interfaces ?? []).filter(iface => iface.carrier === true || iface.ip !== '169.254.120.1');
|
|
3210
3210
|
// Communication device support flags
|
|
3211
3211
|
const commEnsemble = comm.esub ?? {};
|
|
3212
3212
|
const commEncharges = Array.isArray(comm.encharge) ? comm.encharge : [];
|
|
@@ -3527,16 +3527,10 @@ class EnvoyDevice extends EventEmitter {
|
|
|
3527
3527
|
powerPeak: pcu.powerPeak,
|
|
3528
3528
|
});
|
|
3529
3529
|
|
|
3530
|
-
if (this.logInfo) {
|
|
3531
|
-
this.emit('info', `Microinverter, ${pcuData.serialNumber}, power: ${pcuData.power} W`);
|
|
3532
|
-
this.emit('info', `Microinverter, ${pcuData.serialNumber}, phase: ${pcuData.phase}`);
|
|
3533
|
-
}
|
|
3534
|
-
|
|
3535
3530
|
// Add characteristics
|
|
3536
3531
|
characteristics.push(
|
|
3537
3532
|
{ type: Characteristic.PowerW, value: pcuData.power },
|
|
3538
|
-
{ type: Characteristic.PowerPeakW, value: pcuData.powerPeak }
|
|
3539
|
-
);
|
|
3533
|
+
{ type: Characteristic.PowerPeakW, value: pcuData.powerPeak });
|
|
3540
3534
|
}
|
|
3541
3535
|
|
|
3542
3536
|
// Add detailed info if supported
|
|
@@ -3636,9 +3630,9 @@ class EnvoyDevice extends EventEmitter {
|
|
|
3636
3630
|
plcLevel: nsrb.plcLevel,
|
|
3637
3631
|
};
|
|
3638
3632
|
|
|
3639
|
-
if (this.
|
|
3640
|
-
this.emit('
|
|
3641
|
-
this.emit('
|
|
3633
|
+
if (this.logDebug) {
|
|
3634
|
+
this.emit('debug', `Q-Rela state:`, nsrbData.relay);
|
|
3635
|
+
this.emit('debug', `Q-Relay lines:`, nsrbData.linesCount);
|
|
3642
3636
|
}
|
|
3643
3637
|
|
|
3644
3638
|
// Create characteristics
|
|
@@ -4341,10 +4335,10 @@ class EnvoyDevice extends EventEmitter {
|
|
|
4341
4335
|
// Add to ensemble summary characteristics if live data not supported
|
|
4342
4336
|
if (!this.feature.liveData.supported || !this.feature.meters.storage.enabled) {
|
|
4343
4337
|
ensembleSummaryCharacteristics.push(
|
|
4344
|
-
{ type: Characteristic.AggSoc, value: secctrl.aggSoc },
|
|
4345
4338
|
{ type: Characteristic.AggMaxEnergy, value: secctrl.aggMaxEnergyKw },
|
|
4346
|
-
{ type: Characteristic.
|
|
4347
|
-
{ type: Characteristic.
|
|
4339
|
+
{ type: Characteristic.EncAggBackupEnergy, value: secctrl.encAggBackupEnergy },
|
|
4340
|
+
{ type: Characteristic.AggSoc, value: secctrl.aggSoc },
|
|
4341
|
+
{ type: Characteristic.encAggSoc, value: secctrl.encAggSoc });
|
|
4348
4342
|
}
|
|
4349
4343
|
|
|
4350
4344
|
if (phaseA) {
|
|
@@ -4842,8 +4836,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
4842
4836
|
const info = tariffData.tariff ?? {};
|
|
4843
4837
|
const tariff = {};
|
|
4844
4838
|
tariff.info = {
|
|
4845
|
-
currencyCode: info.currency.code,
|
|
4846
|
-
logger: info.logger,
|
|
4839
|
+
currencyCode: info.currency.code ?? '',
|
|
4840
|
+
logger: info.logger ?? '',
|
|
4847
4841
|
date: this.functions.formatTimestamp(info.date, this.pv.homeData.timeZone),
|
|
4848
4842
|
};
|
|
4849
4843
|
|
|
@@ -4851,7 +4845,7 @@ class EnvoyDevice extends EventEmitter {
|
|
|
4851
4845
|
const s = info.storage_settings ?? {};
|
|
4852
4846
|
tariff.storageSettings = {
|
|
4853
4847
|
mode: s.mode,
|
|
4854
|
-
operationModeSubType: s.operation_mode_sub_type,
|
|
4848
|
+
operationModeSubType: s.operation_mode_sub_type ?? '',
|
|
4855
4849
|
reservedSoc: s.reserved_soc,
|
|
4856
4850
|
veryLowSoc: s.very_low_soc,
|
|
4857
4851
|
chargeFromGrid: !!s.charge_from_grid,
|
|
@@ -4898,16 +4892,16 @@ class EnvoyDevice extends EventEmitter {
|
|
|
4898
4892
|
// Schedule
|
|
4899
4893
|
const sched = tariffData.schedule ?? {};
|
|
4900
4894
|
tariff.schedule = {
|
|
4901
|
-
fileName: sched.filename,
|
|
4902
|
-
source: sched.source,
|
|
4895
|
+
fileName: sched.filename ?? '',
|
|
4896
|
+
source: sched.source ?? '',
|
|
4903
4897
|
date: this.functions.formatTimestamp(sched.date, this.pv.homeData.timeZone),
|
|
4904
|
-
version: sched.version,
|
|
4898
|
+
version: sched.version ?? '',
|
|
4905
4899
|
reservedSoc: sched.reserved_soc,
|
|
4906
4900
|
veryLowSoc: sched.very_low_soc,
|
|
4907
4901
|
chargeFromGrid: !!sched.charge_from_grid,
|
|
4908
|
-
battMode: sched.batt_mode,
|
|
4909
|
-
batteryMode: sched.battery_mode,
|
|
4910
|
-
operationModeSubType: sched.operation_mode_sub_type,
|
|
4902
|
+
battMode: sched.batt_mode ?? '',
|
|
4903
|
+
batteryMode: sched.battery_mode ?? '',
|
|
4904
|
+
operationModeSubType: sched.operation_mode_sub_type ?? '',
|
|
4911
4905
|
override: !!sched.override,
|
|
4912
4906
|
overrideBackupSoc: sched.override_backup_soc,
|
|
4913
4907
|
overrideChgDischargeRate: sched.override_chg_discharge_rate,
|
|
@@ -5080,8 +5074,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
5080
5074
|
gridActionBool: settings.gridAction !== 'none',
|
|
5081
5075
|
microGridAction: settings.microGridAction,
|
|
5082
5076
|
genAction: settings.genAction,
|
|
5083
|
-
essentialStartTime: settings.essentialStartTime,
|
|
5084
|
-
essentialEndTime: settings.essentialEndTime,
|
|
5077
|
+
essentialStartTime: settings.essentialStartTime ?? '',
|
|
5078
|
+
essentialEndTime: settings.essentialEndTime ?? '',
|
|
5085
5079
|
priority: settings.priority,
|
|
5086
5080
|
blackSStart: settings.blackSStart,
|
|
5087
5081
|
override: settings.override ?? 'false',
|
|
@@ -5599,8 +5593,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
5599
5593
|
|
|
5600
5594
|
// Agg Energy and Soc
|
|
5601
5595
|
const characteristics = [
|
|
5602
|
-
{ type: Characteristic.AggSoc, value: percentFullSum, valueKey: 'aggSoc' },
|
|
5603
5596
|
{ type: Characteristic.AggMaxEnergy, value: energySumKw, valueKey: 'aggMaxEnergyKw' },
|
|
5597
|
+
{ type: Characteristic.AggSoc, value: percentFullSum, valueKey: 'aggSoc' },
|
|
5604
5598
|
];
|
|
5605
5599
|
|
|
5606
5600
|
// Update storage summary services
|
|
@@ -5669,8 +5663,8 @@ class EnvoyDevice extends EventEmitter {
|
|
|
5669
5663
|
// Update ensemble summary service
|
|
5670
5664
|
if (this.feature.inventory.esubs.secctrl.supported) {
|
|
5671
5665
|
const characteristics = [
|
|
5672
|
-
{ type: Characteristic.EncAggSoc, value: percentFullSumEnc, valueKey: 'encAggSoc' },
|
|
5673
5666
|
{ type: Characteristic.EncAggBackupEnergy, value: energySumEncKw, valueKey: 'encAggBackupEnergy' },
|
|
5667
|
+
{ type: Characteristic.EncAggSoc, value: percentFullSumEnc, valueKey: 'encAggSoc' },
|
|
5674
5668
|
];
|
|
5675
5669
|
|
|
5676
5670
|
// Update storage summary services
|
package/src/functions.js
CHANGED
|
@@ -17,12 +17,33 @@ class Functions {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async readData(path) {
|
|
20
|
+
async readData(path, parseJson = false) {
|
|
21
21
|
try {
|
|
22
|
-
const data = await fsPromises.readFile(path);
|
|
22
|
+
const data = await fsPromises.readFile(path, 'utf8');
|
|
23
|
+
|
|
24
|
+
if (parseJson) {
|
|
25
|
+
if (!data.trim()) {
|
|
26
|
+
// Empty file when expecting JSON
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(data);
|
|
31
|
+
} catch (jsonError) {
|
|
32
|
+
throw new Error(`JSON parse error in file "${path}": ${jsonError.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// For non-JSON, just return file content (can be empty string)
|
|
23
37
|
return data;
|
|
24
38
|
} catch (error) {
|
|
25
|
-
|
|
39
|
+
if (error.code === 'ENOENT') {
|
|
40
|
+
// File does not exist
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Preserve original error details
|
|
44
|
+
const wrappedError = new Error(`Read data error for "${path}": ${error.message}`);
|
|
45
|
+
wrappedError.original = error;
|
|
46
|
+
throw wrappedError;
|
|
26
47
|
}
|
|
27
48
|
}
|
|
28
49
|
|