UNPKG

node-red-contrib-homekit-abc

Version:

HAP-Nodejs based Node-RED nodes to create HomeKit Accessories

340 lines (284 loc) 12.8 kB
const mqtt = require("mqtt"); const path = require('path'); const net = require('net'); const hap = require('hap-nodejs'); const storage = require('node-persist'); const {uuid, Accessory, Service, Characteristic} = hap; const pkg = require('../package.json'); var ping = require("ping"); const accessories = {}; module.exports = function (RED) { //hap.init(path.join(RED.settings.userDir, 'homekit')); RED.httpAdmin.get('/hassbian-abc-homekit-irmqtttv', (req, res) => { if (req.query.config) { const acc = accessories[req.query.config]; if (acc) { res.status(200).send(JSON.stringify({setupURI: acc.setupURI()})); } else { res.status(500).send(JSON.stringify({})); } } else { res.status(404).send(JSON.stringify({})); } }); class hassbianHomeKitirtv { constructor(config) { RED.nodes.createNode(this, config); const that = this; var mqttHost = config.mqtthost; var mqttUsername = config.mqttname; var mqttPassword = config.mqttpassword; this.host = config.pinghost; var mqttOptions = { clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8), username: mqttUsername, password: mqttPassword }; this.mqttTopic = { "abc": config.mqttSend, "bca": config.mqttReceived, }; try { this.mqttClient = mqtt.connect(mqttHost, mqttOptions); this.mqttClient.subscribe(this.mqttTopic['bca']); this.mqttClient.subscribe(this.mqttTopic['abc']); } catch(e) { }; this.power = 0; this.mode = 1; this.screenName = config.screenName; this.HdmiName1 = config.HdmiName1; this.HdmiName2 = config.HdmiName2; this.HdmiName3 = config.HdmiName3; this.HdmiName4 = config.HdmiName4; this.on_off = config.on_off; this.powerModeSelection = config.powerModeSelection; const inputMap = { 1: config.home_screen, 2: config.hdmi1, 3: config.hdmi2, 4: config.hdmi3, 5: config.hdmi4 }; const tvRemoteMap = { 4: config.arrow_up, 7: config.arrow_right, 5: config.arrow_down, 6: config.arrow_left, 8: config.select, 11: config.play_pause, 9: config.back, 15: config.information }; const volumeMap = { 0: config.increment, 1: config.decrement }; function logger(...args) { let str = args.join(' '); if (str.match(/^(error: )/i)) { str = str.replace(/^error: (.*)/i, '$1'); that.error(str); } else if (config.debug) { that.debug(str); } } if (accessories[this.id]) { this.debug('irmqtttv already configured ' + this.name + ' ' + this.id + ' ' + this.username); return; } const [firstLogger] = Object.keys(RED.settings.logging); config.debug = config.debug && (RED.settings.logging[firstLogger].level === 'debug' || RED.settings.logging[firstLogger].level === 'trace'); this.name = config.name || ('irmqtttv ' + this.id); storage.initSync(); const acc = new Accessory(this.name, uuid.generate(config.id), Accessory.Categories.TELEVISION); accessories[this.id] = acc; this.debug('irmqtttv created' + this.name + ' ' + this.id + ' ' + config.username); acc.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, 'hassbian-abc') .setCharacteristic(Characteristic.Model, 'irmqtttv') .setCharacteristic(Characteristic.SerialNumber, config.username) .setCharacteristic(Characteristic.FirmwareRevision, pkg.version); acc.on('identify', (paired, callback) => { this.log('identify ' + this.id + ' ' + this.username + ' ' + paired); callback(); }); this.tvService = acc.addService(Service.Television, this.name, "Television"); this.tvService.setCharacteristic(Characteristic.ConfiguredName, this.name); this.tvService.setCharacteristic( hap.Characteristic.SleepDiscoveryMode, hap.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE ); var power = this.tvService.getCharacteristic(Characteristic.Active); power .on('set', (value, callback) => { var valueStr = this.on_off.toString(); if (power.value == value) { callback(null); } else { this.mqttClient.publish(this.mqttTopic['abc'], valueStr); callback(null); } }) .on('get', (callback) => { ping.promise.probe(this.host) .then((res, err) => { this.power = res.alive ? true : false; //console.log("power", this.power); callback(null, this.power); }); }); setInterval(function() { power.getValue(); }, 10000); this.tvService.setCharacteristic(Characteristic.ActiveIdentifier, 1); var activeIdentifier = this.tvService.getCharacteristic(Characteristic.ActiveIdentifier); activeIdentifier .on('set', (value, callback) => { var valueStr = inputMap[value].toString(); this.mqttClient.publish(this.mqttTopic['abc'], valueStr); callback(null); }); this.mqttClient.on('message', (topic, message) => { if(topic == that.mqttTopic['bca']) { var stat = message.toString(); //console.log("stat", stat); if (stat === config.home_screen) { this.mode = 1; activeIdentifier.updateValue(1); } else if (stat === config.HdmiName1) { if (this.HdmiName1) { this.mode = 2; activeIdentifier.updateValue(2); } } else if (stat === config.HdmiName2) { if (this.HdmiName2) { this.mode = 3; activeIdentifier.updateValue(3); } } else if (stat === config.HdmiName3) { if (this.HdmiName3) { this.mode = 4; activeIdentifier.updateValue(4); } } else if (stat === config.HdmiName4) { if (this.HdmiName4) { this.mode = 5; activeIdentifier.updateValue(5); } } } }); var powerModeSelection = this.tvService.getCharacteristic(Characteristic.PowerModeSelection); powerModeSelection .on('set', (value, callback) => { var valueStr = this.powerModeSelection.toString(); this.mqttClient.publish(this.mqttTopic['abc'], valueStr); callback(null); }); var remoteKey = this.tvService.getCharacteristic(Characteristic.RemoteKey); remoteKey .on('set', (value, callback) => { var valueStr = tvRemoteMap[value].toString(); this.mqttClient.publish(this.mqttTopic['abc'], valueStr); callback(null); }); this.inputHDMI1Service = createInputSource(this.screenName, this.screenName, 1); if (config.Hdmi1) { this.inputHDMI2Service = createInputSource(this.HdmiName1, this.HdmiName1, 2); }; if (config.Hdmi2) { this.inputHDMI3Service = createInputSource(this.HdmiName2, this.HdmiName2, 3); }; if (config.Hdmi3) { this.inputHDMI4Service = createInputSource(this.HdmiName3, this.HdmiName3, 4); }; if (config.Hdmi4) { this.inputNetflixService = createInputSource( this.HdmiName4, this.HdmiName4, 5, Characteristic.InputSourceType.APPLICATION ); }; this.tvService.addLinkedService(this.inputHDMI1Service); if (config.Hdmi1) { this.tvService.addLinkedService(this.inputHDMI2Service); }; if (config.Hdmi2) { this.tvService.addLinkedService(this.inputHDMI3Service); }; if (config.Hdmi3) { this.tvService.addLinkedService(this.inputHDMI4Service); }; if (config.Hdmi4) { this.tvService.addLinkedService(this.inputNetflixService); }; this.speakerService = acc.addService(Service.TelevisionSpeaker); this.speakerService .setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE) .setCharacteristic( Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE ); var speaker = this.speakerService.getCharacteristic(Characteristic.VolumeSelector); speaker .on('set', (value, callback) => { var valueStr = volumeMap[value].toString(); this.mqttClient.publish(this.mqttTopic['abc'], valueStr); callback(null); }); this.tvService.addLinkedService(this.speakerService); function createInputSource( id, name, number, type = Characteristic.InputSourceType.HDMI ) { var input = acc.addService(Service.InputSource, id, name); input .setCharacteristic(Characteristic.Identifier, number) .setCharacteristic(Characteristic.ConfiguredName, name) .setCharacteristic( Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED ) .setCharacteristic(Characteristic.InputSourceType, type); return input; } this.log('publishing irmqtttv ' + this.name + ' ' + config.username); const testPort = net.createServer() .once('error', err => { this.error(err); this.status({fill: 'red', shape: 'dot', text: err.message}); }) .once('listening', () => { testPort.once('close', () => { acc.publish({ username: config.username, port: config.port, pincode: config.pincode, category: Accessory.Categories.TELEVISION }); acc._server.on('listening', () => { this.log('irmqtttv ' + this.name + ' listening on port ' + config.port); this.status({fill: 'green', shape: 'ring', text: ' '}); }); acc._server.on('pair', username => { this.log('irmqtttv ' + this.name + ' paired', username); }); acc._server.on('unpair', username => { this.log('irmqtttv ' + this.name + ' unpaired', username); }); acc._server.on('verify', () => { this.log('irmqtttv ' + this.name + ' verify'); }); }).close(); }) .listen(config.port); this.on('close', () => { }); } } RED.nodes.registerType('hassbian-abc-homekit-irmqtttv', hassbianHomeKitirtv); };