domotz-remote-pawn
Version:
Domotz Agent
264 lines (218 loc) • 10.3 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz UK LLP
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
**/
var protocolDiscoveryOrchestratorFactory = function (domotzApp, discoverySenderFactory) {
"use strict";
/***
* console overrides
*
**/
var discoveryConsole = domotzApp.log.decorateLogs('DISCOVERY - ');
var sortedHashTable = domotzApp.utils.dataStructure.sortedHashTable;
var localDeviceMaps = {
newDeviceIdByIp: {},
ipToId: {},
interfaceNumberByIp: {}
};
var discoverySender = discoverySenderFactory(domotzApp, localDeviceMaps);
var registeredProtocols = {
bonjour: domotzApp.discovery.bonjour_discovery,
upnp: domotzApp.discovery.upnp_device_discovery,
snmp: domotzApp.discovery.snmp_basic_discovery
};
// Driver discovery results indexed by protocol and device ID
var response = {
bonjour: {},
upnp: {},
snmp: {}
};
var responseAccumulatingCache = {
bonjour: {}
};
var lastDriverSnapshot = {
bonjour: {},
upnp: {},
snmp: {}
};
var postDiscoveryHandler = {
snmp: discoverySender.sendSnmpInterfaceNumber
};
//
// ************* MAIN LOOP (discover, map, post result)
function discover(protocolName) {
var protocolDriver = getProtocolDriver(protocolName);
if (!protocolDriver) {
discoveryConsole.error("Driver not found for protocol '%s'", protocolName);
return;
}
discoveryConsole.info("(%s): Discovery Started", protocolDriver.getProtocolNameUpperCase());
var devicesMaps = domotzApp.discovery.utils.getDevicesMaps();
localDeviceMaps.ipToId = devicesMaps.ipToId;
localDeviceMaps.interfaceNumberByIp = domotzApp.discovery.utils.getDetailsFieldsForDevices(['interface_number_snmp']);
discoveryConsole.verbose("(%s): Got device maps, calling driver ...", protocolDriver.getProtocolNameUpperCase());
doProtocolDiscovery(protocolDriver);
}
function getProtocolDriver(protocolName) {
return registeredProtocols[protocolName];
}
function runPostDiscoveryHandler(protocolDriver) {
discoveryConsole.debug("post discovery management");
var selectedPostDiscoveryHandler = postDiscoveryHandler[protocolDriver.getProtocolName()];
if (selectedPostDiscoveryHandler) {
discoveryConsole.debug("post discovery handler call");
protocolDriver.applyFunctionToSnapshotEntries(selectedPostDiscoveryHandler);
}
}
function doProtocolDiscovery(protocolDriver) {
var pretty_protocol_name = protocolDriver.getProtocolNameUpperCase();
if (!protocolDriver.isDiscoveryInProgress()) {
response[protocolDriver.getProtocolName()] = {};
discoveryConsole.debug("(%s): Starting actual discovery ...", pretty_protocol_name);
protocolDriver.discover(function (result) {
discoveryConsole.debug("(%s) Terminated, analysing results ...", pretty_protocol_name);
discoveryConsole.verbose("(%s): Discovery Result is %s", pretty_protocol_name,
JSON.stringify(result));
saveProtocolDiscoveryResult(result, protocolDriver);
parseProtocolDiscoveryResult(result, protocolDriver);
sendResultToEngine(protocolDriver);
runPostDiscoveryHandler(protocolDriver);
});
} else {
discoveryConsole.debug("(%s): discovery already in progress ...", pretty_protocol_name);
}
}
function getProtocolDataForNewDeviceId(protocolDriver, deviceIpAddress, deviceId) {
var protocolName = protocolDriver.getProtocolName();
var last_discovery_result = lastDriverSnapshot[protocolName];
if (protocolName === 'bonjour') {
response[protocolName] = {};
addBonjourResultToDevice(last_discovery_result, protocolDriver);
if (response[protocolName][deviceId]) {
discoveryConsole.debug("(%s): for new device (id %s, ip %s) found protocol driver cache, sending ...",
protocolDriver.getProtocolNameUpperCase(), deviceId, deviceIpAddress);
sendResultToEngine(protocolDriver);
}
else {
discoveryConsole.debug("(%s): cannot find protocol driver cache for new device (id %s, ip %s), rescheduling discovery ...",
protocolDriver.getProtocolNameUpperCase(), deviceId, deviceIpAddress);
setTimeout(function () {
doProtocolDiscovery(protocolDriver);
}, 300);
}
} else { // upnp, etc.
var entry = protocolDriver.searchEntryIntoSnapshot(deviceIpAddress);
if (entry) {
response[protocolName] = {};
parseProtocolDiscoveryResult(last_discovery_result, protocolDriver);
sendResultToEngine(protocolDriver);
}
else {
setTimeout(function () {
doProtocolDiscovery(protocolDriver);
}, 300);
}
}
}
//
// **************** Protocol Result Parsing and Device Id Matching
function saveProtocolDiscoveryResult(result, protocolDriver) {
lastDriverSnapshot[protocolDriver.getProtocolName()] = result;
}
function parseProtocolDiscoveryResult(driver_snapshot, protocolDriver) {
switch (protocolDriver.getProtocolName()) {
case 'bonjour':
addBonjourResultToDevice(driver_snapshot, protocolDriver);
break;
case 'upnp':
case 'snmp':
protocolDriver.applyFunctionToSnapshotEntries(addResultToDeviceById);
break;
}
}
function addBonjourResultToDevice(driver_snapshot, protocolDriver) {
for (var i = 0; i < driver_snapshot.length; i++) {
for (var j = 0; j < driver_snapshot[i].ip.length; j++) {
var matchedId = localDeviceMaps.ipToId[driver_snapshot[i].ip[j]];
if (!matchedId) {
matchedId = localDeviceMaps.newDeviceIdByIp[driver_snapshot[i].ip[j]];
}
if (matchedId) {
if (!response[protocolDriver.getProtocolName()][matchedId]) {
response[protocolDriver.getProtocolName()][matchedId] = {};
}
createBonjourResponsePayload(matchedId, driver_snapshot[i], protocolDriver);
}
}
}
}
function createBonjourResponsePayload(matchedId, driver_snapshot_piece, protocolDriver) {
response[protocolDriver.getProtocolName()][matchedId][driver_snapshot_piece.service] = {
name: driver_snapshot_piece.name,
txt: driver_snapshot_piece.txt,
fqdn: driver_snapshot_piece.fqdn,
host: driver_snapshot_piece.host
};
if (responseAccumulatingCache[protocolDriver.getProtocolName()][matchedId] === undefined) {
responseAccumulatingCache[protocolDriver.getProtocolName()][matchedId] = {};
}
responseAccumulatingCache[protocolDriver.getProtocolName()][matchedId][driver_snapshot_piece.service] =
JSON.parse(JSON.stringify(response[protocolDriver.getProtocolName()][matchedId][driver_snapshot_piece.service]));
}
function addResultToDeviceById(ip, result, protocolName) {
var matchedId = localDeviceMaps.ipToId[ip];
if (!matchedId) {
matchedId = localDeviceMaps.newDeviceIdByIp[ip];
}
if (matchedId) {
discoveryConsole.verbose("(%s): identified device id %s of result %s", protocolName.toUpperCase(),
matchedId, JSON.stringify(result));
response[protocolName][matchedId] = result;
}
}
function sendResultToEngine(protocolDriver) {
var protocolName = protocolDriver.getProtocolName();
var selectedResponseStorage = (protocolName === 'bonjour') ? responseAccumulatingCache : response;
for (var deviceId in selectedResponseStorage[protocolName]) {
if (selectedResponseStorage[protocolName].hasOwnProperty(deviceId)) {
discoverySender.sendAndCacheDiscoveryResult(deviceId,
sortedHashTable(selectedResponseStorage[protocolName][deviceId]),
protocolDriver);
}
}
}
//
// *************** New Device Notification Handling
function addDevice(deviceId, macAddress, deviceIpAddresses) {
var deviceIpAddress = deviceIpAddresses[0];
localDeviceMaps.newDeviceIdByIp[deviceIpAddress] = deviceId;
discoveryConsole.info("-- NEW DEVICE INFO ADDED -- id %s added with mac %s and ip %s", deviceId,
macAddress, deviceIpAddress);
for (var proto in registeredProtocols) {
if (registeredProtocols.hasOwnProperty(proto)) {
var myDriver = getProtocolDriver(proto);
if (!myDriver.isDiscoveryInProgress()) {
discoveryConsole.verbose(" Protocol cache for %s is %s", proto, JSON.stringify(lastDriverSnapshot[proto]));
getProtocolDataForNewDeviceId(myDriver, deviceIpAddress, deviceId);
}
}
}
}
return {
discover: discover,
addDevice: addDevice
};
};
module.exports.factory = protocolDiscoveryOrchestratorFactory;