@enyo-energy/sunspec-sdk 0.0.69 → 0.0.71
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/calibration-snapshot-service.d.ts +67 -0
- package/dist/calibration-snapshot-service.js +160 -0
- package/dist/cjs/calibration-snapshot-service.cjs +164 -0
- package/dist/cjs/calibration-snapshot-service.d.cts +67 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/sunspec-devices.cjs +252 -4
- package/dist/cjs/sunspec-devices.d.cts +26 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/cjs/version.d.cts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sunspec-devices.d.ts +26 -0
- package/dist/sunspec-devices.js +252 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EnergyAppStorage } from "@enyo-energy/energy-app-sdk/dist/packages/energy-app-storage.js";
|
|
2
|
+
import type { SunspecBatteryControls, SunspecInverterControls } from "./sunspec-interfaces.js";
|
|
3
|
+
export type CalibrationDeviceType = "inverter" | "battery";
|
|
4
|
+
/**
|
|
5
|
+
* Persisted snapshot of a device's writable control registers, taken at the start of a
|
|
6
|
+
* calibration. Used to roll the device back when calibration ends.
|
|
7
|
+
*
|
|
8
|
+
* `modifiedFields` records which control-block field names were touched by *other* write
|
|
9
|
+
* commands (SetInverterFeedInLimitV1, StartStorageGridChargeV1, etc.) while the calibration
|
|
10
|
+
* was active — on stop, only those fields are written back, leaving fields the calibration
|
|
11
|
+
* itself didn't disturb alone.
|
|
12
|
+
*/
|
|
13
|
+
export interface CalibrationSnapshot {
|
|
14
|
+
applianceId: string;
|
|
15
|
+
deviceType: CalibrationDeviceType;
|
|
16
|
+
unitId: number;
|
|
17
|
+
startedAtIso: string;
|
|
18
|
+
inverterControls?: SunspecInverterControls;
|
|
19
|
+
batteryControls?: SunspecBatteryControls;
|
|
20
|
+
modifiedFields: string[];
|
|
21
|
+
}
|
|
22
|
+
/** Calibration is capped at 5 minutes of wall time, including across restarts. */
|
|
23
|
+
export declare const CALIBRATION_AUTO_STOP_MS: number;
|
|
24
|
+
export type CalibrationRestoreReason = "stop" | "auto";
|
|
25
|
+
export type CalibrationRestoreCallback = (snapshot: CalibrationSnapshot, reason: CalibrationRestoreReason) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Per-appliance service that snapshots a SunSpec device's writable control registers when a
|
|
28
|
+
* calibration starts, tracks which registers other commands mutate while it is active, and
|
|
29
|
+
* restores those registers when calibration stops (either explicitly or via the 5-minute
|
|
30
|
+
* auto-stop timer).
|
|
31
|
+
*
|
|
32
|
+
* Snapshots are persisted via `EnergyAppStorage` so a crash/restart mid-calibration does not
|
|
33
|
+
* leave the device in a calibration state without a rollback. On `initialize()`, any stored
|
|
34
|
+
* snapshot is reloaded; the auto-stop deadline is computed from `startedAtIso` so total
|
|
35
|
+
* calibration time remains capped at `autoStopMs` across restarts.
|
|
36
|
+
*/
|
|
37
|
+
export declare class CalibrationSnapshotService {
|
|
38
|
+
private readonly storage;
|
|
39
|
+
private readonly applianceId;
|
|
40
|
+
private readonly onRestore;
|
|
41
|
+
private readonly autoStopMs;
|
|
42
|
+
private snapshot?;
|
|
43
|
+
private autoStopTimer?;
|
|
44
|
+
constructor(storage: EnergyAppStorage, applianceId: string, onRestore: CalibrationRestoreCallback, autoStopMs?: number);
|
|
45
|
+
private storageKey;
|
|
46
|
+
initialize(): Promise<void>;
|
|
47
|
+
isCalibrating(): boolean;
|
|
48
|
+
getSnapshot(): CalibrationSnapshot | undefined;
|
|
49
|
+
startCalibration(input: {
|
|
50
|
+
deviceType: CalibrationDeviceType;
|
|
51
|
+
unitId: number;
|
|
52
|
+
inverterControls?: SunspecInverterControls;
|
|
53
|
+
batteryControls?: SunspecBatteryControls;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
recordModification(fields: string[]): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Clear the timer, remove the snapshot from memory + storage, and return the snapshot so
|
|
58
|
+
* the caller can restore the modified registers. Returns undefined if no calibration was
|
|
59
|
+
* active.
|
|
60
|
+
*/
|
|
61
|
+
stopCalibration(): Promise<CalibrationSnapshot | undefined>;
|
|
62
|
+
private remainingMs;
|
|
63
|
+
private scheduleAutoStop;
|
|
64
|
+
private clearAutoStopTimer;
|
|
65
|
+
private fireAutoStop;
|
|
66
|
+
private persist;
|
|
67
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** Calibration is capped at 5 minutes of wall time, including across restarts. */
|
|
2
|
+
export const CALIBRATION_AUTO_STOP_MS = 5 * 60 * 1000;
|
|
3
|
+
/**
|
|
4
|
+
* Per-appliance service that snapshots a SunSpec device's writable control registers when a
|
|
5
|
+
* calibration starts, tracks which registers other commands mutate while it is active, and
|
|
6
|
+
* restores those registers when calibration stops (either explicitly or via the 5-minute
|
|
7
|
+
* auto-stop timer).
|
|
8
|
+
*
|
|
9
|
+
* Snapshots are persisted via `EnergyAppStorage` so a crash/restart mid-calibration does not
|
|
10
|
+
* leave the device in a calibration state without a rollback. On `initialize()`, any stored
|
|
11
|
+
* snapshot is reloaded; the auto-stop deadline is computed from `startedAtIso` so total
|
|
12
|
+
* calibration time remains capped at `autoStopMs` across restarts.
|
|
13
|
+
*/
|
|
14
|
+
export class CalibrationSnapshotService {
|
|
15
|
+
storage;
|
|
16
|
+
applianceId;
|
|
17
|
+
onRestore;
|
|
18
|
+
autoStopMs;
|
|
19
|
+
snapshot;
|
|
20
|
+
autoStopTimer;
|
|
21
|
+
constructor(storage, applianceId, onRestore, autoStopMs = CALIBRATION_AUTO_STOP_MS) {
|
|
22
|
+
this.storage = storage;
|
|
23
|
+
this.applianceId = applianceId;
|
|
24
|
+
this.onRestore = onRestore;
|
|
25
|
+
this.autoStopMs = autoStopMs;
|
|
26
|
+
}
|
|
27
|
+
storageKey() {
|
|
28
|
+
return `sunspec-calibration-snapshot-${this.applianceId}`;
|
|
29
|
+
}
|
|
30
|
+
async initialize() {
|
|
31
|
+
try {
|
|
32
|
+
const loaded = await this.storage.load(this.storageKey());
|
|
33
|
+
if (!loaded) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.snapshot = loaded;
|
|
37
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: loaded persisted snapshot (started ${loaded.startedAtIso}, modifiedFields=[${loaded.modifiedFields.join(', ')}])`);
|
|
38
|
+
const remainingMs = this.remainingMs(loaded);
|
|
39
|
+
if (remainingMs <= 0) {
|
|
40
|
+
console.warn(`CalibrationSnapshotService ${this.applianceId}: snapshot exceeded ${this.autoStopMs}ms deadline — auto-restoring immediately`);
|
|
41
|
+
await this.fireAutoStop();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.scheduleAutoStop(remainingMs);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to load persisted snapshot: ${error}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
isCalibrating() {
|
|
52
|
+
return this.snapshot !== undefined;
|
|
53
|
+
}
|
|
54
|
+
getSnapshot() {
|
|
55
|
+
return this.snapshot;
|
|
56
|
+
}
|
|
57
|
+
async startCalibration(input) {
|
|
58
|
+
this.clearAutoStopTimer();
|
|
59
|
+
this.snapshot = {
|
|
60
|
+
applianceId: this.applianceId,
|
|
61
|
+
deviceType: input.deviceType,
|
|
62
|
+
unitId: input.unitId,
|
|
63
|
+
startedAtIso: new Date().toISOString(),
|
|
64
|
+
inverterControls: input.inverterControls,
|
|
65
|
+
batteryControls: input.batteryControls,
|
|
66
|
+
modifiedFields: [],
|
|
67
|
+
};
|
|
68
|
+
await this.persist();
|
|
69
|
+
this.scheduleAutoStop(this.autoStopMs);
|
|
70
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: calibration started for ${input.deviceType} (auto-stop in ${this.autoStopMs}ms)`);
|
|
71
|
+
}
|
|
72
|
+
async recordModification(fields) {
|
|
73
|
+
if (!this.snapshot) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
let added = false;
|
|
77
|
+
for (const field of fields) {
|
|
78
|
+
if (!this.snapshot.modifiedFields.includes(field)) {
|
|
79
|
+
this.snapshot.modifiedFields.push(field);
|
|
80
|
+
added = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (added) {
|
|
84
|
+
await this.persist();
|
|
85
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: tracked modified fields [${this.snapshot.modifiedFields.join(', ')}]`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Clear the timer, remove the snapshot from memory + storage, and return the snapshot so
|
|
90
|
+
* the caller can restore the modified registers. Returns undefined if no calibration was
|
|
91
|
+
* active.
|
|
92
|
+
*/
|
|
93
|
+
async stopCalibration() {
|
|
94
|
+
this.clearAutoStopTimer();
|
|
95
|
+
const snap = this.snapshot;
|
|
96
|
+
this.snapshot = undefined;
|
|
97
|
+
if (snap) {
|
|
98
|
+
try {
|
|
99
|
+
await this.storage.remove(this.storageKey());
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to remove persisted snapshot: ${error}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return snap;
|
|
106
|
+
}
|
|
107
|
+
remainingMs(snap) {
|
|
108
|
+
const startedAtMs = Date.parse(snap.startedAtIso);
|
|
109
|
+
if (Number.isNaN(startedAtMs)) {
|
|
110
|
+
return this.autoStopMs;
|
|
111
|
+
}
|
|
112
|
+
return startedAtMs + this.autoStopMs - Date.now();
|
|
113
|
+
}
|
|
114
|
+
scheduleAutoStop(delayMs) {
|
|
115
|
+
this.clearAutoStopTimer();
|
|
116
|
+
this.autoStopTimer = setTimeout(() => {
|
|
117
|
+
void this.fireAutoStop();
|
|
118
|
+
}, delayMs);
|
|
119
|
+
if (typeof this.autoStopTimer.unref === "function") {
|
|
120
|
+
this.autoStopTimer.unref();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
clearAutoStopTimer() {
|
|
124
|
+
if (this.autoStopTimer) {
|
|
125
|
+
clearTimeout(this.autoStopTimer);
|
|
126
|
+
this.autoStopTimer = undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async fireAutoStop() {
|
|
130
|
+
const snap = this.snapshot;
|
|
131
|
+
this.snapshot = undefined;
|
|
132
|
+
this.clearAutoStopTimer();
|
|
133
|
+
if (!snap) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
await this.storage.remove(this.storageKey());
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to remove persisted snapshot during auto-stop: ${error}`);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await this.onRestore(snap, "auto");
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: auto-stop restore failed: ${error}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async persist() {
|
|
150
|
+
if (!this.snapshot) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
await this.storage.save(this.storageKey(), this.snapshot);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to persist snapshot: ${error}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CalibrationSnapshotService = exports.CALIBRATION_AUTO_STOP_MS = void 0;
|
|
4
|
+
/** Calibration is capped at 5 minutes of wall time, including across restarts. */
|
|
5
|
+
exports.CALIBRATION_AUTO_STOP_MS = 5 * 60 * 1000;
|
|
6
|
+
/**
|
|
7
|
+
* Per-appliance service that snapshots a SunSpec device's writable control registers when a
|
|
8
|
+
* calibration starts, tracks which registers other commands mutate while it is active, and
|
|
9
|
+
* restores those registers when calibration stops (either explicitly or via the 5-minute
|
|
10
|
+
* auto-stop timer).
|
|
11
|
+
*
|
|
12
|
+
* Snapshots are persisted via `EnergyAppStorage` so a crash/restart mid-calibration does not
|
|
13
|
+
* leave the device in a calibration state without a rollback. On `initialize()`, any stored
|
|
14
|
+
* snapshot is reloaded; the auto-stop deadline is computed from `startedAtIso` so total
|
|
15
|
+
* calibration time remains capped at `autoStopMs` across restarts.
|
|
16
|
+
*/
|
|
17
|
+
class CalibrationSnapshotService {
|
|
18
|
+
storage;
|
|
19
|
+
applianceId;
|
|
20
|
+
onRestore;
|
|
21
|
+
autoStopMs;
|
|
22
|
+
snapshot;
|
|
23
|
+
autoStopTimer;
|
|
24
|
+
constructor(storage, applianceId, onRestore, autoStopMs = exports.CALIBRATION_AUTO_STOP_MS) {
|
|
25
|
+
this.storage = storage;
|
|
26
|
+
this.applianceId = applianceId;
|
|
27
|
+
this.onRestore = onRestore;
|
|
28
|
+
this.autoStopMs = autoStopMs;
|
|
29
|
+
}
|
|
30
|
+
storageKey() {
|
|
31
|
+
return `sunspec-calibration-snapshot-${this.applianceId}`;
|
|
32
|
+
}
|
|
33
|
+
async initialize() {
|
|
34
|
+
try {
|
|
35
|
+
const loaded = await this.storage.load(this.storageKey());
|
|
36
|
+
if (!loaded) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.snapshot = loaded;
|
|
40
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: loaded persisted snapshot (started ${loaded.startedAtIso}, modifiedFields=[${loaded.modifiedFields.join(', ')}])`);
|
|
41
|
+
const remainingMs = this.remainingMs(loaded);
|
|
42
|
+
if (remainingMs <= 0) {
|
|
43
|
+
console.warn(`CalibrationSnapshotService ${this.applianceId}: snapshot exceeded ${this.autoStopMs}ms deadline — auto-restoring immediately`);
|
|
44
|
+
await this.fireAutoStop();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.scheduleAutoStop(remainingMs);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to load persisted snapshot: ${error}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
isCalibrating() {
|
|
55
|
+
return this.snapshot !== undefined;
|
|
56
|
+
}
|
|
57
|
+
getSnapshot() {
|
|
58
|
+
return this.snapshot;
|
|
59
|
+
}
|
|
60
|
+
async startCalibration(input) {
|
|
61
|
+
this.clearAutoStopTimer();
|
|
62
|
+
this.snapshot = {
|
|
63
|
+
applianceId: this.applianceId,
|
|
64
|
+
deviceType: input.deviceType,
|
|
65
|
+
unitId: input.unitId,
|
|
66
|
+
startedAtIso: new Date().toISOString(),
|
|
67
|
+
inverterControls: input.inverterControls,
|
|
68
|
+
batteryControls: input.batteryControls,
|
|
69
|
+
modifiedFields: [],
|
|
70
|
+
};
|
|
71
|
+
await this.persist();
|
|
72
|
+
this.scheduleAutoStop(this.autoStopMs);
|
|
73
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: calibration started for ${input.deviceType} (auto-stop in ${this.autoStopMs}ms)`);
|
|
74
|
+
}
|
|
75
|
+
async recordModification(fields) {
|
|
76
|
+
if (!this.snapshot) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let added = false;
|
|
80
|
+
for (const field of fields) {
|
|
81
|
+
if (!this.snapshot.modifiedFields.includes(field)) {
|
|
82
|
+
this.snapshot.modifiedFields.push(field);
|
|
83
|
+
added = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (added) {
|
|
87
|
+
await this.persist();
|
|
88
|
+
console.log(`CalibrationSnapshotService ${this.applianceId}: tracked modified fields [${this.snapshot.modifiedFields.join(', ')}]`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Clear the timer, remove the snapshot from memory + storage, and return the snapshot so
|
|
93
|
+
* the caller can restore the modified registers. Returns undefined if no calibration was
|
|
94
|
+
* active.
|
|
95
|
+
*/
|
|
96
|
+
async stopCalibration() {
|
|
97
|
+
this.clearAutoStopTimer();
|
|
98
|
+
const snap = this.snapshot;
|
|
99
|
+
this.snapshot = undefined;
|
|
100
|
+
if (snap) {
|
|
101
|
+
try {
|
|
102
|
+
await this.storage.remove(this.storageKey());
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to remove persisted snapshot: ${error}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return snap;
|
|
109
|
+
}
|
|
110
|
+
remainingMs(snap) {
|
|
111
|
+
const startedAtMs = Date.parse(snap.startedAtIso);
|
|
112
|
+
if (Number.isNaN(startedAtMs)) {
|
|
113
|
+
return this.autoStopMs;
|
|
114
|
+
}
|
|
115
|
+
return startedAtMs + this.autoStopMs - Date.now();
|
|
116
|
+
}
|
|
117
|
+
scheduleAutoStop(delayMs) {
|
|
118
|
+
this.clearAutoStopTimer();
|
|
119
|
+
this.autoStopTimer = setTimeout(() => {
|
|
120
|
+
void this.fireAutoStop();
|
|
121
|
+
}, delayMs);
|
|
122
|
+
if (typeof this.autoStopTimer.unref === "function") {
|
|
123
|
+
this.autoStopTimer.unref();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
clearAutoStopTimer() {
|
|
127
|
+
if (this.autoStopTimer) {
|
|
128
|
+
clearTimeout(this.autoStopTimer);
|
|
129
|
+
this.autoStopTimer = undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async fireAutoStop() {
|
|
133
|
+
const snap = this.snapshot;
|
|
134
|
+
this.snapshot = undefined;
|
|
135
|
+
this.clearAutoStopTimer();
|
|
136
|
+
if (!snap) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
await this.storage.remove(this.storageKey());
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to remove persisted snapshot during auto-stop: ${error}`);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
await this.onRestore(snap, "auto");
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: auto-stop restore failed: ${error}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async persist() {
|
|
153
|
+
if (!this.snapshot) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await this.storage.save(this.storageKey(), this.snapshot);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error(`CalibrationSnapshotService ${this.applianceId}: failed to persist snapshot: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.CalibrationSnapshotService = CalibrationSnapshotService;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EnergyAppStorage } from "@enyo-energy/energy-app-sdk/dist/packages/energy-app-storage.js";
|
|
2
|
+
import type { SunspecBatteryControls, SunspecInverterControls } from "./sunspec-interfaces.cjs";
|
|
3
|
+
export type CalibrationDeviceType = "inverter" | "battery";
|
|
4
|
+
/**
|
|
5
|
+
* Persisted snapshot of a device's writable control registers, taken at the start of a
|
|
6
|
+
* calibration. Used to roll the device back when calibration ends.
|
|
7
|
+
*
|
|
8
|
+
* `modifiedFields` records which control-block field names were touched by *other* write
|
|
9
|
+
* commands (SetInverterFeedInLimitV1, StartStorageGridChargeV1, etc.) while the calibration
|
|
10
|
+
* was active — on stop, only those fields are written back, leaving fields the calibration
|
|
11
|
+
* itself didn't disturb alone.
|
|
12
|
+
*/
|
|
13
|
+
export interface CalibrationSnapshot {
|
|
14
|
+
applianceId: string;
|
|
15
|
+
deviceType: CalibrationDeviceType;
|
|
16
|
+
unitId: number;
|
|
17
|
+
startedAtIso: string;
|
|
18
|
+
inverterControls?: SunspecInverterControls;
|
|
19
|
+
batteryControls?: SunspecBatteryControls;
|
|
20
|
+
modifiedFields: string[];
|
|
21
|
+
}
|
|
22
|
+
/** Calibration is capped at 5 minutes of wall time, including across restarts. */
|
|
23
|
+
export declare const CALIBRATION_AUTO_STOP_MS: number;
|
|
24
|
+
export type CalibrationRestoreReason = "stop" | "auto";
|
|
25
|
+
export type CalibrationRestoreCallback = (snapshot: CalibrationSnapshot, reason: CalibrationRestoreReason) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Per-appliance service that snapshots a SunSpec device's writable control registers when a
|
|
28
|
+
* calibration starts, tracks which registers other commands mutate while it is active, and
|
|
29
|
+
* restores those registers when calibration stops (either explicitly or via the 5-minute
|
|
30
|
+
* auto-stop timer).
|
|
31
|
+
*
|
|
32
|
+
* Snapshots are persisted via `EnergyAppStorage` so a crash/restart mid-calibration does not
|
|
33
|
+
* leave the device in a calibration state without a rollback. On `initialize()`, any stored
|
|
34
|
+
* snapshot is reloaded; the auto-stop deadline is computed from `startedAtIso` so total
|
|
35
|
+
* calibration time remains capped at `autoStopMs` across restarts.
|
|
36
|
+
*/
|
|
37
|
+
export declare class CalibrationSnapshotService {
|
|
38
|
+
private readonly storage;
|
|
39
|
+
private readonly applianceId;
|
|
40
|
+
private readonly onRestore;
|
|
41
|
+
private readonly autoStopMs;
|
|
42
|
+
private snapshot?;
|
|
43
|
+
private autoStopTimer?;
|
|
44
|
+
constructor(storage: EnergyAppStorage, applianceId: string, onRestore: CalibrationRestoreCallback, autoStopMs?: number);
|
|
45
|
+
private storageKey;
|
|
46
|
+
initialize(): Promise<void>;
|
|
47
|
+
isCalibrating(): boolean;
|
|
48
|
+
getSnapshot(): CalibrationSnapshot | undefined;
|
|
49
|
+
startCalibration(input: {
|
|
50
|
+
deviceType: CalibrationDeviceType;
|
|
51
|
+
unitId: number;
|
|
52
|
+
inverterControls?: SunspecInverterControls;
|
|
53
|
+
batteryControls?: SunspecBatteryControls;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
recordModification(fields: string[]): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Clear the timer, remove the snapshot from memory + storage, and return the snapshot so
|
|
58
|
+
* the caller can restore the modified registers. Returns undefined if no calibration was
|
|
59
|
+
* active.
|
|
60
|
+
*/
|
|
61
|
+
stopCalibration(): Promise<CalibrationSnapshot | undefined>;
|
|
62
|
+
private remainingMs;
|
|
63
|
+
private scheduleAutoStop;
|
|
64
|
+
private clearAutoStopTimer;
|
|
65
|
+
private fireAutoStop;
|
|
66
|
+
private persist;
|
|
67
|
+
}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -18,6 +18,7 @@ exports.getSdkVersion = exports.SDK_VERSION = exports.ConnectionRetryManager = v
|
|
|
18
18
|
__exportStar(require("./sunspec-interfaces.cjs"), exports);
|
|
19
19
|
__exportStar(require("./sunspec-devices.cjs"), exports);
|
|
20
20
|
__exportStar(require("./sunspec-modbus-client.cjs"), exports);
|
|
21
|
+
__exportStar(require("./calibration-snapshot-service.cjs"), exports);
|
|
21
22
|
var connection_retry_manager_js_1 = require("./connection-retry-manager.cjs");
|
|
22
23
|
Object.defineProperty(exports, "ConnectionRetryManager", { enumerable: true, get: function () { return connection_retry_manager_js_1.ConnectionRetryManager; } });
|
|
23
24
|
var version_js_1 = require("./version.cjs");
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './sunspec-interfaces.cjs';
|
|
2
2
|
export * from './sunspec-devices.cjs';
|
|
3
3
|
export * from './sunspec-modbus-client.cjs';
|
|
4
|
+
export * from './calibration-snapshot-service.cjs';
|
|
4
5
|
export { ConnectionRetryManager } from './connection-retry-manager.cjs';
|
|
5
6
|
export { SDK_VERSION, getSdkVersion } from './version.cjs';
|