signalk-endurance 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/docs/CHANGELOG.md +8 -0
- package/lib/config.js +3 -2
- package/lib/resources/energy.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ npm install github:SCarns/signalk-endurance
|
|
|
37
37
|
Or install a downloaded package with:
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
npm install /absolute/path/signalk-endurance-1.0.
|
|
40
|
+
npm install /absolute/path/signalk-endurance-1.0.1.tgz
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
Restart Signal K, enable **Endurance** in **Server → Plugin Config**, then open its
|
|
@@ -178,6 +178,13 @@ The server's preferred-source model values are used.
|
|
|
178
178
|
`electrical.energy.prediction.status`, or enter a custom path. Healthy measured SOC plus
|
|
179
179
|
`deficit` gives informational WATCH; forecast `critical` gives WARNING. Measured
|
|
180
180
|
low SOC takes priority. A missing forecast does not prevent SOC assessment.
|
|
181
|
+
Forecasts use `resources.energy.predictionStaleSeconds` (default 7200 seconds),
|
|
182
|
+
independently of measured SOC and battery duration (`staleSeconds`, default 300).
|
|
183
|
+
Energy Predictor 0.12.3 recalculates every 60 minutes by default (configurable
|
|
184
|
+
5–60 minutes) and republishes status on each successful cycle, including unchanged
|
|
185
|
+
values. Two hours allows one delayed cycle while still expiring stopped forecasts.
|
|
186
|
+
Configure a shorter forecast window if desired. Missing, expired, or invalidly
|
|
187
|
+
timestamped forecasts leave measured-SOC assessment in control.
|
|
181
188
|
- **Battery duration:** leave `timePath` blank to discover `capacity.timeRemaining`
|
|
182
189
|
for the selected SOC battery bank. For example, selecting
|
|
183
190
|
`electrical.batteries.House.capacity.stateOfCharge` discovers
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 1.0.1
|
|
2
|
+
|
|
3
|
+
- Fix forecasts disappearing after five minutes: use an independent configurable
|
|
4
|
+
`resources.energy.predictionStaleSeconds` window, default two hours, matching
|
|
5
|
+
Energy Predictor's default hourly refresh with allowance for one delayed cycle.
|
|
6
|
+
- Keep measured SOC and battery duration freshness unchanged, including standalone
|
|
7
|
+
SOC fallback when the optional forecast is missing or expired.
|
|
8
|
+
|
|
1
9
|
# Release notes
|
|
2
10
|
|
|
3
11
|
## 1.0.0
|
package/lib/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const {unitSchema,toM3,migrateOptions}=require('./input-units');
|
|
3
3
|
const defaults = {
|
|
4
|
-
energy: { warning: 0.70, critical: 0.50, staleSeconds: 300 },
|
|
4
|
+
energy: { warning: 0.70, critical: 0.50, staleSeconds: 300, predictionStaleSeconds: 7200 },
|
|
5
5
|
freshwater: { warning: 0.35, critical: 0.20, staleSeconds: 1200 },
|
|
6
6
|
fuel: { warning: 0.30, critical: 0.20, staleSeconds: 1200 },
|
|
7
7
|
propane: { warning: 0.25, critical: 0.10, staleSeconds: 86400 },
|
|
@@ -37,7 +37,7 @@ const schema = {
|
|
|
37
37
|
warning: ratio('Warning threshold (ratio)', d.warning),
|
|
38
38
|
critical: ratio('Critical threshold (ratio)', d.critical),
|
|
39
39
|
staleSeconds: { type: 'integer', default: d.staleSeconds, minimum: 1, maximum: 604800 },
|
|
40
|
-
...(name === 'energy' ? { predictionPath: path('Energy Predictor status path (blank: discover electrical.energy.prediction.status)'), timePath: path('Measured time remaining path in seconds (blank: discover for selected battery bank)') } : {
|
|
40
|
+
...(name === 'energy' ? { predictionStaleSeconds: { type: 'integer', title: 'Forecast freshness window (seconds)', description: 'Separate from measured SOC and battery time. Energy Predictor defaults to hourly forecasts; two hours allows one delayed cycle.', default: 7200, minimum: 1, maximum: 604800 }, predictionPath: path('Energy Predictor status path (blank: discover electrical.energy.prediction.status)'), timePath: path('Measured time remaining path in seconds (blank: discover for selected battery bank)') } : {
|
|
41
41
|
tanks: { type: 'array', title: 'Tanks', default: [], maxItems: 64, items: tankSchema },
|
|
42
42
|
...(name !== 'holding' ? { assessmentMode: { type: 'string', enum: ['combined', 'individual'], default: name === 'freshwater' ? 'combined' : 'individual', title: 'Assess combined supply or each tank individually' } } : {})
|
|
43
43
|
}),
|
|
@@ -72,6 +72,7 @@ function normalize(options = {}) {
|
|
|
72
72
|
for (const k of ['warning', 'critical']) if (typeof r[k] !== 'number' || !Number.isFinite(r[k]) || r[k] < 0 || r[k] > 1) throw Error(`${name}: invalid ${k} ratio`);
|
|
73
73
|
if (name === 'holding' ? r.critical <= r.warning : r.critical >= r.warning) throw Error(`${name}: thresholds are reversed or equal`);
|
|
74
74
|
if (!Number.isInteger(r.staleSeconds) || r.staleSeconds < 1 || r.staleSeconds > 604800) throw Error(`${name}: invalid freshness window`);
|
|
75
|
+
if (name === 'energy' && (!Number.isInteger(r.predictionStaleSeconds) || r.predictionStaleSeconds < 1 || r.predictionStaleSeconds > 604800)) throw Error('energy: invalid forecast freshness window');
|
|
75
76
|
for (const k of ['levelPath', 'predictionPath', 'timePath', 'volumePath', 'consumptionPath']) if (r[k] !== undefined && !validPath(r[k])) throw Error(`${name}: invalid input path`);
|
|
76
77
|
if (name !== 'energy') {
|
|
77
78
|
r.assessmentMode ??= name === 'freshwater' ? 'combined' : 'individual';
|
package/lib/resources/energy.js
CHANGED
|
@@ -4,7 +4,7 @@ const { read, resolveEnergyInputs } = require('../discovery');
|
|
|
4
4
|
module.exports = (config, app, selection, now) => {
|
|
5
5
|
const r = assessLevel('energy', config, app, selection, now);
|
|
6
6
|
const paths=resolveEnergyInputs(app,config,selection.path);
|
|
7
|
-
r.prediction = {...read(app, paths.prediction.path, config.
|
|
7
|
+
r.prediction = {...read(app, paths.prediction.path, config.predictionStaleSeconds, now),selection:paths.prediction.selection};
|
|
8
8
|
r.duration = {...read(app, paths.duration.path, config.staleSeconds, now),selection:paths.duration.selection};
|
|
9
9
|
if (r.status === 'UNKNOWN') return r;
|
|
10
10
|
if (r.duration.valid && typeof r.duration.value === 'number' && Number.isFinite(r.duration.value) && r.duration.value >= 0) r.time = r.duration.value;
|