@rfranzoi/scrypted-mqtt-securitysystem 1.0.48 → 1.0.50

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.
@@ -34064,6 +34064,15 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
34064
34064
  const sdk_1 = __importStar(__webpack_require__(/*! @scrypted/sdk */ "./node_modules/@scrypted/sdk/dist/src/index.js"));
34065
34065
  const mqtt_1 = __importDefault(__webpack_require__(/*! mqtt */ "./node_modules/mqtt/build/index.js"));
34066
34066
  const { systemManager, deviceManager } = sdk_1.default;
34067
+ /** ---------- RUNTIME FLAGS (aggiornati da Settings) ---------- */
34068
+ const RUNTIME = {
34069
+ logSensors: false,
34070
+ logMqttAll: false,
34071
+ };
34072
+ function updateRuntimeFromStorage(get) {
34073
+ RUNTIME.logSensors = (get('logSensors') || '') === 'true';
34074
+ RUNTIME.logMqttAll = (get('logMqttAll') || '') === 'true';
34075
+ }
34067
34076
  /** utils */
34068
34077
  function truthy(v) {
34069
34078
  if (!v)
@@ -34086,7 +34095,7 @@ const DEFAULT_OUTGOING = {
34086
34095
  [sdk_1.SecuritySystemMode.AwayArmed]: 'arm_away',
34087
34096
  [sdk_1.SecuritySystemMode.NightArmed]: 'arm_night',
34088
34097
  };
34089
- /** Fallback (non-strict) parser con sinonimi */
34098
+ /** Parser loose con sinonimi (usato se strict=OFF) */
34090
34099
  function payloadToModeLoose(payload) {
34091
34100
  if (payload == null)
34092
34101
  return;
@@ -34108,8 +34117,8 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34108
34117
  super(nativeId);
34109
34118
  this.cfg = cfg;
34110
34119
  }
34111
- /** setter centralizzato + evento */
34112
- setAndEmit(prop, val, iface) {
34120
+ /** setter centralizzato + evento + (log opzionale) */
34121
+ setAndEmit(prop, val, iface, logContext) {
34113
34122
  const prev = this[prop];
34114
34123
  if (prev === val)
34115
34124
  return;
@@ -34120,6 +34129,11 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34120
34129
  catch (e) {
34121
34130
  this.console?.warn?.('onDeviceEvent error', iface, e);
34122
34131
  }
34132
+ if (RUNTIME.logSensors) {
34133
+ const label = logContext?.propLabel || prop;
34134
+ const extra = logContext?.topic ? ` (${logContext.topic}="${logContext.raw ?? ''}")` : '';
34135
+ this.console?.log?.(`[Sensor] ${this.cfg.name} [${this.cfg.id}] ${label} -> ${JSON.stringify(val)}${extra}`);
34136
+ }
34123
34137
  }
34124
34138
  /** Called by parent on each MQTT message */
34125
34139
  handleMqtt(topic, payload) {
@@ -34128,75 +34142,58 @@ class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
34128
34142
  // online
34129
34143
  if (topic === this.cfg.topics.online) {
34130
34144
  if (truthy(np) || np === 'online')
34131
- this.setAndEmit('online', true, sdk_1.ScryptedInterface.Online);
34145
+ this.setAndEmit('online', true, sdk_1.ScryptedInterface.Online, { topic, raw, propLabel: 'online' });
34132
34146
  if (falsy(np) || np === 'offline')
34133
- this.setAndEmit('online', false, sdk_1.ScryptedInterface.Online);
34147
+ this.setAndEmit('online', false, sdk_1.ScryptedInterface.Online, { topic, raw, propLabel: 'online' });
34134
34148
  }
34135
34149
  // tamper
34136
34150
  if (topic === this.cfg.topics.tamper) {
34137
34151
  if (truthy(np) || ['tamper', 'intrusion', 'cover', 'motion', 'magnetic'].includes(np)) {
34138
34152
  const value = ['cover', 'intrusion', 'motion', 'magnetic'].find(x => x === np) || true;
34139
- this.setAndEmit('tampered', value, sdk_1.ScryptedInterface.TamperSensor);
34153
+ this.setAndEmit('tampered', value, sdk_1.ScryptedInterface.TamperSensor, { topic, raw, propLabel: 'tampered' });
34140
34154
  }
34141
34155
  else if (falsy(np)) {
34142
- this.setAndEmit('tampered', false, sdk_1.ScryptedInterface.TamperSensor);
34156
+ this.setAndEmit('tampered', false, sdk_1.ScryptedInterface.TamperSensor, { topic, raw, propLabel: 'tampered' });
34143
34157
  }
34144
34158
  }
34145
34159
  // battery
34146
34160
  if (topic === this.cfg.topics.batteryLevel) {
34147
34161
  const n = clamp(parseFloat(raw), 0, 100);
34148
34162
  if (Number.isFinite(n))
34149
- this.setAndEmit('batteryLevel', n, sdk_1.ScryptedInterface.Battery);
34163
+ this.setAndEmit('batteryLevel', n, sdk_1.ScryptedInterface.Battery, { topic, raw, propLabel: 'batteryLevel' });
34150
34164
  }
34151
34165
  else if (topic === this.cfg.topics.lowBattery && !this.cfg.topics.batteryLevel) {
34166
+ // solo se abbiamo lowBattery (bool) ma NON batteryLevel
34152
34167
  if (truthy(np))
34153
- this.setAndEmit('batteryLevel', 10, sdk_1.ScryptedInterface.Battery);
34168
+ this.setAndEmit('batteryLevel', 10, sdk_1.ScryptedInterface.Battery, { topic, raw, propLabel: 'batteryLevel (low)' });
34154
34169
  else if (falsy(np) && this.batteryLevel === undefined)
34155
- this.setAndEmit('batteryLevel', 100, sdk_1.ScryptedInterface.Battery);
34170
+ this.setAndEmit('batteryLevel', 100, sdk_1.ScryptedInterface.Battery, { topic, raw, propLabel: 'batteryLevel (ok)' });
34156
34171
  }
34157
34172
  // primary handled by subclasses
34158
34173
  this.handlePrimary(topic, np, raw);
34159
34174
  }
34160
34175
  }
34161
34176
  class ContactMqttSensor extends BaseMqttSensor {
34162
- handlePrimary(topic, np) {
34177
+ handlePrimary(topic, np, raw) {
34163
34178
  if (topic === this.cfg.topics.contact) {
34164
34179
  const v = truthy(np);
34165
- this.setAndEmit?.('entryOpen', v, sdk_1.ScryptedInterface.EntrySensor);
34166
- if (this.setAndEmit === undefined) {
34167
- if (this.entryOpen !== v) {
34168
- this.entryOpen = v;
34169
- this.onDeviceEvent(sdk_1.ScryptedInterface.EntrySensor, v);
34170
- }
34171
- }
34180
+ this.setAndEmit('entryOpen', v, sdk_1.ScryptedInterface.EntrySensor, { topic, raw, propLabel: 'entryOpen' });
34172
34181
  }
34173
34182
  }
34174
34183
  }
34175
34184
  class MotionMqttSensor extends BaseMqttSensor {
34176
- handlePrimary(topic, np) {
34185
+ handlePrimary(topic, np, raw) {
34177
34186
  if (topic === this.cfg.topics.motion) {
34178
34187
  const v = truthy(np);
34179
- this.setAndEmit?.('motionDetected', v, sdk_1.ScryptedInterface.MotionSensor);
34180
- if (this.setAndEmit === undefined) {
34181
- if (this.motionDetected !== v) {
34182
- this.motionDetected = v;
34183
- this.onDeviceEvent(sdk_1.ScryptedInterface.MotionSensor, v);
34184
- }
34185
- }
34188
+ this.setAndEmit('motionDetected', v, sdk_1.ScryptedInterface.MotionSensor, { topic, raw, propLabel: 'motionDetected' });
34186
34189
  }
34187
34190
  }
34188
34191
  }
34189
34192
  class OccupancyMqttSensor extends BaseMqttSensor {
34190
- handlePrimary(topic, np) {
34193
+ handlePrimary(topic, np, raw) {
34191
34194
  if (topic === this.cfg.topics.occupancy) {
34192
34195
  const v = truthy(np);
34193
- this.setAndEmit?.('occupied', v, sdk_1.ScryptedInterface.OccupancySensor);
34194
- if (this.setAndEmit === undefined) {
34195
- if (this.occupied !== v) {
34196
- this.occupied = v;
34197
- this.onDeviceEvent(sdk_1.ScryptedInterface.OccupancySensor, v);
34198
- }
34199
- }
34196
+ this.setAndEmit('occupied', v, sdk_1.ScryptedInterface.OccupancySensor, { topic, raw, propLabel: 'occupied' });
34200
34197
  }
34201
34198
  }
34202
34199
  }
@@ -34204,14 +34201,19 @@ class OccupancyMqttSensor extends BaseMqttSensor {
34204
34201
  class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34205
34202
  constructor() {
34206
34203
  super();
34204
+ // sensor management
34207
34205
  this.sensorsCfg = [];
34208
34206
  this.devices = new Map();
34207
+ // carica flag runtime
34208
+ updateRuntimeFromStorage((k) => this.storage.getItem(k) || '');
34209
+ // (facoltativo) Imposta il device type in UI
34209
34210
  setTimeout(() => {
34210
34211
  try {
34211
34212
  systemManager.getDeviceById(this.id)?.setType?.(sdk_1.ScryptedDeviceType.SecuritySystem);
34212
34213
  }
34213
34214
  catch { }
34214
34215
  });
34216
+ // Default state
34215
34217
  this.securitySystemState = this.securitySystemState || {
34216
34218
  mode: sdk_1.SecuritySystemMode.Disarmed,
34217
34219
  supportedModes: [
@@ -34222,9 +34224,12 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34222
34224
  ],
34223
34225
  };
34224
34226
  this.online = this.online ?? false;
34227
+ // Load sensors config and announce devices
34225
34228
  this.loadSensorsFromStorage();
34226
34229
  this.discoverSensors().catch(e => this.console.error('discoverSensors error', e));
34230
+ // Connect on start
34227
34231
  this.connectMqtt().catch(e => this.console.error('MQTT connect error:', e));
34232
+ // chiusura pulita del client MQTT ai reload/stop del plugin
34228
34233
  try {
34229
34234
  process.once('SIGTERM', () => { try {
34230
34235
  this.client?.end(true);
@@ -34241,7 +34246,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34241
34246
  }
34242
34247
  catch { }
34243
34248
  }
34244
- /** ---- Helpers parsing ---- */
34249
+ // ====== Strict parsing helpers ======
34245
34250
  parseJsonArray(key, fallback) {
34246
34251
  try {
34247
34252
  const raw = (this.storage.getItem(key) || '').trim();
@@ -34256,45 +34261,42 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34256
34261
  return fallback;
34257
34262
  }
34258
34263
  }
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
34264
  useStrict() {
34270
34265
  return this.storage.getItem('strictParsing') === 'true';
34271
34266
  }
34272
34267
  parseIncomingMode(payload) {
34273
- const p = payload?.toString?.() ?? String(payload ?? '');
34274
- const np = normalize(p);
34268
+ const np = normalize(payload?.toString?.() ?? String(payload ?? ''));
34275
34269
  if (!this.useStrict())
34276
34270
  return payloadToModeLoose(np);
34277
- // strict: usa SOLO i token configurati
34278
- const { union } = this.getStrictTokens();
34279
- if (union.has('disarmed') && np === 'disarmed')
34271
+ const currentVals = new Set(this.parseJsonArray('currentStateValues', ['armed_home', 'armed_away', 'armed_night', 'disarmed', 'triggered']));
34272
+ if (currentVals.has('disarmed') && np === 'disarmed')
34280
34273
  return sdk_1.SecuritySystemMode.Disarmed;
34281
- if (union.has('armed_home') && np === 'armed_home')
34274
+ if (currentVals.has('armed_home') && np === 'armed_home')
34282
34275
  return sdk_1.SecuritySystemMode.HomeArmed;
34283
- if (union.has('armed_away') && np === 'armed_away')
34276
+ if (currentVals.has('armed_away') && np === 'armed_away')
34284
34277
  return sdk_1.SecuritySystemMode.AwayArmed;
34285
- if (union.has('armed_night') && np === 'armed_night')
34278
+ if (currentVals.has('armed_night') && np === 'armed_night')
34286
34279
  return sdk_1.SecuritySystemMode.NightArmed;
34287
- // transitori/altro: ignora
34288
34280
  return undefined;
34289
34281
  }
34290
34282
  isTriggeredToken(np) {
34291
34283
  if (this.useStrict()) {
34292
- const { triggered } = this.getStrictTokens();
34284
+ const triggered = new Set(this.parseJsonArray('triggeredValues', ['triggered', 'alarm']));
34293
34285
  return triggered.has(np);
34294
34286
  }
34295
- // loose
34296
34287
  return np === 'triggered' || np === 'alarm';
34297
34288
  }
34289
+ /** Token di publish preferito (strict=ON usa targetStateValues) */
34290
+ preferredTokenForMode(mode) {
34291
+ const t = this.parseJsonArray('targetStateValues', ['armed_home', 'armed_away', 'armed_night', 'disarmed']);
34292
+ const pick = (...cands) => cands.find(c => t.includes(c));
34293
+ switch (mode) {
34294
+ case sdk_1.SecuritySystemMode.Disarmed: return pick('disarmed', 'disarm') || 'disarmed';
34295
+ case sdk_1.SecuritySystemMode.HomeArmed: return pick('armed_home', 'arm_home', 'home', 'stay') || 'armed_home';
34296
+ case sdk_1.SecuritySystemMode.AwayArmed: return pick('armed_away', 'arm_away', 'away') || 'armed_away';
34297
+ case sdk_1.SecuritySystemMode.NightArmed: return pick('armed_night', 'arm_night', 'night', 'sleep') || 'armed_night';
34298
+ }
34299
+ }
34298
34300
  // helpers persistenza
34299
34301
  saveSensorsToStorage() {
34300
34302
  try {
@@ -34327,6 +34329,14 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34327
34329
  { 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
34330
  { 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
34331
  { group: 'Parsing / State tokens', key: 'triggeredValues', title: 'Triggered tokens (JSON array)', placeholder: '["triggered","alarm"]', value: this.storage.getItem('triggeredValues') || '["triggered","alarm"]' },
34332
+ // --- Publish Payloads (override) ---
34333
+ { group: 'Publish Payloads (override)', key: 'payloadDisarm', title: 'Payload for Disarm', placeholder: 'disarmed', value: this.storage.getItem('payloadDisarm') || '', description: 'Se vuoto: usa targetStateValues (strict ON) o i default arm_*/disarm (strict OFF).' },
34334
+ { group: 'Publish Payloads (override)', key: 'payloadHome', title: 'Payload for Home Armed', placeholder: 'armed_home', value: this.storage.getItem('payloadHome') || '' },
34335
+ { group: 'Publish Payloads (override)', key: 'payloadAway', title: 'Payload for Away Armed', placeholder: 'armed_away', value: this.storage.getItem('payloadAway') || '' },
34336
+ { group: 'Publish Payloads (override)', key: 'payloadNight', title: 'Payload for Night Armed', placeholder: 'armed_night', value: this.storage.getItem('payloadNight') || '' },
34337
+ // --- Logging ---
34338
+ { group: 'Logging', key: 'logSensors', title: 'Log sensor state changes', type: 'boolean', value: this.storage.getItem('logSensors') === 'true' },
34339
+ { group: 'Logging', key: 'logMqttAll', title: 'Log ALL MQTT messages', type: 'boolean', value: this.storage.getItem('logMqttAll') === 'true', description: 'Attenzione: molto verboso.' },
34330
34340
  ];
34331
34341
  // ---- UI Add Sensor ----
34332
34342
  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.' });
@@ -34334,22 +34344,22 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34334
34344
  for (const cfg of this.sensorsCfg) {
34335
34345
  const gid = `Sensor: ${cfg.name} [${cfg.id}]`;
34336
34346
  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'] });
34337
- if (cfg.kind === 'contact') {
34347
+ if (cfg.kind === 'contact')
34338
34348
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.contact`, title: 'Contact State Topic', value: cfg.topics.contact || '', placeholder: 'paradox/states/zones/XYZ/open' });
34339
- }
34340
- else if (cfg.kind === 'motion') {
34349
+ else if (cfg.kind === 'motion')
34341
34350
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.motion`, title: 'Motion Detected Topic', value: cfg.topics.motion || '', placeholder: 'paradox/states/zones/XYZ/open' });
34342
- }
34343
- else {
34351
+ else
34344
34352
  out.push({ group: gid, key: `sensor.${cfg.id}.topic.occupancy`, title: 'Occupancy Detected Topic', value: cfg.topics.occupancy || '', placeholder: 'paradox/states/zones/XYZ/open' });
34345
- }
34346
34353
  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' });
34347
34354
  }
34348
34355
  return out;
34349
34356
  }
34350
34357
  async putSetting(key, value) {
34358
+ // salva sempre nella storage la value del campo (così resta in UI)
34351
34359
  this.storage.setItem(key, String(value));
34352
- // Add Sensor
34360
+ // aggiorna i flag runtime ogni volta
34361
+ updateRuntimeFromStorage((k) => this.storage.getItem(k) || '');
34362
+ // --- Add Sensor workflow ---
34353
34363
  if (key === 'new.create' && String(value) === 'true') {
34354
34364
  const id = (this.storage.getItem('new.id') || '').trim();
34355
34365
  const name = (this.storage.getItem('new.name') || '').trim() || id;
@@ -34364,6 +34374,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34364
34374
  }
34365
34375
  this.sensorsCfg.push({ id, name, kind, topics: {} });
34366
34376
  this.saveSensorsToStorage();
34377
+ // pulizia campi "new.*"
34367
34378
  this.storage.removeItem('new.id');
34368
34379
  this.storage.removeItem('new.name');
34369
34380
  this.storage.removeItem('new.kind');
@@ -34372,7 +34383,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34372
34383
  await this.connectMqtt(true);
34373
34384
  return;
34374
34385
  }
34375
- // Edit/Remove sensore
34386
+ // --- Edit/Remove sensore esistente ---
34376
34387
  const m = key.match(/^sensor\.([^\.]+)\.(.+)$/);
34377
34388
  if (m) {
34378
34389
  const sid = m[1];
@@ -34383,6 +34394,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34383
34394
  return;
34384
34395
  }
34385
34396
  if (prop === 'remove' && String(value) === 'true') {
34397
+ // elimina
34386
34398
  this.sensorsCfg = this.sensorsCfg.filter(s => s.id !== sid);
34387
34399
  this.saveSensorsToStorage();
34388
34400
  try {
@@ -34390,6 +34402,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34390
34402
  deviceManager.onDeviceRemoved?.(`sensor:${sid}`);
34391
34403
  }
34392
34404
  catch { }
34405
+ // pulisci flag
34393
34406
  this.storage.removeItem(key);
34394
34407
  await this.discoverSensors();
34395
34408
  await this.connectMqtt(true);
@@ -34408,7 +34421,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34408
34421
  await this.connectMqtt(true);
34409
34422
  return;
34410
34423
  }
34411
- // altro
34424
+ // --- Altro (MQTT / Alarm settings / parsing / payloads / logging) ---
34412
34425
  if (key === 'sensorsJson') {
34413
34426
  this.loadSensorsFromStorage();
34414
34427
  await this.discoverSensors();
@@ -34438,6 +34451,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34438
34451
  try {
34439
34452
  const raw = this.storage.getItem('sensorsJson') || '[]';
34440
34453
  const parsed = JSON.parse(raw);
34454
+ // sanitize
34441
34455
  this.sensorsCfg = (parsed || []).filter(x => x && x.id && x.name && x.kind && x.topics);
34442
34456
  }
34443
34457
  catch (e) {
@@ -34447,24 +34461,25 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34447
34461
  }
34448
34462
  /** ===== discoverSensors: annuncia PRIMA, istanzia DOPO ===== */
34449
34463
  async discoverSensors() {
34450
- // 1) Manifests
34464
+ // 1) manifest
34451
34465
  const manifests = this.sensorsCfg.map(cfg => {
34452
34466
  const nativeId = `sensor:${cfg.id}`;
34453
34467
  const t = cfg.topics || {};
34454
34468
  const interfaces = [sdk_1.ScryptedInterface.Online];
34455
- if (t.tamper)
34456
- interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
34457
34469
  if (cfg.kind === 'contact')
34458
34470
  interfaces.unshift(sdk_1.ScryptedInterface.EntrySensor);
34459
34471
  else if (cfg.kind === 'motion')
34460
34472
  interfaces.unshift(sdk_1.ScryptedInterface.MotionSensor);
34461
34473
  else
34462
34474
  interfaces.unshift(sdk_1.ScryptedInterface.OccupancySensor);
34475
+ if (t.tamper)
34476
+ interfaces.push(sdk_1.ScryptedInterface.TamperSensor);
34477
+ // Battery solo se definita davvero
34463
34478
  if ((t.batteryLevel && t.batteryLevel.trim()) || (t.lowBattery && t.lowBattery.trim()))
34464
34479
  interfaces.push(sdk_1.ScryptedInterface.Battery);
34465
34480
  return { nativeId, name: cfg.name, type: sdk_1.ScryptedDeviceType.Sensor, interfaces };
34466
34481
  });
34467
- // 2) Annuncio
34482
+ // 2) annuncio
34468
34483
  const dmAny = deviceManager;
34469
34484
  if (typeof dmAny.onDevicesChanged === 'function') {
34470
34485
  dmAny.onDevicesChanged({ devices: manifests });
@@ -34476,7 +34491,7 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34476
34491
  this.console.log('Annunciato:', m.nativeId);
34477
34492
  }
34478
34493
  }
34479
- // 3) Istanzia/aggiorna
34494
+ // 3) istanzia/aggiorna
34480
34495
  for (const cfg of this.sensorsCfg) {
34481
34496
  const nativeId = `sensor:${cfg.id}`;
34482
34497
  let dev = this.devices.get(nativeId);
@@ -34492,7 +34507,16 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34492
34507
  else {
34493
34508
  dev.cfg = cfg;
34494
34509
  }
34495
- // niente default batteria qui
34510
+ // default OK per battery se non abbiamo ancora un valore
34511
+ const hasBattery = !!(cfg.topics.batteryLevel && cfg.topics.batteryLevel.trim())
34512
+ || !!(cfg.topics.lowBattery && cfg.topics.lowBattery.trim());
34513
+ if (hasBattery && dev.batteryLevel === undefined) {
34514
+ dev.batteryLevel = 100;
34515
+ try {
34516
+ dev.onDeviceEvent(sdk_1.ScryptedInterface.Battery, 100);
34517
+ }
34518
+ catch { }
34519
+ }
34496
34520
  }
34497
34521
  // 4) cleanup
34498
34522
  const announced = new Set(manifests.map(m => m.nativeId));
@@ -34515,7 +34539,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34515
34539
  const clientId = this.storage.getItem('clientId') || 'scrypted-paradox';
34516
34540
  const tls = this.storage.getItem('tls') === 'true';
34517
34541
  const rejectUnauthorized = this.storage.getItem('rejectUnauthorized') !== 'false';
34518
- const opts = { clientId, username, password, clean: true, reconnectPeriod: 3000 };
34542
+ const opts = {
34543
+ clientId,
34544
+ username,
34545
+ password,
34546
+ clean: true,
34547
+ reconnectPeriod: 3000,
34548
+ };
34519
34549
  if (tls) {
34520
34550
  opts.protocol = 'mqtts';
34521
34551
  opts.rejectUnauthorized = rejectUnauthorized;
@@ -34524,11 +34554,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34524
34554
  }
34525
34555
  collectAllSubscriptions() {
34526
34556
  const subs = new Set();
34557
+ // alarm
34527
34558
  for (const k of ['topicGetTarget', 'topicGetCurrent', 'topicTamper', 'topicOnline']) {
34528
34559
  const v = this.storage.getItem(k);
34529
34560
  if (v)
34530
34561
  subs.add(v);
34531
34562
  }
34563
+ // sensors
34532
34564
  for (const s of this.sensorsCfg) {
34533
34565
  const t = s.topics || {};
34534
34566
  [t.contact, t.motion, t.occupancy, t.batteryLevel, t.lowBattery, t.tamper, t.online]
@@ -34547,13 +34579,13 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34547
34579
  this.client.end(true);
34548
34580
  }
34549
34581
  catch { }
34550
- ;
34551
34582
  this.client = undefined;
34552
34583
  }
34553
34584
  const { url, opts } = this.getMqttOptions();
34554
34585
  this.console.log(`Connecting MQTT ${url} ...`);
34555
34586
  const client = mqtt_1.default.connect(url, opts);
34556
34587
  this.client = client;
34588
+ // cache alarm topics
34557
34589
  const tTarget = this.storage.getItem('topicGetTarget') || '';
34558
34590
  const tCurrent = this.storage.getItem('topicGetCurrent') || '';
34559
34591
  const tTamper = this.storage.getItem('topicTamper') || '';
@@ -34583,6 +34615,9 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34583
34615
  try {
34584
34616
  const p = payload?.toString() ?? '';
34585
34617
  const np = normalize(p);
34618
+ if (RUNTIME.logMqttAll)
34619
+ this.console.log(`[MQTT] ${topic} -> "${p}"`);
34620
+ // ---- Alarm handling ----
34586
34621
  if (topic === tOnline) {
34587
34622
  if (truthy(np) || np === 'online') {
34588
34623
  this.online = true;
@@ -34637,11 +34672,14 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34637
34672
  this.onDeviceEvent(sdk_1.ScryptedInterface.SecuritySystem, newState);
34638
34673
  }
34639
34674
  catch { }
34675
+ if (RUNTIME.logSensors)
34676
+ this.console.log(`[Alarm] current: mode=${newState.mode} triggered=${!!newState.triggered} (${topic}="${p}")`);
34640
34677
  return;
34641
34678
  }
34642
34679
  if (topic === tTarget) {
34643
34680
  this.pendingTarget = this.parseIncomingMode(payload);
34644
- this.console.log('Target state reported:', p, '->', this.pendingTarget);
34681
+ if (RUNTIME.logSensors)
34682
+ this.console.log(`[Alarm] target reported: "${p}" -> ${this.pendingTarget}`);
34645
34683
  return;
34646
34684
  }
34647
34685
  // ---- Sensor dispatch ----
@@ -34666,8 +34704,25 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34666
34704
  this.client.publish(topic, payload, { qos, retain }, (err) => {
34667
34705
  if (err)
34668
34706
  this.console.error('publish error', err);
34707
+ else if (RUNTIME.logSensors)
34708
+ this.console.log(`[Alarm] published target "${payload}" to ${topic}`);
34669
34709
  });
34670
34710
  }
34711
+ /** Payload publish: override → strict tokens → default arm_* */
34712
+ getOutgoing(mode) {
34713
+ const overrides = {
34714
+ [sdk_1.SecuritySystemMode.Disarmed]: this.storage.getItem('payloadDisarm') || null,
34715
+ [sdk_1.SecuritySystemMode.HomeArmed]: this.storage.getItem('payloadHome') || null,
34716
+ [sdk_1.SecuritySystemMode.AwayArmed]: this.storage.getItem('payloadAway') || null,
34717
+ [sdk_1.SecuritySystemMode.NightArmed]: this.storage.getItem('payloadNight') || null,
34718
+ };
34719
+ const override = overrides[mode];
34720
+ if (override && override.trim().length)
34721
+ return override.trim();
34722
+ if (this.useStrict())
34723
+ return this.preferredTokenForMode(mode);
34724
+ return DEFAULT_OUTGOING[mode];
34725
+ }
34671
34726
  async armSecuritySystem(mode) {
34672
34727
  const payload = this.getOutgoing(mode);
34673
34728
  this.console.log('armSecuritySystem', mode, '->', payload);
@@ -34680,15 +34735,6 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
34680
34735
  this.pendingTarget = sdk_1.SecuritySystemMode.Disarmed;
34681
34736
  this.publishSetTarget(payload);
34682
34737
  }
34683
- getOutgoing(mode) {
34684
- const map = {
34685
- [sdk_1.SecuritySystemMode.Disarmed]: this.storage.getItem('payloadDisarm') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.Disarmed],
34686
- [sdk_1.SecuritySystemMode.HomeArmed]: this.storage.getItem('payloadHome') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.HomeArmed],
34687
- [sdk_1.SecuritySystemMode.AwayArmed]: this.storage.getItem('payloadAway') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.AwayArmed],
34688
- [sdk_1.SecuritySystemMode.NightArmed]: this.storage.getItem('payloadNight') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.NightArmed],
34689
- };
34690
- return map[mode];
34691
- }
34692
34738
  }
34693
34739
  exports["default"] = ParadoxMqttSecuritySystem;
34694
34740
 
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.48",
3
+ "version": "1.0.50",
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",