@rfranzoi/scrypted-mqtt-securitysystem 1.0.46 → 1.0.48

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',
@@ -34090,8 +34086,8 @@ const DEFAULT_OUTGOING = {
34090
34086
  [sdk_1.SecuritySystemMode.AwayArmed]: 'arm_away',
34091
34087
  [sdk_1.SecuritySystemMode.NightArmed]: 'arm_night',
34092
34088
  };
34093
- /** Parse incoming payload -> final mode (ignore transition states) */
34094
- function payloadToMode(payload) {
34089
+ /** Fallback (non-strict) parser con sinonimi */
34090
+ function payloadToModeLoose(payload) {
34095
34091
  if (payload == null)
34096
34092
  return;
34097
34093
  const p = normalize(payload.toString());
@@ -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;
@@ -34113,7 +34108,7 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34113
34108
  super(nativeId);
34114
34109
  this.cfg = cfg;
34115
34110
  }
34116
- /** setter centralizzato: cambia lo stato SOLO se diverso e poi emette l'evento relativo */
34111
+ /** setter centralizzato + evento */
34117
34112
  setAndEmit(prop, val, iface) {
34118
34113
  const prev = this[prop];
34119
34114
  if (prev === val)
@@ -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,25 @@ 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
+ const n = clamp(parseFloat(raw), 0, 100);
34148
+ if (Number.isFinite(n))
34154
34149
  this.setAndEmit('batteryLevel', n, sdk_1.ScryptedInterface.Battery);
34155
34150
  }
34156
34151
  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);
34152
+ if (truthy(np))
34153
+ this.setAndEmit('batteryLevel', 10, sdk_1.ScryptedInterface.Battery);
34154
+ else if (falsy(np) && this.batteryLevel === undefined)
34155
+ this.setAndEmit('batteryLevel', 100, sdk_1.ScryptedInterface.Battery);
34159
34156
  }
34160
34157
  // primary handled by subclasses
34161
- this.handlePrimary(topic, np, p);
34158
+ this.handlePrimary(topic, np, raw);
34162
34159
  }
34163
34160
  }
34164
34161
  class ContactMqttSensor extends BaseMqttSensor {
34165
- handlePrimary(topic, np, _raw) {
34162
+ handlePrimary(topic, np) {
34166
34163
  if (topic === this.cfg.topics.contact) {
34167
34164
  const v = truthy(np);
34168
- // usa il setter che emette l'evento di EntrySensor
34169
34165
  this.setAndEmit?.('entryOpen', v, sdk_1.ScryptedInterface.EntrySensor);
34170
- // fallback per sicurezza (in alcune minifiche setAndEmit non è visibile):
34171
34166
  if (this.setAndEmit === undefined) {
34172
34167
  if (this.entryOpen !== v) {
34173
34168
  this.entryOpen = v;
@@ -34178,7 +34173,7 @@ class ContactMqttSensor extends BaseMqttSensor {
34178
34173
  }
34179
34174
  }
34180
34175
  class MotionMqttSensor extends BaseMqttSensor {
34181
- handlePrimary(topic, np, _raw) {
34176
+ handlePrimary(topic, np) {
34182
34177
  if (topic === this.cfg.topics.motion) {
34183
34178
  const v = truthy(np);
34184
34179
  this.setAndEmit?.('motionDetected', v, sdk_1.ScryptedInterface.MotionSensor);
@@ -34192,7 +34187,7 @@ class MotionMqttSensor extends BaseMqttSensor {
34192
34187
  }
34193
34188
  }
34194
34189
  class OccupancyMqttSensor extends BaseMqttSensor {
34195
- handlePrimary(topic, np, _raw) {
34190
+ handlePrimary(topic, np) {
34196
34191
  if (topic === this.cfg.topics.occupancy) {
34197
34192
  const v = truthy(np);
34198
34193
  this.setAndEmit?.('occupied', v, sdk_1.ScryptedInterface.OccupancySensor);
@@ -34209,17 +34204,14 @@ class OccupancyMqttSensor extends BaseMqttSensor {
34209
34204
  class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34210
34205
  constructor() {
34211
34206
  super();
34212
- // sensor management
34213
34207
  this.sensorsCfg = [];
34214
34208
  this.devices = new Map();
34215
- // (facoltativo) Imposta il device type in UI
34216
34209
  setTimeout(() => {
34217
34210
  try {
34218
34211
  systemManager.getDeviceById(this.id)?.setType?.(sdk_1.ScryptedDeviceType.SecuritySystem);
34219
34212
  }
34220
34213
  catch { }
34221
34214
  });
34222
- // Default state
34223
34215
  this.securitySystemState = this.securitySystemState || {
34224
34216
  mode: sdk_1.SecuritySystemMode.Disarmed,
34225
34217
  supportedModes: [
@@ -34230,12 +34222,9 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34230
34222
  ],
34231
34223
  };
34232
34224
  this.online = this.online ?? false;
34233
- // Load sensors config and announce devices
34234
34225
  this.loadSensorsFromStorage();
34235
34226
  this.discoverSensors().catch(e => this.console.error('discoverSensors error', e));
34236
- // Connect on start
34237
34227
  this.connectMqtt().catch(e => this.console.error('MQTT connect error:', e));
34238
- // chiusura pulita del client MQTT ai reload/stop del plugin
34239
34228
  try {
34240
34229
  process.once('SIGTERM', () => { try {
34241
34230
  this.client?.end(true);
@@ -34252,6 +34241,60 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34252
34241
  }
34253
34242
  catch { }
34254
34243
  }
34244
+ /** ---- Helpers parsing ---- */
34245
+ parseJsonArray(key, fallback) {
34246
+ try {
34247
+ const raw = (this.storage.getItem(key) || '').trim();
34248
+ if (!raw)
34249
+ return fallback;
34250
+ const arr = JSON.parse(raw);
34251
+ if (!Array.isArray(arr))
34252
+ return fallback;
34253
+ return arr.map((x) => normalize(String(x))).filter(Boolean);
34254
+ }
34255
+ catch {
34256
+ return fallback;
34257
+ }
34258
+ }
34259
+ getStrictTokens() {
34260
+ const target = this.parseJsonArray('targetStateValues', ['armed_home', 'armed_away', 'armed_night', 'disarmed']);
34261
+ const current = this.parseJsonArray('currentStateValues', ['armed_home', 'armed_away', 'armed_night', 'disarmed', 'triggered']);
34262
+ const triggered = this.parseJsonArray('triggeredValues', ['triggered', 'alarm']);
34263
+ const union = new Set([...target, ...current]);
34264
+ return {
34265
+ union,
34266
+ triggered: new Set(triggered),
34267
+ };
34268
+ }
34269
+ useStrict() {
34270
+ return this.storage.getItem('strictParsing') === 'true';
34271
+ }
34272
+ parseIncomingMode(payload) {
34273
+ const p = payload?.toString?.() ?? String(payload ?? '');
34274
+ const np = normalize(p);
34275
+ if (!this.useStrict())
34276
+ return payloadToModeLoose(np);
34277
+ // strict: usa SOLO i token configurati
34278
+ const { union } = this.getStrictTokens();
34279
+ if (union.has('disarmed') && np === 'disarmed')
34280
+ return sdk_1.SecuritySystemMode.Disarmed;
34281
+ if (union.has('armed_home') && np === 'armed_home')
34282
+ return sdk_1.SecuritySystemMode.HomeArmed;
34283
+ if (union.has('armed_away') && np === 'armed_away')
34284
+ return sdk_1.SecuritySystemMode.AwayArmed;
34285
+ if (union.has('armed_night') && np === 'armed_night')
34286
+ return sdk_1.SecuritySystemMode.NightArmed;
34287
+ // transitori/altro: ignora
34288
+ return undefined;
34289
+ }
34290
+ isTriggeredToken(np) {
34291
+ if (this.useStrict()) {
34292
+ const { triggered } = this.getStrictTokens();
34293
+ return triggered.has(np);
34294
+ }
34295
+ // loose
34296
+ return np === 'triggered' || np === 'alarm';
34297
+ }
34255
34298
  // helpers persistenza
34256
34299
  saveSensorsToStorage() {
34257
34300
  try {
@@ -34279,6 +34322,11 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34279
34322
  { group: 'Alarm Topics', key: 'topicOnline', title: 'Get Online (subscribe)', placeholder: 'paradox/interface/availability', value: this.storage.getItem('topicOnline') || '' },
34280
34323
  { group: 'Publish Options', key: 'qos', title: 'QoS', type: 'integer', value: parseInt(this.storage.getItem('qos') || '0') },
34281
34324
  { group: 'Publish Options', key: 'retain', title: 'Retain', type: 'boolean', value: this.storage.getItem('retain') === 'true' },
34325
+ // --- Parsing / State tokens ---
34326
+ { group: 'Parsing / State tokens', key: 'strictParsing', title: 'Use strict tokens (disable synonyms)', type: 'boolean', value: this.storage.getItem('strictParsing') === 'true' },
34327
+ { group: 'Parsing / State tokens', key: 'targetStateValues', title: 'Accepted Target State Values (JSON array)', placeholder: '["armed_home","armed_away","armed_night","disarmed"]', value: this.storage.getItem('targetStateValues') || '["armed_home","armed_away","armed_night","disarmed"]' },
34328
+ { group: 'Parsing / State tokens', key: 'currentStateValues', title: 'Accepted Current State Values (JSON array)', placeholder: '["armed_home","armed_away","armed_night","disarmed","triggered"]', value: this.storage.getItem('currentStateValues') || '["armed_home","armed_away","armed_night","disarmed","triggered"]' },
34329
+ { group: 'Parsing / State tokens', key: 'triggeredValues', title: 'Triggered tokens (JSON array)', placeholder: '["triggered","alarm"]', value: this.storage.getItem('triggeredValues') || '["triggered","alarm"]' },
34282
34330
  ];
34283
34331
  // ---- UI Add Sensor ----
34284
34332
  out.push({ group: 'Add Sensor', key: 'new.id', title: 'New Sensor ID', placeholder: 'porta-ingresso', value: this.storage.getItem('new.id') || '' }, { group: 'Add Sensor', key: 'new.name', title: 'Name', placeholder: 'Porta Ingresso', value: this.storage.getItem('new.name') || '' }, { group: 'Add Sensor', key: 'new.kind', title: 'Type', value: this.storage.getItem('new.kind') || 'contact', choices: ['contact', 'motion', 'occupancy'] }, { group: 'Add Sensor', key: 'new.create', title: 'Create sensor', type: 'boolean', description: 'Fill the fields above and toggle this on to create the sensor. After creation, restart this plugin to see the accessory listed below. To show it in HomeKit, restart the HomeKit plugin as well.' });
@@ -34286,7 +34334,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34286
34334
  for (const cfg of this.sensorsCfg) {
34287
34335
  const gid = `Sensor: ${cfg.name} [${cfg.id}]`;
34288
34336
  out.push({ group: gid, key: `sensor.${cfg.id}.name`, title: 'Name', value: cfg.name }, { group: gid, key: `sensor.${cfg.id}.kind`, title: 'Type', value: cfg.kind, choices: ['contact', 'motion', 'occupancy'] });
34289
- // primary per tipo
34290
34337
  if (cfg.kind === 'contact') {
34291
34338
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.contact`, title: 'Contact State Topic', value: cfg.topics.contact || '', placeholder: 'paradox/states/zones/XYZ/open' });
34292
34339
  }
@@ -34296,15 +34343,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34296
34343
  else {
34297
34344
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.occupancy`, title: 'Occupancy Detected Topic', value: cfg.topics.occupancy || '', placeholder: 'paradox/states/zones/XYZ/open' });
34298
34345
  }
34299
- // extra opzionali
34300
34346
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.batteryLevel`, title: 'Battery Level Topic (0..100)', value: cfg.topics.batteryLevel || '' }, { group: gid, key: `sensor.${cfg.id}.topic.lowBattery`, title: 'Low Battery Topic (bool)', value: cfg.topics.lowBattery || '' }, { group: gid, key: `sensor.${cfg.id}.topic.tamper`, title: 'Tamper Topic', value: cfg.topics.tamper || '' }, { group: gid, key: `sensor.${cfg.id}.topic.online`, title: 'Online Topic', value: cfg.topics.online || '' }, { group: gid, key: `sensor.${cfg.id}.remove`, title: 'Remove sensor', type: 'boolean' });
34301
34347
  }
34302
34348
  return out;
34303
34349
  }
34304
34350
  async putSetting(key, value) {
34305
- // salva sempre nella storage la value del campo (così resta in UI)
34306
34351
  this.storage.setItem(key, String(value));
34307
- // --- Add Sensor workflow ---
34352
+ // Add Sensor
34308
34353
  if (key === 'new.create' && String(value) === 'true') {
34309
34354
  const id = (this.storage.getItem('new.id') || '').trim();
34310
34355
  const name = (this.storage.getItem('new.name') || '').trim() || id;
@@ -34319,7 +34364,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34319
34364
  }
34320
34365
  this.sensorsCfg.push({ id, name, kind, topics: {} });
34321
34366
  this.saveSensorsToStorage();
34322
- // pulisci i campi "new.*"
34323
34367
  this.storage.removeItem('new.id');
34324
34368
  this.storage.removeItem('new.name');
34325
34369
  this.storage.removeItem('new.kind');
@@ -34328,7 +34372,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34328
34372
  await this.connectMqtt(true);
34329
34373
  return;
34330
34374
  }
34331
- // --- Edit/Remove sensore esistente ---
34375
+ // Edit/Remove sensore
34332
34376
  const m = key.match(/^sensor\.([^\.]+)\.(.+)$/);
34333
34377
  if (m) {
34334
34378
  const sid = m[1];
@@ -34339,7 +34383,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34339
34383
  return;
34340
34384
  }
34341
34385
  if (prop === 'remove' && String(value) === 'true') {
34342
- // elimina
34343
34386
  this.sensorsCfg = this.sensorsCfg.filter(s => s.id !== sid);
34344
34387
  this.saveSensorsToStorage();
34345
34388
  try {
@@ -34347,18 +34390,15 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34347
34390
  deviceManager.onDeviceRemoved?.(`sensor:${sid}`);
34348
34391
  }
34349
34392
  catch { }
34350
- // pulisci flag
34351
34393
  this.storage.removeItem(key);
34352
34394
  await this.discoverSensors();
34353
34395
  await this.connectMqtt(true);
34354
34396
  return;
34355
34397
  }
34356
- if (prop === 'name') {
34398
+ if (prop === 'name')
34357
34399
  cfg.name = String(value);
34358
- }
34359
- else if (prop === 'kind') {
34400
+ else if (prop === 'kind')
34360
34401
  cfg.kind = String(value);
34361
- }
34362
34402
  else if (prop.startsWith('topic.')) {
34363
34403
  const tk = prop.substring('topic.'.length);
34364
34404
  cfg.topics[tk] = String(value).trim();
@@ -34368,9 +34408,8 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34368
34408
  await this.connectMqtt(true);
34369
34409
  return;
34370
34410
  }
34371
- // --- Altro (MQTT / Alarm settings) ---
34411
+ // altro
34372
34412
  if (key === 'sensorsJson') {
34373
- // non più mostrato, ma se presente da vecchie versioni
34374
34413
  this.loadSensorsFromStorage();
34375
34414
  await this.discoverSensors();
34376
34415
  await this.connectMqtt(true);
@@ -34380,15 +34419,12 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34380
34419
  }
34381
34420
  }
34382
34421
  /** ---- DeviceProvider ---- */
34383
- async getDevice(nativeId) {
34384
- return this.devices.get(nativeId);
34385
- }
34422
+ async getDevice(nativeId) { return this.devices.get(nativeId); }
34386
34423
  async releaseDevice(_id, nativeId) {
34387
34424
  try {
34388
34425
  const dev = this.devices.get(nativeId);
34389
- if (dev) {
34426
+ if (dev)
34390
34427
  this.devices.delete(nativeId);
34391
- }
34392
34428
  try {
34393
34429
  deviceManager.onDeviceRemoved?.(nativeId);
34394
34430
  }
@@ -34402,7 +34438,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34402
34438
  try {
34403
34439
  const raw = this.storage.getItem('sensorsJson') || '[]';
34404
34440
  const parsed = JSON.parse(raw);
34405
- // sanitize
34406
34441
  this.sensorsCfg = (parsed || []).filter(x => x && x.id && x.name && x.kind && x.topics);
34407
34442
  }
34408
34443
  catch (e) {
@@ -34412,25 +34447,21 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34412
34447
  }
34413
34448
  /** ===== discoverSensors: annuncia PRIMA, istanzia DOPO ===== */
34414
34449
  async discoverSensors() {
34415
- // 1) Prepara i manifest (niente istanze qui)
34450
+ // 1) Manifests
34416
34451
  const manifests = this.sensorsCfg.map(cfg => {
34417
34452
  const nativeId = `sensor:${cfg.id}`;
34418
34453
  const t = cfg.topics || {};
34419
34454
  const interfaces = [sdk_1.ScryptedInterface.Online];
34420
- // Tamper solo se c'è un topic tamper
34421
34455
  if (t.tamper)
34422
34456
  interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
34423
- // Interfaccia primaria
34424
34457
  if (cfg.kind === 'contact')
34425
34458
  interfaces.unshift(sdk_1.ScryptedInterface.EntrySensor);
34426
34459
  else if (cfg.kind === 'motion')
34427
34460
  interfaces.unshift(sdk_1.ScryptedInterface.MotionSensor);
34428
34461
  else
34429
34462
  interfaces.unshift(sdk_1.ScryptedInterface.OccupancySensor);
34430
- // Battery solo se previsto
34431
- if (t.batteryLevel || t.lowBattery) {
34463
+ if ((t.batteryLevel && t.batteryLevel.trim()) || (t.lowBattery && t.lowBattery.trim()))
34432
34464
  interfaces.push(sdk_1.ScryptedInterface.Battery);
34433
- }
34434
34465
  return { nativeId, name: cfg.name, type: sdk_1.ScryptedDeviceType.Sensor, interfaces };
34435
34466
  });
34436
34467
  // 2) Annuncio
@@ -34445,7 +34476,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34445
34476
  this.console.log('Annunciato:', m.nativeId);
34446
34477
  }
34447
34478
  }
34448
- // 3) Istanzia/aggiorna DOPO l’annuncio
34479
+ // 3) Istanzia/aggiorna
34449
34480
  for (const cfg of this.sensorsCfg) {
34450
34481
  const nativeId = `sensor:${cfg.id}`;
34451
34482
  let dev = this.devices.get(nativeId);
@@ -34461,17 +34492,9 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34461
34492
  else {
34462
34493
  dev.cfg = cfg;
34463
34494
  }
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
- }
34495
+ // niente default batteria qui
34473
34496
  }
34474
- // 4) Rimuovi quelli spariti
34497
+ // 4) cleanup
34475
34498
  const announced = new Set(manifests.map(m => m.nativeId));
34476
34499
  for (const [nativeId] of this.devices) {
34477
34500
  if (!announced.has(nativeId)) {
@@ -34492,13 +34515,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34492
34515
  const clientId = this.storage.getItem('clientId') || 'scrypted-paradox';
34493
34516
  const tls = this.storage.getItem('tls') === 'true';
34494
34517
  const rejectUnauthorized = this.storage.getItem('rejectUnauthorized') !== 'false';
34495
- const opts = {
34496
- clientId,
34497
- username,
34498
- password,
34499
- clean: true,
34500
- reconnectPeriod: 3000,
34501
- };
34518
+ const opts = { clientId, username, password, clean: true, reconnectPeriod: 3000 };
34502
34519
  if (tls) {
34503
34520
  opts.protocol = 'mqtts';
34504
34521
  opts.rejectUnauthorized = rejectUnauthorized;
@@ -34507,17 +34524,15 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34507
34524
  }
34508
34525
  collectAllSubscriptions() {
34509
34526
  const subs = new Set();
34510
- // alarm
34511
34527
  for (const k of ['topicGetTarget', 'topicGetCurrent', 'topicTamper', 'topicOnline']) {
34512
34528
  const v = this.storage.getItem(k);
34513
34529
  if (v)
34514
34530
  subs.add(v);
34515
34531
  }
34516
- // sensors
34517
34532
  for (const s of this.sensorsCfg) {
34518
34533
  const t = s.topics || {};
34519
34534
  [t.contact, t.motion, t.occupancy, t.batteryLevel, t.lowBattery, t.tamper, t.online]
34520
- .filter(Boolean)
34535
+ .filter((x) => !!x && String(x).trim().length > 0)
34521
34536
  .forEach(x => subs.add(String(x)));
34522
34537
  }
34523
34538
  return Array.from(subs);
@@ -34532,13 +34547,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34532
34547
  this.client.end(true);
34533
34548
  }
34534
34549
  catch { }
34550
+ ;
34535
34551
  this.client = undefined;
34536
34552
  }
34537
34553
  const { url, opts } = this.getMqttOptions();
34538
34554
  this.console.log(`Connecting MQTT ${url} ...`);
34539
34555
  const client = mqtt_1.default.connect(url, opts);
34540
34556
  this.client = client;
34541
- // cache alarm topics for fast compare
34542
34557
  const tTarget = this.storage.getItem('topicGetTarget') || '';
34543
34558
  const tCurrent = this.storage.getItem('topicGetCurrent') || '';
34544
34559
  const tTamper = this.storage.getItem('topicTamper') || '';
@@ -34550,12 +34565,9 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34550
34565
  this.onDeviceEvent(sdk_1.ScryptedInterface.Online, true);
34551
34566
  }
34552
34567
  catch { }
34553
- if (subs.length) {
34554
- client.subscribe(subs, { qos: 0 }, (err) => {
34555
- if (err)
34556
- this.console.error('subscribe error', err);
34557
- });
34558
- }
34568
+ if (subs.length)
34569
+ client.subscribe(subs, { qos: 0 }, (err) => { if (err)
34570
+ this.console.error('subscribe error', err); });
34559
34571
  });
34560
34572
  client.on('reconnect', () => this.console.log('MQTT reconnecting...'));
34561
34573
  client.on('close', () => {
@@ -34571,7 +34583,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34571
34583
  try {
34572
34584
  const p = payload?.toString() ?? '';
34573
34585
  const np = normalize(p);
34574
- // ---- Alarm handling ----
34575
34586
  if (topic === tOnline) {
34576
34587
  if (truthy(np) || np === 'online') {
34577
34588
  this.online = true;
@@ -34608,8 +34619,8 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34608
34619
  return;
34609
34620
  }
34610
34621
  if (topic === tCurrent) {
34611
- const mode = payloadToMode(payload);
34612
- const isAlarm = ['alarm', 'triggered'].includes(np);
34622
+ const mode = this.parseIncomingMode(payload);
34623
+ const isAlarm = this.isTriggeredToken(np);
34613
34624
  const current = this.securitySystemState || { mode: sdk_1.SecuritySystemMode.Disarmed };
34614
34625
  const newState = {
34615
34626
  mode: mode ?? current.mode,
@@ -34629,14 +34640,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34629
34640
  return;
34630
34641
  }
34631
34642
  if (topic === tTarget) {
34632
- this.pendingTarget = payloadToMode(payload);
34643
+ this.pendingTarget = this.parseIncomingMode(payload);
34633
34644
  this.console.log('Target state reported:', p, '->', this.pendingTarget);
34634
34645
  return;
34635
34646
  }
34636
34647
  // ---- Sensor dispatch ----
34637
- for (const dev of this.devices.values()) {
34648
+ for (const dev of this.devices.values())
34638
34649
  dev.handleMqtt(topic, payload);
34639
- }
34640
34650
  }
34641
34651
  catch (e) {
34642
34652
  this.console.error('MQTT message handler error', e);
@@ -34661,7 +34671,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34661
34671
  async armSecuritySystem(mode) {
34662
34672
  const payload = this.getOutgoing(mode);
34663
34673
  this.console.log('armSecuritySystem', mode, '->', payload);
34664
- this.pendingTarget = mode; // memorizza target, ma NON cambiare il current
34674
+ this.pendingTarget = mode;
34665
34675
  this.publishSetTarget(payload);
34666
34676
  }
34667
34677
  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.48",
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",