smart-home-engine 0.27.1 → 0.27.3

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-ZrhJ-PgI.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-Ck2pAcwb.js";/*!-----------------------------------------------------------------------------
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
4
4
  * Released under the MIT license
@@ -155,7 +155,7 @@
155
155
  }
156
156
  })();
157
157
  </script>
158
- <script type="module" crossorigin src="/assets/index-ZrhJ-PgI.js"></script>
158
+ <script type="module" crossorigin src="/assets/index-Ck2pAcwb.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-6dbanXAb.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "0.27.1",
3
+ "version": "0.27.3",
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/index.js CHANGED
@@ -243,10 +243,11 @@ let connected = false;
243
243
 
244
244
  // Deferred start — wait for retained MQTT state before running scripts
245
245
  let _started = false;
246
- let _startupTimeout = null;
247
- let _quietTimer = null;
248
- const _RETAIN_QUIET_MS = 300; // ms of silence after last retained msg → start
246
+ let _startupTimeout = null; // fires if broker never connects
247
+ let _sentinelTimeout = null; // fires if sentinel never arrives after connecting
248
+ let _sentinelValue = null; // unique value for this boot's sentinel
249
249
  const _STARTUP_TIMEOUT_MS = 10000; // ms to wait for broker before starting anyway
250
+ const _SENTINEL_TIMEOUT_MS = 10000; // ms to wait for sentinel after connecting
250
251
 
251
252
  function startOnce(reason) {
252
253
  if (_started) return;
@@ -255,9 +256,9 @@ function startOnce(reason) {
255
256
  clearTimeout(_startupTimeout);
256
257
  _startupTimeout = null;
257
258
  }
258
- if (_quietTimer) {
259
- clearTimeout(_quietTimer);
260
- _quietTimer = null;
259
+ if (_sentinelTimeout) {
260
+ clearTimeout(_sentinelTimeout);
261
+ _sentinelTimeout = null;
261
262
  }
262
263
  if (reason) log.info(reason);
263
264
  start();
@@ -346,10 +347,28 @@ if (config.url) {
346
347
  mqtt.subscribe('#');
347
348
  mqttEventCallbacks.filter((c) => c.event === 'connect').forEach((c) => c.callback());
348
349
 
349
- // Arm the quiet-period timer (only needed on first connect, before scripts start)
350
350
  if (!_started) {
351
- if (_quietTimer) clearTimeout(_quietTimer);
352
- _quietTimer = setTimeout(() => startOnce('mqtt: retained state ready, starting scripts'), _RETAIN_QUIET_MS);
351
+ // Cancel the “broker not connecting” startup timeout — we’re connected.
352
+ if (_startupTimeout) {
353
+ clearTimeout(_startupTimeout);
354
+ _startupTimeout = null;
355
+ }
356
+
357
+ // Publish a non-retained sentinel immediately after subscribing to #.
358
+ // MQTT.js sends packets in order on a single TCP connection, so the
359
+ // broker receives SUBSCRIBE before this PUBLISH. Retained messages are
360
+ // queued for us when SUBSCRIBE is processed; the sentinel is queued
361
+ // afterwards. Receiving the sentinel means all retained messages have
362
+ // been delivered — deterministic, no heuristic timer needed.
363
+ _sentinelValue = String(Date.now());
364
+ mqtt.publish(config.name + '/she-sentinel', _sentinelValue, { retain: false });
365
+ log.debug('mqtt: waiting for retained-state sentinel');
366
+
367
+ // Fallback: if sentinel never arrives (e.g. abnormal broker behaviour)
368
+ _sentinelTimeout = setTimeout(() => {
369
+ log.warn('mqtt sentinel timeout — starting scripts without full retained state');
370
+ startOnce();
371
+ }, _SENTINEL_TIMEOUT_MS);
353
372
  }
354
373
  });
355
374
 
@@ -368,10 +387,12 @@ if (config.url) {
368
387
  mqtt.on('message', (topic, payload, msg) => {
369
388
  _mqttMsgCount++;
370
389
 
371
- // Reset the quiet-period timer while the initial retained-message burst is in flight
372
- if (!_started && msg.retain && _quietTimer) {
373
- clearTimeout(_quietTimer);
374
- _quietTimer = setTimeout(() => startOnce('mqtt: retained state ready, starting scripts'), _RETAIN_QUIET_MS);
390
+ // Sentinel detection: a non-retained message we publish to ourselves right
391
+ // after subscribing to #. When it arrives, all retained messages from the
392
+ // broker have already been delivered and stored.
393
+ if (!_started && _sentinelValue !== null && !msg.retain && topic === config.name + '/she-sentinel' && payload.toString() === _sentinelValue) {
394
+ startOnce('mqtt: retained state ready, starting scripts');
395
+ return; // sentinel is internal — don’t process further
375
396
  }
376
397
 
377
398
  if (shedb.handleMqttMessage(topic, payload)) return;
package/src/web/server.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const express = require('express');
4
4
  const path = require('path');
5
- const { spawnSync, spawn } = require('child_process');
5
+ const { spawn } = require('child_process');
6
6
  const semverCompare = require('semantic-compare');
7
7
  const pkg = require('../../package.json');
8
8
  const { router: configRouter } = require('./config-api');
@@ -96,8 +96,10 @@ app.post('/she/check-update', async (req, res) => {
96
96
  app.post('/she/update', (req, res) => {
97
97
  res.json({ ok: true });
98
98
  setTimeout(() => {
99
- spawnSync('sudo', ['npm', 'install', '-g', 'smart-home-engine'], { stdio: 'inherit' });
100
- _systemdRestart();
99
+ // Use async spawn so the HTTP server (and event loop) remain responsive
100
+ // while npm runs. spawnSync would block all HTTP polling from the UI.
101
+ const child = spawn('sudo', ['npm', 'install', '-g', 'smart-home-engine'], { stdio: 'inherit' });
102
+ child.on('close', () => _systemdRestart());
101
103
  }, 200);
102
104
  });
103
105