homebridge-ecobee-security 0.7.0 → 0.7.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.1
4
+
5
+ - Keypad unlock: the plugin now reads ecobee's current state first and only disarms when the system
6
+ is armed, arming or alarming. A disarm sent while it was already off was followed by ecobee camera
7
+ activity alerts.
8
+ - An intrusion incident only shows as **Alarm triggered** while the system is armed. ecobee also
9
+ opens one for a camera activity alert while the system is off, which briefly showed as an alarm in
10
+ the Home app (and counted as one for security holds). Smoke alarms still show at any time.
11
+
3
12
  ## 0.7.0
4
13
 
5
14
  - New setting **Disarm when a door is unlocked with its keypad** (`keypadDisarm`, off by default,
package/README.md CHANGED
@@ -92,8 +92,9 @@ node <plugin folder>/bin/ecobee-login.js --browser # website sign-in
92
92
  exit delay (0 by default). Off needs no PIN.
93
93
  - **Changes from elsewhere** (ecobee app, keypad, ecobee automations) show up within 10 s, about
94
94
  as fast as in the ecobee app (it polls too), and quickly after an exit delay ends.
95
- - **Alarm triggered** while ecobee lists an open incident (see `alarmTrigger` above for when it
96
- starts showing). Disarming clears it. Every change in the incident list is logged.
95
+ - **Alarm triggered** while ecobee lists an open incident: a smoke alarm at any time, an intrusion
96
+ only while armed (ecobee also opens one for a camera activity alert while the system is off; that
97
+ isn't shown as an alarm). See `alarmTrigger` above for when it starts showing. Disarming clears it. Every change in the incident list is logged.
97
98
  - **Siri:** asking for Home or for Stay mode both set Home (ecobee's Stay).
98
99
  - **Night** is only offered with `offerNight`; ecobee has no Night mode of its own.
99
100
  - After three failed reads in a row the accessory shows a fault; reads back off up to 5 minutes.
@@ -111,6 +112,8 @@ schedules, expiry), so the plugin needs no codes of its own.
111
112
  unlocked (HomeKit's Lock Last Known Action), for example homebridge-nest-accfactory's Nest × Yale
112
113
  locks. The settings page lists the locks found; untick the ones that shouldn't disarm. Locks
113
114
  paired to Apple Home directly (not through Homebridge) can't be seen.
115
+ - **Only when armed:** the plugin checks ecobee's current state first and sends nothing if the system
116
+ is already off.
114
117
  - **Only keypad unlocks** count: not the app, Siri, automations, a key or the thumb turn. Any code
115
118
  that opens the lock disarms, yours included.
116
119
  - **Speed:** the plugin checks the locks every second; the disarm reaches ecobee about as fast as
package/lib/security.js CHANGED
@@ -73,7 +73,7 @@ export class SecuritySystem {
73
73
  this.alarmAfterEntryDelay = alarmAfterEntryDelay;
74
74
  // Extra exit-delay time for robots heading back to their base when Away is armed (ms).
75
75
  this.returnWait = Math.max(0, returnWait) * 1000;
76
- this.incidents = [];
76
+ this.openIncidents = [];
77
77
  this.firstSeen = new Map();
78
78
 
79
79
  const { Service, Characteristic } = api.hap;
@@ -259,8 +259,17 @@ export class SecuritySystem {
259
259
  }
260
260
  }
261
261
 
262
+ /**
263
+ * Open incidents that are alarms: smoke always, intrusions only while armed. ecobee also opens an
264
+ * intrusion incident for a camera activity alert while the system is off; that isn't an alarm.
265
+ */
266
+ get incidents() {
267
+ const armed = [ArmedState.STAY, ArmedState.AWAY].includes(this.monitoring?.armedState);
268
+ return this.openIncidents.filter(i => i.detectedSmokeAlarm || armed);
269
+ }
270
+
262
271
  noteIncidents(incidents) {
263
- this.incidents = incidents.filter(i => !CLOSED_INCIDENT.test(i.status ?? ''));
272
+ this.openIncidents = incidents.filter(i => !CLOSED_INCIDENT.test(i.status ?? ''));
264
273
  const summary = JSON.stringify(incidents);
265
274
  if (summary !== this.incidentSummary) {
266
275
  if (incidents.length || this.incidentSummary !== undefined) {
@@ -270,8 +279,8 @@ export class SecuritySystem {
270
279
  }
271
280
  // ecobee's incidents carry no time we read, so the entry delay counts from when we first saw one.
272
281
  const now = Date.now();
273
- const ids = new Set(this.incidents.map(i => i.id));
274
- this.incidents.forEach(i => this.firstSeen.has(i.id) || this.firstSeen.set(i.id, now));
282
+ const ids = new Set(this.openIncidents.map(i => i.id));
283
+ this.openIncidents.forEach(i => this.firstSeen.has(i.id) || this.firstSeen.set(i.id, now));
275
284
  [...this.firstSeen.keys()].filter(id => !ids.has(id)).forEach(id => this.firstSeen.delete(id));
276
285
  }
277
286
 
@@ -388,9 +397,8 @@ export class SecuritySystem {
388
397
 
389
398
 
390
399
  /**
391
- * A door was unlocked with its keypad: disarm, like Nest Guard with a Nest × Yale lock. Always sent,
392
- * even if the last poll said Off: it may be up to 10 s old, and disarming twice does no harm.
393
- * Retried once, since a missed disarm means an alarm.
400
+ * A door was unlocked with its keypad: disarm, like Nest Guard with a Nest × Yale lock. Retried
401
+ * once, since a missed disarm means an alarm.
394
402
  */
395
403
  keypadDisarm(lock) {
396
404
  return this.serial(async () => {
@@ -398,6 +406,13 @@ export class SecuritySystem {
398
406
  this.log.info(`${lock} was unlocked during the ${this.holders().join(' and ')} hold; it won't be put back to Away.`);
399
407
  this.dropHold();
400
408
  }
409
+ // Only disarm what's armed: a disarm sent while ecobee is already off isn't a no-op for ecobee
410
+ // (camera activity alerts followed it), so read the live state first. Can't read it: disarm anyway.
411
+ if (await this.refresh() && this.monitoring?.armedState === ArmedState.DISARMED
412
+ && !this.monitoring.delayedArmedState?.desiredArmedState) {
413
+ this.log.info(`${lock} was unlocked with its keypad; the security system is already off.`);
414
+ return;
415
+ }
401
416
  try {
402
417
  await this.arm(Hk.DISARMED, `${lock} unlocked with its keypad`);
403
418
  } catch {
@@ -552,7 +567,7 @@ export class SecuritySystem {
552
567
  this.apply(home);
553
568
  return true;
554
569
  } catch (error) {
555
- this.log.warn(`Could not read the security state for a hold (${error.message}).`);
570
+ this.log.warn(`Could not read the security state (${error.message}).`);
556
571
  return false;
557
572
  }
558
573
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-ecobee-security",
3
3
  "displayName": "ecobee Smart Security",
4
- "version": "0.7.0",
4
+ "version": "0.7.1",
5
5
  "description": "ecobee Smart Security as a HomeKit security system: arm Stay / Away, disarm, alarm state (unofficial, ecobee cloud)",
6
6
  "type": "module",
7
7
  "main": "index.js",