@rfranzoi/scrypted-mqtt-securitysystem 1.0.20 → 1.0.22

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.
@@ -34112,7 +34112,7 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34112
34112
  constructor(nativeId, cfg) {
34113
34113
  super(nativeId);
34114
34114
  this.cfg = cfg;
34115
- // ⚠️ Non toccare stati nel costruttore (discovery non ancora completata)
34115
+ // non impostare stati qui: l'annuncio device deve avvenire prima
34116
34116
  }
34117
34117
  /** Called by parent on each MQTT message */
34118
34118
  handleMqtt(topic, payload) {
@@ -34141,7 +34141,9 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34141
34141
  this.batteryLevel = n;
34142
34142
  }
34143
34143
  else if (topic === this.cfg.topics.lowBattery && !this.cfg.topics.batteryLevel) {
34144
- // sintetizza se non c'è batteryLevel
34144
+ // Solo se abbiamo lowBattery (booleano) ma NON batteryLevel:
34145
+ // True -> 10% (warning)
34146
+ // False -> 100% (ok)
34145
34147
  this.batteryLevel = truthy(np) ? 10 : 100;
34146
34148
  }
34147
34149
  // primary handled by subclasses
@@ -34225,9 +34227,18 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34225
34227
  }
34226
34228
  catch { }
34227
34229
  }
34230
+ // helpers persistenza
34231
+ saveSensorsToStorage() {
34232
+ try {
34233
+ this.storage.setItem('sensorsJson', JSON.stringify(this.sensorsCfg));
34234
+ }
34235
+ catch (e) {
34236
+ this.console.error('saveSensorsToStorage error', e);
34237
+ }
34238
+ }
34228
34239
  /** ---- Settings UI ---- */
34229
34240
  async getSettings() {
34230
- return [
34241
+ const out = [
34231
34242
  // MQTT Core
34232
34243
  { group: 'MQTT', key: 'brokerUrl', title: 'Broker URL', placeholder: 'mqtt://127.0.0.1:1883', value: this.storage.getItem('brokerUrl') || 'mqtt://127.0.0.1:1883' },
34233
34244
  { group: 'MQTT', key: 'username', title: 'Username', type: 'string', value: this.storage.getItem('username') || '' },
@@ -34243,24 +34254,98 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34243
34254
  { group: 'Alarm Topics', key: 'topicOnline', title: 'Get Online (subscribe)', placeholder: 'paradox/interface/availability', value: this.storage.getItem('topicOnline') || '' },
34244
34255
  { group: 'Publish Options', key: 'qos', title: 'QoS', type: 'integer', value: parseInt(this.storage.getItem('qos') || '0') },
34245
34256
  { group: 'Publish Options', key: 'retain', title: 'Retain', type: 'boolean', value: this.storage.getItem('retain') === 'true' },
34246
- { group: 'Outgoing Payloads', key: 'payloadDisarm', title: 'Payload Disarm', value: this.storage.getItem('payloadDisarm') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.Disarmed] },
34247
- { group: 'Outgoing Payloads', key: 'payloadHome', title: 'Payload HomeArmed', value: this.storage.getItem('payloadHome') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.HomeArmed] },
34248
- { group: 'Outgoing Payloads', key: 'payloadAway', title: 'Payload AwayArmed', value: this.storage.getItem('payloadAway') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.AwayArmed] },
34249
- { group: 'Outgoing Payloads', key: 'payloadNight', title: 'Payload NightArmed', value: this.storage.getItem('payloadNight') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.NightArmed] },
34250
- // Sensors config (JSON)
34251
- {
34252
- group: 'Sensors',
34253
- key: 'sensorsJson',
34254
- title: 'Sensors JSON (contact/motion/occupancy)',
34255
- description: 'Definisci i sensori e i topic MQTT (vedi README). Incolla JSON; le interruzioni di riga sono accettate.',
34256
- type: 'string',
34257
- value: this.storage.getItem('sensorsJson') || '[\n {\n "id": "front-door",\n "name": "Front Door",\n "kind": "contact",\n "topics": { "contact": "SYSTEM/zones/front/contact" }\n }\n]'
34258
- },
34259
34257
  ];
34258
+ // ---- UI Add Sensor ----
34259
+ 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: 'Compila i campi sopra e attiva per creare.' });
34260
+ // ---- UI per sensori esistenti ----
34261
+ for (const cfg of this.sensorsCfg) {
34262
+ const gid = `Sensor: ${cfg.name} [${cfg.id}]`;
34263
+ 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'] });
34264
+ // primary per tipo
34265
+ if (cfg.kind === 'contact') {
34266
+ out.push({ group: gid, key: `sensor.${cfg.id}.topic.contact`, title: 'Contact State Topic', value: cfg.topics.contact || '', placeholder: 'paradox/states/zones/XYZ/open' });
34267
+ }
34268
+ else if (cfg.kind === 'motion') {
34269
+ out.push({ group: gid, key: `sensor.${cfg.id}.topic.motion`, title: 'Motion Detected Topic', value: cfg.topics.motion || '', placeholder: 'paradox/states/zones/XYZ/open' });
34270
+ }
34271
+ else {
34272
+ out.push({ group: gid, key: `sensor.${cfg.id}.topic.occupancy`, title: 'Occupancy Detected Topic', value: cfg.topics.occupancy || '', placeholder: 'paradox/states/zones/XYZ/open' });
34273
+ }
34274
+ // extra opzionali
34275
+ 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' });
34276
+ }
34277
+ return out;
34260
34278
  }
34261
34279
  async putSetting(key, value) {
34280
+ // salva sempre nella storage la value del campo (così resta in UI)
34262
34281
  this.storage.setItem(key, String(value));
34282
+ // --- Add Sensor workflow ---
34283
+ if (key === 'new.create' && String(value) === 'true') {
34284
+ const id = (this.storage.getItem('new.id') || '').trim();
34285
+ const name = (this.storage.getItem('new.name') || '').trim() || id;
34286
+ const kind = (this.storage.getItem('new.kind') || 'contact').trim();
34287
+ if (!id) {
34288
+ this.console.warn('Create sensor: id mancante');
34289
+ return;
34290
+ }
34291
+ if (this.sensorsCfg.find(s => s.id === id)) {
34292
+ this.console.warn('Create sensor: id già esistente');
34293
+ return;
34294
+ }
34295
+ this.sensorsCfg.push({ id, name, kind, topics: {} });
34296
+ this.saveSensorsToStorage();
34297
+ // pulisci i campi "new.*"
34298
+ this.storage.removeItem('new.id');
34299
+ this.storage.removeItem('new.name');
34300
+ this.storage.removeItem('new.kind');
34301
+ this.storage.removeItem('new.create');
34302
+ await this.discoverSensors();
34303
+ await this.connectMqtt(true);
34304
+ return;
34305
+ }
34306
+ // --- Edit/Remove sensore esistente ---
34307
+ const m = key.match(/^sensor\.([^\.]+)\.(.+)$/);
34308
+ if (m) {
34309
+ const sid = m[1];
34310
+ const prop = m[2];
34311
+ const cfg = this.sensorsCfg.find(s => s.id === sid);
34312
+ if (!cfg) {
34313
+ this.console.warn('putSetting: sensor non trovato', sid);
34314
+ return;
34315
+ }
34316
+ if (prop === 'remove' && String(value) === 'true') {
34317
+ // elimina
34318
+ this.sensorsCfg = this.sensorsCfg.filter(s => s.id !== sid);
34319
+ this.saveSensorsToStorage();
34320
+ try {
34321
+ this.devices.delete(`sensor:${sid}`);
34322
+ deviceManager.onDeviceRemoved?.(`sensor:${sid}`);
34323
+ }
34324
+ catch { }
34325
+ // pulisci flag
34326
+ this.storage.removeItem(key);
34327
+ await this.discoverSensors();
34328
+ await this.connectMqtt(true);
34329
+ return;
34330
+ }
34331
+ if (prop === 'name') {
34332
+ cfg.name = String(value);
34333
+ }
34334
+ else if (prop === 'kind') {
34335
+ cfg.kind = String(value);
34336
+ }
34337
+ else if (prop.startsWith('topic.')) {
34338
+ const tk = prop.substring('topic.'.length);
34339
+ cfg.topics[tk] = String(value).trim();
34340
+ }
34341
+ this.saveSensorsToStorage();
34342
+ await this.discoverSensors();
34343
+ await this.connectMqtt(true);
34344
+ return;
34345
+ }
34346
+ // --- Altro (MQTT / Alarm settings) ---
34263
34347
  if (key === 'sensorsJson') {
34348
+ // non più mostrato, ma se presente da vecchie versioni
34264
34349
  this.loadSensorsFromStorage();
34265
34350
  await this.discoverSensors();
34266
34351
  await this.connectMqtt(true);
@@ -34305,30 +34390,25 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34305
34390
  // 1) Prepara i manifest (niente istanze qui)
34306
34391
  const manifests = this.sensorsCfg.map(cfg => {
34307
34392
  const nativeId = `sensor:${cfg.id}`;
34308
- // interfacce dinamiche
34309
34393
  const t = cfg.topics || {};
34310
- const interfaces = [
34311
- sdk_1.ScryptedInterface.Online,
34312
- sdk_1.ScryptedInterface.TamperSensor,
34313
- ];
34394
+ const interfaces = [sdk_1.ScryptedInterface.Online];
34395
+ // Tamper solo se c'è un topic tamper
34396
+ if (t.tamper)
34397
+ interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
34398
+ // Interfaccia primaria
34314
34399
  if (cfg.kind === 'contact')
34315
34400
  interfaces.unshift(sdk_1.ScryptedInterface.EntrySensor);
34316
34401
  else if (cfg.kind === 'motion')
34317
34402
  interfaces.unshift(sdk_1.ScryptedInterface.MotionSensor);
34318
34403
  else
34319
34404
  interfaces.unshift(sdk_1.ScryptedInterface.OccupancySensor);
34320
- // aggiungi Battery solo se previsto
34405
+ // Battery solo se previsto
34321
34406
  if (t.batteryLevel || t.lowBattery) {
34322
34407
  interfaces.push(sdk_1.ScryptedInterface.Battery);
34323
34408
  }
34324
- return {
34325
- nativeId,
34326
- name: cfg.name,
34327
- type: sdk_1.ScryptedDeviceType.Sensor,
34328
- interfaces,
34329
- };
34409
+ return { nativeId, name: cfg.name, type: sdk_1.ScryptedDeviceType.Sensor, interfaces };
34330
34410
  });
34331
- // 2) Annuncia i device
34411
+ // 2) Annuncio
34332
34412
  const dmAny = deviceManager;
34333
34413
  if (typeof dmAny.onDevicesChanged === 'function') {
34334
34414
  dmAny.onDevicesChanged({ devices: manifests });
@@ -34340,7 +34420,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34340
34420
  this.console.log('Annunciato:', m.nativeId);
34341
34421
  }
34342
34422
  }
34343
- // 3) Istanzia/aggiorna le classi DOPO l’annuncio
34423
+ // 3) Istanzia/aggiorna DOPO l’annuncio
34344
34424
  for (const cfg of this.sensorsCfg) {
34345
34425
  const nativeId = `sensor:${cfg.id}`;
34346
34426
  let dev = this.devices.get(nativeId);
@@ -34356,6 +34436,11 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34356
34436
  else {
34357
34437
  dev.cfg = cfg;
34358
34438
  }
34439
+ // Default “OK” se abbiamo Battery ma nessun valore ancora ricevuto
34440
+ const hasBattery = !!(cfg.topics.batteryLevel || cfg.topics.lowBattery);
34441
+ if (hasBattery && dev.batteryLevel === undefined) {
34442
+ dev.batteryLevel = 100;
34443
+ }
34359
34444
  }
34360
34445
  // 4) Rimuovi quelli spariti
34361
34446
  const announced = new Set(manifests.map(m => m.nativeId));
@@ -34408,7 +34493,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34408
34493
  }
34409
34494
  return Array.from(subs);
34410
34495
  }
34411
- async connectMqtt(reconnect = false) {
34496
+ async connectMqtt(_reconnect = false) {
34412
34497
  const subs = this.collectAllSubscriptions();
34413
34498
  if (!subs.length && !this.storage.getItem('topicSetTarget')) {
34414
34499
  this.console.warn('Configura almeno un topic nelle impostazioni.');
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.20",
3
+ "version": "1.0.22",
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",