From b884fc2d69a1c3a9ca7c7e624f3f86d26dbfb475 Mon Sep 17 00:00:00 2001 From: Jason Gouger Date: Sat, 28 May 2022 13:16:40 -0700 Subject: [PATCH 1/3] Add manual discovery setting --- config.schema.json | 30 +++++++++++++++- src/platform.ts | 85 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/config.schema.json b/config.schema.json index 72cc7b7..a0a51b8 100755 --- a/config.schema.json +++ b/config.schema.json @@ -41,6 +41,34 @@ "maximum": 30, "placeholder": "(default: 5)" }, + "manualDiscovery": { + "title": "Manual IP (optional)", + "description": "Disables auto discovery mode and manually configures IP address and port of Konnected Alarm Panels on the network.", + "buttonText": "Add IP Address", + "type": "array", + "orderable": true, + "expandable": true, + "expanded": false, + "items": { + "type": "object", + "properties": { + "ipAddress": { + "title": "IP Address (must be static)", + "type": "string", + "format": "ipv4", + "required": true + }, + "port": { + "title": "Port", + "type": "number", + "step": 1, + "minimum": 8000, + "maximum": 24777, + "required": true + } + } + } + }, "entryDelaySettings": { "type": "object", "expandable": true, @@ -606,4 +634,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/platform.ts b/src/platform.ts index 7d29ab2..be7fcaf 100755 --- a/src/platform.ts +++ b/src/platform.ts @@ -85,7 +85,7 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { private listenerAuth: string[] = []; // for storing random auth strings private ssdpDiscovering = false; // for storing state of SSDP discovery process private ssdpDiscoverAttempts = 0; - + private manualDiscovery = this.config.advanced?.manualDiscovery ? this.config.advanced.manualDiscovery : []; constructor(public readonly log: Logger, public readonly config: PlatformConfig, public readonly api: API) { this.log.debug('Finished initializing platform'); @@ -315,27 +315,7 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { * @reference Alarm Panel V1-V2: urn:schemas-konnected-io:device:Security:1 * @reference Alarm Panel Pro: urn:schemas-konnected-io:device:Security:2 */ - discoverPanels() { - const ssdpClient = new client.Client(); - const ssdpUrnPartial = 'urn:schemas-konnected-io:device'; - const ssdpDeviceIDs: string[] = []; // used later for deduping SSDP reflections - const excludedUUIDs: string[] = String(process.env.KONNECTED_EXCLUDES).split(','); // used for ignoring specific panels (mostly for development) - - // set discovery state - this.ssdpDiscovering = true; - - // begin discovery - ssdpClient.search('ssdp:all'); - - // on discovery - ssdpClient.on('response', (headers) => { - // check for only Konnected devices - if (headers.ST!.indexOf(ssdpUrnPartial) !== -1) { - // store reported URL of panel that responded - const ssdpHeaderLocation: string = headers.LOCATION || ''; - // extract UUID of panel from the USN string - const panelUUID: string = headers.USN!.match(/^uuid:(.*)::.*$/i)![1] || ''; - + discoveredPanel(panelUUID,ssdpHeaderLocation,ssdpDeviceIDs,excludedUUIDs) { // dedupe responses, ignore excluded panels in environment variables, and then provision panel(s) if (!ssdpDeviceIDs.includes(panelUUID) && !excludedUUIDs.includes(panelUUID)) { // get panel status object (not using async await) @@ -378,12 +358,67 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { // add the UUID to the deduping array ssdpDeviceIDs.push(panelUUID); } + } + + discoverPanels() { + const ssdpDeviceIDs: string[] = []; // used later for deduping SSDP reflections + const excludedUUIDs: string[] = String(process.env.KONNECTED_EXCLUDES).split(','); // used for ignoring specific panels (mostly for development) + + // set discovery state + this.ssdpDiscovering = true; + + let ssdpClient; + + if (this.manualDiscovery.length) { + // manual discovery probe ip:port for device info + const self = this; + this.log.debug("Manual discovery enabled attempting connect to modules"); + for (let i=0, iend=this.manualDiscovery.length; i response.text()) + .then(data => { + const panelUUID: string = data.match(/uuid:(.*?)<\/UDN>/i)![1] || ''; + if (panelUUID != '') { + this.log.info(`Manual discovery found panel ${deviceLocation} -- ${panelUUID}`); + this.discoveredPanel(panelUUID,deviceLocation,ssdpDeviceIDs,excludedUUIDs); + } else { + this.log.info(`Manual discovery invalid response from ${deviceLocation} -- ${data}`); + } + }) + .catch (function(error) { + self.log.error("Manual discovery failed for " + manualDiscovery.ipAddress + ":" + manualDiscovery.port + ' - ' + error); + }); } - }); + } else { + this.log.debug("Automatic discovery enabled starting ssdpClient"); + + ssdpClient = new client.Client(); + const ssdpUrnPartial = 'urn:schemas-konnected-io:device'; + + // begin discovery + ssdpClient.search('ssdp:all'); + + // on discovery + ssdpClient.on('response', (headers) => { + // check for only Konnected devices + if (headers.ST!.indexOf(ssdpUrnPartial) !== -1) { + // store reported URL of panel that responded + const ssdpHeaderLocation: string = headers.LOCATION || ''; + // extract UUID of panel from the USN string + const panelUUID: string = headers.USN!.match(/^uuid:(.*)::.*$/i)![1] || ''; + this.discoveredPanel(panelUUID,ssdpHeaderLocation,ssdpDeviceIDs,excludedUUIDs); + } + }); + } // stop discovery after a number of seconds seconds, default is 5 setTimeout(() => { - ssdpClient.stop(); + if (ssdpClient !== undefined) { + ssdpClient.stop(); + } this.ssdpDiscovering = false; if (ssdpDeviceIDs.length) { this.log.debug('Discovery complete. Found panels:\n' + JSON.stringify(ssdpDeviceIDs, null, 2)); @@ -1289,4 +1324,4 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { */ } } -} \ No newline at end of file +} From 5e195f74b79f528bf20406738dc9d536a9a8cf54 Mon Sep 17 00:00:00 2001 From: Jason Gouger Date: Mon, 30 May 2022 13:57:31 -0700 Subject: [PATCH 2/3] Manual Discovery - cleanup spacing and other lint issues --- src/platform.ts | 119 ++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/src/platform.ts b/src/platform.ts index be7fcaf..737ecbf 100755 --- a/src/platform.ts +++ b/src/platform.ts @@ -315,49 +315,49 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { * @reference Alarm Panel V1-V2: urn:schemas-konnected-io:device:Security:1 * @reference Alarm Panel Pro: urn:schemas-konnected-io:device:Security:2 */ - discoveredPanel(panelUUID,ssdpHeaderLocation,ssdpDeviceIDs,excludedUUIDs) { - // dedupe responses, ignore excluded panels in environment variables, and then provision panel(s) - if (!ssdpDeviceIDs.includes(panelUUID) && !excludedUUIDs.includes(panelUUID)) { - // get panel status object (not using async await) - fetch(ssdpHeaderLocation.replace('Device.xml', 'status')) - // convert response to JSON - .then((fetchResponse) => fetchResponse.json()) - .then((panelResponseObject) => { - // create listener object to pass back to panel when provisioning it - const listenerObject = { - ip: this.listenerIP, - port: this.listenerPort, - }; + discoveredPanel(panelUUID, ssdpHeaderLocation, ssdpDeviceIDs, excludedUUIDs) { + // dedupe responses, ignore excluded panels in environment variables, and then provision panel(s) + if (!ssdpDeviceIDs.includes(panelUUID) && !excludedUUIDs.includes(panelUUID)) { + // get panel status object (not using async await) + fetch(ssdpHeaderLocation.replace('Device.xml', 'status')) + // convert response to JSON + .then((fetchResponse) => fetchResponse.json()) + .then((panelResponseObject) => { + // create listener object to pass back to panel when provisioning it + const listenerObject = { + ip: this.listenerIP, + port: this.listenerPort, + }; - // use the above information to construct panel in Homebridge config - this.updateHomebridgeConfig(panelUUID, panelResponseObject); + // use the above information to construct panel in Homebridge config + this.updateHomebridgeConfig(panelUUID, panelResponseObject); - // if the settings property does not exist in the response, - // then we have an unprovisioned panel - if (Object.keys(panelResponseObject.settings).length === 0) { + // if the settings property does not exist in the response, + // then we have an unprovisioned panel + if (Object.keys(panelResponseObject.settings).length === 0) { + this.provisionPanel(panelUUID, panelResponseObject, listenerObject); + } else { + if (panelResponseObject.settings.endpoint_type === 'rest') { + const panelBroadcastEndpoint = new URL(panelResponseObject.settings.endpoint); + + // if the IP address or port are not the same, reprovision endpoint component + if ( + panelBroadcastEndpoint.host !== this.listenerIP || + Number(panelBroadcastEndpoint.port) !== this.listenerPort + ) { this.provisionPanel(panelUUID, panelResponseObject, listenerObject); - } else { - if (panelResponseObject.settings.endpoint_type === 'rest') { - const panelBroadcastEndpoint = new URL(panelResponseObject.settings.endpoint); - - // if the IP address or port are not the same, reprovision endpoint component - if ( - panelBroadcastEndpoint.host !== this.listenerIP || - Number(panelBroadcastEndpoint.port) !== this.listenerPort - ) { - this.provisionPanel(panelUUID, panelResponseObject, listenerObject); - } - } else if (panelResponseObject.settings.endpoint_type === 'aws_iot') { - this.log.error( - `ERROR: Cannot provision panel ${panelUUID} with Homebridge. Panel has previously been provisioned with another platform (Konnected Cloud, SmartThings, Home Assistant, Hubitat,. etc). Please factory reset your Konnected Alarm panel and disable any other platform connectors before associating the panel with Homebridge.` - ); - } } - }); + } else if (panelResponseObject.settings.endpoint_type === 'aws_iot') { + this.log.error( + `ERROR: Cannot provision panel ${panelUUID} with Homebridge. Panel has previously been provisioned with another platform (Konnected Cloud, SmartThings, Home Assistant, Hubitat,. etc). Please factory reset your Konnected Alarm panel and disable any other platform connectors before associating the panel with Homebridge.` + ); + } + } + }); - // add the UUID to the deduping array - ssdpDeviceIDs.push(panelUUID); - } + // add the UUID to the deduping array + ssdpDeviceIDs.push(panelUUID); + } } discoverPanels() { @@ -369,33 +369,32 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { let ssdpClient; - if (this.manualDiscovery.length) { - // manual discovery probe ip:port for device info - const self = this; - this.log.debug("Manual discovery enabled attempting connect to modules"); - for (let i=0, iend=this.manualDiscovery.length; i response.text()) .then(data => { - const panelUUID: string = data.match(/uuid:(.*?)<\/UDN>/i)![1] || ''; - if (panelUUID != '') { - this.log.info(`Manual discovery found panel ${deviceLocation} -- ${panelUUID}`); - this.discoveredPanel(panelUUID,deviceLocation,ssdpDeviceIDs,excludedUUIDs); - } else { - this.log.info(`Manual discovery invalid response from ${deviceLocation} -- ${data}`); - } - }) - .catch (function(error) { - self.log.error("Manual discovery failed for " + manualDiscovery.ipAddress + ":" + manualDiscovery.port + ' - ' + error); - }); + const panelUUID: string = data.match(/uuid:(.*?)<\/UDN>/i)![1] || ''; + if (panelUUID !== '') { + this.log.info(`Manual discovery found panel ${deviceLocation} -- ${panelUUID}`); + this.discoveredPanel(panelUUID, deviceLocation, ssdpDeviceIDs, excludedUUIDs); + } else { + this.log.info(`Manual discovery invalid response from ${deviceLocation} -- ${data}`); + } + }) + .catch (error => { + this.log.error('Manual discovery failed for ' + manualDiscovery.ipAddress + ':' + manualDiscovery.port + ' - ' + error); + }); } } else { - this.log.debug("Automatic discovery enabled starting ssdpClient"); + this.log.debug('Automatic discovery enabled starting ssdpClient'); - ssdpClient = new client.Client(); + ssdpClient = new client.Client(); const ssdpUrnPartial = 'urn:schemas-konnected-io:device'; // begin discovery @@ -409,16 +408,16 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { const ssdpHeaderLocation: string = headers.LOCATION || ''; // extract UUID of panel from the USN string const panelUUID: string = headers.USN!.match(/^uuid:(.*)::.*$/i)![1] || ''; - this.discoveredPanel(panelUUID,ssdpHeaderLocation,ssdpDeviceIDs,excludedUUIDs); + this.discoveredPanel(panelUUID, ssdpHeaderLocation, ssdpDeviceIDs, excludedUUIDs); } }); } // stop discovery after a number of seconds seconds, default is 5 setTimeout(() => { - if (ssdpClient !== undefined) { + if (ssdpClient !== undefined) { ssdpClient.stop(); - } + } this.ssdpDiscovering = false; if (ssdpDeviceIDs.length) { this.log.debug('Discovery complete. Found panels:\n' + JSON.stringify(ssdpDeviceIDs, null, 2)); From c12a7e701fd7c0e41f724761837681f1e751061c Mon Sep 17 00:00:00 2001 From: Jason Gouger Date: Tue, 31 May 2022 08:33:53 -0700 Subject: [PATCH 3/3] Fix reported lint warnings --- src/platform.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platform.ts b/src/platform.ts index 737ecbf..8cf30ce 100755 --- a/src/platform.ts +++ b/src/platform.ts @@ -355,10 +355,10 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { } }); - // add the UUID to the deduping array - ssdpDeviceIDs.push(panelUUID); - } + // add the UUID to the deduping array + ssdpDeviceIDs.push(panelUUID); } + } discoverPanels() { const ssdpDeviceIDs: string[] = []; // used later for deduping SSDP reflections @@ -371,11 +371,11 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { if (this.manualDiscovery.length) { // manual discovery probe ip:port for device info - this.log.debug("Manual discovery enabled attempting connect to modules"); + this.log.debug('Manual discovery enabled attempting connect to modules'); for (let i=0, iend=this.manualDiscovery.length; i response.text()) .then(data => { @@ -394,7 +394,7 @@ export class KonnectedHomebridgePlatform implements DynamicPlatformPlugin { } else { this.log.debug('Automatic discovery enabled starting ssdpClient'); - ssdpClient = new client.Client(); + ssdpClient = new client.Client(); const ssdpUrnPartial = 'urn:schemas-konnected-io:device'; // begin discovery