homeassistant和小米的折腾
Table of Contents
之前小米官方支持了homeassistant集成,米家的生态可以方便的和开源生态联动。我之前一直是使用mqtt server来接入我的一些esp32设备,并且下发指令来管理自己iot平台。这样做还是挺折腾的,需要用esp32的板子通过继电器这些元件来做各种智能化改造。不过,随着小米这次对HA的官方支持,终于可以把小米的设备们也一并加入到自己的流程里来了。
homeassistant 下载以及安装
homeassistant有下面几种部署方式
功能 | HA OS | Container | Core | Supervised |
---|---|---|---|---|
Automations | √ | √ | √ | √ |
Dashboards | √ | √ | √ | √ |
Integrations | √ | √ | √ | √ |
Add-ons | √ | × | × | √ |
Blueprints | √ | √ | √ | √ |
One-click updates | √ | × | × | √ |
Backups | √ | √ | √ | √ |
其中可以看到只有HAOS和Supervised的方式是所有功能都支持的,Contaner和Core是不支持add-ons的。
我并不想太深入去折腾homeassistant,更多得只是想把其作为一个用来接收我的指令并下发给设备的控制平台,所以选择了通过HAOS虚拟机来搭建这个平台。
haos
HA的官方提供了很多虚拟磁盘映像文件给我们直接使用,我个人的偏好是喜欢用qmeu来启动这些虚拟磁盘,但是windows只提供了virtualbox、vmware、hyper-v三种虚拟磁盘文件,所以我就只能在win下用开源的virtualbox了。
virtualbox镜像
先在官网链接中下载vdi的压缩包,解压后获取vdi文件,然后用下面的命令行就可以快速构建vbox的虚拟机了。
注意,虚拟机配置的设置按照vbox常用的来就ok,但是网络这里最好设置成桥接。你可以通过VBoxManage list bridgedifs命令列出所有桥接接口,然后通过–bridgeadapter1来设置。haos启动后默认的hostname是叫homeassistant,设置成桥*接之后,你可以方便得在局域网中通过homeassistant.local发现它并下发指令。
# 1. 创建虚拟机 VBoxManage createvm --name "HAOS" --ostype "Linux_64" --register # 2. 设置内存、CPU和启用EFI VBoxManage modifyvm "HAOS" --memory 4096 --cpus 2 --firmware efi # 3. 设置SATA控制器并附加VDI文件 VBoxManage storagectl "HAOS" --name "SATA" --add sata --controller IntelAHCI VBoxManage storageattach "HAOS" --storagectl "SATA" --port 0 --device 0 --type hdd --medium "haos_ova-14.1.vdi" # 4. 配置网络为桥接模式 VBoxManage modifyvm "HAOS" --nic1 bridged --bridgeadapter1 "Microsoft Network Adapter Multiplexor Driver" # 5. 启用磁盘自动收缩功能 VBoxManage storageattach "HAOS" --storagectl "SATA" --port 0 --device 0 --nonrotational on --discard on
qemu
同理qcow2压缩包下载 ,解压之后qemu一条命令可以启动。
qemu-system-x86_64 -m 4096 -smp cores=2 -hda haos_ova-14.1.qcow2
add-ons 安装
先在设置->加载项项->加载项商店中给HA安装ssh服务。
然后在ssh配置中配置好ssh的密码和密钥之后,连接到HA中进入config目录,用git clone的方式把项目拉下来,用脚本安装好。
cd /config git clone https://github.com/XiaoMi/ha_xiaomi_home.git cd ha_xiaomi_home ./install.sh /config
这里也注意一个点,一开始我看文档的时候,以为是虚拟机中命令行的操作,所以怎么也找不到/config这个目录。后来才发现,这些目录和指令的执行都是在容器中的。这个ssh的add-ons就是帮助你进入容器,然后执行后续操作的入口,而不是虚拟机的控制台。
rest接口使用
三种接口的使用方式
hass-cli
两种方式,一种通过环境变量设置地址和key,一种通过参数设置地址和key。
$env:HASS_SERVER="http://homeassistant.local:8123" $env:HASS_TOKEN="[key]" hass-cli device list hass-cli --server "http://homeassistant.local:8123" --token "[key]" device list
curl
curl -X GET \ -H "Authorization: Bearer [key]" \ -H "Content-Type: application/json" \ http://homeassistant.local:8123/api/states | jq
python
有些复杂点操作还是py比较方便,下面就是一个对小米智能插座进行开关的程序。(设置好host和key)
import requests import json class HomeAssistantAPI: def __init__(self, host="homeassistant.local:8123", token="*****"): self.base_url = f"http://{host}" self.headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json; charset=utf-8" } def get_switch_state(self, entity_id): url = f"{self.base_url}/api/states/{entity_id}" response = requests.get(url, headers=self.headers) if response.status_code == 200: return response.json()['state'] return None def toggle_switch(self, entity_id, state): url = f"{self.base_url}/api/services/switch/turn_{state}" data = {"entity_id": entity_id} response = requests.post(url, headers=self.headers, json=data) return response.status_code == 200 def toggle_switches(self, switches): results = {} for switch in switches: current_state = self.get_switch_state(switch) if current_state is None: results[switch] = False continue new_state = "off" if current_state == "on" else "on" success = self.toggle_switch(switch, new_state) results[switch] = { 'success': success, 'previous_state': current_state, 'new_state': new_state } return results if __name__ == "__main__": ha = HomeAssistantAPI() switches = [ "switch.qmi_cn_**********_psv3_on_p_2_1" ] results = ha.toggle_switches(switches) print("Results:", json.dumps(results, indent=2))
这个脚本的功能就是对switches里的所有开关进行,开启和关闭操作。 其中switch.qmicn**********psv3onp21 就是我们要操作的对象。你可以通过下面的脚本用api template的接口获取所有匹配到switch.*onp_.*$的对象们,然后就可以对其进行设置。
import requests import json def get_device_entities(): url = "http://homeassistant.local:8123/api/template" headers = { 'Authorization': 'Bearer [key]', 'Content-Type': 'application/json; charset=utf-8', 'Accept': 'application/json' } template = """ {% set devices = states | map(attribute="entity_id") | map("device_id") | unique | reject("eq",None) | list %} {%- set ns = namespace(devices = []) %} {%- for device in devices %} {%- set entities = device_entities(device) | select("match", "^switch.*_on_p_.*$") | list %} {%- if entities %} {%- set ns.devices = ns.devices + [ {device: {"name": device_attr(device, "name"), "entities": entities}} ] %} {%- endif %} {%- endfor %} {{ ns.devices | tojson }} """ payload = { "template": template } try: response = requests.post(url, headers=headers, json=payload) response.encoding = 'utf-8' response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Error making request: {e}") return None if __name__ == "__main__": result = get_device_entities() if result: print(json.dumps(result, indent=2, ensure_ascii=False))