documented general use cases

This commit is contained in:
Sam Lewis
2022-11-02 00:04:40 -04:00
parent b0804e39d0
commit 33a92d96bd
4 changed files with 108 additions and 20 deletions

109
README.md
View File

@@ -1,9 +1,11 @@
# Gome-Assistant
Golang ↔️ Home Assistant
Write your [Home Assistant](https://www.home-assistant.io/) automations with a strongly-typed Golang library!
## Disclaimer
Gome-Assistant is a new library, and I'm opening it up early to get some user feedback on the API and help shape the direction. I plan for it to grow to cover all Home Assistant use cases, services, and event types. So it's possible that breaking changes will happen before v1.0.0!
## Quick Start
### Installation
@@ -24,26 +26,37 @@ Keeping with the simplicity that Go is famous for, you don't need a specific env
> _❗ No promises, but I may provide a Docker image with file watching to automatically restart gome-assistant, to make it easier to use gome-assistant on a fully managed Home Assistant installation._
## Disclaimer
## gome-assistant Concepts
Gome-Assistant is a new library, and I'm opening it up early to get some user feedback on the API and help shape the direction. I plan for it to grow to cover all Home Assistant use cases, services, and event types. So it's possible — maybe likely — that breaking changes will happen before v1.0.0!
### Overview
## gome-assistant Concepts (TODO)
The general flow is
First, you'll need to create your app.
1. Create your app
2. Register automations
3. Start app
```go
import ga "github.com/saml-dev/gome-assistant"
// replace with IP and port of your Home Assistant installation if needed
// replace with IP and port of your Home Assistant installation
app := ga.App("0.0.0.0:8123")
// create automations here (see next sections)
// register automations
app.RegisterSchedules(...)
app.RegisterEntityListeners(...)
app.RegisterEventListeners(...)
app.Start()
```
A full reference is available on [pkg.go.dev](https://pkg.go.dev/github.com/saml-dev/gome-assistant), but all you need to know to get started are the three types of automations in gome-assistant.
### Schedules
Schedules are as you expect, a way to run a function on a schedule. The most common schedule will be once a day.
Schedules are simply a way to run a function on a schedule. The most common schedule is once a day at a certain time.
```go
_7pm := ga.NewSchedule().Call(myFunc).Daily().At("19:00").Build()
@@ -56,19 +69,13 @@ Schedules can also be run at sunrise or sunset, with an optional [offset](https:
sunrise := ga.NewSchedule().Call(myFunc).Daily().Sunrise(app, "-30m").Build()
```
Schedules are also used to run a function on a certain interval. Offset is used to offset the first run of a schedule from midnight.
Schedules are also used to run a function on an interval. Offset is used to offset the first run of a schedule from midnight.
```go
// run every hour at the 30-minute mark
interval := ga.NewSchedule().Call(a).Every("1h").Offset("30m").Build()
```
All schedules must be registered with your app. This will panic if there are any issues with the schedule.
```go
app.RegisterSchedules(_7pm, sunrise, interval)
```
#### Schedule Callback function
The function passed to `.Call()` must take
@@ -76,6 +83,78 @@ The function passed to `.Call()` must take
- `*ga.Service` used to call home assistant services
- `*ga.State` used to retrieve state from home assistant
```go
func myFunc(se *ga.Service, st *ga.State) {
...
}
```
### Entity Listeners
Entity Listeners are used to respond to entities changing state. The simplest entity listener looks like:
```go
etl := ga.NewEntityListener().EntityIds("binary_sensor.front_door").Call(myFunc).Build()
```
Entity listeners have other functions to change the behavior.
| Function | Info |
| ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| ToState("on") | Function only called if new state matches argument. |
| FromState("on") | Function only called if old state matches argument. |
| Throttle("30s") | Minimum time between function calls. |
| Duration("30s") | Requires ToState(). Sets how long the entity should be in the state before running your function. |
| OnlyAfter("03:00") | Only run your function after a specified time of day. |
| OnlyBefore("03:00") | Only run your function before a specified time of day. |
| OnlyBetween("03:00", "14:00") | Only run your function between two specified times of day. |
| ExceptionDay(time.Time) | A one time exception on the given date. Time is ignored, applies to whole day. |
| ExceptionRange(time.Time, time.Time) | A one time exception between the two date/times. Both date and time are considered. |
#### Entity Listener Callback function
The function passed to `.Call()` must take
- `*ga.Service` used to call home assistant services
- `*ga.State` used to retrieve state from home assistant
- `ga.EntityData` which is the entity that triggered the listener
```go
func myFunc(se *ga.Service, st *ga.State, e ga.EntityData) {
...
}
```
### Event Listeners
Event Listeners are used to respond to entities changing state. The simplest event listener looks like:
```go
evl := ga.NewEntityListener().EntityIds("binary_sensor.front_door").Call(myFunc).Build()
```
Event listeners have other functions to change the behavior.
| Function | Info |
| ------------------------------------ | ----------------------------------------------------------------------------------- |
| Throttle("30s") | Minimum time between function calls. |
| OnlyAfter("03:00") | Only run your function after a specified time of day. |
| OnlyBefore("03:00") | Only run your function before a specified time of day. |
| OnlyBetween("03:00", "14:00") | Only run your function between two specified times of day. |
| ExceptionDay(time.Time) | A one time exception on the given date. Time is ignored, applies to whole day. |
| ExceptionRange(time.Time, time.Time) | A one time exception between the two date/times. Both date and time are considered. |
#### Event Listener Callback function
The function passed to `.Call()` must take
- `*ga.Service` used to call home assistant services
- `*ga.State` used to retrieve state from home assistant
```go
func myFunc(se *ga.Service, st *ga.State) {
...
}
```
<!-- TODO: add doc for State and Service, prolly don't need it for EntityData/EntityState -->

View File

@@ -27,7 +27,7 @@ type EntityListener struct {
}
// TODO: add state object as second arg
type EntityListenerCallback func(*Service, EntityData)
type EntityListenerCallback func(*Service, *State, EntityData)
type EntityData struct {
TriggerEntityId string
@@ -192,14 +192,14 @@ func callEntityListeners(app *app, msgBytes []byte) {
if l.delay != 0 {
l.delayTimer = time.AfterFunc(l.delay, func() {
go l.callback(app.service, entityData)
go l.callback(app.service, app.state, entityData)
l.lastRan = carbon.Now()
})
return
}
// run now if no delay set
go l.callback(app.service, entityData)
go l.callback(app.service, app.state, entityData)
l.lastRan = carbon.Now()
}
}

View File

@@ -47,7 +47,7 @@ func main() {
}
func pantryLights(service *ga.Service, sensor ga.EntityData) {
func pantryLights(service *ga.Service, state *ga.State, sensor ga.EntityData) {
l := "light.pantry"
if sensor.ToState == "on" {
service.HomeAssistant.TurnOn(l)
@@ -68,6 +68,7 @@ func onEvent(service *ga.Service, data ga.EventData) {
}
func lightsOut(service *ga.Service, state *ga.State) {
// always turn off outside lights
service.Light.TurnOff("light.outside_lights")
s, err := state.Get("binary_sensor.living_room_motion")
if err != nil {
@@ -76,7 +77,7 @@ func lightsOut(service *ga.Service, state *ga.State) {
}
// if no motion detected in living room for 30mins
if s.State == "off" && time.Now().Sub(s.LastChanged).Minutes() > 30 {
if s.State == "off" && time.Since(s.LastChanged).Minutes() > 30 {
service.Light.TurnOff("light.main_lights")
}
}

View File

@@ -32,3 +32,11 @@ func (s *State) Get(entityId string) (EntityState, error) {
json.Unmarshal(resp, &es)
return es, nil
}
func (s *State) Equals(entityId string, expectedState string) (bool, error) {
currentState, err := s.Get(entityId)
if err != nil {
return false, err
}
return currentState.State == expectedState, nil
}