mirror of
https://github.com/Xevion/go-ha.git
synced 2025-12-06 01:15:10 -06:00
261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
# go-ha
|
||
|
||
Write strongly typed [Home Assistant](https://www.home-assistant.io/) automations in Go!
|
||
|
||
```bash
|
||
go get github.com/Xevion/go-ha
|
||
```
|
||
|
||
or in `go.mod`:
|
||
|
||
```go
|
||
require github.com/Xevion/go-ha
|
||
```
|
||
|
||
## Generate Entity Constants
|
||
|
||
You can generate type-safe constants for all your Home Assistant entities using `go generate`. This makes it easier to reference entities in your code.
|
||
|
||
1. Create a `gen.yaml` file in your project root:
|
||
|
||
```yaml
|
||
url: "http://192.168.1.123:8123"
|
||
ha_auth_token: "your_auth_token" # Or set HA_AUTH_TOKEN env var
|
||
home_zone_entity_id: "zone.home" # Optional: defaults to zone.home
|
||
|
||
# Optional: List of domains to include when generating constants
|
||
# If provided, only these domains will be processed
|
||
include_domains: ["light", "switch", "climate"]
|
||
|
||
# Optional: List of domains to exclude when generating constants
|
||
# Only used if include_domains is empty
|
||
exclude_domains: ["device_tracker", "person"]
|
||
```
|
||
|
||
2. Add a `//go:generate` comment in your project:
|
||
|
||
```go
|
||
//go:generate go run github.com/Xevion/go-ha/cmd/generate
|
||
```
|
||
|
||
Optionally use the `-config` flag to customize the file path of the config file.
|
||
|
||
3. Run the generator:
|
||
|
||
```
|
||
go generate
|
||
```
|
||
|
||
This will create an `entities` package with type-safe constants for all your Home Assistant entities, organized by domain. For example:
|
||
|
||
```go
|
||
import "your_project/entities"
|
||
|
||
// Instead of writing "light.living_room" as a string:
|
||
entities.Light.LivingRoom // Type-safe constant
|
||
|
||
// All your entities are organized by domain
|
||
entities.Switch.Kitchen
|
||
entities.Climate.Bedroom
|
||
entities.MediaPlayer.TVRoom
|
||
```
|
||
|
||
The constants are based on the entity ID itself, not the name of the entity in Home Assistant.
|
||
|
||
### Write your automations
|
||
|
||
Check out [`example/example.go`](./example/example.go) for an example of the 3 types of automations — schedules, entity listeners, and event listeners.
|
||
|
||
> ℹ️ Instead of copying and pasting, try typing it yourself to see how autocomplete guides you through the setup using a builder pattern.
|
||
|
||
### Run your code
|
||
|
||
Keeping with the simplicity that Go is famous for, you don't need a specific environment or docker container to run go-ha. You just write and run your code like any other Go binary. So once you build your code, you can run it however you like — using `screen` or `tmux`, a cron job, a linux service, or wrap it up in a docker container if you like!
|
||
|
||
> _❗ No promises, but I may provide a Docker image with file watching to automatically restart go-ha, to make it easier to use go-ha on a fully managed Home Assistant installation._
|
||
|
||
## go-ha Concepts
|
||
|
||
### Overview
|
||
|
||
The general flow is
|
||
|
||
1. Create your app
|
||
2. Register automations
|
||
3. Start app
|
||
|
||
```go
|
||
import ha "github.com/Xevion/go-ha"
|
||
|
||
// replace with IP and port of your Home Assistant installation
|
||
app, err := ga.NewApp(ga.NewAppRequest{
|
||
URL: "http://192.168.1.123:8123",
|
||
HAAuthToken: os.Getenv("HA_AUTH_TOKEN"),
|
||
HomeZoneEntityId: "zone.home",
|
||
})
|
||
|
||
// create automations here (see next sections)
|
||
|
||
// register automations
|
||
app.RegisterSchedules(...)
|
||
app.RegisterEntityListeners(...)
|
||
app.RegisterEventListeners(...)
|
||
app.RegisterIntervals(...)
|
||
|
||
app.Start()
|
||
```
|
||
|
||
A full reference is available on [pkg.go.dev](https://pkg.go.dev/github.com/Xevion/go-ha), but all you need to know to get started are the four types of automations in go-ha.
|
||
|
||
- [Daily Schedules](#daily-schedule)
|
||
- [Entity Listeners](#entity-listener)
|
||
- [Event Listeners](#event-listener)
|
||
- [Intervals](#interval)
|
||
|
||
### Daily Schedule
|
||
|
||
Daily Schedules run at a specific time each day.
|
||
|
||
```go
|
||
_7pm := ga.NewDailySchedule().Call(myFunc).At("19:00").Build()
|
||
```
|
||
|
||
Schedules can also be run at sunrise or sunset, with an optional [offset](https://pkg.go.dev/time#ParseDuration).
|
||
|
||
```go
|
||
// 30 mins before sunrise
|
||
sunrise := ga.NewDailySchedule().Call(myFunc).Sunrise(app, "-30m").Build()
|
||
// at sunset
|
||
sunset := ga.NewDailySchedule().Call(myFunc).Sunset().Build()
|
||
```
|
||
|
||
Daily schedules have other functions to change the behavior.
|
||
|
||
| Function | Info |
|
||
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||
| ExceptionDates(t time.Time, ...time.Time) | Skip the schedule on the given date(s). Functions like a blocklist. Cannot be combined with OnlyOnDates. |
|
||
| OnlyOnDates(t time.Time, ...time.Time) | Run only on the given date(s). Functions like an allowlist. Cannot be combined with ExceptionDates. |
|
||
|
||
#### Schedule 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) {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### Entity Listener
|
||
|
||
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 must 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. |
|
||
| ExceptionDates(time.Time, ...time.Time) | A one time exception on the given date. Time is ignored, applies to whole day. Functions like a "blocklist". |
|
||
| ExceptionRange(time.Time, time.Time) | A one time exception between the two date/times. Both date and time are considered. Functions like a "blocklist". |
|
||
| RunOnStartup() | Run your callback during `App.Start()`. |
|
||
|
||
#### 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 allow you to respond to Home Assistant events in real-time. You can create an event listener using the builder pattern:
|
||
|
||
```go
|
||
eventListener := ga.
|
||
NewEventListener().
|
||
EventTypes("zwave_js_value_notification"). // Specify one or more event types
|
||
Call(myCallbackFunc). // Specify the callback function
|
||
Build()
|
||
|
||
// Register the listener with your app
|
||
app.RegisterEventListeners(eventListener)
|
||
```
|
||
|
||
Event listeners have other functions to change the behavior.
|
||
|
||
| Function | Info |
|
||
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||
| OnlyBetween("03:00", "14:00") | Only run your function between two specified times of day |
|
||
| 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 |
|
||
| Throttle("30s") | Minimum time between function calls |
|
||
| ExceptionDates(time.Time, ...time.Time) | A one time exception on the given date. Time is ignored, applies to whole day. Functions like a "blocklist" |
|
||
| ExceptionRange(time.Time, time.Time) | A one time exception between the two date/times. Both date and time are considered. Functions like a "blocklist" |
|
||
|
||
The callback function receives three parameters:
|
||
|
||
```go
|
||
func myCallback(service *ga.Service, state ga.State, data ga.EventData) {
|
||
// You can unmarshal the raw JSON into a type-safe struct
|
||
ev := ga.EventZWaveJSValueNotification{}
|
||
json.Unmarshal(data.RawEventJSON, &ev)
|
||
|
||
// Handle the event...
|
||
}
|
||
```
|
||
|
||
> 💡 Check `eventTypes.go` for pre-defined event types, or create your own struct type for custom events and contribute them back to go-ha with a PR.
|
||
|
||
### Interval
|
||
|
||
Intervals are used to run a function on an interval.
|
||
|
||
```go
|
||
// run every hour at the 30-minute mark
|
||
interval := ga.NewInterval().Call(myFunc).Every("1h").StartingAt("00:30").Build()
|
||
// run every 5 minutes between 10am and 5pm
|
||
interval = ga.NewInterval().Call(myFunc).Every("5m").StartingAt("10:00").EndingAt("17:00").Build()
|
||
```
|
||
|
||
Intervals have other functions to change the behavior.
|
||
|
||
| Function | Info |
|
||
| --------------------------------------- | ----------------------------------------------------------------------------------- |
|
||
| StartingAt(TimeString) | What time the interval begins to run each day. |
|
||
| EndingAt(TimeString) | What time the interval stops running each day. |
|
||
| ExceptionDates(time.Time, ...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. |
|
||
|
||
#### Interval 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) {
|
||
// ...
|
||
}
|
||
```
|