星期三, 3月 28, 2018

橋接自製配件設備至 Apple HomeKit

前言


在自製感測配件與設備的過程中,曾想是否能整合接入蘋果公司的 HomeKit 軟體框架中,讓 iOS 裝置如 iPhone 或 iPad 上的 App 能讀取其資訊甚或控制其動作。
稍微閱讀了蘋果官方在去年年中釋出的 HAP Specification (Non-Commercial Version) ,開始尋思若是獨自打造通信協定底層的實作有些費時,感謝廣博的網路資源,借重 HAP-python 於此可嘗試快速建一個簡單的 HomeKit Bridge 讓自製的配件設備橋接入 HomeKit。





技術摘記


從製造配件設備的觀點,簡要摘記對 HomeKit 框架概念的理解:
一個配件 (Accessory) 即代表一個設備實體,其可能提供了若干個功能服務 (Service) 並被裝設在家庭 (Home) 裡的某個房間 (Room) 內,而每個服務可能包含了若干個可供讀取或寫入的特性 (Characteristic)。
支援 HAP (HomeKit Accessory Protocol) 的配件扮演著 HAP Accessory Server 可以直接與扮演 HAP Client 的控制端 (iOS 裝置) 交談。
不直接支援 HAP 的設備則需經由一種特殊型態的 HAP Accessory Server--橋接器 (Bridge) 來轉接至 HomeKit 網路。

一些被 HAP 引用的技術也備記在此,以後有機會再進一步了解:




動手做


計畫試將自製的兩座群組警報設備橋接入 HomeKit。(如圖)

首先,定義一個配件類別代表我的警報設備,借用蘋果已定義的服務選用一個與我設備性質接近的 SecuritySystem 來試:(以下僅列出與 HAP 相關的代碼)
# import HAP-python module
from pyhap.loader import get_serv_loader, get_char_loader
from pyhap.accessory import Accessory,Bridge,Category
from pyhap.accessory_driver import AccessoryDriver

# define my accessory class for alarm system
class MyAlarmer(Accessory):
    category = Category.ALARM_SYSTEM
    def __init__(self,*args,**kwargs):
        self.logger = logging.getLogger(__name__)
        super(MyAlarmer,self).__init__(*args,**kwargs)
        serv_alarmer = get_serv_loader().get('SecuritySystem')
        char_cstate = serv_alarmer.get_characteristic('SecuritySystemCurrentState') 
        char_cstate.set_value(0)
        char_cstate.setter_callback = self.current_state_changed
        char_tstate = serv_alarmer.get_characteristic('SecuritySystemTargetState')
        char_tstate.set_value(0)
        char_tstate.setter_callback = self.target_state_changed
        # optional characteristic
        char_type = get_char_loader().get('SecuritySystemAlarmType')
        char_type.setter_callback = self.type_changed
        serv_alarmer.add_opt_characteristic(char_type)
        self.add_service(serv_alarmer)
    def target_state_changed(self, value):
        self.logger.info('*** {} target state changed {} ***'.format(self.display_name, value))
    def current_state_changed(self, value):
        self.logger.info('*** {} current state changed {} ***'.format(self.display_name, value))
    def type_changed(self, value):
        self.logger.info('*** {} alarm type changed {} ***'.format(self.display_name, value))

主程式中創建自訂的 accessory 實例並將其加入 bridge,再以此 bridge 創建一個 driver,然後啟動:
# create an instance of Bridge
bridge = Bridge(display_name='MyBridge')
# create an instance of my accessory and add it to the bridge
a = MyAlarmer(name)
bridge.add_accessory(a)
# create an instance of driver and start it
driver = AccessoryDriver(bridge, port=51826)
driver.start()

程式應當在接受到設備的狀態訊息時更新 accessory 的 SecuritySystemCurrentState 值:
# when received my alarm system's status, update the characteristic's value
s = a.get_service('SecuritySystem')
s.get_characteristic('SecuritySystemCurrentState').set_value(4 if msg['state'] == 3 else 0)

此外,iOS 的 Home App 對 SecuritySystem 服務可操作 SecuritySystemTargetState 設定,我試藉此做發送解除警報訊息給設備實體,所以在上列代碼的 target_state_changed 處應再添加此控制作用。


執行結果就如影片所呈現,程式在開始的前一分鐘等待設備狀態訊息創建 accessory 並將其加入 bridge,啟動 driver 後若未曾與 iOS 裝置配對過會顯示 QR Code,配對後的 iOS 裝置便可進行被允許的操作。


HomeKit 允許自訂服務與特性,不過蘋果家的 Home App 會不認識,此時就另有 App 的開發需要,所以建議先自蘋果提供的服務與特性選擇適用的開始。

沒有留言:

張貼留言