@homebridge-plugins/homebridge-wemo 7.7.1-beta.0 → 7.7.1-beta.2

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
@@ -12,6 +12,10 @@ All notable changes to `@homebridge-plugins/homebridge-wemo` will be documented
12
12
  - fix: send a colour change when only the saturation moves, and mend the al cleanup
13
13
  - fix: explain a port clash on the notification listener instead of crashing the bridge
14
14
  - fix: stop gating a few log lines on a debug setting that was never readable
15
+ - fix: keep an accessory when a device answers without its service list
16
+ - fix: cancel a garage door's settle timer when homebridge shuts down
17
+ - fix: wait for the manually configured devices before reporting which ones failed to connect
18
+ - fix: reject an interval so large it would make the plugin poll every millisecond
15
19
 
16
20
  ## v7.7.0 (2026-08-02)
17
21
 
@@ -42,7 +42,8 @@
42
42
  "title": "Discovery Interval",
43
43
  "description": "An interval (in seconds) in which the plugin will broadcast on your network to initally find devices and repair connection errors. Must be 15 or more.",
44
44
  "placeholder": 30,
45
- "minimum": 15
45
+ "minimum": 15,
46
+ "maximum": 2073600
46
47
  },
47
48
  "wemoClient": {
48
49
  "title": "Discovery Settings",
@@ -87,14 +88,16 @@
87
88
  "title": "HTTP Polling Interval",
88
89
  "description": "An interval (in seconds) in which the plugin will request status updates for devices that don't support UPnP or have it manually disabled. Must be 15 or more.",
89
90
  "placeholder": 30,
90
- "minimum": 15
91
+ "minimum": 15,
92
+ "maximum": 2073600
91
93
  },
92
94
  "upnpInterval": {
93
95
  "type": "integer",
94
96
  "title": "UPnP Subscription Interval",
95
97
  "description": "An interval (in seconds) in which the plugin will resubscribe to UPnP notifications. Must be 60 or more.",
96
98
  "placeholder": 300,
97
- "minimum": 60
99
+ "minimum": 60,
100
+ "maximum": 2073600
98
101
  },
99
102
  "disableUPNP": {
100
103
  "type": "boolean",
@@ -162,6 +165,7 @@
162
165
  "description": "The time in seconds for how long it takes the garage door to open/close. Must be 1 or more.",
163
166
  "placeholder": 20,
164
167
  "minimum": 1,
168
+ "maximum": 2073600,
165
169
  "condition": {
166
170
  "functionBody": "return (model.makerTypes && model.makerTypes[arrayIndices] && model.makerTypes[arrayIndices].makerType === 'garageDoor' && !model.makerTypes[arrayIndices].ignoreDevice);"
167
171
  }
@@ -252,6 +256,7 @@
252
256
  "description": "The plugin will not log device wattage if the time difference (in seconds) from the previous value is less than this number. Must be 1 or more.",
253
257
  "placeholder": 1,
254
258
  "minimum": 1,
259
+ "maximum": 2073600,
255
260
  "condition": {
256
261
  "functionBody": "return (model.wemoInsights && model.wemoInsights[arrayIndices] && model.wemoInsights[arrayIndices].serialNumber && model.wemoInsights[arrayIndices].serialNumber.length > 0 && !model.wemoInsights[arrayIndices].ignoreDevice);"
257
262
  }
@@ -492,6 +497,7 @@
492
497
  "description": "A timer (in seconds) for how long after motion is not detected that the state is changed.",
493
498
  "placeholder": 60,
494
499
  "minimum": 0,
500
+ "maximum": 2073600,
495
501
  "condition": {
496
502
  "functionBody": "return (model.wemoMotions && model.wemoMotions[arrayIndices] && model.wemoMotions[arrayIndices].serialNumber && model.wemoMotions[arrayIndices].serialNumber.length > 0 && !model.wemoMotions[arrayIndices].ignoreDevice);"
497
503
  }
package/lib/platform.js CHANGED
@@ -100,6 +100,9 @@ export default class {
100
100
  const logIncrease = (k, min) => {
101
101
  this.log.warn('%s [%s] %s %s.', platformLang.cfgItem, k, platformLang.cfgLow, min)
102
102
  }
103
+ const logDecrease = (k, max) => {
104
+ this.log.warn('%s [%s] %s %s.', platformLang.cfgItem, k, platformLang.cfgHigh, max)
105
+ }
103
106
  const logQuotes = (k) => {
104
107
  this.log.warn('%s [%s] %s.', platformLang.cfgItem, k, platformLang.cfgQts)
105
108
  }
@@ -133,6 +136,9 @@ export default class {
133
136
  } else if (intVal < platformConsts.minValues[key]) {
134
137
  logIncrease(key, platformConsts.minValues[key])
135
138
  this.config[key] = platformConsts.minValues[key]
139
+ } else if (platformConsts.maxValues[key] !== undefined && intVal > platformConsts.maxValues[key]) {
140
+ logDecrease(key, platformConsts.maxValues[key])
141
+ this.config[key] = platformConsts.maxValues[key]
136
142
  } else {
137
143
  this.config[key] = intVal
138
144
  }
@@ -187,6 +193,9 @@ export default class {
187
193
  } else if (intVal < platformConsts.minValues[k]) {
188
194
  logIncrease(`${key}.${id}.${k}`, platformConsts.minValues[k])
189
195
  this.deviceConf[id][k] = platformConsts.minValues[k]
196
+ } else if (platformConsts.maxValues[k] !== undefined && intVal > platformConsts.maxValues[k]) {
197
+ logDecrease(`${key}.${id}.${k}`, platformConsts.maxValues[k])
198
+ this.deviceConf[id][k] = platformConsts.maxValues[k]
190
199
  } else {
191
200
  this.deviceConf[id][k] = intVal
192
201
  }
@@ -531,6 +540,13 @@ export default class {
531
540
  if (accessory.control?.pollingInterval) {
532
541
  clearInterval(accessory.control.pollingInterval)
533
542
  }
543
+ // A garage door part-way through opening or closing has a timer waiting to
544
+ // write its settled state. That was left running, so it fired against a
545
+ // torn-down accessory and held the process open until it did.
546
+ if (accessory.control?.movingTimer) {
547
+ clearTimeout(accessory.control.movingTimer)
548
+ accessory.control.movingTimer = false
549
+ }
534
550
  if (accessory.client) {
535
551
  accessory.client.stopSubscriptions()
536
552
  }
@@ -648,7 +664,15 @@ export default class {
648
664
  // *************************** \\
649
665
  // Manually Configured Devices \\
650
666
  // *************************** \\
651
- this.manualDevices.forEach(async (device) => {
667
+ // Awaited, unlike the `forEach(async ...)` this used to be, which threw the
668
+ // promises away. The "still awaiting" warning further down then ran before
669
+ // these probes had finished, so a manually configured device that was about
670
+ // to connect could be reported as one that had not.
671
+ // Still concurrent rather than a for..of loop, because each entry is an
672
+ // independent port scan and running them one after another would drag out
673
+ // discovery. Each callback keeps its own try/catch, so one failure cannot
674
+ // reject the whole batch.
675
+ await Promise.all(this.manualDevices.map(async (device) => {
652
676
  try {
653
677
  // Check to see if the entry is a full address or an IP
654
678
  let deviceIP
@@ -703,7 +727,7 @@ export default class {
703
727
  this.log.warn('[%s] %s: %s.', device, platformLang.connError, eText)
704
728
  }
705
729
  }
706
- })
730
+ }))
707
731
 
708
732
  // ************************ \\
709
733
  // Erroneous Device Logging \\
@@ -842,10 +866,15 @@ export default class {
842
866
  }
843
867
  })
844
868
  } else {
845
- // Device has no services so is useless to Homebridge
846
- if (devicesInHB.has(uuid)) {
847
- this.removeAccessory(devicesInHB.get(uuid))
848
- }
869
+ // Device has no services so cannot be set up on this run.
870
+ //
871
+ // The cached accessory used to be unregistered here. A setup document
872
+ // that arrives truncated, or from a device part-way through a reboot,
873
+ // parses fine but has no serviceList - and one of those was enough to
874
+ // take the accessory out of HomeKit along with its room, its scenes and
875
+ // any automations using it. A later restart brings it back as a brand new
876
+ // accessory, so none of that comes back with it. Leave it alone: it shows
877
+ // as No Response until the device answers properly again.
849
878
  throw new Error(platformLang.noServices)
850
879
  }
851
880
 
@@ -44,6 +44,20 @@ export default {
44
44
  wattDiff: 1,
45
45
  },
46
46
 
47
+ // A Node timer holds its delay in a signed 32-bit integer. Over 2147483647 ms
48
+ // it does not throw - it quietly becomes 1 ms, so any of these set past that
49
+ // point would fire a thousand times a second rather than slowly. 2073600
50
+ // seconds is 24 days, comfortably inside the limit once multiplied up.
51
+ maxValues: {
52
+ discoveryInterval: 2073600,
53
+ doorOpenTimer: 2073600,
54
+ makerTimer: 2073600,
55
+ noMotionTimer: 2073600,
56
+ pollingInterval: 2073600,
57
+ timeDiff: 2073600,
58
+ upnpInterval: 2073600,
59
+ },
60
+
47
61
  minValues: {
48
62
  adaptiveLightingShift: -1,
49
63
  discoveryInterval: 15,
@@ -12,6 +12,7 @@ export default {
12
12
  cfgIgn: 'is not configured correctly so ignoring',
13
13
  cfgIgnItem: 'has an invalid entry which will be ignored',
14
14
  cfgItem: 'Config entry',
15
+ cfgHigh: 'is set too high so reducing to',
15
16
  cfgLow: 'is set too low so increasing to',
16
17
  cfgRmv: 'is unused and can be removed',
17
18
  cfgQts: 'should not have quotes around its entry',
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@homebridge-plugins/homebridge-wemo",
3
3
  "alias": "Wemo",
4
4
  "type": "module",
5
- "version": "7.7.1-beta.0",
5
+ "version": "7.7.1-beta.2",
6
6
  "description": "Homebridge plugin to integrate Wemo devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",