两年前就想自己写Homebridge插件,奈何没有nodejis功底,而且这个js代码的可读性真的太差了,当时根本看不懂这些插件写的是什么,好在最近重新看了homebridge-aqara插件,内部重新整理了,花了几天算是看懂了,最后我进行了修改,将每个控件直接解耦,每个控件一个.js文件,非常方便。
而且去年我还尝试在windows上面安装homebridge,发现各种问题,今年重新试了一下,竟然一次成功,比linux上面简单不少,终于可以抛弃linux了,调试各种麻烦。
Homebridge插件内支持的设备类型,见HomeKitTypes.js,可以自己搜索一下。
具体的见我的代码,我直接把整个上传了,内部按照C语言的格式整理好了,也写了注释。
下面是我摘取的一部分,可以看到灯的一些属性。
**
* Characteristic "Color Temperature"
*/
Characteristic.ColorTemperature = function() {
Characteristic.call(this, 'Color Temperature', '000000CE-0000-1000-8000-0026BB765291');
this.setProps({
format: Characteristic.Formats.UINT32,
maxValue: 500,
minValue: 140,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]
});
this.value = this.getDefaultValue();
};
inherits(Characteristic.ColorTemperature, Characteristic);
Characteristic.ColorTemperature.UUID = '000000CE-0000-1000-8000-0026BB765291';
/**
* Service "Motion Sensor"
*/
Service.MotionSensor = function(displayName, subtype) {
Service.call(this, displayName, '00000085-0000-1000-8000-0026BB765291', subtype);
// Required Characteristics
this.addCharacteristic(Characteristic.MotionDetected);
// Optional Characteristics
this.addOptionalCharacteristic(Characteristic.StatusActive);
this.addOptionalCharacteristic(Characteristic.StatusFault);
this.addOptionalCharacteristic(Characteristic.StatusTampered);
this.addOptionalCharacteristic(Characteristic.StatusLowBattery);
this.addOptionalCharacteristic(Characteristic.Name);
};
inherits(Service.MotionSensor, Service);
Service.MotionSensor.UUID = '00000085-0000-1000-8000-0026BB765291';
/**
* Service "Outlet" 电源插座
*/
Service.Outlet = function(displayName, subtype) {
Service.call(this, displayName, '00000047-0000-1000-8000-0026BB765291', subtype);
// Required Characteristics
this.addCharacteristic(Characteristic.On);
this.addCharacteristic(Characteristic.OutletInUse);
// Optional Characteristics
this.addOptionalCharacteristic(Characteristic.Name);
};
inherits(Service.Outlet, Service);
Service.Outlet.UUID = '00000047-0000-1000-8000-0026BB765291';
/**
* Service "Lightbulb" 灯泡
*/
Service.Lightbulb = function(displayName, subtype) {
Service.call(this, displayName, '00000043-0000-1000-8000-0026BB765291', subtype);
// Required Characteristics 所需特性
this.addCharacteristic(Characteristic.On); //开关
// Optional Characteristics 可选特征
this.addOptionalCharacteristic(Characteristic.Brightness); //亮度
this.addOptionalCharacteristic(Characteristic.Hue); //色调
this.addOptionalCharacteristic(Characteristic.Saturation); //饱和度
this.addOptionalCharacteristic(Characteristic.Name); //名称
this.addOptionalCharacteristic(Characteristic.ColorTemperature);//色温 //Manual fix to add temperature
};
inherits(Service.Lightbulb, Service);
Service.Lightbulb.UUID = '00000043-0000-1000-8000-0026BB765291';
windows下的配置文件在 用户文件夹下 .homebridge 中
{
"bridge": {
"name": "Homebridge2",
"username": "12:23:3D:E3:CE:32",
"port": 51826,
"pin": "124-42-153"
},
"platforms": [
{
"platform": "AqaraPlatform",
"sid":["6409802da3ca"],
"password":["1234567812345678"]
}
],
"accessories": [
]
}
比如下面是我写的一个插件,Platform_Switch.js
//电源开关
const inherits = require('util').inherits; //引用继承方法
const BaseParser = require('./BaseParser').BaseParser; //引用基类
const mBaseCommander = require('./BaseCommander').BaseCommander; //引用基类
const crypto = require('crypto'); //引用加密方法
const iv = require('./Config').iv; //加密公钥
//申明Parser
SwitchParser = function(platform)
{
this.init(platform);
this.commanders = {};
}
//从基类继承Parser
inherits(SwitchParser, BaseParser);
//具体方法实现
SwitchParser.prototype.parse = function(report, rinfo)
{
var deviceSid = report['sid'];
var gatewaySid = this.platform.gatewaySids[deviceSid];
var data = JSON.parse(report['data']);
// channel_0 can be three states: on, off, unknown.
// we can't do anything when state is unknown, so just ignore it.
if (data.channel_0 === undefined)
{
this.platform.log.warn("warn %s(sid:%s):channel_0's state is unknown, ignore it.", report['model'], deviceSid);
}
else
{
var on = (data.channel_0 === 'on');
var commander;
//this.platform.log.warn("channel_0:%s bright_0:%s",on,Brightness );
//下发命令到设备
if (deviceSid in this.commanders)
{
commander = this.commanders[deviceSid];
}
else
{
commander = new SwitchCommander(this.platform, deviceSid, report['model']);
this.commanders[deviceSid] = commander;
}
var ThisValue = {};
ThisValue.channel_0 = on; //开关
commander.SetValueCache(ThisValue); //存储状态
//控制开关
this.setSwitch(gatewaySid, deviceSid, 'SWITCH' + deviceSid, on, commander); //初始化命令回调接口或设备命令到homekit,设备主动上传状态
}
}
//与AccessoryFactory对接,与homebridge通信,初始化控件与监听接口(从AccessoryFactory中分离,以后增加一个设备类型只需修改各自独立的文件,而无需修改AccessoryFactory.js)
SwitchParser.prototype.setSwitch = function(gatewaySid, deviceSid, uuidSeed, on, commander)
{
this.platform.log.warn("setSwitch: %s->channel_0:%s",deviceSid, on);
//开关控制
isNaN(on) || this.factory.findServiceAndSetValue(
gatewaySid,
deviceSid,
this.factory.UUIDGen.generate(uuidSeed),
this.factory.Accessory.Categories.Switch,
this.factory. Service.Switch,
this.factory.Characteristic.On,
on,
commander); //开关
}
//==================================下面是与设备通信相关==========================================
// Commander
SwitchCommander = function(platform, deviceSid, deviceModel)
{
this.init(platform, deviceSid, deviceModel);
}
inherits(SwitchCommander, BaseCommander);
//用来执行手机端命令发送到设备
SwitchCommander.prototype.send = function(Characteristic, characteristicType, Value)
{
var platform = this.platform;
var gatewaySid = platform.gatewaySids[this.deviceSid];
var password = platform.passwords[gatewaySid];
// No password for this device, please edit ~/.homebridge/config.json
if (!password)
{
platform.log.error("No password for gateway %s, please edit ~/.homebridge/config.json 出现这个问题请重新发送一次设备列表", gatewaySid);
return;
}
var cipher = crypto.createCipheriv('aes-128-cbc', password, iv); //使用aes-128加密
var gatewayToken = platform.gatewayTokens[gatewaySid];
// platform.log.debug("cipher gateway %s, device %s, password %s", gatewaySid, this.deviceSid, password);
var key = "hello";
if (cipher && gatewayToken)
{
key = cipher.update(gatewayToken, "ascii", "hex");
cipher.final('hex'); // Useless data, don't know why yet.
}
if(characteristicType == Characteristic.On) //开关设置
{
this.SetValue.channel_0 = Value; //记录当前要设置的灯状态
//开始发送数据了
//核心data数据,每个命令对应的不一样
var data =
{
channel_0 : this.SetValue.channel_0?'on':'off', //开关
key : key //密匙
}
//通讯基本的json对象格式
var JsonObj =
{
cmd : 'write', //命令码
model : this.deviceModel, //设备类型
sid : this.deviceSid, //设备id
data :JSON.stringify(data) //data转换为字符串
}
var command = JSON.stringify(JsonObj) + '\r\n';//转换为json
this.sendCommand(command);
}
else
{
platform.log.error("无效的characteristicType:%s",characteristicType);
}
}
//申明外部接口
module.exports = {
SwitchParser
}
进行了解耦,每个设备就一个文件,互补干扰
SwitchParser.prototype.parse = function(report, rinfo)
这个方法中对来自设备的数据进行解析,解析后通过
this.setSwitch(gatewaySid, deviceSid, 'SWITCH' + deviceSid, on, commander); //初始化命令回调接口或设备命令到homekit,设备主动上传状态
进行控制。
SwitchCommander.prototype.send = function(Characteristic, characteristicType, Value)
这个是与设备通讯的接口
新增一个接口后只需要在 AqaraPlatform.js中添加一个引用,然后将接口放入到数组中。
var SwitchParser = require('./Platform_Switch').SwitchParser; //引用Platform
//接口注册
function AqaraPlatform(log, config, api) {
// Initialize
this.log = log;
this.factory = new AqaraAccessoryFactory(log, config, api);
this.parsers = {
'sensor_ht' : new TemperatureAndHumidityParser(this),
'motion' : new MotionParser(this),
'magnet' : new ContactParser(this),
'ctrl_neutral1' : new LightSwitchParser(this),
'ctrl_neutral2' : new DuplexLightSwitchParser(this),
'ctrl_ln1' : new LightSwitchParser(this),
'ctrl_ln2' : new DuplexLightSwitchParser(this),
'86sw1' : new EightySixSwitchParser(this),
'86sw2' : new DuplexEightySixSwitchParser(this),
'plug' : new PlugSwitchParser(this),
'Bright_Light' : new BrightnessLightParser (this), //可调亮度的灯
'Lightbulb' : new LightbulbParser(this), //灯泡
'Colored_Light' : new ColoredLightParser(this), //彩灯
'Outlet' : new OutletParser(this), //电源插座
'Switch' : new SwitchParser(this), //电源开关
'DoubleSwitch' : new DoubleSwitchParser(this), //双通道电源开关
};
通讯可以看我之前写的博文,通讯json还是一样的
先发送
{
"cmd": "get_id_list_ack",
"sid": "6409802da3ca",
"token": "1234567812345678",
"data": "[\"4000\"]"
}
再发送
{
"cmd": "return_read",
"model": "Switch",
"sid": "4000",
"data": "{\"channel_0\": \"on\"}"
}
[第一步]homekit智能家居,homebridge与homebridge-aqara通信协议:https://blog.csdn.net/cp1300/article/details/52970883