{"version":3,"sources":["jsdelivr-header.js","/npm/homematic-manager@1.0.14/main.js"],"names":[],"mappings":"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","file":"/npm/homematic-manager@1.0.14/main.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/homematic-manager@1.0.14/main.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/**\n *      homematic-manager\n *\n *  Copyright (c) 2014, 2015 Anli, Hobbyquaker\n *\n *  CC BY-NC-SA 4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/)\n *\n */\n\n\"use strict\";\n\nvar fs =        require('fs');\nvar http =      require('http');\n\nvar async =     require('async');\n\nvar express =   require('express');\nvar socketio =  require('socket.io');\n\nvar xmlrpc =    require('homematic-xmlrpc');\nvar binrpc =    require('binrpc');\n\nvar config =    require('./lib/config.js');\n\nvar app;\nvar server;\nvar io;\nvar rpc;\n\nvar localNames = {};\nvar localDevices = {};\nvar localParamsetDescriptions = {};\nvar rpcClients = {};\n\nvar rpcServer;\nvar rpcServerBin;\nvar rpcServerStarted;\nvar rpcServerBinStarted;\nvar daemonIndex = {};\nvar lastEvent = {};\n\nvar log = {\n    debug: function (msg) {\n        if (config.debug) {\n            console.log(msg);\n        }\n    },\n    info: function (msg) {\n        console.log(msg);\n    },\n    error: function (msg) {\n        console.error(msg)\n    }\n};\n\n\nloadJson(config.devicesFile, function (err, data) {\n    if (!err && data) localDevices = data;\n    loadJson(config.paramsetDescriptionsFile, function (err, data) {\n        if (!err && data) localParamsetDescriptions = data;\n        loadJson(config.namesFile, function (err, data) {\n            if (!err && data) localNames = data;\n            initWebServer();\n            initRpcClients();\n        });\n    });\n});\n\n\nfunction initRpcClients() {\n\n    function init(_daemon) {\n        var protocol = (config.daemons[_daemon].protocol === 'binrpc' ? 'xmlrpc_bin://' : 'http://');\n        var port = (config.daemons[_daemon].protocol === 'binrpc' ? config.rpcListenPortBin : config.rpcListenPort);\n        var initUrl = protocol + config.rpcListenIp + ':' + port;\n        var ident = config.daemons[_daemon].ident;\n\n        log.debug('RPC -> ' + config.daemons[_daemon].ip + ':' + config.daemons[_daemon].port + ' init ' + initUrl + ' ' + ident);\n        rpcClients[_daemon].methodCall('init', [initUrl, ident], function (err, data) {\n            log.debug('    <- init response ' + JSON.stringify(err) + ' ' + JSON.stringify(data));\n        });\n    }\n\n    function pingPong() {\n        var now = (new Date()).getTime();\n        for (var daemon in config.daemons) {\n            var elapsed = now - lastEvent[daemon];\n            if (elapsed > 45000) {\n                log.debug('RPC -> re-init ' + daemon + ' ' + elapsed);\n                init(daemon);\n            } else if (elapsed > 29000) {\n                log.debug('RPC -> ping ' + daemon + ' ' + elapsed);\n                rpcClients[daemon].methodCall('ping', ['hmm'], function (err, res) {\n                    if (err) {\n                        log.error('RPC -> ping', err)\n                    }\n                });\n            }\n        }\n    }\n\n    var count = 0; //Math.floor(Math.random() * 65536);\n    for (var daemon in config.daemons) {\n        config.daemons[daemon].ident = 'hmm_' + count;\n\n\n        daemonIndex[config.daemons[daemon].ident] = daemon;\n\n        rpcClients[daemon] = (config.daemons[daemon].protocol === 'binrpc' ? binrpc : xmlrpc).createClient({\n            host: config.daemons[daemon].ip,\n            port: config.daemons[daemon].port,\n            path: '/'\n        });\n\n        initRpcServer(config.daemons[daemon].protocol);\n\n        (function (_daemon) {\n            if (config.daemons[_daemon].protocol === 'binrpc') {\n                rpcClients[_daemon].on('connect', function () {\n                    init(_daemon);\n                });\n                rpcClients[_daemon].on('error', function (e) {\n                    log.error(e);\n                });\n\n            } else {\n                init(_daemon);\n            }\n        })(daemon);\n\n        count += 1;\n    }\n    setInterval(pingPong, 15000);\n}\n\nfunction initRpcServer(protocol) {\n    var server;\n    if (protocol === 'binrpc' && !rpcServerBinStarted) {\n        rpcServerBinStarted =  true;\n        rpcServerBin =      binrpc.createServer({host: config.rpcListenIp, port: config.rpcListenPortBin});\n        server = rpcServerBin;\n        log.debug('binrpc server listening on ' + config.rpcListenIp + ':' + (config.rpcListenPortBin));\n    } else if (!rpcServerStarted) {\n        rpcServerStarted =  true;\n        rpcServer =         xmlrpc.createServer({host: config.rpcListenIp, port: config.rpcListenPort});\n        server = rpcServer;\n        log.debug('xmlrpc server listening on ' + config.rpcListenIp + ':' + (config.rpcListenPort));\n    } else {\n        log.debug(protocol + ' server already started');\n        return;\n    }\n\n    server.on('NotFound', function (method, params) {\n        log.debug('RPC <- undefined method ' + method + ' ' + JSON.stringify(params).slice(0,80));\n        io.sockets.emit('rpc', method, params);\n    });\n\n    server.on('system.multicall', function (method, params, callback) {\n        var response = [];\n        for (var i = 0; i < params[0].length; i++) {\n            if (rpcMethods[params[0][i].methodName]) {\n                response.push(rpcMethods[params[0][i].methodName](null, params[0][i].params));\n            } else {\n                log.debug('RPC <- undefined method ' + method + ' ' + JSON.stringify(params).slice(0,80));\n                response.push('');\n            }\n        }\n        callback(null, response);\n    });\n\n    server.on('event', function (err, params, callback) {\n        lastEvent[daemonIndex[params[0]]] = (new Date()).getTime();\n        callback(null, rpcMethods.event(err, params));\n    });\n    server.on('newDevices', function (err, params, callback) {\n        callback(null, rpcMethods.newDevices(err, params));\n    });\n    server.on('deleteDevices', function(err, params, callback) {\n        callback(null, rpcMethods.deleteDevices(err, params));\n    });\n    server.on('replaceDevice', function(err, params, callback) {\n        callback(null, rpcMethods.replaceDevice(err, params));\n    });\n    server.on('listDevices', function(err, params, callback) {\n        callback(null, rpcMethods.listDevices(err, params));\n    });\n    server.on('system.listMethods', function(err, params, callback) {\n        callback(null, rpcMethods['system.listMethods'](err, params));\n    });\n\n}\n\nvar rpcMethods = {\n    event: function (err, params) {\n        log.debug('RPC <- event ' + JSON.stringify(params));\n        io.sockets.emit('rpc', 'event', params);\n        return '';\n    },\n    newDevices: function (err, params) {\n        log.debug('RPC <- newDevices ' + JSON.stringify(params).slice(0, 80));\n        io.sockets.emit('rpc', 'newDevices', params);\n        var daemon = daemonIndex[params[0]];\n        lastEvent[daemon] = (new Date()).getTime();\n        if (!localDevices[daemon]) localDevices[daemon] = {};\n        for (var i = 0; i < params[1].length; i++) {\n            var dev = params[1][i];\n            localDevices[daemon][dev.ADDRESS] = dev;\n        }\n        saveJson(config.devicesFile, localDevices);\n        return '';\n    },\n    deleteDevices: function (err, params) {\n        log.debug('RPC <- deleteDevices ' + JSON.stringify(params));\n        io.sockets.emit('rpc', 'deleteDevices', params);\n        var daemon = daemonIndex[params[0]];\n        lastEvent[daemon] = (new Date()).getTime();\n        if (!localDevices[daemon] || !params[1]) return;\n\n        for (var i = 0; i < params[1].length; i++) {\n            var address = params[1][i];\n            delete localDevices[daemon][address];\n        }\n        saveJson(config.devicesFile, localDevices);\n        return '';\n    },\n    replaceDevice: function (err, params) {\n        log.debug('RPC <- replaceDevice ' + JSON.stringify(params));\n        io.sockets.emit('rpc', 'replaceDevice', params);\n        var daemon = daemonIndex[params[0]];\n        lastEvent[daemon] = (new Date()).getTime();\n        if (!localDevices[daemon] || !params[1]) return;\n        localNames[params[2]] = localNames[params[1]];\n        delete localNames[params[1]];\n        saveJson(config.namesFile, localNames);\n        delete localDevices[daemon][params[1]];\n        saveJson(config.devicesFile, localDevices);\n        return '';\n    },\n    listDevices: function (err, params) {\n        log.debug('RPC <- listDevices ' + JSON.stringify(params));\n        io.sockets.emit('rpc', 'listDevices', params);\n        var daemon = daemonIndex[params[0]];\n        var res = [];\n        for (var address in localDevices[daemon]) {\n            res.push({ADDRESS: address, VERSION: localDevices[daemon][address].VERSION});\n        }\n        log.debug('RPC -> listDevices response length ' + res.length);\n        return res;\n    },\n    'system.listMethods': function (err, params) {\n        return ['system.multicall', 'system.listMethods', 'listDevices', 'deleteDevices', 'newDevices', 'event'];\n    }\n};\n\nfunction initSocket() {\n\n    io.sockets.on('connection', function (socket) {\n\n        socket.on('getConfig', function (callback) {\n            callback(config);\n        });\n\n        socket.on('getNames', function (callback) {\n            callback(localNames);\n        });\n\n        socket.on('setName', function (address, name, callback) {\n                localNames[address] = name;\n                log.debug('local rename ' + address + ' \"' + name + '\"');\n                if (!address.match(/:/)) {\n                    localNames[address + ':0'] = name + ':0';\n                }\n                saveJson(config.namesFile, localNames, function () {\n                    if (callback) callback();\n                });\n        });\n\n        socket.on('rpc', function (daemon, method, paramArray, callback) {\n            if (!rpcClients[daemon]) {\n                log.debug('RPC unknown daemon ' + daemon);\n                if (callback) {\n                    callback(new Error('unknown daemon'), null);\n                }\n                return;\n            }\n            if (method) rpcProxy(daemon, method, paramArray, callback);\n        });\n\n    });\n}\n\nfunction rpcProxy(daemon, method, params, callback) {\n    switch (method) {\n        case 'listDevices':\n            var res = [];\n            for (var address in localDevices[daemon]) {\n                res.push(localDevices[daemon][address]);\n            }\n            log.debug('RPC -> respond to listDevices from cache (' + res.length + ')');\n            callback(null, res);\n            break;\n\n        case 'getParamsetDescription':\n            var dev = localDevices[daemon][params[0]];\n            console.log('...', params[0], dev);\n            var ident = dev.TYPE + '/' + dev.VERSION + '/' + params[1];\n            if (dev.PARENT_TYPE) ident = dev.PARENT_TYPE + '/' + ident;\n\n            if (localParamsetDescriptions[ident]) {\n                console.log('paramset cache hit ' + ident);\n                callback(null, localParamsetDescriptions[ident]);\n            } else {\n                rpcClients[daemon].methodCall(method, params, function (error, result) {\n                    if (!error && result) localParamsetDescriptions[ident] = result;\n                    saveJson(config.paramsetDescriptionsFile, localParamsetDescriptions);\n                    if (callback) callback(error, result);\n                });\n            }\n            break;\n\n        default:\n            log.debug('RPC -> ' + config.daemons[daemon].ip + ':' + config.daemons[daemon].port + ' ' + method + '(' + JSON.stringify(params).slice(1).slice(0, -1).replace(/,/, ', ') + ')');\n            rpcClients[daemon].methodCall(method, params, function (error, result) {\n                if (callback) callback(error, result);\n            });\n    }\n}\n\nfunction initWebServer() {\n    app = express();\n    app.use('/', express.static(__dirname + '/www'));\n    server = require('http').createServer(app);\n    server.listen(config.webServerPort);\n\n    io = socketio.listen(server);\n\n    // FIXME redirect socket.io log\n    /*\n    io.set('logger', {\n        debug: function(obj) {\n            log.debug('socket.io debug: ' + obj);\n        },\n        info: function(obj) {\n            log.info('socket.io info: ' + obj);\n        },\n        error: function(obj) {\n            log.error('socket.io error: ' + obj);\n        },\n        warn: function(obj) {\n            log.error('socket.io warn: ' + obj);\n        }\n    });\n    */\n    initSocket();\n\n    log.info('webserver/socket.io listening on port ' + config.webServerPort);\n}\n\n\n\n\nfunction saveJson(file, obj, callback) {\n    log.debug('saveJson ' + file);\n    fs.writeFile(file, JSON.stringify(obj, null, '    '), function (err) {\n        if (callback) {\n            callback(err);\n        }\n    });\n}\n\nfunction loadJson(file, callback) {\n    log.debug('loadJson ' + file);\n    fs.readFile(file, function (err, data) {\n        if (err) {\n            callback(err, null);\n        } else {\n            callback(null, JSON.parse(data.toString()));\n        }\n    });\n}\n\n\n\nvar stopping;\n\nfunction stop() {\n    if (stopping) {\n        log.debug('force terminate');\n        process.exit(1);\n        return;\n    }\n\n    var tasks = [];\n    for (var daemon in config.daemons) {\n        var protocol = (config.daemons[daemon].protocol === 'binrpc' ? 'xmlrpc_bin://' : 'http://');\n        var initUrl = protocol + config.rpcListenIp + ':' + (config.daemons[daemon].protocol === 'binrpc' ? config.rpcListenPortBin : config.rpcListenPort);\n\n        (function (_daemon, _initUrl) {\n            tasks.push(function (cb) {\n                log.debug(\"RPC -> \" + config.daemons[daemon].ip + ':' + config.daemons[daemon].port + ' init ' + initUrl + ' \"\"');\n                rpcClients[_daemon].methodCall('init', [_initUrl, ''], function (err, data) {\n                    log.debug(\"    <- \" + JSON.stringify(err) + ' ' + JSON.stringify(data));\n                    cb(null, data);\n                });\n            })\n        })(daemon, initUrl);\n\n    }\n    async.parallel(tasks, function () {\n        log.debug('terminate');\n        process.exit(0);\n    });\n    setTimeout(stop, 2000);\n}\n\nprocess.on('SIGINT', stop);\nprocess.on('SIGTERM', stop);\n"]}