@homebridge-wyze-node24/homebridge-wyze-node24 1.1.5 → 1.1.7
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/package.json +1 -1
- package/src/WyzeSmartHome.js +71 -1
package/package.json
CHANGED
package/src/WyzeSmartHome.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
1
3
|
const { homebridge, Accessory, UUIDGen } = require('./types')
|
|
2
4
|
const { OutdoorPlugModels, PlugModels, CommonModels, CameraModels, LeakSensorModels,
|
|
3
5
|
TemperatureHumidityModels, LockModels, MotionSensorModels, ContactSensorModels, LightModels,
|
|
@@ -29,6 +31,12 @@ const PLUGIN_NAME = 'homebridge-wyze-smart-home'
|
|
|
29
31
|
const PLATFORM_NAME = 'WyzeSmartHome'
|
|
30
32
|
|
|
31
33
|
const DEFAULT_REFRESH_INTERVAL = 30000
|
|
34
|
+
const AUTH_FILE_PATTERNS = [
|
|
35
|
+
/^wyze-.*\.json$/i,
|
|
36
|
+
/.*-wyze\.json$/i,
|
|
37
|
+
/^wyze-tokens\.json$/i
|
|
38
|
+
]
|
|
39
|
+
const AUTH_RECOVERY_COOLDOWN_MS = 60000
|
|
32
40
|
|
|
33
41
|
function delay(ms) {
|
|
34
42
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
@@ -39,6 +47,7 @@ module.exports = class WyzeSmartHome {
|
|
|
39
47
|
this.log = wrapLogger(log)
|
|
40
48
|
this.config = resolveSecrets(config || {}, this.log)
|
|
41
49
|
this.api = api
|
|
50
|
+
this.lastAuthRecoveryAt = 0
|
|
42
51
|
this.client = this.getClient()
|
|
43
52
|
|
|
44
53
|
this.accessories = []
|
|
@@ -50,6 +59,10 @@ module.exports = class WyzeSmartHome {
|
|
|
50
59
|
homebridge.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, WyzeSmartHome)
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
getPersistPath() {
|
|
63
|
+
return this.config.persistPath || homebridge.user.persistPath()
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
getClient() {
|
|
54
67
|
const { authBaseUrl, apiBaseUrl } = getValidatedBaseUrls(this.config, this.log)
|
|
55
68
|
|
|
@@ -65,7 +78,7 @@ module.exports = class WyzeSmartHome {
|
|
|
65
78
|
//App Config
|
|
66
79
|
lowBatteryPercentage: this.config.lowBatteryPercentage,
|
|
67
80
|
//Storage Path
|
|
68
|
-
persistPath:
|
|
81
|
+
persistPath: this.getPersistPath(),
|
|
69
82
|
//URLs (strictly validated)
|
|
70
83
|
authBaseUrl,
|
|
71
84
|
apiBaseUrl,
|
|
@@ -87,6 +100,59 @@ module.exports = class WyzeSmartHome {
|
|
|
87
100
|
}, this.log)
|
|
88
101
|
}
|
|
89
102
|
|
|
103
|
+
shouldRecoverFromAuthError(error) {
|
|
104
|
+
const message = String(error?.message || error || '')
|
|
105
|
+
|
|
106
|
+
return message.includes('access token is error') ||
|
|
107
|
+
message.includes('refresh token is error') ||
|
|
108
|
+
message.includes('Refresh Token could not be used to get a new access token')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
clearPersistedAuth() {
|
|
112
|
+
const persistPath = this.getPersistPath()
|
|
113
|
+
let removedCount = 0
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const stat = fs.statSync(persistPath)
|
|
117
|
+
|
|
118
|
+
if (stat.isDirectory()) {
|
|
119
|
+
for (const fileName of fs.readdirSync(persistPath)) {
|
|
120
|
+
if (!AUTH_FILE_PATTERNS.some(pattern => pattern.test(fileName))) continue
|
|
121
|
+
|
|
122
|
+
fs.unlinkSync(path.join(persistPath, fileName))
|
|
123
|
+
removedCount += 1
|
|
124
|
+
}
|
|
125
|
+
} else if (stat.isFile()) {
|
|
126
|
+
fs.unlinkSync(persistPath)
|
|
127
|
+
removedCount = 1
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error?.code !== 'ENOENT') {
|
|
131
|
+
this.log.warn(`Failed to clear persisted Wyze auth: ${error?.message || error}`)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return removedCount
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
recoverFromAuthError(error) {
|
|
139
|
+
if (!this.shouldRecoverFromAuthError(error)) {
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const now = Date.now()
|
|
144
|
+
if (now - this.lastAuthRecoveryAt < AUTH_RECOVERY_COOLDOWN_MS) {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.lastAuthRecoveryAt = now
|
|
149
|
+
const removedCount = this.clearPersistedAuth()
|
|
150
|
+
this.client = this.getClient()
|
|
151
|
+
|
|
152
|
+
this.log.warn(`Detected invalid Wyze auth tokens. Cleared ${removedCount} persisted auth file(s) from ${this.getPersistPath()} and recreated the Wyze client.`)
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
|
|
90
156
|
didFinishLaunching() {
|
|
91
157
|
this.runLoop()
|
|
92
158
|
}
|
|
@@ -124,6 +190,10 @@ module.exports = class WyzeSmartHome {
|
|
|
124
190
|
if (this.config.pluginLoggingEnabled) this.log(`Found ${devices.length} device(s)`)
|
|
125
191
|
await this.loadDevices(devices, timestamp)
|
|
126
192
|
} catch (e) {
|
|
193
|
+
if (this.recoverFromAuthError(e)) {
|
|
194
|
+
return this.refreshDevices()
|
|
195
|
+
}
|
|
196
|
+
|
|
127
197
|
this.log.error(`Error getting devices: ${e?.message || e}`)
|
|
128
198
|
throw e
|
|
129
199
|
}
|