smart-home-engine 1.15.0 → 1.16.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-DfHNSs-x.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-Dt8a9eeo.js";import{t as I}from"./index-BIE09pOh.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-DfHNSs-x.js"></script>
175
+ <script type="module" crossorigin src="/assets/index-BIE09pOh.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-LTpStzqg.css">
178
+ <link rel="stylesheet" crossorigin href="/assets/index-B3w0pv6a.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.15.0",
3
+ "version": "1.16.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": {
@@ -13,9 +13,12 @@
13
13
  * she.db.prop(id, method, prop, val)→ void (method: 'set'|'create'|'del')
14
14
  * she.db.sub(pattern, callback) → void (callback(id, doc))
15
15
  * she.db.query(filter, mapFn, reduceFn) → Array (ad-hoc synchronous query)
16
+ * she.db.getView(id) → Array|undefined (current view result)
17
+ * she.db.subView(pattern, callback) → void (callback(id, result))
18
+ * she.db.setView(id, definition) → void (create/update named view)
16
19
  */
17
20
 
18
- const { getCore, addListener, removeListenersByScript } = require('../web/shedb');
21
+ const { getCore, addListener, removeListenersByScript, addViewListener, removeViewListenersByScript } = require('../web/shedb');
19
22
 
20
23
  module.exports = function (she, { scriptDomain, scriptName, scriptFile }) {
21
24
  const core = getCore();
@@ -80,6 +83,57 @@ module.exports = function (she, { scriptDomain, scriptName, scriptFile }) {
80
83
  if (!core) return [];
81
84
  return core.adhocQuery(filter, mapFn, reduceFn);
82
85
  },
86
+
87
+ /**
88
+ * Return the current computed result array for a named view, or undefined if
89
+ * the view does not exist, has not yet completed, or produced an error.
90
+ *
91
+ * @param {string} id - view name
92
+ * @returns {Array|undefined}
93
+ */
94
+ getView(id) {
95
+ if (!core) return undefined;
96
+ const view = core.views[id];
97
+ if (!view || view.error) return undefined;
98
+ return view.result;
99
+ },
100
+
101
+ /**
102
+ * Subscribe to view result changes matching an MQTT wildcard pattern.
103
+ * callback(id, result) fires whenever a matching view's result changes;
104
+ * result is undefined if the view errored.
105
+ * Subscriptions are automatically removed when the script is hot-reloaded.
106
+ *
107
+ * @param {string} pattern - MQTT wildcard, e.g. 'stats/#'
108
+ * @param {Function} callback - called as callback(id, result)
109
+ */
110
+ subView(pattern, callback) {
111
+ if (!core) return;
112
+ const wrapped = scriptDomain.bind(callback);
113
+ addViewListener(pattern, wrapped, trackingKey);
114
+ },
115
+
116
+ /**
117
+ * Create or update a named persistent view.
118
+ * Definition: { map, filter?, reduce?, publish?, retain?, description? }
119
+ * `publish` maps to `mqttpub` internally.
120
+ *
121
+ * @param {string} id
122
+ * @param {{ map: string, filter?: string, reduce?: string, publish?: boolean, retain?: boolean, description?: string }} definition
123
+ */
124
+ setView(id, definition) {
125
+ if (!core) return;
126
+ if (!definition || typeof definition !== 'object') return;
127
+ const { map, filter, reduce, publish, retain, description } = definition;
128
+ if (typeof map !== 'string' || !map.trim()) return;
129
+ const payload = { map };
130
+ if (filter) payload.filter = filter;
131
+ if (reduce) payload.reduce = reduce;
132
+ if (publish) payload.mqttpub = true;
133
+ if (retain) payload.retain = true;
134
+ if (description) payload.description = String(description).slice(0, 500);
135
+ core.query(id, payload);
136
+ },
83
137
  };
84
138
  };
85
139
 
@@ -89,4 +143,5 @@ module.exports = function (she, { scriptDomain, scriptName, scriptFile }) {
89
143
  */
90
144
  module.exports.cleanup = function (scriptFile) {
91
145
  removeListenersByScript(scriptFile);
146
+ removeViewListenersByScript(scriptFile);
92
147
  };
package/src/web/shedb.js CHANGED
@@ -23,6 +23,9 @@ let _broadcast = () => {};
23
23
  /** Registry for she.db.sub() sandbox subscriptions */
24
24
  const _listeners = []; // { pattern: string, callback: Function, _script: string }
25
25
 
26
+ /** Registry for she.db.subView() sandbox subscriptions */
27
+ const _viewListeners = []; // { pattern: string, callback: Function, _script: string }
28
+
26
29
  /**
27
30
  * Initialise sheDB.
28
31
  *
@@ -80,6 +83,18 @@ function init({ dbPath, dbPublish, dbRetain, dbPrefix, mqttName, mqtt, log, broa
80
83
  if (query && query.mqttpub && _mqtt && view && !view.error) {
81
84
  _mqtt.publish(_dbPrefix + 'view/' + id, JSON.stringify(view.result ?? []), { retain: query.retain === true });
82
85
  }
86
+
87
+ // Fire sandbox she.db.subView() listeners
88
+ const result = view && !view.error ? view.result : undefined;
89
+ for (const sub of _viewListeners) {
90
+ if (mqttWildcard(id, sub.pattern) !== null) {
91
+ try {
92
+ sub.callback(id, result);
93
+ } catch {
94
+ /* errors are caught by the script domain wrapper */
95
+ }
96
+ }
97
+ }
83
98
  });
84
99
 
85
100
  // When a view definition is saved, immediately publish the existing cached
@@ -165,16 +180,28 @@ function addListener(pattern, callback, scriptName) {
165
180
  _listeners.push({ pattern, callback, _script: scriptName });
166
181
  }
167
182
 
168
- /** Remove all listeners registered by a script (called on hot-reload / unload). */
183
+ /** Remove all she.db.sub() listeners registered by a script (called on hot-reload / unload). */
169
184
  function removeListenersByScript(scriptName) {
170
185
  for (let i = _listeners.length - 1; i >= 0; i--) {
171
186
  if (_listeners[i]._script === scriptName) _listeners.splice(i, 1);
172
187
  }
173
188
  }
174
189
 
190
+ /** Register a she.db.subView() listener. */
191
+ function addViewListener(pattern, callback, scriptName) {
192
+ _viewListeners.push({ pattern, callback, _script: scriptName });
193
+ }
194
+
195
+ /** Remove all she.db.subView() listeners registered by a script (called on hot-reload / unload). */
196
+ function removeViewListenersByScript(scriptName) {
197
+ for (let i = _viewListeners.length - 1; i >= 0; i--) {
198
+ if (_viewListeners[i]._script === scriptName) _viewListeners.splice(i, 1);
199
+ }
200
+ }
201
+
175
202
  /** Return the live core instance (used by shedb-api.js). */
176
203
  function getCore() {
177
204
  return _core;
178
205
  }
179
206
 
180
- module.exports = { init, handleMqttMessage, addListener, removeListenersByScript, getCore };
207
+ module.exports = { init, handleMqttMessage, addListener, removeListenersByScript, addViewListener, removeViewListenersByScript, getCore };