服务([HMService](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/cl/HMService))代表了一个配件(accessory)的某个功能和一些具有可读写的特性([HMCharacteristic](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMCharacteristic_Class/index.html#//apple_ref/occ/cl/HMCharacteristic))。一个配件可以拥有多项服务,一个服务也可以有很多特性。比如一个车库开门器可能拥有一个照明和开关的服务。照明服务可能拥有打开/关闭和调节亮度的特性。用户不能制造智能家电配件和它们的服务-配件制造商会制造配件和它们的服务-但是用户可以改变服务的特性。一些拥有可读写属性的特性代表着某种物理状态,比如,一个恒温器中的当前温度就是一个只可读的值,但是目标温度又是可读写的。苹果预先定义了一些服务和特性的名称,以便让Siri能够识别它们。
**获得配件的服务和属性**
在依照[Getting the Accessroties in a Room](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/HomeKitDeveloperGuide/FindingandAddingAccessories/FindingandAddingAccessories.html#//apple_ref/doc/uid/TP40015050-CH3-SW5)中描述,你创建了一个配件对象之后,你可以获得配件的服务和特性。当然你也可以直接从home中按照类型获得不同的服务。
重要:不要暴露匿名服务-比如固件升级服务-给用户
通过[HMAccessory](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMAccessory_Class/index.html#//apple_ref/occ/cl/HMAccessory)类对象的[services](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMAccessory_Class/index.html#//apple_ref/occ/instp/HMAccessory/services)属性,我们可以获得一个配件的服务。
~~~
NSArray *services = accessroy.services;
~~~
要获得一个home当中配件提供的特定服务,使用[HMHome](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/cl/HMHome)类对象的[servicesWithTypes:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/instm/HMHome/servicesWithTypes:)方法。
~~~
// Get all lights and thermostats in a home
NSArray *lightServices = [home servicesWithTypes:[HMServicesTypeLightbulb]];
NSArray *thermostatServices = [home servicesWithTypes:[HMServicesTypeThermostat]];
~~~
使用[HMServices](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/cl/HMService)类对象的[name](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/instp/HMService/name)属性来获得服务的名称
~~~
NSString *name = services.name;
~~~
要获得一个服务的特性,请使用[characteristics](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/instp/HMService/characteristics)属性。
~~~
NSArray *characteristics = service.characteristics
~~~
使用[servicesType](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/instp/HMService/serviceType)属性来获得服务的类型
NSString *serviceType = service.serviceType;
苹果定义了一些服务类型,并能被Siri识别:
* 门锁(Door locks)
* 车库开门器(Garage door openers)
* 灯光(Lights)
* 插座(Outlets)
* 恒温器(Thermostats)
**改变服务名称**
使用[updateName:completionHandler:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/instm/HMService/updateName:completionHandler:)异步方法来改变服务名称。传入此方法的服务名称参数必须在一个home当中是唯一的,并且服务名可被Siri识别。
~~~
[service updateName:@"Garage 1 Opener" completionHandler:^(NSError *error) {
if (error) {
// Failed to change the name
} else {
// Successfully changed the name
}
}];
~~~
**访问特性的值**
特性代表了一个服务的一个参数,它要么是只读、可读写或者只写。它提供了这个参数可能的值的信息,比如,一个布尔或者一个范围值。恒温器中的温度就是只读的,而目标温度又是可读写的。一个执行某个任务的命令且不要求任何返回-比如播放一段声音或者闪烁一下灯光来确认某个配件-可能就是只写的。
苹果定义了一些特性的类型,并能被Siri识别:
* 亮度(Brightness)
* 最近温度(Current temperature)
* 锁的状态(Lock state)
* 电源的状态(Power state)
* 目标状态(Target state)
* 目标温度(Target temperature)
比如,对于一个车库开门器来说,目标状态就是打开或者关闭。对于一个锁来说,目标状态又是上锁和未上锁。
在你获得了一个[HMService](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/cl/HMService)对象之后,如 [Getting Services and Their Properties](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/HomeKitDeveloperGuide/AccessingServicesandTheirCharacteristics/AccessingServicesandTheirCharacteristics.html#//apple_ref/doc/uid/TP40015050-CH6-SW2)所描述的,你可以获得每个服务的特性的值。因为这些值是从配件中获得的,这些读写的方法都是异步的,并可以传入一个完成回调的block。
使[用readValueWithCompletionHandler:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMCharacteristic_Class/index.html#//apple_ref/occ/instm/HMCharacteristic/readValueWithCompletionHandler:)异步方法来读取一个特性的值。
~~~
[characteristic readValueWithCompletionHandler:^(NSError *error) {
if (error == nil) {
// Successfully read the value
id value = characteristic.value;
}
else {
// Unable to read the value
} }];
~~~
在if语句块中,加入你的代码以更新app的视图。
使用[writeValue:completionHandler:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMCharacteristic_Class/index.html#//apple_ref/occ/instm/HMCharacteristic/writeValue:completionHandler:)异步方法来向一个特性写入值。
~~~
[self.characteristic writeValue:@42 withCompletionHandler:^(NSError *error) {
if (error == nil) {
// Successfully wrote the value
}
else {
// Unable to write the value
} }];
~~~
不要以为函数调用完成就意味着写入成功,实际上只有在当完成回调执行并没有错误产生时才表示写入成功。比如,直到一个开关的特性改变之前都不要改变这个开关的状态。在if语句块中,加入你的代码,以更新app的视图。
另外,在别的app更新了特性的值时也需要更新视图,在[Observing Changes to Accessories](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/HomeKitDeveloperGuide/RespondingtoHomeKitDatabaseChanges/RespondingtoHomeKitDatabaseChanges.html#//apple_ref/doc/uid/TP40015050-CH5-SW1)中有描述。
**创建服务组**
一个服务组([HMServiceGroup](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMServiceGroup_Class/index.html#//apple_ref/occ/cl/HMServiceGroup))提供了控制不同配件的任意数量服务的快捷方式-比如,当用户离开家之后控制家中的某些灯。
![405.png](https://box.kancloud.cn/2015-08-08_55c5769bb36e0.png "1427340681778912.png")
在你创建了一个[HMHome](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/cl/HMHome)对象之后,如[Getting the Primary Home and Collection of Homes](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/HomeKitDeveloperGuide/FindingandAddingAccessories/FindingandAddingAccessories.html#//apple_ref/doc/uid/TP40015050-CH3-SW3)中描述,你也就在这个家中创建一个服务组。
为了创建一个服务组,我们使用[HMHome](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/cl/HMHome)类对象的[addServiceGroupWithName:completionHandler:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/instm/HMHome/addServiceGroupWithName:completionHandler:)方法。方法中参数服务组的名称必须在此家中唯一,并可以被Siri识别。
~~~
[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](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMServiceGroup_Class/index.html#//apple_ref/occ/cl/HMServiceGroup)类对象的[addService:completionHandler:](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMServiceGroup_Class/index.html#//apple_ref/occ/instm/HMServiceGroup/addService:completionHandler:)方法来向服务组中添加一个服务。服务可以在一个或多个服务组中。
~~~
[serviceGroup addService:service completionHandler:^(NSError *error) {
if (error == nil) {
// Successfully added service to service group
}
// Unable to add the service to the service group
}];
~~~
通过[HMHome](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/cl/HMHome)类对象的[serviceGroups](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMHome_Class/index.html#//apple_ref/occ/instp/HMHome/serviceGroups)属性,来获得这个家的所有服务组。
~~~
NSArray *serviceGroups = self.home.serviceGroups;
~~~
通过[HMServiceGroup](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMServiceGroup_Class/index.html#//apple_ref/occ/cl/HMServiceGroup)类对象的[accessory](https://developer.apple.com/library/ios/documentation/HomeKit/Reference/HMService_Class/index.html#//apple_ref/occ/instp/HMService/accessory)属性,我们获得服务所对应的智能电器。
~~~
HMAccessory *accessory = service.accessory;
~~~
和配件类似,代理方法在别的app改变服务组时也会被调用。如果你的app使用了服务组,请阅读HMHomeDelegate Protocol Reference文档,获悉你应该实现哪些方法以观察这些变化。