Skip to content

Commit

Permalink
Merge branch 'tv' #132
Browse files Browse the repository at this point in the history
  • Loading branch information
brutella committed Mar 26, 2019
2 parents 0f65b42 + e7eba10 commit af48562
Show file tree
Hide file tree
Showing 47 changed files with 4,344 additions and 3,159 deletions.
11 changes: 11 additions & 0 deletions .travis.yml
@@ -1,2 +1,13 @@
sudo: false
language: go
go:
- 1.11.x
- master
os:
- linux
- osx
dist: trusty
install: true
script:
- env GO111MODULE=on go build
- env GO111MODULE=on go test
140 changes: 75 additions & 65 deletions README.md
@@ -1,50 +1,72 @@
# HomeControl
# hc

[![Build Status](https://travis-ci.org/brutella/hc.svg)](https://travis-ci.org/brutella/hc)
[![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis]

HomeControl is an implementation of the [HomeKit][homekit] Accessory Protocol (HAP) to create your own HomeKit accessory in [Go](https://golang.org). [HomeKit][homekit] is a set of protocols and libraries to access devices for Home Automation. ~~The actual protocol documentation is only available to MFi members.~~ A non-commercial version of the documentation is now available on the [HomeKit developer website](https://developer.apple.com/homekit/).
`hc` is a lightweight framework to develop HomeKit accessories in Go.
It abstracts the **H**omeKit **A**ccessory **P**rotocol (HAP) and makes it easy to work with [services](service/README.md) and [characteristics](characteristic/README.md).

You can use this library to make existing Home Automation devices HomeKit compatible. I've already developed the following HomeKit bridges with in:
`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`.

- [LIFX](https://github.com/brutella/hklifx/)
- [UVR1611](https://github.com/brutella/hkuvr)
- [Fronius Symo](https://github.com/brutella/hksymo)

## HomeKit on iOS
**What is HomeKit?**

[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](https://developer.apple.com/homekit/).

HomeKit is fully integrated into iOS since iOS 8. Developers can use [HomeKit.framework](https://developer.apple.com/documentation/homekit) to communicate with accessories using high-level APIs.

<img alt="Home.app" src="_img/home-icon.png?raw=true" width="92" />

HomeKit is fully integrated since iOS 8. Developers can use the HomeKit framework to communicate with HomeKit using high-level APIs.
I've developed the [Home][home] app (for iPhone, iPad, Apple Watch) to control HomeKit accessories. If you [purchase Home][home-appstore] on the App Store, you not only support my work but also get an awesome iOS app. Thank you.
I've developed the [Home][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][home-appstore]. That would be awesome. ❤️

Once you've setup HomeKit, you can use Siri to interact with your accessories using voice command (*Hey Siri, turn off the lights in the living room*).
Checkout the official [website][home].

[home]: http://hochgatterer.me/home/
[home]: https://hochgatterer.me/home/
[home-appstore]: http://itunes.apple.com/app/id995994352
[GoDoc]: https://godoc.org/github.com/brutella/hc
[GoDoc Widget]: https://godoc.org/github.com/brutella/hc?status.svg
[Travis]: https://travis-ci.org/brutella/hc
[Travis Widget]: https://travis-ci.org/brutella/hc.svg

## Features

- Full implementation of the HAP in Go
- Supports all HomeKit [services and characteristics](service/README.md)
- Built-in service announcement via DNS-SD using [dnssd](http://github.com/brutella/dnssd)
- Runs on multiple platforms (already in use on Linux and OS X)
- Runs on linux and macOS
- Documentation: http://godoc.org/github.com/brutella/hc

## Getting Started

1. [Install Go](http://golang.org/doc/install)
2. [Setup Go workspace](http://golang.org/doc/code.html#Organization)
3. Create your own HomeKit bridge or clone an existing one (e.g. [hklight](https://github.com/brutella/hklight))
1. [Install](http://golang.org/doc/install) and [set up](http://golang.org/doc/code.html#Organization) Go
2. Create your own HomeKit accessory or clone an existing one (e.g. [hklight](https://github.com/brutella/hklight))

cd $GOPATH/src
# Clone project
git clone https://github.com/brutella/hklight && cd hklight
# Run the project
go run hklightd.go
make run

3. Pair with your HomeKit App of choice (e.g. [Home][home-appstore])

**Go Modules**

`hc` supports [Go module](https://github.com/golang/go/wiki/Modules) since `v1.0.0`.
Make sure to set the environment variable `GO111MODULE=on`.

4. Pair with your HomeKit App of choice (e.g. [Home][home-appstore])
## Example

## API Example
See [_example](_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*.

Expand All @@ -58,88 +80,76 @@ import (
)

func main() {
info := accessory.Info{
Name: "Lamp",
}
acc := accessory.NewSwitch(info)
// 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, acc.Accessory)
if err != nil {
log.Panic(err)
}
t, err := hc.NewIPTransport(config, ac.Accessory)
if err != nil {
log.Panic(err)
}

hc.OnTermination(func(){
<-t.Stop()
})

t.Start()
t.Start()
}
```

You should change some default values for your own needs
You can define more specific accessory info, if you want.

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

### Callbacks
### Events

You get a callback when the power state of a switch changed by a client.
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/on.go) characteristic value changes.

```go
acc.Switch.On.OnValueRemoteUpdate(func(on bool) {
if on == true {
log.Println("Client changed switch to on")
} else {
log.Println("Client changed switch to off")
}
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.

acc.Switch.On.SetValue(true)

A complete example is available in `_example/example.go`.

## Model

The HomeKit model hierarchy looks like this:

Accessory
|-- Accessory Info Service
| |-- Identify Characteristic
| |-- Manufacturer Characteristic
| |-- Model Characteristic
| |-- Name Characteristic
| |-- Serial Characteristic
|
|-- * Service
| |-- * Characteristic
```go
ac.Switch.On.SetValue(true)
```

HomeKit accessories are container for services. Every accessory must provide the `Accessory Information Service`. Every service provides one or more characteristics (a characteristic might be the power state of an outlet). HomeKit has predefined service and characteristic types, which are supported by iOS. You can define your own service and characteristic types, but it's recommended to use predefined ones.
## Accessory Architecture

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

HomeControl uses vendor directories (`vendor/`) to integrate the following libraries
For example a [lightbulb accessory](accessory/lightbulb.go) contains a [lightbulb service](service/lightbulb.go).
This service contains characteristics like [on](characteristic/on.go) and [brightness](characteristic/brightness.go).

- `github.com/tadglines/go-pkgs/crypto/srp` for *SRP* algorithm
- `github.com/agl/ed25519` for *ed25519* signature
- `github.com/gosexy/to` for type conversion
- `github.com/brutella/dnssd` for DNS service discovery
There are predefined accessories, services and characteristics available in HomeKit.
Those types are defined in the packages [accessory](accessory), [service](service), [characteristic](characteristic).

# Contact

Matthias Hochgatterer

Website: [http://hochgatterer.me](http://hochgatterer.me)
Website: [https://hochgatterer.me](https://hochgatterer.me)

Github: [https://github.com/brutella](https://github.com/brutella/)

Expand Down
File renamed without changes.
160 changes: 160 additions & 0 deletions _example/tv/main.go
@@ -0,0 +1,160 @@
package main

import (
"fmt"

"github.com/brutella/hc"
"github.com/brutella/hc/accessory"
"github.com/brutella/hc/characteristic"
"github.com/brutella/hc/log"
"github.com/brutella/hc/service"
)

func addInputSource(t *accessory.Television, id int, name string, inputSourceType int) {
in := service.NewInputSource()

in.Identifier.SetValue(id)
in.ConfiguredName.SetValue(name)
in.Name.SetValue(name)
in.InputSourceType.SetValue(inputSourceType)
in.IsConfigured.SetValue(characteristic.IsConfiguredConfigured)

t.AddService(in.Service)
t.Television.AddLinkedService(in.Service)

in.ConfiguredName.OnValueRemoteUpdate(func(str string) {
fmt.Printf(" %s configured name => %s\n", name, str)
})
in.InputSourceType.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s source type => %v\n", name, v)
})
in.IsConfigured.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s configured => %v\n", name, v)
})
in.CurrentVisibilityState.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s current visibility => %v\n", name, v)
})
in.Identifier.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s identifier => %v\n", name, v)
})
in.InputDeviceType.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s device type => %v\n", name, v)
})
in.TargetVisibilityState.OnValueRemoteUpdate(func(v int) {
fmt.Printf(" %s target visibility => %v\n", name, v)
})
in.Name.OnValueRemoteUpdate(func(str string) {
fmt.Printf(" %s name => %s\n", name, str)
})
}

// TODO
// - Refactoring how to store characteristic values
func main() {
log.Debug.Enable()

info := accessory.Info{
Name: "Television",
}
acc := accessory.NewTelevision(info)

acc.Television.Active.SetValue(characteristic.ActiveActive)
acc.Television.SleepDiscoveryMode.SetValue(characteristic.SleepDiscoveryModeAlwaysDiscoverable)
acc.Television.ActiveIdentifier.SetValue(1)
acc.Television.CurrentMediaState.SetValue(characteristic.CurrentMediaStatePause)
acc.Television.TargetMediaState.SetValue(characteristic.TargetMediaStatePause)

acc.Television.Active.OnValueRemoteUpdate(func(v int) {
fmt.Printf("active => %d\n", v)
})

acc.Television.ActiveIdentifier.OnValueRemoteUpdate(func(v int) {
fmt.Printf("active identifier => %d\n", v)
})

acc.Television.ConfiguredName.OnValueRemoteUpdate(func(v string) {
fmt.Printf("configured name => %s\n", v)
})
acc.Television.SleepDiscoveryMode.OnValueRemoteUpdate(func(v int) {
fmt.Printf("sleep discovery mode => %d\n", v)
})
acc.Television.Brightness.OnValueRemoteUpdate(func(v int) {
fmt.Printf("brightness => %d\n", v)
})
acc.Television.ClosedCaptions.OnValueRemoteUpdate(func(v int) {
fmt.Printf("closed captions => %d\n", v)
})
acc.Television.DisplayOrder.OnValueRemoteUpdate(func(v []byte) {
fmt.Printf("display order => %v\n", v)
})
acc.Television.CurrentMediaState.OnValueRemoteUpdate(func(v int) {
fmt.Printf("current media state => %d\n", v)
})
acc.Television.TargetMediaState.OnValueRemoteUpdate(func(v int) {
fmt.Printf("target media state => %d\n", v)
})

acc.Television.PowerModeSelection.OnValueRemoteUpdate(func(v int) {
fmt.Printf("power mode selection => %d\n", v)
})

acc.Television.PictureMode.OnValueRemoteUpdate(func(v int) {
fmt.Printf("PictureMode => %d\n", v)
})

acc.Television.RemoteKey.OnValueRemoteUpdate(func(v int) {
switch v {
case characteristic.RemoteKeyRewind:
fmt.Println("Rewind")
case characteristic.RemoteKeyFastForward:
fmt.Println("Fast forward")
case characteristic.RemoteKeyExit:
fmt.Println("Exit")
case characteristic.RemoteKeyPlayPause:
fmt.Println("Play/Pause")
case characteristic.RemoteKeyInfo:
fmt.Println("Info")
case characteristic.RemoteKeyNextTrack:
fmt.Println("Next")
case characteristic.RemoteKeyPrevTrack:
fmt.Println("Prev")
case characteristic.RemoteKeyArrowUp:
fmt.Println("Up")
case characteristic.RemoteKeyArrowDown:
fmt.Println("Down")
case characteristic.RemoteKeyArrowLeft:
fmt.Println("Left")
case characteristic.RemoteKeyArrowRight:
fmt.Println("Right")
case characteristic.RemoteKeySelect:
fmt.Println("Select")
case characteristic.RemoteKeyBack:
fmt.Println("Back")
}
})

config := hc.Config{Pin: "12344321", StoragePath: "./db"}
t, err := hc.NewIPTransport(config, acc.Accessory)

addInputSource(acc, 1, "HDMI 1", characteristic.InputSourceTypeHdmi)
addInputSource(acc, 2, "HDMI 2", characteristic.InputSourceTypeHdmi)
addInputSource(acc, 3, "Netflix", characteristic.InputSourceTypeApplication)
addInputSource(acc, 4, "YouTube", characteristic.InputSourceTypeApplication)
addInputSource(acc, 5, "Twitter", characteristic.InputSourceTypeApplication)
addInputSource(acc, 6, "Composite Video", characteristic.InputSourceTypeCompositeVideo)
addInputSource(acc, 7, "S-Video", characteristic.InputSourceTypeSVideo)
addInputSource(acc, 8, "Component Video", characteristic.InputSourceTypeComponentVideo)
addInputSource(acc, 9, "Dvi", characteristic.InputSourceTypeDvi)
addInputSource(acc, 10, "Airplay", characteristic.InputSourceTypeAirplay)
addInputSource(acc, 11, "Usb", characteristic.InputSourceTypeUsb)

if err != nil {
log.Info.Panic(err)
}

hc.OnTermination(func() {
<-t.Stop()
})

t.Start()
}
Binary file added _img/home-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit af48562

Please sign in to comment.