homebridge-switcher-laundry 1.0.1 → 1.0.3
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 +19 -6
- package/config.schema.json +2 -2
- package/dist/accessory.js +16 -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 +39 -3
- package/dist/switcher-power.js +125 -31
- package/dist/switcher-power.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,11 +6,13 @@ Apple Home does not show live watts. Eve can. This plugin reads Switcher wattage
|
|
|
6
6
|
|
|
7
7
|
## What you get in Home
|
|
8
8
|
|
|
9
|
-
For each configured plug:
|
|
9
|
+
For each configured plug, Home shows **one device** (named like the washer/dryer):
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **Clear Done** — momentary switch to close
|
|
11
|
+
- **Primary tile — Running** — occupancy; on while the machine draws sustained power
|
|
12
|
+
- **Done** (inside the device) — contact sensor that opens when a cycle finishes (use for notifications)
|
|
13
|
+
- **Clear Done** (inside the device) — momentary switch to close Done without waiting for the next cycle
|
|
14
|
+
|
|
15
|
+
This mirrors the Home Connect “appliance + sensors” layout as closely as classic HomeKit allows.
|
|
14
16
|
|
|
15
17
|
## Install
|
|
16
18
|
|
|
@@ -52,11 +54,22 @@ Restart Homebridge, then add **Switcher Laundry** from the Plugins page.
|
|
|
52
54
|
|
|
53
55
|
`identifier` can be the Switcher device id, a static IP, or the exact device name from the Switcher app.
|
|
54
56
|
|
|
57
|
+
### Washer + dryer (two plugs)
|
|
58
|
+
|
|
59
|
+
Add **two** entries under `devices` — one per Switcher plug. Each gets its own Done / Running / Clear Done sensors in Home.
|
|
60
|
+
|
|
61
|
+
v1.0.2+ shares one UDP listener across all laundry plugs. Older builds could only hear the first plug.
|
|
62
|
+
|
|
55
63
|
### Running next to homebridge-switcher-platform
|
|
56
64
|
|
|
57
|
-
Both plugins want Switcher UDP port **20002**. If Platform is already running, Laundry
|
|
65
|
+
Both plugins want Switcher UDP port **20002**. If Platform is already running, Laundry falls back to **TCP polling**.
|
|
66
|
+
|
|
67
|
+
For that fallback, **every** laundry device needs:
|
|
68
|
+
|
|
69
|
+
- **`deviceId`** — hex id from Switcher Platform logs (e.g. `Found New Switcher ... | ID:e4eaf6`)
|
|
70
|
+
- **`deviceIp`** — if `identifier` is not already the plug’s IP
|
|
58
71
|
|
|
59
|
-
|
|
72
|
+
Missing `deviceId`/`deviceIp` on the dryer is the usual reason washer sensors move while dryer sensors stay idle.
|
|
60
73
|
|
|
61
74
|
### Calibrating thresholds
|
|
62
75
|
|
package/config.schema.json
CHANGED
|
@@ -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.js
CHANGED
|
@@ -35,29 +35,39 @@ class LaundryAccessory {
|
|
|
35
35
|
?.setCharacteristic(Characteristic.Manufacturer, 'Switcher')
|
|
36
36
|
.setCharacteristic(Characteristic.Model, 'Power Plug Laundry')
|
|
37
37
|
.setCharacteristic(Characteristic.SerialNumber, device.deviceId || device.identifier);
|
|
38
|
-
//
|
|
38
|
+
// One HomeKit device (like Home Connect): Running is the primary tile;
|
|
39
|
+
// Done + Clear Done are linked sensors inside that device.
|
|
40
|
+
this.runningService = this.accessory.getServiceById(Service.OccupancySensor, 'washer-running')
|
|
41
|
+
|| this.accessory.addService(Service.OccupancySensor, device.name, 'washer-running');
|
|
42
|
+
this.runningService.setCharacteristic(Characteristic.Name, device.name);
|
|
43
|
+
this.runningService.getCharacteristic(Characteristic.OccupancyDetected)
|
|
44
|
+
.onGet(() => this.runningOccupancy());
|
|
39
45
|
this.doneService = this.accessory.getServiceById(Service.ContactSensor, 'washer-done')
|
|
40
46
|
|| this.accessory.addService(Service.ContactSensor, `${device.name} Done`, 'washer-done');
|
|
41
47
|
this.doneService.setCharacteristic(Characteristic.Name, `${device.name} Done`);
|
|
42
48
|
this.doneService.getCharacteristic(Characteristic.ContactSensorState)
|
|
43
49
|
.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
50
|
this.clearService = this.accessory.getServiceById(Service.Switch, 'clear-done')
|
|
50
51
|
|| this.accessory.addService(Service.Switch, `${device.name} Clear Done`, 'clear-done');
|
|
51
52
|
this.clearService.setCharacteristic(Characteristic.Name, `${device.name} Clear Done`);
|
|
52
53
|
this.clearService.getCharacteristic(Characteristic.On)
|
|
53
54
|
.onGet(() => false)
|
|
54
55
|
.onSet((value) => this.clearDone(value));
|
|
56
|
+
this.runningService.setPrimaryService(true);
|
|
57
|
+
this.runningService.addLinkedService(this.doneService);
|
|
58
|
+
this.runningService.addLinkedService(this.clearService);
|
|
55
59
|
this.applySnapshot(this.snapshot, true);
|
|
56
60
|
this.power.on('power', (sample) => this.onPower(sample));
|
|
57
61
|
this.power.on('error', (error) => {
|
|
58
62
|
this.platform.log.error(`[${device.name}] ${error.message}`);
|
|
59
63
|
});
|
|
60
64
|
this.power.start();
|
|
65
|
+
// If no watt samples arrive, HomeKit sensors stay idle forever — surface that clearly.
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
if (this.lastWatts === 0 && this.snapshot.phase === 'idle') {
|
|
68
|
+
this.platform.log.warn(`[${device.name}] no power readings yet — check identifier/deviceId/deviceIp, or look for UDP/TCP errors above`);
|
|
69
|
+
}
|
|
70
|
+
}, 60_000);
|
|
61
71
|
}
|
|
62
72
|
stop() {
|
|
63
73
|
this.power.stop();
|
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;IAeR;IACA;IACA;IACA;IAjBF,WAAW,CAAU;IACrB,cAAc,CAAU;IACxB,YAAY,CAAU;IACtB,QAAQ,CAAuB;IACxC,SAAS,GAAG,CAAC,CAAC;IACd,QAAQ,GAAkB;QAChC,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,KAAK;KACf,CAAC;IAEF,YACmB,QAAiC,EACjC,SAA4B,EAC5B,MAA2B,EAC3B,KAAkB;QAHlB,aAAQ,GAAR,QAAQ,CAAyB;QACjC,cAAS,GAAT,SAAS,CAAmB;QAC5B,WAAM,GAAN,MAAM,CAAqB;QAC3B,UAAK,GAAL,KAAK,CAAa;QAEnC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAElD,IAAI,CAAC,QAAQ,GAAG,IAAI,4BAAoB,CAAC;YACvC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,WAAW,EAAE,MAAM,CAAC,WAAW;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,oBAAoB,CAAC;YACrD,EAAE,iBAAiB,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC;aAC3D,iBAAiB,CAAC,cAAc,CAAC,KAAK,EAAE,oBAAoB,CAAC;aAC7D,iBAAiB,CAAC,cAAc,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC;QAExF,
|
|
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,46 @@ 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
|
-
* Prefers UDP broadcasts; if the Switcher UDP port is already taken
|
|
60
|
+
* Prefers shared UDP broadcasts; if the Switcher UDP port is already taken
|
|
27
61
|
* (e.g. by homebridge-switcher-platform), falls back to TCP status polling.
|
|
28
62
|
*/
|
|
29
63
|
export declare class SwitcherPowerSource extends EventEmitter implements PowerSource {
|
|
30
64
|
private readonly options;
|
|
31
|
-
private listener?;
|
|
32
65
|
private pollTimer?;
|
|
33
66
|
private device?;
|
|
34
67
|
private started;
|
|
@@ -36,10 +69,11 @@ export declare class SwitcherPowerSource extends EventEmitter implements PowerSo
|
|
|
36
69
|
private readonly pollSeconds;
|
|
37
70
|
private resolvedId?;
|
|
38
71
|
private resolvedIp?;
|
|
72
|
+
private hubSubscriber?;
|
|
73
|
+
private usingTcp;
|
|
39
74
|
constructor(options: SwitcherPowerOptions);
|
|
40
75
|
start(): void;
|
|
41
76
|
stop(): void;
|
|
42
|
-
private closeListener;
|
|
43
77
|
private startTcpPoll;
|
|
44
78
|
private handleWatts;
|
|
45
79
|
}
|
|
@@ -49,3 +83,5 @@ export declare class FakePowerSource extends EventEmitter implements PowerSource
|
|
|
49
83
|
stop(): void;
|
|
50
84
|
push(watts: number, extras?: Partial<PowerSample>): void;
|
|
51
85
|
}
|
|
86
|
+
export declare function matchesIdentifier(identifier: string, message: SwitcherMessage): boolean;
|
|
87
|
+
export { SwitcherUdpHub };
|
package/dist/switcher-power.js
CHANGED
|
@@ -1,16 +1,110 @@
|
|
|
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
|
+
* 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
|
+
* Prefers shared UDP broadcasts; if the Switcher UDP port is already taken
|
|
9
104
|
* (e.g. by homebridge-switcher-platform), falls back to TCP status polling.
|
|
10
105
|
*/
|
|
11
106
|
class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
12
107
|
options;
|
|
13
|
-
listener;
|
|
14
108
|
pollTimer;
|
|
15
109
|
device;
|
|
16
110
|
started = false;
|
|
@@ -18,6 +112,8 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
18
112
|
pollSeconds;
|
|
19
113
|
resolvedId;
|
|
20
114
|
resolvedIp;
|
|
115
|
+
hubSubscriber;
|
|
116
|
+
usingTcp = false;
|
|
21
117
|
constructor(options) {
|
|
22
118
|
super();
|
|
23
119
|
this.options = options;
|
|
@@ -31,28 +127,29 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
31
127
|
return;
|
|
32
128
|
}
|
|
33
129
|
this.started = true;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.log(`UDP listen failed (${error.message}); trying TCP poll fallback`);
|
|
38
|
-
void this.closeListener().then(() => this.startTcpPoll());
|
|
39
|
-
});
|
|
40
|
-
this.listener.on('message', (message) => {
|
|
130
|
+
this.hubSubscriber = {
|
|
131
|
+
match: (message) => matchesIdentifier(this.options.identifier, message),
|
|
132
|
+
onMessage: (message) => {
|
|
41
133
|
this.resolvedId = message.device_id;
|
|
42
134
|
this.resolvedIp = message.device_ip;
|
|
43
135
|
this.handleWatts(Number(message.state?.power_consumption), message.device_id, message.device_ip, message.name, Number(message.state?.power) === 1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
136
|
+
},
|
|
137
|
+
onUdpFailed: (error) => {
|
|
138
|
+
this.log(`UDP listen failed (${error.message}); trying TCP poll fallback`);
|
|
139
|
+
this.startTcpPoll();
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
SwitcherUdpHub.get().subscribe(this.hubSubscriber);
|
|
50
143
|
}
|
|
51
144
|
stop() {
|
|
52
145
|
if (!this.started) {
|
|
53
146
|
return;
|
|
54
147
|
}
|
|
55
148
|
this.started = false;
|
|
149
|
+
if (this.hubSubscriber) {
|
|
150
|
+
SwitcherUdpHub.get().unsubscribe(this.hubSubscriber);
|
|
151
|
+
this.hubSubscriber = undefined;
|
|
152
|
+
}
|
|
56
153
|
if (this.pollTimer) {
|
|
57
154
|
clearInterval(this.pollTimer);
|
|
58
155
|
this.pollTimer = undefined;
|
|
@@ -64,29 +161,17 @@ class SwitcherPowerSource extends node_events_1.EventEmitter {
|
|
|
64
161
|
// ignore
|
|
65
162
|
}
|
|
66
163
|
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
164
|
}
|
|
81
165
|
startTcpPoll() {
|
|
82
|
-
if (this.pollTimer || !this.started) {
|
|
166
|
+
if (this.pollTimer || this.usingTcp || !this.started) {
|
|
83
167
|
return;
|
|
84
168
|
}
|
|
85
169
|
if (!this.resolvedId || !this.resolvedIp) {
|
|
86
|
-
const error = new Error(
|
|
170
|
+
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
171
|
this.emit('error', error);
|
|
88
172
|
return;
|
|
89
173
|
}
|
|
174
|
+
this.usingTcp = true;
|
|
90
175
|
this.log(`Polling Switcher ${this.resolvedId} at ${this.resolvedIp} every ${this.pollSeconds}s over TCP`);
|
|
91
176
|
this.device = new Switcher(this.resolvedId, this.resolvedIp, (message) => this.log(message), false, 'power_plug', undefined, this.options.token);
|
|
92
177
|
const tick = () => {
|
|
@@ -134,6 +219,15 @@ class FakePowerSource extends node_events_1.EventEmitter {
|
|
|
134
219
|
}
|
|
135
220
|
}
|
|
136
221
|
exports.FakePowerSource = FakePowerSource;
|
|
222
|
+
function matchesIdentifier(identifier, message) {
|
|
223
|
+
const needle = identifier.trim().toLowerCase();
|
|
224
|
+
if (!needle) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return (message.device_id.toLowerCase() === needle
|
|
228
|
+
|| message.name.toLowerCase() === needle
|
|
229
|
+
|| message.device_ip === identifier.trim());
|
|
230
|
+
}
|
|
137
231
|
function isIpAddress(value) {
|
|
138
232
|
return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(value);
|
|
139
233
|
}
|
|
@@ -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.3",
|
|
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",
|