@homebridge-plugins/homebridge-govee 11.22.0 → 11.22.1-beta.0
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 +9 -0
- package/lib/connection/http.js +39 -13
- package/lib/device/index.js +2 -0
- package/lib/device/sensor-co2.js +156 -0
- package/lib/platform.js +32 -10
- package/lib/utils/constants.js +3 -1
- package/lib/utils/lang-en.js +1 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@homebridge-plugins/homebridge-govee` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v11.22.1 (Pending Release)
|
|
6
|
+
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- feat: add support for Floor Lamp H16B0 and Table Lamp H1741 (#1279) (@gzimbric)
|
|
10
|
+
- feat: add H5140 Smart CO2 Monitor support (closes #1179) (#1281) (@itskevinb)
|
|
11
|
+
- chore: dependency updates
|
|
12
|
+
- fix: self-heal invalid TTR token without full re-login
|
|
13
|
+
|
|
5
14
|
## v11.22.0 (2026-05-05)
|
|
6
15
|
|
|
7
16
|
### Changed
|
package/lib/connection/http.js
CHANGED
|
@@ -107,20 +107,11 @@ export default class {
|
|
|
107
107
|
throw new Error(res.data.message || platformLang.noToken)
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// Also grab an access token specifically for the get tap to run endpoint
|
|
111
|
-
const ttrRes = await axios({
|
|
112
|
-
url: 'https://community-api.govee.com/os/v1/login',
|
|
113
|
-
method: 'post',
|
|
114
|
-
data: {
|
|
115
|
-
email: this.username,
|
|
116
|
-
password: this.password,
|
|
117
|
-
},
|
|
118
|
-
timeout: 30000,
|
|
119
|
-
})
|
|
120
|
-
|
|
121
110
|
// Make the token available in other functions
|
|
122
111
|
this.token = res.data.client.token
|
|
123
|
-
|
|
112
|
+
|
|
113
|
+
// Also grab an access token specifically for the get-tap-to-run endpoint
|
|
114
|
+
await this.loginTTR()
|
|
124
115
|
|
|
125
116
|
// Mark this request complete if in debug mode
|
|
126
117
|
this.log.debug('[HTTP] %s.', platformLang.loginSuccess)
|
|
@@ -166,6 +157,24 @@ export default class {
|
|
|
166
157
|
}
|
|
167
158
|
}
|
|
168
159
|
|
|
160
|
+
async loginTTR() {
|
|
161
|
+
// The tap-to-run endpoint uses a separate, shorter-lived token obtained from
|
|
162
|
+
// a different login endpoint to the main Govee account token. This is split
|
|
163
|
+
// out so it can be refreshed on its own without a full account re-login.
|
|
164
|
+
const ttrRes = await axios({
|
|
165
|
+
url: 'https://community-api.govee.com/os/v1/login',
|
|
166
|
+
method: 'post',
|
|
167
|
+
data: {
|
|
168
|
+
email: this.username,
|
|
169
|
+
password: this.password,
|
|
170
|
+
},
|
|
171
|
+
timeout: 30000,
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
this.tokenTTR = ttrRes.data?.data?.token
|
|
175
|
+
return this.tokenTTR
|
|
176
|
+
}
|
|
177
|
+
|
|
169
178
|
async logout() {
|
|
170
179
|
try {
|
|
171
180
|
await axios({
|
|
@@ -232,7 +241,17 @@ export default class {
|
|
|
232
241
|
}
|
|
233
242
|
}
|
|
234
243
|
|
|
235
|
-
async getTapToRuns() {
|
|
244
|
+
async getTapToRuns(isRetry = false) {
|
|
245
|
+
// The TTR token is separate to the main account token and shorter-lived. If
|
|
246
|
+
// we don't have a usable one (eg. restored from an old cache where it was
|
|
247
|
+
// never saved, saved as the literal string 'undefined', or expired) then
|
|
248
|
+
// fetch a fresh one before continuing. This lets a user with a valid main
|
|
249
|
+
// token but a bad TTR token self-heal without a full account re-login.
|
|
250
|
+
if (!this.tokenTTR || this.tokenTTR === 'undefined') {
|
|
251
|
+
await this.loginTTR()
|
|
252
|
+
this.tokenTTRRefreshed = true
|
|
253
|
+
}
|
|
254
|
+
|
|
236
255
|
// Build and send the request
|
|
237
256
|
const res = await axios({
|
|
238
257
|
url: 'https://app2.govee.com/bff-app/v1/exec-plat/home',
|
|
@@ -251,6 +270,13 @@ export default class {
|
|
|
251
270
|
|
|
252
271
|
// Check to see we got a response
|
|
253
272
|
if (!res?.data?.data?.components) {
|
|
273
|
+
// The token may have expired since we obtained it - try one fresh TTR
|
|
274
|
+
// login and retry once before giving up
|
|
275
|
+
if (!isRetry) {
|
|
276
|
+
await this.loginTTR()
|
|
277
|
+
this.tokenTTRRefreshed = true
|
|
278
|
+
return this.getTapToRuns(true)
|
|
279
|
+
}
|
|
254
280
|
throw new Error('not a valid response')
|
|
255
281
|
}
|
|
256
282
|
|
package/lib/device/index.js
CHANGED
|
@@ -46,6 +46,7 @@ import devicePurifierH7128 from './purifier-H7128.js'
|
|
|
46
46
|
import devicePurifierH7129 from './purifier-H7129.js'
|
|
47
47
|
import devicePurifierSingle from './purifier-single.js'
|
|
48
48
|
import deviceSensorButton from './sensor-button.js'
|
|
49
|
+
import deviceSensorCO2 from './sensor-co2.js'
|
|
49
50
|
import deviceSensorContact from './sensor-contact.js'
|
|
50
51
|
import deviceSensorLeak from './sensor-leak.js'
|
|
51
52
|
import deviceSensorMonitor from './sensor-monitor.js'
|
|
@@ -110,6 +111,7 @@ export default {
|
|
|
110
111
|
devicePurifierH7129,
|
|
111
112
|
devicePurifierSingle,
|
|
112
113
|
deviceSensorButton,
|
|
114
|
+
deviceSensorCO2,
|
|
113
115
|
deviceSensorContact,
|
|
114
116
|
deviceSensorLeak,
|
|
115
117
|
deviceSensorMonitor,
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base64ToHex,
|
|
3
|
+
cenToFar,
|
|
4
|
+
getTwoItemPosition,
|
|
5
|
+
hexToTwoItems,
|
|
6
|
+
parseError,
|
|
7
|
+
} from '../utils/functions.js'
|
|
8
|
+
import platformLang from '../utils/lang-en.js'
|
|
9
|
+
|
|
10
|
+
// HomeKit triggers "CO2 detected" abnormal flag at this threshold (ppm).
|
|
11
|
+
// Govee app default warn level is 1000 ppm; override via deviceConf.co2AbnormalThreshold.
|
|
12
|
+
const DEFAULT_CO2_ABNORMAL_PPM = 1000
|
|
13
|
+
|
|
14
|
+
export default class {
|
|
15
|
+
constructor(platform, accessory) {
|
|
16
|
+
// Set up variables from the platform
|
|
17
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
18
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
19
|
+
this.hapServ = platform.api.hap.Service
|
|
20
|
+
this.platform = platform
|
|
21
|
+
|
|
22
|
+
// Set up variables from the accessory
|
|
23
|
+
this.accessory = accessory
|
|
24
|
+
|
|
25
|
+
// Set up custom variables for this device type
|
|
26
|
+
const deviceConf = platform.deviceConf[accessory.context.gvDeviceId] || {}
|
|
27
|
+
this.co2AbnormalThreshold = deviceConf.co2AbnormalThreshold || DEFAULT_CO2_ABNORMAL_PPM
|
|
28
|
+
|
|
29
|
+
// Add the CO2 sensor service (with level + peak characteristics) if it doesn't already exist
|
|
30
|
+
this.co2Service = this.accessory.getService(this.hapServ.CarbonDioxideSensor)
|
|
31
|
+
|| this.accessory.addService(this.hapServ.CarbonDioxideSensor)
|
|
32
|
+
if (!this.co2Service.testCharacteristic(this.hapChar.CarbonDioxideLevel)) {
|
|
33
|
+
this.co2Service.addCharacteristic(this.hapChar.CarbonDioxideLevel)
|
|
34
|
+
}
|
|
35
|
+
if (!this.co2Service.testCharacteristic(this.hapChar.CarbonDioxidePeakLevel)) {
|
|
36
|
+
this.co2Service.addCharacteristic(this.hapChar.CarbonDioxidePeakLevel)
|
|
37
|
+
}
|
|
38
|
+
this.cacheCO2 = this.co2Service.getCharacteristic(this.hapChar.CarbonDioxideLevel).value || 0
|
|
39
|
+
this.cacheCO2Peak = this.co2Service.getCharacteristic(this.hapChar.CarbonDioxidePeakLevel).value || 0
|
|
40
|
+
this.cacheCO2Detected = this.co2Service.getCharacteristic(this.hapChar.CarbonDioxideDetected).value || 0
|
|
41
|
+
|
|
42
|
+
// Add the temperature service if it doesn't already exist
|
|
43
|
+
this.tempService = this.accessory.getService(this.hapServ.TemperatureSensor)
|
|
44
|
+
|| this.accessory.addService(this.hapServ.TemperatureSensor)
|
|
45
|
+
this.cacheTemp = this.tempService.getCharacteristic(this.hapChar.CurrentTemperature).value
|
|
46
|
+
|
|
47
|
+
// Add the humidity service if it doesn't already exist
|
|
48
|
+
this.humiService = this.accessory.getService(this.hapServ.HumiditySensor)
|
|
49
|
+
|| this.accessory.addService(this.hapServ.HumiditySensor)
|
|
50
|
+
this.cacheHumi = this.humiService.getCharacteristic(this.hapChar.CurrentRelativeHumidity).value
|
|
51
|
+
|
|
52
|
+
// No Battery service — H5140 is mains-powered via USB; the Govee cloud
|
|
53
|
+
// stream doesn't carry a meaningful battery level. Remove any stale service
|
|
54
|
+
// left over from earlier versions of this handler.
|
|
55
|
+
const staleBattery = this.accessory.getService(this.hapServ.Battery)
|
|
56
|
+
if (staleBattery) {
|
|
57
|
+
this.accessory.removeService(staleBattery)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.updateCache()
|
|
61
|
+
|
|
62
|
+
// Pass the accessory to Fakegato to set up with Eve
|
|
63
|
+
this.accessory.eveService = new platform.eveService('custom', this.accessory, {
|
|
64
|
+
log: () => {},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// Output the customised options to the log
|
|
68
|
+
const opts = JSON.stringify({
|
|
69
|
+
co2AbnormalThreshold: this.co2AbnormalThreshold,
|
|
70
|
+
})
|
|
71
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async externalUpdate(params) {
|
|
75
|
+
// Parse AWS reading packets — opcode 0x0A carries live CO2 / temp / humidity
|
|
76
|
+
const commands = params.commands || []
|
|
77
|
+
commands.forEach((command) => {
|
|
78
|
+
const hexString = base64ToHex(command)
|
|
79
|
+
const hexParts = hexToTwoItems(hexString)
|
|
80
|
+
if (!hexParts || hexParts.length < 20) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
if (getTwoItemPosition(hexParts, 1) !== 'aa') {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if (getTwoItemPosition(hexParts, 2) !== '0a') {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 1-indexed: position N -> byte (N-1). LE u16: low byte at lower position.
|
|
91
|
+
const u16le = (lsbPos, msbPos) => Number.parseInt(
|
|
92
|
+
`${getTwoItemPosition(hexParts, msbPos)}${getTwoItemPosition(hexParts, lsbPos)}`,
|
|
93
|
+
16,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const offTemp = this.accessory.context.offTemp || 0
|
|
97
|
+
const offHumi = this.accessory.context.offHumi || 0
|
|
98
|
+
|
|
99
|
+
const tempRaw = u16le(3, 4) // bytes 2-3 of packet, °C × 100
|
|
100
|
+
const humiRaw = u16le(5, 6) // bytes 4-5, %RH × 100
|
|
101
|
+
const co2Raw = u16le(7, 8) // bytes 6-7, ppm
|
|
102
|
+
|
|
103
|
+
const newTemp = Math.round(tempRaw + offTemp) / 100
|
|
104
|
+
const newHumi = Math.max(0, Math.min(100, Math.round((humiRaw + offHumi) / 100)))
|
|
105
|
+
const newCO2 = co2Raw
|
|
106
|
+
|
|
107
|
+
if (newTemp !== this.cacheTemp && newTemp > -40 && newTemp < 100) {
|
|
108
|
+
this.cacheTemp = newTemp
|
|
109
|
+
this.tempService.updateCharacteristic(this.hapChar.CurrentTemperature, this.cacheTemp)
|
|
110
|
+
this.accessory.eveService.addEntry({ temp: this.cacheTemp })
|
|
111
|
+
this.accessory.log(`${platformLang.curTemp} [${this.cacheTemp}°C / ${cenToFar(this.cacheTemp)}°F]`)
|
|
112
|
+
this.updateCache()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (newHumi !== this.cacheHumi) {
|
|
116
|
+
this.cacheHumi = newHumi
|
|
117
|
+
this.humiService.updateCharacteristic(this.hapChar.CurrentRelativeHumidity, this.cacheHumi)
|
|
118
|
+
this.accessory.eveService.addEntry({ humidity: this.cacheHumi })
|
|
119
|
+
this.accessory.log(`${platformLang.curHumi} [${this.cacheHumi}%]`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (newCO2 !== this.cacheCO2 && newCO2 >= 0 && newCO2 <= 40000) {
|
|
123
|
+
this.cacheCO2 = newCO2
|
|
124
|
+
this.co2Service.updateCharacteristic(this.hapChar.CarbonDioxideLevel, this.cacheCO2)
|
|
125
|
+
|
|
126
|
+
if (newCO2 > this.cacheCO2Peak) {
|
|
127
|
+
this.cacheCO2Peak = newCO2
|
|
128
|
+
this.co2Service.updateCharacteristic(this.hapChar.CarbonDioxidePeakLevel, this.cacheCO2Peak)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const detected = newCO2 >= this.co2AbnormalThreshold ? 1 : 0
|
|
132
|
+
if (detected !== this.cacheCO2Detected) {
|
|
133
|
+
this.cacheCO2Detected = detected
|
|
134
|
+
this.co2Service.updateCharacteristic(this.hapChar.CarbonDioxideDetected, detected)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.accessory.eveService.addEntry({ ppm: this.cacheCO2 })
|
|
138
|
+
this.accessory.log(`${platformLang.curCO2} [${this.cacheCO2} ppm]`)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async updateCache() {
|
|
144
|
+
if (!this.platform.storageClientData) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
await this.platform.storageData.setItem(
|
|
149
|
+
`${this.accessory.context.gvDeviceId}_temp`,
|
|
150
|
+
this.cacheTemp,
|
|
151
|
+
)
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this.accessory.logWarn(`${platformLang.storageWriteErr} ${parseError(err)}`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
package/lib/platform.js
CHANGED
|
@@ -479,8 +479,8 @@ export default class {
|
|
|
479
479
|
|
|
480
480
|
this.accountId = data.accountId
|
|
481
481
|
this.accountTopic = data.topic
|
|
482
|
-
|
|
483
|
-
|
|
482
|
+
this.accountToken = data.token
|
|
483
|
+
this.accountTokenTTR = data.tokenTTR
|
|
484
484
|
this.clientId = data.client
|
|
485
485
|
this.iotEndpoint = data.endpoint
|
|
486
486
|
this.iotPass = data.iotPass
|
|
@@ -489,14 +489,7 @@ export default class {
|
|
|
489
489
|
await promises.writeFile(iotFile, Buffer.from(data.iot, 'base64'))
|
|
490
490
|
|
|
491
491
|
// Try and save these to the cache for future reference
|
|
492
|
-
|
|
493
|
-
await this.storageData.setItem(
|
|
494
|
-
'Govee_All_Devices_temp',
|
|
495
|
-
`${this.accountTopic}:::${accountToken}:::${this.config.username}:::${this.accountId}:::${this.iotEndpoint}:::${this.iotPass}:::${accountTokenTTR}`,
|
|
496
|
-
)
|
|
497
|
-
} catch (e) {
|
|
498
|
-
this.log.warn('[HTTP] %s %s.', platformLang.accTokenStoreErr, parseError(e))
|
|
499
|
-
}
|
|
492
|
+
await this.persistAccountCache()
|
|
500
493
|
await getDevices()
|
|
501
494
|
}
|
|
502
495
|
|
|
@@ -806,6 +799,16 @@ export default class {
|
|
|
806
799
|
if (this.httpClient) {
|
|
807
800
|
try {
|
|
808
801
|
const scenes = await this.httpClient.getTapToRuns()
|
|
802
|
+
|
|
803
|
+
// If the TTR token had to be refreshed (eg. it was missing or expired
|
|
804
|
+
// in the cache), write the new one back so the next startup is fixed
|
|
805
|
+
if (this.httpClient.tokenTTRRefreshed) {
|
|
806
|
+
this.accountTokenTTR = this.httpClient.tokenTTR
|
|
807
|
+
this.httpClient.tokenTTRRefreshed = false
|
|
808
|
+
await this.persistAccountCache()
|
|
809
|
+
this.log.debug('[HTTP] refreshed TTR token saved to cache.')
|
|
810
|
+
}
|
|
811
|
+
|
|
809
812
|
scenes.forEach((scene) => {
|
|
810
813
|
if (scene.oneClicks) {
|
|
811
814
|
scene.oneClicks.forEach((oneClick) => {
|
|
@@ -946,6 +949,20 @@ export default class {
|
|
|
946
949
|
this.log('----------------------------')
|
|
947
950
|
}
|
|
948
951
|
|
|
952
|
+
async persistAccountCache() {
|
|
953
|
+
// Persist the account details to the cache for future startups, so we can
|
|
954
|
+
// skip a full login. Kept in one place so the TTR self-heal path can also
|
|
955
|
+
// re-save once it has refreshed the (separate, shorter-lived) TTR token.
|
|
956
|
+
try {
|
|
957
|
+
await this.storageData.setItem(
|
|
958
|
+
'Govee_All_Devices_temp',
|
|
959
|
+
`${this.accountTopic}:::${this.accountToken}:::${this.config.username}:::${this.accountId}:::${this.iotEndpoint}:::${this.iotPass}:::${this.accountTokenTTR}`,
|
|
960
|
+
)
|
|
961
|
+
} catch (err) {
|
|
962
|
+
this.log.warn('[HTTP] %s %s.', platformLang.accTokenStoreErr, parseError(err))
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
949
966
|
pluginShutdown() {
|
|
950
967
|
// A function that is called when the plugin fails to load or Homebridge restarts
|
|
951
968
|
try {
|
|
@@ -1150,6 +1167,11 @@ export default class {
|
|
|
1150
1167
|
devInstance = deviceTypes.deviceSensorMonitor
|
|
1151
1168
|
doAWSPolling = true
|
|
1152
1169
|
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1170
|
+
} else if (platformConsts.models.sensorCO2.includes(device.model)) {
|
|
1171
|
+
// Device is a CO2 + temperature + humidity monitor (H5140)
|
|
1172
|
+
devInstance = deviceTypes.deviceSensorCO2
|
|
1173
|
+
doAWSPolling = true
|
|
1174
|
+
accessory = devicesInHB.get(uuid) || this.addAccessory(device)
|
|
1153
1175
|
} else if (platformConsts.models.fan.includes(device.model)) {
|
|
1154
1176
|
// Device is a fan
|
|
1155
1177
|
devInstance = deviceTypes[`deviceFan${device.model}`]
|
package/lib/utils/constants.js
CHANGED
|
@@ -129,6 +129,8 @@ export default {
|
|
|
129
129
|
models: {
|
|
130
130
|
rgb: [
|
|
131
131
|
'H1401',
|
|
132
|
+
'H16B0', // https://github.com/homebridge-plugins/homebridge-govee/issues/1278
|
|
133
|
+
'H1741', // https://github.com/homebridge-plugins/homebridge-govee/issues/1278
|
|
132
134
|
'H6001',
|
|
133
135
|
'H6002',
|
|
134
136
|
'H6003',
|
|
@@ -515,6 +517,7 @@ export default {
|
|
|
515
517
|
],
|
|
516
518
|
sensorThermo4: ['H5198'],
|
|
517
519
|
sensorMonitor: ['H5106'],
|
|
520
|
+
sensorCO2: ['H5140'], // CO2 + temp + humidity monitor, AWS opcode 0x0A — closes #1179
|
|
518
521
|
fan: ['H7100', 'H7101', 'H7102', 'H7105', 'H7106', 'H7107', 'H7111'],
|
|
519
522
|
heater1: ['H7130', 'H713A', 'H713B', 'H713C'],
|
|
520
523
|
heater2: ['H7131', 'H7132', 'H7133', 'H7134', 'H7135'],
|
|
@@ -542,7 +545,6 @@ export default {
|
|
|
542
545
|
'H5126', // https://github.com/homebridge-plugins/homebridge-govee/issues/910
|
|
543
546
|
'H5129', // https://github.com/homebridge-plugins/homebridge-govee/issues/1084
|
|
544
547
|
'H5125', // https://github.com/homebridge-plugins/homebridge-govee/issues/981
|
|
545
|
-
'H5140', // https://github.com/homebridge-plugins/homebridge-govee/issues/1179
|
|
546
548
|
'H5185', // https://github.com/homebridge-plugins/homebridge-govee/issues/804
|
|
547
549
|
'H5191', // https://github.com/homebridge-plugins/homebridge-govee/issues/1121
|
|
548
550
|
],
|
package/lib/utils/lang-en.js
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@homebridge-plugins/homebridge-govee",
|
|
3
3
|
"alias": "Govee",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "11.22.0",
|
|
5
|
+
"version": "11.22.1-beta.0",
|
|
6
6
|
"description": "Homebridge plugin to integrate Govee devices into HomeKit.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "bwp91",
|
|
@@ -58,19 +58,19 @@
|
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@homebridge/plugin-ui-utils": "^2.2.3",
|
|
60
60
|
"aws-iot-device-sdk": "^2.2.16",
|
|
61
|
-
"axios": "^1.16.
|
|
61
|
+
"axios": "^1.16.1",
|
|
62
62
|
"mqtt": "^5.12.1",
|
|
63
63
|
"node-persist": "^4.0.4",
|
|
64
64
|
"node-rsa": "^1.1.1",
|
|
65
|
-
"p-queue": "^9.
|
|
65
|
+
"p-queue": "^9.3.0",
|
|
66
66
|
"patch-package": "^8.0.1",
|
|
67
67
|
"pem": "^1.14.8"
|
|
68
68
|
},
|
|
69
69
|
"optionalDependencies": {
|
|
70
70
|
"@stoprocent/bluetooth-hci-socket": "^2.2.6",
|
|
71
|
-
"@stoprocent/noble": "^2.
|
|
71
|
+
"@stoprocent/noble": "^2.5.3"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@antfu/eslint-config": "^
|
|
74
|
+
"@antfu/eslint-config": "^9.0.0"
|
|
75
75
|
}
|
|
76
76
|
}
|