@enyo-energy/sunspec-sdk 0.0.74 → 0.0.75
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/dist/cjs/sunspec-devices.cjs +4 -138
- package/dist/cjs/sunspec-devices.d.cts +0 -11
- package/dist/cjs/sunspec-modbus-client.cjs +53 -34
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/sunspec-devices.d.ts +0 -11
- package/dist/sunspec-devices.js +4 -138
- package/dist/sunspec-modbus-client.js +53 -34
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -711,8 +711,6 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
711
711
|
this.dataBus = this.energyApp.useDataBus();
|
|
712
712
|
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
713
713
|
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetInverterFeedInLimitV1,
|
|
714
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
715
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
716
714
|
], (entry) => this.handleInverterCommand(entry));
|
|
717
715
|
console.log(`Inverter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
|
|
718
716
|
}
|
|
@@ -737,12 +735,6 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
737
735
|
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetInverterFeedInLimitV1:
|
|
738
736
|
await this.handleSetFeedInLimit(entry);
|
|
739
737
|
break;
|
|
740
|
-
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StartCalibrationV1:
|
|
741
|
-
await this.handleStartCalibration(entry);
|
|
742
|
-
break;
|
|
743
|
-
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StopCalibrationV1:
|
|
744
|
-
await this.handleStopCalibration(entry);
|
|
745
|
-
break;
|
|
746
738
|
}
|
|
747
739
|
}
|
|
748
740
|
catch (error) {
|
|
@@ -788,51 +780,6 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
788
780
|
await this.snapshotService.initialize();
|
|
789
781
|
console.log(`Inverter ${this.applianceId}: snapshot service initialized`);
|
|
790
782
|
}
|
|
791
|
-
async handleStartCalibration(msg) {
|
|
792
|
-
if (!this.isConnected() || !this.applianceId) {
|
|
793
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not connected');
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
|
-
if (!this.snapshotService) {
|
|
797
|
-
await this.initSnapshotService();
|
|
798
|
-
}
|
|
799
|
-
if (!this.snapshotService) {
|
|
800
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Snapshot service unavailable');
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
if (this.snapshotService.isCalibrating()) {
|
|
804
|
-
console.log(`Inverter ${this.applianceId}: calibration already active — ack idempotently`);
|
|
805
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
console.log(`Inverter ${this.applianceId}: handling StartCalibrationV1`);
|
|
809
|
-
const controls = await this.sunspecClient.readInverterControls(this.unitId);
|
|
810
|
-
if (!controls) {
|
|
811
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to read inverter controls for snapshot');
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
try {
|
|
815
|
-
await this.snapshotService.startCalibration(controls);
|
|
816
|
-
}
|
|
817
|
-
catch (error) {
|
|
818
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, `Failed to persist snapshot: ${error}`);
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
822
|
-
}
|
|
823
|
-
async handleStopCalibration(msg) {
|
|
824
|
-
if (!this.applianceId) {
|
|
825
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not initialized');
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
console.log(`Inverter ${this.applianceId}: handling StopCalibrationV1`);
|
|
829
|
-
// SnapshotService.stopCalibration fires `restoreInverterSnapshot` internally with
|
|
830
|
-
// reason="stop" before returning. Any restore failure is logged by the callback
|
|
831
|
-
// (the persisted snapshot is already gone by then — see library notes) so we
|
|
832
|
-
// always ack Accepted from here.
|
|
833
|
-
await this.snapshotService?.stopCalibration();
|
|
834
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
835
|
-
}
|
|
836
783
|
/**
|
|
837
784
|
* `onRestore` callback for the inverter's {@link SnapshotService}. Writes only the
|
|
838
785
|
* subset of writable inverter fields that other commands actually touched during the
|
|
@@ -1371,7 +1318,10 @@ class SunspecBattery extends BaseSunspecDevice {
|
|
|
1371
1318
|
console.error('Battery not connected');
|
|
1372
1319
|
return false;
|
|
1373
1320
|
}
|
|
1374
|
-
|
|
1321
|
+
// Redundant with the modbus client's own per-write debug trace. Keep at debug
|
|
1322
|
+
// so the high-level info logs (setStorageMode / enableGridCharging / etc.) stay
|
|
1323
|
+
// the only info-level signal per battery-control action.
|
|
1324
|
+
console.debug('Writing battery controls:', controls);
|
|
1375
1325
|
return this.sunspecClient.writeBatteryControls(this.unitId, controls);
|
|
1376
1326
|
}
|
|
1377
1327
|
mapToEnyoStorageMode(storageMode) {
|
|
@@ -1469,8 +1419,6 @@ class SunspecBattery extends BaseSunspecDevice {
|
|
|
1469
1419
|
this.dataBus = this.energyApp.useDataBus();
|
|
1470
1420
|
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
1471
1421
|
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetStorageScheduleV1,
|
|
1472
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
1473
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
1474
1422
|
], (entry) => this.handleStorageCommand(entry));
|
|
1475
1423
|
if (!this.applianceId) {
|
|
1476
1424
|
throw new Error("SunspecBattery.startDataBusListening: applianceId required — call connect() first.");
|
|
@@ -1509,12 +1457,6 @@ class SunspecBattery extends BaseSunspecDevice {
|
|
|
1509
1457
|
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.SetStorageScheduleV1:
|
|
1510
1458
|
this.handleSetStorageScheduleAck(entry);
|
|
1511
1459
|
break;
|
|
1512
|
-
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StartCalibrationV1:
|
|
1513
|
-
await this.handleStartCalibration(entry);
|
|
1514
|
-
break;
|
|
1515
|
-
case enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StopCalibrationV1:
|
|
1516
|
-
await this.handleStopCalibration(entry);
|
|
1517
|
-
break;
|
|
1518
1460
|
}
|
|
1519
1461
|
}
|
|
1520
1462
|
catch (error) {
|
|
@@ -1554,51 +1496,6 @@ class SunspecBattery extends BaseSunspecDevice {
|
|
|
1554
1496
|
await this.snapshotService.initialize();
|
|
1555
1497
|
console.log(`Battery ${this.applianceId}: snapshot service initialized`);
|
|
1556
1498
|
}
|
|
1557
|
-
async handleStartCalibration(msg) {
|
|
1558
|
-
if (!this.isConnected() || !this.applianceId) {
|
|
1559
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not connected');
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
if (!this.snapshotService) {
|
|
1563
|
-
await this.initSnapshotService();
|
|
1564
|
-
}
|
|
1565
|
-
if (!this.snapshotService) {
|
|
1566
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Snapshot service unavailable');
|
|
1567
|
-
return;
|
|
1568
|
-
}
|
|
1569
|
-
if (this.snapshotService.isCalibrating()) {
|
|
1570
|
-
console.log(`Battery ${this.applianceId}: calibration already active — ack idempotently`);
|
|
1571
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1572
|
-
return;
|
|
1573
|
-
}
|
|
1574
|
-
console.log(`Battery ${this.applianceId}: handling StartCalibrationV1`);
|
|
1575
|
-
const controls = await this.sunspecClient.readBatteryControls(this.unitId);
|
|
1576
|
-
if (!controls) {
|
|
1577
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to read battery controls for snapshot');
|
|
1578
|
-
return;
|
|
1579
|
-
}
|
|
1580
|
-
try {
|
|
1581
|
-
await this.snapshotService.startCalibration(controls);
|
|
1582
|
-
}
|
|
1583
|
-
catch (error) {
|
|
1584
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, `Failed to persist snapshot: ${error}`);
|
|
1585
|
-
return;
|
|
1586
|
-
}
|
|
1587
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1588
|
-
}
|
|
1589
|
-
async handleStopCalibration(msg) {
|
|
1590
|
-
if (!this.applianceId) {
|
|
1591
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not initialized');
|
|
1592
|
-
return;
|
|
1593
|
-
}
|
|
1594
|
-
console.log(`Battery ${this.applianceId}: handling StopCalibrationV1`);
|
|
1595
|
-
// SnapshotService.stopCalibration fires `restoreBatterySnapshot` internally
|
|
1596
|
-
// with reason="stop" before returning. Failures from the restore callback are
|
|
1597
|
-
// logged inside the callback (the persisted snapshot is already gone — see
|
|
1598
|
-
// library notes) so we always ack Accepted here.
|
|
1599
|
-
await this.snapshotService?.stopCalibration();
|
|
1600
|
-
this.sendCommandAcknowledge(msg.id, msg.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1601
|
-
}
|
|
1602
1499
|
/**
|
|
1603
1500
|
* `onRestore` callback for the battery's {@link SnapshotService}. Writes only the
|
|
1604
1501
|
* subset of writable battery fields touched during the calibration. Errors are
|
|
@@ -1704,13 +1601,11 @@ class SunspecMeter extends BaseSunspecDevice {
|
|
|
1704
1601
|
console.error(`Failed to update meter appliance: ${error}`);
|
|
1705
1602
|
}
|
|
1706
1603
|
}
|
|
1707
|
-
this.startDataBusListening();
|
|
1708
1604
|
}
|
|
1709
1605
|
/**
|
|
1710
1606
|
* Disconnect from the meter and update appliance state
|
|
1711
1607
|
*/
|
|
1712
1608
|
async disconnect() {
|
|
1713
|
-
this.stopDataBusListening();
|
|
1714
1609
|
if (this.applianceId) {
|
|
1715
1610
|
try {
|
|
1716
1611
|
await this.applianceManager.updateApplianceState(this.applianceId, enyo_appliance_js_1.EnyoApplianceConnectionType.Connector, enyo_appliance_js_1.EnyoApplianceStateEnum.Offline);
|
|
@@ -1722,35 +1617,6 @@ class SunspecMeter extends BaseSunspecDevice {
|
|
|
1722
1617
|
// Close just this meter's unit; other devices on the same network device stay open.
|
|
1723
1618
|
await this.sunspecClient.disconnectUnit(this.unitId);
|
|
1724
1619
|
}
|
|
1725
|
-
/**
|
|
1726
|
-
* Meter does not implement calibration; it only subscribes to Start/StopCalibrationV1 to
|
|
1727
|
-
* answer NotSupported (per the data-bus contract that every command must be acknowledged).
|
|
1728
|
-
*/
|
|
1729
|
-
startDataBusListening() {
|
|
1730
|
-
if (this.dataBusListenerId) {
|
|
1731
|
-
return;
|
|
1732
|
-
}
|
|
1733
|
-
this.dataBus = this.energyApp.useDataBus();
|
|
1734
|
-
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
1735
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
1736
|
-
enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
1737
|
-
], (entry) => this.handleMeterCommand(entry));
|
|
1738
|
-
console.log(`Meter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
|
|
1739
|
-
}
|
|
1740
|
-
stopDataBusListening() {
|
|
1741
|
-
if (this.dataBusListenerId && this.dataBus) {
|
|
1742
|
-
this.dataBus.unsubscribe(this.dataBusListenerId);
|
|
1743
|
-
console.log(`Meter ${this.applianceId}: stopped data bus listening (listener ${this.dataBusListenerId})`);
|
|
1744
|
-
}
|
|
1745
|
-
this.dataBusListenerId = undefined;
|
|
1746
|
-
this.dataBus = undefined;
|
|
1747
|
-
}
|
|
1748
|
-
handleMeterCommand(entry) {
|
|
1749
|
-
if (entry.applianceId !== this.applianceId) {
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
this.sendCommandAcknowledge(entry.id, entry.message, enyo_data_bus_value_js_1.EnyoCommandAcknowledgeAnswerEnum.NotSupported, 'Meter does not support calibration');
|
|
1753
|
-
}
|
|
1754
1620
|
/**
|
|
1755
1621
|
* Update meter data and return data bus messages
|
|
1756
1622
|
*/
|
|
@@ -178,8 +178,6 @@ export declare class SunspecInverter extends BaseSunspecDevice {
|
|
|
178
178
|
* process restarts. Idempotent.
|
|
179
179
|
*/
|
|
180
180
|
private initSnapshotService;
|
|
181
|
-
private handleStartCalibration;
|
|
182
|
-
private handleStopCalibration;
|
|
183
181
|
/**
|
|
184
182
|
* `onRestore` callback for the inverter's {@link SnapshotService}. Writes only the
|
|
185
183
|
* subset of writable inverter fields that other commands actually touched during the
|
|
@@ -408,8 +406,6 @@ export declare class SunspecBattery extends BaseSunspecDevice {
|
|
|
408
406
|
* process restarts. Idempotent.
|
|
409
407
|
*/
|
|
410
408
|
private initSnapshotService;
|
|
411
|
-
private handleStartCalibration;
|
|
412
|
-
private handleStopCalibration;
|
|
413
409
|
/**
|
|
414
410
|
* `onRestore` callback for the battery's {@link SnapshotService}. Writes only the
|
|
415
411
|
* subset of writable battery fields touched during the calibration. Errors are
|
|
@@ -432,13 +428,6 @@ export declare class SunspecMeter extends BaseSunspecDevice {
|
|
|
432
428
|
* Disconnect from the meter and update appliance state
|
|
433
429
|
*/
|
|
434
430
|
disconnect(): Promise<void>;
|
|
435
|
-
/**
|
|
436
|
-
* Meter does not implement calibration; it only subscribes to Start/StopCalibrationV1 to
|
|
437
|
-
* answer NotSupported (per the data-bus contract that every command must be acknowledged).
|
|
438
|
-
*/
|
|
439
|
-
startDataBusListening(): void;
|
|
440
|
-
stopDataBusListening(): void;
|
|
441
|
-
private handleMeterCommand;
|
|
442
431
|
/**
|
|
443
432
|
* Update meter data and return data bus messages
|
|
444
433
|
*/
|
|
@@ -486,7 +486,10 @@ class SunspecModbusClient {
|
|
|
486
486
|
}
|
|
487
487
|
catch (error) {
|
|
488
488
|
console.debug(`No SunSpec device at unit ${unitId}: ${error}`);
|
|
489
|
-
|
|
489
|
+
// Discovery summary at line 594 below is the info-level outcome.
|
|
490
|
+
// This 0-models branch is the same outcome as a normal "no device found",
|
|
491
|
+
// covered already by the debug above. Keep at debug.
|
|
492
|
+
console.debug(`Discovery complete for unit ${unitId}. Found 0 models: []`);
|
|
490
493
|
return models;
|
|
491
494
|
}
|
|
492
495
|
currentAddress = addressInfo.nextAddress;
|
|
@@ -495,14 +498,14 @@ class SunspecModbusClient {
|
|
|
495
498
|
const buffer = await instance.readHoldingRegisters(currentAddress, 2);
|
|
496
499
|
const modelData = [buffer.readUInt16BE(0), buffer.readUInt16BE(2)];
|
|
497
500
|
if (!modelData || modelData.length < 2) {
|
|
498
|
-
console.
|
|
501
|
+
console.debug(`No data at address ${currentAddress}, ending discovery`);
|
|
499
502
|
break;
|
|
500
503
|
}
|
|
501
504
|
const modelId = modelData[0];
|
|
502
505
|
const modelLength = modelData[1];
|
|
503
506
|
// Check for end marker
|
|
504
507
|
if (modelId === 0xFFFF || modelId === 65535) {
|
|
505
|
-
console.
|
|
508
|
+
console.debug(`Found end marker at address ${currentAddress}`);
|
|
506
509
|
break;
|
|
507
510
|
}
|
|
508
511
|
// Store discovered model
|
|
@@ -512,7 +515,9 @@ class SunspecModbusClient {
|
|
|
512
515
|
length: modelLength
|
|
513
516
|
};
|
|
514
517
|
models.set(modelId, model);
|
|
515
|
-
|
|
518
|
+
// Per-model discovery step. The end-of-discovery summary below is the
|
|
519
|
+
// info-level outcome; the per-model walk is debug-only.
|
|
520
|
+
console.debug(`Discovered Model ${modelId} at address ${currentAddress} with length ${modelLength} (unit ${unitId})`);
|
|
516
521
|
// Jump to next model: current address + 2 (header) + model length
|
|
517
522
|
currentAddress = currentAddress + 2 + modelLength;
|
|
518
523
|
}
|
|
@@ -701,7 +706,9 @@ class SunspecModbusClient {
|
|
|
701
706
|
// Write to holding registers
|
|
702
707
|
await instance.writeMultipleRegisters(address, registerValues);
|
|
703
708
|
this.connectionHealth.recordSuccess();
|
|
704
|
-
|
|
709
|
+
// Per-register write success — fires for every parameter inside higher-level
|
|
710
|
+
// writers (writeBatteryControls / writeInverterControls). Demoted to debug.
|
|
711
|
+
console.debug(`Successfully wrote value ${value} to register ${address} (unit ${unitId})`);
|
|
705
712
|
return true;
|
|
706
713
|
}
|
|
707
714
|
catch (error) {
|
|
@@ -1599,7 +1606,10 @@ class SunspecModbusClient {
|
|
|
1599
1606
|
data.dischargePower = Math.abs((data.outWRte / 100) * data.wChaMax);
|
|
1600
1607
|
console.debug(`Calculated Discharge Power (inWRte: ${data.outWRte}, wChaMax: ${data.wChaMax}): ${data.dischargePower?.toFixed(2)} W`);
|
|
1601
1608
|
}
|
|
1602
|
-
|
|
1609
|
+
// Single-line JSON debug dump — Node's default formatter would split
|
|
1610
|
+
// the object across many lines; stringify keeps the whole snapshot
|
|
1611
|
+
// on one log entry so it stays grep-able and round-trippable.
|
|
1612
|
+
console.debug(`[Battery] unit=${unitId} model=124 data=${JSON.stringify(data)}`);
|
|
1603
1613
|
return data;
|
|
1604
1614
|
}
|
|
1605
1615
|
else if (model.id === 802) {
|
|
@@ -1636,17 +1646,18 @@ class SunspecModbusClient {
|
|
|
1636
1646
|
chargePower,
|
|
1637
1647
|
dischargePower,
|
|
1638
1648
|
};
|
|
1639
|
-
console.debug(
|
|
1649
|
+
console.debug(`[Battery] unit=${unitId} model=802 data=${JSON.stringify(result)}`);
|
|
1640
1650
|
return result;
|
|
1641
1651
|
}
|
|
1642
1652
|
else {
|
|
1643
|
-
// Handle other battery models (803) if needed
|
|
1644
|
-
|
|
1645
|
-
return {
|
|
1653
|
+
// Handle other battery models (803) if needed.
|
|
1654
|
+
const stub = {
|
|
1646
1655
|
blockNumber: model.id,
|
|
1647
1656
|
blockAddress: model.address,
|
|
1648
|
-
blockLength: model.length
|
|
1657
|
+
blockLength: model.length,
|
|
1649
1658
|
};
|
|
1659
|
+
console.debug(`[Battery] unit=${unitId} model=${model.id} (not yet implemented) data=${JSON.stringify(stub)}`);
|
|
1660
|
+
return stub;
|
|
1650
1661
|
}
|
|
1651
1662
|
}
|
|
1652
1663
|
catch (error) {
|
|
@@ -1664,7 +1675,11 @@ class SunspecModbusClient {
|
|
|
1664
1675
|
return false;
|
|
1665
1676
|
}
|
|
1666
1677
|
const baseAddr = model.address;
|
|
1667
|
-
|
|
1678
|
+
// Per-register write trace is high-volume (every schedule entry transition fires
|
|
1679
|
+
// a writeBatteryControls). Demoted to debug; consumers that need the trail can
|
|
1680
|
+
// raise their log level. High-level callers (setStorageMode, enableGridCharging,
|
|
1681
|
+
// setFeedInLimit, etc.) still emit one info-level line per action.
|
|
1682
|
+
console.debug(`Writing Battery Controls to Model 124 at base address: ${baseAddr} (unit ${unitId})`);
|
|
1668
1683
|
try {
|
|
1669
1684
|
// Write order: source pin and parameter writes land BEFORE the
|
|
1670
1685
|
// control mode so the device only "starts acting" once every
|
|
@@ -1674,7 +1689,7 @@ class SunspecModbusClient {
|
|
|
1674
1689
|
// Write charge source setting (Register 17)
|
|
1675
1690
|
if (controls.chaGriSet !== undefined) {
|
|
1676
1691
|
await this.writeRegisterValue(unitId, baseAddr + 17, controls.chaGriSet, 'uint16');
|
|
1677
|
-
console.
|
|
1692
|
+
console.debug(`Set charge source to ${controls.chaGriSet === sunspec_interfaces_js_1.SunspecChargeSource.GRID ? 'GRID' : 'PV'}`);
|
|
1678
1693
|
}
|
|
1679
1694
|
// Write maximum charge power (Register 2) - needs scale factor
|
|
1680
1695
|
if (controls.wChaMax !== undefined) {
|
|
@@ -1682,7 +1697,7 @@ class SunspecModbusClient {
|
|
|
1682
1697
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1683
1698
|
const scaledValue = Math.round(controls.wChaMax / Math.pow(10, scaleFactor));
|
|
1684
1699
|
await this.writeRegisterValue(unitId, baseAddr + 2, scaledValue, 'uint16');
|
|
1685
|
-
console.
|
|
1700
|
+
console.debug(`Set max charge power to ${controls.wChaMax}W (scaled: ${scaledValue})`);
|
|
1686
1701
|
}
|
|
1687
1702
|
// Write charge rate (Register 13) - needs scale factor
|
|
1688
1703
|
if (controls.inWRte !== undefined) {
|
|
@@ -1690,7 +1705,7 @@ class SunspecModbusClient {
|
|
|
1690
1705
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1691
1706
|
const scaledValue = Math.round(controls.inWRte / Math.pow(10, scaleFactor));
|
|
1692
1707
|
await this.writeRegisterValue(unitId, baseAddr + 13, scaledValue, 'int16');
|
|
1693
|
-
console.
|
|
1708
|
+
console.debug(`Set charge rate to ${controls.inWRte}% (scaled: ${scaledValue})`);
|
|
1694
1709
|
}
|
|
1695
1710
|
// Write discharge rate (Register 12) - needs scale factor
|
|
1696
1711
|
if (controls.outWRte !== undefined) {
|
|
@@ -1698,7 +1713,7 @@ class SunspecModbusClient {
|
|
|
1698
1713
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1699
1714
|
const scaledValue = Math.round(controls.outWRte / Math.pow(10, scaleFactor));
|
|
1700
1715
|
await this.writeRegisterValue(unitId, baseAddr + 12, scaledValue, 'int16');
|
|
1701
|
-
console.
|
|
1716
|
+
console.debug(`Set discharge rate to ${controls.outWRte}% (scaled: ${scaledValue})`);
|
|
1702
1717
|
}
|
|
1703
1718
|
// Write minimum reserve percentage (Register 7) - needs scale factor
|
|
1704
1719
|
if (controls.minRsvPct !== undefined) {
|
|
@@ -1706,16 +1721,16 @@ class SunspecModbusClient {
|
|
|
1706
1721
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1707
1722
|
const scaledValue = Math.round(controls.minRsvPct / Math.pow(10, scaleFactor));
|
|
1708
1723
|
await this.writeRegisterValue(unitId, baseAddr + 7, scaledValue, 'uint16');
|
|
1709
|
-
console.
|
|
1724
|
+
console.debug(`Set minimum reserve to ${controls.minRsvPct}% (scaled: ${scaledValue})`);
|
|
1710
1725
|
}
|
|
1711
1726
|
// Storage control mode (Register 5) — written LAST so all
|
|
1712
1727
|
// governing parameters are already in place when the device
|
|
1713
1728
|
// transitions into the new mode.
|
|
1714
1729
|
if (controls.storCtlMod !== undefined) {
|
|
1715
1730
|
await this.writeRegisterValue(unitId, baseAddr + 5, controls.storCtlMod, 'uint16');
|
|
1716
|
-
console.
|
|
1731
|
+
console.debug(`Set storage control mode to 0x${controls.storCtlMod.toString(16)}`);
|
|
1717
1732
|
}
|
|
1718
|
-
console.
|
|
1733
|
+
console.debug('Battery controls written successfully');
|
|
1719
1734
|
return true;
|
|
1720
1735
|
}
|
|
1721
1736
|
catch (error) {
|
|
@@ -1766,10 +1781,12 @@ class SunspecModbusClient {
|
|
|
1766
1781
|
async readBatteryControls(unitId) {
|
|
1767
1782
|
const model = this.findModel(unitId, sunspec_interfaces_js_1.SunspecModelId.Battery);
|
|
1768
1783
|
if (!model) {
|
|
1769
|
-
|
|
1784
|
+
// Fires per readBatteryControls attempt — noisy on non-battery units
|
|
1785
|
+
// that go through the same code path. Demoted to debug.
|
|
1786
|
+
console.debug(`Battery model 124 not found on unit ${unitId}`);
|
|
1770
1787
|
return null;
|
|
1771
1788
|
}
|
|
1772
|
-
console.
|
|
1789
|
+
console.debug(`Reading Battery Controls from Model 124 at base address: ${model.address} (unit ${unitId})`);
|
|
1773
1790
|
try {
|
|
1774
1791
|
// Read entire model block in a single Modbus call
|
|
1775
1792
|
const buffer = await this.readModelBlock(unitId, model);
|
|
@@ -1984,7 +2001,7 @@ class SunspecModbusClient {
|
|
|
1984
2001
|
console.debug(`Common block model not found on unit ${unitId}`);
|
|
1985
2002
|
return null;
|
|
1986
2003
|
}
|
|
1987
|
-
console.
|
|
2004
|
+
console.debug(`Reading Common Block - Model address: ${model.address} (unit ${unitId})`);
|
|
1988
2005
|
try {
|
|
1989
2006
|
// Read entire model block in a single Modbus call
|
|
1990
2007
|
const buffer = await this.readModelBlock(unitId, model);
|
|
@@ -2144,7 +2161,8 @@ class SunspecModbusClient {
|
|
|
2144
2161
|
async readInverterControls(unitId) {
|
|
2145
2162
|
const model = this.findModel(unitId, sunspec_interfaces_js_1.SunspecModelId.Controls);
|
|
2146
2163
|
if (!model) {
|
|
2147
|
-
|
|
2164
|
+
// Same trace-only rationale as the Model 124 not-found message above.
|
|
2165
|
+
console.debug(`Controls model 123 not found on unit ${unitId}`);
|
|
2148
2166
|
return null;
|
|
2149
2167
|
}
|
|
2150
2168
|
try {
|
|
@@ -2253,17 +2271,17 @@ class SunspecModbusClient {
|
|
|
2253
2271
|
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
2254
2272
|
const scaledValue = Math.round(settings.WMax / Math.pow(10, scaleFactor));
|
|
2255
2273
|
// Writing registers needs to be implemented in EnergyAppModbusInstance
|
|
2256
|
-
// For now, log the write operation
|
|
2257
|
-
console.
|
|
2274
|
+
// For now, log the (would-be) write operation. Dry-run trace only — debug.
|
|
2275
|
+
console.debug(`Would write value ${scaledValue} to register ${baseAddr}`);
|
|
2258
2276
|
}
|
|
2259
2277
|
if (settings.VRef !== undefined) {
|
|
2260
2278
|
const sfBuffer = await instance.readHoldingRegisters(baseAddr + 23, 1);
|
|
2261
2279
|
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
2262
2280
|
const scaledValue = Math.round(settings.VRef / Math.pow(10, scaleFactor));
|
|
2263
|
-
console.
|
|
2281
|
+
console.debug(`Would write value ${scaledValue} to register ${baseAddr + 1}`);
|
|
2264
2282
|
}
|
|
2265
2283
|
// Add more write operations for other settings as needed
|
|
2266
|
-
console.
|
|
2284
|
+
console.debug('Inverter settings written successfully');
|
|
2267
2285
|
return true;
|
|
2268
2286
|
}
|
|
2269
2287
|
catch (error) {
|
|
@@ -2282,36 +2300,37 @@ class SunspecModbusClient {
|
|
|
2282
2300
|
}
|
|
2283
2301
|
const baseAddr = model.address;
|
|
2284
2302
|
try {
|
|
2285
|
-
//
|
|
2303
|
+
// Per-field inverter-control writes are debug-only; the high-level callers
|
|
2304
|
+
// (`setFeedInLimit`, etc.) still log the action and outcome at info level.
|
|
2286
2305
|
if (controls.Conn !== undefined) {
|
|
2287
2306
|
await this.writeRegisterValue(unitId, baseAddr + 2, controls.Conn, 'uint16');
|
|
2288
|
-
console.
|
|
2307
|
+
console.debug(`Set connection control to ${controls.Conn}`);
|
|
2289
2308
|
}
|
|
2290
2309
|
// Power limit control (Register 3) - needs scale factor
|
|
2291
2310
|
if (controls.WMaxLimPct !== undefined) {
|
|
2292
2311
|
const scaleFactor = await this.readRegisterValue(unitId, baseAddr + 21, 1, 'int16');
|
|
2293
2312
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
2294
2313
|
await this.writeRegisterValue(unitId, baseAddr + 3, scaledValue, 'uint16');
|
|
2295
|
-
console.
|
|
2314
|
+
console.debug(`Set power limit to ${controls.WMaxLimPct}% (scaled: ${scaledValue})`);
|
|
2296
2315
|
}
|
|
2297
2316
|
// Throttle enable/disable (Register 7)
|
|
2298
2317
|
if (controls.WMaxLim_Ena !== undefined) {
|
|
2299
2318
|
await this.writeRegisterValue(unitId, baseAddr + 7, controls.WMaxLim_Ena, 'uint16');
|
|
2300
|
-
console.
|
|
2319
|
+
console.debug(`Set throttle enable to ${controls.WMaxLim_Ena}`);
|
|
2301
2320
|
}
|
|
2302
2321
|
// Power factor control (Register 8) - needs scale factor
|
|
2303
2322
|
if (controls.OutPFSet !== undefined) {
|
|
2304
2323
|
const scaleFactor = await this.readRegisterValue(unitId, baseAddr + 22, 1, 'int16');
|
|
2305
2324
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
2306
2325
|
await this.writeRegisterValue(unitId, baseAddr + 8, scaledValue, 'int16');
|
|
2307
|
-
console.
|
|
2326
|
+
console.debug(`Set power factor to ${controls.OutPFSet} (scaled: ${scaledValue})`);
|
|
2308
2327
|
}
|
|
2309
2328
|
// Power factor enable/disable (Register 12)
|
|
2310
2329
|
if (controls.OutPFSet_Ena !== undefined) {
|
|
2311
2330
|
await this.writeRegisterValue(unitId, baseAddr + 12, controls.OutPFSet_Ena, 'uint16');
|
|
2312
|
-
console.
|
|
2331
|
+
console.debug(`Set PF enable to ${controls.OutPFSet_Ena}`);
|
|
2313
2332
|
}
|
|
2314
|
-
console.
|
|
2333
|
+
console.debug('Inverter controls written successfully');
|
|
2315
2334
|
return true;
|
|
2316
2335
|
}
|
|
2317
2336
|
catch (error) {
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -9,7 +9,7 @@ exports.getSdkVersion = getSdkVersion;
|
|
|
9
9
|
/**
|
|
10
10
|
* Current version of the enyo Energy App SDK.
|
|
11
11
|
*/
|
|
12
|
-
exports.SDK_VERSION = '0.0.
|
|
12
|
+
exports.SDK_VERSION = '0.0.75';
|
|
13
13
|
/**
|
|
14
14
|
* Gets the current SDK version.
|
|
15
15
|
* @returns The semantic version string of the SDK
|
package/dist/cjs/version.d.cts
CHANGED
|
@@ -178,8 +178,6 @@ export declare class SunspecInverter extends BaseSunspecDevice {
|
|
|
178
178
|
* process restarts. Idempotent.
|
|
179
179
|
*/
|
|
180
180
|
private initSnapshotService;
|
|
181
|
-
private handleStartCalibration;
|
|
182
|
-
private handleStopCalibration;
|
|
183
181
|
/**
|
|
184
182
|
* `onRestore` callback for the inverter's {@link SnapshotService}. Writes only the
|
|
185
183
|
* subset of writable inverter fields that other commands actually touched during the
|
|
@@ -408,8 +406,6 @@ export declare class SunspecBattery extends BaseSunspecDevice {
|
|
|
408
406
|
* process restarts. Idempotent.
|
|
409
407
|
*/
|
|
410
408
|
private initSnapshotService;
|
|
411
|
-
private handleStartCalibration;
|
|
412
|
-
private handleStopCalibration;
|
|
413
409
|
/**
|
|
414
410
|
* `onRestore` callback for the battery's {@link SnapshotService}. Writes only the
|
|
415
411
|
* subset of writable battery fields touched during the calibration. Errors are
|
|
@@ -432,13 +428,6 @@ export declare class SunspecMeter extends BaseSunspecDevice {
|
|
|
432
428
|
* Disconnect from the meter and update appliance state
|
|
433
429
|
*/
|
|
434
430
|
disconnect(): Promise<void>;
|
|
435
|
-
/**
|
|
436
|
-
* Meter does not implement calibration; it only subscribes to Start/StopCalibrationV1 to
|
|
437
|
-
* answer NotSupported (per the data-bus contract that every command must be acknowledged).
|
|
438
|
-
*/
|
|
439
|
-
startDataBusListening(): void;
|
|
440
|
-
stopDataBusListening(): void;
|
|
441
|
-
private handleMeterCommand;
|
|
442
431
|
/**
|
|
443
432
|
* Update meter data and return data bus messages
|
|
444
433
|
*/
|
package/dist/sunspec-devices.js
CHANGED
|
@@ -705,8 +705,6 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
705
705
|
this.dataBus = this.energyApp.useDataBus();
|
|
706
706
|
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
707
707
|
EnyoDataBusMessageEnum.SetInverterFeedInLimitV1,
|
|
708
|
-
EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
709
|
-
EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
710
708
|
], (entry) => this.handleInverterCommand(entry));
|
|
711
709
|
console.log(`Inverter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
|
|
712
710
|
}
|
|
@@ -731,12 +729,6 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
731
729
|
case EnyoDataBusMessageEnum.SetInverterFeedInLimitV1:
|
|
732
730
|
await this.handleSetFeedInLimit(entry);
|
|
733
731
|
break;
|
|
734
|
-
case EnyoDataBusMessageEnum.StartCalibrationV1:
|
|
735
|
-
await this.handleStartCalibration(entry);
|
|
736
|
-
break;
|
|
737
|
-
case EnyoDataBusMessageEnum.StopCalibrationV1:
|
|
738
|
-
await this.handleStopCalibration(entry);
|
|
739
|
-
break;
|
|
740
732
|
}
|
|
741
733
|
}
|
|
742
734
|
catch (error) {
|
|
@@ -782,51 +774,6 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
782
774
|
await this.snapshotService.initialize();
|
|
783
775
|
console.log(`Inverter ${this.applianceId}: snapshot service initialized`);
|
|
784
776
|
}
|
|
785
|
-
async handleStartCalibration(msg) {
|
|
786
|
-
if (!this.isConnected() || !this.applianceId) {
|
|
787
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not connected');
|
|
788
|
-
return;
|
|
789
|
-
}
|
|
790
|
-
if (!this.snapshotService) {
|
|
791
|
-
await this.initSnapshotService();
|
|
792
|
-
}
|
|
793
|
-
if (!this.snapshotService) {
|
|
794
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Snapshot service unavailable');
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
if (this.snapshotService.isCalibrating()) {
|
|
798
|
-
console.log(`Inverter ${this.applianceId}: calibration already active — ack idempotently`);
|
|
799
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
console.log(`Inverter ${this.applianceId}: handling StartCalibrationV1`);
|
|
803
|
-
const controls = await this.sunspecClient.readInverterControls(this.unitId);
|
|
804
|
-
if (!controls) {
|
|
805
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to read inverter controls for snapshot');
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
try {
|
|
809
|
-
await this.snapshotService.startCalibration(controls);
|
|
810
|
-
}
|
|
811
|
-
catch (error) {
|
|
812
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, `Failed to persist snapshot: ${error}`);
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
816
|
-
}
|
|
817
|
-
async handleStopCalibration(msg) {
|
|
818
|
-
if (!this.applianceId) {
|
|
819
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not initialized');
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
console.log(`Inverter ${this.applianceId}: handling StopCalibrationV1`);
|
|
823
|
-
// SnapshotService.stopCalibration fires `restoreInverterSnapshot` internally with
|
|
824
|
-
// reason="stop" before returning. Any restore failure is logged by the callback
|
|
825
|
-
// (the persisted snapshot is already gone by then — see library notes) so we
|
|
826
|
-
// always ack Accepted from here.
|
|
827
|
-
await this.snapshotService?.stopCalibration();
|
|
828
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
829
|
-
}
|
|
830
777
|
/**
|
|
831
778
|
* `onRestore` callback for the inverter's {@link SnapshotService}. Writes only the
|
|
832
779
|
* subset of writable inverter fields that other commands actually touched during the
|
|
@@ -1364,7 +1311,10 @@ export class SunspecBattery extends BaseSunspecDevice {
|
|
|
1364
1311
|
console.error('Battery not connected');
|
|
1365
1312
|
return false;
|
|
1366
1313
|
}
|
|
1367
|
-
|
|
1314
|
+
// Redundant with the modbus client's own per-write debug trace. Keep at debug
|
|
1315
|
+
// so the high-level info logs (setStorageMode / enableGridCharging / etc.) stay
|
|
1316
|
+
// the only info-level signal per battery-control action.
|
|
1317
|
+
console.debug('Writing battery controls:', controls);
|
|
1368
1318
|
return this.sunspecClient.writeBatteryControls(this.unitId, controls);
|
|
1369
1319
|
}
|
|
1370
1320
|
mapToEnyoStorageMode(storageMode) {
|
|
@@ -1462,8 +1412,6 @@ export class SunspecBattery extends BaseSunspecDevice {
|
|
|
1462
1412
|
this.dataBus = this.energyApp.useDataBus();
|
|
1463
1413
|
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
1464
1414
|
EnyoDataBusMessageEnum.SetStorageScheduleV1,
|
|
1465
|
-
EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
1466
|
-
EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
1467
1415
|
], (entry) => this.handleStorageCommand(entry));
|
|
1468
1416
|
if (!this.applianceId) {
|
|
1469
1417
|
throw new Error("SunspecBattery.startDataBusListening: applianceId required — call connect() first.");
|
|
@@ -1502,12 +1450,6 @@ export class SunspecBattery extends BaseSunspecDevice {
|
|
|
1502
1450
|
case EnyoDataBusMessageEnum.SetStorageScheduleV1:
|
|
1503
1451
|
this.handleSetStorageScheduleAck(entry);
|
|
1504
1452
|
break;
|
|
1505
|
-
case EnyoDataBusMessageEnum.StartCalibrationV1:
|
|
1506
|
-
await this.handleStartCalibration(entry);
|
|
1507
|
-
break;
|
|
1508
|
-
case EnyoDataBusMessageEnum.StopCalibrationV1:
|
|
1509
|
-
await this.handleStopCalibration(entry);
|
|
1510
|
-
break;
|
|
1511
1453
|
}
|
|
1512
1454
|
}
|
|
1513
1455
|
catch (error) {
|
|
@@ -1547,51 +1489,6 @@ export class SunspecBattery extends BaseSunspecDevice {
|
|
|
1547
1489
|
await this.snapshotService.initialize();
|
|
1548
1490
|
console.log(`Battery ${this.applianceId}: snapshot service initialized`);
|
|
1549
1491
|
}
|
|
1550
|
-
async handleStartCalibration(msg) {
|
|
1551
|
-
if (!this.isConnected() || !this.applianceId) {
|
|
1552
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not connected');
|
|
1553
|
-
return;
|
|
1554
|
-
}
|
|
1555
|
-
if (!this.snapshotService) {
|
|
1556
|
-
await this.initSnapshotService();
|
|
1557
|
-
}
|
|
1558
|
-
if (!this.snapshotService) {
|
|
1559
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Snapshot service unavailable');
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
if (this.snapshotService.isCalibrating()) {
|
|
1563
|
-
console.log(`Battery ${this.applianceId}: calibration already active — ack idempotently`);
|
|
1564
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1565
|
-
return;
|
|
1566
|
-
}
|
|
1567
|
-
console.log(`Battery ${this.applianceId}: handling StartCalibrationV1`);
|
|
1568
|
-
const controls = await this.sunspecClient.readBatteryControls(this.unitId);
|
|
1569
|
-
if (!controls) {
|
|
1570
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Failed to read battery controls for snapshot');
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
try {
|
|
1574
|
-
await this.snapshotService.startCalibration(controls);
|
|
1575
|
-
}
|
|
1576
|
-
catch (error) {
|
|
1577
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, `Failed to persist snapshot: ${error}`);
|
|
1578
|
-
return;
|
|
1579
|
-
}
|
|
1580
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1581
|
-
}
|
|
1582
|
-
async handleStopCalibration(msg) {
|
|
1583
|
-
if (!this.applianceId) {
|
|
1584
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Rejected, 'Not initialized');
|
|
1585
|
-
return;
|
|
1586
|
-
}
|
|
1587
|
-
console.log(`Battery ${this.applianceId}: handling StopCalibrationV1`);
|
|
1588
|
-
// SnapshotService.stopCalibration fires `restoreBatterySnapshot` internally
|
|
1589
|
-
// with reason="stop" before returning. Failures from the restore callback are
|
|
1590
|
-
// logged inside the callback (the persisted snapshot is already gone — see
|
|
1591
|
-
// library notes) so we always ack Accepted here.
|
|
1592
|
-
await this.snapshotService?.stopCalibration();
|
|
1593
|
-
this.sendCommandAcknowledge(msg.id, msg.message, EnyoCommandAcknowledgeAnswerEnum.Accepted);
|
|
1594
|
-
}
|
|
1595
1492
|
/**
|
|
1596
1493
|
* `onRestore` callback for the battery's {@link SnapshotService}. Writes only the
|
|
1597
1494
|
* subset of writable battery fields touched during the calibration. Errors are
|
|
@@ -1696,13 +1593,11 @@ export class SunspecMeter extends BaseSunspecDevice {
|
|
|
1696
1593
|
console.error(`Failed to update meter appliance: ${error}`);
|
|
1697
1594
|
}
|
|
1698
1595
|
}
|
|
1699
|
-
this.startDataBusListening();
|
|
1700
1596
|
}
|
|
1701
1597
|
/**
|
|
1702
1598
|
* Disconnect from the meter and update appliance state
|
|
1703
1599
|
*/
|
|
1704
1600
|
async disconnect() {
|
|
1705
|
-
this.stopDataBusListening();
|
|
1706
1601
|
if (this.applianceId) {
|
|
1707
1602
|
try {
|
|
1708
1603
|
await this.applianceManager.updateApplianceState(this.applianceId, EnyoApplianceConnectionType.Connector, EnyoApplianceStateEnum.Offline);
|
|
@@ -1714,35 +1609,6 @@ export class SunspecMeter extends BaseSunspecDevice {
|
|
|
1714
1609
|
// Close just this meter's unit; other devices on the same network device stay open.
|
|
1715
1610
|
await this.sunspecClient.disconnectUnit(this.unitId);
|
|
1716
1611
|
}
|
|
1717
|
-
/**
|
|
1718
|
-
* Meter does not implement calibration; it only subscribes to Start/StopCalibrationV1 to
|
|
1719
|
-
* answer NotSupported (per the data-bus contract that every command must be acknowledged).
|
|
1720
|
-
*/
|
|
1721
|
-
startDataBusListening() {
|
|
1722
|
-
if (this.dataBusListenerId) {
|
|
1723
|
-
return;
|
|
1724
|
-
}
|
|
1725
|
-
this.dataBus = this.energyApp.useDataBus();
|
|
1726
|
-
this.dataBusListenerId = this.dataBus.listenForMessages([
|
|
1727
|
-
EnyoDataBusMessageEnum.StartCalibrationV1,
|
|
1728
|
-
EnyoDataBusMessageEnum.StopCalibrationV1,
|
|
1729
|
-
], (entry) => this.handleMeterCommand(entry));
|
|
1730
|
-
console.log(`Meter ${this.applianceId}: started data bus listening (listener ${this.dataBusListenerId})`);
|
|
1731
|
-
}
|
|
1732
|
-
stopDataBusListening() {
|
|
1733
|
-
if (this.dataBusListenerId && this.dataBus) {
|
|
1734
|
-
this.dataBus.unsubscribe(this.dataBusListenerId);
|
|
1735
|
-
console.log(`Meter ${this.applianceId}: stopped data bus listening (listener ${this.dataBusListenerId})`);
|
|
1736
|
-
}
|
|
1737
|
-
this.dataBusListenerId = undefined;
|
|
1738
|
-
this.dataBus = undefined;
|
|
1739
|
-
}
|
|
1740
|
-
handleMeterCommand(entry) {
|
|
1741
|
-
if (entry.applianceId !== this.applianceId) {
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
this.sendCommandAcknowledge(entry.id, entry.message, EnyoCommandAcknowledgeAnswerEnum.NotSupported, 'Meter does not support calibration');
|
|
1745
|
-
}
|
|
1746
1612
|
/**
|
|
1747
1613
|
* Update meter data and return data bus messages
|
|
1748
1614
|
*/
|
|
@@ -481,7 +481,10 @@ export class SunspecModbusClient {
|
|
|
481
481
|
}
|
|
482
482
|
catch (error) {
|
|
483
483
|
console.debug(`No SunSpec device at unit ${unitId}: ${error}`);
|
|
484
|
-
|
|
484
|
+
// Discovery summary at line 594 below is the info-level outcome.
|
|
485
|
+
// This 0-models branch is the same outcome as a normal "no device found",
|
|
486
|
+
// covered already by the debug above. Keep at debug.
|
|
487
|
+
console.debug(`Discovery complete for unit ${unitId}. Found 0 models: []`);
|
|
485
488
|
return models;
|
|
486
489
|
}
|
|
487
490
|
currentAddress = addressInfo.nextAddress;
|
|
@@ -490,14 +493,14 @@ export class SunspecModbusClient {
|
|
|
490
493
|
const buffer = await instance.readHoldingRegisters(currentAddress, 2);
|
|
491
494
|
const modelData = [buffer.readUInt16BE(0), buffer.readUInt16BE(2)];
|
|
492
495
|
if (!modelData || modelData.length < 2) {
|
|
493
|
-
console.
|
|
496
|
+
console.debug(`No data at address ${currentAddress}, ending discovery`);
|
|
494
497
|
break;
|
|
495
498
|
}
|
|
496
499
|
const modelId = modelData[0];
|
|
497
500
|
const modelLength = modelData[1];
|
|
498
501
|
// Check for end marker
|
|
499
502
|
if (modelId === 0xFFFF || modelId === 65535) {
|
|
500
|
-
console.
|
|
503
|
+
console.debug(`Found end marker at address ${currentAddress}`);
|
|
501
504
|
break;
|
|
502
505
|
}
|
|
503
506
|
// Store discovered model
|
|
@@ -507,7 +510,9 @@ export class SunspecModbusClient {
|
|
|
507
510
|
length: modelLength
|
|
508
511
|
};
|
|
509
512
|
models.set(modelId, model);
|
|
510
|
-
|
|
513
|
+
// Per-model discovery step. The end-of-discovery summary below is the
|
|
514
|
+
// info-level outcome; the per-model walk is debug-only.
|
|
515
|
+
console.debug(`Discovered Model ${modelId} at address ${currentAddress} with length ${modelLength} (unit ${unitId})`);
|
|
511
516
|
// Jump to next model: current address + 2 (header) + model length
|
|
512
517
|
currentAddress = currentAddress + 2 + modelLength;
|
|
513
518
|
}
|
|
@@ -696,7 +701,9 @@ export class SunspecModbusClient {
|
|
|
696
701
|
// Write to holding registers
|
|
697
702
|
await instance.writeMultipleRegisters(address, registerValues);
|
|
698
703
|
this.connectionHealth.recordSuccess();
|
|
699
|
-
|
|
704
|
+
// Per-register write success — fires for every parameter inside higher-level
|
|
705
|
+
// writers (writeBatteryControls / writeInverterControls). Demoted to debug.
|
|
706
|
+
console.debug(`Successfully wrote value ${value} to register ${address} (unit ${unitId})`);
|
|
700
707
|
return true;
|
|
701
708
|
}
|
|
702
709
|
catch (error) {
|
|
@@ -1594,7 +1601,10 @@ export class SunspecModbusClient {
|
|
|
1594
1601
|
data.dischargePower = Math.abs((data.outWRte / 100) * data.wChaMax);
|
|
1595
1602
|
console.debug(`Calculated Discharge Power (inWRte: ${data.outWRte}, wChaMax: ${data.wChaMax}): ${data.dischargePower?.toFixed(2)} W`);
|
|
1596
1603
|
}
|
|
1597
|
-
|
|
1604
|
+
// Single-line JSON debug dump — Node's default formatter would split
|
|
1605
|
+
// the object across many lines; stringify keeps the whole snapshot
|
|
1606
|
+
// on one log entry so it stays grep-able and round-trippable.
|
|
1607
|
+
console.debug(`[Battery] unit=${unitId} model=124 data=${JSON.stringify(data)}`);
|
|
1598
1608
|
return data;
|
|
1599
1609
|
}
|
|
1600
1610
|
else if (model.id === 802) {
|
|
@@ -1631,17 +1641,18 @@ export class SunspecModbusClient {
|
|
|
1631
1641
|
chargePower,
|
|
1632
1642
|
dischargePower,
|
|
1633
1643
|
};
|
|
1634
|
-
console.debug(
|
|
1644
|
+
console.debug(`[Battery] unit=${unitId} model=802 data=${JSON.stringify(result)}`);
|
|
1635
1645
|
return result;
|
|
1636
1646
|
}
|
|
1637
1647
|
else {
|
|
1638
|
-
// Handle other battery models (803) if needed
|
|
1639
|
-
|
|
1640
|
-
return {
|
|
1648
|
+
// Handle other battery models (803) if needed.
|
|
1649
|
+
const stub = {
|
|
1641
1650
|
blockNumber: model.id,
|
|
1642
1651
|
blockAddress: model.address,
|
|
1643
|
-
blockLength: model.length
|
|
1652
|
+
blockLength: model.length,
|
|
1644
1653
|
};
|
|
1654
|
+
console.debug(`[Battery] unit=${unitId} model=${model.id} (not yet implemented) data=${JSON.stringify(stub)}`);
|
|
1655
|
+
return stub;
|
|
1645
1656
|
}
|
|
1646
1657
|
}
|
|
1647
1658
|
catch (error) {
|
|
@@ -1659,7 +1670,11 @@ export class SunspecModbusClient {
|
|
|
1659
1670
|
return false;
|
|
1660
1671
|
}
|
|
1661
1672
|
const baseAddr = model.address;
|
|
1662
|
-
|
|
1673
|
+
// Per-register write trace is high-volume (every schedule entry transition fires
|
|
1674
|
+
// a writeBatteryControls). Demoted to debug; consumers that need the trail can
|
|
1675
|
+
// raise their log level. High-level callers (setStorageMode, enableGridCharging,
|
|
1676
|
+
// setFeedInLimit, etc.) still emit one info-level line per action.
|
|
1677
|
+
console.debug(`Writing Battery Controls to Model 124 at base address: ${baseAddr} (unit ${unitId})`);
|
|
1663
1678
|
try {
|
|
1664
1679
|
// Write order: source pin and parameter writes land BEFORE the
|
|
1665
1680
|
// control mode so the device only "starts acting" once every
|
|
@@ -1669,7 +1684,7 @@ export class SunspecModbusClient {
|
|
|
1669
1684
|
// Write charge source setting (Register 17)
|
|
1670
1685
|
if (controls.chaGriSet !== undefined) {
|
|
1671
1686
|
await this.writeRegisterValue(unitId, baseAddr + 17, controls.chaGriSet, 'uint16');
|
|
1672
|
-
console.
|
|
1687
|
+
console.debug(`Set charge source to ${controls.chaGriSet === SunspecChargeSource.GRID ? 'GRID' : 'PV'}`);
|
|
1673
1688
|
}
|
|
1674
1689
|
// Write maximum charge power (Register 2) - needs scale factor
|
|
1675
1690
|
if (controls.wChaMax !== undefined) {
|
|
@@ -1677,7 +1692,7 @@ export class SunspecModbusClient {
|
|
|
1677
1692
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1678
1693
|
const scaledValue = Math.round(controls.wChaMax / Math.pow(10, scaleFactor));
|
|
1679
1694
|
await this.writeRegisterValue(unitId, baseAddr + 2, scaledValue, 'uint16');
|
|
1680
|
-
console.
|
|
1695
|
+
console.debug(`Set max charge power to ${controls.wChaMax}W (scaled: ${scaledValue})`);
|
|
1681
1696
|
}
|
|
1682
1697
|
// Write charge rate (Register 13) - needs scale factor
|
|
1683
1698
|
if (controls.inWRte !== undefined) {
|
|
@@ -1685,7 +1700,7 @@ export class SunspecModbusClient {
|
|
|
1685
1700
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1686
1701
|
const scaledValue = Math.round(controls.inWRte / Math.pow(10, scaleFactor));
|
|
1687
1702
|
await this.writeRegisterValue(unitId, baseAddr + 13, scaledValue, 'int16');
|
|
1688
|
-
console.
|
|
1703
|
+
console.debug(`Set charge rate to ${controls.inWRte}% (scaled: ${scaledValue})`);
|
|
1689
1704
|
}
|
|
1690
1705
|
// Write discharge rate (Register 12) - needs scale factor
|
|
1691
1706
|
if (controls.outWRte !== undefined) {
|
|
@@ -1693,7 +1708,7 @@ export class SunspecModbusClient {
|
|
|
1693
1708
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1694
1709
|
const scaledValue = Math.round(controls.outWRte / Math.pow(10, scaleFactor));
|
|
1695
1710
|
await this.writeRegisterValue(unitId, baseAddr + 12, scaledValue, 'int16');
|
|
1696
|
-
console.
|
|
1711
|
+
console.debug(`Set discharge rate to ${controls.outWRte}% (scaled: ${scaledValue})`);
|
|
1697
1712
|
}
|
|
1698
1713
|
// Write minimum reserve percentage (Register 7) - needs scale factor
|
|
1699
1714
|
if (controls.minRsvPct !== undefined) {
|
|
@@ -1701,16 +1716,16 @@ export class SunspecModbusClient {
|
|
|
1701
1716
|
const scaleFactor = await this.readRegisterValue(unitId, scaleFactorAddr, 1, 'int16');
|
|
1702
1717
|
const scaledValue = Math.round(controls.minRsvPct / Math.pow(10, scaleFactor));
|
|
1703
1718
|
await this.writeRegisterValue(unitId, baseAddr + 7, scaledValue, 'uint16');
|
|
1704
|
-
console.
|
|
1719
|
+
console.debug(`Set minimum reserve to ${controls.minRsvPct}% (scaled: ${scaledValue})`);
|
|
1705
1720
|
}
|
|
1706
1721
|
// Storage control mode (Register 5) — written LAST so all
|
|
1707
1722
|
// governing parameters are already in place when the device
|
|
1708
1723
|
// transitions into the new mode.
|
|
1709
1724
|
if (controls.storCtlMod !== undefined) {
|
|
1710
1725
|
await this.writeRegisterValue(unitId, baseAddr + 5, controls.storCtlMod, 'uint16');
|
|
1711
|
-
console.
|
|
1726
|
+
console.debug(`Set storage control mode to 0x${controls.storCtlMod.toString(16)}`);
|
|
1712
1727
|
}
|
|
1713
|
-
console.
|
|
1728
|
+
console.debug('Battery controls written successfully');
|
|
1714
1729
|
return true;
|
|
1715
1730
|
}
|
|
1716
1731
|
catch (error) {
|
|
@@ -1761,10 +1776,12 @@ export class SunspecModbusClient {
|
|
|
1761
1776
|
async readBatteryControls(unitId) {
|
|
1762
1777
|
const model = this.findModel(unitId, SunspecModelId.Battery);
|
|
1763
1778
|
if (!model) {
|
|
1764
|
-
|
|
1779
|
+
// Fires per readBatteryControls attempt — noisy on non-battery units
|
|
1780
|
+
// that go through the same code path. Demoted to debug.
|
|
1781
|
+
console.debug(`Battery model 124 not found on unit ${unitId}`);
|
|
1765
1782
|
return null;
|
|
1766
1783
|
}
|
|
1767
|
-
console.
|
|
1784
|
+
console.debug(`Reading Battery Controls from Model 124 at base address: ${model.address} (unit ${unitId})`);
|
|
1768
1785
|
try {
|
|
1769
1786
|
// Read entire model block in a single Modbus call
|
|
1770
1787
|
const buffer = await this.readModelBlock(unitId, model);
|
|
@@ -1979,7 +1996,7 @@ export class SunspecModbusClient {
|
|
|
1979
1996
|
console.debug(`Common block model not found on unit ${unitId}`);
|
|
1980
1997
|
return null;
|
|
1981
1998
|
}
|
|
1982
|
-
console.
|
|
1999
|
+
console.debug(`Reading Common Block - Model address: ${model.address} (unit ${unitId})`);
|
|
1983
2000
|
try {
|
|
1984
2001
|
// Read entire model block in a single Modbus call
|
|
1985
2002
|
const buffer = await this.readModelBlock(unitId, model);
|
|
@@ -2139,7 +2156,8 @@ export class SunspecModbusClient {
|
|
|
2139
2156
|
async readInverterControls(unitId) {
|
|
2140
2157
|
const model = this.findModel(unitId, SunspecModelId.Controls);
|
|
2141
2158
|
if (!model) {
|
|
2142
|
-
|
|
2159
|
+
// Same trace-only rationale as the Model 124 not-found message above.
|
|
2160
|
+
console.debug(`Controls model 123 not found on unit ${unitId}`);
|
|
2143
2161
|
return null;
|
|
2144
2162
|
}
|
|
2145
2163
|
try {
|
|
@@ -2248,17 +2266,17 @@ export class SunspecModbusClient {
|
|
|
2248
2266
|
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
2249
2267
|
const scaledValue = Math.round(settings.WMax / Math.pow(10, scaleFactor));
|
|
2250
2268
|
// Writing registers needs to be implemented in EnergyAppModbusInstance
|
|
2251
|
-
// For now, log the write operation
|
|
2252
|
-
console.
|
|
2269
|
+
// For now, log the (would-be) write operation. Dry-run trace only — debug.
|
|
2270
|
+
console.debug(`Would write value ${scaledValue} to register ${baseAddr}`);
|
|
2253
2271
|
}
|
|
2254
2272
|
if (settings.VRef !== undefined) {
|
|
2255
2273
|
const sfBuffer = await instance.readHoldingRegisters(baseAddr + 23, 1);
|
|
2256
2274
|
const scaleFactor = sfBuffer.readInt16BE(0);
|
|
2257
2275
|
const scaledValue = Math.round(settings.VRef / Math.pow(10, scaleFactor));
|
|
2258
|
-
console.
|
|
2276
|
+
console.debug(`Would write value ${scaledValue} to register ${baseAddr + 1}`);
|
|
2259
2277
|
}
|
|
2260
2278
|
// Add more write operations for other settings as needed
|
|
2261
|
-
console.
|
|
2279
|
+
console.debug('Inverter settings written successfully');
|
|
2262
2280
|
return true;
|
|
2263
2281
|
}
|
|
2264
2282
|
catch (error) {
|
|
@@ -2277,36 +2295,37 @@ export class SunspecModbusClient {
|
|
|
2277
2295
|
}
|
|
2278
2296
|
const baseAddr = model.address;
|
|
2279
2297
|
try {
|
|
2280
|
-
//
|
|
2298
|
+
// Per-field inverter-control writes are debug-only; the high-level callers
|
|
2299
|
+
// (`setFeedInLimit`, etc.) still log the action and outcome at info level.
|
|
2281
2300
|
if (controls.Conn !== undefined) {
|
|
2282
2301
|
await this.writeRegisterValue(unitId, baseAddr + 2, controls.Conn, 'uint16');
|
|
2283
|
-
console.
|
|
2302
|
+
console.debug(`Set connection control to ${controls.Conn}`);
|
|
2284
2303
|
}
|
|
2285
2304
|
// Power limit control (Register 3) - needs scale factor
|
|
2286
2305
|
if (controls.WMaxLimPct !== undefined) {
|
|
2287
2306
|
const scaleFactor = await this.readRegisterValue(unitId, baseAddr + 21, 1, 'int16');
|
|
2288
2307
|
const scaledValue = Math.round(controls.WMaxLimPct / Math.pow(10, scaleFactor));
|
|
2289
2308
|
await this.writeRegisterValue(unitId, baseAddr + 3, scaledValue, 'uint16');
|
|
2290
|
-
console.
|
|
2309
|
+
console.debug(`Set power limit to ${controls.WMaxLimPct}% (scaled: ${scaledValue})`);
|
|
2291
2310
|
}
|
|
2292
2311
|
// Throttle enable/disable (Register 7)
|
|
2293
2312
|
if (controls.WMaxLim_Ena !== undefined) {
|
|
2294
2313
|
await this.writeRegisterValue(unitId, baseAddr + 7, controls.WMaxLim_Ena, 'uint16');
|
|
2295
|
-
console.
|
|
2314
|
+
console.debug(`Set throttle enable to ${controls.WMaxLim_Ena}`);
|
|
2296
2315
|
}
|
|
2297
2316
|
// Power factor control (Register 8) - needs scale factor
|
|
2298
2317
|
if (controls.OutPFSet !== undefined) {
|
|
2299
2318
|
const scaleFactor = await this.readRegisterValue(unitId, baseAddr + 22, 1, 'int16');
|
|
2300
2319
|
const scaledValue = Math.round(controls.OutPFSet / Math.pow(10, scaleFactor));
|
|
2301
2320
|
await this.writeRegisterValue(unitId, baseAddr + 8, scaledValue, 'int16');
|
|
2302
|
-
console.
|
|
2321
|
+
console.debug(`Set power factor to ${controls.OutPFSet} (scaled: ${scaledValue})`);
|
|
2303
2322
|
}
|
|
2304
2323
|
// Power factor enable/disable (Register 12)
|
|
2305
2324
|
if (controls.OutPFSet_Ena !== undefined) {
|
|
2306
2325
|
await this.writeRegisterValue(unitId, baseAddr + 12, controls.OutPFSet_Ena, 'uint16');
|
|
2307
|
-
console.
|
|
2326
|
+
console.debug(`Set PF enable to ${controls.OutPFSet_Ena}`);
|
|
2308
2327
|
}
|
|
2309
|
-
console.
|
|
2328
|
+
console.debug('Inverter controls written successfully');
|
|
2310
2329
|
return true;
|
|
2311
2330
|
}
|
|
2312
2331
|
catch (error) {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enyo-energy/sunspec-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.75",
|
|
4
4
|
"description": "enyo Energy Sunspec SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@enyo-energy/appliance-calibration": "0.0.2",
|
|
44
|
-
"@enyo-energy/energy-app-sdk": "0.0.
|
|
44
|
+
"@enyo-energy/energy-app-sdk": "0.0.144"
|
|
45
45
|
},
|
|
46
46
|
"volta": {
|
|
47
47
|
"node": "22.17.0"
|