@rfranzoi/scrypted-mqtt-securitysystem 1.0.14 → 1.0.17
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 +253 -49
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
package/dist/main.nodejs.js
CHANGED
|
@@ -34063,7 +34063,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
34063
34063
|
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
|
-
const { systemManager } = sdk_1.default;
|
|
34066
|
+
const { systemManager, deviceManager } = sdk_1.default;
|
|
34067
|
+
/** utils */
|
|
34067
34068
|
function truthy(v) {
|
|
34068
34069
|
if (!v)
|
|
34069
34070
|
return false;
|
|
@@ -34079,20 +34080,21 @@ function falsy(v) {
|
|
|
34079
34080
|
function normalize(s) {
|
|
34080
34081
|
return (s || '').trim().toLowerCase();
|
|
34081
34082
|
}
|
|
34082
|
-
|
|
34083
|
+
function clamp(n, min, max) {
|
|
34084
|
+
return Math.max(min, Math.min(max, n));
|
|
34085
|
+
}
|
|
34086
|
+
/** SecuritySystem outgoing defaults (PAI-like) */
|
|
34083
34087
|
const DEFAULT_OUTGOING = {
|
|
34084
34088
|
[sdk_1.SecuritySystemMode.Disarmed]: 'disarm',
|
|
34085
34089
|
[sdk_1.SecuritySystemMode.HomeArmed]: 'arm_home',
|
|
34086
34090
|
[sdk_1.SecuritySystemMode.AwayArmed]: 'arm_away',
|
|
34087
34091
|
[sdk_1.SecuritySystemMode.NightArmed]: 'arm_night',
|
|
34088
34092
|
};
|
|
34089
|
-
/**
|
|
34090
|
-
* (FIX: gli stati transitori non alterano la modalità) */
|
|
34093
|
+
/** Parse incoming payload -> final mode (ignore transition states) */
|
|
34091
34094
|
function payloadToMode(payload) {
|
|
34092
34095
|
if (payload == null)
|
|
34093
34096
|
return;
|
|
34094
34097
|
const p = normalize(payload.toString());
|
|
34095
|
-
// final modes
|
|
34096
34098
|
if (['disarm', 'disarmed', 'off', '0', 'idle', 'ready'].includes(p))
|
|
34097
34099
|
return sdk_1.SecuritySystemMode.Disarmed;
|
|
34098
34100
|
if (['arm_home', 'home', 'stay', 'armed_home'].includes(p))
|
|
@@ -34101,14 +34103,88 @@ function payloadToMode(payload) {
|
|
|
34101
34103
|
return sdk_1.SecuritySystemMode.AwayArmed;
|
|
34102
34104
|
if (['arm_night', 'night', 'armed_night', 'sleep', 'arm_sleep', 'armed_sleep'].includes(p))
|
|
34103
34105
|
return sdk_1.SecuritySystemMode.NightArmed;
|
|
34104
|
-
//
|
|
34106
|
+
// transitori: non cambiano il mode
|
|
34105
34107
|
if (['entry_delay', 'exit_delay', 'pending', 'arming', 'disarming'].includes(p))
|
|
34106
34108
|
return undefined;
|
|
34107
34109
|
return undefined;
|
|
34108
34110
|
}
|
|
34111
|
+
class BaseMqttSensor extends sdk_1.ScryptedDeviceBase {
|
|
34112
|
+
constructor(nativeId, cfg) {
|
|
34113
|
+
super(nativeId);
|
|
34114
|
+
this.cfg = cfg;
|
|
34115
|
+
this.online = this.online ?? true;
|
|
34116
|
+
}
|
|
34117
|
+
/** Called by parent on each MQTT message */
|
|
34118
|
+
handleMqtt(topic, payload) {
|
|
34119
|
+
const p = payload?.toString() ?? '';
|
|
34120
|
+
const np = normalize(p);
|
|
34121
|
+
// online
|
|
34122
|
+
if (topic === this.cfg.topics.online) {
|
|
34123
|
+
if (truthy(np) || np === 'online')
|
|
34124
|
+
this.online = true;
|
|
34125
|
+
if (falsy(np) || np === 'offline')
|
|
34126
|
+
this.online = false;
|
|
34127
|
+
}
|
|
34128
|
+
// tamper
|
|
34129
|
+
if (topic === this.cfg.topics.tamper) {
|
|
34130
|
+
if (truthy(np) || ['tamper', 'intrusion', 'cover', 'motion', 'magnetic'].includes(np)) {
|
|
34131
|
+
this.tampered = ['cover', 'intrusion', 'motion', 'magnetic'].find(x => x === np) || true;
|
|
34132
|
+
}
|
|
34133
|
+
else if (falsy(np)) {
|
|
34134
|
+
this.tampered = false;
|
|
34135
|
+
}
|
|
34136
|
+
}
|
|
34137
|
+
// battery
|
|
34138
|
+
if (topic === this.cfg.topics.batteryLevel) {
|
|
34139
|
+
const n = clamp(parseFloat(p), 0, 100);
|
|
34140
|
+
if (isFinite(n))
|
|
34141
|
+
this.batteryLevel = n;
|
|
34142
|
+
}
|
|
34143
|
+
else if (topic === this.cfg.topics.lowBattery && !this.cfg.topics.batteryLevel) {
|
|
34144
|
+
// sintetizza se non c'è batteryLevel
|
|
34145
|
+
this.batteryLevel = truthy(np) ? 10 : 100;
|
|
34146
|
+
}
|
|
34147
|
+
// primary handled by subclasses
|
|
34148
|
+
this.handlePrimary(topic, np, p);
|
|
34149
|
+
}
|
|
34150
|
+
}
|
|
34151
|
+
class ContactMqttSensor extends BaseMqttSensor {
|
|
34152
|
+
constructor(nativeId, cfg) {
|
|
34153
|
+
super(nativeId, cfg);
|
|
34154
|
+
}
|
|
34155
|
+
handlePrimary(topic, np, _raw) {
|
|
34156
|
+
if (topic === this.cfg.topics.contact) {
|
|
34157
|
+
this.entryOpen = truthy(np);
|
|
34158
|
+
}
|
|
34159
|
+
}
|
|
34160
|
+
}
|
|
34161
|
+
class MotionMqttSensor extends BaseMqttSensor {
|
|
34162
|
+
constructor(nativeId, cfg) {
|
|
34163
|
+
super(nativeId, cfg);
|
|
34164
|
+
}
|
|
34165
|
+
handlePrimary(topic, np, _raw) {
|
|
34166
|
+
if (topic === this.cfg.topics.motion) {
|
|
34167
|
+
this.motionDetected = truthy(np);
|
|
34168
|
+
}
|
|
34169
|
+
}
|
|
34170
|
+
}
|
|
34171
|
+
class OccupancyMqttSensor extends BaseMqttSensor {
|
|
34172
|
+
constructor(nativeId, cfg) {
|
|
34173
|
+
super(nativeId, cfg);
|
|
34174
|
+
}
|
|
34175
|
+
handlePrimary(topic, np, _raw) {
|
|
34176
|
+
if (topic === this.cfg.topics.occupancy) {
|
|
34177
|
+
this.occupied = truthy(np);
|
|
34178
|
+
}
|
|
34179
|
+
}
|
|
34180
|
+
}
|
|
34181
|
+
/** ----------------- Main Plugin ----------------- */
|
|
34109
34182
|
class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
34110
34183
|
constructor() {
|
|
34111
34184
|
super();
|
|
34185
|
+
// sensor management
|
|
34186
|
+
this.sensorsCfg = [];
|
|
34187
|
+
this.devices = new Map();
|
|
34112
34188
|
// (facoltativo) Imposta il device type in UI
|
|
34113
34189
|
setTimeout(() => {
|
|
34114
34190
|
try {
|
|
@@ -34127,49 +34203,156 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34127
34203
|
],
|
|
34128
34204
|
};
|
|
34129
34205
|
this.online = this.online ?? false;
|
|
34206
|
+
// Load sensors config and announce devices
|
|
34207
|
+
this.loadSensorsFromStorage();
|
|
34208
|
+
this.discoverSensors();
|
|
34130
34209
|
// Connect on start
|
|
34131
34210
|
this.connectMqtt().catch(e => this.console.error('MQTT connect error:', e));
|
|
34132
34211
|
// chiusura pulita del client MQTT ai reload/stop del plugin
|
|
34133
|
-
|
|
34134
|
-
|
|
34135
|
-
|
|
34136
|
-
|
|
34137
|
-
|
|
34138
|
-
|
|
34139
|
-
|
|
34140
|
-
|
|
34141
|
-
|
|
34142
|
-
|
|
34212
|
+
try {
|
|
34213
|
+
process.once('SIGTERM', () => { try {
|
|
34214
|
+
this.client?.end(true);
|
|
34215
|
+
}
|
|
34216
|
+
catch { } });
|
|
34217
|
+
process.once('SIGINT', () => { try {
|
|
34218
|
+
this.client?.end(true);
|
|
34219
|
+
}
|
|
34220
|
+
catch { } });
|
|
34221
|
+
process.on('exit', () => { try {
|
|
34222
|
+
this.client?.end(true);
|
|
34223
|
+
}
|
|
34224
|
+
catch { } });
|
|
34143
34225
|
}
|
|
34144
|
-
catch { }
|
|
34226
|
+
catch { }
|
|
34145
34227
|
}
|
|
34146
|
-
|
|
34228
|
+
/** ---- Settings UI ---- */
|
|
34147
34229
|
async getSettings() {
|
|
34148
34230
|
return [
|
|
34231
|
+
// MQTT Core
|
|
34149
34232
|
{ 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' },
|
|
34150
34233
|
{ group: 'MQTT', key: 'username', title: 'Username', type: 'string', value: this.storage.getItem('username') || '' },
|
|
34151
34234
|
{ group: 'MQTT', key: 'password', title: 'Password', type: 'password', value: this.storage.getItem('password') || '' },
|
|
34152
34235
|
{ group: 'MQTT', key: 'clientId', title: 'Client ID', placeholder: 'scrypted-paradox', value: this.storage.getItem('clientId') || 'scrypted-paradox' },
|
|
34153
34236
|
{ group: 'MQTT', key: 'tls', title: 'Use TLS', type: 'boolean', value: this.storage.getItem('tls') === 'true' },
|
|
34154
34237
|
{ group: 'MQTT', key: 'rejectUnauthorized', title: 'Reject Unauthorized (TLS)', type: 'boolean', value: this.storage.getItem('rejectUnauthorized') !== 'false', description: 'Disattiva solo con broker self-signed.' },
|
|
34155
|
-
|
|
34156
|
-
{ group: 'Topics', key: '
|
|
34157
|
-
{ group: 'Topics', key: '
|
|
34158
|
-
{ group: 'Topics', key: '
|
|
34159
|
-
{ group: 'Topics', key: '
|
|
34238
|
+
// Alarm Topics
|
|
34239
|
+
{ group: 'Alarm Topics', key: 'topicSetTarget', title: 'Set Target State (publish)', placeholder: 'paradox/control/partitions/Area_1', value: this.storage.getItem('topicSetTarget') || '' },
|
|
34240
|
+
{ group: 'Alarm Topics', key: 'topicGetTarget', title: 'Get Target State (subscribe)', placeholder: 'paradox/states/partitions/Area_1/target_state', value: this.storage.getItem('topicGetTarget') || '' },
|
|
34241
|
+
{ group: 'Alarm Topics', key: 'topicGetCurrent', title: 'Get Current State (subscribe)', placeholder: 'paradox/states/partitions/Area_1/current_state', value: this.storage.getItem('topicGetCurrent') || '' },
|
|
34242
|
+
{ group: 'Alarm Topics', key: 'topicTamper', title: 'Get Status Tampered (subscribe)', placeholder: 'paradox/states/system/troubles/zone_tamper_trouble', value: this.storage.getItem('topicTamper') || '' },
|
|
34243
|
+
{ group: 'Alarm Topics', key: 'topicOnline', title: 'Get Online (subscribe)', placeholder: 'paradox/interface/availability', value: this.storage.getItem('topicOnline') || '' },
|
|
34160
34244
|
{ group: 'Publish Options', key: 'qos', title: 'QoS', type: 'integer', value: parseInt(this.storage.getItem('qos') || '0') },
|
|
34161
34245
|
{ group: 'Publish Options', key: 'retain', title: 'Retain', type: 'boolean', value: this.storage.getItem('retain') === 'true' },
|
|
34162
34246
|
{ group: 'Outgoing Payloads', key: 'payloadDisarm', title: 'Payload Disarm', value: this.storage.getItem('payloadDisarm') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.Disarmed] },
|
|
34163
34247
|
{ group: 'Outgoing Payloads', key: 'payloadHome', title: 'Payload HomeArmed', value: this.storage.getItem('payloadHome') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.HomeArmed] },
|
|
34164
34248
|
{ group: 'Outgoing Payloads', key: 'payloadAway', title: 'Payload AwayArmed', value: this.storage.getItem('payloadAway') || DEFAULT_OUTGOING[sdk_1.SecuritySystemMode.AwayArmed] },
|
|
34165
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
|
+
},
|
|
34166
34259
|
];
|
|
34167
34260
|
}
|
|
34168
34261
|
async putSetting(key, value) {
|
|
34169
34262
|
this.storage.setItem(key, String(value));
|
|
34170
|
-
|
|
34263
|
+
if (key === 'sensorsJson') {
|
|
34264
|
+
this.loadSensorsFromStorage();
|
|
34265
|
+
await this.discoverSensors();
|
|
34266
|
+
await this.connectMqtt(true);
|
|
34267
|
+
}
|
|
34268
|
+
else {
|
|
34269
|
+
await this.connectMqtt(true);
|
|
34270
|
+
}
|
|
34271
|
+
}
|
|
34272
|
+
/** ---- DeviceProvider ---- */
|
|
34273
|
+
async getDevice(nativeId) {
|
|
34274
|
+
return this.devices.get(nativeId);
|
|
34171
34275
|
}
|
|
34172
|
-
|
|
34276
|
+
async releaseDevice(id, nativeId) {
|
|
34277
|
+
try {
|
|
34278
|
+
// chiudi e rimuovi l’istanza locale se esiste
|
|
34279
|
+
const dev = this.devices.get(nativeId);
|
|
34280
|
+
if (dev) {
|
|
34281
|
+
this.devices.delete(nativeId);
|
|
34282
|
+
}
|
|
34283
|
+
// notifica (best effort) la rimozione al device manager
|
|
34284
|
+
try {
|
|
34285
|
+
deviceManager.onDeviceRemoved?.(nativeId);
|
|
34286
|
+
}
|
|
34287
|
+
catch { }
|
|
34288
|
+
}
|
|
34289
|
+
catch (e) {
|
|
34290
|
+
this.console.warn('releaseDevice error', e);
|
|
34291
|
+
}
|
|
34292
|
+
}
|
|
34293
|
+
loadSensorsFromStorage() {
|
|
34294
|
+
try {
|
|
34295
|
+
const raw = this.storage.getItem('sensorsJson') || '[]';
|
|
34296
|
+
const parsed = JSON.parse(raw);
|
|
34297
|
+
// sanitize
|
|
34298
|
+
this.sensorsCfg = (parsed || []).filter(x => x && x.id && x.name && x.kind && x.topics);
|
|
34299
|
+
}
|
|
34300
|
+
catch (e) {
|
|
34301
|
+
this.console.error('Invalid sensorsJson:', e);
|
|
34302
|
+
this.sensorsCfg = [];
|
|
34303
|
+
}
|
|
34304
|
+
}
|
|
34305
|
+
async discoverSensors() {
|
|
34306
|
+
const announced = new Set();
|
|
34307
|
+
for (const cfg of this.sensorsCfg) {
|
|
34308
|
+
const nativeId = `sensor:${cfg.id}`;
|
|
34309
|
+
announced.add(nativeId);
|
|
34310
|
+
let interfaces = [sdk_1.ScryptedInterface.Online, sdk_1.ScryptedInterface.TamperSensor, sdk_1.ScryptedInterface.Battery];
|
|
34311
|
+
switch (cfg.kind) {
|
|
34312
|
+
case 'contact':
|
|
34313
|
+
interfaces = [sdk_1.ScryptedInterface.EntrySensor, ...interfaces];
|
|
34314
|
+
break;
|
|
34315
|
+
case 'motion':
|
|
34316
|
+
interfaces = [sdk_1.ScryptedInterface.MotionSensor, ...interfaces];
|
|
34317
|
+
break;
|
|
34318
|
+
case 'occupancy':
|
|
34319
|
+
interfaces = [sdk_1.ScryptedInterface.OccupancySensor, ...interfaces];
|
|
34320
|
+
break;
|
|
34321
|
+
}
|
|
34322
|
+
deviceManager.onDeviceDiscovered({
|
|
34323
|
+
nativeId,
|
|
34324
|
+
name: cfg.name,
|
|
34325
|
+
type: sdk_1.ScryptedDeviceType.Sensor,
|
|
34326
|
+
interfaces,
|
|
34327
|
+
});
|
|
34328
|
+
// create/update instance
|
|
34329
|
+
let dev = this.devices.get(nativeId);
|
|
34330
|
+
if (!dev) {
|
|
34331
|
+
if (cfg.kind === 'contact')
|
|
34332
|
+
dev = new ContactMqttSensor(nativeId, cfg);
|
|
34333
|
+
else if (cfg.kind === 'motion')
|
|
34334
|
+
dev = new MotionMqttSensor(nativeId, cfg);
|
|
34335
|
+
else
|
|
34336
|
+
dev = new OccupancyMqttSensor(nativeId, cfg);
|
|
34337
|
+
this.devices.set(nativeId, dev);
|
|
34338
|
+
}
|
|
34339
|
+
else {
|
|
34340
|
+
// update config reference
|
|
34341
|
+
dev.cfg = cfg;
|
|
34342
|
+
}
|
|
34343
|
+
}
|
|
34344
|
+
// drop removed sensors
|
|
34345
|
+
for (const [nativeId] of this.devices) {
|
|
34346
|
+
if (!announced.has(nativeId)) {
|
|
34347
|
+
try {
|
|
34348
|
+
this.devices.delete(nativeId);
|
|
34349
|
+
deviceManager.onDeviceRemoved?.(nativeId);
|
|
34350
|
+
}
|
|
34351
|
+
catch { }
|
|
34352
|
+
}
|
|
34353
|
+
}
|
|
34354
|
+
}
|
|
34355
|
+
/** ---- MQTT ---- */
|
|
34173
34356
|
getMqttOptions() {
|
|
34174
34357
|
const url = this.storage.getItem('brokerUrl') || 'mqtt://127.0.0.1:1883';
|
|
34175
34358
|
const username = this.storage.getItem('username') || undefined;
|
|
@@ -34190,13 +34373,25 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34190
34373
|
}
|
|
34191
34374
|
return { url, opts };
|
|
34192
34375
|
}
|
|
34376
|
+
collectAllSubscriptions() {
|
|
34377
|
+
const subs = new Set();
|
|
34378
|
+
// alarm
|
|
34379
|
+
for (const k of ['topicGetTarget', 'topicGetCurrent', 'topicTamper', 'topicOnline']) {
|
|
34380
|
+
const v = this.storage.getItem(k);
|
|
34381
|
+
if (v)
|
|
34382
|
+
subs.add(v);
|
|
34383
|
+
}
|
|
34384
|
+
// sensors
|
|
34385
|
+
for (const s of this.sensorsCfg) {
|
|
34386
|
+
const t = s.topics;
|
|
34387
|
+
[t.contact, t.motion, t.occupancy, t.batteryLevel, t.lowBattery, t.tamper, t.online]
|
|
34388
|
+
.filter(Boolean)
|
|
34389
|
+
.forEach(x => subs.add(String(x)));
|
|
34390
|
+
}
|
|
34391
|
+
return Array.from(subs);
|
|
34392
|
+
}
|
|
34193
34393
|
async connectMqtt(reconnect = false) {
|
|
34194
|
-
const subs =
|
|
34195
|
-
this.storage.getItem('topicGetTarget'),
|
|
34196
|
-
this.storage.getItem('topicGetCurrent'),
|
|
34197
|
-
this.storage.getItem('topicTamper'),
|
|
34198
|
-
this.storage.getItem('topicOnline'),
|
|
34199
|
-
].filter(Boolean);
|
|
34394
|
+
const subs = this.collectAllSubscriptions();
|
|
34200
34395
|
if (!subs.length && !this.storage.getItem('topicSetTarget')) {
|
|
34201
34396
|
this.console.warn('Configura almeno un topic nelle impostazioni.');
|
|
34202
34397
|
}
|
|
@@ -34211,6 +34406,11 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34211
34406
|
this.console.log(`Connecting MQTT ${url} ...`);
|
|
34212
34407
|
const client = mqtt_1.default.connect(url, opts);
|
|
34213
34408
|
this.client = client;
|
|
34409
|
+
// cache alarm topics for fast compare
|
|
34410
|
+
const tTarget = this.storage.getItem('topicGetTarget') || '';
|
|
34411
|
+
const tCurrent = this.storage.getItem('topicGetCurrent') || '';
|
|
34412
|
+
const tTamper = this.storage.getItem('topicTamper') || '';
|
|
34413
|
+
const tOnline = this.storage.getItem('topicOnline') || '';
|
|
34214
34414
|
client.on('connect', () => {
|
|
34215
34415
|
this.console.log('MQTT connected');
|
|
34216
34416
|
this.online = true;
|
|
@@ -34227,29 +34427,27 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34227
34427
|
client.on('message', (topic, payload) => {
|
|
34228
34428
|
try {
|
|
34229
34429
|
const p = payload?.toString() ?? '';
|
|
34230
|
-
|
|
34231
|
-
|
|
34232
|
-
|
|
34430
|
+
const np = normalize(p);
|
|
34431
|
+
// ---- Alarm handling ----
|
|
34432
|
+
if (topic === tOnline) {
|
|
34433
|
+
if (truthy(np) || np === 'online')
|
|
34233
34434
|
this.online = true;
|
|
34234
|
-
if (falsy(
|
|
34435
|
+
if (falsy(np) || np === 'offline')
|
|
34235
34436
|
this.online = false;
|
|
34236
34437
|
return;
|
|
34237
34438
|
}
|
|
34238
|
-
|
|
34239
|
-
|
|
34240
|
-
|
|
34241
|
-
if (truthy(np) || ['tamper', 'intrusion', 'cover', 'motion', 'magnetic'].includes(np)) {
|
|
34242
|
-
this.tampered = ['cover', 'intrusion', 'motion', 'magnetic'].find(x => x === np) || true;
|
|
34439
|
+
if (topic === tTamper) {
|
|
34440
|
+
if (truthy(np) || ['tamper', 'intrusion', 'cover'].includes(np)) {
|
|
34441
|
+
this.tampered = ['cover', 'intrusion'].find(x => x === np) || true;
|
|
34243
34442
|
}
|
|
34244
34443
|
else if (falsy(np)) {
|
|
34245
34444
|
this.tampered = false;
|
|
34246
34445
|
}
|
|
34247
34446
|
return;
|
|
34248
34447
|
}
|
|
34249
|
-
|
|
34250
|
-
if ([this.storage.getItem('topicGetTarget'), this.storage.getItem('topicGetCurrent')].includes(topic)) {
|
|
34448
|
+
if (topic === tCurrent) {
|
|
34251
34449
|
const mode = payloadToMode(payload);
|
|
34252
|
-
const isAlarm = ['alarm', 'triggered'].includes(
|
|
34450
|
+
const isAlarm = ['alarm', 'triggered'].includes(np);
|
|
34253
34451
|
const current = this.securitySystemState || { mode: sdk_1.SecuritySystemMode.Disarmed };
|
|
34254
34452
|
const newState = {
|
|
34255
34453
|
mode: mode ?? current.mode,
|
|
@@ -34264,13 +34462,22 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34264
34462
|
this.securitySystemState = newState;
|
|
34265
34463
|
return;
|
|
34266
34464
|
}
|
|
34465
|
+
if (topic === tTarget) {
|
|
34466
|
+
this.pendingTarget = payloadToMode(payload);
|
|
34467
|
+
this.console.log('Target state reported:', p, '->', this.pendingTarget);
|
|
34468
|
+
return;
|
|
34469
|
+
}
|
|
34470
|
+
// ---- Sensor dispatch ----
|
|
34471
|
+
for (const dev of this.devices.values()) {
|
|
34472
|
+
dev.handleMqtt(topic, payload);
|
|
34473
|
+
}
|
|
34267
34474
|
}
|
|
34268
34475
|
catch (e) {
|
|
34269
34476
|
this.console.error('MQTT message handler error', e);
|
|
34270
34477
|
}
|
|
34271
34478
|
});
|
|
34272
34479
|
}
|
|
34273
|
-
|
|
34480
|
+
/** ---- SecuritySystem commands ---- */
|
|
34274
34481
|
publishSetTarget(payload) {
|
|
34275
34482
|
const topic = this.storage.getItem('topicSetTarget');
|
|
34276
34483
|
if (!topic || !this.client) {
|
|
@@ -34288,17 +34495,14 @@ class ParadoxMqttSecuritySystem extends sdk_1.ScryptedDeviceBase {
|
|
|
34288
34495
|
async armSecuritySystem(mode) {
|
|
34289
34496
|
const payload = this.getOutgoing(mode);
|
|
34290
34497
|
this.console.log('armSecuritySystem', mode, '->', payload);
|
|
34498
|
+
this.pendingTarget = mode; // memorizza target, ma NON cambiare il current
|
|
34291
34499
|
this.publishSetTarget(payload);
|
|
34292
|
-
// Optimistic UI update until broker echoes target/current state
|
|
34293
|
-
const st = this.securitySystemState || { mode: sdk_1.SecuritySystemMode.Disarmed };
|
|
34294
|
-
this.securitySystemState = { ...st, mode, triggered: false };
|
|
34295
34500
|
}
|
|
34296
34501
|
async disarmSecuritySystem() {
|
|
34297
34502
|
const payload = this.getOutgoing(sdk_1.SecuritySystemMode.Disarmed);
|
|
34298
34503
|
this.console.log('disarmSecuritySystem ->', payload);
|
|
34504
|
+
this.pendingTarget = sdk_1.SecuritySystemMode.Disarmed;
|
|
34299
34505
|
this.publishSetTarget(payload);
|
|
34300
|
-
const st = this.securitySystemState || { mode: sdk_1.SecuritySystemMode.Disarmed };
|
|
34301
|
-
this.securitySystemState = { ...st, mode: sdk_1.SecuritySystemMode.Disarmed, triggered: false };
|
|
34302
34506
|
}
|
|
34303
34507
|
getOutgoing(mode) {
|
|
34304
34508
|
const map = {
|
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED