-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathplatform.ts
174 lines (144 loc) · 7.06 KB
/
platform.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import { PLATFORM_NAME, PLUGIN_NAME } from './settings';
import http, {IncomingMessage, Server, ServerResponse} from 'http';
import {
API,
APIEvent,
CameraControllerOptions,
DynamicPlatformPlugin,
HAP,
Logging,
PlatformAccessory,
PlatformAccessoryEvent,
PlatformConfig,
} from 'homebridge';
import {ExampleFFMPEGStreamingDelegate} from './streamingDelegate';
/*
* IMPORTANT NOTICE
*
* One thing you need to take care of is, that you never ever ever import anything directly from the "homebridge" module (or the "hap-nodejs" module).
* The above import block may seem like, that we do exactly that, but actually those imports are only used for types and interfaces
* and will disappear once the code is compiled to Javascript.
* In fact you can check that by running `npm run build` and opening the compiled Javascript file in the `dist` folder.
* You will notice that the file does not contain a `... = require("homebridge");` statement anywhere in the code.
*
* The contents of the above import statement MUST ONLY be used for type annotation or accessing things like CONST ENUMS,
* which is a special case as they get replaced by the actual value and do not remain as a reference in the compiled code.
* Meaning normal enums are bad, const enums can be used.
*
* You MUST NOT import anything else which remains as a reference in the code, as this will result in
* a `... = require("homebridge");` to be compiled into the final Javascript code.
* This typically leads to unexpected behavior at runtime, as in many cases it won't be able to find the module
* or will import another instance of homebridge causing collisions.
*
* To mitigate this the {@link API | Homebridge API} exposes the whole suite of HAP-NodeJS inside the `hap` property
* of the api object, which can be acquired for example in the initializer function. This reference can be stored
* like this for example and used to access all exported variables and classes from HAP-NodeJS.
*/
let hap: HAP;
let Accessory: typeof PlatformAccessory;
export class ExampleCameraPlatform implements DynamicPlatformPlugin {
private readonly log: Logging;
private readonly api: API;
private requestServer?: Server;
private readonly accessories: PlatformAccessory[] = [];
constructor(log: Logging, config: PlatformConfig, api: API) {
this.log = log;
this.api = api;
// probably parse config or something here
log.info('Example platform finished initializing!');
/*
* When this event is fired, homebridge restored all cached accessories from disk and did call their respective
* `configureAccessory` method for all of them. Dynamic Platform plugins should only register new accessories
* after this event was fired, in order to ensure they weren't added to homebridge already.
* This event can also be used to start discovery of new accessories.
*/
api.on(APIEvent.DID_FINISH_LAUNCHING, () => {
log.info('Example platform \'didFinishLaunching\'');
// The idea of this plugin is that we open a http service which exposes api calls to add or remove accessories
this.createHttpService();
});
}
/*
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to setup event handlers for characteristics and update respective values.
*/
configureAccessory(accessory: PlatformAccessory): void {
this.log('Configuring accessory %s', accessory.displayName);
accessory.on(PlatformAccessoryEvent.IDENTIFY, () => {
this.log('%s identified!', accessory.displayName);
});
const streamingDelegate = new ExampleFFMPEGStreamingDelegate(hap);
const options: CameraControllerOptions = {
cameraStreamCount: 2, // HomeKit requires at least 2 streams, but 1 is also just fine
delegate: streamingDelegate,
streamingOptions: {
// srtp: true, // legacy option which will just enable AES_CM_128_HMAC_SHA1_80 (can still be used though)
supportedCryptoSuites: [hap.SRTPCryptoSuites.NONE, hap.SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80], // NONE is not supported by iOS just there for testing with Wireshark for example
video: {
codec: {
profiles: [hap.H264Profile.BASELINE, hap.H264Profile.MAIN, hap.H264Profile.HIGH],
levels: [hap.H264Level.LEVEL3_1, hap.H264Level.LEVEL3_2, hap.H264Level.LEVEL4_0],
},
resolutions: [
[1920, 1080, 30], // width, height, framerate
[1280, 960, 30],
[1280, 720, 30],
[1024, 768, 30],
[640, 480, 30],
[640, 360, 30],
[480, 360, 30],
[480, 270, 30],
[320, 240, 30],
[320, 240, 15], // Apple Watch requires this configuration (Apple Watch also seems to required OPUS @16K)
[320, 180, 30],
],
},
/* audio option is omitted, as it is not supported in this example; HAP-NodeJS will fake an appropriate audio codec
audio: {
comfort_noise: false, // optional, default false
codecs: [
{
type: AudioStreamingCodecType.OPUS,
audioChannels: 1, // optional, default 1
samplerate: [AudioStreamingSamplerate.KHZ_16, AudioStreamingSamplerate.KHZ_24], // 16 and 24 must be present for AAC-ELD or OPUS
},
],
},
// */
},
};
const cameraController = new hap.CameraController(options);
streamingDelegate.controller = cameraController;
accessory.configureController(cameraController);
this.accessories.push(accessory);
}
// --------------------------- CUSTOM METHODS ---------------------------
addAccessory(name: string) {
this.log.info('Adding new accessory with name %s', name);
// uuid must be generated from a unique but not changing data source, name should not be used in the most cases. But works in this specific example.
const uuid = hap.uuid.generate(name);
const accessory = new Accessory(name, uuid);
this.configureAccessory(accessory); // abusing the configureAccessory here
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
removeAccessories() {
// we don't have any special identifiers, we just remove all our accessories
this.log.info('Removing all accessories');
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, this.accessories);
this.accessories.splice(0, this.accessories.length); // clear out the array
}
createHttpService() {
this.requestServer = http.createServer(this.handleRequest.bind(this));
this.requestServer.listen(18081, () => this.log.info('Http server listening on 18081...'));
}
private handleRequest(request: IncomingMessage, response: ServerResponse) {
if (request.url === '/add') {
this.addAccessory(new Date().toISOString());
} else if (request.url === '/remove') {
this.removeAccessories();
}
response.writeHead(204); // 204 No content
response.end();
}
// ----------------------------------------------------------------------
}