This repository has been archived on 2023-08-29. You can view files and clone it, but cannot push or open issues or pull requests.
automation/main.go
Dreaded_X 4c023ad933
All checks were successful
continuous-integration/drone/push Build is passing
Did some groundwork for setting up more complex interactions
2022-11-18 04:27:43 +01:00

204 lines
4.3 KiB
Go

package main
import (
"automation/device"
"automation/integration/hue"
"automation/integration/kasa"
"automation/integration/mqtt"
"automation/integration/ntfy"
"automation/presence"
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
"gopkg.in/yaml.v3"
)
type config struct {
Hue hue.Config `yaml:"hue"`
NTFY ntfy.Config `yaml:"ntfy"`
MQTT mqtt.Config `yaml:"mqtt"`
Kasa struct {
Outlets map[string]string `yaml:"outlets"`
} `yaml:"kasa"`
Computer map[string]struct {
MACAddress string `yaml:"mac"`
Room string `yaml:"room"`
Url string `yaml:"url"`
} `yaml:"computers"`
Google device.Config `yaml:"google"`
}
func GetConfig() config {
// First load the config from the yaml file
f, err := os.Open("config.yml")
if err != nil {
log.Fatalln("Failed to open config file", err)
}
defer f.Close()
var cfg config
decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
log.Fatalln("Failed to parse config file", err)
}
// Then load values from environment
// This can be used to either override the config or pass in secrets
err = envconfig.Process("", &cfg)
if err != nil {
log.Fatalln("Failed to parse environmet config", err)
}
return cfg
}
var devices map[string]interface{}
// // @TODO Implement this for the other devices as well
// func GetDeviceKasa(name string) (*kasa.Kasa, error) {
// deviceGeneric, ok := devices[name]
// if !ok {
// return nil, fmt.Errorf("Device does not exist")
// }
// device, ok := deviceGeneric.(kasa.Kasa)
// if !ok {
// return nil, fmt.Errorf("Device is not a Kasa device")
// }
// return &device, nil
// }
// func SetupBindings(m *mqtt.MQTT) {
// m.AddHandler("zigbee2mqtt/living_room/audio_remote", func(_ paho.Client, msg paho.Message) {
// mixer, err := GetDeviceKasa("living_room/mixer")
// if err != nil {
// log.Println(err)
// return
// }
// speakers, err := GetDeviceKasa("living_room/speakers")
// if err != nil {
// log.Println(err)
// return
// }
// var message struct {
// Action string `json:"action"`
// }
// err = json.Unmarshal(msg.Payload(), &message)
// if err != nil {
// log.Println(err)
// return
// }
// if message.Action == "on" {
// if mixer.GetState() {
// mixer.SetState(false)
// speakers.SetState(false)
// } else {
// mixer.SetState(true)
// }
// } else if message.Action == "brightness_move_up" {
// if speakers.GetState() {
// speakers.SetState(false)
// } else {
// speakers.SetState(true)
// mixer.SetState(true)
// }
// }
// })
// }
func main() {
_ = godotenv.Load()
config := GetConfig()
devices = make(map[string]interface{})
// MQTT
m := mqtt.Connect(config.MQTT)
defer m.Disconnect()
// Hue
h := hue.Connect(config.Hue)
// Kasa
for name, ip := range config.Kasa.Outlets {
devices[name] = kasa.New(ip)
}
// ntfy.sh
n := ntfy.Connect(config.NTFY)
// Presence
p := presence.New()
m.AddHandler("automation/presence/+", p.PresenceHandler)
// Devices that we control and expose to google home
provider := device.NewProvider(config.Google, &m)
r := mux.NewRouter()
r.HandleFunc("/assistant", provider.Service.FullfillmentHandler)
for name, info := range config.Computer {
provider.AddDevice(device.NewComputer(info.MACAddress, name, info.Room, info.Url))
}
// Event loop
go func() {
fmt.Println("Starting event loop")
for {
select {
case present := <-p.Presence:
fmt.Printf("Presence: %t\n", present)
// Notify users of presence update
n.Presence(present)
// Set presence on the hue bridge
h.SetFlag(41, present)
if !present {
// Turn off all the devices that we manage ourselves
provider.TurnAllOff()
// Turn off all devices
// @TODO Maybe allow for exceptions, could be a list in the config that we check against?
for _, device := range devices {
switch d := device.(type) {
case kasa.Kasa:
d.SetState(false)
}
}
// @TODO Turn off nest thermostat
} else {
// @TODO Turn on the nest thermostat again
}
case <-h.Events:
break
}
}
}()
addr := ":8090"
srv := http.Server{
Addr: addr,
Handler: r,
}
log.Printf("Starting server on %s (PID: %d)\n", addr, os.Getpid())
srv.ListenAndServe()
}