smart-home-engine 0.28.2 → 0.29.0
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-BW2J83t5.js";import{t as I}from"./index-
|
|
1
|
+
import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-BNu1W94k.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
package/dist/web/index.html
CHANGED
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
}
|
|
156
156
|
})();
|
|
157
157
|
</script>
|
|
158
|
-
<script type="module" crossorigin src="/assets/index-
|
|
158
|
+
<script type="module" crossorigin src="/assets/index-BNu1W94k.js"></script>
|
|
159
159
|
<link rel="modulepreload" crossorigin href="/assets/monaco-langs-BW2J83t5.js">
|
|
160
160
|
<link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
|
|
161
161
|
<link rel="stylesheet" crossorigin href="/assets/index-DMJ3LJnK.css">
|
package/package.json
CHANGED
package/src/sandbox/stdlib.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable func-name-matching, func-names, camelcase */
|
|
2
2
|
|
|
3
|
-
module.exports = function (she) {
|
|
3
|
+
module.exports = function (she, ctx = {}) {
|
|
4
4
|
/**
|
|
5
5
|
* @method now
|
|
6
6
|
* @returns {number} ms since epoch
|
|
@@ -9,106 +9,20 @@ module.exports = function (she) {
|
|
|
9
9
|
return new Date().getTime();
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
she.age = function Sandbox_age(topic) {
|
|
18
|
-
return Math.round((new Date().getTime() - she.getProp(topic, 'lc')) / 1000);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Link topic(s) to other topic(s)
|
|
23
|
-
* @method link
|
|
24
|
-
* @param {(string|string[])} source - topic or array of topics to subscribe
|
|
25
|
-
* @param {(string|string[])} target - topic or array of topics to publish
|
|
26
|
-
* @param {mixed} [value] - value to publish. If omitted the sources value is published. A function can be used to transform the value.
|
|
27
|
-
*/
|
|
28
|
-
she.link = function Sandbox_link(source, target, /* optional */ value) {
|
|
29
|
-
she.mqttsub(source, (topic, val) => {
|
|
30
|
-
if (typeof value === 'function') {
|
|
31
|
-
val = value(val);
|
|
32
|
-
} else if (typeof value !== 'undefined') {
|
|
33
|
-
val = value;
|
|
34
|
-
}
|
|
12
|
+
// Route a computed value to a topic string (setValue) or a callback (called as fn(topic, val)).
|
|
13
|
+
function sink(target, topic, val) {
|
|
14
|
+
if (typeof target === 'function') {
|
|
15
|
+
target(topic, val);
|
|
16
|
+
} else {
|
|
35
17
|
she.setValue(target, val);
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Combine topics through boolean or
|
|
41
|
-
* @method combineBool
|
|
42
|
-
* @param {string[]} srcs - array of topics to subscribe
|
|
43
|
-
* @param {string} targets - topic to publish
|
|
44
|
-
*/
|
|
45
|
-
she.combineBool = function Sandbox_combineBool(srcs, target) {
|
|
46
|
-
function combine() {
|
|
47
|
-
let result = 0;
|
|
48
|
-
srcs.forEach((src) => {
|
|
49
|
-
if (she.getValue(src)) {
|
|
50
|
-
result = 1;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
she.setValue(target, result);
|
|
54
18
|
}
|
|
55
|
-
|
|
56
|
-
she.mqttsub(srcs, { retain: true }, combine);
|
|
57
|
-
};
|
|
19
|
+
}
|
|
58
20
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @method combineMax
|
|
62
|
-
* @param {string[]} srcs - array of topics to subscribe
|
|
63
|
-
* @param {string} targets - topic to publish
|
|
64
|
-
*/
|
|
65
|
-
she.combineMax = function (srcs, target) {
|
|
66
|
-
function combine() {
|
|
67
|
-
let result = 0;
|
|
68
|
-
srcs.forEach((src) => {
|
|
69
|
-
const srcVal = she.getValue(src);
|
|
70
|
-
if (srcVal > result) {
|
|
71
|
-
result = srcVal;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
she.setValue(target, result);
|
|
75
|
-
}
|
|
76
|
-
combine();
|
|
77
|
-
she.mqttsub(srcs, { retain: true }, combine);
|
|
78
|
-
};
|
|
21
|
+
// Timeout handles indexed by target (string key or function reference).
|
|
22
|
+
const timerHandles = new Map();
|
|
79
23
|
|
|
80
|
-
const timeouts = {};
|
|
81
24
|
/**
|
|
82
|
-
*
|
|
83
|
-
* @method timer
|
|
84
|
-
* @param {(string|string[])} src - topic or array of topics to subscribe
|
|
85
|
-
* @param {string} target - topic to publish
|
|
86
|
-
* @param {number} time - timeout in milliseconds
|
|
87
|
-
*/
|
|
88
|
-
she.timer = function (src, target, time) {
|
|
89
|
-
she.mqttsub(src, { retain: false }, (topic, val) => {
|
|
90
|
-
if (val) {
|
|
91
|
-
she.clearTimeout(timeouts[target]);
|
|
92
|
-
if (!she.getValue(target)) {
|
|
93
|
-
she.setValue(target, 1);
|
|
94
|
-
}
|
|
95
|
-
timeouts[target] = she.setTimeout(() => {
|
|
96
|
-
if (she.getValue(target)) {
|
|
97
|
-
she.setValue(target, 0);
|
|
98
|
-
}
|
|
99
|
-
}, time);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
timeouts[target] = she.setTimeout(() => {
|
|
104
|
-
if (she.getValue(target)) {
|
|
105
|
-
she.setValue(target, 0);
|
|
106
|
-
}
|
|
107
|
-
}, time);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Namespaced MQTT API — the primary way to interact with MQTT from scripts.
|
|
25
|
+
* Namespaced MQTT API - the primary way to interact with MQTT from scripts.
|
|
112
26
|
* @namespace she.mqtt
|
|
113
27
|
*/
|
|
114
28
|
she.mqtt = {
|
|
@@ -123,35 +37,166 @@ module.exports = function (she) {
|
|
|
123
37
|
/** Get a specific property from a topic's state object. */
|
|
124
38
|
getProp: (...args) => she.getProp(...args),
|
|
125
39
|
/** Forward value changes from source topic(s) to target topic(s). */
|
|
126
|
-
link: (
|
|
40
|
+
link: function Sandbox_mqtt_link(source, target, /* optional */ value) {
|
|
41
|
+
she.mqttsub(source, (topic, val) => {
|
|
42
|
+
if (typeof value === 'function') {
|
|
43
|
+
val = value(val);
|
|
44
|
+
} else if (typeof value !== 'undefined') {
|
|
45
|
+
val = value;
|
|
46
|
+
}
|
|
47
|
+
she.setValue(target, val);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
127
50
|
/** Seconds since the topic's value last changed. */
|
|
128
|
-
age: (topic)
|
|
51
|
+
age: function Sandbox_mqtt_age(topic) {
|
|
52
|
+
return Math.round((new Date().getTime() - she.getProp(topic, 'lc')) / 1000);
|
|
53
|
+
},
|
|
129
54
|
/** Register a callback for MQTT connection lifecycle events ('connect' or 'disconnect'). */
|
|
130
55
|
on: (event, cb) => she._registerMqttEvent(event, cb),
|
|
56
|
+
/**
|
|
57
|
+
* Publish 1 to target when any source is truthy, 0 otherwise.
|
|
58
|
+
* target may be a topic string or a callback(topic, val).
|
|
59
|
+
*/
|
|
60
|
+
or: function Sandbox_mqtt_or(srcs, target) {
|
|
61
|
+
function combine(topic) {
|
|
62
|
+
const result = srcs.some((src) => she.getValue(src)) ? 1 : 0;
|
|
63
|
+
sink(target, topic ?? null, result);
|
|
64
|
+
}
|
|
65
|
+
combine(null);
|
|
66
|
+
she.mqttsub(srcs, { retain: true }, (topic) => combine(topic));
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Publish 1 to target when all sources are truthy, 0 otherwise.
|
|
70
|
+
* target may be a topic string or a callback(topic, val).
|
|
71
|
+
*/
|
|
72
|
+
and: function Sandbox_mqtt_and(srcs, target) {
|
|
73
|
+
function combine(topic) {
|
|
74
|
+
const result = srcs.every((src) => she.getValue(src)) ? 1 : 0;
|
|
75
|
+
sink(target, topic ?? null, result);
|
|
76
|
+
}
|
|
77
|
+
combine(null);
|
|
78
|
+
she.mqttsub(srcs, { retain: true }, (topic) => combine(topic));
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* Publish the maximum of all source values to target.
|
|
82
|
+
* target may be a topic string or a callback(topic, val).
|
|
83
|
+
*/
|
|
84
|
+
max: function Sandbox_mqtt_max(srcs, target) {
|
|
85
|
+
function combine(topic) {
|
|
86
|
+
let result = 0;
|
|
87
|
+
srcs.forEach((src) => {
|
|
88
|
+
const v = she.getValue(src);
|
|
89
|
+
if (v > result) result = v;
|
|
90
|
+
});
|
|
91
|
+
sink(target, topic ?? null, result);
|
|
92
|
+
}
|
|
93
|
+
combine(null);
|
|
94
|
+
she.mqttsub(srcs, { retain: true }, (topic) => combine(topic));
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* Publish the minimum of all source values to target.
|
|
98
|
+
* target may be a topic string or a callback(topic, val).
|
|
99
|
+
*/
|
|
100
|
+
min: function Sandbox_mqtt_min(srcs, target) {
|
|
101
|
+
function combine(topic) {
|
|
102
|
+
const values = srcs.map((src) => she.getValue(src)).filter((v) => v !== undefined && v !== null);
|
|
103
|
+
const result = values.length ? Math.min(...values) : 0;
|
|
104
|
+
sink(target, topic ?? null, result);
|
|
105
|
+
}
|
|
106
|
+
combine(null);
|
|
107
|
+
she.mqttsub(srcs, { retain: true }, (topic) => combine(topic));
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
* Pulse target to 1 for ms after src goes truthy, then reset to 0.
|
|
111
|
+
* Signature: timer(src, ms, target)
|
|
112
|
+
* target may be a topic string or a callback(topic, val).
|
|
113
|
+
* When target is a topic string, any lingering 1 from a previous run is
|
|
114
|
+
* cleared by an initial timeout on startup.
|
|
115
|
+
*/
|
|
116
|
+
timer: function Sandbox_mqtt_timer(src, ms, target) {
|
|
117
|
+
const key = target;
|
|
118
|
+
she.mqttsub(src, { retain: false }, (topic, val) => {
|
|
119
|
+
if (val) {
|
|
120
|
+
she.clearTimeout(timerHandles.get(key));
|
|
121
|
+
sink(target, topic, 1);
|
|
122
|
+
timerHandles.set(
|
|
123
|
+
key,
|
|
124
|
+
she.setTimeout(() => {
|
|
125
|
+
sink(target, null, 0);
|
|
126
|
+
}, ms),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
if (typeof target !== 'function') {
|
|
131
|
+
timerHandles.set(
|
|
132
|
+
key,
|
|
133
|
+
she.setTimeout(() => {
|
|
134
|
+
if (she.getValue(target)) she.setValue(target, 0);
|
|
135
|
+
}, ms),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
131
139
|
};
|
|
132
140
|
|
|
133
141
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* Rejects on non-2xx responses.
|
|
137
|
-
* @method fetch
|
|
138
|
-
* @param {string} url
|
|
139
|
-
* @param {RequestInit} [options]
|
|
140
|
-
* @returns {Promise<string|object>}
|
|
142
|
+
* HTTP helpers — fetch and webhook receiver.
|
|
143
|
+
* @namespace she.http
|
|
141
144
|
*/
|
|
142
|
-
she.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
she.http = {
|
|
146
|
+
/**
|
|
147
|
+
* Fetch a URL and return a Promise that resolves to the response body.
|
|
148
|
+
* Resolves to parsed JSON when the Content-Type is application/json, plain text otherwise.
|
|
149
|
+
* Rejects on non-2xx responses.
|
|
150
|
+
* @param {string} url
|
|
151
|
+
* @param {RequestInit} [options]
|
|
152
|
+
* @returns {Promise<string|object>}
|
|
153
|
+
*/
|
|
154
|
+
fetch: function Sandbox_http_fetch(url, options) {
|
|
155
|
+
const TIMEOUT_MS = 30_000;
|
|
156
|
+
let signal = options?.signal;
|
|
157
|
+
let timer;
|
|
158
|
+
if (!signal) {
|
|
159
|
+
const ac = new AbortController();
|
|
160
|
+
signal = ac.signal;
|
|
161
|
+
timer = setTimeout(
|
|
162
|
+
() => ac.abort(new Error(`she.http.fetch timed out after ${TIMEOUT_MS / 1000}s`)),
|
|
163
|
+
TIMEOUT_MS,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return fetch(url, { ...options, signal })
|
|
167
|
+
.then((r) => {
|
|
168
|
+
if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`);
|
|
169
|
+
const ct = r.headers.get('content-type') || '';
|
|
170
|
+
return ct.includes('json') ? r.json() : r.text();
|
|
171
|
+
})
|
|
172
|
+
.finally(() => clearTimeout(timer));
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* Register a POST webhook endpoint at /api/<scriptName><path>.
|
|
176
|
+
* The endpoint auto-responds { ok: true } (200) when the callback resolves,
|
|
177
|
+
* or { error } (500) when it throws. The callback receives:
|
|
178
|
+
* callback(body, { params, query, headers })
|
|
179
|
+
* @param {string} path - Route path, e.g. '/webhook/mydevice'
|
|
180
|
+
* @param {function} callback
|
|
181
|
+
*/
|
|
182
|
+
sub: function Sandbox_http_sub(path, callback) {
|
|
183
|
+
const { registerRoute } = require('../web/server');
|
|
184
|
+
const { scriptName } = ctx;
|
|
185
|
+
if (typeof path !== 'string') throw new TypeError('path must be a string');
|
|
186
|
+
if (typeof callback !== 'function') throw new TypeError('callback must be a function');
|
|
187
|
+
const fullPath = '/api/' + (scriptName || 'unknown') + path;
|
|
188
|
+
registerRoute('post', fullPath, (req, res) => {
|
|
189
|
+
let result;
|
|
190
|
+
try {
|
|
191
|
+
result = callback(req.body, { params: req.params, query: req.query, headers: req.headers });
|
|
192
|
+
} catch (err) {
|
|
193
|
+
res.status(500).json({ error: err.message });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
Promise.resolve(result)
|
|
197
|
+
.then(() => res.json({ ok: true }))
|
|
198
|
+
.catch((err) => res.status(500).json({ error: err.message }));
|
|
199
|
+
});
|
|
200
|
+
},
|
|
156
201
|
};
|
|
157
202
|
};
|
|
@@ -64,17 +64,20 @@ she.matter.send(nodeId, endpointId, cluster, cmd, [args]) → Promise<result>
|
|
|
64
64
|
|
|
65
65
|
### Helpers
|
|
66
66
|
```
|
|
67
|
-
she.timer(src,
|
|
68
|
-
|
|
69
|
-
she.
|
|
70
|
-
she.
|
|
71
|
-
she.
|
|
67
|
+
she.mqtt.timer(src, ms, topicOrCb) Pulse topicOrCb=1 for ms after src goes truthy
|
|
68
|
+
topicOrCb: topic string or callback(topic, val)
|
|
69
|
+
she.mqtt.or(srcs[], topicOrCb) Publish 1 if any source truthy, else 0
|
|
70
|
+
she.mqtt.and(srcs[], topicOrCb) Publish 1 if all sources truthy, else 0
|
|
71
|
+
she.mqtt.max(srcs[], topicOrCb) Publish maximum of source values
|
|
72
|
+
she.mqtt.min(srcs[], topicOrCb) Publish minimum of source values (0 if none set)
|
|
73
|
+
All topicOrCb: topic string or callback(topic, val)
|
|
72
74
|
she.now() Current timestamp in ms
|
|
73
75
|
she.debug / .info / .warn / .error Structured logging (prefixed with script name)
|
|
74
76
|
she.global Shared mutable object across all scripts
|
|
75
|
-
she.fetch(url, [opts])
|
|
76
|
-
Auto-parses JSON by Content-Type.
|
|
77
|
-
|
|
77
|
+
she.http.fetch(url, [opts]) HTTP/HTTPS fetch → Promise<string|object>
|
|
78
|
+
Auto-parses JSON by Content-Type. Throws on non-2xx.
|
|
79
|
+
she.http.sub(path, cb) Register POST /api/<script><path> webhook endpoint
|
|
80
|
+
cb(body, { params, query, headers })
|
|
78
81
|
she.config.latitude Read-only: geographic latitude from daemon config
|
|
79
82
|
she.config.longitude Read-only: geographic longitude from daemon config
|
|
80
83
|
```
|