diff --git a/integration/mqtt/mqtt.go b/integration/mqtt/mqtt.go index 814285a..83b5038 100644 --- a/integration/mqtt/mqtt.go +++ b/integration/mqtt/mqtt.go @@ -23,6 +23,7 @@ func Connect(config Config) MQTT { opts.SetDefaultPublishHandler(defaultHandler) opts.SetUsername(config.Username) opts.SetPassword(config.Password) + opts.SetOrderMatters(false) client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { diff --git a/main.go b/main.go index 994fb38..6bba956 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "automation/integration/mqtt" "automation/integration/ntfy" "automation/presence" + "encoding/json" "fmt" "log" "net/http" @@ -16,6 +17,8 @@ import ( "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" "gopkg.in/yaml.v3" + + paho "github.com/eclipse/paho.mqtt.golang" ) type config struct { @@ -138,11 +141,7 @@ func main() { } // ntfy.sh - n := ntfy.Connect(config.NTFY) - - // Presence - p := presence.New() - m.AddHandler("automation/presence/+", p.PresenceHandler) + // n := ntfy.Connect(config.NTFY) // Devices that we control and expose to google home provider := device.NewProvider(config.Google, &m) @@ -154,43 +153,48 @@ func main() { 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) + // Presence + p := presence.New() + m.AddHandler("automation/presence/+", p.PresenceHandler) - // 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 - } + m.AddHandler("automation/presence", func(client paho.Client, msg paho.Message) { + if len(msg.Payload()) == 0 { + // In this case we clear the persistent message + return } - }() + var message presence.Message + err := json.Unmarshal(msg.Payload(), &message) + if err != nil { + log.Println(err) + return + } + + fmt.Printf("Presence: %t\n", message.State) + // Notify users of presence update + // n.Presence(present) + + // Set presence on the hue bridge + h.SetFlag(41, message.State) + + if !message.State { + // 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 + } + }) addr := ":8090" srv := http.Server{ diff --git a/presence/presence.go b/presence/presence.go index afe8a1e..8ee2c61 100644 --- a/presence/presence.go +++ b/presence/presence.go @@ -1,40 +1,48 @@ package presence import ( - "fmt" - "strconv" + "encoding/json" + "log" "strings" + "time" mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/kr/pretty" ) type Presence struct { - Presence chan bool - devices map[string]bool - current *bool + devices map[string]bool + current *bool +} + +type Message struct { + State bool `json:"state"` + Updated int64 `json:"updated"` } func New() Presence { - return Presence{Presence: make(chan bool), devices: make(map[string]bool), current: nil} + return Presence{devices: make(map[string]bool), current: nil} } // Handler got automation/presence/+ func (p *Presence) PresenceHandler(client mqtt.Client, msg mqtt.Message) { name := strings.Split(msg.Topic(), "/")[2] + if len(msg.Payload()) == 0 { - // @TODO What happens if we delete a device that does not exist delete(p.devices, name) } else { - value, err := strconv.Atoi(string(msg.Payload())) + var message Message + err := json.Unmarshal(msg.Payload(), &message) if err != nil { - panic(err) + log.Println(err) + return } - p.devices[name] = value == 1 + p.devices[name] = message.State } present := false - fmt.Println(p.devices) + pretty.Println(p.devices) for _, value := range p.devices { if value { present = true @@ -42,9 +50,23 @@ func (p *Presence) PresenceHandler(client mqtt.Client, msg mqtt.Message) { } } + log.Println(present) + if p.current == nil || *p.current != present { p.current = &present - p.Presence <- present + + msg, err := json.Marshal(Message{ + State: present, + Updated: time.Now().UnixMilli(), + }) + + if err != nil { + log.Println(err) + } + + token := client.Publish("automation/presence", 1, true, msg) + if token.Wait() && token.Error() != nil { + log.Println(token.Error()) + } } } -