@rfranzoi/scrypted-mqtt-securitysystem 1.0.46 → 1.0.47

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.
@@ -34077,12 +34077,8 @@ function falsy(v) {
34077
34077
  const s = v.toString().trim().toLowerCase();
34078
34078
  return s === '0' || s === 'false' || s === 'offline' || s === 'no' || s === 'off';
34079
34079
  }
34080
- function normalize(s) {
34081
- return (s || '').trim().toLowerCase();
34082
- }
34083
- function clamp(n, min, max) {
34084
- return Math.max(min, Math.min(max, n));
34085
- }
34080
+ function normalize(s) { return (s || '').trim().toLowerCase(); }
34081
+ function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }
34086
34082
  /** SecuritySystem outgoing defaults (PAI-like) */
34087
34083
  const DEFAULT_OUTGOING = {
34088
34084
  [sdk_1.SecuritySystemMode.Disarmed]: 'disarm',
@@ -34103,7 +34099,6 @@ function payloadToMode(payload) {
34103
34099
  return sdk_1.SecuritySystemMode.AwayArmed;
34104
34100
  if (['arm_night', 'night', 'armed_night', 'sleep', 'arm_sleep', 'armed_sleep'].includes(p))
34105
34101
  return sdk_1.SecuritySystemMode.NightArmed;
34106
- // transitori: non cambiano il mode
34107
34102
  if (['entry_delay', 'exit_delay', 'pending', 'arming', 'disarming'].includes(p))
34108
34103
  return undefined;
34109
34104
  return undefined;
@@ -34128,8 +34123,8 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34128
34123
  }
34129
34124
  /** Called by parent on each MQTT message */
34130
34125
  handleMqtt(topic, payload) {
34131
- const p = payload?.toString() ?? '';
34132
- const np = normalize(p);
34126
+ const raw = payload?.toString() ?? '';
34127
+ const np = normalize(raw);
34133
34128
  // online
34134
34129
  if (topic === this.cfg.topics.online) {
34135
34130
  if (truthy(np) || np === 'online')
@@ -34149,25 +34144,29 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34149
34144
  }
34150
34145
  // battery
34151
34146
  if (topic === this.cfg.topics.batteryLevel) {
34152
- const n = clamp(parseFloat(p), 0, 100);
34153
- if (isFinite(n))
34147
+ // aggiorna solo con numeri validi 0..100
34148
+ const n = clamp(parseFloat(raw), 0, 100);
34149
+ if (Number.isFinite(n))
34154
34150
  this.setAndEmit('batteryLevel', n, sdk_1.ScryptedInterface.Battery);
34155
34151
  }
34156
34152
  else if (topic === this.cfg.topics.lowBattery && !this.cfg.topics.batteryLevel) {
34157
- // Solo se abbiamo lowBattery (booleano) ma NON batteryLevel:
34158
- this.setAndEmit('batteryLevel', truthy(np) ? 10 : 100, sdk_1.ScryptedInterface.Battery);
34153
+ // LowBattery booleano: quando TRUE -> 10%; quando FALSE non forzare nulla, lascia il valore precedente
34154
+ if (truthy(np))
34155
+ this.setAndEmit('batteryLevel', 10, sdk_1.ScryptedInterface.Battery);
34156
+ else if (falsy(np) && this.batteryLevel === undefined) {
34157
+ // se è il primo messaggio ed è "ok", metti 100 (altrimenti resta undefined)
34158
+ this.setAndEmit('batteryLevel', 100, sdk_1.ScryptedInterface.Battery);
34159
+ }
34159
34160
  }
34160
34161
  // primary handled by subclasses
34161
- this.handlePrimary(topic, np, p);
34162
+ this.handlePrimary(topic, np, raw);
34162
34163
  }
34163
34164
  }
34164
34165
  class ContactMqttSensor extends BaseMqttSensor {
34165
- handlePrimary(topic, np, _raw) {
34166
+ handlePrimary(topic, np) {
34166
34167
  if (topic === this.cfg.topics.contact) {
34167
34168
  const v = truthy(np);
34168
- // usa il setter che emette l'evento di EntrySensor
34169
34169
  this.setAndEmit?.('entryOpen', v, sdk_1.ScryptedInterface.EntrySensor);
34170
- // fallback per sicurezza (in alcune minifiche setAndEmit non è visibile):
34171
34170
  if (this.setAndEmit === undefined) {
34172
34171
  if (this.entryOpen !== v) {
34173
34172
  this.entryOpen = v;
@@ -34178,7 +34177,7 @@ class ContactMqttSensor extends BaseMqttSensor {
34178
34177
  }
34179
34178
  }
34180
34179
  class MotionMqttSensor extends BaseMqttSensor {
34181
- handlePrimary(topic, np, _raw) {
34180
+ handlePrimary(topic, np) {
34182
34181
  if (topic === this.cfg.topics.motion) {
34183
34182
  const v = truthy(np);
34184
34183
  this.setAndEmit?.('motionDetected', v, sdk_1.ScryptedInterface.MotionSensor);
@@ -34192,7 +34191,7 @@ class MotionMqttSensor extends BaseMqttSensor {
34192
34191
  }
34193
34192
  }
34194
34193
  class OccupancyMqttSensor extends BaseMqttSensor {
34195
- handlePrimary(topic, np, _raw) {
34194
+ handlePrimary(topic, np) {
34196
34195
  if (topic === this.cfg.topics.occupancy) {
34197
34196
  const v = truthy(np);
34198
34197
  this.setAndEmit?.('occupied', v, sdk_1.ScryptedInterface.OccupancySensor);
@@ -34302,7 +34301,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34302
34301
  return out;
34303
34302
  }
34304
34303
  async putSetting(key, value) {
34305
- // salva sempre nella storage la value del campo (così resta in UI)
34306
34304
  this.storage.setItem(key, String(value));
34307
34305
  // --- Add Sensor workflow ---
34308
34306
  if (key === 'new.create' && String(value) === 'true') {
@@ -34319,7 +34317,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34319
34317
  }
34320
34318
  this.sensorsCfg.push({ id, name, kind, topics: {} });
34321
34319
  this.saveSensorsToStorage();
34322
- // pulisci i campi "new.*"
34323
34320
  this.storage.removeItem('new.id');
34324
34321
  this.storage.removeItem('new.name');
34325
34322
  this.storage.removeItem('new.kind');
@@ -34339,7 +34336,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34339
34336
  return;
34340
34337
  }
34341
34338
  if (prop === 'remove' && String(value) === 'true') {
34342
- // elimina
34343
34339
  this.sensorsCfg = this.sensorsCfg.filter(s => s.id !== sid);
34344
34340
  this.saveSensorsToStorage();
34345
34341
  try {
@@ -34347,18 +34343,15 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34347
34343
  deviceManager.onDeviceRemoved?.(`sensor:${sid}`);
34348
34344
  }
34349
34345
  catch { }
34350
- // pulisci flag
34351
34346
  this.storage.removeItem(key);
34352
34347
  await this.discoverSensors();
34353
34348
  await this.connectMqtt(true);
34354
34349
  return;
34355
34350
  }
34356
- if (prop === 'name') {
34351
+ if (prop === 'name')
34357
34352
  cfg.name = String(value);
34358
- }
34359
- else if (prop === 'kind') {
34353
+ else if (prop === 'kind')
34360
34354
  cfg.kind = String(value);
34361
- }
34362
34355
  else if (prop.startsWith('topic.')) {
34363
34356
  const tk = prop.substring('topic.'.length);
34364
34357
  cfg.topics[tk] = String(value).trim();
@@ -34370,7 +34363,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34370
34363
  }
34371
34364
  // --- Altro (MQTT / Alarm settings) ---
34372
34365
  if (key === 'sensorsJson') {
34373
- // non più mostrato, ma se presente da vecchie versioni
34374
34366
  this.loadSensorsFromStorage();
34375
34367
  await this.discoverSensors();
34376
34368
  await this.connectMqtt(true);
@@ -34386,9 +34378,8 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34386
34378
  async releaseDevice(_id, nativeId) {
34387
34379
  try {
34388
34380
  const dev = this.devices.get(nativeId);
34389
- if (dev) {
34381
+ if (dev)
34390
34382
  this.devices.delete(nativeId);
34391
- }
34392
34383
  try {
34393
34384
  deviceManager.onDeviceRemoved?.(nativeId);
34394
34385
  }
@@ -34402,7 +34393,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34402
34393
  try {
34403
34394
  const raw = this.storage.getItem('sensorsJson') || '[]';
34404
34395
  const parsed = JSON.parse(raw);
34405
- // sanitize
34406
34396
  this.sensorsCfg = (parsed || []).filter(x => x && x.id && x.name && x.kind && x.topics);
34407
34397
  }
34408
34398
  catch (e) {
@@ -34412,23 +34402,21 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34412
34402
  }
34413
34403
  /** ===== discoverSensors: annuncia PRIMA, istanzia DOPO ===== */
34414
34404
  async discoverSensors() {
34415
- // 1) Prepara i manifest (niente istanze qui)
34405
+ // 1) Manifests
34416
34406
  const manifests = this.sensorsCfg.map(cfg => {
34417
34407
  const nativeId = `sensor:${cfg.id}`;
34418
34408
  const t = cfg.topics || {};
34419
34409
  const interfaces = [sdk_1.ScryptedInterface.Online];
34420
- // Tamper solo se c'è un topic tamper
34421
34410
  if (t.tamper)
34422
34411
  interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
34423
- // Interfaccia primaria
34424
34412
  if (cfg.kind === 'contact')
34425
34413
  interfaces.unshift(sdk_1.ScryptedInterface.EntrySensor);
34426
34414
  else if (cfg.kind === 'motion')
34427
34415
  interfaces.unshift(sdk_1.ScryptedInterface.MotionSensor);
34428
34416
  else
34429
34417
  interfaces.unshift(sdk_1.ScryptedInterface.OccupancySensor);
34430
- // Battery solo se previsto
34431
- if (t.batteryLevel || t.lowBattery) {
34418
+ // Espone Battery SOLTANTO se è configurato almeno un topic batteria (stringa non vuota)
34419
+ if ((t.batteryLevel && t.batteryLevel.trim()) || (t.lowBattery && t.lowBattery.trim())) {
34432
34420
  interfaces.push(sdk_1.ScryptedInterface.Battery);
34433
34421
  }
34434
34422
  return { nativeId, name: cfg.name, type: sdk_1.ScryptedDeviceType.Sensor, interfaces };
@@ -34461,15 +34449,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34461
34449
  else {
34462
34450
  dev.cfg = cfg;
34463
34451
  }
34464
- // Default “OK” se abbiamo Battery ma nessun valore ancora ricevuto
34465
- const hasBattery = !!(cfg.topics.batteryLevel || cfg.topics.lowBattery);
34466
- if (hasBattery && dev.batteryLevel === undefined) {
34467
- dev.batteryLevel = 100;
34468
- try {
34469
- dev.onDeviceEvent(sdk_1.ScryptedInterface.Battery, 100);
34470
- }
34471
- catch { }
34472
- }
34452
+ // <<< RIMOSSO il default batteryLevel=100: niente valore finché non arriva un messaggio >>>
34473
34453
  }
34474
34454
  // 4) Rimuovi quelli spariti
34475
34455
  const announced = new Set(manifests.map(m => m.nativeId));
@@ -34517,7 +34497,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34517
34497
  for (const s of this.sensorsCfg) {
34518
34498
  const t = s.topics || {};
34519
34499
  [t.contact, t.motion, t.occupancy, t.batteryLevel, t.lowBattery, t.tamper, t.online]
34520
- .filter(Boolean)
34500
+ .filter((x) => !!x && String(x).trim().length > 0)
34521
34501
  .forEach(x => subs.add(String(x)));
34522
34502
  }
34523
34503
  return Array.from(subs);
@@ -34661,7 +34641,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34661
34641
  async armSecuritySystem(mode) {
34662
34642
  const payload = this.getOutgoing(mode);
34663
34643
  this.console.log('armSecuritySystem', mode, '->', payload);
34664
- this.pendingTarget = mode; // memorizza target, ma NON cambiare il current
34644
+ this.pendingTarget = mode;
34665
34645
  this.publishSetTarget(payload);
34666
34646
  }
34667
34647
  async disarmSecuritySystem() {
package/dist/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rfranzoi/scrypted-mqtt-securitysystem",
3
- "version": "1.0.46",
3
+ "version": "1.0.47",
4
4
  "description": "Scrypted plugin: Paradox Security System via MQTT (PAI/PAI-MQTT style).",
5
5
  "license": "MIT",
6
6
  "main": "dist/main.nodejs.js",