Rust implementation of the Apple HomeKit Accessory Protocol (HAP).
This crate supports all HomeKit Services and Characteristics currently implemented by Apple and provides the ability to create custom Characteristics, Services and Accessories.
The HomeKit Accessory Protocol supports transports over IP and Bluetooth LE. Currently only the transport over IP is implemented in this crate. Accessories are exposed by the implemented HAP Accessory HTTP server and announced via built-in mDNS.
The HAP defines HomeKit enabled devices as virtual Accessories
that are composed of Services
that are composed of Characteristics
.
Characteristics hold values of various data types as well as optional metadata like max/min values or units. Services group Characteristics and represent features of the Accessory. Every Accessory consist of at least one Accessory Information Service
and any number of additional Services. For example a custom ceiling fan Accessory may consist of an Accessory Information Service
, a Fan Service
and a Lightbulb Service
.
Ceiling Fan Accessory
|
|-- Accessory Information Service
| |-- Identify Characteristic
| |-- Manufacturer Characteristic
| |-- Model Characteristic
| |-- Name Characteristic
| |-- Serial Characteristic
|
|-- Fan Service
| |-- On Characteristic
| |-- Rotation Direction Characteristic
| |-- Rotation Speed Characteristic
|
|-- Lightbulb Service
| |-- On Characteristic
| |-- Brightness Characteristic
| |-- Hue Characteristic
| |-- Saturation Characteristic
This crate provides a pre-built Accessory for every Service predefined by Apple. Custom Characteristics and Services can be created, assembled and used alongside the predefined ones.
For a full list of the predefined Characteristics, Services and Accessories, see the docs or Apple's official specification.
Creating a simple outlet Accessory and starting the IP transport:
```rust use hap::{ transport::{Transport, IpTransport}, accessory::{Category, Information, outlet}, Config, };
fn main() { let info = Information { name: "Outlet".into(), ..Default::default() };
let outlet = outlet::new(info).unwrap();
let config = Config {
name: "Outlet".into(),
category: Category::Outlet,
..Default::default()
};
let mut ip_transport = IpTransport::new(config).unwrap();
ip_transport.add_accessory(outlet).unwrap();
ip_transport.start().unwrap();
} ```
Dynamically adding and removing Accessories:
```rust use hap::{ transport::{Transport, IpTransport}, accessory::{Category, Information, bridge, outlet}, Config, };
fn main() { let bridge = bridge::new(Information { name: "Acme Bridge".into(), ..Default::default() }).unwrap();
let first_outlet = outlet::new(Information {
name: "Outlet 1".into(),
..Default::default()
}).unwrap();
let second_outlet = outlet::new(Information {
name: "Outlet 2".into(),
..Default::default()
}).unwrap();
let mut ip_transport = IpTransport::new(Config {
name: "Acme".into(),
category: Category::Outlet,
..Default::default()
}).unwrap();
let _bridge = ip_transport.add_accessory(bridge).unwrap();
let _first_outlet = ip_transport.add_accessory(first_outlet).unwrap();
let second_outlet = ip_transport.add_accessory(second_outlet).unwrap();
ip_transport.remove_accessory(&second_outlet).unwrap();
ip_transport.start().unwrap();
}
```
Using the Readable
and Updatable
traits to react to remote value reads and updates:
```rust use hap::{ transport::{Transport, IpTransport}, accessory::{Category, Information, outlet}, characteristic::{Readable, Updatable}, Config, HapType, };
pub struct VirtualOutlet { on: bool, }
impl Readable
impl Updatable
fn main() { let info = Information { name: "Outlet".into(), ..Default::default() };
let mut outlet = outlet::new(info).unwrap();
let virtual_outlet = VirtualOutlet { on: false };
outlet.inner.outlet.inner.on.set_readable(virtual_outlet.clone()).unwrap();
outlet.inner.outlet.inner.on.set_updatable(virtual_outlet).unwrap();
let config = Config {
name: "Outlet".into(),
category: Category::Outlet,
..Default::default()
};
let mut ip_transport = IpTransport::new(config).unwrap();
ip_transport.add_accessory(outlet).unwrap();
ip_transport.start().unwrap();
} ```
Setting a Characteristic value directly:
rust
outlet.inner.outlet.inner.on.set_value(true).unwrap();
Change dependent Characteristics on value changes:
```rust use std::{rc::Rc, cell::RefCell};
use hap::{ transport::{Transport, IpTransport}, accessory::{Category, Information, door}, characteristic::{Characteristic, Readable, Updatable}, Config, HapType, };
pub struct VirtualDoorInner { currentposition: u8, targetposition: u8, }
pub struct VirtualDoor {
inner: Arc
impl VirtualDoor {
pub fn new(inner: VirtualDoorInner, currentposition: Characteristic
impl Readable
impl Updatable
fn main() { let mut door = door::new(Information { name: "Door".into(), ..Default::default() }).unwrap(); let virtualdoor = VirtualDoor::new( VirtualDoorInner { currentposition: 0, targetposition: 0 }, door.inner.door.inner.currentposition.clone(), ); door.inner.door.inner.currentposition.setreadable(virtualdoor.clone()).unwrap(); door.inner.door.inner.currentposition.setupdatable(virtualdoor.clone()).unwrap(); door.inner.door.inner.targetposition.setreadable(virtualdoor.clone()).unwrap(); door.inner.door.inner.targetposition.setupdatable(virtualdoor).unwrap();
let config = Config {
name: "Door".into(),
category: Category::Door,
..Default::default()
};
let mut ip_transport = IpTransport::new(config).unwrap();
ip_transport.add_accessory(door).unwrap();
ip_transport.start().unwrap();
} ```
HAP is licensed under either of
at your option.