frugal-iot-client 2.0.6 → 2.0.7
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/CLAUDE.md +3 -1
- package/core.js +9 -2
- package/package.json +2 -2
- package/test/datatree.test.js +9 -0
- package/test/embedded.test.js +15 -0
package/CLAUDE.md
CHANGED
|
@@ -259,7 +259,9 @@ else — no `mqtt-wrapper`, no discovery tree, no `/config.json`. `standaloneTop
|
|
|
259
259
|
the `mqtt-client` element instead. `test/embedded.test.js` guards this — it has broken twice
|
|
260
260
|
unnoticed.
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
Graph history has no `server_config.logger.url` to read on such a page, so `loggerUrl()` falls back
|
|
263
|
+
to `/data` on the server the modules were loaded from. `/data` needs a login, so a page on another
|
|
264
|
+
site gets dotted "no data" gaps instead of history.
|
|
263
265
|
|
|
264
266
|
### Looking up topics
|
|
265
267
|
|
package/core.js
CHANGED
|
@@ -66,6 +66,11 @@ function nextUniqueId() { return ++unique_id; }
|
|
|
66
66
|
const RECEIVER_ATTRIBUTES = ['value','color','type','label','topic','graphable','wired','wiring'];
|
|
67
67
|
let server_config; // { user, organizations, logger, mqtt, server } - note loaded by MqttWrapper OR MqttAdmin
|
|
68
68
|
|
|
69
|
+
// An embedded page loads no /config.json, so assume the logger is on the server these modules came from
|
|
70
|
+
function loggerUrl() {
|
|
71
|
+
return server_config?.logger?.url ?? new URL('/data', import.meta.url).href;
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
// This structure defines each of the common Input/Output types included within a sensor or acctuator
|
|
70
75
|
// NOTE BELOW COPIED TO SERVER config.d/schema on 20 Feb - append any changes since then
|
|
71
76
|
|
|
@@ -2186,7 +2191,9 @@ class MqttTopic {
|
|
|
2186
2191
|
} else {
|
|
2187
2192
|
// Headless path: update the data tree directly (also reached by detached MqttGraphDataset topics).
|
|
2188
2193
|
// topicSetPath throws if nodeMt is null, so guard the startsWith call with nodeMt.
|
|
2189
|
-
|
|
2194
|
+
// The node publishes its own wired paths on the read path, the UX writes them on the set path.
|
|
2195
|
+
const isParam = this.nodeMt
|
|
2196
|
+
&& (topic.startsWith(this.topicPath + '/') || topic.startsWith(this.topicSetPath + '/'));
|
|
2190
2197
|
if (isParam) {
|
|
2191
2198
|
// Parameter subtopic arriving (e.g. now/wired, temperature/max).
|
|
2192
2199
|
if (topic.split('/').pop() === 'wired') this.setWired(message);
|
|
@@ -2329,7 +2336,7 @@ class MqttTopic {
|
|
|
2329
2336
|
addDataFrom(filename, first, cb) {
|
|
2330
2337
|
//TODO this location may change
|
|
2331
2338
|
// noinspection JSUnresolvedReference
|
|
2332
|
-
let filepath = `${
|
|
2339
|
+
let filepath = `${loggerUrl()}/${this.topicPath}/${filename}`;
|
|
2333
2340
|
console.log("Adding from", filepath);
|
|
2334
2341
|
//let self = this; // if needed in Promise
|
|
2335
2342
|
fetch(filepath)
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"async": "^3.2.6",
|
|
11
11
|
"chart.js": "^4.5.1",
|
|
12
|
-
"csv-parse": "^
|
|
12
|
+
"csv-parse": "^7.0.3",
|
|
13
13
|
"dial-gauge": "^0.1.5",
|
|
14
14
|
"esptool-js": "^0.6.1",
|
|
15
15
|
"html-element-extended": "^0.1.7",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"unlink:hee": "rm -f node_modules/html-element-extended && npm install html-element-extended"
|
|
42
42
|
},
|
|
43
43
|
"type": "module",
|
|
44
|
-
"version": "2.0.
|
|
44
|
+
"version": "2.0.7"
|
|
45
45
|
}
|
package/test/datatree.test.js
CHANGED
|
@@ -260,6 +260,15 @@ describe('controls and wiring', () => {
|
|
|
260
260
|
assert.equal(nowMt.wiredTopic, nodeMt.groups.sht.topics.temperature);
|
|
261
261
|
});
|
|
262
262
|
|
|
263
|
+
test('a wired path the node publishes on its read path is a parameter, not a value', () => {
|
|
264
|
+
const { projectMt } = mock.runScenario('control-wired');
|
|
265
|
+
const groupMt = projectMt.nodes['esp8266-fb94bb'].groups.controlhysteresis;
|
|
266
|
+
mock.deliver('dev/lotus/esp8266-fb94bb/controlhysteresis/out/wired', 'dev/lotus/esp8266-fb94bb/set/relay/on');
|
|
267
|
+
assert.equal(groupMt.state.out_wired, 'dev/lotus/esp8266-fb94bb/set/relay/on');
|
|
268
|
+
assert.equal(groupMt.topics.out.wired, 'dev/lotus/esp8266-fb94bb/set/relay/on');
|
|
269
|
+
assert.equal(typeof groupMt.summaryShort(), 'string');
|
|
270
|
+
});
|
|
271
|
+
|
|
263
272
|
test('an unwired control has no wiredTopic', () => {
|
|
264
273
|
const { projectMt } = mock.runScenario('control-unwired');
|
|
265
274
|
const nowMt = projectMt.nodes['esp8266-fb94bb'].groups.controlhysteresis?.topics?.now;
|
package/test/embedded.test.js
CHANGED
|
@@ -47,4 +47,19 @@ describe('embedded widgets', () => {
|
|
|
47
47
|
// Not delivering a message here: dataChanged() calls chart.update(), and Chart.js cannot
|
|
48
48
|
// measure text without a canvas, which jsdom does not have.
|
|
49
49
|
});
|
|
50
|
+
|
|
51
|
+
test('a graphdataset fetches history from the logger with no server config', (t, done) => {
|
|
52
|
+
document.body.innerHTML =
|
|
53
|
+
'<mqtt-graph><mqtt-graphdataset topic="dev/lotus/esp32-784702/sht/temperature" color="red" label="Office Temperature" min=0 max=50 type="float"></mqtt-graphdataset></mqtt-graph>';
|
|
54
|
+
const ds = document.querySelector('mqtt-graphdataset');
|
|
55
|
+
const realFetch = globalThis.fetch;
|
|
56
|
+
let asked;
|
|
57
|
+
globalThis.fetch = (url) => { asked = url; return Promise.resolve({ ok: false, status: 404, statusText: 'Not Found' }); };
|
|
58
|
+
ds.mt.addDataFrom('2026-10-18.csv', true, (err) => {
|
|
59
|
+
globalThis.fetch = realFetch;
|
|
60
|
+
assert.equal(err, null);
|
|
61
|
+
assert.match(asked, /\/data\/dev\/lotus\/esp32-784702\/sht\/temperature\/2026-10-18\.csv$/);
|
|
62
|
+
done();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
50
65
|
});
|