matterbridge-homey 0.2.0 → 0.3.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 +10 -0
- package/README.md +3 -1
- package/dist/module.js +61 -17
- package/matterbridge-homey.config.json +1 -0
- package/matterbridge-homey.schema.json +6 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-10-28
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- [platform]: Devices added to Homey are exposed right away, without a Matterbridge restart. The new `exposeNewDevices` setting (default on) turns this off.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- [platform]: Homey device changes are applied one at a time, so a device that changes while the plugin starts or adds it isn't registered twice.
|
|
16
|
+
|
|
7
17
|
## [0.2.0] - 2026-10-18
|
|
8
18
|
|
|
9
19
|
### Added
|
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ The key is shown only once, so copy it right away.
|
|
|
36
36
|
| `outletList` | These devices are exposed as plugs, whatever their Homey class. |
|
|
37
37
|
| `capabilityBlackList` | These Homey capabilities are never exposed, e.g. `meter_power`. |
|
|
38
38
|
| `deviceCapabilityBlackList` | Capabilities not exposed for one device: the device name (or ID) and its list of capabilities, e.g. `{ "Multisensor": ["alarm_occupancy"] }`. |
|
|
39
|
+
| `exposeNewDevices` | Expose devices added to Homey right away, without a restart (default on). The lists above still apply. When off, new devices are exposed after a restart. |
|
|
39
40
|
| `debug` | Log the homey-api debug output. |
|
|
40
41
|
| `unregisterOnShutdown` | Remove all devices from the controllers on shutdown. For development only. |
|
|
41
42
|
|
|
@@ -113,7 +114,8 @@ The white and black lists accept Homey device IDs as well as names. Entries by n
|
|
|
113
114
|
|
|
114
115
|
### Limitations
|
|
115
116
|
|
|
116
|
-
- New Homey devices need a Matterbridge restart; the frontend asks for it.
|
|
117
|
+
- New Homey devices are exposed right away and deleted devices are removed right away. With `exposeNewDevices` off, new devices need a Matterbridge restart; the frontend asks for it. To keep new devices out entirely, use the white list.
|
|
118
|
+
- A new device is exposed with the capabilities Homey reports for it. Capabilities that its app adds later appear after a restart.
|
|
117
119
|
- Devices with the same name get their zone appended, e.g. `Ceiling light Kitchen`.
|
|
118
120
|
- Not supported yet: buttons and remotes, media players and TVs (only their `onoff`), window covering tilt, plant moisture, `level_aqi`.
|
|
119
121
|
|
package/dist/module.js
CHANGED
|
@@ -54,6 +54,9 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
54
54
|
homey;
|
|
55
55
|
bridgedDevices = new Map();
|
|
56
56
|
zones = [];
|
|
57
|
+
newDeviceIds = new Set();
|
|
58
|
+
areDevicesConfigured = false;
|
|
59
|
+
deviceChanges = Promise.resolve();
|
|
57
60
|
constructor(matterbridge, log, config, homey) {
|
|
58
61
|
super(matterbridge, log, config);
|
|
59
62
|
this.config = config;
|
|
@@ -73,20 +76,26 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
73
76
|
this.config.outletList = isValidArray(config.outletList) ? config.outletList : [];
|
|
74
77
|
this.config.capabilityBlackList = isValidArray(config.capabilityBlackList) ? config.capabilityBlackList : [];
|
|
75
78
|
this.config.deviceCapabilityBlackList = isValidObject(config.deviceCapabilityBlackList) ? config.deviceCapabilityBlackList : {};
|
|
79
|
+
this.config.exposeNewDevices ??= true;
|
|
76
80
|
this.config.debug ??= false;
|
|
77
81
|
this.config.unregisterOnShutdown ??= false;
|
|
78
82
|
this.homey =
|
|
79
83
|
homey ?? new HomeyApiConnection(config.address, config.token, 15000, config.debug ? (message) => this.log.debug(`[homey-api] ${message}`) : undefined);
|
|
80
84
|
this.homey.on('device.create', (device) => {
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
this.
|
|
85
|
+
if (!this.config.exposeNewDevices) {
|
|
86
|
+
this.log.notice(`New Homey device ${CYAN}${device.name}${nf}: restart Matterbridge to expose it`);
|
|
87
|
+
if (this.isConfigured)
|
|
88
|
+
this.wssSendRestartRequired();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.log.notice(`New Homey device ${CYAN}${device.name}${nf}`);
|
|
92
|
+
fireAndForget(this.serialize(async () => this.addNewDevice(device)), this.log, `addition of ${device.name}`);
|
|
84
93
|
});
|
|
85
94
|
this.homey.on('device.update', (device) => {
|
|
86
|
-
fireAndForget(this.updateDevice(device), this.log, `update of ${device.name}`);
|
|
95
|
+
fireAndForget(this.serialize(async () => this.updateDevice(device)), this.log, `update of ${device.name}`);
|
|
87
96
|
});
|
|
88
97
|
this.homey.on('device.delete', (device) => {
|
|
89
|
-
fireAndForget(this.removeDevice(device.id), this.log, `removal of ${device.id}`);
|
|
98
|
+
fireAndForget(this.serialize(async () => this.removeDevice(device.id)), this.log, `removal of ${device.id}`);
|
|
90
99
|
});
|
|
91
100
|
this.homey.on('disconnected', () => {
|
|
92
101
|
this.log.warn('Disconnected from Homey');
|
|
@@ -113,22 +122,27 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
113
122
|
throw new Error(`Unable to connect to Homey at ${this.config.address}: ${getErrorMessage(error)}`, { cause: error });
|
|
114
123
|
}
|
|
115
124
|
this.log.notice(`Connected to Homey ${CYAN}${this.homey.homeyId ?? ''}${nf}`);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
await this.serialize(async () => {
|
|
126
|
+
const [devices, zones] = await Promise.all([this.homey.getDevices(), this.homey.getZones()]);
|
|
127
|
+
this.zones = zones;
|
|
128
|
+
this.log.info(`Found ${CYAN}${devices.length}${nf} devices in ${CYAN}${zones.length}${nf} zones`);
|
|
129
|
+
const names = getUniqueDeviceNames(devices, zones);
|
|
130
|
+
for (const device of devices.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
131
|
+
await this.addDevice(device, names.get(device.id) ?? device.name, zones);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
123
134
|
this.log.info(`Started platform ${idn}${this.config.name}${rs}${nf} with ${CYAN}${this.bridgedDevices.size}${nf} devices`);
|
|
124
135
|
}
|
|
125
136
|
async onConfigure() {
|
|
126
137
|
await super.onConfigure();
|
|
127
138
|
this.log.info(`Configuring platform ${idn}${this.config.name}${rs}${nf}...`);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
await this.serialize(async () => {
|
|
140
|
+
for (const bridgedDevice of this.bridgedDevices.values()) {
|
|
141
|
+
await bridgedDevice.applyAll();
|
|
142
|
+
bridgedDevice.subscribe();
|
|
143
|
+
}
|
|
144
|
+
this.areDevicesConfigured = true;
|
|
145
|
+
});
|
|
132
146
|
this.log.info(`Configured platform ${idn}${this.config.name}${rs}${nf}`);
|
|
133
147
|
}
|
|
134
148
|
async onChangeLoggerLevel(logLevel) {
|
|
@@ -203,10 +217,33 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
203
217
|
inspectError(this.log, `Failed to register device ${name}`, error);
|
|
204
218
|
}
|
|
205
219
|
}
|
|
220
|
+
async addNewDevice(device) {
|
|
221
|
+
if (this.bridgedDevices.has(device.id))
|
|
222
|
+
return;
|
|
223
|
+
const [devices, zones] = await Promise.all([this.homey.getDevices(), this.homey.getZones()]);
|
|
224
|
+
this.zones = zones;
|
|
225
|
+
const names = getUniqueDeviceNames([...devices.filter((other) => other.id !== device.id), device], zones);
|
|
226
|
+
for (const [deviceId, bridged] of this.bridgedDevices)
|
|
227
|
+
await bridged.setName(names.get(deviceId) ?? bridged.device.name);
|
|
228
|
+
await this.addDevice(device, names.get(device.id) ?? device.name, zones);
|
|
229
|
+
const bridgedDevice = this.bridgedDevices.get(device.id);
|
|
230
|
+
if (!bridgedDevice) {
|
|
231
|
+
this.newDeviceIds.add(device.id);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this.newDeviceIds.delete(device.id);
|
|
235
|
+
if (!this.areDevicesConfigured)
|
|
236
|
+
return;
|
|
237
|
+
await bridgedDevice.applyAll();
|
|
238
|
+
bridgedDevice.subscribe();
|
|
239
|
+
}
|
|
206
240
|
async updateDevice(device) {
|
|
207
241
|
const bridgedDevice = this.bridgedDevices.get(device.id);
|
|
208
|
-
if (!bridgedDevice)
|
|
242
|
+
if (!bridgedDevice) {
|
|
243
|
+
if (this.newDeviceIds.has(device.id))
|
|
244
|
+
await this.addNewDevice(device);
|
|
209
245
|
return;
|
|
246
|
+
}
|
|
210
247
|
const isRenamed = device.name !== bridgedDevice.homeyName;
|
|
211
248
|
await bridgedDevice.update(device);
|
|
212
249
|
if (!isRenamed)
|
|
@@ -216,6 +253,7 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
216
253
|
await bridged.setName(names.get(deviceId) ?? bridged.device.name);
|
|
217
254
|
}
|
|
218
255
|
async removeDevice(deviceId) {
|
|
256
|
+
this.newDeviceIds.delete(deviceId);
|
|
219
257
|
const bridgedDevice = this.bridgedDevices.get(deviceId);
|
|
220
258
|
if (!bridgedDevice)
|
|
221
259
|
return;
|
|
@@ -224,4 +262,10 @@ export class HomeyPlatform extends MatterbridgeDynamicPlatform {
|
|
|
224
262
|
this.bridgedDevices.delete(deviceId);
|
|
225
263
|
await this.unregisterDevice(bridgedDevice.endpoint);
|
|
226
264
|
}
|
|
265
|
+
async serialize(change) {
|
|
266
|
+
const result = this.deviceChanges.then(change);
|
|
267
|
+
this.deviceChanges = result.catch(() => {
|
|
268
|
+
});
|
|
269
|
+
await result;
|
|
270
|
+
}
|
|
227
271
|
}
|
|
@@ -104,6 +104,12 @@
|
|
|
104
104
|
"selectDeviceEntityFrom": "name"
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
|
+
"exposeNewDevices": {
|
|
108
|
+
"title": "Expose New Devices",
|
|
109
|
+
"description": "Expose the devices added to Homey right away, without restarting Matterbridge. The white list, black list and zone white list still apply. When off, new devices are exposed after the next restart.",
|
|
110
|
+
"type": "boolean",
|
|
111
|
+
"default": true
|
|
112
|
+
},
|
|
107
113
|
"debug": {
|
|
108
114
|
"title": "Enable Debug",
|
|
109
115
|
"description": "Enable debug mode for the plugin. Only for debugging purposes, not recommended for production use.",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-homey",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge-homey",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.3.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"homey-api": "3.20.0"
|