homebridge-switcher-laundry 1.0.1 → 1.0.4
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/README.md +22 -7
- package/config.schema.json +3 -3
- package/dist/accessory.d.ts +1 -0
- package/dist/accessory.js +24 -6
- package/dist/accessory.js.map +1 -1
- package/dist/platform.js +3 -1
- package/dist/platform.js.map +1 -1
- package/dist/switcher-power.d.ts +43 -4
- package/dist/switcher-power.js +147 -30
- package/dist/switcher-power.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Homebridge plugin that watches a **Switcher Power Plug** and tells HomeKit when a washer or dryer finishes.
|
|
4
4
|
|
|
5
|
-
Apple Home does not show live watts. Eve can. This plugin reads Switcher
|
|
5
|
+
Apple Home does not show live watts. Eve can — but Eve only mirrors HomeKit energy history and can go stale. This plugin reads the Switcher plug **directly** over your LAN (TCP status poll when `deviceId` + `deviceIp` are set, plus optional UDP) and turns power into sensors Home can automate.
|
|
6
|
+
|
|
7
|
+
If Eve freezes but the **Switcher app** still shows live watts, laundry TCP polling should still work. If the Switcher app is also stuck, fix Wi‑Fi / the plug first.
|
|
6
8
|
|
|
7
9
|
## What you get in Home
|
|
8
10
|
|
|
9
|
-
For each configured plug:
|
|
11
|
+
For each configured plug, Home shows **one device** (named like the washer/dryer):
|
|
12
|
+
|
|
13
|
+
- **Primary tile — Running** — occupancy; on while the machine draws sustained power
|
|
14
|
+
- **Done** (inside the device) — contact sensor that opens when a cycle finishes (use for notifications)
|
|
15
|
+
- **Clear Done** (inside the device) — momentary switch to close Done without waiting for the next cycle
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
- **Running** — occupancy sensor while the machine is drawing sustained power
|
|
13
|
-
- **Clear Done** — momentary switch to close the done sensor without waiting for the next cycle
|
|
17
|
+
This mirrors the Home Connect “appliance + sensors” layout as closely as classic HomeKit allows.
|
|
14
18
|
|
|
15
19
|
## Install
|
|
16
20
|
|
|
@@ -52,11 +56,22 @@ Restart Homebridge, then add **Switcher Laundry** from the Plugins page.
|
|
|
52
56
|
|
|
53
57
|
`identifier` can be the Switcher device id, a static IP, or the exact device name from the Switcher app.
|
|
54
58
|
|
|
59
|
+
### Washer + dryer (two plugs)
|
|
60
|
+
|
|
61
|
+
Add **two** entries under `devices` — one per Switcher plug. Each gets its own Done / Running / Clear Done sensors in Home.
|
|
62
|
+
|
|
63
|
+
v1.0.2+ shares one UDP listener across all laundry plugs. Older builds could only hear the first plug.
|
|
64
|
+
|
|
55
65
|
### Running next to homebridge-switcher-platform
|
|
56
66
|
|
|
57
|
-
Both plugins want Switcher UDP port **20002**. If Platform is already running, Laundry
|
|
67
|
+
Both plugins want Switcher UDP port **20002**. If Platform is already running, Laundry falls back to **TCP polling**.
|
|
68
|
+
|
|
69
|
+
For that fallback, **every** laundry device needs:
|
|
70
|
+
|
|
71
|
+
- **`deviceId`** — hex id from Switcher Platform logs (e.g. `Found New Switcher ... | ID:e4eaf6`)
|
|
72
|
+
- **`deviceIp`** — if `identifier` is not already the plug’s IP
|
|
58
73
|
|
|
59
|
-
|
|
74
|
+
Missing `deviceId`/`deviceIp` on the dryer is the usual reason washer sensors move while dryer sensors stay idle.
|
|
60
75
|
|
|
61
76
|
### Calibrating thresholds
|
|
62
77
|
|
package/config.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"pluginAlias": "SwitcherLaundry",
|
|
3
3
|
"pluginType": "platform",
|
|
4
4
|
"singular": true,
|
|
5
|
-
"headerDisplay": "Watches a Switcher Power Plug and exposes HomeKit sensors when a laundry cycle finishes.
|
|
5
|
+
"headerDisplay": "Watches a Switcher Power Plug and exposes HomeKit sensors when a laundry cycle finishes. Set deviceId + deviceIp on each plug so watts are polled over TCP (recommended). Does not use Eve — talks to the Switcher on your LAN.",
|
|
6
6
|
"schema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"deviceId": {
|
|
50
50
|
"title": "Device ID (for TCP fallback)",
|
|
51
51
|
"type": "string",
|
|
52
|
-
"description": "Hex id from Switcher Platform logs (e.g. e4eaf6). Required if you also run homebridge-switcher-platform
|
|
52
|
+
"description": "Hex id from Switcher Platform logs (e.g. e4eaf6). Required for each plug if you also run homebridge-switcher-platform (UDP 20002 busy). Washer and dryer each need their own id."
|
|
53
53
|
},
|
|
54
54
|
"deviceIp": {
|
|
55
55
|
"title": "Device IP (optional)",
|
|
56
56
|
"type": "string",
|
|
57
|
-
"description": "
|
|
57
|
+
"description": "Required with deviceId for TCP fallback when identifier is a name. Leave blank if identifier is already the plug IP."
|
|
58
58
|
},
|
|
59
59
|
"startWatts": {
|
|
60
60
|
"title": "Start threshold (W)",
|
package/dist/accessory.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare class LaundryAccessory {
|
|
|
22
22
|
private readonly clearService;
|
|
23
23
|
private readonly detector;
|
|
24
24
|
private lastWatts;
|
|
25
|
+
private lastLoggedWattsAt;
|
|
25
26
|
private snapshot;
|
|
26
27
|
constructor(platform: SwitcherLaundryPlatform, accessory: PlatformAccessory, device: LaundryDeviceConfig, power: PowerSource);
|
|
27
28
|
stop(): void;
|
package/dist/accessory.js
CHANGED
|
@@ -12,6 +12,7 @@ class LaundryAccessory {
|
|
|
12
12
|
clearService;
|
|
13
13
|
detector;
|
|
14
14
|
lastWatts = 0;
|
|
15
|
+
lastLoggedWattsAt = 0;
|
|
15
16
|
snapshot = {
|
|
16
17
|
phase: 'idle',
|
|
17
18
|
watts: 0,
|
|
@@ -35,29 +36,39 @@ class LaundryAccessory {
|
|
|
35
36
|
?.setCharacteristic(Characteristic.Manufacturer, 'Switcher')
|
|
36
37
|
.setCharacteristic(Characteristic.Model, 'Power Plug Laundry')
|
|
37
38
|
.setCharacteristic(Characteristic.SerialNumber, device.deviceId || device.identifier);
|
|
38
|
-
//
|
|
39
|
+
// One HomeKit device (like Home Connect): Running is the primary tile;
|
|
40
|
+
// Done + Clear Done are linked sensors inside that device.
|
|
41
|
+
this.runningService = this.accessory.getServiceById(Service.OccupancySensor, 'washer-running')
|
|
42
|
+
|| this.accessory.addService(Service.OccupancySensor, device.name, 'washer-running');
|
|
43
|
+
this.runningService.setCharacteristic(Characteristic.Name, device.name);
|
|
44
|
+
this.runningService.getCharacteristic(Characteristic.OccupancyDetected)
|
|
45
|
+
.onGet(() => this.runningOccupancy());
|
|
39
46
|
this.doneService = this.accessory.getServiceById(Service.ContactSensor, 'washer-done')
|
|
40
47
|
|| this.accessory.addService(Service.ContactSensor, `${device.name} Done`, 'washer-done');
|
|
41
48
|
this.doneService.setCharacteristic(Characteristic.Name, `${device.name} Done`);
|
|
42
49
|
this.doneService.getCharacteristic(Characteristic.ContactSensorState)
|
|
43
50
|
.onGet(() => this.doneContactState());
|
|
44
|
-
this.runningService = this.accessory.getServiceById(Service.OccupancySensor, 'washer-running')
|
|
45
|
-
|| this.accessory.addService(Service.OccupancySensor, `${device.name} Running`, 'washer-running');
|
|
46
|
-
this.runningService.setCharacteristic(Characteristic.Name, `${device.name} Running`);
|
|
47
|
-
this.runningService.getCharacteristic(Characteristic.OccupancyDetected)
|
|
48
|
-
.onGet(() => this.runningOccupancy());
|
|
49
51
|
this.clearService = this.accessory.getServiceById(Service.Switch, 'clear-done')
|
|
50
52
|
|| this.accessory.addService(Service.Switch, `${device.name} Clear Done`, 'clear-done');
|
|
51
53
|
this.clearService.setCharacteristic(Characteristic.Name, `${device.name} Clear Done`);
|
|
52
54
|
this.clearService.getCharacteristic(Characteristic.On)
|
|
53
55
|
.onGet(() => false)
|
|
54
56
|
.onSet((value) => this.clearDone(value));
|
|
57
|
+
this.runningService.setPrimaryService(true);
|
|
58
|
+
this.runningService.addLinkedService(this.doneService);
|
|
59
|
+
this.runningService.addLinkedService(this.clearService);
|
|
55
60
|
this.applySnapshot(this.snapshot, true);
|
|
56
61
|
this.power.on('power', (sample) => this.onPower(sample));
|
|
57
62
|
this.power.on('error', (error) => {
|
|
58
63
|
this.platform.log.error(`[${device.name}] ${error.message}`);
|
|
59
64
|
});
|
|
60
65
|
this.power.start();
|
|
66
|
+
// If no watt samples arrive, HomeKit sensors stay idle forever — surface that clearly.
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
if (this.lastWatts === 0 && this.snapshot.phase === 'idle') {
|
|
69
|
+
this.platform.log.warn(`[${device.name}] no power readings yet — check identifier/deviceId/deviceIp, or look for UDP/TCP errors above`);
|
|
70
|
+
}
|
|
71
|
+
}, 60_000);
|
|
61
72
|
}
|
|
62
73
|
stop() {
|
|
63
74
|
this.power.stop();
|
|
@@ -76,10 +87,17 @@ class LaundryAccessory {
|
|
|
76
87
|
}, now);
|
|
77
88
|
}
|
|
78
89
|
onPower(sample, now = Date.now()) {
|
|
90
|
+
const previousWatts = this.lastWatts;
|
|
79
91
|
this.lastWatts = sample.watts;
|
|
80
92
|
const next = this.detector.update(sample.watts, now);
|
|
81
93
|
if (next.changed) {
|
|
82
94
|
this.platform.log.info(`[${this.device.name}] ${next.phase} at ${sample.watts} W`);
|
|
95
|
+
this.lastLoggedWattsAt = now;
|
|
96
|
+
}
|
|
97
|
+
else if (Math.abs(sample.watts - previousWatts) >= 5 || now - this.lastLoggedWattsAt >= 60_000) {
|
|
98
|
+
// Heartbeat so logs prove TCP/UDP samples keep arriving (Eve can be stale).
|
|
99
|
+
this.platform.log.info(`[${this.device.name}] ${sample.watts} W (${next.phase})`);
|
|
100
|
+
this.lastLoggedWattsAt = now;
|
|
83
101
|
}
|
|
84
102
|
else {
|
|
85
103
|
this.platform.log.debug(`[${this.device.name}] ${sample.watts} W (${next.phase})`);
|
package/dist/accessory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":";;;AAEA,mCAAmE;AAenE,MAAa,gBAAgB;
|
|
1
|
+
{"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"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"}
|
package/dist/platform.js
CHANGED
|
@@ -52,7 +52,9 @@ class SwitcherLaundryPlatform {
|
|
|
52
52
|
const uuid = this.api.hap.uuid.generate(`${settings_1.PLUGIN_NAME}:${device.identifier}`);
|
|
53
53
|
activeIds.add(uuid);
|
|
54
54
|
const existing = this.cachedAccessories.find((item) => item.UUID === uuid);
|
|
55
|
-
const
|
|
55
|
+
const category = 10 /* this.api.hap.Categories.SENSOR */;
|
|
56
|
+
const accessory = existing ?? new this.api.platformAccessory(device.name, uuid, category);
|
|
57
|
+
accessory.category = category;
|
|
56
58
|
accessory.context.identifier = device.identifier;
|
|
57
59
|
const handler = new accessory_1.LaundryAccessory(this, accessory, device, this.powerFactory(device));
|
|
58
60
|
this.handlers.push(handler);
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/switcher-power.d.ts
CHANGED
|
@@ -22,13 +22,48 @@ export interface SwitcherPowerOptions {
|
|
|
22
22
|
pollSeconds?: number;
|
|
23
23
|
log?: (message: string) => void;
|
|
24
24
|
}
|
|
25
|
+
interface SwitcherMessage {
|
|
26
|
+
device_id: string;
|
|
27
|
+
device_ip: string;
|
|
28
|
+
name: string;
|
|
29
|
+
type?: string;
|
|
30
|
+
state?: {
|
|
31
|
+
power?: number;
|
|
32
|
+
power_consumption?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
type HubSubscriber = {
|
|
36
|
+
match: (message: SwitcherMessage) => boolean;
|
|
37
|
+
onMessage: (message: SwitcherMessage) => void;
|
|
38
|
+
onUdpFailed: (error: Error) => void;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* One UDP bind for the whole plugin. Two laundry plugs cannot each call
|
|
42
|
+
* Switcher.listen() — the second hits EADDRINUSE and never sees watts.
|
|
43
|
+
*/
|
|
44
|
+
declare class SwitcherUdpHub {
|
|
45
|
+
private static shared?;
|
|
46
|
+
private listener?;
|
|
47
|
+
private readonly subscribers;
|
|
48
|
+
private started;
|
|
49
|
+
private failed;
|
|
50
|
+
private failError?;
|
|
51
|
+
static get(): SwitcherUdpHub;
|
|
52
|
+
/** Test helper — reset between unit tests. */
|
|
53
|
+
static resetForTests(): void;
|
|
54
|
+
subscribe(subscriber: HubSubscriber): void;
|
|
55
|
+
unsubscribe(subscriber: HubSubscriber): void;
|
|
56
|
+
private ensureListening;
|
|
57
|
+
private closeListener;
|
|
58
|
+
}
|
|
25
59
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
60
|
+
* Reads watts from the Switcher plug over the LAN.
|
|
61
|
+
* When deviceId + IP are set, TCP status polling is primary (reliable next to
|
|
62
|
+
* homebridge-switcher-platform). Shared UDP is optional extra.
|
|
63
|
+
* Eve/HomeKit energy history is unrelated — this talks to the plug directly.
|
|
28
64
|
*/
|
|
29
65
|
export declare class SwitcherPowerSource extends EventEmitter implements PowerSource {
|
|
30
66
|
private readonly options;
|
|
31
|
-
private listener?;
|
|
32
67
|
private pollTimer?;
|
|
33
68
|
private device?;
|
|
34
69
|
private started;
|
|
@@ -36,10 +71,12 @@ export declare class SwitcherPowerSource extends EventEmitter implements PowerSo
|
|
|
36
71
|
private readonly pollSeconds;
|
|
37
72
|
private resolvedId?;
|
|
38
73
|
private resolvedIp?;
|
|
74
|
+
private hubSubscriber?;
|
|
75
|
+
private usingTcp;
|
|
39
76
|
constructor(options: SwitcherPowerOptions);
|
|
40
77
|
start(): void;
|
|
78
|
+
private matchesMessage;
|
|
41
79
|
stop(): void;
|
|
42
|
-
private closeListener;
|
|
43
80
|
private startTcpPoll;
|
|
44
81
|
private handleWatts;
|
|
45
82
|
}
|
|
@@ -49,3 +86,5 @@ export declare class FakePowerSource extends EventEmitter implements PowerSource
|
|
|
49
86
|
stop(): void;
|
|
50
87
|
push(watts: number, extras?: Partial<PowerSample>): void;
|
|
51
88
|
}
|
|
89
|
+
export declare function matchesIdentifier(identifier: string, message: SwitcherMessage): boolean;
|
|
90
|
+
export { SwitcherUdpHub };
|
package/dist/switcher-power.js
CHANGED
|
@@ -1,16 +1,112 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FakePowerSource = exports.SwitcherPowerSource = void 0;
|
|
3
|
+
exports.SwitcherUdpHub = exports.FakePowerSource = exports.SwitcherPowerSource = void 0;
|
|
4
|
+
exports.matchesIdentifier = matchesIdentifier;
|
|
4
5
|
const node_events_1 = require("node:events");
|
|
5
6
|
// switcher-js2 ships as CommonJS without TypeScript types.
|
|
6
7
|
const Switcher = require('switcher-js2');
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
9
|
+
* One UDP bind for the whole plugin. Two laundry plugs cannot each call
|
|
10
|
+
* Switcher.listen() — the second hits EADDRINUSE and never sees watts.
|
|
11
|
+
*/
|
|
12
|
+
class SwitcherUdpHub {
|
|
13
|
+
static shared;
|
|
14
|
+
listener;
|
|
15
|
+
subscribers = new Set();
|
|
16
|
+
started = false;
|
|
17
|
+
failed = false;
|
|
18
|
+
failError;
|
|
19
|
+
static get() {
|
|
20
|
+
if (!SwitcherUdpHub.shared) {
|
|
21
|
+
SwitcherUdpHub.shared = new SwitcherUdpHub();
|
|
22
|
+
}
|
|
23
|
+
return SwitcherUdpHub.shared;
|
|
24
|
+
}
|
|
25
|
+
/** Test helper — reset between unit tests. */
|
|
26
|
+
static resetForTests() {
|
|
27
|
+
const hub = SwitcherUdpHub.shared;
|
|
28
|
+
if (hub) {
|
|
29
|
+
void hub.closeListener();
|
|
30
|
+
hub.subscribers.clear();
|
|
31
|
+
hub.started = false;
|
|
32
|
+
hub.failed = false;
|
|
33
|
+
hub.failError = undefined;
|
|
34
|
+
}
|
|
35
|
+
SwitcherUdpHub.shared = undefined;
|
|
36
|
+
}
|
|
37
|
+
subscribe(subscriber) {
|
|
38
|
+
this.subscribers.add(subscriber);
|
|
39
|
+
if (this.failed && this.failError) {
|
|
40
|
+
subscriber.onUdpFailed(this.failError);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.ensureListening();
|
|
44
|
+
}
|
|
45
|
+
unsubscribe(subscriber) {
|
|
46
|
+
this.subscribers.delete(subscriber);
|
|
47
|
+
if (this.subscribers.size === 0) {
|
|
48
|
+
void this.closeListener();
|
|
49
|
+
this.started = false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
ensureListening() {
|
|
53
|
+
if (this.started || this.failed) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.started = true;
|
|
57
|
+
try {
|
|
58
|
+
// No identifier filter — fan out to each laundry device by match().
|
|
59
|
+
this.listener = Switcher.listen((message) => {
|
|
60
|
+
// Keep library chatter at debug via first subscriber's logger if needed.
|
|
61
|
+
void message;
|
|
62
|
+
});
|
|
63
|
+
this.listener.on('error', (error) => {
|
|
64
|
+
this.failed = true;
|
|
65
|
+
this.failError = error;
|
|
66
|
+
void this.closeListener().then(() => {
|
|
67
|
+
for (const subscriber of [...this.subscribers]) {
|
|
68
|
+
subscriber.onUdpFailed(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
this.listener.on('message', (message) => {
|
|
73
|
+
for (const subscriber of this.subscribers) {
|
|
74
|
+
if (subscriber.match(message)) {
|
|
75
|
+
subscriber.onMessage(message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
this.failed = true;
|
|
82
|
+
this.failError = error instanceof Error ? error : new Error(String(error));
|
|
83
|
+
for (const subscriber of [...this.subscribers]) {
|
|
84
|
+
subscriber.onUdpFailed(this.failError);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async closeListener() {
|
|
89
|
+
const closing = this.listener?.close();
|
|
90
|
+
this.listener = undefined;
|
|
91
|
+
if (closing && typeof closing.then === 'function') {
|
|
92
|
+
try {
|
|
93
|
+
await closing;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.SwitcherUdpHub = SwitcherUdpHub;
|
|
102
|
+
/**
|
|
103
|
+
* Reads watts from the Switcher plug over the LAN.
|
|
104
|
+
* When deviceId + IP are set, TCP status polling is primary (reliable next to
|
|
105
|
+
* homebridge-switcher-platform). Shared UDP is optional extra.
|
|
106
|
+
* Eve/HomeKit energy history is unrelated — this talks to the plug directly.
|
|
10
107
|
*/
|
|
11
108
|
class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
12
109
|
options;
|
|
13
|
-
listener;
|
|
14
110
|
pollTimer;
|
|
15
111
|
device;
|
|
16
112
|
started = false;
|
|
@@ -18,6 +114,8 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
18
114
|
pollSeconds;
|
|
19
115
|
resolvedId;
|
|
20
116
|
resolvedIp;
|
|
117
|
+
hubSubscriber;
|
|
118
|
+
usingTcp = false;
|
|
21
119
|
constructor(options) {
|
|
22
120
|
super();
|
|
23
121
|
this.options = options;
|
|
@@ -31,28 +129,50 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
31
129
|
return;
|
|
32
130
|
}
|
|
33
131
|
this.started = true;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
132
|
+
// Prefer TCP whenever we already know id+ip — do not wait for UDP to fail.
|
|
133
|
+
// UDP can look "fine" (socket bound) while this plug never matches / stops broadcasting.
|
|
134
|
+
if (this.resolvedId && this.resolvedIp) {
|
|
135
|
+
this.startTcpPoll();
|
|
136
|
+
}
|
|
137
|
+
this.hubSubscriber = {
|
|
138
|
+
match: (message) => this.matchesMessage(message),
|
|
139
|
+
onMessage: (message) => {
|
|
41
140
|
this.resolvedId = message.device_id;
|
|
42
141
|
this.resolvedIp = message.device_ip;
|
|
43
142
|
this.handleWatts(Number(message.state?.power_consumption), message.device_id, message.device_ip, message.name, Number(message.state?.power) === 1);
|
|
44
|
-
}
|
|
143
|
+
},
|
|
144
|
+
onUdpFailed: (error) => {
|
|
145
|
+
this.log(`UDP listen failed (${error.message})`);
|
|
146
|
+
if (!this.usingTcp) {
|
|
147
|
+
this.startTcpPoll();
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
SwitcherUdpHub.get().subscribe(this.hubSubscriber);
|
|
152
|
+
}
|
|
153
|
+
matchesMessage(message) {
|
|
154
|
+
if (matchesIdentifier(this.options.identifier, message)) {
|
|
155
|
+
return true;
|
|
45
156
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
157
|
+
const id = this.options.deviceId?.trim().toLowerCase();
|
|
158
|
+
if (id && message.device_id.toLowerCase() === id) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
const ip = this.options.deviceIp?.trim();
|
|
162
|
+
if (ip && message.device_ip === ip) {
|
|
163
|
+
return true;
|
|
49
164
|
}
|
|
165
|
+
return false;
|
|
50
166
|
}
|
|
51
167
|
stop() {
|
|
52
168
|
if (!this.started) {
|
|
53
169
|
return;
|
|
54
170
|
}
|
|
55
171
|
this.started = false;
|
|
172
|
+
if (this.hubSubscriber) {
|
|
173
|
+
SwitcherUdpHub.get().unsubscribe(this.hubSubscriber);
|
|
174
|
+
this.hubSubscriber = undefined;
|
|
175
|
+
}
|
|
56
176
|
if (this.pollTimer) {
|
|
57
177
|
clearInterval(this.pollTimer);
|
|
58
178
|
this.pollTimer = undefined;
|
|
@@ -64,29 +184,17 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
64
184
|
// ignore
|
|
65
185
|
}
|
|
66
186
|
this.device = undefined;
|
|
67
|
-
void this.closeListener();
|
|
68
|
-
}
|
|
69
|
-
async closeListener() {
|
|
70
|
-
const closing = this.listener?.close();
|
|
71
|
-
this.listener = undefined;
|
|
72
|
-
if (closing && typeof closing.then === 'function') {
|
|
73
|
-
try {
|
|
74
|
-
await closing;
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
// ignore
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
187
|
}
|
|
81
188
|
startTcpPoll() {
|
|
82
|
-
if (this.pollTimer || !this.started) {
|
|
189
|
+
if (this.pollTimer || this.usingTcp || !this.started) {
|
|
83
190
|
return;
|
|
84
191
|
}
|
|
85
192
|
if (!this.resolvedId || !this.resolvedIp) {
|
|
86
|
-
const error = new Error(
|
|
193
|
+
const error = new Error(`UDP port busy for "${this.options.identifier}". Set deviceId and deviceIp on this laundry device (from Switcher Platform logs) so watts can be polled over TCP.`);
|
|
87
194
|
this.emit('error', error);
|
|
88
195
|
return;
|
|
89
196
|
}
|
|
197
|
+
this.usingTcp = true;
|
|
90
198
|
this.log(`Polling Switcher ${this.resolvedId} at ${this.resolvedIp} every ${this.pollSeconds}s over TCP`);
|
|
91
199
|
this.device = new Switcher(this.resolvedId, this.resolvedIp, (message) => this.log(message), false, 'power_plug', undefined, this.options.token);
|
|
92
200
|
const tick = () => {
|
|
@@ -134,6 +242,15 @@ class FakePowerSource extends node_events_1.EventEmitter {
|
|
|
134
242
|
}
|
|
135
243
|
}
|
|
136
244
|
exports.FakePowerSource = FakePowerSource;
|
|
245
|
+
function matchesIdentifier(identifier, message) {
|
|
246
|
+
const needle = identifier.trim().toLowerCase();
|
|
247
|
+
if (!needle) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return (message.device_id.toLowerCase() === needle
|
|
251
|
+
|| message.name.toLowerCase() === needle
|
|
252
|
+
|| message.device_ip === identifier.trim());
|
|
253
|
+
}
|
|
137
254
|
function isIpAddress(value) {
|
|
138
255
|
return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(value);
|
|
139
256
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switcher-power.js","sourceRoot":"","sources":["../src/switcher-power.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"switcher-power.js","sourceRoot":"","sources":["../src/switcher-power.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-switcher-laundry",
|
|
3
3
|
"displayName": "Switcher Laundry",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"description": "Detect when a washer or dryer finishes by watching a Switcher Power Plug wattage.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "mirimalulkasif",
|