hc is a lightweight framework to develop HomeKit accessories in Go.

Related tags

go homekit hap


GoDoc Widget Travis Widget

hc is a lightweight framework to develop HomeKit accessories in Go. It abstracts the HomeKit Accessory Protocol (HAP) and makes it easy to work with services and characteristics.

hc handles the underlying communication between HomeKit accessories and clients. You can focus on implementing the business logic for your accessory, without having to worry about the protocol.

Here are some projects which use hc.

What is HomeKit?

HomeKit is a set of protocols and libraries from Apple. It is used by Apple's platforms to communicate with smart home appliances. A non-commercial version of the documentation is now available on the HomeKit developer website.

HomeKit is fully integrated into iOS since iOS 8. Developers can use HomeKit.framework to communicate with accessories using high-level APIs.


I've developed the Home+ app to control HomeKit accessories from iPhone, iPad, and Apple Watch. If you want to support hc, please purchase Home from the App Store. That would be awesome. ❤️

Checkout the official website.


Getting Started

  1. Install and set up Go

  2. Create your own HomeKit accessory or clone an existing one (e.g. hklight)

     cd $GOPATH/src
     # Clone project
     git clone https://github.com/brutella/hklight && cd hklight
     # Run the project
     make run
  3. Pair with your HomeKit App of choice (e.g. Home)

Go Modules

hc supports Go module since v1.0.0. Make sure to set the environment variable GO111MODULE=on.


See _example for a variety of examples.

Basic switch accessory

Create a simple on/off switch, which is accessible via IP and secured using the pin 00102003.

package main

import (

func main() {
    // create an accessory
    info := accessory.Info{Name: "Lamp"}
    ac := accessory.NewSwitch(info)
    // configure the ip transport
    config := hc.Config{Pin: "00102003"}
    t, err := hc.NewIPTransport(config, ac.Accessory)
    if err != nil {

You can define more specific accessory info, if you want.

info := accessory.Info{
    Name: "Lamp",
    SerialNumber: "051AC-23AAM1",
    Manufacturer: "Apple",
    Model: "AB",
    FirmwareRevision: "1.0.1",


The library provides callback functions, which let you know when a clients updates a characteristic value. The following example shows how to get notified when the On characteristic value changes.

ac.Switch.On.OnValueRemoteUpdate(func(on bool) {
    if on == true {
        log.Println("Switch is on")
    } else {
        log.Println("Switch is off")

When the switch is turned on "the analog way", you should set the state of the accessory.


Multiple Accessories

When you create an IP transport, you can specify more than one accessory like this

bridge := accessory.NewBridge(...)
outlet := accessory.NewOutlet(...)
lightbulb := accessory.NewColoredLightbulb(...)

hc.NewIPTransport(config, bridge.Accessory, outlet.Accessory, lightbulb.Accessory)

By doing so, the bridge accessory will become a HomeKit bridge. The outlet and lightbulb are the bridged accessories.

When adding the accessories to HomeKit, iOS only shows the bridge accessory. Once the bridge was added, the other accessories appear automatically.

HomeKit requires that every accessory has a unique id, which must not change between system restarts. hc automatically assigns the ids for you based on the order in which the accessories are added to the bridge.

But I recommend that you specify the accessory id yourself, via the accessory.Config.ID field, like this.

bridge := accessory.NewBridge(accessory.Info{Name: "Bridge", ID: 1})
outlet := accessory.NewOutlet(accessory.Info{Name: "Outlet", ID: 2})
lightbulb := accessory.NewColoredLightbulb(accessory.Info{Name: "Light", ID: 3})

Accessory Architecture

HomeKit uses a hierarchical architecture to define accessories, services and characeristics. At the root level there is an accessory. Every accessory contains services. And every service contains characteristics.

For example a lightbulb accessory contains a lightbulb service. This service contains characteristics like on and brightness.

There are predefined accessories, services and characteristics available in HomeKit. Those types are defined in the packages accessory, service, characteristic.


Matthias Hochgatterer

Website: https://hochgatterer.me

Github: https://github.com/brutella

Twitter: https://twitter.com/brutella


hc is available under the Apache License 2.0 license. See the LICENSE file for more info.

  • iPhone on iOS 9 can't reconnect to bridge

    iPhone on iOS 9 can't reconnect to bridge

    iOS 9 connects and findes products in home app, after connection is lost (wifi | flight mode toggle on/off) and products remains disconnected..

    Philips hue bridge connects right away (so no ios9/ iPhone problem)

    opened by djeclemen 46
  • IP-cam support / video / image

    IP-cam support / video / image

    Hi Matthias, did you play with camera connected to Homekit? Do you have any example how to set up? I would like to add my local IPcam - and look at it, when is e.g. motion detected. Maybe good start can be to see a picture from can, in my case located here:

    Do you have any Idea how to do it?

    Thanks M.,-

    opened by mkrcek 17
  • Add Characteristic.OnValueGet hook

    Add Characteristic.OnValueGet hook

    This change proposes a characteristic hook that is invoked when /characteristics is accessed.

    The is useful when the underlying accessory does not implement a change event system and must be polled. High frequency polling may also be excessive when the value is not changing all that often. Such a hook allows for a hybrid approach of low frequency polling and higher frequency checking when a user is actively engaging with the Home app.

    acc.Switch.On.OnValueGet(func() {
      // sync value check before responding
    acc.Switch.On.OnValueGet(func() {
      // or kick off an async value check between polling intervals
      go fetchLatestSwitchValue()

    The HAP-NodeJS library offers a similar getCharacteristic hook.

    opened by josh 14
  • the 8th device is always

    the 8th device is always "Not Supported"

    Hi Matthias,

    I'm using your library for a home project, and I see that if I add more than 7 devices, the 8th device is always broken (It gives an error while adding the accessory to Home) and in Home, it shows up as "Not Supported" with a Home Icon.

    The code to reproduce it is shown in this gist: https://gist.github.com/gerardbos/df971f24eef9a8a41b5fccc86d87d7bf

    17-11-08 11-11-35 0503

    opened by gerardbos 13
  • High latency client can crash the bridge

    High latency client can crash the bridge

    If the client is under enough packet loss or experiencing high enough latency, the following happens to me:

    2015/07/18 19:28:44 [ERRO] Decryption failed: read tcp operation timed out 2015/07/18 19:28:44 [ERRO] Decryption failed: use of closed network connection 2015/07/18 19:28:44 [INFO] Close connection and remove session

    The server is then dead and must be halted and resumed. If this is a fatal error, it should be handled as such and the program should terminate. If not, the connection should be retried. I haven't gotten deep enough into netio to see where to handle this better, and this is my first real foray into reading someone else's go.

    opened by bwdezend 13
  • OnValueRemoteUpdate error

    OnValueRemoteUpdate error

    Hello! I'm think commits on 3 feb 2020 cause some errors With last version of sources, when I'm open Homekit App , hc calls "OnValueRemoteUpdate" method on opening Homekit app. But this method should be called only of user press something on device icon With "git checkout" to 27 jan 2020 it works fine.

    opened by reejinbouk 12
  • Multiple devices

    Multiple devices

    Hey all! I am so happy I found this project!!! :-) I don't have exactly "an issue" but rather a very noob question, need small help and don't know where else to ask... I am trying to create multiple accessories at the same time as it seems to be supported by the ip transport: "The transports can contain more than one accessory. If this is the case, the first accessory acts as the HomeKit bridge." ...yet I got stuck here... I have a map of the accessories (my code needs to store the device name and its unique address I am passing to the ON/OFF commands which communicate further via telnet), but I am not sure how to properly construct multiple accessories from this map and pass all the accessories to the transport... My idea is to build a slice and pass it to the transport, yet the is the place where I got unsure:

    1. The example creates acc := accessory.NewLightbulb(info), but what if I have more light bulbs? Should I create a slice and append all these acc's (if so then of what type this slice should be?)? I tried to create a slice var lampSlice []*accessory.Lightbulb and fed my custom lightbulbs into this slice, yet I can't use this slice in NewIPTransport() and this function expects *accessory.Accessory.......
    2. how to handle functions like acc.Lightbulb.On.OnValueRemoteUpdate(func(on bool) {} which are created for the accessory which are created for the specific accessory?

    Is there an example of creating multiple accessories I could check and consult with? Thanks a lot!

    opened by maaraneasi 12
  • NewIPTransport seems not to work.

    NewIPTransport seems not to work.

    When I try the lightbulb example I get the following error:

    #github.com/brutella/hc ../../../github.com/brutella/hc/ip_transport.go:296:12: undefined: dnssd.Config ../../../github.com/brutella/hc/ip_transport.go:304:15: assignment mismatch: 2 variables but 1 values ../../../github.com/brutella/hc/ip_transport.go:304:34: not enough arguments in call to dnssd.NewService

    opened by cpp 11
  • example describing how to modify a characteristic

    example describing how to modify a characteristic

    It'd be nice to have an example laying out exactly how to change the value of a characteristic on a device. That is, being able to turn on a lamp or tv or some such.

    The projects and examples given all seem to either read data from homekit devices or expose non-homekit devices using homekit, but none seem to write back to those devices using HAP.

    opened by jmhodges 10
  • Multiple devices

    Multiple devices

    How would one add multiple devices at once?

    When I run hap.NewIPTransport("00102003", sw.Accessory) for every device, I get:

    2015/07/15 21:49:18 [ERR] bonjour: Failed to bind to udp6 port: listen udp6 :5353: bind: address already in use
    opened by bahlo 10
  • TemperatureSensor steps ignored

    TemperatureSensor steps ignored

    Temperature values are always rounded to 0.5 values. So a temperature of 19.8° will be 20.0° and a temperature of 19.6° will become 19.5° (Celsius units)

    I'm just using: t := accessory.NewTemperatureSensor(createInfo("temp", 1234), 0, -55, 55, 0.1) t.TempSensor.CurrentTemperature.SetValue(19.8)

    The step value seems to be ignored at all. I would like to see the exact value 19.8° instead of 20.0°. Any fix on this?

    opened by jurjevic 0
  • bridge must not expose more than 150 objects

    bridge must not expose more than 150 objects

    A bridge must not expose more than 150 HAP accessory objects. (HomeKit Accessory Protocol Specification R2)

    func NewIPTransport ... t.addAccessory(a) for n, a := range as { if n > 150 { log.Info.Printf("a bridge must not expose more than 150 hap accessory objects.\n") break } t.addAccessory(a) } ...

    opened by alpr777 0
  • Add

    Add "Valid Values" and "Valid Value Range" characteristic objects

    As described in "6.3.3 Characteristic Objects" section of the HAP Specification.

    This is required for characteristics that are enumeration values and when a service doesn't support all the possible enumeration values.

    This has been tested with the following code (for which it was required) with an iOS client and results in the expected behavior:

    ss := service.NewSecuritySystem()

    This descriptor should only be used for UInt8 and for Apple-defined characteristics, but this is not enforced in this PR.

    opened by rblenkinsopp 0
  • not support utf-8 chinese language

    not support utf-8 chinese language

    Homekit couldn't find the accessory when I named it in Chinese

    	info := accessory.Info{
    		Name:         "车库门",
    		SerialNumber: "051AC-23AAN1",
    		Manufacturer: "Apple",
    		Model:        "AB",
    opened by zeropool 0
  • Fix type name in readme

    Fix type name in readme

    This PR changes accessory.Config to accessory.Info (as the type is currently called) in the readme.

    opened by hirschsn 0
  • multiple accessories events

    multiple accessories events

    for k, v := arr {
      as := accessory.New(v.Conf, v.Type)
        _ = s.Container.AddAccessory(as)

    I add accessories in a loop, but I find that I don't know how to set a callback for each accessory.

    Can someone help.

    opened by zuozhehao 0
  • Build window fail

    Build window fail

    DEBUG 2021/06/11 09:05:33 main.go:39: Switch is on
    INFO 2021/06/11 09:05:33 ip_transport.go:184: Listening on port 12345
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal 0xc0000005 code=0x0 addr=0x10 pc=0x57244d]
    goroutine 9 [running]:
    github.com/brutella/dnssd.(*Service).IPsAtInterface(0xc0000756c0, 0x0, 0xc0001b6320, 0x4, 0x6cb924)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/service.go:150 +0x4d
    github.com/brutella/dnssd.A(0xc0001b6320, 0x4, 0x6cb924, 0x9, 0x6ca58d, 0x5, 0xc0001b6324, 0xc, 0xc0001acd20, 0x0, ...)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/dns.go:116 +0x5f
    github.com/brutella/dnssd.containsConflictingAnswers(0xc000004be8, 0xc0000064b0, 0x31a64c)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/responder.go:465 +0xc5
    github.com/brutella/dnssd.findConflicts(0xc000004be8, 0xc0003b8010, 0x1, 0x1, 0x2, 0x2, 0xc000075b38)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/responder.go:446 +0xa5
    github.com/brutella/dnssd.(*responder).handleRequest(0xc00012a780, 0xc000004be8)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/responder.go:238 +0x99
    github.com/brutella/dnssd.(*responder).respond(0xc00012a780, 0x747718, 0xc00005ab40, 0x0, 0x0)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/responder.go:203 +0x19d
    github.com/brutella/dnssd.(*responder).Respond(0xc00012a780, 0x747718, 0xc00005ab40, 0x0, 0x0)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/responder.go:113 +0x48c
    github.com/brutella/hc.(*ipTransport).Start.func1(0xc00012e240, 0x747718, 0xc00005ab40, 0xc0000618c0)
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/ip_transport.go:168 +0x59
    created by github.com/brutella/hc.(*ipTransport).Start
    	D:/*******/pkg/mod/github.com/brutella/[email protected]/ip_transport.go:167 +0x327
    Process finished with the exit code 2
    opened by zuozhehao 1
  • Characteristic: Button Event

    Characteristic: Button Event

    Am I correct that the Button Event characteristic (0x126) isn't supported yet?

    opened by D-r-P-3-p-p-3-r 0
  • specify advertised IP for dnssd

    specify advertised IP for dnssd

    Hello, in some cases it is necessary to advertise a different IP to the one belonging to the host where the service is running. For instance if you are running the service in a docker container without using host networking or if you need the service outside of the connected network but routing isn't an option and you need to rely on NAT instead.

    Currently, dnssd supports specifying an arbitrary IP and hc actually sets the necessary parameter if you set the configuration option Config.IP. This works great, however, Config.IP's documentation states it is a deprecated option. As I think running an hc service inside docker is probably a common use case I'd suggest removing the deprecation notice in the Config.IP option. Alternatively, allowing a custom dnssd configuration but I don't think there are any other parameters that might be worth overriding so the first option seems more convenient. A third option would be to allow specifying a dns entry and resolve it instead of passing an IP address, this would keep things dynamic while allowing you to override the advertised IP.

    Thanks for the awesome work :)

    Regards David

    opened by dbarrosop 0
  • Issues with dnssd v1.2.0

    Issues with dnssd v1.2.0

    Hi everybody,

    I'm facing issues with commit 4da759009452b2863db09ff6b03c349103ec5bd6. I haven't found the root cause, but it seems that dnssd detects conflicts even if there are none.

    • conflicts are always detected; the first name passing is with "(2)" added
    • since it's adding braces, the name might become invalid for Home

    https://github.com/brutella/dnssd/blob/665cf86dce8e8a8b4c6164e2ac038d6b667dc80b/probe.go#L77 https://github.com/brutella/dnssd/blob/665cf86dce8e8a8b4c6164e2ac038d6b667dc80b/probe.go#L83

    I tried patching the format, but still the .local address is being incremented. Reverting 4da759009452b2863db09ff6b03c349103ec5bd6 works for me.

    opened by mrlnc 3
  • v1.2.3(Oct 22, 2020)

  • v1.2.2(May 11, 2020)

    • Add window accessory
    • Add public method to get X-HM uri from ip transport config
    • Use crypto/ed25519 instead of github.com/agl/ed25519 (requires Go 1.13)
    • Use device id as hostname to silence avahi
    Source code(tar.gz)
    Source code(zip)
  • v1.2.1(Jan 27, 2020)

    • Add accessory id to config struct
    • Change value type of ids from int64 to uint64
    • Change Lightbulb service to only include On characteristic. Use ColoredLightbulb instead.
    • Create new colored lightbulb accessory.
    • Generate X-HM URI which are accessible via the ip transport hc.Transport.XHMURI().
    • Removes accents from published dns-sd service name
    • Add separate header and cooler services
    • Use github.com/xiam/to instead of github.com/gosexy/to
    • Removes vendor directory
    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Jun 21, 2019)

  • v1.1.0(Apr 29, 2019)

    This release includes additions for working with IP cameras.

    There is now a

    • tlv8 package to encode and decode structs
    • rtp package which defines structs for IP cameras
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Mar 26, 2019)

  • v0.1.0(Apr 7, 2016)

    The model package is removed completely. This changes the import paths of the accessory, service, and characteristic packages, which are now in the library's root directory.

    Also the code for all services and characteristics is now auto-generated by importing the data from the metadata plist file in the HAPAccessoryKit.framework (see gen and cmd package). We can now import new types more easily in the future.

    Existing code should migrate to the new version by replace import paths containing github.com/brutella/hc/model* with github.com/brutella/hc/*

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Nov 23, 2015)

Developing Home Automation software (HomeKit, KNX).
Squzy - is a high-performance open-source monitoring, incident and alert system written in Golang with Bazel and love.

Squzy - opensource monitoring, incident and alerting system About Squzy - is a high-performance open-source monitoring and alerting system written in

Squzy 365 Sep 13, 2021
A modern layer 7 load balancer from baidu

BFE BFE is a modern layer 7 load balancer from baidu. Advantages Multiple protocols supported, including HTTP, HTTPS, SPDY, HTTP2, WebSocket, TLS, Fas

null 5k Sep 23, 2021
🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go./ gnet 是一个高性能、轻量级、非阻塞的事件驱动 Go 网络框架。

English | ???? 中文 ?? Introduction gnet is an event-driven networking framework that is fast and lightweight. It makes direct epoll and kqueue syscalls

Andy Pan 5.2k Sep 24, 2021
A LWM2M Client and Server implementation (For Go/Golang)

Betwixt - A LWM2M Client and Server in Go Betwixt is a Lightweight M2M implementation written in Go OMA Lightweight M2M is a protocol from the Open Mo

Zubair Hamed 51 Aug 28, 2021
NFF-Go -Network Function Framework for GO (former YANFF)

Network Function Framework for Go (former YANFF) Wonderful news : we are now supporting AF_XDP and supporting(almost) getting packets directly from Li

Intel Go Team 1.1k Sep 15, 2021
Fast and Scalable RPC Framework

Rony (Fast and Scalable RPC Framework) About Rony lets you create a clustered aware service easily. Checkout Wiki Performance Rony is very fast and wi

Ronak Software Group 35 Sep 20, 2021
⚡️Lightweight framework for microservices & web services in golang

Quickstart Zepto is a lightweight framework for the development of microservices & web services in golang. As an opinionated framework, zepto proposes

null 104 Sep 19, 2021
Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http

fasthttp Fast HTTP implementation for Go. Currently fasthttp is successfully used by VertaMedia in a production serving up to 200K rps from more than

Aliaksandr Valialkin 16.1k Sep 24, 2021
A toy MMO example built using Ebiten and WebRTC DataChannels (UDP)

Ebiten WebRTC Toy MMO ⚠️ This is a piece of incomplete hobby work and not robust. Please read the "Why does this project exist?" section. What is this

Jae Bentvelzen 8 Jun 3, 2021
A modern, fast and scalable websocket framework with elegant API written in Go

About neffos Neffos is a cross-platform real-time framework with expressive, elegant API written in Go. Neffos takes the pain out of development by ea

Gerasimos (Makis) Maropoulos 367 Sep 16, 2021
Fast Static File Analysis Framework

Florentino; Fast Static File Analysis Framework Story Florentino is named after a fiction warrior. Flarentino: "I'd wear a fedora but they haven't inv

null 96 Jul 8, 2021
Netpoll is a high-performance non-blocking I/O networking framework, which focused on RPC scenarios, developed by ByteDance.

Netpoll is a high-performance non-blocking I/O networking framework, which focused on RPC scenarios, developed by ByteDance. RPC is usually heavy on processing logic and therefore cannot handle I/O serially. But Go's standard library net designed blocking I/O API, so that the RPC framework can only follow the One Conn One Goroutine design.

CloudWeGo 1.7k Sep 19, 2021

JUPITER: Governance-oriented Microservice Framework Introduction JUPITER is a governance-oriented microservice framework, which is being used for year

Douyu 3.2k Sep 21, 2021
Centralized Configuration System written in Golang - Spring cloud compatible

Centralized Configuration System What is Vecosy Vecosy is a configuration service exposed through REST/GRPC. Is Spring Cloud Conf compatible and also

null 17 Aug 4, 2021
GO2P is a P2P framework, designed with flexibility and simplicity in mind

go2p golang p2p framework By v-braun - viktor-braun.de. Description GO2P is a P2P framework, designed with flexibility and simplicity in mind. You can

Viktor Braun 83 Jun 3, 2021
Pglet server and client

Pglet - Web UI framework for backend developers Build web apps like a frontend pro in the language you already know. No knowledge of HTML, CSS or Java

Web UI framework for backend developers 66 Sep 11, 2021
A simple RPC framework with protobuf service definitions

Twirp is a framework for service-to-service communication emphasizing simplicity and minimalism. It generates routing and serialization from API defin

Twitch 5.1k Sep 20, 2021
🚀Gev is a lightweight, fast non-blocking TCP network library based on Reactor mode. Support custom protocols to quickly and easily build high-performance servers.

gev 中文 | English gev is a lightweight, fast non-blocking TCP network library based on Reactor mode. Support custom protocols to quickly and easily bui

徐旭 1.2k Sep 14, 2021