node-red-contrib-homekit-abc
Version:
HAP-Nodejs based Node-RED nodes to create HomeKit Accessories
340 lines (284 loc) • 12.8 kB
JavaScript
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);
};