@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.
- package/dist/main.nodejs.js +99 -89
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
package/dist/main.nodejs.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
34094
|
-
function
|
|
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
|
|
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
|
|
34132
|
-
const np = normalize(
|
|
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(
|
|
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
|
-
|
|
34158
|
-
|
|
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,
|
|
34158
|
+
this.handlePrimary(topic, np, raw);
|
|
34162
34159
|
}
|
|
34163
34160
|
}
|
|
34164
34161
|
class ContactMqttSensor extends BaseMqttSensor {
|
|
34165
|
-
handlePrimary(topic, np
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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)
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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)
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
34612
|
-
const isAlarm =
|
|
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 =
|
|
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;
|
|
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