More refactoring, moved kettle auto off out of the kettle implementation and into a seperate automation
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
20e7e830a6
commit
5aefcf0157
|
@ -1,5 +1,5 @@
|
||||||
.git/
|
.git/
|
||||||
storage/
|
storage/
|
||||||
automation
|
app
|
||||||
.env
|
.env
|
||||||
tmp/
|
tmp/
|
||||||
|
|
|
@ -11,4 +11,5 @@ import (
|
||||||
func RegisterAutomations(client paho.Client, hue *hue.Hue, notify *ntfy.Notify, home *home.Home) {
|
func RegisterAutomations(client paho.Client, hue *hue.Hue, notify *ntfy.Notify, home *home.Home) {
|
||||||
presenceAutomation(client, hue, notify, home)
|
presenceAutomation(client, hue, notify, home)
|
||||||
mixerAutomation(client, home)
|
mixerAutomation(client, home)
|
||||||
|
kettleAutomation(client, home)
|
||||||
}
|
}
|
||||||
|
|
50
automation/kettle.go
Normal file
50
automation/kettle.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package automation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"automation/device"
|
||||||
|
"automation/home"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
paho "github.com/eclipse/paho.mqtt.golang"
|
||||||
|
)
|
||||||
|
|
||||||
|
func kettleAutomation(client paho.Client, home *home.Home) {
|
||||||
|
const name = "kitchen/kettle"
|
||||||
|
const length = 5 * time.Minute
|
||||||
|
|
||||||
|
timer := time.NewTimer(length)
|
||||||
|
|
||||||
|
var handler paho.MessageHandler = func(c paho.Client, m paho.Message) {
|
||||||
|
kettle, err := device.GetDevice[device.OnOff](&home.Devices, name)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if kettle.GetOnOff() {
|
||||||
|
timer.Reset(length)
|
||||||
|
} else {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if token := client.Subscribe(fmt.Sprintf("zigbee2mqtt/%s", name), 1, handler); token.Wait() && token.Error() != nil {
|
||||||
|
log.Println(token.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-timer.C
|
||||||
|
log.Println("Turning kettle automatically off")
|
||||||
|
kettle, err := device.GetDevice[device.OnOff](&home.Devices, name)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
kettle.SetOnOff(false)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package automation
|
||||||
import (
|
import (
|
||||||
"automation/device"
|
"automation/device"
|
||||||
"automation/home"
|
"automation/home"
|
||||||
"automation/integration/kasa"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
@ -12,12 +11,12 @@ import (
|
||||||
|
|
||||||
func mixerAutomation(client paho.Client, home *home.Home) {
|
func mixerAutomation(client paho.Client, home *home.Home) {
|
||||||
var handler paho.MessageHandler = func(client paho.Client, msg paho.Message) {
|
var handler paho.MessageHandler = func(client paho.Client, msg paho.Message) {
|
||||||
mixer, err := device.GetDevice[*kasa.Outlet](&home.Devices, "living_room/mixer")
|
mixer, err := device.GetDevice[device.OnOff](&home.Devices, "living_room/mixer")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
speakers, err := device.GetDevice[*kasa.Outlet](&home.Devices, "living_room/speakers")
|
speakers, err := device.GetDevice[device.OnOff](&home.Devices, "living_room/speakers")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
|
|
||||||
"google.golang.org/api/homegraph/v1"
|
"google.golang.org/api/homegraph/v1"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
|
|
||||||
paho "github.com/eclipse/paho.mqtt.golang"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Home struct {
|
type Home struct {
|
||||||
|
@ -21,7 +19,7 @@ type Home struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto populate and update the device list
|
// Auto populate and update the device list
|
||||||
func New(username string, credentials config.Credentials, client paho.Client) *Home {
|
func New(username string, credentials config.Credentials) *Home {
|
||||||
home := &Home{Username: username, Devices: make(map[device.InternalName]device.Basic)}
|
home := &Home{Username: username, Devices: make(map[device.InternalName]device.Basic)}
|
||||||
|
|
||||||
homegraphService, err := homegraph.NewService(context.Background(), option.WithCredentialsJSON(credentials))
|
homegraphService, err := homegraph.NewService(context.Background(), option.WithCredentialsJSON(credentials))
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
type DeviceInterface interface {
|
type DeviceInterface interface {
|
||||||
device.Basic
|
device.Basic
|
||||||
|
|
||||||
|
IsGoogleDevice()
|
||||||
Sync() *Device
|
Sync() *Device
|
||||||
Query() DeviceState
|
Query() DeviceState
|
||||||
Execute(execution Execution, updatedState *DeviceState) (errCode string, online bool)
|
Execute(execution Execution, updatedState *DeviceState) (errCode string, online bool)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package kasa
|
package kasa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"automation/device"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -12,6 +13,9 @@ import (
|
||||||
// https://www.softscheck.com/en/blog/tp-link-reverse-engineering/
|
// https://www.softscheck.com/en/blog/tp-link-reverse-engineering/
|
||||||
|
|
||||||
type Device interface {
|
type Device interface {
|
||||||
|
device.Basic
|
||||||
|
|
||||||
|
IsKasaDevice()
|
||||||
GetIP() string
|
GetIP() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ func NewOutlet(name device.InternalName, ip string) *Outlet {
|
||||||
|
|
||||||
// kasa.Device
|
// kasa.Device
|
||||||
var _ Device = (*Outlet)(nil)
|
var _ Device = (*Outlet)(nil)
|
||||||
|
func (*Outlet) IsKasaDevice() {}
|
||||||
|
|
||||||
|
// kasa.Device
|
||||||
func (o *Outlet) GetIP() string {
|
func (o *Outlet) GetIP() string {
|
||||||
return o.ip
|
return o.ip
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ func (c *computer) GetID() device.InternalName {
|
||||||
|
|
||||||
// google.DeviceInterface
|
// google.DeviceInterface
|
||||||
var _ google.DeviceInterface = (*computer)(nil)
|
var _ google.DeviceInterface = (*computer)(nil)
|
||||||
|
func (*computer) IsGoogleDevice() {}
|
||||||
|
|
||||||
|
// google.DeviceInterface
|
||||||
func (c *computer) Sync() *google.Device {
|
func (c *computer) Sync() *google.Device {
|
||||||
device := google.NewDevice(c.GetID().String(), google.TypeScene)
|
device := google.NewDevice(c.GetID().String(), google.TypeScene)
|
||||||
device.AddSceneTrait(false)
|
device.AddSceneTrait(false)
|
||||||
|
|
|
@ -30,6 +30,8 @@ func DevicesHandler(client paho.Client, home *home.Home) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send sync request
|
// Send sync request
|
||||||
|
// @TODO Instead of sending a sync request we should do something like home.sync <- interface{}
|
||||||
|
// This will then restart a timer, that way the sync will only trigger once everything has settled from multiple locations
|
||||||
home.Service.RequestSync(context.Background(), home.Username)
|
home.Service.RequestSync(context.Background(), home.Username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,21 +20,12 @@ type kettle struct {
|
||||||
|
|
||||||
updated chan bool
|
updated chan bool
|
||||||
|
|
||||||
timerLength time.Duration
|
|
||||||
timer *time.Timer
|
|
||||||
stop chan interface{}
|
|
||||||
|
|
||||||
isOn bool
|
isOn bool
|
||||||
online bool
|
online bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKettle(info Info, client paho.Client, service *google.Service) *kettle {
|
func NewKettle(info Info, client paho.Client, service *google.Service) *kettle {
|
||||||
k := &kettle{info: info, client: client, service: service, updated: make(chan bool, 1), timerLength: 5 * time.Minute, stop: make(chan interface{})}
|
k := &kettle{info: info, client: client, service: service, updated: make(chan bool, 1)}
|
||||||
k.timer = time.NewTimer(k.timerLength)
|
|
||||||
k.timer.Stop()
|
|
||||||
|
|
||||||
// Start function
|
|
||||||
go k.timerFunc()
|
|
||||||
|
|
||||||
if token := k.client.Subscribe(fmt.Sprintf("zigbee2mqtt/%s", k.info.FriendlyName), 1, k.stateHandler); token.Wait() && token.Error() != nil {
|
if token := k.client.Subscribe(fmt.Sprintf("zigbee2mqtt/%s", k.info.FriendlyName), 1, k.stateHandler); token.Wait() && token.Error() != nil {
|
||||||
log.Println(token.Error())
|
log.Println(token.Error())
|
||||||
|
@ -64,27 +55,6 @@ func (k *kettle) stateHandler(client paho.Client, msg paho.Message) {
|
||||||
k.service.ReportState(context.Background(), id, map[string]google.DeviceState{
|
k.service.ReportState(context.Background(), id, map[string]google.DeviceState{
|
||||||
id: k.getState(),
|
id: k.getState(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if k.isOn {
|
|
||||||
k.timer.Reset(k.timerLength)
|
|
||||||
} else {
|
|
||||||
k.timer.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *kettle) timerFunc() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <- k.timer.C:
|
|
||||||
log.Println("Turning kettle automatically off")
|
|
||||||
if token := k.client.Publish(fmt.Sprintf("zigbee2mqtt/%s/set", k.info.FriendlyName), 1, false, `{"state": "OFF"}`); token.Wait() && token.Error() != nil {
|
|
||||||
log.Println(token.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
case <- k.stop:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *kettle) getState() google.DeviceState {
|
func (k *kettle) getState() google.DeviceState {
|
||||||
|
@ -94,21 +64,20 @@ func (k *kettle) getState() google.DeviceState {
|
||||||
|
|
||||||
// zigbee.Device
|
// zigbee.Device
|
||||||
var _ Device = (*kettle)(nil)
|
var _ Device = (*kettle)(nil)
|
||||||
func (k *kettle) Delete() {
|
func (k *kettle) IsZigbeeDevice() {}
|
||||||
k.stop <- struct{}{}
|
|
||||||
|
|
||||||
if token := k.client.Subscribe(fmt.Sprintf("zigbee2mqtt/%s", k.info.FriendlyName), 1, k.stateHandler); token.Wait() && token.Error() != nil {
|
// zigbee.Device
|
||||||
|
func (k *kettle) Delete() {
|
||||||
|
if token := k.client.Unsubscribe(fmt.Sprintf("zigbee2mqtt/%s", k.info.FriendlyName)); token.Wait() && token.Error() != nil {
|
||||||
log.Println(token.Error())
|
log.Println(token.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *kettle) IsZigbeeDevice() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// google.DeviceInterface
|
// google.DeviceInterface
|
||||||
var _ google.DeviceInterface = (*kettle)(nil)
|
var _ google.DeviceInterface = (*kettle)(nil)
|
||||||
|
func (*kettle) IsGoogleDevice() {}
|
||||||
|
|
||||||
|
// google.DeviceInterface
|
||||||
func (k *kettle) Sync() *google.Device {
|
func (k *kettle) Sync() *google.Device {
|
||||||
device := google.NewDevice(k.GetID().String(), google.TypeKettle)
|
device := google.NewDevice(k.GetID().String(), google.TypeKettle)
|
||||||
device.AddOnOffTrait(false, false)
|
device.AddOnOffTrait(false, false)
|
||||||
|
|
|
@ -14,7 +14,6 @@ type Info struct {
|
||||||
type Device interface {
|
type Device interface {
|
||||||
device.Basic
|
device.Basic
|
||||||
|
|
||||||
// This function only exists to make this interface unique
|
IsZigbeeDevice()
|
||||||
IsZigbeeDevice() bool
|
|
||||||
Delete()
|
Delete()
|
||||||
}
|
}
|
||||||
|
|
42
main.go
42
main.go
|
@ -26,7 +26,21 @@ func main() {
|
||||||
|
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
|
|
||||||
// Setup all the connections to other services
|
notify := ntfy.New(cfg.Ntfy.Topic)
|
||||||
|
hue := hue.New(cfg.Hue.IP, cfg.Hue.Token)
|
||||||
|
|
||||||
|
home := home.New(cfg.Google.Username, cfg.Google.Credentials)
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.HandleFunc("/assistant", home.Service.FullfillmentHandler)
|
||||||
|
|
||||||
|
for name, info := range cfg.Computer {
|
||||||
|
home.AddDevice(wol.NewComputer(info.MACAddress, name, info.Url))
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, ip := range cfg.Kasa.Outlets {
|
||||||
|
home.AddDevice(kasa.NewOutlet(name, ip))
|
||||||
|
}
|
||||||
|
|
||||||
opts := paho.NewClientOptions().AddBroker(fmt.Sprintf("%s:%d", cfg.MQTT.Host, cfg.MQTT.Port))
|
opts := paho.NewClientOptions().AddBroker(fmt.Sprintf("%s:%d", cfg.MQTT.Host, cfg.MQTT.Port))
|
||||||
opts.SetClientID(cfg.MQTT.ClientID)
|
opts.SetClientID(cfg.MQTT.ClientID)
|
||||||
opts.SetUsername(cfg.MQTT.Username)
|
opts.SetUsername(cfg.MQTT.Username)
|
||||||
|
@ -38,32 +52,20 @@ func main() {
|
||||||
panic(token.Error())
|
panic(token.Error())
|
||||||
}
|
}
|
||||||
defer client.Disconnect(250)
|
defer client.Disconnect(250)
|
||||||
notify := ntfy.New(cfg.Ntfy.Topic)
|
|
||||||
hue := hue.New(cfg.Hue.IP, cfg.Hue.Token)
|
|
||||||
|
|
||||||
// Devices that we control and expose to google home
|
zigbee.DevicesHandler(client, home)
|
||||||
home := home.New(cfg.Google.Username, cfg.Google.Credentials, client)
|
|
||||||
|
|
||||||
// Setup presence system
|
|
||||||
p := presence.New(client, hue, notify, home)
|
p := presence.New(client, hue, notify, home)
|
||||||
defer p.Delete(client)
|
defer p.Delete(client)
|
||||||
|
|
||||||
r := mux.NewRouter()
|
opts.SetClientID(fmt.Sprintf("%s-2", cfg.MQTT.ClientID))
|
||||||
r.HandleFunc("/assistant", home.Service.FullfillmentHandler)
|
automationClient := paho.NewClient(opts)
|
||||||
|
if token := automationClient.Connect(); token.Wait() && token.Error() != nil {
|
||||||
// Register computers
|
panic(token.Error())
|
||||||
for name, info := range cfg.Computer {
|
|
||||||
home.AddDevice(wol.NewComputer(info.MACAddress, name, info.Url))
|
|
||||||
}
|
}
|
||||||
|
defer automationClient.Disconnect(250)
|
||||||
|
|
||||||
// Register all kasa devies
|
automation.RegisterAutomations(automationClient, hue, notify, home)
|
||||||
for name, ip := range cfg.Kasa.Outlets {
|
|
||||||
home.AddDevice(kasa.NewOutlet(name, ip))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup handler that automatically registers and updates all zigbee devices
|
|
||||||
zigbee.DevicesHandler(client, home)
|
|
||||||
automation.RegisterAutomations(client, hue, notify, home)
|
|
||||||
|
|
||||||
addr := ":8090"
|
addr := ":8090"
|
||||||
srv := http.Server{
|
srv := http.Server{
|
||||||
|
|
Reference in New Issue
Block a user