猫·仁波切

会研发的PM才是好OP.

折腾 Raspberry Pi + HomeKit 手记

9月14日凌晨苹果终于推送了 iOS 10 的更新。从之前发布会来看,并没有多少亮点,除了几天的新鲜感之外, 尤其是对于目前还在用上两代机型的我来说,2333。

两年前苹果发布 Swift 语言的同时,新增了 HomeKit,当时用工具 dump 过最老版本的 Swift 声明。传送门:HomeKit.swift。目前所有官方相关的资料位于 HomeKit - Apple

好消息是期待很久的 HomeKit 应用终于上线,屏幕上多了“家庭(Home)”应用,控制中心(从屏幕下方滑动)、 Siri 均对此有支持。 iOS 10 终于强化了推出已有两年智能家居平台,提供了官方 App,有不少硬件厂商支持。

简单说,HomeKit 就是苹果官方的智能家居平台解决方案,包括移动设备 SDK,智能家居硬件通信协议(HAP: HomeKit Accessory Protocol)、以及 MFi(Made for iPhone/iPod/iPad) 认证等等。通过 WiFi 或蓝牙连接智能家居设备(或 bridge 设备),也可以利用 Apple TV(4代) 或闲家中的置 iPad 实现设备的远程控制(HAP over iCloud)。

Home App 的维度划分:

  • Home: 家,和地理位置绑定,支持共享给好友控制。
  • Room: 房间,用于对设备进行分组。
  • Scene: 场景,一组对设备的配置,例如“起床”,那么可能的配置是打开卧室灯、窗帘、放段舒缓music等等。

众所周知苹果是卖数据线等硬件的公司(嗯,假设你数据线也坏过不少),HAP 协议部分是需要加入 MFi Program 才能获取文档,而且 MFi Program 无法以个人开发者身份加入。

好在有好心人逆向了 HAP 的服务端协议(对于智能硬件来说,硬件是服务端,手机App是客户端)。

对于折腾党来说,机会来了,自己动手改造家居!本文不涉及 App 开发,只涉及如何自制支持 HomeKit 的设备。

准备工作

设备列表:

  • iPhone 6P (iOS 10)
  • Raspberry Pi 3 (Debian jessie)

考察了两个比较靠谱的 HAP 实现:

最终选择使用 golang 的 brutella/hc,准备环境。

需要保证树莓派和手机位于统一子网,因为 HAP 底层是基于 Apple mDNS(RFC 6762)。

brutella/hc 要求 golang >= 1.4,而 Debian jessie 版本较低, 需要配置 jessie-backports 源:

1
deb ftp://ftp.cn.debian.org/debian jessie-backports main contrib non-free

同时导入源的 GPG Key。方法参考 这里

安装好 golang 1.6.2,建立开发目录。

1
2
3
# 似乎直接 install golang 会出点小问题,所以折衷用了如下方法:
> sudo apt-get install -t jessie-backports golang-1.6 golang-1.6-go golang-1.6-src golang-1.6-doc
> sudo apt-get install -t jessie-backports golang

示例

跑通官方示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
  "github.com/brutella/hc"
  "github.com/brutella/hc/accessory"
  "log"
)

func main() {
  info := accessory.Info{
      Name:         "Lamp",
      SerialNumber: "051AC-23AAM1",
      Manufacturer: "Apple",
      Model:        "AB",
  }
  acc := accessory.NewSwitch(info)

  acc.Switch.On.OnValueRemoteUpdate(func(on bool) {
      if on == true {
          log.Println("Client changed switch to on")
      } else {
          log.Println("Client changed switch to off")
      }
  })

  config := hc.Config{Pin: "00102003"}
  t, err := hc.NewIPTransport(config, acc.Accessory)
  if err != nil {
      log.Fatal(err)
  }

  hc.OnTermination(func() {
      t.Stop()
  })

  t.Start()
}

编译执行.

1
2
3
4
5
6
7
8
9
10
$ AppleHome> # current dir

$ AppleHome> go get
...

$ AppleHome> go build
...

$ AppleHome> ./AppleHome
...

随后打开手机的 Home App,添加设备,选择 Lamp,输入 PIN 00102003,完成配对,即可使用。

自定义设备

树莓派外接小音箱一只,用来放电台,尝试用 HomeKit 控制树莓派的禁音。命令:

1
2
amixer set PCM on
amixer set PCM off

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
        "os/exec"
        "github.com/brutella/hc"
        "github.com/brutella/hc/accessory"
        "log"
)

func main() {
        info := accessory.Info{
                Name:            "Radio",
                SerialNumber: "051AC-23AAM2",
                Manufacturer: "Apple",
                Model:          "RPI3",
        }
        acc := accessory.NewSwitch(info)

        acc.Switch.On.OnValueRemoteUpdate(func(on bool) {
                log.Println("Toggled PCM!")

                if on == true {
                        exec.Command("amixer", "set", "PCM", "on").Run()
                        log.Println("Client changed switch to on")
                } else {
                        exec.Command("amixer", "set", "PCM", "off").Run()
                        log.Println("Client changed switch to off")
                }
        })

        config := hc.Config{Pin: "00102004"}
        t, err := hc.NewIPTransport(config, acc.Accessory)
        if err != nil {
                log.Fatal(err)
        }

        hc.OnTermination(func() {
                t.Stop()
        })

        t.Start()
}

其他

HAP 将智能家居分为以下维度:

  • Accessory: 单个设备,例如开关,温度计,调节器
  • Service: 一组值,合起来提供服务,例如中央空调(调节温度,风速等等)

TODO

Comments