@rfranzoi/scrypted-mqtt-securitysystem 1.0.21 → 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 impostare stati qui: il discovery non è ancora completato
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,7 @@ 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
- // Solo se abbiamo il topic lowBattery e NON c'è un batteryLevel numerico:
34144
+ // Solo se abbiamo lowBattery (booleano) ma NON batteryLevel:
34145
34145
  // True -> 10% (warning)
34146
34146
  // False -> 100% (ok)
34147
34147
  this.batteryLevel = truthy(np) ? 10 : 100;
@@ -34227,9 +34227,18 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34227
34227
  }
34228
34228
  catch { }
34229
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
+ }
34230
34239
  /** ---- Settings UI ---- */
34231
34240
  async getSettings() {
34232
- return [
34241
+ const out = [
34233
34242
  // MQTT Core
34234
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' },
34235
34244
  { group: 'MQTT', key: 'username', title: 'Username', type: 'string', value: this.storage.getItem('username') || '' },
@@ -34245,24 +34254,98 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34245
34254
  { group: 'Alarm Topics', key: 'topicOnline', title: 'Get Online (subscribe)', placeholder: 'paradox/interface/availability', value: this.storage.getItem('topicOnline') || '' },
34246
34255
  { group: 'Publish Options', key: 'qos', title: 'QoS', type: 'integer', value: parseInt(this.storage.getItem('qos') || '0') },
34247
34256
  { group: 'Publish Options', key: 'retain', title: 'Retain', type: 'boolean', value: this.storage.getItem('retain') === 'true' },
34248
- { group: 'Outgoing Payloads', key: 'payloadDisarm', title: 'Payload Disarm', value: this.storage.getItem('payloadDisarm') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.Disarmed] },
34249
- { group: 'Outgoing Payloads', key: 'payloadHome', title: 'Payload HomeArmed', value: this.storage.getItem('payloadHome') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.HomeArmed] },
34250
- { group: 'Outgoing Payloads', key: 'payloadAway', title: 'Payload AwayArmed', value: this.storage.getItem('payloadAway') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.AwayArmed] },
34251
- { group: 'Outgoing Payloads', key: 'payloadNight', title: 'Payload NightArmed', value: this.storage.getItem('payloadNight') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.NightArmed] },
34252
- // Sensors config (JSON)
34253
- {
34254
- group: 'Sensors',
34255
- key: 'sensorsJson',
34256
- title: 'Sensors JSON (contact/motion/occupancy)',
34257
- description: 'Definisci i sensori e i topic MQTT (vedi README). Incolla JSON; le interruzioni di riga sono accettate.',
34258
- type: 'string',
34259
- 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]'
34260
- },
34261
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;
34262
34278
  }
34263
34279
  async putSetting(key, value) {
34280
+ // salva sempre nella storage la value del campo (così resta in UI)
34264
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) ---
34265
34347
  if (key === 'sensorsJson') {
34348
+ // non più mostrato, ma se presente da vecchie versioni
34266
34349
  this.loadSensorsFromStorage();
34267
34350
  await this.discoverSensors();
34268
34351
  await this.connectMqtt(true);
@@ -34308,9 +34391,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34308
34391
  const manifests = this.sensorsCfg.map(cfg => {
34309
34392
  const nativeId = `sensor:${cfg.id}`;
34310
34393
  const t = cfg.topics || {};
34311
- const interfaces = [
34312
- sdk_1.ScryptedInterface.Online,
34313
- ];
34394
+ const interfaces = [sdk_1.ScryptedInterface.Online];
34314
34395
  // Tamper solo se c'è un topic tamper
34315
34396
  if (t.tamper)
34316
34397
  interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
@@ -34325,14 +34406,9 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34325
34406
  if (t.batteryLevel || t.lowBattery) {
34326
34407
  interfaces.push(sdk_1.ScryptedInterface.Battery);
34327
34408
  }
34328
- return {
34329
- nativeId,
34330
- name: cfg.name,
34331
- type: sdk_1.ScryptedDeviceType.Sensor,
34332
- interfaces,
34333
- };
34409
+ return { nativeId, name: cfg.name, type: sdk_1.ScryptedDeviceType.Sensor, interfaces };
34334
34410
  });
34335
- // 2) Annuncia i device
34411
+ // 2) Annuncio
34336
34412
  const dmAny = deviceManager;
34337
34413
  if (typeof dmAny.onDevicesChanged === 'function') {
34338
34414
  dmAny.onDevicesChanged({ devices: manifests });
@@ -34344,7 +34420,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34344
34420
  this.console.log('Annunciato:', m.nativeId);
34345
34421
  }
34346
34422
  }
34347
- // 3) Istanzia/aggiorna le classi DOPO l’annuncio
34423
+ // 3) Istanzia/aggiorna DOPO l’annuncio
34348
34424
  for (const cfg of this.sensorsCfg) {
34349
34425
  const nativeId = `sensor:${cfg.id}`;
34350
34426
  let dev = this.devices.get(nativeId);
@@ -34360,7 +34436,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34360
34436
  else {
34361
34437
  dev.cfg = cfg;
34362
34438
  }
34363
- // Default “OK” se abbiamo Battery ma nessun valore ancora ricevuto
34439
+ // Default “OK” se abbiamo Battery ma nessun valore ancora ricevuto
34364
34440
  const hasBattery = !!(cfg.topics.batteryLevel || cfg.topics.lowBattery);
34365
34441
  if (hasBattery && dev.batteryLevel === undefined) {
34366
34442
  dev.batteryLevel = 100;
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.21",
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",