前言

Homebridge is a lightweight NodeJS server that emulates the iOS HomeKit API; 之前在linux上部署过homebridge,最近玩路由在路由器上部署了homebridge,具体怎么部署就不说了,还是来重温一下homebridge插件如何编写,搭建一个Siri物联网吧,小白不会nodejs有点忧桑!

Plugins can publish Accessories and/or Platforms. Accessories are individual devices, like a smart switch or a garage door. Platforms act like a single device but can expose a set of devices, like a house full of smart lightbulbs. 即插件会发布 Accessories 和 Platforms,Accessories是一个独立的设备,而Platforms是这些设备所连接的同一个平台/同一组设备

如何编写插件

上图可知,homebridge的作用就是连接Homekit协议和Device协议,起一个桥的作用。插件包含两个文件pakage.json和index.js,package.json是管理依赖的,index.js是写插件核心逻辑的。

先来看一下HomeKit协议的layout布局:

  • Home:整栋房子的Accessory设备
  • Room:一间屋子的Accessory设备
  • Platform:一组Accessory设备
  • Accessory:独立的Accessory设备
  • Bridge:特殊的Accessory设备,允许与不能直接与HomeKit通信的Accessory设备通信
  • Service:Service对应Accessory设备的功能,如车库门有开门和关门服务
  • Characteristic:每个服务有一系列的Characteristic特性,每个特性有3个权限,read、write和notify,这些Characteristic特性可以在 here 里找到

再来看一下homebridge插件的文件结构

--> mySwitch--> config--> config.json #插件配置文件--> plugin--> index.js #核心逻辑,需要自己编写-->package.json #插件包管理

config.json模板如下:

{"bridge": {"name": "Homebridge","username": "94:A1:A2:BE:0B:30","port": 59376,"pin": "033-73-874"},"description": "This is an example configuration file with one fake accessory and one fake platform. You can use this as a template for creating your own configuration file containing devices you actually own.","accessories": [{"accessory": "MyDormSwitch",//index.js会调用"name": "灯"}],"platforms": []
}

package.json模板如下:

{"name": "homebridge-myswitch",//必须以homebridge-开头,index.js会调用"version": "1.0.0","description": "this is a switch plugin","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": ["homebridge-plugin"],"author": "Bayer","license": "ISC","engines": {"node": ">=0.12.0","homebridge": ">=0.2.0"}
}
//以上字段必须都要有

第一步:API需求分析

以一个开关插件为例,假设它需要的API有:

  • GET请求能够返回一个布尔值表示当前开关的状态(通过读取switch的On characteristic )
  • POST请求包含一个布尔值表示开关的目标状态(通过设置switch的On characteristic)

第二步:注册Accessory设备

将mySwitch插件注入到homebridge,mySwitch是一个javascript对象

  module.exports = function (homebridge) {Service = homebridge.hap.Service;Characteristic = homebridge.hap.Characteristic;/* registerAccessory' three parameters is plugin-name(must begin with homebridge-* add must be defined in package.json), accessory-name(must be defined in config.js),constructor-name(must be a function)*/homebridge.registerAccessory("homebridge-myswitch", "MyDormSwitch", mySwitch);
};

第三步:实例化Service服务

支持的service有(可以从HomeKitTypes.js文件中找到)

HomeKitTypes.js:2650: * Service "Accessory Information"
HomeKitTypes.js:2674: * Service "Air Purifier" #空气净化器
HomeKitTypes.js:2697: * Service "Air Quality Sensor" #空气质量传感器
HomeKitTypes.js:2727: * Service "Battery Service"
HomeKitTypes.js:2747: * Service "Camera RTP Stream Management"
HomeKitTypes.js:2770: * Service "Carbon Dioxide Sensor"
HomeKitTypes.js:2794: * Service "Carbon Monoxide Sensor"
HomeKitTypes.js:2818: * Service "Contact Sensor" #接触传感器
HomeKitTypes.js:2840: * Service "Door"
HomeKitTypes.js:2862: * Service "Doorbell"
HomeKitTypes.js:2882: * Service "Fan" #风扇
HomeKitTypes.js:2902: * Service "Fan v2"
HomeKitTypes.js:2926: * Service "Filter Maintenance"
HomeKitTypes.js:2946: * Service "Faucet"
HomeKitTypes.js:2965: * Service "Garage Door Opener"
HomeKitTypes.js:2987: * Service "Heater Cooler"
HomeKitTypes.js:3014: * Service "Humidifier Dehumidifier"
HomeKitTypes.js:3041: * Service "Humidity Sensor" #湿度传感器
HomeKitTypes.js:3063: * Service "Irrigation System"
HomeKitTypes.js:3085: * Service "Leak Sensor"
HomeKitTypes.js:3107: * Service "Light Sensor" #光照传感器
HomeKitTypes.js:3129: * Service "Lightbulb" #灯泡
HomeKitTypes.js:3151: * Service "Lock Management"
HomeKitTypes.js:3177: * Service "Lock Mechanism"
HomeKitTypes.js:3196: * Service "Microphone"
HomeKitTypes.js:3215: * Service "Motion Sensor"
HomeKitTypes.js:3237: * Service "Occupancy Sensor" #人体传感器
HomeKitTypes.js:3259: * Service "Outlet"
HomeKitTypes.js:3278: * Service "Security System" #安全系统
HomeKitTypes.js:3300: * Service "Service Label"
HomeKitTypes.js:3318: * Service "Slat"
HomeKitTypes.js:3340: * Service "Smoke Sensor" #烟雾传感器
HomeKitTypes.js:3362: * Service "Speaker"
HomeKitTypes.js:3381: * Service "Stateless Programmable Switch"
HomeKitTypes.js:3400: * Service "Switch" #开关
HomeKitTypes.js:3418: * Service "Temperature Sensor" #温度传感器
HomeKitTypes.js:3440: * Service "Thermostat" #恒温器
HomeKitTypes.js:3466: * Service "Valve"
HomeKitTypes.js:3491: * Service "Window" #窗户
HomeKitTypes.js:3513: * Service "Window Covering" #窗帘

注册switch时涉及到的两个service:

  • AccessoryInformation service:每个Accessory都需要广播与设备本身相关的信息,无论其类型如何;它包含了ManufacturerModel SerialNumber等特性

    /*** Service "Accessory Information"*/Service.AccessoryInformation = function(displayName, subtype) {Service.call(this, displayName, '0000003E-0000-1000-8000-0026BB765291', subtype);// Required Characteristicsthis.addCharacteristic(Characteristic.Identify);this.addCharacteristic(Characteristic.Manufacturer);this.addCharacteristic(Characteristic.Model);this.addCharacteristic(Characteristic.Name);this.addCharacteristic(Characteristic.SerialNumber);this.addCharacteristic(Characteristic.FirmwareRevision);// Optional Characteristicsthis.addOptionalCharacteristic(Characteristic.HardwareRevision);this.addOptionalCharacteristic(Characteristic.AccessoryFlags);
    };
    
  • Swith service:真正的开关service,switch设备具有布尔特性On

    /*** Service "Switch"*/Service.Switch = function(displayName, subtype) {Service.call(this, displayName, '00000049-0000-1000-8000-0026BB765291', subtype);// Required Characteristicsthis.addCharacteristic(Characteristic.On);// Optional Characteristicsthis.addOptionalCharacteristic(Characteristic.Name);
    };
    
  • 每种设备的特性都可以在HomeKitTypes.js源码中查找到

mySwitch对象的getServices原型函数中,需要实例化以上两个service。每个service的每个characteristic 的getter和setter方法,将调用来自Homekit每个requests

function mySwitch(log, config) {this.log = log;this.name = config['name'];//get parameter from config.jsonthis.informationService = new Service.AccessoryInformation();this.switchService = new Service.Switch(this.name);// Service.Switch means this is a plugin of Switchthis.switchService.getCharacteristic(Characteristic.On).on('get',this.getSwitchOnCharacteristic.bind(this))//getter.on('set',this.setSwitchOnCharacteristic.bind(this));//setter
}mySwitch.prototype.getServices = function() {return [this.switchService];
}

不同于AccessoryInformation服务的特性(它是可读的,但只能在插件初始化时设置),On特性既可以写亦可以响应getter和setter

第四步:实例化Characteristic

可以从HomeKitTypes.js文件中找到Characteristic "On"有关代码,可以看出On特性有一个value值,需要在setter中使用。

/*** Characteristic "On"*/Characteristic.On = function() {Characteristic.call(this, 'On', '00000025-0000-1000-8000-0026BB765291');this.setProps({format: Characteristic.Formats.BOOL,perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]});this.value = this.getDefaultValue();
};inherits(Characteristic.On, Characteristic);Characteristic.On.UUID = '00000025-0000-1000-8000-0026BB765291';

mySwitch对象的原型函数中编写On特性的getter和setter的逻辑。这部分涉及硬件操作,这里硬件上使用ser2net服务将tcp接口转为串口设备,通过向tcp写入不同的命令从而控制下位机的硬件开关设备舵机

//create tcp connect
var net = require('net');
var client = new net.Socket();var onbuf =  new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x66],'hex');//cmd: turn on
var offbuf = new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x44],'hex');//cmd: turn off
var inibuf = new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x55],'hex');//cmd: resetclient.setEncoding('binary');
client.connect(2018,'localhost',function(){console.log('Connecte Server OK!');//connect ser2net
});//实例化
mySwitch.prototype.getSwitchOnCharacteristic = function (callback) {//getterconsole.log("**************Get on Function**************");callback(null);}mySwitch.prototype.setSwitchOnCharacteristic = function (on,callback) {//setterconsole.log("**************Set on Function:"+ on + "**************");if(on)client.write(onbuf);elseclient.write(offbuf);setTimeout(function(){client.write(inibuf);},500);//500ms Rest init statuecallback(null);
}

第五步:启动homebridge

可以使用homebridge -help查看如何启用,这里写一个常用Debug命令:DEBUG=* homebridge -D -U ./config/ -P ./plugin/,若要设置为开机自启,将命令行DEBUG=* homebridge -D -U ./config/ -P ./plugin/ >/dev/null添加到rc.local文件即可

附录一:开关插件

//mySwitch完整index.js代码
var Service, Characteristic;
var net = require('net');
var client = new net.Socket();var onbuf =  new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x66],'hex');//cmd: turn on
var offbuf = new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x44],'hex');//cmd: turn off
var inibuf = new Buffer([0xaa,0x08,0,0,0,0x14,0,0,0,0x55],'hex');//cmd: resetclient.setEncoding('binary');
client.connect(2018,'localhost',function(){console.log('Connecte Server OK!');//connect ser2net
});module.exports = function (homebridge) {Service = homebridge.hap.Service;Characteristic = homebridge.hap.Characteristic;/* registerAccessory' three parameters is plugin-name(must begin with homebridge-* add must be defined in package.json), accessory-name(must be defined in config.js),constructor-name(must be a function)*/homebridge.registerAccessory("homebridge-myswitch", "MyDormSwitch", mySwitch);
};function mySwitch(log, config) {this.log = log;this.name = config['name'];//get parameter from config.jsonthis.informationService = new Service.AccessoryInformation();this.switchService = new Service.Switch(this.name);// Service.Switch means this is a plugin of Switchthis.switchService.getCharacteristic(Characteristic.On).on('get',this.getSwitchOnCharacteristic.bind(this)).on('set',this.setSwitchOnCharacteristic.bind(this));
}mySwitch.prototype.getServices = function() {return [this.switchService];
}mySwitch.prototype.getSwitchOnCharacteristic = function (callback) {console.log("**************Get on Function**************");callback(null);}mySwitch.prototype.setSwitchOnCharacteristic = function (on,callback) {console.log("**************Set on Function:"+ on + "**************");if(on)client.write(onbuf);elseclient.write(offbuf);setTimeout(function(){client.write(inibuf);},500);//500ms Rest init statuecallback(null);
}

附录二:温度传感器插件

//mySensor完整index.js代码
var Service, Characteristic;
var net = require('net');
var client = new net.Socket();
var that;module.exports = function (homebridge) {Service = homebridge.hap.Service;Characteristic = homebridge.hap.Characteristic;homebridge.registerAccessory("homebridge-mysensor", "MyDepartTemp", mySensor);
};function mySensor(log, config) {this.log = log;this.name = config['name'];this.informationService = new Service.AccessoryInformation();this.service = new Service.TemperatureSensor(this.name);this.service.getCharacteristic(Characteristic.CurrentTemperature).on('get', this.getState.bind(this));that = this;
}mySensor.prototype.getServices = function() {return [this.service];
}mySensor.prototype.getState = function(callback) {  callback(null, 25.0);//default valuereturn;
}client.setEncoding('binary');
client.connect(2018,'localhost',function() {console.log('Connecte Server OK!');//connect ser2net
});client.on('data',function(data) {console.log('from server:'+ data);that.service.getCharacteristic(Characteristic.CurrentTemperature).updateValue(data);//update
});client.on('error',function(error){console.log('error:'+ error);client.destory();
});

备注:

如果从iPhone家庭终端删除了设备,想再次连接该设备时,先删除persist和accessories(homebridge自动生成的)文件,再运行homebridge。最后附上一个python的server代码,可以替代ser2net,通过调用系统命令操作硬件设备舵机

#coding=utf-8
import socket
import os
server = socket.socket()
server.bind(('localhost',6969))
server.listen(5)
print("start----")
while True:conn,addr = server.accept()print(conn,addr)print("*****")while True:data = conn.recv(1024)if not data:print("client has lost")breakelse:print("data",data)param = data.decode()if 'false' == param:print("false")os.system('./pwm.sh 1135000')else:print("true")os.system('./pwm.sh 2240000')conn.send(data.upper())
server.close()

参考链接

Github homebridge
npm中已发布的插件
如何开发homebridge插件
HomeBridge教程 v 0.0.3
树莓派3B+ 智能家居HomeKit
使用homebridge-mqtt对接设备到HomeKit
How To Make Siri your Perfect Home Companion With Devices not Supported by Apple Homekit

Homebridge 插件编写相关推荐

  1. Homebridge插件编写-基于homebridge-aqara

    两年前就想自己写Homebridge插件,奈何没有nodejis功底,而且这个js代码的可读性真的太差了,当时根本看不懂这些插件写的是什么,好在最近重新看了homebridge-aqara插件,内部重 ...

  2. Gulp:插件编写入门

    之前挖了个坑,准备写篇gulp插件编写入门的科普文,之后迟迟没有动笔,因为不知道该肿么讲清楚Stream这货,毕竟,gulp插件的实现不像grunt插件的实现那么直观. 好吧,于是决定单刀直入了.文中 ...

  3. VS2010插件编写学习总结

    VS2010 Addins 外接程序(插件)开发 http://www.cnblogs.com/Leo_wl/archive/2013/03/21/2973886.html 简单做了一个添加文件头注视 ...

  4. [Linux实用工具]munin-node插件配置和插件编写

    前面介绍了2篇munin使用的相关文章: [Linux实用工具]Linux监控工具munin的安装和配置 [Linux实用工具]Linux监控工具munin的展示(Nginx) 这次介绍一下munin ...

  5. maven插件编写_编写Maven插件的提示

    maven插件编写 最近,我花了很多时间为Maven编写插件或在其中工作. 它们简单,有趣且有趣. 我以为我会分享一些技巧,使编写它们时的生活更轻松. 提示1:将任务与Mojo分开 最初,您将把moj ...

  6. 【转载】Nessus安全测试插件编写教程

    Nessus安全测试插件编写教程 作者:Renaud Deraison 翻译:nixe0n 1.怎样编写一个高效的Nessus安全测试插件 在Nessus安全测试系统中, 所有的安全测试都是由ness ...

  7. Soul网关源码阅读(十)自定义简单插件编写

    Soul网关源码阅读(十)自定义简单插件编写 简介     综合前面所分析的插件处理流程相关知识,此次我们来编写自定义的插件:统计请求在插件链中的经历时长 编写准备     首先我们先探究一下,一个P ...

  8. jQuery插件编写,

    jQuery插件编写 jQuery插件 最近搞jquery插件的编写这里做下笔记 给jquery扩展的方式很多,看的我眼花缭乱 方式1 $.fun=function(){} 方式2 $.fn.fun= ...

  9. 3ds max sdk导出插件编写的心得

    3ds max sdk导出插件编写的心得 作者:yhchinabest 来自:CG先生-3D图形插件开发网http://www.cgsir.com 写在前面 为什么要写这个心得?去年11月份的时候我写 ...

最新文章

  1. LeetCode 6 Z字形变换
  2. TensorFlow高层次机器学习API (tf.contrib.learn)
  3. HDP 2.6 requires libtirpc-devel
  4. 不能用蛮力法解决的问题_溆浦事蒙汉:脱贫攻坚绝不能心浮气躁骄傲自满疲倦厌战...
  5. DBNull与Null的区别
  6. Java集合类的整理
  7. [转载] Java:简述Java中的自定义异常
  8. 64位centos下QQ无法输入中文只能输入英文
  9. 云客Drupal源码分析之插件系统(中)
  10. android edge 去广告,edge浏览器怎么去广告? edge浏览器去广告插件adsafe的使用方法...
  11. 韩波兄的好文:写给过去,现在和未来的自己
  12. 益聚星荣|网络主播雪梨、林珊珊偷逃税被罚,2个月前已进行立案检查
  13. c语言求最小公倍数和最大公约数三种算法
  14. 雅居乐万豪酒店java_“万豪,我心所属之地” | 上海雅居乐万豪酒店Terence Sun的实习故事...
  15. 手机大厂开始走上AI芯片 将来掀起AI应用
  16. Pytorch.Dataloader 详细深度解读和微修改源代码心得
  17. MySQL的下载(最新版本)(一)
  18. OpenStack Tracker
  19. JVM-04.垃圾回收机制
  20. 【转】网站流量UV是什么意思?什么是流量UV?

热门文章

  1. 如何快速查询mysql_mysql如何快速查询
  2. 作业要求20190919-2 功能测试
  3. 风控基础指标之决策树的特征选择
  4. 电商用户行为和可视化分析
  5. 【Linux】第十一篇:线程安全(互斥锁,死锁,条件变量)
  6. 电影主页面及电影详情页面实现
  7. Word 自带公式转为mathtype格式
  8. 客户端(浏览器)缓存
  9. (转)数学符号英文说法和发音大全!
  10. 怀旧服务器联盟优势,魔兽世界怀旧服阵营怎么选择 联盟和部落区别对比分析...