@homebridge-plugins/homebridge-ecovacs 7.0.2 → 7.1.4-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 +49 -7
- package/README.md +6 -4
- package/config.schema.json +7 -6
- package/lib/homebridge-ui/public/index.html +39 -3
- package/lib/platform.js +357 -34
- package/lib/utils/custom-chars.js +34 -33
- package/lib/utils/lang-en.js +1 -1
- package/package.json +10 -10
- package/patches/{ecovacs-deebot+0.9.6-beta.8.patch → ecovacs-deebot+0.9.6-beta.12.patch} +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,11 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to homebridge-ecovacs will be documented in this file.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## v7.2.4 (Unreleased)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- updated dependencies
|
|
10
|
+
- option to expose as a vacuum robot in homekit
|
|
11
|
+
|
|
12
|
+
## v7.2.3 (2025-07-24)
|
|
13
|
+
|
|
14
|
+
### Other Changes
|
|
15
|
+
|
|
16
|
+
- dependency updates
|
|
17
|
+
|
|
18
|
+
## v7.2.2 (2025-07-18)
|
|
19
|
+
|
|
20
|
+
### Other Changes
|
|
21
|
+
|
|
22
|
+
- add maintainer message
|
|
23
|
+
|
|
24
|
+
## v7.2.1 (2025-07-16)
|
|
25
|
+
|
|
26
|
+
### Other Changes
|
|
27
|
+
|
|
28
|
+
- clear cache values on reset, fix go charge on
|
|
29
|
+
|
|
30
|
+
## v7.2.0 (2025-07-13)
|
|
31
|
+
|
|
32
|
+
### Notable Changes
|
|
33
|
+
|
|
34
|
+
- fix custom plugin config modal styles in ui 5
|
|
35
|
+
- fix custom characteristics for hb 2
|
|
36
|
+
|
|
37
|
+
### Other Changes
|
|
38
|
+
|
|
39
|
+
- add `lint` step to build workflow
|
|
40
|
+
- add permissions to workflows
|
|
41
|
+
- improvements to the deprecate workflow
|
|
42
|
+
|
|
43
|
+
## v7.1.0 (2025-07-12)
|
|
44
|
+
|
|
45
|
+
### Notable Changes
|
|
46
|
+
|
|
47
|
+
- set `strictValidation` to `true` in the config schema file
|
|
48
|
+
|
|
49
|
+
### Other Changes
|
|
50
|
+
|
|
51
|
+
- github repo maintenance
|
|
10
52
|
|
|
11
53
|
## v7.0.2 (2025-07-11)
|
|
12
54
|
|
|
@@ -296,7 +338,7 @@ This project tries to adhere to [Semantic Versioning](http://semver.org/). In pr
|
|
|
296
338
|
|
|
297
339
|
- Ignore `Robot is operational` error in log
|
|
298
340
|
- Updated `ecovacs-deebot` library to v0.6.3
|
|
299
|
-
- Remove `node-machine-id` in favour of generating a client id based on
|
|
341
|
+
- Remove `node-machine-id` in favour of generating a client id based on Ecovacs username
|
|
300
342
|
|
|
301
343
|
## v4.1.0 (2021-08-30)
|
|
302
344
|
|
|
@@ -338,7 +380,7 @@ _Unpublished_
|
|
|
338
380
|
|
|
339
381
|
### Added
|
|
340
382
|
|
|
341
|
-
- Support for cleaning 'Spot Areas' customised in the
|
|
383
|
+
- Support for cleaning 'Spot Areas' customised in the Ecovacs app
|
|
342
384
|
|
|
343
385
|
## v3.3.1 (2021-07-18)
|
|
344
386
|
|
|
@@ -442,7 +484,7 @@ _Unpublished_
|
|
|
442
484
|
|
|
443
485
|
- Configuration settings per Deebot device
|
|
444
486
|
- Support for Chinese server login
|
|
445
|
-
- Enter your
|
|
487
|
+
- Enter your Ecovacs password as a base64 encoded string and use the option `encodedPassword` to let the plugin know
|
|
446
488
|
- More viewable information in the Homebridge plugin-ui:
|
|
447
489
|
- Device model, company and an image of your device in case you didn't know what it looked like
|
|
448
490
|
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
# homebridge-ecovacs
|
|
7
7
|
|
|
8
|
-
Homebridge plugin to integrate
|
|
8
|
+
Homebridge plugin to integrate Ecovacs Deebot or Yeedi devices into HomeKit
|
|
9
9
|
|
|
10
10
|
[](https://www.npmjs.com/package/@homebridge-plugins/homebridge-ecovacs)
|
|
11
11
|
[](https://github.com/homebridge-plugins/homebridge-ecovacs/wiki/Beta-Version)
|
|
@@ -20,10 +20,12 @@ Homebridge plugin to integrate ECOVACS Deebot or Yeedi devices into HomeKit
|
|
|
20
20
|
|
|
21
21
|
### Plugin Information
|
|
22
22
|
|
|
23
|
-
- This plugin allows you to view and control your
|
|
24
|
-
- requires your
|
|
23
|
+
- This plugin allows you to view and control your Ecovacs or Yeedi devices within HomeKit. The plugin:
|
|
24
|
+
- requires your Ecovacs or Yeedi credentials to function
|
|
25
25
|
- uses a cloud-based connection (i.e. requires the internet)
|
|
26
26
|
|
|
27
|
+
> I'm looking for some lovely people to help maintain this plugin, please get in touch on GitHub or Discord if you'd like to help out 😄
|
|
28
|
+
|
|
27
29
|
### Prerequisites
|
|
28
30
|
|
|
29
31
|
- To use this plugin, you will need to already have:
|
|
@@ -59,5 +61,5 @@ Homebridge plugin to integrate ECOVACS Deebot or Yeedi devices into HomeKit
|
|
|
59
61
|
|
|
60
62
|
### Disclaimer
|
|
61
63
|
|
|
62
|
-
- I am in no way affiliated with
|
|
64
|
+
- I am in no way affiliated with Ecovacs/Deebot/Yeedi and this plugin is a personal project that I maintain in my free time.
|
|
63
65
|
- Use this plugin entirely at your own risk - please see licence for more information.
|
package/config.schema.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"pluginAlias": "Deebot",
|
|
3
3
|
"pluginType": "platform",
|
|
4
4
|
"singular": true,
|
|
5
|
+
"strictValidation": true,
|
|
5
6
|
"customUi": true,
|
|
6
7
|
"customUiPath": "./lib/homebridge-ui",
|
|
7
8
|
"headerDisplay": "<p align=\"center\"><img width=\"60%\" src=\"https://user-images.githubusercontent.com/43026681/101321841-f0eb5280-385d-11eb-8dd4-f57113f6e078.png\"></p><p align=\"center\">For help and support please visit our <a href=\"https://github.com/homebridge-plugins/homebridge-ecovacs/wiki\">GitHub Wiki</a>. We hope you find this plugin useful!</p>",
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
"useYeedi": {
|
|
17
18
|
"title": "Use Yeedi Login",
|
|
18
19
|
"type": "boolean",
|
|
19
|
-
"description": "Enable this for Yeedi login instead of
|
|
20
|
+
"description": "Enable this for Yeedi login instead of Ecovacs."
|
|
20
21
|
},
|
|
21
22
|
"countryCode": {
|
|
22
23
|
"title": "Country Code",
|
|
@@ -25,13 +26,13 @@
|
|
|
25
26
|
"required": true
|
|
26
27
|
},
|
|
27
28
|
"username": {
|
|
28
|
-
"title": "
|
|
29
|
+
"title": "Ecovacs/Yeedi Username",
|
|
29
30
|
"required": true,
|
|
30
31
|
"type": "string",
|
|
31
|
-
"description": "Your username, if your account is based in China you should use your
|
|
32
|
+
"description": "Your username, if your account is based in China you should use your Ecovacs ID."
|
|
32
33
|
},
|
|
33
34
|
"password": {
|
|
34
|
-
"title": "
|
|
35
|
+
"title": "Ecovacs/Yeedi Password",
|
|
35
36
|
"required": true,
|
|
36
37
|
"type": "string",
|
|
37
38
|
"description": "Your password, can also be a base64 encoded version of your password."
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
"deviceId": {
|
|
57
58
|
"title": "Device ID",
|
|
58
59
|
"type": "string",
|
|
59
|
-
"description": "
|
|
60
|
+
"description": "Ecovacs Device ID of the device these settings apply to, can be of format E2000000000000000000 or 11111111-aaaa-bbbb-2222-cccccccccccc."
|
|
60
61
|
},
|
|
61
62
|
"ignoreDevice": {
|
|
62
63
|
"type": "boolean",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"title": "Polling Interval",
|
|
71
72
|
"type": "integer",
|
|
72
73
|
"placeholder": 120,
|
|
73
|
-
"description": "An interval (in seconds) in which this device will refresh with
|
|
74
|
+
"description": "An interval (in seconds) in which this device will refresh with Ecovacs. Set to 0 to disable, otherwise must be 30 or more."
|
|
74
75
|
},
|
|
75
76
|
"hideMotionSensor": {
|
|
76
77
|
"title": "Hide Motion Sensor",
|
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
.dark-mode {
|
|
3
|
+
background-color: #242424;
|
|
4
|
+
color: lightgrey;
|
|
5
|
+
|
|
6
|
+
.form-control {
|
|
7
|
+
background-color: #333333 !important;
|
|
8
|
+
border: none !important;
|
|
9
|
+
color: #eeeeee !important;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
thead, tbody, tr {
|
|
13
|
+
border-style: hidden;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
th {
|
|
17
|
+
color: #eeeeee !important;
|
|
18
|
+
font-weight: 500 !important;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
select {
|
|
22
|
+
background-image:
|
|
23
|
+
linear-gradient(45deg, transparent 50%, gray 50%),
|
|
24
|
+
linear-gradient(135deg, gray 50%, transparent 50%),
|
|
25
|
+
linear-gradient(to right, #ccc, #ccc);
|
|
26
|
+
background-position:
|
|
27
|
+
calc(100% - 20px) calc(1em + 2px),
|
|
28
|
+
calc(100% - 15px) calc(1em + 2px),
|
|
29
|
+
calc(100% - 2.5em) 0.5em;
|
|
30
|
+
background-size:
|
|
31
|
+
5px 5px,
|
|
32
|
+
5px 5px,
|
|
33
|
+
1px 1.5em;
|
|
34
|
+
background-repeat: no-repeat;
|
|
35
|
+
}
|
|
36
|
+
</style>
|
|
1
37
|
<p class="text-center">
|
|
2
38
|
<img
|
|
3
39
|
src="https://user-images.githubusercontent.com/43026681/101321841-f0eb5280-385d-11eb-8dd4-f57113f6e078.png"
|
|
@@ -7,7 +43,7 @@
|
|
|
7
43
|
</p>
|
|
8
44
|
<div id="pageIntro" class="text-center" style="display: none;">
|
|
9
45
|
<p class="lead">Thank you for installing <strong>homebridge-ecovacs</strong></p>
|
|
10
|
-
<p>You will need to enter your
|
|
46
|
+
<p>You will need to enter your Ecovacs username, password and region on the next page</p>
|
|
11
47
|
<button type="button" class="btn btn-primary" id="introContinue">Continue →</button>
|
|
12
48
|
</div>
|
|
13
49
|
<div
|
|
@@ -28,7 +64,7 @@
|
|
|
28
64
|
<select class="form-control" id="deviceSelect"></select>
|
|
29
65
|
</div>
|
|
30
66
|
</form>
|
|
31
|
-
<table class="table w-100" id="deviceTable" style="display: none;">
|
|
67
|
+
<table class="table w-100 mt-3" id="deviceTable" style="display: none;">
|
|
32
68
|
<thead>
|
|
33
69
|
<tr class="table-active">
|
|
34
70
|
<th scope="col" style="width: 40%;">Device Name</th>
|
|
@@ -161,7 +197,7 @@
|
|
|
161
197
|
<h4>Disclaimer</h4>
|
|
162
198
|
<ul>
|
|
163
199
|
<li>
|
|
164
|
-
I am in no way affiliated with
|
|
200
|
+
I am in no way affiliated with Ecovacs and this plugin is a personal project that I maintain
|
|
165
201
|
in my free time.
|
|
166
202
|
</li>
|
|
167
203
|
<li>Use this plugin entirely at your own risk - please see licence for more information.</li>
|
package/lib/platform.js
CHANGED
|
@@ -13,6 +13,7 @@ const require = createRequire(import.meta.url)
|
|
|
13
13
|
const plugin = require('../package.json')
|
|
14
14
|
|
|
15
15
|
const devicesInHB = new Map()
|
|
16
|
+
const matterDevicesInHB = new Map()
|
|
16
17
|
|
|
17
18
|
export default class {
|
|
18
19
|
constructor(log, config, api) {
|
|
@@ -26,6 +27,9 @@ export default class {
|
|
|
26
27
|
this.log = log
|
|
27
28
|
this.isBeta = plugin.version.includes('beta')
|
|
28
29
|
|
|
30
|
+
// Check if Matter is available and enabled
|
|
31
|
+
this.matterEnabled = api.isMatterAvailable?.() && api.isMatterEnabled?.()
|
|
32
|
+
|
|
29
33
|
// Configuration objects for accessories
|
|
30
34
|
this.deviceConf = {}
|
|
31
35
|
this.ignoredDevices = []
|
|
@@ -363,7 +367,7 @@ export default class {
|
|
|
363
367
|
// Require any libraries that the accessory instances use
|
|
364
368
|
this.cusChar = new platformChars(this.api)
|
|
365
369
|
|
|
366
|
-
// Connect to
|
|
370
|
+
// Connect to Ecovacs/Yeedi
|
|
367
371
|
this.ecovacsAPI = new EcoVacsAPI(
|
|
368
372
|
EcoVacsAPI.getDeviceId(this.api.hap.uuid.generate(this.config.username)),
|
|
369
373
|
this.config.countryCode,
|
|
@@ -374,7 +378,14 @@ export default class {
|
|
|
374
378
|
// Display version of the ecovacs-deebot library in the log
|
|
375
379
|
this.log('%s v%s.', platformLang.ecovacsLibVersion, this.ecovacsAPI.getVersion())
|
|
376
380
|
|
|
377
|
-
//
|
|
381
|
+
// Log Matter availability
|
|
382
|
+
if (this.matterEnabled) {
|
|
383
|
+
this.log('Matter support is enabled - vacuums will be exposed via both HAP and Matter.')
|
|
384
|
+
} else {
|
|
385
|
+
this.log('Matter support is not available - vacuums will be exposed via HAP only.')
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Attempt to log in to Ecovacs/Yeedi
|
|
378
389
|
try {
|
|
379
390
|
await this.ecovacsAPI.connect(this.config.username, EcoVacsAPI.md5(this.config.password))
|
|
380
391
|
} catch (err) {
|
|
@@ -393,7 +404,7 @@ export default class {
|
|
|
393
404
|
}
|
|
394
405
|
}
|
|
395
406
|
|
|
396
|
-
// Get a device list from
|
|
407
|
+
// Get a device list from Ecovacs/Yeedi
|
|
397
408
|
const deviceList = await this.ecovacsAPI.devices()
|
|
398
409
|
|
|
399
410
|
// Check the request for device list was successful
|
|
@@ -402,7 +413,7 @@ export default class {
|
|
|
402
413
|
}
|
|
403
414
|
|
|
404
415
|
// Initialise each device into Homebridge
|
|
405
|
-
this.log('[%s] %s.', deviceList.length, platformLang.deviceCount(this.config.useYeedi ? 'Yeedi' : '
|
|
416
|
+
this.log('[%s] %s.', deviceList.length, platformLang.deviceCount(this.config.useYeedi ? 'Yeedi' : 'Ecovacs'))
|
|
406
417
|
for (let i = 0; i < deviceList.length; i += 1) {
|
|
407
418
|
await this.initialiseDevice(deviceList[i])
|
|
408
419
|
}
|
|
@@ -429,7 +440,7 @@ export default class {
|
|
|
429
440
|
clearInterval(this.refreshIntervals[id])
|
|
430
441
|
})
|
|
431
442
|
|
|
432
|
-
// Disconnect from each
|
|
443
|
+
// Disconnect from each Ecovacs/Yeedi device
|
|
433
444
|
devicesInHB.forEach((accessory) => {
|
|
434
445
|
if (accessory.control?.is_ready) {
|
|
435
446
|
accessory.control.disconnect()
|
|
@@ -440,6 +451,230 @@ export default class {
|
|
|
440
451
|
}
|
|
441
452
|
}
|
|
442
453
|
|
|
454
|
+
createMatterRVCAccessory(device, accessory) {
|
|
455
|
+
// Create a Matter RVC (Robotic Vacuum Cleaner) accessory config
|
|
456
|
+
const serialNumber = device.did
|
|
457
|
+
const displayName = device.nick || device.did
|
|
458
|
+
|
|
459
|
+
// Get device types we need
|
|
460
|
+
const RoboticVacuumCleanerType = this.api.matter.deviceTypes.RoboticVacuumCleaner
|
|
461
|
+
|
|
462
|
+
// Create bound handler functions that preserve the platform context
|
|
463
|
+
const boundHandlers = {
|
|
464
|
+
rvcRunMode: {
|
|
465
|
+
changeToMode: (request) => {
|
|
466
|
+
return this.handleMatterRunModeChange(accessory, request)
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
rvcOperationalState: {
|
|
470
|
+
pause: () => {
|
|
471
|
+
return this.handleMatterPause(accessory)
|
|
472
|
+
},
|
|
473
|
+
stop: () => {
|
|
474
|
+
return this.handleMatterStop(accessory)
|
|
475
|
+
},
|
|
476
|
+
start: () => {
|
|
477
|
+
return this.handleMatterStart(accessory)
|
|
478
|
+
},
|
|
479
|
+
resume: () => {
|
|
480
|
+
return this.handleMatterResume(accessory)
|
|
481
|
+
},
|
|
482
|
+
goHome: () => {
|
|
483
|
+
return this.handleMatterGoHome(accessory)
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
uuid: this.api.matter.uuid.generate(serialNumber),
|
|
490
|
+
displayName,
|
|
491
|
+
deviceType: RoboticVacuumCleanerType,
|
|
492
|
+
serialNumber,
|
|
493
|
+
manufacturer: device.company || 'Ecovacs',
|
|
494
|
+
model: device.deviceModel || 'Deebot',
|
|
495
|
+
firmwareRevision: '1.0.0',
|
|
496
|
+
hardwareRevision: '1.0.0',
|
|
497
|
+
|
|
498
|
+
context: {
|
|
499
|
+
hapAccessoryUUID: accessory.UUID,
|
|
500
|
+
deviceId: device.did,
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
clusters: {
|
|
504
|
+
// Run Mode: Idle or Cleaning
|
|
505
|
+
rvcRunMode: {
|
|
506
|
+
supportedModes: [
|
|
507
|
+
{ label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] }, // RvcRunMode.ModeTag.Idle
|
|
508
|
+
{ label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] }, // RvcRunMode.ModeTag.Cleaning
|
|
509
|
+
],
|
|
510
|
+
currentMode: 0, // Start as Idle
|
|
511
|
+
},
|
|
512
|
+
// Clean Mode: Just Vacuum mode
|
|
513
|
+
rvcCleanMode: {
|
|
514
|
+
supportedModes: [
|
|
515
|
+
{ label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] }, // RvcCleanMode.ModeTag.Vacuum
|
|
516
|
+
],
|
|
517
|
+
currentMode: 0,
|
|
518
|
+
},
|
|
519
|
+
// Operational State
|
|
520
|
+
rvcOperationalState: {
|
|
521
|
+
operationalStateList: [
|
|
522
|
+
{ operationalStateId: 0, operationalStateLabel: 'Stopped' },
|
|
523
|
+
{ operationalStateId: 1, operationalStateLabel: 'Running' },
|
|
524
|
+
{ operationalStateId: 2, operationalStateLabel: 'Paused' },
|
|
525
|
+
{ operationalStateId: 3, operationalStateLabel: 'Error' },
|
|
526
|
+
{ operationalStateId: 64, operationalStateLabel: 'Seeking Charger' },
|
|
527
|
+
{ operationalStateId: 65, operationalStateLabel: 'Charging' },
|
|
528
|
+
{ operationalStateId: 66, operationalStateLabel: 'Docked' },
|
|
529
|
+
],
|
|
530
|
+
operationalState: 66, // Start docked
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
handlers: boundHandlers,
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async handleMatterRunModeChange(accessory, request) {
|
|
539
|
+
try {
|
|
540
|
+
const { newMode } = request
|
|
541
|
+
accessory.logDebug(`Matter: ChangeToMode (run) request received: mode=${newMode}`)
|
|
542
|
+
|
|
543
|
+
if (newMode === 1) {
|
|
544
|
+
// Switching to Cleaning mode - start cleaning
|
|
545
|
+
await this.internalCleanUpdate(accessory, true)
|
|
546
|
+
} else if (newMode === 0) {
|
|
547
|
+
// Switching to Idle mode - stop or return to dock
|
|
548
|
+
await this.internalChargeUpdate(accessory, true)
|
|
549
|
+
}
|
|
550
|
+
} catch (err) {
|
|
551
|
+
accessory.logWarn(`Matter: Run mode change failed ${parseError(err)}`)
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async handleMatterPause(accessory) {
|
|
556
|
+
try {
|
|
557
|
+
accessory.logDebug('Matter: Pause request received')
|
|
558
|
+
// Stop the vacuum
|
|
559
|
+
// TODO work out which devices have a pause and implement properly
|
|
560
|
+
await this.internalCleanUpdate(accessory, false)
|
|
561
|
+
|
|
562
|
+
// Update Matter states immediately
|
|
563
|
+
this.updateMatterRunMode(accessory, 0) // idle
|
|
564
|
+
this.updateMatterOperationalState(accessory, 0) // stopped
|
|
565
|
+
} catch (err) {
|
|
566
|
+
accessory.logWarn(`Matter: Pause failed ${parseError(err)}`)
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async handleMatterStop(accessory) {
|
|
571
|
+
try {
|
|
572
|
+
accessory.logDebug('Matter: Stop request received')
|
|
573
|
+
await this.internalCleanUpdate(accessory, false)
|
|
574
|
+
|
|
575
|
+
// Update Matter states immediately
|
|
576
|
+
this.updateMatterRunMode(accessory, 0) // idle
|
|
577
|
+
this.updateMatterOperationalState(accessory, 0) // stopped
|
|
578
|
+
} catch (err) {
|
|
579
|
+
accessory.logWarn(`Matter: Stop failed ${parseError(err)}`)
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async handleMatterStart(accessory) {
|
|
584
|
+
try {
|
|
585
|
+
accessory.logDebug('Matter: Start request received')
|
|
586
|
+
await this.internalCleanUpdate(accessory, true)
|
|
587
|
+
} catch (err) {
|
|
588
|
+
accessory.logWarn(`Matter: Start failed ${parseError(err)}`)
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
async handleMatterResume(accessory) {
|
|
593
|
+
try {
|
|
594
|
+
accessory.logDebug('Matter: Resume request received')
|
|
595
|
+
await this.internalCleanUpdate(accessory, true)
|
|
596
|
+
} catch (err) {
|
|
597
|
+
accessory.logWarn(`Matter: Resume failed ${parseError(err)}`)
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async handleMatterGoHome(accessory) {
|
|
602
|
+
try {
|
|
603
|
+
accessory.logDebug('Matter: GoHome request received')
|
|
604
|
+
await this.internalChargeUpdate(accessory, true)
|
|
605
|
+
} catch (err) {
|
|
606
|
+
accessory.logWarn(`Matter: GoHome failed ${parseError(err)}`)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
updateMatterOperationalState(accessory, state) {
|
|
611
|
+
// Update Matter operational state if Matter is enabled and accessory has Matter UUID
|
|
612
|
+
if (!this.matterEnabled || !accessory.matterUUID || !accessory.matterReady) {
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
try {
|
|
617
|
+
this.api.matter.updateAccessoryState(accessory.matterUUID, 'rvcOperationalState', {
|
|
618
|
+
operationalState: state,
|
|
619
|
+
})
|
|
620
|
+
accessory.logDebug(`Matter: Operational state updated to ${state}`)
|
|
621
|
+
} catch (err) {
|
|
622
|
+
accessory.logError(`Matter: Failed to update operational state: ${parseError(err)}`)
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
updateMatterRunMode(accessory, mode) {
|
|
627
|
+
// Update Matter run mode if Matter is enabled and accessory has Matter UUID
|
|
628
|
+
if (!this.matterEnabled || !accessory.matterUUID || !accessory.matterReady) {
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
this.api.matter.updateAccessoryState(accessory.matterUUID, 'rvcRunMode', {
|
|
634
|
+
currentMode: mode,
|
|
635
|
+
})
|
|
636
|
+
accessory.logDebug(`Matter: Run mode updated to ${mode}`)
|
|
637
|
+
} catch (err) {
|
|
638
|
+
accessory.logError(`Matter: Failed to update run mode: ${parseError(err)}`)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
updateMatterStateFromHAP(accessory) {
|
|
643
|
+
// Sync current HAP state to Matter when Matter becomes ready
|
|
644
|
+
if (!this.matterEnabled || !accessory.matterUUID || !accessory.matterReady) {
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
// Sync cleaning state
|
|
650
|
+
const isActive = ['auto', 'clean', 'edge', 'spot', 'spotarea', 'customarea'].includes(
|
|
651
|
+
(accessory.context.cacheClean || '').toLowerCase().replace(/[^a-z]+/g, ''),
|
|
652
|
+
)
|
|
653
|
+
if (isActive) {
|
|
654
|
+
this.updateMatterRunMode(accessory, 1) // Cleaning
|
|
655
|
+
this.updateMatterOperationalState(accessory, 1) // Running
|
|
656
|
+
} else {
|
|
657
|
+
this.updateMatterRunMode(accessory, 0) // Idle
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Sync charging state
|
|
661
|
+
const chargeStateLower = (accessory.context.cacheCharge || '').toLowerCase()
|
|
662
|
+
if (chargeStateLower === 'charging') {
|
|
663
|
+
this.updateMatterOperationalState(accessory, 65) // charging
|
|
664
|
+
} else if (chargeStateLower === 'returning') {
|
|
665
|
+
this.updateMatterOperationalState(accessory, 64) // seeking charger
|
|
666
|
+
} else if (chargeStateLower === 'idle') {
|
|
667
|
+
this.updateMatterOperationalState(accessory, 66) // docked
|
|
668
|
+
} else if (!isActive) {
|
|
669
|
+
this.updateMatterOperationalState(accessory, 0) // stopped
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
accessory.logDebug('Matter: Initial state synchronized from HAP')
|
|
673
|
+
} catch (err) {
|
|
674
|
+
accessory.logError(`Matter: Failed to sync initial state: ${parseError(err)}`)
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
443
678
|
initialiseDevice(device) {
|
|
444
679
|
try {
|
|
445
680
|
// Generate the Homebridge UUID from the device id
|
|
@@ -453,7 +688,7 @@ export default class {
|
|
|
453
688
|
return
|
|
454
689
|
}
|
|
455
690
|
|
|
456
|
-
// Load the device control information from
|
|
691
|
+
// Load the device control information from Ecovacs/Yeedi
|
|
457
692
|
const loadedDevice = this.ecovacsAPI.getVacBot(
|
|
458
693
|
this.ecovacsAPI.uid,
|
|
459
694
|
EcoVacsAPI.REALM,
|
|
@@ -495,6 +730,21 @@ export default class {
|
|
|
495
730
|
accessory.context.isOnline = false
|
|
496
731
|
accessory.context.lastMsg = ''
|
|
497
732
|
|
|
733
|
+
// Remove the existing cache values from the context
|
|
734
|
+
// accessory.context.cacheClean
|
|
735
|
+
// accessory.context.cacheCharge
|
|
736
|
+
// accessory.context.cacheAirDrying
|
|
737
|
+
// accessory.context.cacheSpeed
|
|
738
|
+
// accessory.context.cacheBattery
|
|
739
|
+
const cacheKeys = [
|
|
740
|
+
'cacheClean',
|
|
741
|
+
'cacheCharge',
|
|
742
|
+
'cacheAirDrying',
|
|
743
|
+
'cacheSpeed',
|
|
744
|
+
'cacheBattery',
|
|
745
|
+
]
|
|
746
|
+
cacheKeys.forEach(key => delete accessory.context[key])
|
|
747
|
+
|
|
498
748
|
// Add the 'clean' switch service if it doesn't already exist
|
|
499
749
|
const cleanService = accessory.getService('Clean') || accessory.addService(this.hapServ.Switch, 'Clean', 'clean')
|
|
500
750
|
if (!cleanService.testCharacteristic(this.hapChar.ConfiguredName)) {
|
|
@@ -774,6 +1024,49 @@ export default class {
|
|
|
774
1024
|
this.api.updatePlatformAccessories([accessory])
|
|
775
1025
|
devicesInHB.set(accessory.UUID, accessory)
|
|
776
1026
|
|
|
1027
|
+
// Register Matter RVC accessory if Matter is enabled
|
|
1028
|
+
if (this.matterEnabled) {
|
|
1029
|
+
try {
|
|
1030
|
+
const matterUUID = this.api.matter.uuid.generate(device.did)
|
|
1031
|
+
|
|
1032
|
+
// Check if this Matter accessory already exists
|
|
1033
|
+
const existingMatterAccessory = matterDevicesInHB.get(matterUUID)
|
|
1034
|
+
|
|
1035
|
+
if (!existingMatterAccessory) {
|
|
1036
|
+
// Create and publish new Matter RVC accessory
|
|
1037
|
+
const matterAccessory = this.createMatterRVCAccessory(device, accessory)
|
|
1038
|
+
|
|
1039
|
+
// Mark Matter accessory as not ready yet
|
|
1040
|
+
accessory.matterReady = false
|
|
1041
|
+
accessory.matterUUID = matterUUID
|
|
1042
|
+
|
|
1043
|
+
// Publish the accessory and listen for the READY event
|
|
1044
|
+
const published = this.api.matter.publishExternalAccessories(plugin.name, [matterAccessory])
|
|
1045
|
+
|
|
1046
|
+
// Listen for when the Matter accessory is ready
|
|
1047
|
+
if (published && published[0] && published[0]._eventEmitter) {
|
|
1048
|
+
published[0]._eventEmitter.on('ready', () => {
|
|
1049
|
+
accessory.matterReady = true
|
|
1050
|
+
accessory.log('Matter RVC accessory is ready.')
|
|
1051
|
+
|
|
1052
|
+
// Update initial state now that it's ready
|
|
1053
|
+
this.updateMatterStateFromHAP(accessory)
|
|
1054
|
+
})
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
matterDevicesInHB.set(matterUUID, matterAccessory)
|
|
1058
|
+
this.log('[%s] Matter RVC accessory published.', accessory.displayName)
|
|
1059
|
+
} else {
|
|
1060
|
+
// Matter accessory already exists, just link it
|
|
1061
|
+
accessory.matterUUID = matterUUID
|
|
1062
|
+
accessory.matterReady = true // Assume existing accessories are ready
|
|
1063
|
+
accessory.logDebug('Matter RVC accessory already registered.')
|
|
1064
|
+
}
|
|
1065
|
+
} catch (err) {
|
|
1066
|
+
accessory.logWarn(`Failed to register Matter RVC accessory: ${parseError(err)}`)
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
777
1070
|
// Log configuration and device initialisation
|
|
778
1071
|
this.log(
|
|
779
1072
|
'[%s] %s: %s.',
|
|
@@ -897,31 +1190,38 @@ export default class {
|
|
|
897
1190
|
}
|
|
898
1191
|
|
|
899
1192
|
configureAccessory(accessory) {
|
|
900
|
-
|
|
1193
|
+
this.log.debug(`[${accessory.displayName}] loading cached hap accessory.`)
|
|
901
1194
|
devicesInHB.set(accessory.UUID, accessory)
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
throw new this.api.hap.HapStatusError(-70402)
|
|
908
|
-
})
|
|
909
|
-
.updateValue(new this.api.hap.HapStatusError(-70402))
|
|
910
|
-
accessory
|
|
911
|
-
.getService('Go Charge')
|
|
912
|
-
.getCharacteristic(this.api.hap.Characteristic.On)
|
|
913
|
-
.onSet(() => {
|
|
914
|
-
this.log.warn('[%s] %s.', accessory.displayName, platformLang.accNotReady)
|
|
915
|
-
throw new this.api.hap.HapStatusError(-70402)
|
|
916
|
-
})
|
|
917
|
-
.updateValue(new this.api.hap.HapStatusError(-70402))
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
configureMatterAccessory(accessory) {
|
|
1198
|
+
this.log.debug(`[${accessory.displayName}] loading cached matter accessory.`)
|
|
1199
|
+
matterDevicesInHB.set(accessory.uuid, accessory)
|
|
918
1200
|
}
|
|
919
1201
|
|
|
920
1202
|
removeAccessory(accessory) {
|
|
921
1203
|
// Remove an accessory from Homebridge
|
|
922
1204
|
try {
|
|
1205
|
+
// Remove HAP accessory
|
|
923
1206
|
this.api.unregisterPlatformAccessories(plugin.name, plugin.alias, [accessory])
|
|
924
1207
|
devicesInHB.delete(accessory.UUID)
|
|
1208
|
+
|
|
1209
|
+
// Remove corresponding Matter accessory if it exists
|
|
1210
|
+
if (this.matterEnabled && accessory.context.ecoDeviceId) {
|
|
1211
|
+
try {
|
|
1212
|
+
const matterUUID = this.api.matter.uuid.generate(accessory.context.ecoDeviceId)
|
|
1213
|
+
const matterAccessory = matterDevicesInHB.get(matterUUID)
|
|
1214
|
+
|
|
1215
|
+
if (matterAccessory) {
|
|
1216
|
+
this.api.matter.unpublishExternalAccessories([matterAccessory])
|
|
1217
|
+
matterDevicesInHB.delete(matterUUID)
|
|
1218
|
+
this.log.debug('[%s] Matter RVC accessory removed.', accessory.displayName)
|
|
1219
|
+
}
|
|
1220
|
+
} catch (matterErr) {
|
|
1221
|
+
this.log.warn('[%s] Failed to remove Matter accessory: %s', accessory.displayName, parseError(matterErr))
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
925
1225
|
this.log('[%s] %s.', accessory.displayName, platformLang.devRemove)
|
|
926
1226
|
} catch (err) {
|
|
927
1227
|
// Catch any errors during remove
|
|
@@ -949,7 +1249,7 @@ export default class {
|
|
|
949
1249
|
const order = value ? `Clean${accessory.context.commandSuffix}` : 'Stop'
|
|
950
1250
|
|
|
951
1251
|
// Log the update
|
|
952
|
-
accessory.log(`${platformLang.curCleaning} [${value ? platformLang.cleaning : platformLang.stop}
|
|
1252
|
+
accessory.log(`${platformLang.curCleaning} [${value ? platformLang.cleaning : platformLang.stop}]`)
|
|
953
1253
|
|
|
954
1254
|
// Send the command
|
|
955
1255
|
accessory.logDebug(`${platformLang.sendCmd} [${order}]`)
|
|
@@ -1023,7 +1323,7 @@ export default class {
|
|
|
1023
1323
|
// Avoid quick switching with this function
|
|
1024
1324
|
const updateKey = Math.random()
|
|
1025
1325
|
.toString(36)
|
|
1026
|
-
.
|
|
1326
|
+
.slice(2, 10)
|
|
1027
1327
|
accessory.context.lastCommandKey = updateKey
|
|
1028
1328
|
await sleep(1)
|
|
1029
1329
|
if (updateKey !== accessory.context.lastCommandKey) {
|
|
@@ -1135,7 +1435,7 @@ export default class {
|
|
|
1135
1435
|
setTimeout(() => {
|
|
1136
1436
|
accessory
|
|
1137
1437
|
.getService('Go Charge')
|
|
1138
|
-
.updateCharacteristic(this.hapChar.On, accessory.context.cacheCharge === '
|
|
1438
|
+
.updateCharacteristic(this.hapChar.On, accessory.context.cacheCharge === 'returning')
|
|
1139
1439
|
}, 2000)
|
|
1140
1440
|
throw new this.hapErr(-70402)
|
|
1141
1441
|
}
|
|
@@ -1240,15 +1540,25 @@ export default class {
|
|
|
1240
1540
|
|
|
1241
1541
|
// Check if the new cleaning state is different from the cached state
|
|
1242
1542
|
if (accessory.context.cacheClean !== newVal) {
|
|
1243
|
-
|
|
1543
|
+
const isActive = ['auto', 'clean', 'edge', 'spot', 'spotarea', 'customarea'].includes(
|
|
1544
|
+
newVal.toLowerCase().replace(/[^a-z]+/g, ''),
|
|
1545
|
+
)
|
|
1546
|
+
|
|
1547
|
+
// State is different so update HAP service
|
|
1244
1548
|
accessory
|
|
1245
1549
|
.getService('Clean')
|
|
1246
|
-
.updateCharacteristic(
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
)
|
|
1550
|
+
.updateCharacteristic(this.hapChar.On, isActive)
|
|
1551
|
+
|
|
1552
|
+
// Update Matter states
|
|
1553
|
+
if (isActive) {
|
|
1554
|
+
// Vacuum is cleaning
|
|
1555
|
+
this.updateMatterRunMode(accessory, 1) // Cleaning mode
|
|
1556
|
+
this.updateMatterOperationalState(accessory, 1) // Running state
|
|
1557
|
+
} else {
|
|
1558
|
+
// Vacuum is not cleaning - could be idle or stopped
|
|
1559
|
+
this.updateMatterRunMode(accessory, 0) // Idle mode
|
|
1560
|
+
this.updateMatterOperationalState(accessory, 0) // Stopped state
|
|
1561
|
+
}
|
|
1252
1562
|
|
|
1253
1563
|
// Log the change
|
|
1254
1564
|
accessory.log(`${platformLang.curCleaning} [${newVal}]`)
|
|
@@ -1341,7 +1651,7 @@ export default class {
|
|
|
1341
1651
|
|
|
1342
1652
|
// Check if the new charging state is different from the cached state
|
|
1343
1653
|
if (accessory.context.cacheCharge !== newVal) {
|
|
1344
|
-
// State is different so update service
|
|
1654
|
+
// State is different so update HAP service
|
|
1345
1655
|
accessory
|
|
1346
1656
|
.getService('Go Charge')
|
|
1347
1657
|
.updateCharacteristic(this.hapChar.On, newVal === 'returning')
|
|
@@ -1350,6 +1660,19 @@ export default class {
|
|
|
1350
1660
|
.getService(this.hapServ.Battery)
|
|
1351
1661
|
.updateCharacteristic(this.hapChar.ChargingState, chargeState)
|
|
1352
1662
|
|
|
1663
|
+
// Update Matter operational state based on charge state
|
|
1664
|
+
const chargeStateLower = newVal.toLowerCase()
|
|
1665
|
+
if (chargeStateLower === 'charging') {
|
|
1666
|
+
this.updateMatterRunMode(accessory, 0) // Idle mode
|
|
1667
|
+
this.updateMatterOperationalState(accessory, 65) // Charging
|
|
1668
|
+
} else if (chargeStateLower === 'returning') {
|
|
1669
|
+
this.updateMatterRunMode(accessory, 0) // Idle mode
|
|
1670
|
+
this.updateMatterOperationalState(accessory, 64) // Seeking Charger
|
|
1671
|
+
} else if (chargeStateLower === 'idle') {
|
|
1672
|
+
this.updateMatterRunMode(accessory, 0) // Idle mode
|
|
1673
|
+
this.updateMatterOperationalState(accessory, 66) // Docked
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1353
1676
|
// Log the change
|
|
1354
1677
|
accessory.log(`${platformLang.curCharging} [${newVal}]`)
|
|
1355
1678
|
}
|
|
@@ -1,50 +1,51 @@
|
|
|
1
|
-
import { inherits } from 'node:util'
|
|
2
|
-
|
|
3
1
|
export default class {
|
|
4
2
|
constructor(api) {
|
|
5
|
-
this.hapChar = api.hap.Characteristic
|
|
6
3
|
this.uuids = {
|
|
7
4
|
maxSpeed: 'E963F001-079E-48FF-8F27-9C2605A29F52',
|
|
8
5
|
predefinedArea: 'E963F002-079E-48FF-8F27-9C2605A29F52',
|
|
9
6
|
trueDetect: 'E963F003-079E-48FF-8F27-9C2605A29F52',
|
|
10
7
|
}
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
const uuids = this.uuids
|
|
9
|
+
|
|
10
|
+
this.MaxSpeed = class extends api.hap.Characteristic {
|
|
11
|
+
constructor() {
|
|
12
|
+
super('Max Speed', uuids.maxSpeed)
|
|
13
|
+
this.setProps({
|
|
14
|
+
format: api.hap.Formats.BOOL,
|
|
15
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.PAIRED_WRITE, api.hap.Perms.NOTIFY],
|
|
16
|
+
})
|
|
17
|
+
this.value = this.getDefaultValue()
|
|
18
|
+
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
this.PredefinedArea =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
this.PredefinedArea = class extends api.hap.Characteristic {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('Predefined Area', uuids.predefinedArea)
|
|
24
|
+
this.setProps({
|
|
25
|
+
format: api.hap.Formats.UINT8,
|
|
26
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.PAIRED_WRITE, api.hap.Perms.NOTIFY],
|
|
27
|
+
minValue: 0,
|
|
28
|
+
maxValue: 15,
|
|
29
|
+
minStep: 1,
|
|
30
|
+
validValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
31
|
+
})
|
|
32
|
+
this.value = this.getDefaultValue()
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
this.TrueDetect =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
this.TrueDetect = class extends api.hap.Characteristic {
|
|
37
|
+
constructor() {
|
|
38
|
+
super('TrueDetect', uuids.trueDetect)
|
|
39
|
+
this.setProps({
|
|
40
|
+
format: api.hap.Formats.BOOL,
|
|
41
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.PAIRED_WRITE, api.hap.Perms.NOTIFY],
|
|
42
|
+
})
|
|
43
|
+
this.value = this.getDefaultValue()
|
|
44
|
+
}
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
inherits(this.MaxSpeed, this.hapChar)
|
|
44
|
-
inherits(this.PredefinedArea, this.hapChar)
|
|
45
|
-
inherits(this.TrueDetect, this.hapChar)
|
|
46
|
-
this.MaxSpeed.UUID = this.uuids.maxSpeed
|
|
47
47
|
this.PredefinedArea.UUID = this.uuids.predefinedArea
|
|
48
48
|
this.TrueDetect.UUID = this.uuids.trueDetect
|
|
49
|
+
this.MaxSpeed.UUID = this.uuids.maxSpeed
|
|
49
50
|
}
|
|
50
51
|
}
|
package/lib/utils/lang-en.js
CHANGED
|
@@ -74,6 +74,6 @@ export default {
|
|
|
74
74
|
speedFail: 'sending speed update failed as',
|
|
75
75
|
stop: 'stop',
|
|
76
76
|
typeForArea: 'Type for predefined area',
|
|
77
|
-
welcome: '
|
|
77
|
+
welcome: 'I\'m looking for some lovely people to help maintain this plugin, please get in touch on GitHub or Discord if you\'d like to help out 😄',
|
|
78
78
|
unknownCommandTypeForArea: 'Unknown command type for predefined area',
|
|
79
79
|
}
|
package/package.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"displayName": "Homebridge Ecovacs",
|
|
4
4
|
"alias": "Deebot",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "7.0
|
|
7
|
-
"description": "Homebridge plugin to integrate
|
|
6
|
+
"version": "7.1.4-beta.0",
|
|
7
|
+
"description": "Homebridge plugin to integrate Ecovacs Deebot devices into HomeKit.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "bwp91",
|
|
10
10
|
"email": "bwp91@icloud.com"
|
|
@@ -57,21 +57,21 @@
|
|
|
57
57
|
"node": "^20 || ^22 || ^24"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
|
-
"lint": "eslint . --
|
|
61
|
-
"
|
|
62
|
-
"
|
|
60
|
+
"lint": "eslint . --max-warnings=0",
|
|
61
|
+
"lint:fix": "npm run lint -- --fix",
|
|
62
|
+
"postinstall": "patch-package"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@homebridge/plugin-ui-utils": "^2.1.0",
|
|
66
|
-
"ecovacs-deebot": "^0.9.6-beta.
|
|
67
|
-
"patch-package": "^8.0.
|
|
66
|
+
"ecovacs-deebot": "^0.9.6-beta.12",
|
|
67
|
+
"patch-package": "^8.0.1"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@antfu/eslint-config": "^
|
|
70
|
+
"@antfu/eslint-config": "^6.1.0"
|
|
71
71
|
},
|
|
72
72
|
"overrides": {
|
|
73
|
-
"
|
|
74
|
-
"
|
|
73
|
+
"request": {
|
|
74
|
+
"form-data": "^2.5.4"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/node_modules/ecovacs-deebot/library/tools.js b/node_modules/ecovacs-deebot/library/tools.js
|
|
2
|
-
index d484ba1..
|
|
2
|
+
index d484ba1..eb8e866 100644
|
|
3
3
|
--- a/node_modules/ecovacs-deebot/library/tools.js
|
|
4
4
|
+++ b/node_modules/ecovacs-deebot/library/tools.js
|
|
5
5
|
@@ -369,11 +369,13 @@ function envLogRaw(message) {
|