UNPKG

20.3 kBTypeScriptView Raw
1import { EventEmitter } from "events";
2import { ConstructorArgs, HAPPincode, InterfaceName, IPAddress, MacAddress, Nullable, VoidCallback, WithUUID } from "../types";
3import { Advertiser } from "./Advertiser";
4import { Characteristic } from "./Characteristic";
5import { Controller, ControllerConstructor, ControllerIdentifier, ControllerServiceMap } from "./controller";
6import { HAPServer } from "./HAPServer";
7import { AccessoryInfo } from "./model/AccessoryInfo";
8import { ControllerStorage } from "./model/ControllerStorage";
9import { IdentifierCache } from "./model/IdentifierCache";
10import { SerializedService, Service, ServiceCharacteristicChange, ServiceId } from "./Service";
11/**
12 * Known category values. Category is a hint to iOS clients about what "type" of Accessory this represents, for UI only.
13 *
14 * @group Accessory
15 */
16export declare const enum Categories {
17 OTHER = 1,
18 BRIDGE = 2,
19 FAN = 3,
20 GARAGE_DOOR_OPENER = 4,
21 LIGHTBULB = 5,
22 DOOR_LOCK = 6,
23 OUTLET = 7,
24 SWITCH = 8,
25 THERMOSTAT = 9,
26 SENSOR = 10,
27 ALARM_SYSTEM = 11,
28 SECURITY_SYSTEM = 11,//Added to conform to HAP naming
29 DOOR = 12,
30 WINDOW = 13,
31 WINDOW_COVERING = 14,
32 PROGRAMMABLE_SWITCH = 15,
33 RANGE_EXTENDER = 16,
34 CAMERA = 17,
35 IP_CAMERA = 17,//Added to conform to HAP naming
36 VIDEO_DOORBELL = 18,
37 AIR_PURIFIER = 19,
38 AIR_HEATER = 20,
39 AIR_CONDITIONER = 21,
40 AIR_HUMIDIFIER = 22,
41 AIR_DEHUMIDIFIER = 23,
42 APPLE_TV = 24,
43 HOMEPOD = 25,
44 SPEAKER = 26,
45 AIRPORT = 27,
46 SPRINKLER = 28,
47 FAUCET = 29,
48 SHOWER_HEAD = 30,
49 TELEVISION = 31,
50 TARGET_CONTROLLER = 32,// Remote Control
51 ROUTER = 33,
52 AUDIO_RECEIVER = 34,
53 TV_SET_TOP_BOX = 35,
54 TV_STREAMING_STICK = 36
55}
56/**
57 * @group Accessory
58 */
59export interface SerializedAccessory {
60 displayName: string;
61 UUID: string;
62 lastKnownUsername?: MacAddress;
63 category: Categories;
64 services: SerializedService[];
65 linkedServices?: Record<ServiceId, ServiceId[]>;
66 controllers?: SerializedControllerContext[];
67}
68/**
69 * @group Controller API
70 */
71export interface SerializedControllerContext {
72 type: ControllerIdentifier;
73 services: SerializedServiceMap;
74}
75/**
76 * @group Controller API
77 */
78export type SerializedServiceMap = Record<string, ServiceId>;
79/**
80 * @group Controller API
81 */
82export interface ControllerContext {
83 controller: Controller;
84 serviceMap: ControllerServiceMap;
85}
86/**
87 * @group Accessory
88 */
89export declare const enum CharacteristicWarningType {
90 SLOW_WRITE = "slow-write",
91 TIMEOUT_WRITE = "timeout-write",
92 SLOW_READ = "slow-read",
93 TIMEOUT_READ = "timeout-read",
94 WARN_MESSAGE = "warn-message",
95 ERROR_MESSAGE = "error-message",
96 DEBUG_MESSAGE = "debug-message"
97}
98/**
99 * @group Accessory
100 */
101export interface CharacteristicWarning {
102 characteristic: Characteristic;
103 type: CharacteristicWarningType;
104 message: string;
105 originatorChain: string[];
106 stack?: string;
107}
108/**
109 * @group Accessory
110 */
111export interface PublishInfo {
112 username: MacAddress;
113 pincode: HAPPincode;
114 /**
115 * Specify the category for the HomeKit accessory.
116 * The category is used only in the mdns advertisement and specifies the devices type
117 * for the HomeKit controller.
118 * Currently, this only affects the icon shown in the pairing screen.
119 * For the Television and Smart Speaker service it also affects the icon shown in
120 * the Home app when paired.
121 */
122 category?: Categories;
123 setupID?: string;
124 /**
125 * Defines the host where the HAP server will be bound to.
126 * When undefined the HAP server will bind to all available interfaces
127 * (see https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback).
128 *
129 * This property accepts a mixture of IPAddresses and network interface names.
130 * Depending on the mixture of supplied addresses/names hap-nodejs will bind differently.
131 *
132 * It is advised to not just bind to a specific address, but specifying the interface name
133 * in oder to bind on all address records (and ip version) available.
134 *
135 * HAP-NodeJS (or the underlying ciao library) will not report about misspelled interface names,
136 * as it could be that the interface is currently just down and will come up later.
137 *
138 * Here are a few examples:
139 * - bind: "::"
140 * Pretty much identical to not specifying anything, as most systems (with ipv6 support)
141 * will default to the unspecified ipv6 address (with dual stack support).
142 *
143 * - bind: "0.0.0.0"
144 * Binding TCP socket to the unspecified ipv4 address.
145 * The mdns advertisement will exclude any ipv6 address records.
146 *
147 * - bind: ["en0", "lo0"]
148 * The mdns advertising will advertise all records of the en0 and loopback interface (if available) and
149 * will also react to address changes on those interfaces.
150 * In order for the HAP server to accept all those address records (which may contain ipv6 records)
151 * it will bind on the unspecified ipv6 address "::" (assuming dual stack is supported).
152 *
153 * - bind: ["en0", "lo0", "0.0.0.0"]
154 * Same as above, only that the HAP server will bind on the unspecified ipv4 address "0.0.0.0".
155 * The mdns advertisement will not advertise any ipv6 records.
156 *
157 * - bind: "169.254.104.90"
158 * This will bind the HAP server to the address 0.0.0.0.
159 * The mdns advertisement will only advertise the A record 169.254.104.90.
160 * If the given network interface of that address encounters an ip address change (to a different address),
161 * the mdns advertisement will result in not advertising an address at all.
162 * So it is advised to specify an interface name instead of a specific address.
163 * This is identical with ipv6 addresses.
164 *
165 * - bind: ["169.254.104.90", "192.168.1.4"]
166 * As the HAP TCP socket can only bind to a single address, when specifying multiple ip addresses
167 * the HAP server will bind to the unspecified ip address (0.0.0.0 if only ipv4 addresses are supplied,
168 * :: if a mixture or only ipv6 addresses are supplied).
169 * The mdns advertisement will only advertise the specified ip addresses.
170 * If the given network interface of that address encounters an ip address change (to different addresses),
171 * the mdns advertisement will result in not advertising an address at all.
172 * So it is advised to specify an interface name instead of a specific address.
173 *
174 */
175 bind?: (InterfaceName | IPAddress) | (InterfaceName | IPAddress)[];
176 /**
177 * Defines the port where the HAP server will be bound to.
178 * When undefined port 0 will be used resulting in a random port.
179 */
180 port?: number;
181 /**
182 * If this option is set to true, HAP-NodeJS will add identifying material (based on {@link username})
183 * to the end of the accessory display name (and bonjour instance name).
184 * Default: true
185 */
186 addIdentifyingMaterial?: boolean;
187 /**
188 * Defines the advertiser used with the published Accessory.
189 */
190 advertiser?: MDNSAdvertiser;
191}
192/**
193 * @group Accessory
194 */
195export declare const enum MDNSAdvertiser {
196 /**
197 * Use the `@homebridge/ciao` module as advertiser.
198 */
199 CIAO = "ciao",
200 /**
201 * Use the `bonjour-hap` module as advertiser.
202 */
203 BONJOUR = "bonjour-hap",
204 /**
205 * Use Avahi/D-Bus as advertiser.
206 */
207 AVAHI = "avahi",
208 /**
209 * Use systemd-resolved/D-Bus as advertiser.
210 *
211 * Note: The systemd-resolved D-Bus interface doesn't provide means to detect restarts of the service.
212 * Therefore, we can't detect if our advertisement might be lost due to a restart of the systemd-resolved daemon restart.
213 * Consequentially, treat this feature as an experimental feature.
214 */
215 RESOLVED = "resolved"
216}
217/**
218 * @group Accessory
219 */
220export type AccessoryCharacteristicChange = ServiceCharacteristicChange & {
221 service: Service;
222};
223/**
224 * @group Service
225 */
226export interface ServiceConfigurationChange {
227 service: Service;
228}
229/**
230 * @group Accessory
231 */
232export declare const enum AccessoryEventTypes {
233 /**
234 * Emitted when an iOS device wishes for this Accessory to identify itself. If `paired` is false, then
235 * this device is currently browsing for Accessories in the system-provided "Add Accessory" screen. If
236 * `paired` is true, then this is a device that has already paired with us. Note that if `paired` is true,
237 * listening for this event is a shortcut for the underlying mechanism of setting the `Identify` Characteristic:
238 * `getService(Service.AccessoryInformation).getCharacteristic(Characteristic.Identify).on('set', ...)`
239 * You must call the callback for identification to be successful.
240 */
241 IDENTIFY = "identify",
242 /**
243 * This event is emitted once the HAP TCP socket is bound.
244 * At this point the mdns advertisement isn't yet available. Use the {@link ADVERTISED} if you require the accessory to be discoverable.
245 */
246 LISTENING = "listening",
247 /**
248 * This event is emitted once the mDNS suite has fully advertised the presence of the accessory.
249 * This event is guaranteed to be called after {@link LISTENING}.
250 */
251 ADVERTISED = "advertised",
252 SERVICE_CONFIGURATION_CHANGE = "service-configurationChange",
253 /**
254 * Emitted after a change in the value of one of the provided Service's Characteristics.
255 */
256 SERVICE_CHARACTERISTIC_CHANGE = "service-characteristic-change",
257 PAIRED = "paired",
258 UNPAIRED = "unpaired",
259 CHARACTERISTIC_WARNING = "characteristic-warning"
260}
261/**
262 * @group Accessory
263 */
264export declare interface Accessory {
265 on(event: "identify", listener: (paired: boolean, callback: VoidCallback) => void): this;
266 on(event: "listening", listener: (port: number, address: string) => void): this;
267 on(event: "advertised", listener: () => void): this;
268 on(event: "service-configurationChange", listener: (change: ServiceConfigurationChange) => void): this;
269 on(event: "service-characteristic-change", listener: (change: AccessoryCharacteristicChange) => void): this;
270 on(event: "paired", listener: () => void): this;
271 on(event: "unpaired", listener: () => void): this;
272 on(event: "characteristic-warning", listener: (warning: CharacteristicWarning) => void): this;
273 emit(event: "identify", paired: boolean, callback: VoidCallback): boolean;
274 emit(event: "listening", port: number, address: string): boolean;
275 emit(event: "advertised"): boolean;
276 emit(event: "service-configurationChange", change: ServiceConfigurationChange): boolean;
277 emit(event: "service-characteristic-change", change: AccessoryCharacteristicChange): boolean;
278 emit(event: "paired"): boolean;
279 emit(event: "unpaired"): boolean;
280 emit(event: "characteristic-warning", warning: CharacteristicWarning): boolean;
281}
282/**
283 * Accessory is a virtual HomeKit device. It can publish an associated HAP server for iOS devices to communicate
284 * with - or it can run behind another "Bridge" Accessory server.
285 *
286 * Bridged Accessories in this implementation must have a UUID that is unique among all other Accessories that
287 * are hosted by the Bridge. This UUID must be "stable" and unchanging, even when the server is restarted. This
288 * is required so that the Bridge can provide consistent "Accessory IDs" (aid) and "Instance IDs" (iid) for all
289 * Accessories, Services, and Characteristics for iOS clients to reference later.
290 *
291 * @group Accessory
292 */
293export declare class Accessory extends EventEmitter {
294 displayName: string;
295 UUID: string;
296 private static readonly TIMEOUT_WARNING;
297 private static readonly TIMEOUT_AFTER_WARNING;
298 aid: Nullable<number>;
299 _isBridge: boolean;
300 bridged: boolean;
301 bridge?: Accessory;
302 bridgedAccessories: Accessory[];
303 reachable: boolean;
304 lastKnownUsername?: MacAddress;
305 category: Categories;
306 services: Service[];
307 private primaryService?;
308 shouldPurgeUnusedIDs: boolean;
309 /**
310 * Captures if initialization steps inside {@link publish} have been called.
311 * This is important when calling {@link publish} multiple times (e.g. after calling {@link unpublish}).
312 * @private Private API
313 */
314 private initialized;
315 private controllers;
316 private serializedControllers?;
317 private activeCameraController?;
318 /**
319 * @private Private API.
320 */
321 _accessoryInfo?: Nullable<AccessoryInfo>;
322 /**
323 * @private Private API.
324 */
325 _setupID: Nullable<string>;
326 /**
327 * @private Private API.
328 */
329 _identifierCache?: Nullable<IdentifierCache>;
330 /**
331 * @private Private API.
332 */
333 controllerStorage: ControllerStorage;
334 /**
335 * @private Private API.
336 */
337 _advertiser?: Advertiser;
338 /**
339 * @private Private API.
340 */
341 _server?: HAPServer;
342 /**
343 * @private Private API.
344 */
345 _setupURI?: string;
346 private configurationChangeDebounceTimeout?;
347 /**
348 * This property captures the time when we last served a /accessories request.
349 * For multiple bursts of /accessories request we don't want to always contact GET handlers
350 */
351 private lastAccessoriesRequest;
352 constructor(displayName: string, UUID: string);
353 private identificationRequest;
354 /**
355 * Add the given service instance to the Accessory.
356 *
357 * @param service - A {@link Service} instance.
358 * @returns Returns the service instance passed to the method call.
359 */
360 addService(service: Service): Service;
361 /**
362 * Adds a given service by calling the provided {@link Service} constructor with the provided constructor arguments.
363 * @param serviceConstructor - A {@link Service} service constructor (e.g. {@link Service.Switch}).
364 * @param constructorArgs - The arguments passed to the given constructor.
365 * @returns Returns the constructed service instance.
366 */
367 addService<S extends typeof Service>(serviceConstructor: S, ...constructorArgs: ConstructorArgs<S>): Service;
368 removeService(service: Service): void;
369 private removeLinkedService;
370 getService<T extends WithUUID<typeof Service>>(name: string | T): Service | undefined;
371 getServiceById<T extends WithUUID<typeof Service>>(uuid: string | T, subType: string): Service | undefined;
372 /**
373 * Returns the bridging accessory if this accessory is bridged.
374 * Otherwise, returns itself.
375 *
376 * @returns the primary accessory
377 */
378 getPrimaryAccessory: () => Accessory;
379 addBridgedAccessory(accessory: Accessory, deferUpdate?: boolean): Accessory;
380 addBridgedAccessories(accessories: Accessory[]): void;
381 removeBridgedAccessory(accessory: Accessory, deferUpdate?: boolean): void;
382 removeBridgedAccessories(accessories: Accessory[]): void;
383 removeAllBridgedAccessories(): void;
384 private getCharacteristicByIID;
385 protected getAccessoryByAID(aid: number): Accessory | undefined;
386 protected findCharacteristic(aid: number, iid: number): Characteristic | undefined;
387 /**
388 * This method is used to set up a new Controller for this accessory. See {@link Controller} for a more detailed
389 * explanation what a Controller is and what it is capable of.
390 *
391 * The controller can be passed as an instance of the class or as a constructor (without any necessary parameters)
392 * for a new Controller.
393 * Only one Controller of a given {@link ControllerIdentifier} can be configured for a given Accessory.
394 *
395 * When called, it will be checked if there are any services and persistent data the Controller (for the given
396 * {@link ControllerIdentifier}) can be restored from. Otherwise, the Controller will be created with new services.
397 *
398 *
399 * @param controllerConstructor - The Controller instance or constructor to the Controller with no required arguments.
400 */
401 configureController(controllerConstructor: Controller | ControllerConstructor): void;
402 /**
403 * This method will remove a given Controller from this accessory.
404 * The controller object will be restored to its initial state.
405 * This also means that any event handlers setup for the controller will be removed.
406 *
407 * @param controller - The controller which should be removed from the accessory.
408 */
409 removeController(controller: Controller): void;
410 private handleAccessoryUnpairedForControllers;
411 private handleUpdatedControllerServiceMap;
412 setupURI(): string;
413 /**
414 * This method is called right before the accessory is published. It should be used to check for common
415 * mistakes in Accessory structured, which may lead to HomeKit rejecting the accessory when pairing.
416 * If it is called on a bridge it will call this method for all bridged accessories.
417 */
418 private validateAccessory;
419 /**
420 * Assigns aid/iid to ourselves, any Accessories we are bridging, and all associated Services+Characteristics. Uses
421 * the provided identifierCache to keep IDs stable.
422 * @private Private API
423 */
424 _assignIDs(identifierCache: IdentifierCache): void;
425 disableUnusedIDPurge(): void;
426 enableUnusedIDPurge(): void;
427 /**
428 * Manually purge the unused ids if you like, comes handy
429 * when you have disabled auto purge, so you can do it manually
430 */
431 purgeUnusedIDs(): void;
432 /**
433 * Returns a JSON representation of this accessory suitable for delivering to HAP clients.
434 */
435 private toHAP;
436 /**
437 * Returns a JSON representation of this accessory without characteristic values.
438 */
439 private internalHAPRepresentation;
440 /**
441 * Publishes this accessory on the local network for iOS clients to communicate with.
442 * - `info.username` - formatted as a MAC address, like `CC:22:3D:E3:CE:F6`, of this accessory.
443 * Must be globally unique from all Accessories on your local network.
444 * - `info.pincode` - the 8-digit pin code for clients to use when pairing this Accessory.
445 * Must be formatted as a string like `031-45-154`.
446 * - `info.category` - one of the values of the `Accessory.Category` enum, like `Accessory.Category.SWITCH`.
447 * This is a hint to iOS clients about what "type" of Accessory this represents, so
448 * that for instance an appropriate icon can be drawn for the user while adding a
449 * new Accessory.
450 * @param {{
451 * username: string;
452 * pincode: string;
453 * category: Accessory.Categories;
454 * }} info - Required info for publishing.
455 * @param {boolean} allowInsecureRequest - Will allow unencrypted and unauthenticated access to the http server
456 */
457 publish(info: PublishInfo, allowInsecureRequest?: boolean): Promise<void>;
458 /**
459 * Removes this Accessory from the local network
460 * Accessory object will no longer valid after invoking this method
461 * Trying to invoke publish() on the object will result undefined behavior
462 */
463 destroy(): Promise<void>;
464 unpublish(): Promise<void>;
465 private enqueueConfigurationUpdate;
466 private onListening;
467 private handleInitialPairSetupFinished;
468 private handleAddPairing;
469 private handleRemovePairing;
470 private handleListPairings;
471 private handleAccessories;
472 private handleGetCharacteristics;
473 private handleCharacteristicRead;
474 private handleSetCharacteristics;
475 private handleCharacteristicWrite;
476 private handleResource;
477 private handleHAPConnectionClosed;
478 private handleServiceConfigurationChangeEvent;
479 private handleCharacteristicChangeEvent;
480 private sendCharacteristicWarning;
481 private handleCharacteristicWarning;
482 private setupServiceEventHandlers;
483 private _sideloadServices;
484 private static _generateSetupID;
485 static serialize(accessory: Accessory): SerializedAccessory;
486 static deserialize(json: SerializedAccessory): Accessory;
487 static cleanupAccessoryData(username: MacAddress): void;
488 private static serializeServiceMap;
489 private static deserializeServiceMap;
490 private static parseBindOption;
491}
492//# sourceMappingURL=Accessory.d.ts.map
\No newline at end of file