@nubisco/openbridge-compatibility-homebridge 0.29.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/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/__tests__/prune.test.d.ts +2 -0
- package/dist/__tests__/prune.test.d.ts.map +1 -0
- package/dist/__tests__/prune.test.js +149 -0
- package/dist/__tests__/prune.test.js.map +1 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +677 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nubisco Lda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @nubisco/openbridge-compatibility-homebridge
|
|
2
|
+
|
|
3
|
+
Compatibility shim that lets existing Homebridge `platform` plugins run inside OpenBridge unmodified.
|
|
4
|
+
|
|
5
|
+
Part of [OpenBridge](https://github.com/nubisco/openbridge), a local-first home automation bridge for developers.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @nubisco/openbridge-compatibility-homebridge
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js >= 20.
|
|
14
|
+
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
See the [OpenBridge documentation](https://github.com/nubisco/openbridge#readme). This package is published from the OpenBridge monorepo and versioned in lockstep with the `openbridge` daemon.
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
MIT (c) Jose Silva
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/prune.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { HomebridgeAPI } from '../index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Minimal hap-nodejs stand-in. Only the surface HomebridgeAPI touches.
|
|
8
|
+
*/
|
|
9
|
+
function fakeHap() {
|
|
10
|
+
class Accessory {
|
|
11
|
+
displayName;
|
|
12
|
+
UUID;
|
|
13
|
+
services = [];
|
|
14
|
+
category = 1;
|
|
15
|
+
constructor(displayName, UUID) {
|
|
16
|
+
this.displayName = displayName;
|
|
17
|
+
this.UUID = UUID;
|
|
18
|
+
}
|
|
19
|
+
getService() {
|
|
20
|
+
return { setCharacteristic: () => ({ setCharacteristic: () => ({}) }) };
|
|
21
|
+
}
|
|
22
|
+
addService() {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
Accessory,
|
|
28
|
+
Service: { AccessoryInformation: 'info' },
|
|
29
|
+
Characteristic: { Manufacturer: 'm', Model: 'mo', SerialNumber: 's', FirmwareRevision: 'f' },
|
|
30
|
+
Categories: { BRIDGE: 2 },
|
|
31
|
+
uuid: { generate: (s) => `uuid-${s}` },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function fakeBridge() {
|
|
35
|
+
return {
|
|
36
|
+
added: [],
|
|
37
|
+
removed: [],
|
|
38
|
+
addBridgedAccessory(acc) {
|
|
39
|
+
this.added.push(acc);
|
|
40
|
+
return acc;
|
|
41
|
+
},
|
|
42
|
+
removeBridgedAccessory(acc) {
|
|
43
|
+
this.removed.push(acc);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
let home;
|
|
48
|
+
let api;
|
|
49
|
+
let bridge;
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
home = mkdtempSync(join(tmpdir(), 'ob-prune-'));
|
|
52
|
+
bridge = fakeBridge();
|
|
53
|
+
api = new HomebridgeAPI(fakeHap(), bridge);
|
|
54
|
+
// Keep the accessory cache inside the temp dir.
|
|
55
|
+
api.user.cachedAccessoryPath = () => home;
|
|
56
|
+
});
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
rmSync(home, { recursive: true, force: true });
|
|
59
|
+
vi.restoreAllMocks();
|
|
60
|
+
});
|
|
61
|
+
/** Add an accessory attributed to a platform, as loadCachedAccessories would. */
|
|
62
|
+
function seed(displayName, uuid, platform) {
|
|
63
|
+
const acc = { displayName, UUID: uuid, _associatedPlatform: platform };
|
|
64
|
+
api._accessories.set(uuid, acc);
|
|
65
|
+
bridge.addBridgedAccessory(acc);
|
|
66
|
+
return acc;
|
|
67
|
+
}
|
|
68
|
+
describe('pruneOrphanedAccessories', () => {
|
|
69
|
+
it('removes accessories whose platform is no longer registered', () => {
|
|
70
|
+
api.registerPlatform('ShellyDS9', class {
|
|
71
|
+
});
|
|
72
|
+
seed('Pool Switch 1', 'u1', 'ShellyDS9');
|
|
73
|
+
seed('Home - Total', 'u2', 'Shelly3EM');
|
|
74
|
+
seed('Home - Fase A', 'u3', 'Shelly3EM');
|
|
75
|
+
const removed = api.pruneOrphanedAccessories();
|
|
76
|
+
// The uninstalled plugin's accessories go; the installed one's stay.
|
|
77
|
+
expect(removed.sort()).toEqual(['Home - Fase A', 'Home - Total']);
|
|
78
|
+
expect(api.getRawAccessories().map((a) => a.displayName)).toEqual(['Pool Switch 1']);
|
|
79
|
+
expect(bridge.removed.map((a) => a.UUID).sort()).toEqual(['u2', 'u3']);
|
|
80
|
+
});
|
|
81
|
+
it('reclaims the accessories of a plugin that loaded but was then unregistered as disabled', () => {
|
|
82
|
+
// A disabled plugin still has to be loaded before its platform name is
|
|
83
|
+
// knowable, so the loader registers it and then rolls the registration
|
|
84
|
+
// back. From here that is indistinguishable from never having registered,
|
|
85
|
+
// which is the point: its cached accessories must not survive as inert
|
|
86
|
+
// "Default-Manufacturer" entries in the Home app.
|
|
87
|
+
const before = api._platformRegistrations.length;
|
|
88
|
+
api.registerPlatform('ShellyDS9', class {
|
|
89
|
+
});
|
|
90
|
+
api._platformRegistrations.length = before;
|
|
91
|
+
seed('Pool Switch 1', 'u1', 'ShellyDS9');
|
|
92
|
+
seed('Pool Switch 2', 'u2', 'ShellyDS9');
|
|
93
|
+
expect(api.pruneOrphanedAccessories().sort()).toEqual(['Pool Switch 1', 'Pool Switch 2']);
|
|
94
|
+
expect(api.getRawAccessories()).toHaveLength(0);
|
|
95
|
+
expect(bridge.removed.map((a) => a.UUID).sort()).toEqual(['u1', 'u2']);
|
|
96
|
+
});
|
|
97
|
+
it('keeps accessories belonging to a registered platform', () => {
|
|
98
|
+
api.registerPlatform('WizSmarthome', class {
|
|
99
|
+
});
|
|
100
|
+
seed('Basement 1', 'u1', 'WizSmarthome');
|
|
101
|
+
expect(api.pruneOrphanedAccessories()).toEqual([]);
|
|
102
|
+
expect(api.getRawAccessories()).toHaveLength(1);
|
|
103
|
+
expect(bridge.removed).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
it('matches on plugin name as well as platform name', () => {
|
|
106
|
+
// publishExternalAccessories attributes by plugin name, not platform name.
|
|
107
|
+
api.registerPlatform('homebridge-camera-ffmpeg', 'Camera-ffmpeg', class {
|
|
108
|
+
}, true);
|
|
109
|
+
seed('Living Room', 'u1', 'homebridge-camera-ffmpeg');
|
|
110
|
+
seed('Lobby', 'u2', 'Camera-ffmpeg');
|
|
111
|
+
expect(api.pruneOrphanedAccessories()).toEqual([]);
|
|
112
|
+
expect(api.getRawAccessories()).toHaveLength(2);
|
|
113
|
+
});
|
|
114
|
+
it('leaves unattributed accessories alone', () => {
|
|
115
|
+
// No owner recorded, so it cannot be attributed. Silently deleting a user's
|
|
116
|
+
// HomeKit device is worse than leaving one stale entry behind.
|
|
117
|
+
seed('Mystery', 'u1', '');
|
|
118
|
+
expect(api.pruneOrphanedAccessories()).toEqual([]);
|
|
119
|
+
expect(api.getRawAccessories()).toHaveLength(1);
|
|
120
|
+
});
|
|
121
|
+
it('does nothing when there is nothing to prune', () => {
|
|
122
|
+
api.registerPlatform('ShellyDS9', class {
|
|
123
|
+
});
|
|
124
|
+
seed('Pool Switch 1', 'u1', 'ShellyDS9');
|
|
125
|
+
expect(api.pruneOrphanedAccessories()).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
it('survives a bridge that refuses the removal', () => {
|
|
128
|
+
api.registerPlatform('ShellyDS9', class {
|
|
129
|
+
});
|
|
130
|
+
bridge.removeBridgedAccessory = () => {
|
|
131
|
+
throw new Error('not bridged');
|
|
132
|
+
};
|
|
133
|
+
seed('Home - Total', 'u1', 'Shelly3EM');
|
|
134
|
+
// The accessory must still leave the map and the cache even if the bridge
|
|
135
|
+
// never had it.
|
|
136
|
+
expect(api.pruneOrphanedAccessories()).toEqual(['Home - Total']);
|
|
137
|
+
expect(api.getRawAccessories()).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
it('notifies the removal listener so the UI drops the device too', () => {
|
|
140
|
+
const onRemove = vi.fn();
|
|
141
|
+
api.onAccessoryRemove(onRemove);
|
|
142
|
+
api.registerPlatform('ShellyDS9', class {
|
|
143
|
+
});
|
|
144
|
+
seed('Home - Total', 'u1', 'Shelly3EM');
|
|
145
|
+
api.pruneOrphanedAccessories();
|
|
146
|
+
expect(onRemove).toHaveBeenCalledTimes(1);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=prune.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.test.js","sourceRoot":"","sources":["../../src/__tests__/prune.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nubisco/openbridge-compatibility-homebridge
|
|
3
|
+
*
|
|
4
|
+
* Homebridge API compatibility shim for OpenBridge.
|
|
5
|
+
* Wraps hap-nodejs so Homebridge platform plugins can run inside OpenBridge.
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
export declare function loadHapSync(): any;
|
|
9
|
+
export declare let hap: any;
|
|
10
|
+
export declare function initHap(hapNodeJs: any): void;
|
|
11
|
+
export interface PlatformRegistration {
|
|
12
|
+
pluginName: string;
|
|
13
|
+
platformName: string;
|
|
14
|
+
Constructor: new (...args: any[]) => any;
|
|
15
|
+
dynamic: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface AccessoryRegistration {
|
|
18
|
+
pluginName: string;
|
|
19
|
+
accessoryName: string;
|
|
20
|
+
Constructor: new (...args: any[]) => any;
|
|
21
|
+
}
|
|
22
|
+
export declare class HomebridgeAPI extends EventEmitter {
|
|
23
|
+
hap: any;
|
|
24
|
+
platformAccessory: any;
|
|
25
|
+
version: string;
|
|
26
|
+
serverVersion: string;
|
|
27
|
+
user: {
|
|
28
|
+
storagePath: () => string;
|
|
29
|
+
configPath: () => string;
|
|
30
|
+
persistPath: () => string;
|
|
31
|
+
cachedAccessoryPath: () => string;
|
|
32
|
+
};
|
|
33
|
+
private _bridge;
|
|
34
|
+
private _platformRegistrations;
|
|
35
|
+
private _platformInstances;
|
|
36
|
+
private _accessories;
|
|
37
|
+
private _onAccessoryAdd?;
|
|
38
|
+
private _onAccessoryRemove?;
|
|
39
|
+
/** Stored launch configs so platforms can be restarted after failure */
|
|
40
|
+
private _platformLaunchConfigs;
|
|
41
|
+
/** Tracks consecutive restart failures per platform for exponential backoff */
|
|
42
|
+
private _platformRestartAttempts;
|
|
43
|
+
/** Tracks last error time per platform to detect recurring failures */
|
|
44
|
+
private _platformLastError;
|
|
45
|
+
constructor(hapNodeJs: any, bridge: any);
|
|
46
|
+
onAccessoryAdd(fn: (accessory: any) => void): void;
|
|
47
|
+
onAccessoryRemove(fn: (accessory: any) => void): void;
|
|
48
|
+
/**
|
|
49
|
+
* Called by plugins in two forms:
|
|
50
|
+
* 4-arg: registerPlatform(pluginName, platformName, Constructor, dynamic)
|
|
51
|
+
* 2-arg: registerPlatform(platformName, Constructor, dynamic?)
|
|
52
|
+
*/
|
|
53
|
+
registerPlatform(...args: any[]): void;
|
|
54
|
+
/**
|
|
55
|
+
* Called by plugins: homebridge.registerAccessory(pluginName, accessoryName, Constructor)
|
|
56
|
+
*/
|
|
57
|
+
registerAccessory(_pluginName: string, _accessoryName: string, _Constructor: new (...args: any[]) => any): void;
|
|
58
|
+
/**
|
|
59
|
+
* Called by platform instances to add accessories to the bridge.
|
|
60
|
+
* api.registerPlatformAccessories(pluginName, platformName, [accessory, ...])
|
|
61
|
+
*/
|
|
62
|
+
registerPlatformAccessories(_pluginName: string, _platformName: string, accessories: any | any[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* Called by platform instances after mutating an accessory they already own,
|
|
65
|
+
* to persist the change. api.updatePlatformAccessories([accessory, ...])
|
|
66
|
+
*
|
|
67
|
+
* Homebridge only re-writes its accessory cache here, so we do the same: the
|
|
68
|
+
* accessory object is already live on the bridge, nothing needs re-adding.
|
|
69
|
+
* Plugins call this from discovery callbacks (often outside any try/catch),
|
|
70
|
+
* so it must never throw.
|
|
71
|
+
*/
|
|
72
|
+
updatePlatformAccessories(accessories: any | any[]): void;
|
|
73
|
+
/**
|
|
74
|
+
* Called by some platform plugins to publish accessories as standalone (not bridged).
|
|
75
|
+
* In real Homebridge these get their own HAP server, but OpenBridge funnels
|
|
76
|
+
* everything through a single bridge so we add them as bridged accessories.
|
|
77
|
+
*/
|
|
78
|
+
publishExternalAccessories(_pluginName: string, accessories: any | any[]): void;
|
|
79
|
+
/**
|
|
80
|
+
* Returns whether the running Homebridge version is >= the given semver string.
|
|
81
|
+
* We always return false (we are not Homebridge), which disables optional features
|
|
82
|
+
* like Adaptive Lighting that check for minimum versions.
|
|
83
|
+
*/
|
|
84
|
+
versionGreaterOrEqual(_version: string): boolean;
|
|
85
|
+
getRawAccessories(): any[];
|
|
86
|
+
getAccessories(): SerializedAccessory[];
|
|
87
|
+
/**
|
|
88
|
+
* Called by platform instances to remove accessories.
|
|
89
|
+
*/
|
|
90
|
+
unregisterPlatformAccessories(_pluginName: string, _platformName: string, accessories: any[]): void;
|
|
91
|
+
getPlatformRegistrations(): PlatformRegistration[];
|
|
92
|
+
/**
|
|
93
|
+
* Instantiate all registered platforms with the given config entries.
|
|
94
|
+
* registry is optional: if provided, each platform is registered as a
|
|
95
|
+
* PluginInstance so it appears in the OpenBridge UI plugins page.
|
|
96
|
+
*/
|
|
97
|
+
launchPlatforms(configs: PlatformConfig[], log: any, registry?: any): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Restart a platform that has failed. Removes old accessories,
|
|
100
|
+
* re-instantiates the platform, and re-emits didFinishLaunching.
|
|
101
|
+
*/
|
|
102
|
+
restartPlatform(platformName: string): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Returns the backoff delay (ms) for the next restart attempt of a platform.
|
|
105
|
+
* Exponential: 30s, 60s, 120s, 240s, capped at 5 minutes.
|
|
106
|
+
*/
|
|
107
|
+
getRestartBackoff(platformName: string): number;
|
|
108
|
+
getPlatformInstances(): Map<string, any>;
|
|
109
|
+
/**
|
|
110
|
+
* Load previously cached accessories from disk and pre-populate
|
|
111
|
+
* the in-memory map + bridge so that platforms can re-adopt them
|
|
112
|
+
* via configureAccessory() before starting device discovery.
|
|
113
|
+
*
|
|
114
|
+
* Must be called after the bridge is set up but before launchPlatforms().
|
|
115
|
+
*/
|
|
116
|
+
loadCachedAccessories(): void;
|
|
117
|
+
/**
|
|
118
|
+
* Drop cached accessories whose owning platform is not active.
|
|
119
|
+
*
|
|
120
|
+
* `loadCachedAccessories()` restores everything it finds and adds it to the
|
|
121
|
+
* bridge, because a platform has to be able to re-adopt its accessories via
|
|
122
|
+
* configureAccessory() before discovery starts. Nothing ever removed the
|
|
123
|
+
* accessories of a plugin that was no longer active, so they stayed on the
|
|
124
|
+
* bridge forever: still in the Home app, still listed in OpenBridge, but with
|
|
125
|
+
* no plugin behind them to update or control them. They show up as
|
|
126
|
+
* "Default-Manufacturer / Default-Model" entries that do nothing.
|
|
127
|
+
*
|
|
128
|
+
* "Not active" covers both an uninstalled plugin and a disabled one: the
|
|
129
|
+
* loader deliberately leaves a disabled plugin out of _platformRegistrations
|
|
130
|
+
* so it is reclaimed here rather than lingering as dead HomeKit entries.
|
|
131
|
+
*
|
|
132
|
+
* Attribution is by platform registration, not by discovery, so this is safe
|
|
133
|
+
* to call as soon as platforms have been launched: a platform registers at
|
|
134
|
+
* load time, long before it finishes finding devices.
|
|
135
|
+
*
|
|
136
|
+
* Accessories with no recorded owner are left alone. They cannot be
|
|
137
|
+
* attributed, and silently deleting a user's HomeKit devices is far worse
|
|
138
|
+
* than leaving one stale entry behind.
|
|
139
|
+
*/
|
|
140
|
+
pruneOrphanedAccessories(): string[];
|
|
141
|
+
/**
|
|
142
|
+
* Persist the current accessory map to disk so accessories survive
|
|
143
|
+
* container restarts without being re-discovered by HomeKit.
|
|
144
|
+
*/
|
|
145
|
+
private saveCachedAccessories;
|
|
146
|
+
}
|
|
147
|
+
export interface PlatformConfig {
|
|
148
|
+
platform: string;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
}
|
|
151
|
+
/** Minimal representation of an accessory stored on disk between restarts. */
|
|
152
|
+
export interface CachedAccessoryEntry {
|
|
153
|
+
UUID: string;
|
|
154
|
+
displayName: string;
|
|
155
|
+
category: number;
|
|
156
|
+
context: any;
|
|
157
|
+
associatedPlatform: string;
|
|
158
|
+
}
|
|
159
|
+
export interface SerializedCharacteristic {
|
|
160
|
+
uuid: string;
|
|
161
|
+
name: string;
|
|
162
|
+
value: unknown;
|
|
163
|
+
format: string;
|
|
164
|
+
perms: string[];
|
|
165
|
+
}
|
|
166
|
+
export interface SerializedService {
|
|
167
|
+
uuid: string;
|
|
168
|
+
name: string;
|
|
169
|
+
displayName: string;
|
|
170
|
+
characteristics: SerializedCharacteristic[];
|
|
171
|
+
}
|
|
172
|
+
export interface SerializedAccessory {
|
|
173
|
+
uuid: string;
|
|
174
|
+
displayName: string;
|
|
175
|
+
category: number;
|
|
176
|
+
services: SerializedService[];
|
|
177
|
+
reachable: boolean;
|
|
178
|
+
}
|
|
179
|
+
export declare function serializeAccessory(acc: any): SerializedAccessory;
|
|
180
|
+
/**
|
|
181
|
+
* Load a Homebridge-compatible plugin from a file path.
|
|
182
|
+
* Plugins use CommonJS: module.exports = function(homebridge) { ... }
|
|
183
|
+
*/
|
|
184
|
+
export declare function loadHomebridgePlugin(pluginPath: string): (homebridge: HomebridgeAPI) => void;
|
|
185
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nubisco/openbridge-compatibility-homebridge
|
|
3
|
+
*
|
|
4
|
+
* Homebridge API compatibility shim for OpenBridge.
|
|
5
|
+
* Wraps hap-nodejs so Homebridge platform plugins can run inside OpenBridge.
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, resolve } from 'path';
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
import { Logger } from '@nubisco/openbridge-logger';
|
|
14
|
+
const hapLog = Logger.create('hap-compat');
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
// Synchronous load: hap-nodejs is CJS
|
|
17
|
+
export function loadHapSync() {
|
|
18
|
+
const req = createRequire(import.meta.url);
|
|
19
|
+
const candidates = [
|
|
20
|
+
resolve(__dirname, '../../../node_modules/hap-nodejs'),
|
|
21
|
+
resolve(__dirname, '../../../../node_modules/hap-nodejs'),
|
|
22
|
+
resolve(process.cwd(), 'node_modules/hap-nodejs'),
|
|
23
|
+
];
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
return req(candidate);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* try next */
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw new Error('hap-nodejs not found. Run: pnpm add hap-nodejs --filter @nubisco/openbridge-daemon');
|
|
33
|
+
}
|
|
34
|
+
export let hap = null;
|
|
35
|
+
export function initHap(hapNodeJs) {
|
|
36
|
+
hap = hapNodeJs;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The Homebridge API object passed to plugins.
|
|
40
|
+
* Plugins call: module.exports = function(homebridge) { homebridge.registerPlatform(...) }
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* PlatformAccessory: Homebridge-compatible wrapper around a hap.Accessory.
|
|
44
|
+
*
|
|
45
|
+
* hap-nodejs does NOT export PlatformAccessory; it is a Homebridge construct.
|
|
46
|
+
* Plugins call `new PlatformAccessory(displayName, uuid, category?)` and then
|
|
47
|
+
* use `.getService()`, `.addService()`, `.context`, etc.
|
|
48
|
+
*/
|
|
49
|
+
function makePlatformAccessoryClass(hapNodeJs) {
|
|
50
|
+
const HapAccessory = hapNodeJs.Accessory;
|
|
51
|
+
class PlatformAccessory extends HapAccessory {
|
|
52
|
+
context = {};
|
|
53
|
+
_associatedPlugin;
|
|
54
|
+
_associatedPlatform;
|
|
55
|
+
constructor(displayName, uuid, category) {
|
|
56
|
+
super(displayName, uuid);
|
|
57
|
+
if (category !== undefined) {
|
|
58
|
+
this.category = category;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Override addService to match Homebridge's behavior:
|
|
63
|
+
* when a plugin calls addService(ServiceType, displayName) without a subtype and a service
|
|
64
|
+
* of the same UUID already exists, automatically use displayName as the subtype.
|
|
65
|
+
* Without this, hap-nodejs throws for accessories with multiple services of the same type
|
|
66
|
+
* (e.g. multiple TemperatureSensor services on a heat-pump accessory).
|
|
67
|
+
*/
|
|
68
|
+
addService(serviceType, ...args) {
|
|
69
|
+
if (typeof serviceType === 'function' && args.length >= 1 && args[1] === undefined) {
|
|
70
|
+
const displayName = args[0];
|
|
71
|
+
const typeUUID = serviceType.UUID;
|
|
72
|
+
if (displayName && typeUUID) {
|
|
73
|
+
const hasConflict = this.services?.some((s) => s.UUID === typeUUID);
|
|
74
|
+
if (hasConflict) {
|
|
75
|
+
// Use displayName as subtype to allow multiple services of the same type
|
|
76
|
+
return super.addService(serviceType, displayName, displayName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return super.addService(serviceType, ...args);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return PlatformAccessory;
|
|
84
|
+
}
|
|
85
|
+
export class HomebridgeAPI extends EventEmitter {
|
|
86
|
+
hap;
|
|
87
|
+
platformAccessory;
|
|
88
|
+
version = '2.6.0';
|
|
89
|
+
serverVersion = '2.6.0';
|
|
90
|
+
user;
|
|
91
|
+
_bridge;
|
|
92
|
+
_platformRegistrations = [];
|
|
93
|
+
_platformInstances = new Map();
|
|
94
|
+
_accessories = new Map();
|
|
95
|
+
_onAccessoryAdd;
|
|
96
|
+
_onAccessoryRemove;
|
|
97
|
+
/** Stored launch configs so platforms can be restarted after failure */
|
|
98
|
+
_platformLaunchConfigs = new Map();
|
|
99
|
+
/** Tracks consecutive restart failures per platform for exponential backoff */
|
|
100
|
+
_platformRestartAttempts = new Map();
|
|
101
|
+
/** Tracks last error time per platform to detect recurring failures */
|
|
102
|
+
_platformLastError = new Map();
|
|
103
|
+
constructor(hapNodeJs, bridge) {
|
|
104
|
+
super();
|
|
105
|
+
this._bridge = bridge;
|
|
106
|
+
// User storage paths (Homebridge plugins call api.user.storagePath(), etc.)
|
|
107
|
+
const storageBase = resolve(os.homedir(), '.openbridge');
|
|
108
|
+
this.user = {
|
|
109
|
+
storagePath: () => storageBase,
|
|
110
|
+
configPath: () => resolve(storageBase, 'config.json'),
|
|
111
|
+
persistPath: () => resolve(storageBase, 'persist'),
|
|
112
|
+
cachedAccessoryPath: () => resolve(storageBase, 'accessories'),
|
|
113
|
+
};
|
|
114
|
+
this.hap = hapNodeJs;
|
|
115
|
+
// Build PlatformAccessory from the hap.Accessory base class
|
|
116
|
+
this.platformAccessory = makePlatformAccessoryClass(hapNodeJs);
|
|
117
|
+
// Alias for plugins that access api.hap.Accessory.Categories
|
|
118
|
+
if (!this.hap.Accessory) {
|
|
119
|
+
this.hap.Accessory = { Categories: hapNodeJs.Categories };
|
|
120
|
+
}
|
|
121
|
+
else if (!this.hap.Accessory.Categories) {
|
|
122
|
+
this.hap.Accessory.Categories = hapNodeJs.Categories;
|
|
123
|
+
}
|
|
124
|
+
// Some plugins use api.hap.uuid
|
|
125
|
+
if (!this.hap.uuid) {
|
|
126
|
+
this.hap.uuid = hapNodeJs.uuid;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
onAccessoryAdd(fn) {
|
|
130
|
+
this._onAccessoryAdd = fn;
|
|
131
|
+
}
|
|
132
|
+
onAccessoryRemove(fn) {
|
|
133
|
+
this._onAccessoryRemove = fn;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Called by plugins in two forms:
|
|
137
|
+
* 4-arg: registerPlatform(pluginName, platformName, Constructor, dynamic)
|
|
138
|
+
* 2-arg: registerPlatform(platformName, Constructor, dynamic?)
|
|
139
|
+
*/
|
|
140
|
+
registerPlatform(...args) {
|
|
141
|
+
let pluginName;
|
|
142
|
+
let platformName;
|
|
143
|
+
let Constructor;
|
|
144
|
+
let dynamic;
|
|
145
|
+
if (typeof args[0] === 'string' && typeof args[1] === 'string') {
|
|
146
|
+
// 4-arg form: (pluginName, platformName, Constructor, dynamic)
|
|
147
|
+
;
|
|
148
|
+
[pluginName, platformName, Constructor, dynamic = false] = args;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// 2-arg form: (platformName, Constructor, dynamic?)
|
|
152
|
+
pluginName = args[0];
|
|
153
|
+
platformName = args[0];
|
|
154
|
+
Constructor = args[1];
|
|
155
|
+
dynamic = args[2] ?? false;
|
|
156
|
+
}
|
|
157
|
+
this._platformRegistrations.push({ pluginName, platformName, Constructor, dynamic });
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Called by plugins: homebridge.registerAccessory(pluginName, accessoryName, Constructor)
|
|
161
|
+
*/
|
|
162
|
+
registerAccessory(_pluginName, _accessoryName, _Constructor) {
|
|
163
|
+
// TODO: implement accessory (non-platform) registration
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Called by platform instances to add accessories to the bridge.
|
|
167
|
+
* api.registerPlatformAccessories(pluginName, platformName, [accessory, ...])
|
|
168
|
+
*/
|
|
169
|
+
registerPlatformAccessories(_pluginName, _platformName, accessories) {
|
|
170
|
+
const arr = Array.isArray(accessories) ? accessories : [accessories];
|
|
171
|
+
hapLog.info(`registerPlatformAccessories: adding ${arr.length} accessory(ies)`);
|
|
172
|
+
for (const acc of arr) {
|
|
173
|
+
try {
|
|
174
|
+
if (!acc?.UUID) {
|
|
175
|
+
hapLog.warn(` skipped accessory with no UUID: ${acc?.displayName}`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
acc._associatedPlatform = _platformName;
|
|
179
|
+
// If a cached version is already on the bridge, remove it first
|
|
180
|
+
const existing = this._accessories.get(acc.UUID);
|
|
181
|
+
if (existing && this._bridge) {
|
|
182
|
+
try {
|
|
183
|
+
this._bridge.removeBridgedAccessory(existing, false);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
/* not on bridge yet */
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
hapLog.info(` + ${acc.displayName} (UUID: ${acc.UUID})`);
|
|
190
|
+
this._accessories.set(acc.UUID, acc);
|
|
191
|
+
if (this._bridge) {
|
|
192
|
+
try {
|
|
193
|
+
this._bridge.addBridgedAccessory(acc);
|
|
194
|
+
}
|
|
195
|
+
catch (bridgeErr) {
|
|
196
|
+
hapLog.warn(` bridge.addBridgedAccessory failed (non-fatal): ${bridgeErr}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this._onAccessoryAdd?.(acc);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
hapLog.error(` ✗ failed to register ${acc?.displayName}: ${err}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
this.saveCachedAccessories();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Called by platform instances after mutating an accessory they already own,
|
|
209
|
+
* to persist the change. api.updatePlatformAccessories([accessory, ...])
|
|
210
|
+
*
|
|
211
|
+
* Homebridge only re-writes its accessory cache here, so we do the same: the
|
|
212
|
+
* accessory object is already live on the bridge, nothing needs re-adding.
|
|
213
|
+
* Plugins call this from discovery callbacks (often outside any try/catch),
|
|
214
|
+
* so it must never throw.
|
|
215
|
+
*/
|
|
216
|
+
updatePlatformAccessories(accessories) {
|
|
217
|
+
const arr = Array.isArray(accessories) ? accessories : [accessories];
|
|
218
|
+
for (const acc of arr) {
|
|
219
|
+
if (!acc?.UUID) {
|
|
220
|
+
hapLog.warn(`updatePlatformAccessories: skipped accessory with no UUID: ${acc?.displayName}`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (!this._accessories.has(acc.UUID)) {
|
|
224
|
+
hapLog.warn(`updatePlatformAccessories: unknown accessory ${acc.displayName} (UUID: ${acc.UUID}), ignoring`);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// Keep the map pointing at whatever object the plugin considers current
|
|
228
|
+
this._accessories.set(acc.UUID, acc);
|
|
229
|
+
}
|
|
230
|
+
this.saveCachedAccessories();
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Called by some platform plugins to publish accessories as standalone (not bridged).
|
|
234
|
+
* In real Homebridge these get their own HAP server, but OpenBridge funnels
|
|
235
|
+
* everything through a single bridge so we add them as bridged accessories.
|
|
236
|
+
*/
|
|
237
|
+
publishExternalAccessories(_pluginName, accessories) {
|
|
238
|
+
const arr = Array.isArray(accessories) ? accessories : [accessories];
|
|
239
|
+
hapLog.info(`publishExternalAccessories: adding ${arr.length} accessory(ies)`);
|
|
240
|
+
for (const acc of arr) {
|
|
241
|
+
try {
|
|
242
|
+
if (!acc?.UUID) {
|
|
243
|
+
hapLog.warn(` skipped accessory with no UUID: ${acc?.displayName}`);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
acc._associatedPlatform = _pluginName;
|
|
247
|
+
const existing = this._accessories.get(acc.UUID);
|
|
248
|
+
if (existing && this._bridge) {
|
|
249
|
+
try {
|
|
250
|
+
this._bridge.removeBridgedAccessory(existing, false);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
/* not on bridge yet */
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
hapLog.info(` + ${acc.displayName} (UUID: ${acc.UUID})`);
|
|
257
|
+
this._accessories.set(acc.UUID, acc);
|
|
258
|
+
if (this._bridge) {
|
|
259
|
+
try {
|
|
260
|
+
this._bridge.addBridgedAccessory(acc);
|
|
261
|
+
}
|
|
262
|
+
catch (bridgeErr) {
|
|
263
|
+
hapLog.warn(` bridge.addBridgedAccessory failed (non-fatal): ${bridgeErr}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this._onAccessoryAdd?.(acc);
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
hapLog.error(` failed to publish ${acc?.displayName}: ${err}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
this.saveCachedAccessories();
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Returns whether the running Homebridge version is >= the given semver string.
|
|
276
|
+
* We always return false (we are not Homebridge), which disables optional features
|
|
277
|
+
* like Adaptive Lighting that check for minimum versions.
|
|
278
|
+
*/
|
|
279
|
+
versionGreaterOrEqual(_version) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
getRawAccessories() {
|
|
283
|
+
return Array.from(this._accessories.values());
|
|
284
|
+
}
|
|
285
|
+
getAccessories() {
|
|
286
|
+
hapLog.debug(`getAccessories called: map has ${this._accessories.size} entries`);
|
|
287
|
+
return Array.from(this._accessories.values()).map(serializeAccessory);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Called by platform instances to remove accessories.
|
|
291
|
+
*/
|
|
292
|
+
unregisterPlatformAccessories(_pluginName, _platformName, accessories) {
|
|
293
|
+
for (const acc of accessories) {
|
|
294
|
+
this._accessories.delete(acc.UUID);
|
|
295
|
+
try {
|
|
296
|
+
this._bridge.removeBridgedAccessory(acc, false);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
/* ignore if not found */
|
|
300
|
+
}
|
|
301
|
+
this._onAccessoryRemove?.(acc);
|
|
302
|
+
}
|
|
303
|
+
this.saveCachedAccessories();
|
|
304
|
+
}
|
|
305
|
+
getPlatformRegistrations() {
|
|
306
|
+
return [...this._platformRegistrations];
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Instantiate all registered platforms with the given config entries.
|
|
310
|
+
* registry is optional: if provided, each platform is registered as a
|
|
311
|
+
* PluginInstance so it appears in the OpenBridge UI plugins page.
|
|
312
|
+
*/
|
|
313
|
+
async launchPlatforms(configs, log, registry) {
|
|
314
|
+
for (const reg of this._platformRegistrations) {
|
|
315
|
+
const config = configs.find((c) => c.platform === reg.platformName);
|
|
316
|
+
if (!config) {
|
|
317
|
+
log.warn(`No config found for platform "${reg.platformName}", skipping`);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
// Register as a plugin instance so the UI shows it, marked as homebridge source
|
|
321
|
+
if (registry) {
|
|
322
|
+
const pseudoPlugin = {
|
|
323
|
+
manifest: {
|
|
324
|
+
name: reg.pluginName,
|
|
325
|
+
version: config.version ?? '?.?.?',
|
|
326
|
+
description: `Platform: ${reg.platformName}`,
|
|
327
|
+
author: 'homebridge-compat',
|
|
328
|
+
},
|
|
329
|
+
setup: undefined,
|
|
330
|
+
start: undefined,
|
|
331
|
+
stop: undefined,
|
|
332
|
+
};
|
|
333
|
+
const instance = registry.register(pseudoPlugin, reg.platformName);
|
|
334
|
+
instance.source = 'homebridge';
|
|
335
|
+
instance.platformName = reg.platformName;
|
|
336
|
+
registry.updateStatus(instance.id, 'loading');
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
// Give each platform its own named logger so log entries carry plugin: platformName
|
|
340
|
+
const platformLog = createPlatformLogger(Logger.create(reg.platformName), reg.platformName);
|
|
341
|
+
const instance = new reg.Constructor(platformLog, config, this);
|
|
342
|
+
this._platformInstances.set(reg.platformName, instance);
|
|
343
|
+
log.info(`Platform "${reg.platformName}" instantiated`);
|
|
344
|
+
// Dynamic platforms implement configureAccessory() to re-adopt cached accessories.
|
|
345
|
+
// Feed any existing accessories for this platform back to the new instance.
|
|
346
|
+
if (typeof instance.configureAccessory === 'function') {
|
|
347
|
+
const cached = Array.from(this._accessories.values()).filter((acc) => acc._associatedPlatform === reg.platformName);
|
|
348
|
+
for (const acc of cached) {
|
|
349
|
+
try {
|
|
350
|
+
instance.configureAccessory(acc);
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
hapLog.warn(`configureAccessory failed for ${acc.displayName}: ${err}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (cached.length > 0) {
|
|
357
|
+
hapLog.info(`Restored ${cached.length} cached accessory(ies) for "${reg.platformName}"`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
registry?.updateStatus(reg.platformName, 'running');
|
|
361
|
+
// Store launch config so we can restart the platform if it fails later
|
|
362
|
+
this._platformLaunchConfigs.set(reg.platformName, { config, log, registry });
|
|
363
|
+
this._platformRestartAttempts.set(reg.platformName, 0);
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
log.error(`Platform "${reg.platformName}" failed to start: ${err}`);
|
|
367
|
+
registry?.updateStatus(reg.platformName, 'error', String(err));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Signal that all plugins have loaded: platforms listen for this
|
|
371
|
+
hapLog.info('Emitting didFinishLaunching: platforms should start device discovery');
|
|
372
|
+
this.emit('didFinishLaunching');
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Restart a platform that has failed. Removes old accessories,
|
|
376
|
+
* re-instantiates the platform, and re-emits didFinishLaunching.
|
|
377
|
+
*/
|
|
378
|
+
async restartPlatform(platformName) {
|
|
379
|
+
const reg = this._platformRegistrations.find((r) => r.platformName === platformName);
|
|
380
|
+
const launchConfig = this._platformLaunchConfigs.get(platformName);
|
|
381
|
+
if (!reg || !launchConfig) {
|
|
382
|
+
hapLog.warn(`Cannot restart platform "${platformName}": no registration or config found`);
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const { config, registry } = launchConfig;
|
|
386
|
+
const attempts = (this._platformRestartAttempts.get(platformName) ?? 0) + 1;
|
|
387
|
+
this._platformRestartAttempts.set(platformName, attempts);
|
|
388
|
+
hapLog.info(`Restarting platform "${platformName}" (attempt ${attempts})...`);
|
|
389
|
+
registry?.updateStatus(platformName, 'loading');
|
|
390
|
+
// Remove old accessories registered by this platform
|
|
391
|
+
const oldInstance = this._platformInstances.get(platformName);
|
|
392
|
+
if (oldInstance) {
|
|
393
|
+
const toRemove = [];
|
|
394
|
+
for (const [_uuid, acc] of this._accessories.entries()) {
|
|
395
|
+
if (acc._associatedPlatform === platformName) {
|
|
396
|
+
toRemove.push(acc);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (const acc of toRemove) {
|
|
400
|
+
this._accessories.delete(acc.UUID);
|
|
401
|
+
try {
|
|
402
|
+
this._bridge?.removeBridgedAccessory(acc, false);
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
/* ignore */
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
hapLog.info(` Removed ${toRemove.length} stale accessory(ies)`);
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
const platformLog = createPlatformLogger(Logger.create(reg.platformName), reg.platformName);
|
|
412
|
+
const instance = new reg.Constructor(platformLog, config, this);
|
|
413
|
+
this._platformInstances.set(platformName, instance);
|
|
414
|
+
// Re-emit didFinishLaunching so the new instance starts discovery
|
|
415
|
+
this.emit('didFinishLaunching');
|
|
416
|
+
hapLog.info(`Platform "${platformName}" restarted successfully`);
|
|
417
|
+
registry?.updateStatus(platformName, 'running');
|
|
418
|
+
this._platformRestartAttempts.set(platformName, 0);
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
hapLog.error(`Platform "${platformName}" restart failed: ${err}`);
|
|
423
|
+
registry?.updateStatus(platformName, 'error', String(err));
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Returns the backoff delay (ms) for the next restart attempt of a platform.
|
|
429
|
+
* Exponential: 30s, 60s, 120s, 240s, capped at 5 minutes.
|
|
430
|
+
*/
|
|
431
|
+
getRestartBackoff(platformName) {
|
|
432
|
+
const attempts = this._platformRestartAttempts.get(platformName) ?? 0;
|
|
433
|
+
return Math.min(30_000 * Math.pow(2, attempts), 5 * 60_000);
|
|
434
|
+
}
|
|
435
|
+
getPlatformInstances() {
|
|
436
|
+
return this._platformInstances;
|
|
437
|
+
}
|
|
438
|
+
// ── Accessory cache persistence ──────────────────────────────────────────
|
|
439
|
+
/**
|
|
440
|
+
* Load previously cached accessories from disk and pre-populate
|
|
441
|
+
* the in-memory map + bridge so that platforms can re-adopt them
|
|
442
|
+
* via configureAccessory() before starting device discovery.
|
|
443
|
+
*
|
|
444
|
+
* Must be called after the bridge is set up but before launchPlatforms().
|
|
445
|
+
*/
|
|
446
|
+
loadCachedAccessories() {
|
|
447
|
+
const cachePath = resolve(this.user.cachedAccessoryPath(), 'cachedAccessories.json');
|
|
448
|
+
if (!existsSync(cachePath)) {
|
|
449
|
+
hapLog.debug('No cached accessories file found, starting fresh');
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
let entries;
|
|
453
|
+
try {
|
|
454
|
+
entries = JSON.parse(readFileSync(cachePath, 'utf8'));
|
|
455
|
+
}
|
|
456
|
+
catch (err) {
|
|
457
|
+
hapLog.warn(`Failed to read cached accessories (will start fresh): ${err}`);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
461
|
+
return;
|
|
462
|
+
hapLog.info(`Restoring ${entries.length} cached accessory(ies) from disk`);
|
|
463
|
+
const PlatformAccessory = this.platformAccessory;
|
|
464
|
+
for (const entry of entries) {
|
|
465
|
+
try {
|
|
466
|
+
const acc = new PlatformAccessory(entry.displayName, entry.UUID, entry.category);
|
|
467
|
+
acc.context = entry.context ?? {};
|
|
468
|
+
acc._associatedPlatform = entry.associatedPlatform;
|
|
469
|
+
this._accessories.set(acc.UUID, acc);
|
|
470
|
+
if (this._bridge) {
|
|
471
|
+
try {
|
|
472
|
+
this._bridge.addBridgedAccessory(acc);
|
|
473
|
+
}
|
|
474
|
+
catch (bridgeErr) {
|
|
475
|
+
hapLog.warn(` bridge.addBridgedAccessory failed for cached "${entry.displayName}": ${bridgeErr}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
hapLog.debug(` restored: ${entry.displayName} (UUID: ${entry.UUID})`);
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
hapLog.warn(` failed to restore cached accessory "${entry.displayName}": ${err}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Drop cached accessories whose owning platform is not active.
|
|
487
|
+
*
|
|
488
|
+
* `loadCachedAccessories()` restores everything it finds and adds it to the
|
|
489
|
+
* bridge, because a platform has to be able to re-adopt its accessories via
|
|
490
|
+
* configureAccessory() before discovery starts. Nothing ever removed the
|
|
491
|
+
* accessories of a plugin that was no longer active, so they stayed on the
|
|
492
|
+
* bridge forever: still in the Home app, still listed in OpenBridge, but with
|
|
493
|
+
* no plugin behind them to update or control them. They show up as
|
|
494
|
+
* "Default-Manufacturer / Default-Model" entries that do nothing.
|
|
495
|
+
*
|
|
496
|
+
* "Not active" covers both an uninstalled plugin and a disabled one: the
|
|
497
|
+
* loader deliberately leaves a disabled plugin out of _platformRegistrations
|
|
498
|
+
* so it is reclaimed here rather than lingering as dead HomeKit entries.
|
|
499
|
+
*
|
|
500
|
+
* Attribution is by platform registration, not by discovery, so this is safe
|
|
501
|
+
* to call as soon as platforms have been launched: a platform registers at
|
|
502
|
+
* load time, long before it finishes finding devices.
|
|
503
|
+
*
|
|
504
|
+
* Accessories with no recorded owner are left alone. They cannot be
|
|
505
|
+
* attributed, and silently deleting a user's HomeKit devices is far worse
|
|
506
|
+
* than leaving one stale entry behind.
|
|
507
|
+
*/
|
|
508
|
+
pruneOrphanedAccessories() {
|
|
509
|
+
const known = new Set();
|
|
510
|
+
for (const reg of this._platformRegistrations) {
|
|
511
|
+
known.add(reg.platformName);
|
|
512
|
+
known.add(reg.pluginName);
|
|
513
|
+
}
|
|
514
|
+
const orphans = Array.from(this._accessories.values()).filter((acc) => {
|
|
515
|
+
const owner = acc._associatedPlatform;
|
|
516
|
+
return typeof owner === 'string' && owner.length > 0 && !known.has(owner);
|
|
517
|
+
});
|
|
518
|
+
if (orphans.length === 0)
|
|
519
|
+
return [];
|
|
520
|
+
const removed = [];
|
|
521
|
+
for (const acc of orphans) {
|
|
522
|
+
this._accessories.delete(acc.UUID);
|
|
523
|
+
try {
|
|
524
|
+
this._bridge?.removeBridgedAccessory(acc, false);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
/* was not on the bridge */
|
|
528
|
+
}
|
|
529
|
+
this._onAccessoryRemove?.(acc);
|
|
530
|
+
removed.push(acc.displayName);
|
|
531
|
+
hapLog.info(`Removed orphaned accessory "${acc.displayName}" (plugin "${acc._associatedPlatform}" is not active)`);
|
|
532
|
+
}
|
|
533
|
+
this.saveCachedAccessories();
|
|
534
|
+
return removed;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Persist the current accessory map to disk so accessories survive
|
|
538
|
+
* container restarts without being re-discovered by HomeKit.
|
|
539
|
+
*/
|
|
540
|
+
saveCachedAccessories() {
|
|
541
|
+
const dir = this.user.cachedAccessoryPath();
|
|
542
|
+
const cachePath = resolve(dir, 'cachedAccessories.json');
|
|
543
|
+
const entries = [];
|
|
544
|
+
for (const acc of this._accessories.values()) {
|
|
545
|
+
entries.push({
|
|
546
|
+
UUID: acc.UUID,
|
|
547
|
+
displayName: acc.displayName,
|
|
548
|
+
category: acc.category ?? 1,
|
|
549
|
+
context: acc.context ?? {},
|
|
550
|
+
associatedPlatform: acc._associatedPlatform ?? '',
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
mkdirSync(dir, { recursive: true });
|
|
555
|
+
writeFileSync(cachePath, JSON.stringify(entries, null, 2));
|
|
556
|
+
hapLog.debug(`Saved ${entries.length} accessory(ies) to cache`);
|
|
557
|
+
}
|
|
558
|
+
catch (err) {
|
|
559
|
+
hapLog.warn(`Failed to save accessory cache: ${err}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function createPlatformLogger(baseLog, platformName) {
|
|
564
|
+
const prefix = `[${platformName}]`;
|
|
565
|
+
// Homebridge platform logger is a callable function with .info/.warn/.error/.log methods
|
|
566
|
+
const fn = (message, ...args) => baseLog.info(`${prefix} ${message}`, ...args);
|
|
567
|
+
fn.info = (message, ...args) => baseLog.info(`${prefix} ${message}`, ...args);
|
|
568
|
+
fn.warn = (message, ...args) => baseLog.warn(`${prefix} ${message}`, ...args);
|
|
569
|
+
fn.error = (message, ...args) => baseLog.error(`${prefix} ${message}`, ...args);
|
|
570
|
+
fn.debug = (message, ...args) => baseLog.debug(`${prefix} ${message}`, ...args);
|
|
571
|
+
// Generic log(level, message, ...args): used by some plugins (e.g. homebridge-shelly-ds9)
|
|
572
|
+
fn.log = (level, message, ...args) => {
|
|
573
|
+
const method = fn[level] ?? fn.info;
|
|
574
|
+
method(message, ...args);
|
|
575
|
+
};
|
|
576
|
+
// success is treated as info in some plugins
|
|
577
|
+
fn.success = (message, ...args) => baseLog.info(`${prefix} ${message}`, ...args);
|
|
578
|
+
return fn;
|
|
579
|
+
}
|
|
580
|
+
// Map HAP service UUIDs to accessory categories. Used to infer the category
|
|
581
|
+
// when a plugin doesn't explicitly set one (many Homebridge plugins don't).
|
|
582
|
+
const SERVICE_UUID_TO_CATEGORY = {
|
|
583
|
+
'00000043-0000-1000-8000-0026BB765291': 5, // Lightbulb
|
|
584
|
+
'00000040-0000-1000-8000-0026BB765291': 3, // Fan / Fanv2
|
|
585
|
+
'000000B7-0000-1000-8000-0026BB765291': 3, // Fanv2
|
|
586
|
+
'00000041-0000-1000-8000-0026BB765291': 4, // GarageDoorOpener
|
|
587
|
+
'00000045-0000-1000-8000-0026BB765291': 6, // LockMechanism
|
|
588
|
+
'00000047-0000-1000-8000-0026BB765291': 7, // Outlet
|
|
589
|
+
'00000049-0000-1000-8000-0026BB765291': 8, // Switch
|
|
590
|
+
'0000004A-0000-1000-8000-0026BB765291': 9, // Thermostat
|
|
591
|
+
'00000080-0000-1000-8000-0026BB765291': 10, // ContactSensor
|
|
592
|
+
'00000082-0000-1000-8000-0026BB765291': 10, // HumiditySensor
|
|
593
|
+
'00000083-0000-1000-8000-0026BB765291': 10, // LeakSensor
|
|
594
|
+
'00000084-0000-1000-8000-0026BB765291': 10, // LightSensor
|
|
595
|
+
'00000085-0000-1000-8000-0026BB765291': 10, // MotionSensor
|
|
596
|
+
'00000086-0000-1000-8000-0026BB765291': 10, // OccupancySensor
|
|
597
|
+
'0000008A-0000-1000-8000-0026BB765291': 10, // TemperatureSensor
|
|
598
|
+
'0000007E-0000-1000-8000-0026BB765291': 11, // SecuritySystem
|
|
599
|
+
'00000081-0000-1000-8000-0026BB765291': 12, // Door
|
|
600
|
+
'0000008B-0000-1000-8000-0026BB765291': 13, // Window
|
|
601
|
+
'0000008C-0000-1000-8000-0026BB765291': 14, // WindowCovering
|
|
602
|
+
'00000110-0000-1000-8000-0026BB765291': 17, // CameraRTPStreamManagement
|
|
603
|
+
'00000121-0000-1000-8000-0026BB765291': 18, // Doorbell
|
|
604
|
+
'000000BB-0000-1000-8000-0026BB765291': 19, // AirPurifier
|
|
605
|
+
'000000BC-0000-1000-8000-0026BB765291': 20, // HeaterCooler
|
|
606
|
+
'000000BD-0000-1000-8000-0026BB765291': 22, // HumidifierDehumidifier
|
|
607
|
+
'000000D0-0000-1000-8000-0026BB765291': 29, // Valve (FAUCET)
|
|
608
|
+
'000000D8-0000-1000-8000-0026BB765291': 31, // Television
|
|
609
|
+
};
|
|
610
|
+
function inferCategoryFromServices(acc) {
|
|
611
|
+
try {
|
|
612
|
+
for (const svc of acc.services ?? []) {
|
|
613
|
+
const cat = SERVICE_UUID_TO_CATEGORY[svc.UUID];
|
|
614
|
+
if (cat !== undefined)
|
|
615
|
+
return cat;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
/* ignore */
|
|
620
|
+
}
|
|
621
|
+
return 1; // OTHER
|
|
622
|
+
}
|
|
623
|
+
export function serializeAccessory(acc) {
|
|
624
|
+
const services = [];
|
|
625
|
+
try {
|
|
626
|
+
for (const svc of acc.services ?? []) {
|
|
627
|
+
const characteristics = [];
|
|
628
|
+
for (const ch of svc.characteristics ?? []) {
|
|
629
|
+
try {
|
|
630
|
+
characteristics.push({
|
|
631
|
+
uuid: ch.UUID,
|
|
632
|
+
name: ch.constructor?.name ?? ch.displayName ?? ch.UUID,
|
|
633
|
+
value: ch.value,
|
|
634
|
+
format: ch.props?.format ?? 'unknown',
|
|
635
|
+
perms: ch.props?.perms ?? [],
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
/* skip bad characteristic */
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
services.push({
|
|
643
|
+
uuid: svc.UUID,
|
|
644
|
+
name: svc.constructor?.name ?? svc.displayName ?? svc.UUID,
|
|
645
|
+
displayName: svc.displayName ?? '',
|
|
646
|
+
characteristics,
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
/* skip bad service */
|
|
652
|
+
}
|
|
653
|
+
// Use the explicit category if set and not OTHER (1), otherwise infer from services
|
|
654
|
+
const explicitCategory = acc.category ?? 1;
|
|
655
|
+
const category = explicitCategory !== 1 ? explicitCategory : inferCategoryFromServices(acc);
|
|
656
|
+
return {
|
|
657
|
+
uuid: acc.UUID,
|
|
658
|
+
displayName: acc.displayName,
|
|
659
|
+
category,
|
|
660
|
+
services,
|
|
661
|
+
reachable: acc.reachable !== false,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Load a Homebridge-compatible plugin from a file path.
|
|
666
|
+
* Plugins use CommonJS: module.exports = function(homebridge) { ... }
|
|
667
|
+
*/
|
|
668
|
+
export function loadHomebridgePlugin(pluginPath) {
|
|
669
|
+
const req = createRequire(import.meta.url);
|
|
670
|
+
const mod = req(pluginPath);
|
|
671
|
+
const fn = mod.default ?? mod;
|
|
672
|
+
if (typeof fn !== 'function') {
|
|
673
|
+
throw new Error(`Homebridge plugin at ${pluginPath} must export a function`);
|
|
674
|
+
}
|
|
675
|
+
return fn;
|
|
676
|
+
}
|
|
677
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nubisco/openbridge-compatibility-homebridge",
|
|
3
|
+
"version": "0.29.0",
|
|
4
|
+
"description": "Homebridge platform plugin compatibility shim for OpenBridge",
|
|
5
|
+
"author": "José Silva <jose@nubisco.io>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/nubisco/openbridge#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/nubisco/openbridge.git",
|
|
11
|
+
"directory": "packages/compatibility-homebridge"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/nubisco/openbridge/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"homebridge",
|
|
18
|
+
"compatibility",
|
|
19
|
+
"homekit",
|
|
20
|
+
"home-automation",
|
|
21
|
+
"typescript",
|
|
22
|
+
"openbridge"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=20"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.0.0",
|
|
46
|
+
"typescript": "^5.4.0",
|
|
47
|
+
"vitest": "^3.2.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@nubisco/openbridge-logger": "^0.29.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc",
|
|
54
|
+
"dev": "tsc --watch",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest"
|
|
58
|
+
}
|
|
59
|
+
}
|