前言
HomeKit庫(kù)是用來溝通和控制家庭自動(dòng)化配件的,這些家庭自動(dòng)化配件都支持蘋果的HomeKit Accessory Protocol。 HomeKit應(yīng)用程序可讓用戶發(fā)現(xiàn)兼容配件并配置它們。用戶可以創(chuàng)建一些action來控制智能配件(例如恒溫或者管線強(qiáng)弱),對(duì)其進(jìn)行分組,并且可以通過Siri觸發(fā)。
HomeKit對(duì)象被存儲(chǔ)在用戶iOS設(shè)備的數(shù)據(jù)庫(kù)中,并且可以通過iClound同步到其他iOS設(shè)備。HomeKit支持遠(yuǎn)程訪問智能配件,并支持多個(gè)用戶設(shè)備和多個(gè)用戶。HomeKit還對(duì)用戶的安全和隱私做了處理。
開發(fā)HomeKit應(yīng)用程序的信息可以參考的文檔
HomeKit User Interface Guidelines 提供了用戶界面設(shè)計(jì)指南
App Store Review Guidelines: HomeKit 提供了加快app審核的技巧
HomeKit Framework Reference 描述了HomeKit框架中的類和方法
External Accessory Framework Reference 列出了系統(tǒng)提供的發(fā)現(xiàn)和配置無(wú)線智能家居產(chǎn)品UI
HomeKit Catalog 提供示例演示HomeKit特性
WWDC 2014: Introducing HomeKit 對(duì)HomeKit更高層次的分析
iOS Security 描述HomeKit如何處理iOS上的安全和隱私
HomeKit開發(fā)準(zhǔn)備工作
HomeKit應(yīng)用服務(wù)只提供給通過AppStore發(fā)布的app應(yīng)用程序。在Xcode工程中,HomeKit需要額外的配置,你的app必須有開發(fā)者證書和代碼簽名才能使用HomeKit,不過可以在Xcode的Capablities面板使用HomeKit,可避免代碼簽名的問題。
Xocde 項(xiàng)目的plist文件要配置 Privacy - HomeKit Usage Description 這個(gè)key,否則會(huì)奔潰,老鐵,你懂得~~
-
Xcode將會(huì)添加HomeKit權(quán)限到你的工程授權(quán)文件中和會(huì)員中心的App ID授權(quán)文件中,也會(huì)將HomeKit框架添加到你的工程中。HomeKit 需要一個(gè)明確的App ID, 這個(gè)App ID是為了你完成這些步奏而創(chuàng)建的。
創(chuàng)建具有HomeKit功能的證書,如下圖
image.png
在創(chuàng)建Xcode項(xiàng)目的時(shí)候需要勾選好Team,否則會(huì)出錯(cuò)

啟動(dòng)HomeKitde 步驟如下:
- 在Xcode中,選擇View > Navigators > Show Project Navigator。
- 從Project/Targets彈出菜單中target(或者從Project/Targets的側(cè)邊欄)
- 點(diǎn)擊Capabilities查看你可以添加的應(yīng)用服務(wù)列表。
- 滑到HomeKit 所在的行并打開關(guān)。
下載HomeKit Accessory Simulator
無(wú)需為了開發(fā)Homekit 應(yīng)用程序而購(gòu)買硬件產(chǎn)品。你可以使HomeKit Accessory Simulator來測(cè)試HomeKit app和模擬配件設(shè)備之間的通信。HomeKit Accessory Simulator不是和Xcode一起發(fā)布的。
下載HomeKit Accessory Simulator步驟如下:
- 在Capabilities面板的HomeKit分區(qū),點(diǎn)擊Download HomeKit Accessory Simulator按鈕。(或者選擇Xcode > Open Developer Tool > More Developer Tools)
- 在瀏覽器中搜索并且下載"Hardware IO Tools for Xcode ".dmg文件。
- 在 Finder中雙擊~/Downloads中的.dmg文件。
-
把HomeKit Accessory Simulator拖拽到/Application文件中。
image.png
之后,你可以使用使用HomeKit Accessory Simulator測(cè)試你的HomeKit應(yīng)用程序。可參考 yourHomeKit App
HomeKit布局
HomeKit允許用戶創(chuàng)建一個(gè)或者多個(gè)Home布局。每個(gè)Home代表一個(gè)有網(wǎng)絡(luò)涉筆的住所。用戶擁有Home的數(shù)據(jù)并可通過自己的任何一條iOS設(shè)備進(jìn)行訪問。用戶也可以和客戶共享一個(gè)Home,但客戶的權(quán)限會(huì)有更多限制。被置頂為Primary home的home默認(rèn)是Siri指令的對(duì)象,并且不能指定home。
每個(gè)Home一般有多個(gè)room,并且每個(gè)room一般會(huì)有多個(gè)智能配件。在home中,每個(gè)房間是獨(dú)立的room,并且具有一個(gè)有意義的名字,例如“臥室”或者“廚房”,這些名字可以在Siri命令中使用。一個(gè)accessory(HMAccessory)代表實(shí)際家庭中的自動(dòng)化設(shè)備,例如車庫(kù)開門器。每個(gè)sevice中也會(huì)有多個(gè)特征(characteristic), 一個(gè)sevice(HMService)是accessory提供的一種實(shí)際服務(wù),例如打開或者關(guān)閉車庫(kù),或者車庫(kù)上的燈

如果你的app緩存了home布局的信息,那么當(dāng)其布局發(fā)聲改變的時(shí)候,app就需要更新這些信息。使用HMHomeManager對(duì)象可以從HomeKit數(shù)據(jù)庫(kù)獲取HMHome和其他相關(guān)的對(duì)象。獲取對(duì)象后,最應(yīng)該做的就是通過代理回調(diào)函數(shù)保持獲取對(duì)象和HomeKit數(shù)據(jù)庫(kù)同步,可參考Observing HomeKit Database Changes
創(chuàng)建Home Manager對(duì)象
使用Home Manager一個(gè)HMHomeMagager對(duì)象的訪問home,room,配件,服務(wù)以及其他HomeKit對(duì)象。在創(chuàng)建家庭對(duì)象管理器(home manager) 之后,直接設(shè)置它的代理,以便獲取到這些對(duì)象之后及時(shí)的通知你。
self.homeManager = [[HMHomeManager alloc] init];
self.homeManager.delegate = self;
當(dāng)你創(chuàng)建一個(gè)home manager對(duì)象時(shí),HomeKit就開始從HomeKit數(shù)據(jù)庫(kù)獲取這些homes和相關(guān)對(duì)象,例如room和accessory對(duì)象。當(dāng)HomeKit正在獲取哪些對(duì)象時(shí),home manager 的primaryHome屬性時(shí)nil,并且homes屬性是個(gè)空數(shù)組。你的app應(yīng)該處理用戶還沒有完成創(chuàng)建home的其概況,但是app應(yīng)該等待知道HomeKit完成初始化。當(dāng)獲取對(duì)象完成之后,HomeKit會(huì)發(fā)送homeManagerDidUpdateHomes:消息給home manager的代理。
ps:當(dāng)app進(jìn)入前臺(tái)或者在后臺(tái)Home manager屬性發(fā)生改變時(shí),這個(gè)homeManagerDidUpdateHomes: 方法就會(huì)調(diào)用,可參考Observing Changes to the Collection of Homes
獲取Primary Home 和Homes集合
homeKit允許用戶有多個(gè)home,每一個(gè)home代表一個(gè)有智能設(shè)備的住所。用戶擁有Home的數(shù)據(jù)并可通過自己的任何一臺(tái)iOS設(shè)備進(jìn)行訪問。用戶也可以和客戶共享一個(gè)Home,但是客戶的權(quán)限會(huì)有更多限制。在用戶的所有home中,會(huì)有一個(gè)常用的home,即為primary home。被指定為primary home的home默認(rèn)是Siri指令的對(duì)象,并且不能指定home,就是說primary home是只讀的不能去設(shè)定。
我們可以通過創(chuàng)建一個(gè)HMHomeManager對(duì)象去管理home。使用這個(gè)HMHomeManager對(duì)象的訪問home、room、配件、服務(wù)以及其他HomeKit對(duì)象
通過home manager的primaryHome屬性,可以得到peimary home,代碼如下:
HMHome *home = self.homeManager.primaryHome;
使用home manager的homes屬性可以得到用戶的所有home集合,例如自家主要居所,度假別墅以及辦公室。每個(gè)home都對(duì)應(yīng)一個(gè)獨(dú)立的home對(duì)象
HMHome *home;
for(home in self.homeManager.homes ){
…}
獲取 Home中的所有room
在一個(gè)home中,rooms屬性定義accessories的物理位置。用home的rooms屬性可以枚舉home中的room
HMHome *home = self.homeManager.primaryHome;
HMRome *room;
for(room in home.rooms){
…
}
*** Accessories ***
*** Accessories 數(shù)組屬于home,但是被指定給了home中不同的room。假如用戶沒有給一個(gè)accessory指定room,那么這個(gè)accessories被指定一個(gè)默認(rèn)的room ,這個(gè)room是roomForEntireHome方法的返回值。用room的accessories屬性可以枚舉room中所有的accessory ***
*** 比如說我的home中有一個(gè)智能燈泡,一個(gè)智能冰箱,一個(gè)智能熱水器,一個(gè)智能電視。我把燈泡指定給了臥室這個(gè)room,冰箱指定給了廚房這個(gè)room,熱水器指定給了衛(wèi)生間room。電視沒有指定room,那么就默認(rèn)給它指定了一個(gè)room,這個(gè)room就是 HMRoom *defultRoom = [home roomForEntireHome]。當(dāng)我需要獲取到我的智能硬件對(duì)象的時(shí)候,我通過home.accessories獲取到的是我家的所有智能硬件,也就是燈泡,冰箱和洗衣機(jī)以及電視。如果room是臥室,我通過room.accessories獲得到的就是燈泡(我這里就舉一個(gè)例子,臥室也可以有多個(gè)硬件),如果room是廚房,我通過room.accessories獲得到的就是冰箱 ***
示例代碼:
HMAccessory *accessory;
for(accessory in room.accessories)
{
//獲取到room中的所有 accessory對(duì)象
}
HMAccessory *accessory;
for(accessory in hoom.accessories)
{
//獲取到hoom中的所有accessory對(duì)象
}
一旦我們獲取到accessory對(duì)象,我們就可以展示一個(gè)個(gè)accessory的相關(guān)信息或者訪問它的服務(wù)和對(duì)象這樣就可以允許用戶控制它,可設(shè)置accessory的代理方法并實(shí)現(xiàn)這個(gè)代理方法
獲取room中的Accessories
Accessories 數(shù)組屬于home,但是被指定給了home中的room。假如用戶沒有給一個(gè)accessory指定room,那么這個(gè)accessories被指定一個(gè)默認(rèn)的room,這個(gè)room是roomForEntireHome方法的返回值。用room的accessores屬性可以枚舉room中所有的accessory
HMAccessory *accessory;
for(accessory in room.accessories){
…
}
如果你要展示一個(gè)個(gè)accessory的相關(guān)信息或者允許用戶控制它,可設(shè)置accessory的代理方法并實(shí)現(xiàn)這個(gè)代理方法, 如果一旦獲取到一個(gè)accessory對(duì)象,你就可以訪問它的服務(wù)的對(duì)象
具體內(nèi)容可參考
Observing Changes to Accessories
Accessing Services and Characteristics
獲取Home 中的Accessories屬性
使用HMHome類中的accessories的方法,可以直接從Home對(duì)象中獲取所有的accessory對(duì)象,而不用枚舉home中的所有room對(duì)象,詳情見Getting the Accessories in a Room
創(chuàng)建Homes和添加Accessories
--
我們所添加或者移除的這些homeKit對(duì)象都是會(huì)保存在一個(gè)共享的homeKit數(shù)據(jù)庫(kù)中的。我們?cè)谧约簩懙膆omeKitAPP中添加和移除的home,room等homekit對(duì)象,在系統(tǒng)自帶的家庭APP中的數(shù)據(jù)都是同步的。
HomeKit對(duì)象被保存在一個(gè)可以共享的HomeKit數(shù)據(jù)庫(kù)里,它可以通過HomeKit框架被多個(gè)應(yīng)用程序訪問,所以HomeKit調(diào)用的方法都是異步寫入的,并且這些方法都包含一個(gè)完成處理后的參數(shù)。如果這個(gè)方法處理成功了,你的應(yīng)用將會(huì)在完成處理函數(shù)里更新本地對(duì)象。應(yīng)用程序啟動(dòng)時(shí),HomeKit對(duì)象發(fā)生改變的并不能收到代理回調(diào),只能接受處理完成后的回調(diào)函數(shù)。
想要觀察其他應(yīng)用程序啟動(dòng)的HomeKit對(duì)象的變化請(qǐng)參閱:Observing HomeKit Database Changes。查閱異步消息完成處理后傳過來的錯(cuò)誤碼的信息,請(qǐng)參閱:HomeKit Constants Reference.
對(duì)象命名規(guī)則
HomeKit對(duì)象的名字,例如home,room和zone對(duì)象都可以被Siri識(shí)別,這一點(diǎn)已經(jīng)在文檔中指出。
以下幾點(diǎn)是HomeKit對(duì)象的命名規(guī)則:
- 對(duì)象名字在其命名空間內(nèi)必須是唯一的。
- 屬于用戶所有的home名字都在一個(gè)命名空間內(nèi)。
- 一個(gè)home對(duì)象及其所包含的對(duì)象在另一個(gè)命名空間內(nèi)。
- 名字只能包含數(shù)字、字母、空格以及省略號(hào)字符。
- 名字必須以數(shù)字或者字母字符開始。
- 在名字比較的時(shí)候,空格或者省略號(hào)是忽略的(例如home1和home 1 同一個(gè)名字)。
- 名字沒有大小寫之分。
關(guān)于那些語(yǔ)言可以與Siri進(jìn)行交互,請(qǐng)參閱HomeKit User Interface Guidelines文檔中的"Siri Integration"
創(chuàng)建Homes
在HMHomeManager類中使用addHomeWithName:completionHandler: 異步方法可以添加一個(gè)home。作為參數(shù)傳到那個(gè)方法中的home的名字,必須是唯一獨(dú)特的,并且是Siri 可以識(shí)別的home名字。
[self.homeManager addHomeWithName:@"My Home"
completionHandler:^(HMHome *home, NSError *error) {
if (error != nil) {
// Failed to add a home
} else {
// Successfully added a home
} }];
在else語(yǔ)句中,寫入代碼以更新應(yīng)用程序的視圖。為了獲取home manager對(duì)象,請(qǐng)參閱 Getting the Home Manager Object.
在Home中添加一個(gè)Room
--
每個(gè)Home一般有多個(gè)room,并且每個(gè)room一般會(huì)有多個(gè)智能配件。在home中,每個(gè)房間是獨(dú)立的room
使用addRoomWithName:completionHandler: 異步方法可以在一個(gè)home中添加一個(gè)room對(duì)象。作為參數(shù)傳到那個(gè)方法中的room的名字,必須是唯一獨(dú)特的,并且是Siri可識(shí)別的room的名字。
NSString *roomName = @"Living Room";
[home addRoomWithName:roomName completionHandler:^(HMRoom
*room, NSError *error) {
if (error != nil) {
// Failed to add a room to a home
} else {
// Successfully added a room to a home
} }];
在else語(yǔ)句中,寫入代碼更新應(yīng)用程序的視圖。
發(fā)現(xiàn)配件
Accessories封裝了物理配件的狀態(tài),因此它不能被用戶創(chuàng)建。想要允許用戶給家添加新的配件,我們可以使HMAccessoryBrowser對(duì)象找到一個(gè)與home沒有關(guān)聯(lián)的配件。HMAccessoryBrower對(duì)象在后臺(tái)搜尋配件,當(dāng)它找到配件的時(shí)候,使用委托來通知你的應(yīng)用程序。只有在startSearchingForNewAccessories方法調(diào)用之后或者stopSearchingForNewAccessories方法調(diào)用之前,HMAccessoryBrowserDelegate消息才被發(fā)送給代理對(duì)象。
發(fā)現(xiàn)home中的配件
一個(gè)accessory代表一個(gè)家庭中的自動(dòng)化設(shè)備,例如一個(gè)智能插座,一個(gè)智能燈具等
- 在類接口中天啊及配件瀏覽器委托協(xié)議,并且添加一個(gè)配件瀏覽器屬性。代碼如下:
@interface EditHomeViewController ()
@property HMAccessoryBrowser *accessoryBrowser;
@end
用自己的類名代替EditHomeViewController
- 創(chuàng)建配件瀏覽器對(duì)象,并設(shè)置它的代理
self.accessoryBrowser = [[HMAccessoryBrowser alloc] init];
self.accessoryBrowser.delegate = self;
- 開始搜尋配件
[self.accessoryBrowser startSearchingForNewAccessories];
- 將找到的配件添加到你的收藏里
- (void)accessoryBrowser:(HMAccessoryBrowser *)browser
didFindNewAccessory:(HMAccessory *)accessory {
// Update the UI per the new accessory; for example,
reload a picker
view.
[self.accessoryPicker reloadAllComponents];
}
用你自己的代碼實(shí)現(xiàn)上面的accessoryBrowser:didFindNewAccessory:方法。 當(dāng)然也可以實(shí)現(xiàn)accessoryBrowser:didRemoveNewAccessory: 這個(gè)方法來移除配件,這個(gè)配件對(duì)你的視圖或者收藏來說不再是新的。
- 停止搜尋配件
如果一個(gè)視圖控制器正在開始搜尋配件,那么可以重寫viewWillDisappear:方法來停止搜尋配件
- (void)viewWillDisappear:(BOOL)animated {
[self.accessoryBrowser stopSearchingForNewAccessories];
}
在WIFI網(wǎng)絡(luò)環(huán)境下,為了安全的獲取新的并且能夠被HomeKit發(fā)現(xiàn)的無(wú)線配件,可參閱External Accessory Framework Reference
為home和room添加配件(Accessory)
配件歸屬于home,并且它可以被隨意添加到home中的任意一個(gè)room中。使用addAccessory:completionHandler:這個(gè)異步方法可以在home中添加配件。這個(gè)配件的名字作為一個(gè)參數(shù)傳遞到上述異步方法中,并且這個(gè)名字在配件所屬的home中必須是唯一的。使用assignAccessory:toRoom:completionHandler: 這個(gè)異步方法可以給home中的room添加配件。配件默認(rèn)的room是roomForEntireHome這個(gè)方法返回值room,下面的代碼演示了如何給home和room添加配件
// Add an accesory to a home and a room
// 1. Get the home and room objects for the completion
handlers.
__block HMHome *home = self.home;
__block HMRoom *room = roomInHome;
// 2. Add the accessory to the home
[home addAccessory:accessory completionHandler:^(NSError
*error) {
if (error) {
// Failed to add accessory to home
} else {
if (accessory.room != room) {
// 3. If successfully, add the accessory to
the room
[home assignAccessory:accessory toRoom:room
completionHandler:^(NSError *error) {
if (error) {
// Failed to add accessory to room
} }];
} }
}];
配件可提供一項(xiàng)或者多項(xiàng)服務(wù),這些服務(wù)的特性是由制造商定義.配件的服務(wù)和特性,可參閱Accessing Services and Characteristics.
更改配件名稱
使用updateName:completionHandler: 異步方法可以改變配件的名稱,代碼如下:
[accessory updateName:@"Kid's Night Light"
completionHandler:^(NSError *error) {
if (error) {
// Failed to change the name
} else {
// Successfully changed the name
}
}];
為Homes和Room添加Bridge (橋接口)
橋接口是配件中的一個(gè)特殊對(duì)象,它允許你和其他配件交流,但是不允許你直接和HomeKit交流,例如一個(gè)橋接口可以是控制多個(gè)燈的樞紐,它使用的是自己的通信協(xié)議,而不是HomeKit配件通信協(xié)議。想要給home添加多個(gè)橋接口,你可以安裝Adding Accessories to Homes and Rooms 中描述的步驟,添加任何類型的配件到home中。當(dāng)你給home添加一個(gè)橋接口時(shí),在橋接口底層的配件也會(huì)被添加到home中。正如Observing HomeKit Database Changes 中所描述的那樣,每次更改通知設(shè)計(jì)模型,home的代理不會(huì)接受到橋接口的home:didAddAccessory:代理消息,而是接收一個(gè)有關(guān)于配件的home:didAddAccessory:代理消息。在home中,要把橋接口后的配件和任何類型的配件看成一樣的,例如:把他們加入配件列表的配置表中,相反的是,當(dāng)你給room增添一個(gè)橋接口時(shí),這個(gè)橋接口底層的配件并不會(huì)自動(dòng)添加到room中,原因是橋接口和它的配件可以位于不同的room中。
創(chuàng)建分區(qū)
分區(qū)(HMZone)是任意可選的房間rooms分組,例如樓上,樓下,或者臥室。房間可以被天機(jī)到一個(gè)或者多個(gè)區(qū)域

可使用addZoneWithName:completionHandler: 異步方法創(chuàng)建分區(qū)。所創(chuàng)建的行為參數(shù)傳遞到這個(gè)方法中分區(qū)的名稱,在home中必須是唯一的,并且應(yīng)該能被Siri識(shí)別。代碼如下:
__block HMHome *home = self.home;
NSString *zoneName = @"Upstairs";
[home addZoneWithName:zoneName completionHandler:^(HMZone
*zone, NSError *error)
{
if (error) {
// Failed to create zone
} else {
// Successfully created zone, now add the rooms
}
}];
可使用addRoom:completionHandler:異步方法給分區(qū)添加一個(gè)room,代碼如下:
__block HMRoom *room = roomInHome;
[zone addRoom:room completionHandler:^(NSError *error) {
if (error) {
// Failed to add room to zone
} else {
// Successfully added room to zone
} }];
觀察HomeKit數(shù)據(jù)庫(kù)的變化
每個(gè)Home都有一個(gè)HomeKit數(shù)據(jù)庫(kù)。如圖,HomeKit數(shù)據(jù)庫(kù)會(huì)安全地和home授權(quán)的用戶的iOS設(shè)備以及潛在的客人的iOS設(shè)備進(jìn)行同步。為了給用戶展示當(dāng)前最新的數(shù)據(jù),你的應(yīng)用需要觀察HomeKit數(shù)據(jù)庫(kù)的變化。

HomeKit代理方法
HomeKit使用代理設(shè)計(jì)模式(delegate design pattern)來通知應(yīng)用程序HomeKit對(duì)象的改變。一般來講,如果你的應(yīng)用程序調(diào)用了一個(gè)帶有完成處理參數(shù)的HomeKit方法,并且這個(gè)方法被成功調(diào)用了,那么相關(guān)聯(lián)的代理消息就會(huì)被發(fā)送給其他HomeKit應(yīng)用,無(wú)論這些應(yīng)用時(shí)安裝在同一臺(tái)iOS設(shè)備上還是遠(yuǎn)程iOS設(shè)備上。這些應(yīng)用甚至可以運(yùn)行在客人的iOS設(shè)備上。如果你的應(yīng)用發(fā)起了數(shù)據(jù)改變,但是代理消息并沒有發(fā)送到你的應(yīng)用,那么添加代碼到完成代理方法和相關(guān)聯(lián)的代理方法中來刷新數(shù)據(jù)和更新視圖就成為必須了。如果home布局發(fā)生了顯著變化,那么就重新加載關(guān)于這個(gè)home的所有信息。在完成程序處理的情況下,請(qǐng)?jiān)诟聭?yīng)用之前檢查那個(gè)方法是否成功。HomeKit也會(huì)調(diào)用代理方法來通知你的應(yīng)用程序home網(wǎng)絡(luò)狀態(tài)的改變。
下圖演示了 使用代理方法的過程:響應(yīng)用戶的操作,你的應(yīng)用程序調(diào)用了addRoomWithName:completionHandler:方法,并沒有錯(cuò)誤發(fā)生,完成處理程序應(yīng)當(dāng)更新home的所有視圖。如果成功了,homekit將會(huì)發(fā)送home:didAddRoom:消息給其他應(yīng)用中homes的代理。因此,你實(shí)現(xiàn)的這個(gè)home:didAddRoom:方法也應(yīng)該更新home的所有視圖

應(yīng)用程序只有在前臺(tái)運(yùn)行的時(shí)候才能接受代理消息。當(dāng)你的應(yīng)用在后臺(tái)時(shí),homeKit數(shù)據(jù)庫(kù)的改變并不會(huì)成批處理。也就是說,如果你的應(yīng)用在后臺(tái),當(dāng)其他的應(yīng)用成果地添加一個(gè)room到home中的時(shí)候,你的應(yīng)用程序并不會(huì)接受到home:didAddRoom: 消息。當(dāng)你的應(yīng)用程序到前臺(tái)運(yùn)行時(shí),你的應(yīng)用程序?qū)?huì)接受到homeManagerDidUpdateHomes:消息,這個(gè)消息是表示你的應(yīng)用程序要重新加載所有的數(shù)據(jù)。
觀察Homes集合的改變
設(shè)置home manager的代理并且實(shí)現(xiàn)HMHomeManagerDelegate協(xié)議,當(dāng)primary home或者h(yuǎn)ome集合發(fā)生改變時(shí),可以接受代理消息。
所有的應(yīng)用都需要實(shí)現(xiàn)homeManagerDidUpdateHomes:方法,這個(gè)方法在完成最初獲勝homes之后被調(diào)用。對(duì)新建的home manager來說,在這個(gè)方法被調(diào)用之前,primaryHome屬性的值是nil,homes數(shù)組是空的數(shù)組。當(dāng)應(yīng)用程序開始在前臺(tái)運(yùn)行時(shí)也會(huì)調(diào)用homeManagerDidUpdateHomes: 方法,當(dāng)其在后臺(tái)運(yùn)行時(shí)數(shù)據(jù)發(fā)生改變,改homeManagerDidUpdateHomes:方法會(huì)重新加載與homes相關(guān)聯(lián)的所有數(shù)據(jù)。
觀察homes的變化
- 在類的接口中添加HMHomeManagerDelegate代理和homeManager屬性。代碼如下:
@interface AppDelegate () @property (strong, nonatomic) HMHomeManager *homeManager;
@end
- 創(chuàng)建home manager對(duì)象并設(shè)置其代理
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.homeManager = [[HMHomeManager alloc] init];
self.homeManager.delegate = self;
return YES;
}
- 實(shí)現(xiàn)hoems發(fā)生改變時(shí)調(diào)用的代理方法。例如:如果多個(gè)視圖控制器展示了hoems相關(guān)信息,你可以發(fā)布一個(gè)更改通知去更新所有視圖
- (void)homeManagerDidUpdateHomes:(HMHomeManager *)manager {
// Send a notification to the other objects
[[NSNotificationCenter defaultCenter]
postNotificationName:@"UpdateHomesNotification"
object:self];
}
- (void)homeManagerDidUpdatePrimaryHome:(HMHomeManager
*)manager {
// Send a notification to the other objects
[[NSNotificationCenter defaultCenter]
postNotificationName:@"UpdatePrimaryHomeNotification"
object:self];
}
視圖控制器注冊(cè)更改通知并且執(zhí)行適當(dāng)?shù)牟僮?/p>
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateHomes:)
name:@"UpdateHomesNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updatePrimaryHome:)
name:@"UpdatePrimaryHomeNotification" object:nil];
觀察個(gè)別home的變化
展示home信息的視圖控制器應(yīng)該成為home對(duì)象的代理,并且當(dāng)home發(fā)生改變時(shí)更新視圖控制器的視圖。
觀察特定home對(duì)象的改變
- 在類的接口中添加home代理協(xié)議
@interface HomeViewController ()<HMHomeDelegate>
@end
- 設(shè)置配件代理
home.delegate = self;
- 實(shí)現(xiàn)HMHomeDelegate協(xié)議
實(shí)現(xiàn)home:didAddAccessory:和home:didRemoveAccessory: 方法來更新展示配件的視圖。用HMAccessory類的room屬性可以獲取配件所屬的room。對(duì)配件來說,默認(rèn)的room是roomForEntireHome這個(gè)方法的返回值
Bridge Note: 當(dāng)你為home添加橋接口時(shí),橋接口底層的配件會(huì)自動(dòng)被添加到home中。你的代理會(huì)接收到橋接口后每個(gè)配件的 home:didAddAccessory:消息,但是你的代理不會(huì)接收到橋接口的home:didAddAccessory:消息。
觀察配件的變化
配件的狀態(tài)可以在任何時(shí)間發(fā)生變化。配件可能不能被獲得,可以被移除,或者被關(guān)閉。請(qǐng)更新用戶界面以反映配件狀態(tài)的更改,尤其是如果你的app允許用戶控制配件時(shí)。
如果從HomeKit數(shù)據(jù)庫(kù)中檢索到了配件對(duì)象,可以觀察個(gè)別配件的變化,具體步驟如下:
- 在類接口中添加配件代理協(xié)議
@interface AccessoryViewController () < HMAccessoryDelegate>
@end
- 設(shè)置配件的代理
accessory.delegate = self;
- 實(shí)現(xiàn)HMAccessoryDelegate 協(xié)議
比如,執(zhí)行 accessoryDidUpdateReachability: 方法以啟用或者禁用配件控制
- (void)accessoryDidUpdateReachability:(HMAccessory *)accessory {
if (accessory.reachable == YES) {
// Can communicate with the accessory
} else {
// The accessory is out of range, turned off, etc
}
}
如果你展示了配件的服務(wù)狀態(tài)和特性,那么請(qǐng)執(zhí)行以下代理方法來相應(yīng)地更新視圖
accessoryDidUpdateServices:
accessory:service:didUpdateValueForCharacteristic:
配件的服務(wù)具體內(nèi)容,請(qǐng)參閱Accessing Services and Their Characteristics
訪問服務(wù)和特性
服務(wù)(HMService)代表了一個(gè)配件(accessory)的某個(gè)功能和一些具體可讀寫的特性(HMCharacteristic)。一個(gè)配件可以擁有多項(xiàng)服務(wù),一個(gè)服務(wù)可以有很多特性。比如一個(gè)車庫(kù)開門器可能擁有一個(gè)照明和開關(guān)的服務(wù)。照明服務(wù)可能擁有打開/關(guān)閉和調(diào)節(jié)亮度的特性。用戶不能制造智能家電配件和他們的服務(wù)-配件制造商會(huì)制造配件和它們的服務(wù)-但是用戶可以改變服務(wù)的特性。一些擁有可讀寫屬性的特性代表著某種物理狀態(tài),比如,一個(gè)恒溫器中的當(dāng)前問題就是一個(gè)只可讀的值,但是目標(biāo)溫度又是可讀寫的。蘋果預(yù)先定義了一些服務(wù)和特性的名稱,以便讓Siri能夠識(shí)別它們。
獲得配件的服務(wù)和屬性
在依照Getting the Accessroties in a Room 中描述,你創(chuàng)建了一個(gè)配件對(duì)象之后,你可以獲得配件的服務(wù)和特性。當(dāng)然你也可以直接從home中按照類型獲得不同的服務(wù)。
通過HMAccessory類對(duì)象的services屬性,我們可以獲得一個(gè)配件的服務(wù)。
NSArray *services = accessroy.services;
要獲得一個(gè)home當(dāng)中配件提供的特定服務(wù),使用HMHome類對(duì)象的servicesWithTypes:方法
NSArray *lightServices = [home servicesWithTypes:[HMServicesTypeLightbulb]];
NSArray *thermostatServices = [home servicesWithTypes:[HMServicesTypeThermostat]];
使用HMServices類對(duì)象的name屬性來獲得服務(wù)的名稱
NSString *name = services.name;
要獲得一個(gè)服務(wù)的特性,可以使用characteristics屬性
NSArray *characteristics = service.characteristics
使用servicesType屬性來獲得服務(wù)的類型
NSString *serviceType = service.serviceType
蘋果定義了一些服務(wù)類型,并能被Siri識(shí)別:
- 門鎖(Door locks)
- 車庫(kù)開門器(Garage door openers)
- 燈光(Lights)
- 插座(Outlets)
- 恒溫器(Thermostats)
改變服務(wù)名稱
使用updateName:completionHandler:異步方法來改變服務(wù)名稱。傳入此方法的服務(wù)名稱參數(shù)必須在一個(gè)home當(dāng)中是唯一的,并且服務(wù)名可被Siri識(shí)別。
[service updateName:@"Garage 1 Opener" completionHandler:^(NSError *error) {
if (error) {
// Failed to change the name
} else {
// Successfully changed the name
}
}];
訪問特性的值
特性代表了一個(gè)服務(wù)的一個(gè)參數(shù),它可以是只讀,只寫或者可讀寫,它提供了這個(gè)參數(shù)可能的值的信息,比如,一個(gè)布爾或者一個(gè)范圍值。恒溫器中的溫度就是只讀的,而目標(biāo)溫度又是可續(xù)寫的,一個(gè)執(zhí)行某個(gè)任務(wù)命令且不要求任何返回,比如播放一段聲音或者閃爍一下燈光確認(rèn)某個(gè)配件,可能就是只寫的。
蘋果定義了一些特性的類型,并能被Siri識(shí)別:
- 亮度(Brightness)
- 最近溫度(Current temperature)
- 鎖的狀態(tài)(Lock state)
- 電源的狀態(tài)(Power state)
- 目標(biāo)狀態(tài)(Target state)
- 目標(biāo)溫度(Target temperature)
當(dāng)對(duì)于一個(gè)車庫(kù)開門器來說,目標(biāo)狀態(tài)就是打開或者關(guān)閉。對(duì)于一個(gè)鎖來說,目標(biāo)狀態(tài)就是上鎖和未上鎖。
在你獲得了一個(gè)HMService對(duì)象之后,如 Getting Services and Their Properties所描述的,你可以獲得每個(gè)服務(wù)的特性的值。因?yàn)檫@些值是從配件中獲得的,這些讀寫的方法都是異步的,并可以傳入一個(gè)完成回調(diào)的block
使用readValueWithCompletionHandler:異步方法來讀取一個(gè)特性的值。
[characteristic readValueWithCompletionHandler:^(NSError *error) {
if (error == nil) {
//可以加入代碼更新app視圖
// Successfully read the value
id value = characteristic.value;
}
else {
// Unable to read the value
} }];
使用writeValue:completionHandler:異步方法來向一個(gè)特性寫入值
[self.characteristic writeValue:@42 withCompletionHandler:^(NSError *error) {
if (error == nil) {
// Successfully wrote the value
}
else {
// Unable to write the value
} }];
不要以為函數(shù)調(diào)用完成就以為這寫入成功,實(shí)際上只有在當(dāng)完成回調(diào)執(zhí)行并沒有錯(cuò)誤產(chǎn)生時(shí)才表示寫入成功。比如,知道一個(gè)開關(guān)的特性改變之前都不要改變這個(gè)開關(guān)的狀態(tài)。在if語(yǔ)句中,加入你的代碼,以更新app的視圖
另外,在別的app更新了特性的值時(shí)也需要更新視圖,在Observing Changes to Accessories中有描述。
創(chuàng)建服務(wù)組
一個(gè)服務(wù)組(HMServiceGroup)提供了控制不同配件的任意數(shù)量服務(wù)的快捷方式-比如,當(dāng)用戶離開家之后控制家中的某些燈。

在你創(chuàng)建了一個(gè)HMHome對(duì)象之后,如Getting the Primary Home and Collection of Homes中描述,你也就在這個(gè)家中創(chuàng)建一個(gè)服務(wù)組。
為了創(chuàng)建一個(gè)服務(wù)組,我們使用HMHome類對(duì)象的addServiceGroupWithName:completionHandler:方法,方法中參數(shù)服務(wù)組的名稱必須在此家中唯一,并可以被Siri識(shí)別
[self.home addServiceGroupWithName:@"Away Lights" completionHandler:^(HMServiceGroup *serviceGroup, NSError *error) {
if (error == nil) {
// Successfully created the service group
} else {
// Unable to create the service group
}];
我們使用HMServiceGroup類對(duì)象的addService:completionHandler:方法來向服務(wù)組中添加一個(gè)服務(wù)。服務(wù)可以在一個(gè)或多個(gè)服務(wù)組中。
[serviceGroup addService:service completionHandler:^(NSError *error) {
if (error == nil) {
// Successfully added service to service group
}
// Unable to add the service to the service group
}];
通過HMServiceGroup類對(duì)象的accessory屬性,我們獲得服務(wù)所對(duì)應(yīng)的智能電器。
HMAccessory *accessory = service.accessory;
和配件類似,代理方法在別的app改變服務(wù)組時(shí)也會(huì)被調(diào)用。如果你的app使用了服務(wù)組,請(qǐng)參閱HMHomeDelegate Protocol Reference文檔,獲悉你應(yīng)該事先哪些方法以觀察這些變化。
測(cè)試HomeKitApp
如果你沒有智能電器,你可以使用HomeKit Accessroy Simulator來模擬home中的智能電器。每個(gè)模擬配件都擁有服務(wù)和特性,你可以從你的App當(dāng)中控制它。你的App在HomeKit數(shù)據(jù)庫(kù)中創(chuàng)建對(duì)象和關(guān)系。它可以創(chuàng)建home布局,可以添加新的配件到模擬的home環(huán)境中,最后向home中的每個(gè)房間添加只能配件,然后,你的app就能控制這些在HomeKitAccessory Simulator展示的模擬智能配件了。為了使用HomeKit Accessory Simulator,請(qǐng)?jiān)趇OS模擬器中運(yùn)行你的應(yīng)用程序,或者使用Xcode在Ios設(shè)備上運(yùn)行應(yīng)用程序。
HomeKitAccessory Simulator是一個(gè)附加的開發(fā)者工具,怎么安裝,之前已經(jīng)講過。
添加智能電器(配件)
使用HomeKit Accessory Simulator來添加只能電器到模擬網(wǎng)絡(luò)中。
向網(wǎng)絡(luò)中添加只能電器配件,請(qǐng)按照下面的步驟添加:
- 在HomeKit Accessory Simulator中,點(diǎn)擊底部左邊‘+’按鈕。
- 從彈出菜單中選擇添加智能電器(Add Accessory)
- 輸入智能電器的名字和制造商。
- 點(diǎn)擊完成

向智能電器中添加服務(wù)
一個(gè)智能電器需要一項(xiàng)服務(wù)和特性,你可以從app控制它。從預(yù)定了服務(wù)列表中選擇一項(xiàng)服務(wù),并自定義特性。
按照下面步驟向智能電器中添加服務(wù)
- 在HomeKit Accessory Simulator中,選擇Accessories列中的某個(gè)配件。
該配件的服務(wù)信息會(huì)展示在一個(gè)詳情界面中。
注意:所有智能電器都有一個(gè)Accessory Information,顯示在所有其他服務(wù)的下方。你可以向這個(gè)Accessory Information服務(wù)添加特性,但你不能刪除默認(rèn)的特性。
-
點(diǎn)擊添加服務(wù)(Add Service),并從彈出視圖中選擇一個(gè)服務(wù)類型。
新添加的服務(wù)會(huì)在右邊詳細(xì)顯示。HomeKit Accessory Simulator為每種服務(wù)創(chuàng)建通用的特性。比如一個(gè)燈光服務(wù)的默認(rèn)特性為色彩(Hue),飽和度(Saturation),亮度(Brightness)和開關(guān)。(開關(guān)特性和電源狀態(tài)特性是一樣的,正如 Accessing Values of Characteristics中描述的那樣。)一些特性是強(qiáng)制性的有一些也是可選擇的。比如,開關(guān)特性就是強(qiáng)制性的,而色彩,飽和度,亮度這些特性都是可選擇的
image.png
向服務(wù)中添加特性
你可以向服務(wù)中添加預(yù)定義的特性,或者自定義的特性。每種特性你都只能添加一個(gè)
按照下面的步驟向服務(wù)中添加特性
- 在HomeKit Accessory Simulator中,服務(wù)詳情視圖,點(diǎn)擊添加特性(Add Characteristic)
- 在特性類型菜單中,選擇一個(gè)類型或者自定義類型。
- 在其他文本框中輸入此特性的其他信息,并點(diǎn)擊完成(Finish).新添加的特性會(huì)在詳細(xì)視圖展示出來。
- 點(diǎn)擊特性右邊的減號(hào)來刪除一個(gè)特性。如果特性右邊并沒有減號(hào)顯示,這說明這個(gè)特性對(duì)這個(gè)服務(wù)來說是必須的。比如,你可以刪除電燈服務(wù)中的色彩(Hue),飽和度(Saturation)和亮度(Brightness),但是你不可以刪除開關(guān)特性。

通過你的app向家庭中添加智能電器(配件)
在你通過HomeKit Accessory Simulator創(chuàng)建了一個(gè)智能電器后,運(yùn)行你的App然后添加一個(gè)新的智能電器到你的家庭。
控制智能電器(配件)
在HomeKit Accessory Simulator中,你可以獲得智能電器的服務(wù),并在其他HomeKit App中設(shè)置服務(wù)的特性值來模擬控制這個(gè)智能電器,或者手動(dòng)地模擬控制智能電器
想要控制一個(gè)智能電器你需要:
- 在HomeKit Accessory Simulator中的智能電器列表(Accessories column)中選擇一個(gè)智能電器。這個(gè)智能電器的服務(wù)和特性會(huì)被展示在詳情界面。
- 操作一個(gè)特性的控件來改變它的值。
比如,為了改變一個(gè)燈泡的顏色(Hue),飽和度(Saturation)和亮度(Brightness),請(qǐng)滑動(dòng)這個(gè)滑塊。為了打開這個(gè)燈泡請(qǐng)選擇On選項(xiàng)

如果你的app展示了一個(gè)服務(wù)的特性,比如燈泡的開關(guān)狀態(tài),當(dāng)你在HomeKit Accessory Simulator中改變這些特性的值時(shí),它應(yīng)當(dāng)更新視圖。
++++++++++++++++++++++++++++
觀察HomeKit數(shù)據(jù)庫(kù)的變化,可參閱Observing HomeKit Database Changes
如果你想從app中通過編寫代碼來控制一個(gè)智能電器,請(qǐng)閱讀Accessing Services and Characteristics
++++++++++++++++++=
添加橋接口
為了模擬哪些不支持HomeKit Accessory Protocol協(xié)議的智能電器,需要添加一個(gè)虛擬橋接口,然后將智能家電添加到這個(gè)虛擬橋接口。配置虛擬橋接口底層的智能電器和配置其他類型的智能電器差不多。
添加一個(gè)虛擬橋接口到網(wǎng)絡(luò)
具體步驟如下:
- 在HomeKit Accessory Simulator中,點(diǎn)擊智能電器列表底部的“+”按鈕。
- 在彈出框中選擇Add 虛擬橋接口。
- 輸入一個(gè)智能電器的名稱和制造商
-
點(diǎn)擊完成
image.png
向虛擬橋接口添加智能電器配件
可向一個(gè)虛擬橋接口添加一個(gè)或多個(gè)智能電器。
為了向一個(gè)虛擬橋接口添加一個(gè)智能電器,需要:
在HomeKit Accessory Simulator左邊的列表中,選擇虛擬橋接口中的一個(gè)虛擬橋接口。
在詳情頁(yè)面選擇Add Accessory。
輸入一個(gè)智能電器名字和制造商。
點(diǎn)擊完成

在你的app中添加虛擬橋接口到home
將虛擬橋接口和home匹配的過程和講一個(gè)智能電器配置到一個(gè)home的過程是一樣的,可參考Adding Accessories to a Home in Your App 在虛擬橋接口底層的智能電器配件也一樣被加入到了home,可參閱 Adding Bridges to Homes and Rooms
控制虛擬橋接口底層的智能電器
如何控制虛擬橋接口的智能電器和直接控制智能電器的步驟一致,可參閱Controlling Accessories in HomeKit Accessory Simulator除了你直接選擇虛擬橋接口下的智能電器之外。
在多設(shè)備和多用戶環(huán)境中測(cè)試
在iOS模擬器中你不能測(cè)試分享HomeKit數(shù)據(jù)庫(kù)到多個(gè)iOS設(shè)備和用戶。你應(yīng)該安裝你的App到多臺(tái)iOS設(shè)備上,在這些設(shè)備中輸入iCloud證書,然后運(yùn)行你的App?;蛘?,使用ad hoc授權(quán)來在多臺(tái)注冊(cè)設(shè)備中測(cè)試你的app。
為了測(cè)試單用戶多設(shè)備環(huán)境,你應(yīng)該使用同一個(gè)iCloud賬戶在多臺(tái)設(shè)備登陸。
為了測(cè)試多用戶使用同一家庭的智能電器,你應(yīng)該在多臺(tái)設(shè)備使用不同的iCloud賬戶登陸。
你的App應(yīng)該應(yīng)該可以允許一個(gè)用戶邀請(qǐng)客人到你的家中,如Managing Users所述。
創(chuàng)建動(dòng)作集(Action Sets)和觸發(fā)器(Triggers)
一個(gè)動(dòng)作集合HMActionSet和觸發(fā)器HMTimerTrigger允許你同時(shí)控制多個(gè)智能電器。比如,一個(gè)動(dòng)作集合可能會(huì)在用戶上床休息之前執(zhí)行一組動(dòng)作HMAction。一個(gè)寫作向一個(gè)特性寫入了值。動(dòng)作集合中的動(dòng)作是以不確定的順序執(zhí)行的。一個(gè)觸發(fā)器會(huì)在一個(gè)特定的時(shí)間觸發(fā)一個(gè)動(dòng)作集并可以重復(fù)執(zhí)行。每一個(gè)動(dòng)作集合在一個(gè)家庭中都有唯一的名稱并可被Siri識(shí)別。

創(chuàng)建寫入動(dòng)作
寫入動(dòng)作會(huì)想一個(gè)服務(wù)的特性寫入值并被加入到動(dòng)作集合中去。HMAction類是HMCharacteristicWriteAction具體類的抽象基類。一個(gè)動(dòng)作有一個(gè)相關(guān)聯(lián)的特性對(duì)象,你可以通過Accessing Services and Characteristics中描述的來獲取相關(guān)的服務(wù)和特性,然后創(chuàng)建這個(gè)HMCharacteristicWriteAction
為了創(chuàng)建一個(gè)動(dòng)作,我們使用HMCharacteristicWriteAction類中的initWithCharacteristic:targetValue:方法。
HMCharacteristicWriteAction *action = [[HMCharacteristicWriteAction alloc] initWithCharacteristic:characteristic targetValue:value];
在你的代碼中,你使用對(duì)應(yīng)的特性的期望來替換value參數(shù),并使用對(duì)應(yīng)的HMCharacteristic對(duì)象來替換characteristic參數(shù)
創(chuàng)建并執(zhí)行動(dòng)作集
一個(gè)動(dòng)作集就是一個(gè)共同執(zhí)行的動(dòng)作的集合。比如一個(gè)夜間動(dòng)作集合可能包含關(guān)閉電燈,調(diào)低恒溫水平和鎖上房門。
為了創(chuàng)建一個(gè)動(dòng)作集我們使用addActionSetWithName:completionHandler:異步方法
[self.home addActionSetWithName:@"NightTime" completionHandler:^(HMActionSet *actionSet, NSError *error) {
if (error == nil) {
// 成功添加了一個(gè)動(dòng)作集
} else {
// 添加一個(gè)動(dòng)作集失敗
}
}];
為了添加一個(gè)動(dòng)作到動(dòng)作集,我們使用addAction:completionHandler:異步方法
[actionSet addAction:action completionHandler:^(NSError *error) {
if (error == nil) {
// 成功添加了一個(gè)動(dòng)作到動(dòng)作集
} else {
// 添加一個(gè)動(dòng)作到動(dòng)作集失敗
}
}];
想要移除一個(gè)動(dòng)作,可使用removeAction:completionHandler:方法。
想要執(zhí)行一個(gè)動(dòng)作集,可使用HMHome類的executeActionSet:completionHandler:方法。比如,用戶希望控制所有的節(jié)日彩燈。我們就創(chuàng)建一個(gè)動(dòng)作集來打開所有的節(jié)日彩燈,另外一個(gè)動(dòng)作集來關(guān)閉所有的節(jié)日彩燈。為了打開所有的節(jié)日彩燈,發(fā)送executeActionSet:completionHandler:消息給home對(duì)象,并傳遞"打開節(jié)日彩燈"動(dòng)作集。
用戶管理
創(chuàng)建home的用戶是該home的管理員,可以執(zhí)行所有操作,包括添加一個(gè)客人用戶到home。任何管理員添加到這個(gè)home的用戶(HMUser)都有一個(gè)有限的權(quán)限??腿瞬荒芨募彝サ牟季?,但是可以執(zhí)行下面的動(dòng)作:
- 識(shí)別智能電器
- 讀寫特性
- 觀察特性值變化
- 執(zhí)行動(dòng)作集
比如,一個(gè)家庭的戶主可以創(chuàng)建一個(gè)home布局并向其中添加家庭成員。每個(gè)家庭成員必須擁有一個(gè)iOS設(shè)備和Apple ID以及相關(guān)的iCloud賬戶。iCloud需要個(gè)人輸入的APPle ID和戶主提供的APPle ID 相吻合,以便讓他們?cè)L問這個(gè)home??紤]到隱私問題,Apple ID對(duì)你的App是不可見的。
管理員需要遵從一下步驟來添加一個(gè)客人到home中:
- 管理員調(diào)用一個(gè)動(dòng)作將客人添加到home中。
- 你的App調(diào)用addUserWithCompletionHandler:異步方法
- HomeKit展示一個(gè)對(duì)話框,要求輸入客人的Apple ID
- 用戶輸入客人的Apple ID
- 在完成回調(diào)中返回一個(gè)新的用戶
- 你的App展示客人的名字
添加一個(gè)客人到home,需要在客人的iOS設(shè)備上做一下操作:
用戶在iCloud偏好設(shè)置中輸入iClound憑證(Apple ID 和密碼)
用戶啟動(dòng)你的App
你的App通過home manager object 獲得一個(gè)home集合
如果iClound的憑證和管理員輸入的Apple ID相同,那么管理員的home將會(huì)出現(xiàn)在homes屬性中。
客人執(zhí)行的操作可能會(huì)失敗。如果一個(gè)異步方法中出現(xiàn)HMErrorCodeInsufficientPrivileges錯(cuò)誤碼的話,這就意味著用戶沒有足夠的權(quán)限來執(zhí)行動(dòng)作-也許這個(gè)用戶是客人,而不是管理員。
為了測(cè)試你的App是否正確處理了客人用戶,可參閱Testting Multiple iOS Devices and Users
添加和移除用戶
Xcode10, iOS12 使用下面方法:
//MARK:-添加刪除用戶
- (void)_addHomeUser
{
[self.currentHome manageUsersWithCompletionHandler:^(NSError * _Nullable error) {
if (error == nil)
{
// Successfully added a user
NSLog(@"---Successfully added a user");
}
else
{
// Unable to add a user
NSLog(@"---Unable to add a user---%@", error);
}
}];
NSLog(@"----users----%@", self.currentHome.users);
NSLog(@"---current-users----%@", self.currentHome.currentUser.name);
}
會(huì)彈出一個(gè)頁(yè)面進(jìn)行添加,這個(gè)地方添加時(shí),使用Apple ID

添加之后,你添加的用戶會(huì)收到一個(gè)推送消息,同意之后會(huì)看到我共享給他的所有home的信息,界面如下:

獲得用戶名
你的app對(duì)用戶名只有讀寫權(quán)限,并不能讀寫用戶的Apple ID。使用HMHome對(duì)象的users屬性來獲取用戶。使用HMUser類的name屬性來獲取用戶名。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
代碼示例
獲取homes
初始化之后我們?cè)谄浠卣{(diào)方法中可以獲取到manager.homes,這是一個(gè)數(shù)組,里邊是用戶的全部HMhome對(duì)象,我們可以遍歷這個(gè)數(shù)組獲取到全部的home,通過home.name得到home的名字。代碼如下:
#pragma mark - HMHomeManager delegate
- (void)homeManagerDidUpdateHomes:(HMHomeManager *)manager
{
NSLog(@"---primaryHome 主房間只有一個(gè)--");
NSLog(@"----已經(jīng)獲取到homes數(shù)據(jù),primary:是不是主房間---%@", manager.homes);
[[NSNotificationCenter defaultCenter] postNotificationName:@"gethomes" object:nil];
for (HMHome *home in manager.homes) {
NSLog(@"------查看home---%@", home);
NSLog(@"------查看home---%@", @(home.primary).stringValue);
}
}
- (void)homeManagerDidUpdatePrimaryHome:(HMHomeManager *)manager
{
NSLog(@"--已經(jīng)更新了primaryhome: %@", manager.primaryHome);
}
添加刪除home
使用homeManager對(duì)象來調(diào)用。這兩個(gè)方法同樣有兩個(gè)回調(diào)方法來告訴我們是否操作成功
- (void)initHomeKitManager
{
self.homeManager = [[HMHomeManager alloc] init];
self.homeManager.delegate = self;
}
#pragma mark - Home 添加刪除操作
- (void)addHome:(NSString *)homeName
{
[self.homeManager addHomeWithName:homeName completionHandler:^(HMHome * _Nullable home, NSError * _Nullable error) {
NSLog(@"----添加了home----%@----%@", home, home.name);
}];
}
- (void)removeHome:(HMHome *)home
{
[self.homeManager removeHome:home completionHandler:^(NSError * _Nullable error) {
NSLog(@"-----刪除了home---%@----%@", home, home.name);
}];
}
獲取room
獲取到HMhome對(duì)象之后可以通過home.rooms獲取到該home的全部room。同樣通過遍歷這個(gè)數(shù)組獲取到全部的HMroom對(duì)象,然后通過room.name獲取到room的名字
- (void)_updateCurrentHomesRooms:(HMHome *)home
{
NSLog(@"------開始更新rooms數(shù)據(jù)");
if (home.rooms.count)
{
self.roomArray = home.rooms;
for (NSInteger i = 0; i < self.roomArray.count; i++) {
HMRoom *room = self.roomArray[i];
NSLog(@"++++++%@", room.name);
}
}
else
{
self.roomArray = home.rooms;
}
[self.collectionView reloadData];
}
添加和移除room
同添加和移除home,不過是使用HMhome對(duì)象來調(diào)用
//刪除room
- (void)romoveRoom:(HMHome *)home
{
if (home.rooms.count > 0)
{
[home removeRoom:home.rooms.firstObject completionHandler:^(NSError * _Nullable error) {
NSLog(@"-----刪除了Room---%@----%@", home, home.rooms);
}];
}
}
//添加room
[self.currentHome addRoomWithName:newName completionHandler:^(HMRoom * _Nullable room, NSError * _Nullable error) {
if (!error) {
[weakSelf _updateCurrentHomesRooms:weakSelf.currentHome];
}
}];
Accessories
尋找一個(gè)新的accessory
Accessories封裝了物理配件的狀態(tài),因此它不能被用戶創(chuàng)建,也就是說我們不能去創(chuàng)建智能硬件對(duì)象,只能通過去搜尋它,然后添加。想要允許用戶給家添加新的配件,我們可以使HMAccessoryBrowser對(duì)象在后臺(tái)搜尋一個(gè)與home沒有關(guān)聯(lián)的配件,當(dāng)它找到配件的時(shí)候,系統(tǒng)會(huì)調(diào)用委托方法來通知你的應(yīng)用程序。
具體步驟如下:
- 設(shè)置 HMAccessoryBrowserDelegate的代理
- 添加一個(gè)HMAccessoryBrowser對(duì)象屬性
- (void)_initConfigure
{
self.browser = [[HMAccessoryBrowser alloc] init];
self.browser.delegate = self;
self.accessoryArray = [NSMutableArray array];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- 創(chuàng)建一個(gè)搜索按鈕,在按鈕的點(diǎn)擊方法里開始搜索硬件,需要實(shí)現(xiàn)代理方法
#pragma mark - HMAccessoryBrowser delegate
- (void)accessoryBrowser:(HMAccessoryBrowser *)browser didFindNewAccessory:(HMAccessory *)accessory
{
NSLog(@">>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<");
[self.accessoryArray addObject:accessory];
[self.tableView reloadData];
}
- (void)accessoryBrowser:(HMAccessoryBrowser *)browser didRemoveNewAccessory:(HMAccessory *)accessory
{
NSLog(@"-----一個(gè)硬件已經(jīng)移除了------");
}
- 點(diǎn)擊按鈕開始搜索新的智能硬件,如果搜索到硬件系統(tǒng)會(huì)通過didFindNewAccessory這個(gè)回調(diào)方法來通知我們發(fā)現(xiàn)了硬件,每次發(fā)現(xiàn)一個(gè)智能硬件這個(gè)方法都會(huì)調(diào)用一次
搜索結(jié)果如下:
2018-11-20 15:50:05.846864+0800 TETETE[14430:2872234] -------開始搜索設(shè)備----
2018-11-20 15:50:06.326251+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.330283+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.330490+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.330645+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.330839+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.331000+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.331163+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 15:50:06.331322+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
2018-11-20 16:02:06.950860+0800 TETETE[14430:2872234] >>>>>>>>>>>已經(jīng)發(fā)現(xiàn)了一個(gè)新的硬件<<<<<<<<<<<
對(duì)應(yīng)的設(shè)備信息

為新的Accessory對(duì)象,指定一個(gè)room
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//選取目標(biāo) 進(jìn)行配置
//獲取當(dāng)前選擇的外設(shè)對(duì)象
HMAccessory *acc = self.accessoryArray[indexPath.row];
__block HMHome *home = self.currentHome;
__weak typeof(self) weakSelf = self;
[self.currentHome addAccessory:acc completionHandler:^(NSError * _Nullable error) {
if (!error)
{
//如果添加home成功,將設(shè)備指定給某一個(gè)room
if (acc.room != weakSelf.currentRoom)
{
[home assignAccessory:acc toRoom:weakSelf.currentRoom completionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"---已經(jīng)將設(shè)備加入了%@",weakSelf.currentRoom.name);
}
else
{
NSLog(@"---指定房間失敗");
}
}];
}
}
else
{
NSLog(@"--添加硬件到home失敗--%@-", error);
}
}];
}
添加外設(shè)和指定room的方法都是由HMHome對(duì)象調(diào)用的。如果我們只是向home中添加了設(shè)備,沒有指定room那么它就會(huì)被放入到一個(gè)默認(rèn)的room中



移除一個(gè)已經(jīng)添加到home中的accessory對(duì)象
//MARK:- 移除一個(gè)已經(jīng)添加到home中的accessory對(duì)象
- (void)_updateAccessoryName:(HMAccessory *)accessory
{
[accessory updateName:@"測(cè)試Home" completionHandler:^(NSError * _Nullable error) {
if (error)
{
NSLog(@"-----更改名字失敗---");
}
else
{
NSLog(@"-----更改名字成功---");
}
}];
}
//MARK:- 移除Accessory
- (void)_removeAccessory:(HMAccessory *)accessory
{
[self.currentHome removeAccessory:accessory completionHandler:^(NSError * _Nullable error) {
if (error)
{
NSLog(@"-----移除設(shè)備失敗---");
}
else
{
NSLog(@"-----移除設(shè)備成功---");
}
}];
}
查看當(dāng)前的設(shè)備
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"-----w進(jìn)入了我的房間-------");
[self.currentRoom.accessories enumerateObjectsUsingBlock:^(HMAccessory * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"----當(dāng)前的設(shè)備----%@", obj.name);
}];
}
*** 注意事項(xiàng) ***
*** 使用真機(jī)調(diào)試,連接時(shí)要先打開手機(jī)藍(lán)牙
- 電腦使用WiFI 一定要和手機(jī)使用的WIFI同一個(gè),電腦的藍(lán)牙也打開
- 連接會(huì)不穩(wěn)當(dāng),時(shí)而可以連接,時(shí)而又會(huì)不彈出添加設(shè)備的界面
- 建議可以先使用手機(jī)自帶的家庭App進(jìn)行連接,如果手機(jī)自帶的App可以連接成功,再去測(cè)試你自己寫的Demo
- 一旦設(shè)備添加到Room中,再次搜素就不會(huì)再出現(xiàn)
使用APP實(shí)現(xiàn)對(duì)硬件對(duì)象的基本控制
我是這樣處理的,如果有設(shè)備了則給一個(gè)展示設(shè)備的頁(yè)面,如果沒有設(shè)備的,跳轉(zhuǎn)到搜索設(shè)備的界面,當(dāng)然你也可以都用一個(gè),只是測(cè)試Demo
在展示設(shè)備的頁(yè)面中

在tableView的cell點(diǎn)擊方法中,設(shè)置設(shè)備的代理,進(jìn)行對(duì)設(shè)備的值進(jìn)行讀寫操作,代碼如下:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//拿到只能硬件 首先 獲取硬件開啟的所有服務(wù)
HMAccessory *acc = self.currentRoom.accessories[indexPath.row];
NSArray *serviceArray = acc.services;
acc.delegate = self;
NSLog(@">>>>>這個(gè)外設(shè)中有%lu個(gè)服務(wù)>>>", serviceArray.count);
//遍歷所有的服務(wù), 獲取每個(gè)服務(wù)的特征
for (HMService *service in serviceArray) {
NSLog(@">>>>服務(wù)的名字>>%@", service.name);
/*
獲取該服務(wù)中所有的特性
判斷遍歷到的特征的讀寫屬性 然后賦值
通過打印可看到HMCharacteristic是可讀可寫的
*/
NSArray *arr = service.characteristics;
for (HMCharacteristic *chara in arr) {
NSLog(@"-0000000000000-特征為:%@", chara.properties);
if ([chara.properties isEqual:HMCharacteristicPropertyReadable])
{
self.charaWrite = chara;
}
else
{
self.charaRead = chara;
//接收外設(shè)的notifiy,類似于Ble開發(fā)中的通知
[self.charaRead enableNotification:YES completionHandler:^(NSError * _Nullable error) {
}];
}
}
if (self.charaRead)
{//判斷一下如果有這個(gè)讀寫特性的話 就讀取它的值
[self.charaRead readValueWithCompletionHandler:^(NSError * _Nullable error) {
if (!error)
{
/*
如果讀取成功那么打印該值,根據(jù)當(dāng)前值來改變外設(shè)狀態(tài)
如果服務(wù)是開關(guān),則讀取到的值是0(關(guān)閉) 1(打開)
*/
id value = self.charaRead.value;
NSLog(@"--讀取到了值%@", value);
if ([value intValue] == 0)
{//q讀取到的值如果是0 那么當(dāng)前狀態(tài)為關(guān)閉, 那么就寫入1
[self.charaRead writeValue:@(1) completionHandler:^(NSError * _Nullable error) {
if (!error)
{
NSLog(@"---寫入成功");
}
else
{
NSLog(@"---寫入失敗");
}
}];
}
else
{//讀取到的為1, 寫入0
[self.charaRead writeValue:@(0) completionHandler:^(NSError * _Nullable error) {
if (!error)
{
NSLog(@"---寫入成功");
}
else
{
NSLog(@"---寫入失敗");
}
}];
}
}
else
{
NSLog(@"---讀取失敗---");
}
}];
}
}
}
打印結(jié)果:
2018-11-21 16:57:51.869010+0800 TETETE[12656:1982261] >>>>服務(wù)的名字>>Switch 466044934
2018-11-21 16:57:51.869178+0800 TETETE[12656:1982261] -0000000000000-特征為:(
HMCharacteristicPropertyReadable
)
2018-11-21 16:57:51.869399+0800 TETETE[12656:1982261] -0000000000000-特征為:(
HMCharacteristicPropertyWritable,
HMCharacteristicPropertyReadable,
HMCharacteristicPropertySupportsEventNotification
)
2018-11-21 16:57:51.902866+0800 TETETE[12656:1982261] --讀取到了值0
2018-11-21 16:57:51.908635+0800 TETETE[12656:1982261] --讀取到了值0
2018-11-21 16:57:51.918117+0800 TETETE[12656:1982261] ---寫入成功
2018-11-21 16:57:51.919101+0800 TETETE[12656:1982261] ---寫入成功
當(dāng)你點(diǎn)擊cell的時(shí)候,進(jìn)行讀寫設(shè)備的數(shù)據(jù),可以通過HomeKit Accessory Simulator 模擬器看到界面上的控件(比如開關(guān)在變化)

------------我是分哥線------------------------
需要思考的小問題O(∩_∩)O,路過大神如果知道請(qǐng)賜教~~
Question:
1.添加過的設(shè)備,無(wú)法再次添加,即便把這個(gè)設(shè)備的room刪除了也不行
2.在現(xiàn)有Room,再添加一個(gè)其他的設(shè)備,則會(huì)無(wú)法控制設(shè)備開關(guān)



