UNPKG

hap-nodejs

Version:

HAP-NodeJS is a Node.js implementation of HomeKit Accessory Server.

205 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataStreamManagement = exports.DataStreamStatus = void 0; const tslib_1 = require("tslib"); const debug_1 = tslib_1.__importDefault(require("debug")); const Characteristic_1 = require("../Characteristic"); const Service_1 = require("../Service"); const tlv = tslib_1.__importStar(require("../util/tlv")); const DataStreamServer_1 = require("./DataStreamServer"); const debug = (0, debug_1.default)("HAP-NodeJS:DataStream:Management"); var TransferTransportConfigurationTypes; (function (TransferTransportConfigurationTypes) { TransferTransportConfigurationTypes[TransferTransportConfigurationTypes["TRANSFER_TRANSPORT_CONFIGURATION"] = 1] = "TRANSFER_TRANSPORT_CONFIGURATION"; })(TransferTransportConfigurationTypes || (TransferTransportConfigurationTypes = {})); var TransportTypeTypes; (function (TransportTypeTypes) { TransportTypeTypes[TransportTypeTypes["TRANSPORT_TYPE"] = 1] = "TRANSPORT_TYPE"; })(TransportTypeTypes || (TransportTypeTypes = {})); var SetupDataStreamSessionTypes; (function (SetupDataStreamSessionTypes) { SetupDataStreamSessionTypes[SetupDataStreamSessionTypes["SESSION_COMMAND_TYPE"] = 1] = "SESSION_COMMAND_TYPE"; SetupDataStreamSessionTypes[SetupDataStreamSessionTypes["TRANSPORT_TYPE"] = 2] = "TRANSPORT_TYPE"; SetupDataStreamSessionTypes[SetupDataStreamSessionTypes["CONTROLLER_KEY_SALT"] = 3] = "CONTROLLER_KEY_SALT"; })(SetupDataStreamSessionTypes || (SetupDataStreamSessionTypes = {})); var SetupDataStreamWriteResponseTypes; (function (SetupDataStreamWriteResponseTypes) { SetupDataStreamWriteResponseTypes[SetupDataStreamWriteResponseTypes["STATUS"] = 1] = "STATUS"; SetupDataStreamWriteResponseTypes[SetupDataStreamWriteResponseTypes["TRANSPORT_TYPE_SESSION_PARAMETERS"] = 2] = "TRANSPORT_TYPE_SESSION_PARAMETERS"; SetupDataStreamWriteResponseTypes[SetupDataStreamWriteResponseTypes["ACCESSORY_KEY_SALT"] = 3] = "ACCESSORY_KEY_SALT"; })(SetupDataStreamWriteResponseTypes || (SetupDataStreamWriteResponseTypes = {})); var TransportSessionConfiguration; (function (TransportSessionConfiguration) { TransportSessionConfiguration[TransportSessionConfiguration["TCP_LISTENING_PORT"] = 1] = "TCP_LISTENING_PORT"; })(TransportSessionConfiguration || (TransportSessionConfiguration = {})); var TransportType; (function (TransportType) { TransportType[TransportType["HOMEKIT_DATA_STREAM"] = 0] = "HOMEKIT_DATA_STREAM"; })(TransportType || (TransportType = {})); var SessionCommandType; (function (SessionCommandType) { SessionCommandType[SessionCommandType["START_SESSION"] = 0] = "START_SESSION"; })(SessionCommandType || (SessionCommandType = {})); /** * @group HomeKit Data Streams (HDS) */ var DataStreamStatus; (function (DataStreamStatus) { DataStreamStatus[DataStreamStatus["SUCCESS"] = 0] = "SUCCESS"; DataStreamStatus[DataStreamStatus["GENERIC_ERROR"] = 1] = "GENERIC_ERROR"; DataStreamStatus[DataStreamStatus["BUSY"] = 2] = "BUSY"; })(DataStreamStatus || (exports.DataStreamStatus = DataStreamStatus = {})); /** * @group HomeKit Data Streams (HDS) */ class DataStreamManagement { // one server per accessory is probably the best practice dataStreamServer = new DataStreamServer_1.DataStreamServer(); // TODO how to handle Remote+future HKSV controller at the same time? dataStreamTransportManagementService; supportedDataStreamTransportConfiguration; lastSetupDataStreamTransportResponse = ""; // stripped. excludes ACCESSORY_KEY_SALT constructor(service) { const supportedConfiguration = [TransportType.HOMEKIT_DATA_STREAM]; this.supportedDataStreamTransportConfiguration = this.buildSupportedDataStreamTransportConfigurationTLV(supportedConfiguration); this.dataStreamTransportManagementService = service || this.constructService(); this.setupServiceHandlers(); } destroy() { this.dataStreamServer.destroy(); // removes ALL listeners this.dataStreamTransportManagementService.getCharacteristic(Characteristic_1.Characteristic.SetupDataStreamTransport) .removeOnGet() .removeAllListeners("set" /* CharacteristicEventTypes.SET */); this.lastSetupDataStreamTransportResponse = ""; } /** * @returns the DataStreamTransportManagement service */ getService() { return this.dataStreamTransportManagementService; } /** * Registers a new event handler to handle incoming event messages. * The handler is only called for a connection if for the give protocol no ProtocolHandler * was registered on the connection level. * * @param protocol - name of the protocol to register the handler for * @param event - name of the event (also referred to as topic. See {@link Topics} for some known ones) * @param handler - function to be called for every occurring event */ onEventMessage(protocol, event, handler) { this.dataStreamServer.onEventMessage(protocol, event, handler); return this; } /** * Removes a registered event handler. * * @param protocol - name of the protocol to unregister the handler for * @param event - name of the event (also referred to as topic. See {@link Topics} for some known ones) * @param handler - registered event handler */ removeEventHandler(protocol, event, handler) { this.dataStreamServer.removeEventHandler(protocol, event, handler); return this; } /** * Registers a new request handler to handle incoming request messages. * The handler is only called for a connection if for the give protocol no ProtocolHandler * was registered on the connection level. * * @param protocol - name of the protocol to register the handler for * @param request - name of the request (also referred to as topic. See {@link Topics} for some known ones) * @param handler - function to be called for every occurring request */ onRequestMessage(protocol, request, handler) { this.dataStreamServer.onRequestMessage(protocol, request, handler); return this; } /** * Removes a registered request handler. * * @param protocol - name of the protocol to unregister the handler for * @param request - name of the request (also referred to as topic. See {@link Topics} for some known ones) * @param handler - registered request handler */ removeRequestHandler(protocol, request, handler) { this.dataStreamServer.removeRequestHandler(protocol, request, handler); return this; } /** * Forwards any event listener for an DataStreamServer event to the DataStreamServer instance * * @param event - the event to register for * @param listener - the event handler */ onServerEvent(event, listener) { // @ts-expect-error: event type this.dataStreamServer.on(event, listener); return this; } // eslint-disable-next-line @typescript-eslint/no-explicit-any handleSetupDataStreamTransportWrite(value, callback, connection) { const data = Buffer.from(value, "base64"); const objects = tlv.decode(data); const sessionCommandType = objects[1 /* SetupDataStreamSessionTypes.SESSION_COMMAND_TYPE */][0]; const transportType = objects[2 /* SetupDataStreamSessionTypes.TRANSPORT_TYPE */][0]; const controllerKeySalt = objects[3 /* SetupDataStreamSessionTypes.CONTROLLER_KEY_SALT */]; debug("Received setup write with command %s and transport type %s", SessionCommandType[sessionCommandType], TransportType[transportType]); if (sessionCommandType === SessionCommandType.START_SESSION) { if (transportType !== TransportType.HOMEKIT_DATA_STREAM || controllerKeySalt.length !== 32) { callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */); return; } this.dataStreamServer.prepareSession(connection, controllerKeySalt, (error, preparedSession) => { if (error || !preparedSession) { callback(error ?? new Error("PreparedSession was undefined!")); return; } const listeningPort = tlv.encode(1 /* TransportSessionConfiguration.TCP_LISTENING_PORT */, tlv.writeUInt16(preparedSession.port)); let response = Buffer.concat([ tlv.encode(1 /* SetupDataStreamWriteResponseTypes.STATUS */, 0 /* DataStreamStatus.SUCCESS */), tlv.encode(2 /* SetupDataStreamWriteResponseTypes.TRANSPORT_TYPE_SESSION_PARAMETERS */, listeningPort), ]); this.lastSetupDataStreamTransportResponse = response.toString("base64"); // save last response without accessory key salt response = Buffer.concat([ response, tlv.encode(3 /* SetupDataStreamWriteResponseTypes.ACCESSORY_KEY_SALT */, preparedSession.accessoryKeySalt), ]); callback(null, response.toString("base64")); }); } else { callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */); return; } } buildSupportedDataStreamTransportConfigurationTLV(supportedConfiguration) { const buffers = []; supportedConfiguration.forEach(type => { const transportType = tlv.encode(1 /* TransportTypeTypes.TRANSPORT_TYPE */, type); const transferTransportConfiguration = tlv.encode(1 /* TransferTransportConfigurationTypes.TRANSFER_TRANSPORT_CONFIGURATION */, transportType); buffers.push(transferTransportConfiguration); }); return Buffer.concat(buffers).toString("base64"); } constructService() { const dataStreamTransportManagement = new Service_1.Service.DataStreamTransportManagement("", ""); dataStreamTransportManagement.setCharacteristic(Characteristic_1.Characteristic.SupportedDataStreamTransportConfiguration, this.supportedDataStreamTransportConfiguration); dataStreamTransportManagement.setCharacteristic(Characteristic_1.Characteristic.Version, DataStreamServer_1.DataStreamServer.version); return dataStreamTransportManagement; } setupServiceHandlers() { this.dataStreamTransportManagementService.getCharacteristic(Characteristic_1.Characteristic.SetupDataStreamTransport) .onGet(() => this.lastSetupDataStreamTransportResponse) .on("set" /* CharacteristicEventTypes.SET */, (value, callback, context, connection) => { if (!connection) { debug("Set event handler for SetupDataStreamTransport cannot be called from plugin! Connection undefined!"); callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */); return; } this.handleSetupDataStreamTransportWrite(value, callback, connection); }) .updateValue(this.lastSetupDataStreamTransportResponse); } } exports.DataStreamManagement = DataStreamManagement; //# sourceMappingURL=DataStreamManagement.js.map