@enyo-energy/sunspec-sdk 0.0.6 → 0.0.8
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 +11 -2
- package/dist/cjs/sunspec-interfaces.d.cts +5 -5
- package/dist/cjs/sunspec-modbus-client.cjs +142 -24
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/sunspec-devices.js +11 -2
- package/dist/sunspec-interfaces.d.ts +5 -5
- package/dist/sunspec-modbus-client.js +142 -24
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -103,6 +103,15 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
103
103
|
const inverterData = await this.sunspecClient.readInverterData();
|
|
104
104
|
const mpptDataList = await this.sunspecClient.readAllMPPTData();
|
|
105
105
|
if (inverterData) {
|
|
106
|
+
// Log what we're sending
|
|
107
|
+
console.log('Preparing Inverter Message:', {
|
|
108
|
+
pvPowerW: inverterData.dcPower,
|
|
109
|
+
acPower: inverterData.acPower,
|
|
110
|
+
voltageL1: inverterData.voltageAN,
|
|
111
|
+
voltageL2: inverterData.voltageBN,
|
|
112
|
+
voltageL3: inverterData.voltageCN,
|
|
113
|
+
stringsCount: mpptDataList.length
|
|
114
|
+
});
|
|
106
115
|
const inverterMessage = {
|
|
107
116
|
id: (0, node_crypto_1.randomUUID)(),
|
|
108
117
|
message: enyo_data_bus_value_js_1.EnyoDataBusMessageEnum.InverterValuesUpdateV1,
|
|
@@ -117,8 +126,8 @@ class SunspecInverter extends BaseSunspecDevice {
|
|
|
117
126
|
activePowerLimitationW: inverterData.acPower || 0,
|
|
118
127
|
state: this.mapOperatingState(inverterData.operatingState),
|
|
119
128
|
voltageL1: inverterData.voltageAN || 0,
|
|
120
|
-
voltageL2: inverterData.voltageBN ?? undefined, // Use
|
|
121
|
-
voltageL3: inverterData.voltageCN ?? undefined, // Use
|
|
129
|
+
voltageL2: inverterData.voltageBN ?? undefined, // Use undefined if unimplemented phase
|
|
130
|
+
voltageL3: inverterData.voltageCN ?? undefined, // Use undefined if unimplemented phase
|
|
122
131
|
strings: this.mapMPPTToStrings(mpptDataList)
|
|
123
132
|
}
|
|
124
133
|
};
|
|
@@ -79,7 +79,7 @@ export interface SunspecInverterData extends SunspecBlock {
|
|
|
79
79
|
apparentPower?: number;
|
|
80
80
|
reactivePower?: number;
|
|
81
81
|
powerFactor?: number;
|
|
82
|
-
acEnergy?:
|
|
82
|
+
acEnergy?: number;
|
|
83
83
|
dcCurrent?: number;
|
|
84
84
|
dcVoltage?: number;
|
|
85
85
|
dcPower?: number;
|
|
@@ -109,7 +109,7 @@ export interface SunspecMPPTData extends SunspecBlock {
|
|
|
109
109
|
dcVoltageSF?: number;
|
|
110
110
|
dcPower?: number;
|
|
111
111
|
dcPowerSF?: number;
|
|
112
|
-
dcEnergy?:
|
|
112
|
+
dcEnergy?: number;
|
|
113
113
|
dcEnergySF?: number;
|
|
114
114
|
timestamp?: number;
|
|
115
115
|
temperature?: number;
|
|
@@ -141,9 +141,9 @@ export interface SunspecMeterData extends SunspecBlock {
|
|
|
141
141
|
phaseAPower?: number;
|
|
142
142
|
phaseBPower?: number;
|
|
143
143
|
phaseCPower?: number;
|
|
144
|
-
totalEnergy?:
|
|
145
|
-
exportedEnergy?:
|
|
146
|
-
importedEnergy?:
|
|
144
|
+
totalEnergy?: number;
|
|
145
|
+
exportedEnergy?: number;
|
|
146
|
+
importedEnergy?: number;
|
|
147
147
|
voltage?: number;
|
|
148
148
|
current?: number;
|
|
149
149
|
frequency?: number;
|
|
@@ -279,11 +279,14 @@ class SunspecModbusClient {
|
|
|
279
279
|
console.error('No inverter model found');
|
|
280
280
|
return null;
|
|
281
281
|
}
|
|
282
|
+
console.warn('IMPORTANT: Working with single-phase inverter, but 3-phase is expected!');
|
|
282
283
|
return this.readSinglePhaseInverterData(singlePhaseModel);
|
|
283
284
|
}
|
|
285
|
+
console.log(`Found 3-phase inverter model 103 at address ${model.address} with length ${model.length}`);
|
|
284
286
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
285
287
|
try {
|
|
286
288
|
// Read all scale factors first using fault-tolerant reader
|
|
289
|
+
console.log(`Reading Inverter Data from Model 103 at base address: ${baseAddr}`);
|
|
287
290
|
const scaleFactors = await this.readInverterScaleFactors(baseAddr);
|
|
288
291
|
// Read values using fault-tolerant reader with proper data types
|
|
289
292
|
// Read raw voltage values to check for unimplemented phases
|
|
@@ -300,7 +303,12 @@ class SunspecModbusClient {
|
|
|
300
303
|
blockAddress: model.address,
|
|
301
304
|
blockLength: model.length,
|
|
302
305
|
// AC Current values - Offsets 2-5
|
|
303
|
-
acCurrent: this.applyScaleFactor(await
|
|
306
|
+
acCurrent: this.applyScaleFactor(await (async () => {
|
|
307
|
+
const addr = baseAddr + 2;
|
|
308
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
309
|
+
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
310
|
+
return raw;
|
|
311
|
+
})(), scaleFactors.A_SF),
|
|
304
312
|
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
305
313
|
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
306
314
|
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
@@ -313,16 +321,36 @@ class SunspecModbusClient {
|
|
|
313
321
|
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF),
|
|
314
322
|
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF),
|
|
315
323
|
// Power values - Offsets 14, 18, 20, 22
|
|
316
|
-
acPower: this.applyScaleFactor(await
|
|
324
|
+
acPower: this.applyScaleFactor(await (async () => {
|
|
325
|
+
const addr = baseAddr + 14;
|
|
326
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
327
|
+
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
328
|
+
return raw;
|
|
329
|
+
})(), scaleFactors.W_SF, 'int16'),
|
|
317
330
|
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
318
331
|
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
319
332
|
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
320
333
|
// Frequency - Offset 16
|
|
321
334
|
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
322
335
|
// DC values - Offsets 27, 28, 30
|
|
323
|
-
dcCurrent: this.applyScaleFactor(await
|
|
324
|
-
|
|
325
|
-
|
|
336
|
+
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
337
|
+
const addr = baseAddr + 27;
|
|
338
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
339
|
+
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
340
|
+
return raw;
|
|
341
|
+
})(), scaleFactors.DCA_SF),
|
|
342
|
+
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
343
|
+
const addr = baseAddr + 28;
|
|
344
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
345
|
+
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
346
|
+
return raw;
|
|
347
|
+
})(), scaleFactors.DCV_SF),
|
|
348
|
+
dcPower: this.applyScaleFactor(await (async () => {
|
|
349
|
+
const addr = baseAddr + 30;
|
|
350
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
351
|
+
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
352
|
+
return raw;
|
|
353
|
+
})(), scaleFactors.DCW_SF, 'int16'),
|
|
326
354
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
327
355
|
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
328
356
|
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
@@ -340,8 +368,20 @@ class SunspecModbusClient {
|
|
|
340
368
|
vendorEvents4: await this.readRegisterValue(baseAddr + 50, 2, 'uint32')
|
|
341
369
|
};
|
|
342
370
|
// Read AC Energy (32-bit accumulator) - Offset 24-25
|
|
343
|
-
const
|
|
344
|
-
|
|
371
|
+
const acEnergyAddr = baseAddr + 24;
|
|
372
|
+
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
373
|
+
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
374
|
+
data.acEnergy = !this.isUnimplementedValue(acEnergy, 'uint32')
|
|
375
|
+
? acEnergy * Math.pow(10, scaleFactors.WH_SF) // Regular number with scale factor
|
|
376
|
+
: undefined;
|
|
377
|
+
// Log final calculated values
|
|
378
|
+
console.log('Inverter Final Values:', {
|
|
379
|
+
acPower: data.acPower,
|
|
380
|
+
voltageAN: data.voltageAN,
|
|
381
|
+
voltageBN: data.voltageBN,
|
|
382
|
+
voltageCN: data.voltageCN,
|
|
383
|
+
acCurrent: data.acCurrent
|
|
384
|
+
});
|
|
345
385
|
return data;
|
|
346
386
|
}
|
|
347
387
|
catch (error) {
|
|
@@ -357,15 +397,53 @@ class SunspecModbusClient {
|
|
|
357
397
|
// Implementation would be similar but simplified
|
|
358
398
|
const baseAddr = model.address + 2;
|
|
359
399
|
try {
|
|
360
|
-
|
|
400
|
+
console.log(`Reading Single-Phase Inverter Data from Model 101 at base address: ${baseAddr}`);
|
|
401
|
+
// Read scale factors for single phase model
|
|
402
|
+
const scaleFactors = {
|
|
403
|
+
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'),
|
|
404
|
+
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'),
|
|
405
|
+
W_SF: await this.readRegisterValue(baseAddr + 10, 1, 'int16'),
|
|
406
|
+
Hz_SF: await this.readRegisterValue(baseAddr + 12, 1, 'int16'),
|
|
407
|
+
DCA_SF: await this.readRegisterValue(baseAddr + 18, 1, 'int16'),
|
|
408
|
+
DCV_SF: await this.readRegisterValue(baseAddr + 19, 1, 'int16'),
|
|
409
|
+
DCW_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16')
|
|
410
|
+
};
|
|
411
|
+
console.log('Single-Phase Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
412
|
+
// Read raw values with logging
|
|
413
|
+
const acCurrentAddr = baseAddr + 2;
|
|
414
|
+
const acCurrentRaw = await this.readRegisterValue(acCurrentAddr, 1, 'uint16');
|
|
415
|
+
console.log(`AC Current: address=${acCurrentAddr}, raw=0x${acCurrentRaw.toString(16).toUpperCase()} (${acCurrentRaw})`);
|
|
416
|
+
const voltageAddr = baseAddr + 7;
|
|
417
|
+
const voltageRaw = await this.readRegisterValue(voltageAddr, 1, 'uint16');
|
|
418
|
+
console.log(`AC Voltage: address=${voltageAddr}, raw=0x${voltageRaw.toString(16).toUpperCase()} (${voltageRaw})`);
|
|
419
|
+
const acPowerAddr = baseAddr + 9;
|
|
420
|
+
const acPowerRaw = await this.readRegisterValue(acPowerAddr, 1, 'int16');
|
|
421
|
+
console.log(`AC Power: address=${acPowerAddr}, raw=0x${acPowerRaw.toString(16).toUpperCase()} (${acPowerRaw}), SF=${scaleFactors.W_SF}`);
|
|
422
|
+
const freqAddr = baseAddr + 11;
|
|
423
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
424
|
+
console.log(`Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw})`);
|
|
425
|
+
const dcCurrentAddr = baseAddr + 14;
|
|
426
|
+
const dcCurrentRaw = await this.readRegisterValue(dcCurrentAddr, 1, 'uint16');
|
|
427
|
+
console.log(`DC Current: address=${dcCurrentAddr}, raw=0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`);
|
|
428
|
+
const dcVoltageAddr = baseAddr + 15;
|
|
429
|
+
const dcVoltageRaw = await this.readRegisterValue(dcVoltageAddr, 1, 'uint16');
|
|
430
|
+
console.log(`DC Voltage: address=${dcVoltageAddr}, raw=0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`);
|
|
431
|
+
const dcPowerAddr = baseAddr + 20;
|
|
432
|
+
const dcPowerRaw = await this.readRegisterValue(dcPowerAddr, 1, 'int16');
|
|
433
|
+
console.log(`DC Power: address=${dcPowerAddr}, raw=0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw}), SF=${scaleFactors.DCW_SF}`);
|
|
434
|
+
const stateAddr = baseAddr + 24;
|
|
435
|
+
const stateRaw = await this.readRegisterValue(stateAddr, 1, 'uint16');
|
|
436
|
+
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
361
437
|
return {
|
|
362
438
|
blockNumber: 101,
|
|
363
|
-
voltageAN:
|
|
364
|
-
acCurrent:
|
|
365
|
-
acPower:
|
|
366
|
-
frequency:
|
|
367
|
-
|
|
368
|
-
|
|
439
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
440
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
441
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
442
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
443
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
444
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
445
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
446
|
+
operatingState: stateRaw
|
|
369
447
|
};
|
|
370
448
|
}
|
|
371
449
|
catch (error) {
|
|
@@ -403,11 +481,6 @@ class SunspecModbusClient {
|
|
|
403
481
|
if (this.isUnimplementedValue(value, dataType)) {
|
|
404
482
|
return undefined;
|
|
405
483
|
}
|
|
406
|
-
// Validate scale factor is within reasonable range (-10 to +10)
|
|
407
|
-
if (Math.abs(scaleFactor) > 10) {
|
|
408
|
-
console.warn(`Scale factor ${scaleFactor} is outside reasonable range, clamping to ±10`);
|
|
409
|
-
scaleFactor = Math.max(-10, Math.min(10, scaleFactor));
|
|
410
|
-
}
|
|
411
484
|
return value * Math.pow(10, scaleFactor);
|
|
412
485
|
}
|
|
413
486
|
/**
|
|
@@ -474,7 +547,7 @@ class SunspecModbusClient {
|
|
|
474
547
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
475
548
|
// Only calculate if value is not unimplemented
|
|
476
549
|
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'uint32')
|
|
477
|
-
?
|
|
550
|
+
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF) // Regular number with scale factor
|
|
478
551
|
: undefined,
|
|
479
552
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
480
553
|
// Timestamp - Offset 18-19 (32-bit)
|
|
@@ -536,13 +609,58 @@ class SunspecModbusClient {
|
|
|
536
609
|
return null;
|
|
537
610
|
}
|
|
538
611
|
const baseAddr = model.address + 2;
|
|
612
|
+
console.log(`Reading Meter Data from Model ${model.id} at base address: ${baseAddr}`);
|
|
539
613
|
try {
|
|
540
|
-
//
|
|
541
|
-
//
|
|
614
|
+
// Model 201 (single phase meter) - Correct register offsets from Sunspec
|
|
615
|
+
// All offsets are from data start (after ID and Length)
|
|
616
|
+
console.log(`Meter is Model 201 (Single Phase Meter)`);
|
|
617
|
+
// Model 201 register offsets according to Sunspec specification:
|
|
618
|
+
const powerOffset = 18; // W - Total Real Power (int16) at offset 18
|
|
619
|
+
const powerSFOffset = 21; // W_SF - Power scale factor at offset 21
|
|
620
|
+
const freqOffset = 32; // Hz - Frequency (uint16) at offset 32
|
|
621
|
+
const freqSFOffset = 35; // Hz_SF - Frequency scale factor at offset 35
|
|
622
|
+
const exportOffset = 38; // TotWhExp - Total Wh Exported (acc32) at offset 38-39
|
|
623
|
+
const importOffset = 46; // TotWhImp - Total Wh Imported (acc32) at offset 46-47
|
|
624
|
+
const energySFOffset = 4; // TotWh_SF - Total Energy scale factor at offset 4
|
|
625
|
+
// Read scale factors
|
|
626
|
+
const powerSF = await this.readRegisterValue(baseAddr + powerSFOffset, 1, 'int16');
|
|
627
|
+
const freqSF = await this.readRegisterValue(baseAddr + freqSFOffset, 1, 'int16');
|
|
628
|
+
const energySF = await this.readRegisterValue(baseAddr + energySFOffset, 1, 'int16');
|
|
629
|
+
console.log(`Meter Scale Factors: Power_SF=${powerSF}, Freq_SF=${freqSF}, Energy_SF=${energySF}`);
|
|
630
|
+
// Read raw values
|
|
631
|
+
const powerAddr = baseAddr + powerOffset;
|
|
632
|
+
const powerRaw = await this.readRegisterValue(powerAddr, 1, 'int16');
|
|
633
|
+
console.log(`Meter Total Power: address=${powerAddr}, raw=0x${powerRaw.toString(16).toUpperCase()} (${powerRaw}), SF=${powerSF}`);
|
|
634
|
+
const freqAddr = baseAddr + freqOffset;
|
|
635
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
636
|
+
console.log(`Meter Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw}), SF=${freqSF}`);
|
|
637
|
+
const exportAddr = baseAddr + exportOffset;
|
|
638
|
+
const exportRaw = await this.readRegisterValue(exportAddr, 2, 'acc32');
|
|
639
|
+
console.log(`Meter Export Energy: address=${exportAddr}, raw=0x${exportRaw.toString(16).toUpperCase()} (${exportRaw}), SF=${energySF}`);
|
|
640
|
+
const importAddr = baseAddr + importOffset;
|
|
641
|
+
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
642
|
+
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
643
|
+
// Calculate final values with scale factors
|
|
644
|
+
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16');
|
|
645
|
+
const frequency = this.applyScaleFactor(freqRaw, freqSF);
|
|
646
|
+
const exportedEnergy = !this.isUnimplementedValue(exportRaw, 'uint32')
|
|
647
|
+
? exportRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
648
|
+
: undefined;
|
|
649
|
+
const importedEnergy = !this.isUnimplementedValue(importRaw, 'uint32')
|
|
650
|
+
? importRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
651
|
+
: undefined;
|
|
652
|
+
console.log('Meter Final Values:', {
|
|
653
|
+
totalPower,
|
|
654
|
+
frequency,
|
|
655
|
+
exportedEnergy,
|
|
656
|
+
importedEnergy
|
|
657
|
+
});
|
|
542
658
|
return {
|
|
543
659
|
blockNumber: model.id,
|
|
544
|
-
totalPower
|
|
545
|
-
frequency
|
|
660
|
+
totalPower,
|
|
661
|
+
frequency,
|
|
662
|
+
exportedEnergy,
|
|
663
|
+
importedEnergy
|
|
546
664
|
};
|
|
547
665
|
}
|
|
548
666
|
catch (error) {
|
package/dist/cjs/version.cjs
CHANGED
package/dist/cjs/version.d.cts
CHANGED
package/dist/sunspec-devices.js
CHANGED
|
@@ -99,6 +99,15 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
99
99
|
const inverterData = await this.sunspecClient.readInverterData();
|
|
100
100
|
const mpptDataList = await this.sunspecClient.readAllMPPTData();
|
|
101
101
|
if (inverterData) {
|
|
102
|
+
// Log what we're sending
|
|
103
|
+
console.log('Preparing Inverter Message:', {
|
|
104
|
+
pvPowerW: inverterData.dcPower,
|
|
105
|
+
acPower: inverterData.acPower,
|
|
106
|
+
voltageL1: inverterData.voltageAN,
|
|
107
|
+
voltageL2: inverterData.voltageBN,
|
|
108
|
+
voltageL3: inverterData.voltageCN,
|
|
109
|
+
stringsCount: mpptDataList.length
|
|
110
|
+
});
|
|
102
111
|
const inverterMessage = {
|
|
103
112
|
id: randomUUID(),
|
|
104
113
|
message: EnyoDataBusMessageEnum.InverterValuesUpdateV1,
|
|
@@ -113,8 +122,8 @@ export class SunspecInverter extends BaseSunspecDevice {
|
|
|
113
122
|
activePowerLimitationW: inverterData.acPower || 0,
|
|
114
123
|
state: this.mapOperatingState(inverterData.operatingState),
|
|
115
124
|
voltageL1: inverterData.voltageAN || 0,
|
|
116
|
-
voltageL2: inverterData.voltageBN ?? undefined, // Use
|
|
117
|
-
voltageL3: inverterData.voltageCN ?? undefined, // Use
|
|
125
|
+
voltageL2: inverterData.voltageBN ?? undefined, // Use undefined if unimplemented phase
|
|
126
|
+
voltageL3: inverterData.voltageCN ?? undefined, // Use undefined if unimplemented phase
|
|
118
127
|
strings: this.mapMPPTToStrings(mpptDataList)
|
|
119
128
|
}
|
|
120
129
|
};
|
|
@@ -79,7 +79,7 @@ export interface SunspecInverterData extends SunspecBlock {
|
|
|
79
79
|
apparentPower?: number;
|
|
80
80
|
reactivePower?: number;
|
|
81
81
|
powerFactor?: number;
|
|
82
|
-
acEnergy?:
|
|
82
|
+
acEnergy?: number;
|
|
83
83
|
dcCurrent?: number;
|
|
84
84
|
dcVoltage?: number;
|
|
85
85
|
dcPower?: number;
|
|
@@ -109,7 +109,7 @@ export interface SunspecMPPTData extends SunspecBlock {
|
|
|
109
109
|
dcVoltageSF?: number;
|
|
110
110
|
dcPower?: number;
|
|
111
111
|
dcPowerSF?: number;
|
|
112
|
-
dcEnergy?:
|
|
112
|
+
dcEnergy?: number;
|
|
113
113
|
dcEnergySF?: number;
|
|
114
114
|
timestamp?: number;
|
|
115
115
|
temperature?: number;
|
|
@@ -141,9 +141,9 @@ export interface SunspecMeterData extends SunspecBlock {
|
|
|
141
141
|
phaseAPower?: number;
|
|
142
142
|
phaseBPower?: number;
|
|
143
143
|
phaseCPower?: number;
|
|
144
|
-
totalEnergy?:
|
|
145
|
-
exportedEnergy?:
|
|
146
|
-
importedEnergy?:
|
|
144
|
+
totalEnergy?: number;
|
|
145
|
+
exportedEnergy?: number;
|
|
146
|
+
importedEnergy?: number;
|
|
147
147
|
voltage?: number;
|
|
148
148
|
current?: number;
|
|
149
149
|
frequency?: number;
|
|
@@ -276,11 +276,14 @@ export class SunspecModbusClient {
|
|
|
276
276
|
console.error('No inverter model found');
|
|
277
277
|
return null;
|
|
278
278
|
}
|
|
279
|
+
console.warn('IMPORTANT: Working with single-phase inverter, but 3-phase is expected!');
|
|
279
280
|
return this.readSinglePhaseInverterData(singlePhaseModel);
|
|
280
281
|
}
|
|
282
|
+
console.log(`Found 3-phase inverter model 103 at address ${model.address} with length ${model.length}`);
|
|
281
283
|
const baseAddr = model.address + 2; // Skip ID and Length
|
|
282
284
|
try {
|
|
283
285
|
// Read all scale factors first using fault-tolerant reader
|
|
286
|
+
console.log(`Reading Inverter Data from Model 103 at base address: ${baseAddr}`);
|
|
284
287
|
const scaleFactors = await this.readInverterScaleFactors(baseAddr);
|
|
285
288
|
// Read values using fault-tolerant reader with proper data types
|
|
286
289
|
// Read raw voltage values to check for unimplemented phases
|
|
@@ -297,7 +300,12 @@ export class SunspecModbusClient {
|
|
|
297
300
|
blockAddress: model.address,
|
|
298
301
|
blockLength: model.length,
|
|
299
302
|
// AC Current values - Offsets 2-5
|
|
300
|
-
acCurrent: this.applyScaleFactor(await
|
|
303
|
+
acCurrent: this.applyScaleFactor(await (async () => {
|
|
304
|
+
const addr = baseAddr + 2;
|
|
305
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
306
|
+
console.log(`AC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
307
|
+
return raw;
|
|
308
|
+
})(), scaleFactors.A_SF),
|
|
301
309
|
phaseACurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 3, 1, 'uint16'), scaleFactors.A_SF),
|
|
302
310
|
phaseBCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 4, 1, 'uint16'), scaleFactors.A_SF),
|
|
303
311
|
phaseCCurrent: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 5, 1, 'uint16'), scaleFactors.A_SF),
|
|
@@ -310,16 +318,36 @@ export class SunspecModbusClient {
|
|
|
310
318
|
voltageBN: this.applyScaleFactor(voltageBNRaw, scaleFactors.V_SF),
|
|
311
319
|
voltageCN: this.applyScaleFactor(voltageCNRaw, scaleFactors.V_SF),
|
|
312
320
|
// Power values - Offsets 14, 18, 20, 22
|
|
313
|
-
acPower: this.applyScaleFactor(await
|
|
321
|
+
acPower: this.applyScaleFactor(await (async () => {
|
|
322
|
+
const addr = baseAddr + 14;
|
|
323
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
324
|
+
console.log(`AC Power (W): address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.W_SF}`);
|
|
325
|
+
return raw;
|
|
326
|
+
})(), scaleFactors.W_SF, 'int16'),
|
|
314
327
|
apparentPower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 18, 1, 'uint16'), scaleFactors.VA_SF),
|
|
315
328
|
reactivePower: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 20, 1, 'int16'), scaleFactors.VAr_SF),
|
|
316
329
|
powerFactor: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 22, 1, 'int16'), scaleFactors.PF_SF),
|
|
317
330
|
// Frequency - Offset 16
|
|
318
331
|
frequency: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 16, 1, 'uint16'), scaleFactors.Hz_SF),
|
|
319
332
|
// DC values - Offsets 27, 28, 30
|
|
320
|
-
dcCurrent: this.applyScaleFactor(await
|
|
321
|
-
|
|
322
|
-
|
|
333
|
+
dcCurrent: this.applyScaleFactor(await (async () => {
|
|
334
|
+
const addr = baseAddr + 27;
|
|
335
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
336
|
+
console.log(`DC Current: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
337
|
+
return raw;
|
|
338
|
+
})(), scaleFactors.DCA_SF),
|
|
339
|
+
dcVoltage: this.applyScaleFactor(await (async () => {
|
|
340
|
+
const addr = baseAddr + 28;
|
|
341
|
+
const raw = await this.readRegisterValue(addr, 1, 'uint16');
|
|
342
|
+
console.log(`DC Voltage: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw})`);
|
|
343
|
+
return raw;
|
|
344
|
+
})(), scaleFactors.DCV_SF),
|
|
345
|
+
dcPower: this.applyScaleFactor(await (async () => {
|
|
346
|
+
const addr = baseAddr + 30;
|
|
347
|
+
const raw = await this.readRegisterValue(addr, 1, 'int16');
|
|
348
|
+
console.log(`DC Power: address=${addr}, raw=0x${raw.toString(16).toUpperCase()} (${raw}), SF=${scaleFactors.DCW_SF}`);
|
|
349
|
+
return raw;
|
|
350
|
+
})(), scaleFactors.DCW_SF, 'int16'),
|
|
323
351
|
// Temperature values - Offsets 32, 34, 35, 36
|
|
324
352
|
cabinetTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 32, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
325
353
|
heatSinkTemperature: this.applyScaleFactor(await this.readRegisterValue(baseAddr + 34, 1, 'int16'), scaleFactors.Tmp_SF),
|
|
@@ -337,8 +365,20 @@ export class SunspecModbusClient {
|
|
|
337
365
|
vendorEvents4: await this.readRegisterValue(baseAddr + 50, 2, 'uint32')
|
|
338
366
|
};
|
|
339
367
|
// Read AC Energy (32-bit accumulator) - Offset 24-25
|
|
340
|
-
const
|
|
341
|
-
|
|
368
|
+
const acEnergyAddr = baseAddr + 24;
|
|
369
|
+
const acEnergy = await this.readRegisterValue(acEnergyAddr, 2, 'acc32');
|
|
370
|
+
console.log(`AC Energy: address=${acEnergyAddr}, raw=0x${acEnergy.toString(16).toUpperCase()} (${acEnergy}), SF=${scaleFactors.WH_SF}`);
|
|
371
|
+
data.acEnergy = !this.isUnimplementedValue(acEnergy, 'uint32')
|
|
372
|
+
? acEnergy * Math.pow(10, scaleFactors.WH_SF) // Regular number with scale factor
|
|
373
|
+
: undefined;
|
|
374
|
+
// Log final calculated values
|
|
375
|
+
console.log('Inverter Final Values:', {
|
|
376
|
+
acPower: data.acPower,
|
|
377
|
+
voltageAN: data.voltageAN,
|
|
378
|
+
voltageBN: data.voltageBN,
|
|
379
|
+
voltageCN: data.voltageCN,
|
|
380
|
+
acCurrent: data.acCurrent
|
|
381
|
+
});
|
|
342
382
|
return data;
|
|
343
383
|
}
|
|
344
384
|
catch (error) {
|
|
@@ -354,15 +394,53 @@ export class SunspecModbusClient {
|
|
|
354
394
|
// Implementation would be similar but simplified
|
|
355
395
|
const baseAddr = model.address + 2;
|
|
356
396
|
try {
|
|
357
|
-
|
|
397
|
+
console.log(`Reading Single-Phase Inverter Data from Model 101 at base address: ${baseAddr}`);
|
|
398
|
+
// Read scale factors for single phase model
|
|
399
|
+
const scaleFactors = {
|
|
400
|
+
A_SF: await this.readRegisterValue(baseAddr + 6, 1, 'int16'),
|
|
401
|
+
V_SF: await this.readRegisterValue(baseAddr + 13, 1, 'int16'),
|
|
402
|
+
W_SF: await this.readRegisterValue(baseAddr + 10, 1, 'int16'),
|
|
403
|
+
Hz_SF: await this.readRegisterValue(baseAddr + 12, 1, 'int16'),
|
|
404
|
+
DCA_SF: await this.readRegisterValue(baseAddr + 18, 1, 'int16'),
|
|
405
|
+
DCV_SF: await this.readRegisterValue(baseAddr + 19, 1, 'int16'),
|
|
406
|
+
DCW_SF: await this.readRegisterValue(baseAddr + 21, 1, 'int16')
|
|
407
|
+
};
|
|
408
|
+
console.log('Single-Phase Inverter Scale Factors:', JSON.stringify(scaleFactors, null, 2));
|
|
409
|
+
// Read raw values with logging
|
|
410
|
+
const acCurrentAddr = baseAddr + 2;
|
|
411
|
+
const acCurrentRaw = await this.readRegisterValue(acCurrentAddr, 1, 'uint16');
|
|
412
|
+
console.log(`AC Current: address=${acCurrentAddr}, raw=0x${acCurrentRaw.toString(16).toUpperCase()} (${acCurrentRaw})`);
|
|
413
|
+
const voltageAddr = baseAddr + 7;
|
|
414
|
+
const voltageRaw = await this.readRegisterValue(voltageAddr, 1, 'uint16');
|
|
415
|
+
console.log(`AC Voltage: address=${voltageAddr}, raw=0x${voltageRaw.toString(16).toUpperCase()} (${voltageRaw})`);
|
|
416
|
+
const acPowerAddr = baseAddr + 9;
|
|
417
|
+
const acPowerRaw = await this.readRegisterValue(acPowerAddr, 1, 'int16');
|
|
418
|
+
console.log(`AC Power: address=${acPowerAddr}, raw=0x${acPowerRaw.toString(16).toUpperCase()} (${acPowerRaw}), SF=${scaleFactors.W_SF}`);
|
|
419
|
+
const freqAddr = baseAddr + 11;
|
|
420
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
421
|
+
console.log(`Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw})`);
|
|
422
|
+
const dcCurrentAddr = baseAddr + 14;
|
|
423
|
+
const dcCurrentRaw = await this.readRegisterValue(dcCurrentAddr, 1, 'uint16');
|
|
424
|
+
console.log(`DC Current: address=${dcCurrentAddr}, raw=0x${dcCurrentRaw.toString(16).toUpperCase()} (${dcCurrentRaw})`);
|
|
425
|
+
const dcVoltageAddr = baseAddr + 15;
|
|
426
|
+
const dcVoltageRaw = await this.readRegisterValue(dcVoltageAddr, 1, 'uint16');
|
|
427
|
+
console.log(`DC Voltage: address=${dcVoltageAddr}, raw=0x${dcVoltageRaw.toString(16).toUpperCase()} (${dcVoltageRaw})`);
|
|
428
|
+
const dcPowerAddr = baseAddr + 20;
|
|
429
|
+
const dcPowerRaw = await this.readRegisterValue(dcPowerAddr, 1, 'int16');
|
|
430
|
+
console.log(`DC Power: address=${dcPowerAddr}, raw=0x${dcPowerRaw.toString(16).toUpperCase()} (${dcPowerRaw}), SF=${scaleFactors.DCW_SF}`);
|
|
431
|
+
const stateAddr = baseAddr + 24;
|
|
432
|
+
const stateRaw = await this.readRegisterValue(stateAddr, 1, 'uint16');
|
|
433
|
+
console.log(`Operating State: address=${stateAddr}, raw=0x${stateRaw.toString(16).toUpperCase()} (${stateRaw})`);
|
|
358
434
|
return {
|
|
359
435
|
blockNumber: 101,
|
|
360
|
-
voltageAN:
|
|
361
|
-
acCurrent:
|
|
362
|
-
acPower:
|
|
363
|
-
frequency:
|
|
364
|
-
|
|
365
|
-
|
|
436
|
+
voltageAN: this.applyScaleFactor(voltageRaw, scaleFactors.V_SF),
|
|
437
|
+
acCurrent: this.applyScaleFactor(acCurrentRaw, scaleFactors.A_SF),
|
|
438
|
+
acPower: this.applyScaleFactor(acPowerRaw, scaleFactors.W_SF, 'int16'),
|
|
439
|
+
frequency: this.applyScaleFactor(freqRaw, scaleFactors.Hz_SF),
|
|
440
|
+
dcCurrent: this.applyScaleFactor(dcCurrentRaw, scaleFactors.DCA_SF),
|
|
441
|
+
dcVoltage: this.applyScaleFactor(dcVoltageRaw, scaleFactors.DCV_SF),
|
|
442
|
+
dcPower: this.applyScaleFactor(dcPowerRaw, scaleFactors.DCW_SF, 'int16'),
|
|
443
|
+
operatingState: stateRaw
|
|
366
444
|
};
|
|
367
445
|
}
|
|
368
446
|
catch (error) {
|
|
@@ -400,11 +478,6 @@ export class SunspecModbusClient {
|
|
|
400
478
|
if (this.isUnimplementedValue(value, dataType)) {
|
|
401
479
|
return undefined;
|
|
402
480
|
}
|
|
403
|
-
// Validate scale factor is within reasonable range (-10 to +10)
|
|
404
|
-
if (Math.abs(scaleFactor) > 10) {
|
|
405
|
-
console.warn(`Scale factor ${scaleFactor} is outside reasonable range, clamping to ±10`);
|
|
406
|
-
scaleFactor = Math.max(-10, Math.min(10, scaleFactor));
|
|
407
|
-
}
|
|
408
481
|
return value * Math.pow(10, scaleFactor);
|
|
409
482
|
}
|
|
410
483
|
/**
|
|
@@ -471,7 +544,7 @@ export class SunspecModbusClient {
|
|
|
471
544
|
// DC Energy - Offset 15-16 (32-bit accumulator)
|
|
472
545
|
// Only calculate if value is not unimplemented
|
|
473
546
|
dcEnergy: !this.isUnimplementedValue(dcEnergyRaw, 'uint32')
|
|
474
|
-
?
|
|
547
|
+
? dcEnergyRaw * Math.pow(10, scaleFactors.DCWH_SF) // Regular number with scale factor
|
|
475
548
|
: undefined,
|
|
476
549
|
dcEnergySF: scaleFactors.DCWH_SF,
|
|
477
550
|
// Timestamp - Offset 18-19 (32-bit)
|
|
@@ -533,13 +606,58 @@ export class SunspecModbusClient {
|
|
|
533
606
|
return null;
|
|
534
607
|
}
|
|
535
608
|
const baseAddr = model.address + 2;
|
|
609
|
+
console.log(`Reading Meter Data from Model ${model.id} at base address: ${baseAddr}`);
|
|
536
610
|
try {
|
|
537
|
-
//
|
|
538
|
-
//
|
|
611
|
+
// Model 201 (single phase meter) - Correct register offsets from Sunspec
|
|
612
|
+
// All offsets are from data start (after ID and Length)
|
|
613
|
+
console.log(`Meter is Model 201 (Single Phase Meter)`);
|
|
614
|
+
// Model 201 register offsets according to Sunspec specification:
|
|
615
|
+
const powerOffset = 18; // W - Total Real Power (int16) at offset 18
|
|
616
|
+
const powerSFOffset = 21; // W_SF - Power scale factor at offset 21
|
|
617
|
+
const freqOffset = 32; // Hz - Frequency (uint16) at offset 32
|
|
618
|
+
const freqSFOffset = 35; // Hz_SF - Frequency scale factor at offset 35
|
|
619
|
+
const exportOffset = 38; // TotWhExp - Total Wh Exported (acc32) at offset 38-39
|
|
620
|
+
const importOffset = 46; // TotWhImp - Total Wh Imported (acc32) at offset 46-47
|
|
621
|
+
const energySFOffset = 4; // TotWh_SF - Total Energy scale factor at offset 4
|
|
622
|
+
// Read scale factors
|
|
623
|
+
const powerSF = await this.readRegisterValue(baseAddr + powerSFOffset, 1, 'int16');
|
|
624
|
+
const freqSF = await this.readRegisterValue(baseAddr + freqSFOffset, 1, 'int16');
|
|
625
|
+
const energySF = await this.readRegisterValue(baseAddr + energySFOffset, 1, 'int16');
|
|
626
|
+
console.log(`Meter Scale Factors: Power_SF=${powerSF}, Freq_SF=${freqSF}, Energy_SF=${energySF}`);
|
|
627
|
+
// Read raw values
|
|
628
|
+
const powerAddr = baseAddr + powerOffset;
|
|
629
|
+
const powerRaw = await this.readRegisterValue(powerAddr, 1, 'int16');
|
|
630
|
+
console.log(`Meter Total Power: address=${powerAddr}, raw=0x${powerRaw.toString(16).toUpperCase()} (${powerRaw}), SF=${powerSF}`);
|
|
631
|
+
const freqAddr = baseAddr + freqOffset;
|
|
632
|
+
const freqRaw = await this.readRegisterValue(freqAddr, 1, 'uint16');
|
|
633
|
+
console.log(`Meter Frequency: address=${freqAddr}, raw=0x${freqRaw.toString(16).toUpperCase()} (${freqRaw}), SF=${freqSF}`);
|
|
634
|
+
const exportAddr = baseAddr + exportOffset;
|
|
635
|
+
const exportRaw = await this.readRegisterValue(exportAddr, 2, 'acc32');
|
|
636
|
+
console.log(`Meter Export Energy: address=${exportAddr}, raw=0x${exportRaw.toString(16).toUpperCase()} (${exportRaw}), SF=${energySF}`);
|
|
637
|
+
const importAddr = baseAddr + importOffset;
|
|
638
|
+
const importRaw = await this.readRegisterValue(importAddr, 2, 'acc32');
|
|
639
|
+
console.log(`Meter Import Energy: address=${importAddr}, raw=0x${importRaw.toString(16).toUpperCase()} (${importRaw}), SF=${energySF}`);
|
|
640
|
+
// Calculate final values with scale factors
|
|
641
|
+
const totalPower = this.applyScaleFactor(powerRaw, powerSF, 'int16');
|
|
642
|
+
const frequency = this.applyScaleFactor(freqRaw, freqSF);
|
|
643
|
+
const exportedEnergy = !this.isUnimplementedValue(exportRaw, 'uint32')
|
|
644
|
+
? exportRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
645
|
+
: undefined;
|
|
646
|
+
const importedEnergy = !this.isUnimplementedValue(importRaw, 'uint32')
|
|
647
|
+
? importRaw * Math.pow(10, energySF) // Regular number, not BigInt
|
|
648
|
+
: undefined;
|
|
649
|
+
console.log('Meter Final Values:', {
|
|
650
|
+
totalPower,
|
|
651
|
+
frequency,
|
|
652
|
+
exportedEnergy,
|
|
653
|
+
importedEnergy
|
|
654
|
+
});
|
|
539
655
|
return {
|
|
540
656
|
blockNumber: model.id,
|
|
541
|
-
totalPower
|
|
542
|
-
frequency
|
|
657
|
+
totalPower,
|
|
658
|
+
frequency,
|
|
659
|
+
exportedEnergy,
|
|
660
|
+
importedEnergy
|
|
543
661
|
};
|
|
544
662
|
}
|
|
545
663
|
catch (error) {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED