HomeAssistant加入空调控制(LIRC)并接入HomeKit
上一篇中已经能够成功红外控制空调,剩下的就是把它放到HomeAssistant中。
MQTT HVAC
最简单的方法是作为开关加入,然后运行命令,但是这样子只能控制制冷或制热,或者要加入很多开关。这样就很麻烦了,我还希望能够加入HomeKit中控制。
好在HomeAssistant有个东西叫MQTT HVAC,也是属于climate,可以直接接入HomeKit。
MQTT协议就相当于有两方在通信,一个是消息的接收者,一个是消息的发送者。当我们改变空调模式、温度时,消息发送到接收者,接收者去执行shell命令发送红外。大概原理就是这样,所以我们需要的是跑一个接收者的MQTT客户端。
这里面url需要填写HomeAssistant密码,mode和temp两个主题可以改成自己想要的名字, client.on
传入的回调函数就是对发过来的消息做出反应。因为HomeAssistant或HomeKit中打开空调需要先选择制冷、制热、自动,if (payload === 'heat')
这几个if就是对于模式选择的反应,比如选择了制热就发送'制热21度'的红外码,spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_21'])
,'aircon'是lirc配置的名字,'heat_21'是红外码的名字,以此类推。
#!/usr/bin/env node
var mqtt = require('mqtt');
var mode = 'mitsubishi/ac/mode/set';
var temp = 'mitsubishi/ac/temperature/set';
var url = 'mqtt://homeassistant:你的密码@127.0.0.1';
var spawn = require('child_process').spawn;
var mode0;
console.log(url);
var client = mqtt.connect(url);
client.subscribe(mode);
client.subscribe(temp);
client.on('message', (topic, message) => {
const payload = message.toString()
if (payload === 'heat') {
console.log(`Now Mode '${payload}'`)
mode0 = "heat";
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_21'])
}
else if (payload === 'cool') {
console.log(`Now Mode '${payload}'`)
mode0 = "cool"
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_28'])
}
else if (payload === 'auto') {
console.log(`Now Mode '${payload}'`)
mode0 = "auto"
spawn('irsend', ['SEND_ONCE', 'aircon', 'off'])
}
else if (payload === 'off') {
console.log(`Now Mode '${payload}'`)
mode0 = "off"
spawn('irsend', ['SEND_ONCE', 'aircon', 'off'])
}
else if (payload === '17.0') {
if (mode0 === 'heat') {
console.log(`17.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_17'])
}
else if (mode0 === 'cool') {
console.log(`17.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_17'])
}
else if (mode0 === 'auto') {
console.log(`17.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_17'])
}
}
else if (payload === '18.0') {
if (mode0 === 'heat') {
console.log(`18.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_18'])
}
else if (mode0 === 'cool') {
console.log(`18.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_18'])
}
else if (mode0 === 'auto') {
console.log(`18.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_18'])
}
}
else if (payload === '19.0') {
if (mode0 === 'heat') {
console.log(`19.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_19'])
}
else if (mode0 === 'cool') {
console.log(`19.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_19'])
}
else if (mode0 === 'auto') {
console.log(`19.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_19'])
}
}
else if (payload === '20.0') {
if (mode0 === 'heat') {
console.log(`20.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_20'])
}
else if (mode0 === 'cool') {
console.log(`20.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_20'])
}
else if (mode0 === 'auto') {
console.log(`20.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_20'])
}
}
else if (payload === '21.0') {
if (mode0 === 'heat') {
console.log(`21.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_21'])
}
else if (mode0 === 'cool') {
console.log(`21.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_21'])
}
else if (mode0 === 'auto') {
console.log(`21.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_21'])
}
}
else if (payload === '22.0') {
if (mode0 === 'heat') {
console.log(`22.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_22'])
}
else if (mode0 === 'cool') {
console.log(`22.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_22'])
}
else if (mode0 === 'auto') {
console.log(`22.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_22'])
}
}
else if (payload === '23.0') {
if (mode0 === 'heat') {
console.log(`23.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_23'])
}
else if (mode0 === 'cool') {
console.log(`23.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_23'])
}
else if (mode0 === 'auto') {
console.log(`23.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_23'])
}
}
else if (payload === '24.0') {
if (mode0 === 'heat') {
console.log(`24.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_24'])
}
else if (mode0 === 'cool') {
console.log(`24.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_24'])
}
else if (mode0 === 'auto') {
console.log(`24.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_24'])
}
}
else if (payload === '25.0') {
if (mode0 === 'heat') {
console.log(`25.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_25'])
}
else if (mode0 === 'cool') {
console.log(`25.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_25'])
}
else if (mode0 === 'auto') {
console.log(`25.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_25'])
}
}
else if (payload === '26.0') {
if (mode0 === 'heat') {
console.log(`26.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_26'])
}
else if (mode0 === 'cool') {
console.log(`26.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_26'])
}
else if (mode0 === 'auto') {
console.log(`26.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_26'])
}
}
else if (payload === '27.0') {
if (mode0 === 'heat') {
console.log(`27.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_27'])
}
else if (mode0 === 'cool') {
console.log(`27.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_27'])
}
else if (mode0 === 'auto') {
console.log(`27.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_27'])
}
}
else if (payload === '28.0') {
if (mode0 === 'heat') {
console.log(`28.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_28'])
}
else if (mode0 === 'cool') {
console.log(`28.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_28'])
}
else if (mode0 === 'auto') {
console.log(`28.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_28'])
}
}
else if (payload === '29.0') {
if (mode0 === 'heat') {
console.log(`29.0heat`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'heat_29'])
}
else if (mode0 === 'cool') {
console.log(`29.0cool`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_29'])
}
else if (mode0 === 'auto') {
console.log(`29.0auto`)
spawn('irsend', ['SEND_ONCE', 'aircon', 'cool_29'])
}
}
});
安装mqtt库: npm install mqtt --save
运行客户端: node aircon.js
修改HomeAssistant配置,这里需要对应自己设置的主题。
climate:
- platform: mqtt
name: Aircon
modes:
- off
- cool
- heat
- auto
mode_command_topic: "mitsubishi/ac/mode/set"
temperature_command_topic: "mitsubishi/ac/temperature/set"
重启测试能否使用。
mqtt客户端作为服务运行,sudo vim /etc/systemd/system/node-mqtt@pi.service
,需要修改路径。
[Unit]
Description=Mqtt client with Node
[Service]
ExecStart=/home/pi/smarthome/aircon.js
Restart=always
User=%i
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/home/pi/smarthome
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
刷新服务,sudo systemctl enable node-mqtt@pi.service
开机启动。
说明
HomeAssistant中控制的时候有个问题,显示的false并不能关闭空调,而HomeKit中却可以关闭(siri关闭是调成自动模式),所以把选择Auto模式设置成发送关闭的红外码。
其他的Auto温度也都是直接用了制冷的,实在是懒得录了,这模式一般也没用吧。
这里是和遥控器一样通过红外发送来控制的,所以没有办法读到到底空调处于什么状态,也就是optimistic mode。这就会导致一个问题,设置了26度,然后打开制冷,其实执行的是'制冷28度'(这是我前面设置的),也就是说这个显示是不正确的,而模式打开后,再设置的温度就是准确的了。
HomeKit上面显示的室温好像没办法改变。
参考
https://bbs.hassbian.com/thread-1817-1-1.html
https://www.home-assistant.io/components/climate.mqtt/