smart-home-engine 1.20.0 → 1.20.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.
@@ -1,4 +1,4 @@
1
- import{m as O}from"./monaco-langs-Dt8a9eeo.js";import{t as I}from"./index-BUev42o8.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-Dt8a9eeo.js";import{t as I}from"./index-DxR31sxM.js";/*!-----------------------------------------------------------------------------
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
4
4
  * Released under the MIT license
@@ -172,10 +172,10 @@
172
172
  }
173
173
  })();
174
174
  </script>
175
- <script type="module" crossorigin src="/assets/index-BUev42o8.js"></script>
175
+ <script type="module" crossorigin src="/assets/index-DxR31sxM.js"></script>
176
176
  <link rel="modulepreload" crossorigin href="/assets/monaco-langs-Dt8a9eeo.js">
177
177
  <link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
178
- <link rel="stylesheet" crossorigin href="/assets/index-nf1nG74s.css">
178
+ <link rel="stylesheet" crossorigin href="/assets/index-CjRDTePT.css">
179
179
  </head>
180
180
  <body>
181
181
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
4
  "description": "Node.js based script runner for use in MQTT based Smart Home environments",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/influx.js CHANGED
@@ -6,6 +6,10 @@ let _mode = null; // 'v1' | 'v2' | null
6
6
 
7
7
  const V1_TIMEOUT_MS = 10000;
8
8
 
9
+ function v1Timeout() {
10
+ return Number(_opts.timeout) || V1_TIMEOUT_MS;
11
+ }
12
+
9
13
  /**
10
14
  * Initialise the InfluxDB client. Called from index.js when config.influx is set.
11
15
  *
@@ -119,7 +123,7 @@ async function v1Write(lines) {
119
123
  method: 'POST',
120
124
  headers: v1Headers(),
121
125
  body: lines,
122
- signal: AbortSignal.timeout(V1_TIMEOUT_MS),
126
+ signal: AbortSignal.timeout(v1Timeout()),
123
127
  });
124
128
  if (!res.ok) {
125
129
  const body = await res.text().catch(() => '');
@@ -139,7 +143,7 @@ async function v1Query(q) {
139
143
  const params = new URLSearchParams({ db: _opts.database, q, epoch: 'ms' });
140
144
  const res = await fetch(`${_opts.url.replace(/\/$/, '')}/query?${params}`, {
141
145
  headers: v1Headers(),
142
- signal: AbortSignal.timeout(V1_TIMEOUT_MS),
146
+ signal: AbortSignal.timeout(v1Timeout()),
143
147
  });
144
148
  if (!res.ok) {
145
149
  const body = await res.text().catch(() => '');
@@ -170,4 +174,13 @@ function escapeQL(s) {
170
174
  return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
171
175
  }
172
176
 
173
- module.exports = { init, getClient, getOpts, getMode, buildLine, v1Write, v1Query, escapeQL };
177
+ /**
178
+ * Escape a string for use inside an InfluxQL double-quoted identifier
179
+ * (measurement/tag/field name).
180
+ * @param {string} s
181
+ */
182
+ function escapeIdent(s) {
183
+ return String(s).replace(/"/g, '\\"');
184
+ }
185
+
186
+ module.exports = { init, getClient, getOpts, getMode, buildLine, v1Write, v1Query, escapeQL, escapeIdent };
@@ -35,6 +35,18 @@ function v1RowVal(row) {
35
35
  return null;
36
36
  }
37
37
 
38
+ /**
39
+ * Map v1 result rows to the { ts, val } shape, newest-trimmed to n (LIMIT
40
+ * applies per series in InfluxQL, so multi-series results must be merged and
41
+ * re-trimmed), sorted oldest-first.
42
+ * @param {object[]} rows
43
+ * @param {number} [n]
44
+ */
45
+ function v1Points(rows, n) {
46
+ const pts = rows.map((r) => ({ ts: r.time, val: v1RowVal(r) })).sort((a, b) => b.ts - a.ts);
47
+ return (n ? pts.slice(0, Number(n)) : pts).reverse();
48
+ }
49
+
38
50
  module.exports = function (she) {
39
51
  she.influx = {
40
52
  /**
@@ -101,10 +113,12 @@ module.exports = function (she) {
101
113
  },
102
114
 
103
115
  /**
104
- * Return the last N recorded values for an MQTT topic.
105
- * Assumes data was stored with a "topic" tag; the value comes from the
106
- * "_value" field (v2) or the "value" field (v1, falling back to the
107
- * first data column). Results are sorted oldest-first.
116
+ * Return the last N recorded values for an MQTT topic (last 30 days).
117
+ * v2: assumes points carry a "topic" tag, value from "_value".
118
+ * v1: tries a measurement named like the topic first (influx4mqtt-style
119
+ * schema), then falls back to a "topic" tag scan; value from the
120
+ * "value" field, falling back to the first data column.
121
+ * Results are sorted oldest-first.
108
122
  * @param {string} topic
109
123
  * @param {number} n
110
124
  * @returns {Promise<{ ts: number, val: any }[]>}
@@ -113,16 +127,16 @@ module.exports = function (she) {
113
127
  const mode = influx.getMode();
114
128
  if (!mode) return Promise.resolve([]);
115
129
  if (mode === 'v1') {
116
- // LIMIT applies per series in InfluxQL — query across all
117
- // measurements, then merge, trim to n and sort ascending.
118
- const ql = `SELECT * FROM /.*/ WHERE "topic" = '${influx.escapeQL(topic)}' ORDER BY time DESC LIMIT ${Number(n)}`;
119
- return influx.v1Query(ql).then((rows) =>
120
- rows
121
- .map((r) => ({ ts: r.time, val: v1RowVal(r) }))
122
- .sort((a, b) => b.ts - a.ts)
123
- .slice(0, Number(n))
124
- .reverse(),
125
- );
130
+ // Fast path: measurement named like the topic (the common
131
+ // mqtt-to-influx v1 schema, e.g. influx4mqtt). Fallback: points
132
+ // stored with a "topic" tag — that needs a scan across all
133
+ // measurements, so keep it time-bounded (30d, like the v2 path).
134
+ const mQl = `SELECT * FROM "${influx.escapeIdent(topic)}" WHERE time >= now() - 30d ORDER BY time DESC LIMIT ${Number(n)}`;
135
+ return influx.v1Query(mQl).then((rows) => {
136
+ if (rows.length) return v1Points(rows, n);
137
+ const tagQl = `SELECT * FROM /.*/ WHERE "topic" = '${influx.escapeQL(topic)}' AND time >= now() - 30d ORDER BY time DESC LIMIT ${Number(n)}`;
138
+ return influx.v1Query(tagQl).then((tagRows) => v1Points(tagRows, n));
139
+ });
126
140
  }
127
141
  const opts = influx.getOpts();
128
142
  const safeTopic = topic.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
@@ -143,8 +157,13 @@ module.exports = function (she) {
143
157
  if (mode === 'v1') {
144
158
  const fromMs = new Date(from).getTime();
145
159
  const toMs = new Date(to).getTime();
146
- const ql = `SELECT * FROM /.*/ WHERE "topic" = '${influx.escapeQL(topic)}' AND time >= ${fromMs}ms AND time <= ${toMs}ms`;
147
- return influx.v1Query(ql).then((rows) => rows.map((r) => ({ ts: r.time, val: v1RowVal(r) })).sort((a, b) => a.ts - b.ts));
160
+ const bounds = `time >= ${fromMs}ms AND time <= ${toMs}ms`;
161
+ const mQl = `SELECT * FROM "${influx.escapeIdent(topic)}" WHERE ${bounds}`;
162
+ return influx.v1Query(mQl).then((rows) => {
163
+ if (rows.length) return v1Points(rows);
164
+ const tagQl = `SELECT * FROM /.*/ WHERE "topic" = '${influx.escapeQL(topic)}' AND ${bounds}`;
165
+ return influx.v1Query(tagQl).then((tagRows) => v1Points(tagRows));
166
+ });
148
167
  }
149
168
  const opts = influx.getOpts();
150
169
  const safeTopic = topic.replace(/\\/g, '\\\\').replace(/"/g, '\\"');