All Projects β†’ ewilken β†’ hap-rs

ewilken / hap-rs

Licence: other
Rust implementation of the Apple HomeKit Accessory Protocol (HAP)

Programming Languages

rust
11053 projects

Projects that are alternatives of or similar to hap-rs

ESP8266-HomeKit-Air-Quality-Sensor-Elgato-Eve-Room
ESP8266 based  Homekit Indoor Air Quality sensor that acts like Eve Room🌱
Stars: ✭ 58 (-50%)
Mutual labels:  apple, homekit
Sonoff Homekit
Make your Sonoff Switch compatible with Apple Homekit! πŸŽ‰
Stars: ✭ 722 (+522.41%)
Mutual labels:  apple, homekit
Esp32 Homekit
ESP-32 implementation of Apple Homekit Accessory Protocol(HAP)
Stars: ✭ 331 (+185.34%)
Mutual labels:  apple, homekit
homekit-qrcode
Generate a pairing HomeKit QR code label for your HomeKit accessory from the command line
Stars: ✭ 17 (-85.34%)
Mutual labels:  apple, homekit
equinociOS
O blog oficial do equinociOS
Stars: ✭ 71 (-38.79%)
Mutual labels:  apple
Clean-macOS
πŸ’» A simple script to setup a clean environment on macOS
Stars: ✭ 155 (+33.62%)
Mutual labels:  apple
Thoughtless
An iOS app that lets user quickly jot down thoughts with Markdown support
Stars: ✭ 24 (-79.31%)
Mutual labels:  apple
esp-homekit-direct
Connect HomeKit using esp8266 without HomeBridge
Stars: ✭ 72 (-37.93%)
Mutual labels:  homekit
home
Monorepo for all home automation related development, including integrated firmware, PCBs, configuration, and bridges
Stars: ✭ 104 (-10.34%)
Mutual labels:  homekit
apple-bce-arch
Arch Linux package for the Apple BCE driver required for T2-equipped devices.
Stars: ✭ 24 (-79.31%)
Mutual labels:  apple
node-apn-http2
Communicate with Apple Push Notification Service via native Node.js v8.8.1+ HTTP2 module (node-apn drop-in)
Stars: ✭ 25 (-78.45%)
Mutual labels:  apple
sign-in-with-apple-js-node-example
Sign in with Apple using Apple JS and REST API
Stars: ✭ 48 (-58.62%)
Mutual labels:  apple
HomeSpan
HomeKit Library for the Arduino-ESP32
Stars: ✭ 410 (+253.45%)
Mutual labels:  homekit
xiaomi-mi-air-purifier
Homebridge plugin for Xiaomi Mi Air Purifier
Stars: ✭ 67 (-42.24%)
Mutual labels:  homekit
TermiNetwork
🌏 A zero-dependency networking solution for building modern and secure iOS, watchOS, macOS and tvOS applications.
Stars: ✭ 80 (-31.03%)
Mutual labels:  apple
Learning-Core-Audio-Swift-SampleCode
Swift sample code for the book, Learning Core Audio. The original sample code was written in C/Objective-C but I tried to make it in Swift version.
Stars: ✭ 114 (-1.72%)
Mutual labels:  apple
GS-LOC
Apple geolocation services reverse engineering. Database scraper.
Stars: ✭ 36 (-68.97%)
Mutual labels:  apple
homebridge-ranger
A HomeKit range extender for Bluetooth Low Energy (BLE) accessories.
Stars: ✭ 65 (-43.97%)
Mutual labels:  homekit
sign-in-with-apple
An example for sign-in-with-apple, golang-version.
Stars: ✭ 22 (-81.03%)
Mutual labels:  apple
health kit reporter
A Flutter wrapper for the HealthKitReporter library
Stars: ✭ 16 (-86.21%)
Mutual labels:  apple

HAP (HomeKit Accessory Protocol)

CI crates.io docs.rs license: MIT/Apache-2.0

Rust implementation of the Apple HomeKit Accessory Protocol (HAP).

This crate supports all HomeKit services and characteristics currently implemented by Apple (on stable macOS versions) 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.

HomeKit Data Model

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 consists 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
|   |-- Power State Characteristic
|   |-- Rotation Direction Characteristic
|   |-- Rotation Speed Characteristic
|
|-- Lightbulb Service
|   |-- Power State Characteristic
|   |-- Brightness Characteristic
|   |-- Hue Characteristic
|   |-- Saturation Characteristic

This crate provides a pre-built accessory for every service predefined by Apple in the HomeKit Accessory Simulator as well as others like Television. 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.

Usage Examples

Creating a simple lightbulb accessory and starting the IP server

use tokio;

use hap::{
    accessory::{lightbulb::LightbulbAccessory, AccessoryCategory, AccessoryInformation},
    server::{IpServer, Server},
    storage::{FileStorage, Storage},
    Config,
    MacAddress,
    Pin,
    Result,
};

#[tokio::main]
async fn main() -> Result<()> {
    let lightbulb = LightbulbAccessory::new(1, AccessoryInformation {
        name: "Acme Lightbulb".into(),
        ..Default::default()
    })?;

    let mut storage = FileStorage::current_dir().await?;

    let config = match storage.load_config().await {
        Ok(mut config) => {
            config.redetermine_local_ip();
            storage.save_config(&config).await?;
            config
        },
        Err(_) => {
            let config = Config {
                pin: Pin::new([1, 1, 1, 2, 2, 3, 3, 3])?,
                name: "Acme Lightbulb".into(),
                device_id: MacAddress::new([10, 20, 30, 40, 50, 60]),
                category: AccessoryCategory::Lightbulb,
                ..Default::default()
            };
            storage.save_config(&config).await?;
            config
        },
    };

    let server = IpServer::new(config, storage).await?;
    server.add_accessory(lightbulb).await?;

    let handle = server.run_handle();

    std::env::set_var("RUST_LOG", "hap=debug");
    env_logger::init();

    handle.await
}

Setting sync callbacks to react to remote value reads and updates

use hap::characteristic::CharacteristicCallbacks;

lightbulb.lightbulb.power_state.on_read(Some(|| {
    println!("power_state characteristic read");
    Ok(None)
}));

lightbulb.lightbulb.power_state.on_update(Some(|current_val: &bool, new_val: &bool| {
    println!("power_state characteristic updated from {} to {}", current_val, new_val);
    Ok(())
}));

Setting async callbacks to react to remote value reads and updates

use hap::characteristic::AsyncCharacteristicCallbacks;

lightbulb.lightbulb.power_state.on_read_async(Some(|| {
    async {
        println!("power_state characteristic read (async)");
        Ok(None)
    }
    .boxed()
}));

lightbulb.lightbulb.power_state.on_update_async(Some(|current_val: bool, new_val: bool| {
    async move {
        println!("power_state characteristic updated from {} to {} (async)", current_val, new_val);
        Ok(())
    }
    .boxed()
}));

Setting a characteristic value directly

use hap::{
    characteristic::HapCharacteristic,
    serde_json::Value,
};

lightbulb.lightbulb.power_state.set_value(Value::Bool(true)).await.unwrap();

Interacting with accessories added to the server

Server::add_accessory returns a pointer to the accessory that can be used like this:

async {
    let accessory_ptr = server.add_accessory(accessory).await.unwrap();
}

Accessories behind the pointer are represented by the HapAccessory trait. The HapAccessory::get_service and HapAccessory::get_mut_service methods provide access to the services of the accessory, represented by the HapService trait. The HapService::get_characteristic and HapService::get_mut_characteristic methods provide access to the characteristics of the service, represented by the HapCharacteristic trait. All services and characteristics are identified by their HapType.

Accessing and changing the power_state characteristic of the lightbulb service of a lightbulb accessory would look like this:

use hap::{HapType, serde_json::Value};

async {
    let mut lightbulb_accessory = lightbulb_ptr.lock().await;

    let lightbulb_service = lightbulb_accessory.get_mut_service(HapType::Lightbulb).unwrap();
    let power_state_characteristic = lightbulb_service.get_mut_characteristic(HapType::PowerState).unwrap();

    power_state_characteristic.set_value(Value::Bool(true)).await.unwrap();
}

A full working example can be found here.

(Re-)Determining the IP to bind on

IP and port to serve on are set via the host and port fields of the Config struct. On config creation, if not explicitly set, the port defaults to 32000 and the IP is set to that of the first non-loopback network interface detected on the host. After config creation however, that IP isn't implicitly re-evaluated. To do so, an implementor has to explicitly call the redetermine_local_ip() method of the Config struct.

An example of doing that on every program restart while reloading a saved config:

let config = match storage.load_config().await {
    Ok(mut config) => {
        config.redetermine_local_ip(); // on config reload, the IP has to be explicitly redetermined
        let mut storage = FileStorage::current_dir().await?;
        config
    },
    Err(_) => {
        let config = Config {
            pin: Pin::new([1, 1, 1, 2, 2, 3, 3, 3])?,
            name: "Acme Outlet".into(),
            device_id: MacAddress::new([10, 20, 30, 40, 50, 60]),
            category: AccessoryCategory::Outlet,
            ..Default::default() // on config creation, the IP can be implicitly determined
        };
        let mut storage = FileStorage::current_dir().await?;
        config
    },
};

Development

Codegen is handled by the codegen crate in the workspace. Generated files are checked in. To run the code generation, do:

cargo run --package hap-codegen
cargo +nightly fmt

License

HAP is licensed under either of

at your option.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].