mirror of
https://github.com/Xevion/HATray.git
synced 2025-12-05 23:15:09 -06:00
191 lines
4.3 KiB
Go
191 lines
4.3 KiB
Go
package internal
|
|
|
|
import (
|
|
"embed"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/getlantern/systray"
|
|
dotenv "github.com/joho/godotenv"
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
ga "saml.dev/gome-assistant"
|
|
)
|
|
|
|
var ()
|
|
|
|
type TrayApp struct {
|
|
doorIdentifier string
|
|
log *slog.Logger
|
|
stateChannel chan string
|
|
app *ga.App
|
|
service *ga.Service
|
|
}
|
|
|
|
// Status will return the operational status of the service
|
|
func (ta *TrayApp) Status() Status {
|
|
return StatusUnknown
|
|
}
|
|
|
|
func (ta *TrayApp) State() string {
|
|
// TODO: Implement this method
|
|
return ""
|
|
}
|
|
|
|
func (ta *TrayApp) Connected() bool {
|
|
// TODO: Implement this method
|
|
return false
|
|
}
|
|
|
|
func (ta *TrayApp) Reload() error {
|
|
// TODO: Implement this method
|
|
return nil
|
|
}
|
|
|
|
func (ta *TrayApp) Pause() error {
|
|
// TODO: Implement this method
|
|
return nil
|
|
}
|
|
|
|
func (ta *TrayApp) Resume() error {
|
|
// TODO: Implement this method
|
|
return nil
|
|
}
|
|
|
|
func NewApp() *TrayApp {
|
|
// Connect to Home Assistant
|
|
app, err := ga.NewApp(ga.NewAppRequest{
|
|
IpAddress: "home.imfucked.lol", // Replace with your Home Assistant IP Address
|
|
HAAuthToken: os.Getenv("HA_AUTH_TOKEN"),
|
|
HomeZoneEntityId: "zone.home",
|
|
Port: "443",
|
|
Secure: true,
|
|
})
|
|
if err != nil {
|
|
slog.Error("Error connecting to Home Assistant", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
service := app.GetService()
|
|
|
|
return &TrayApp{
|
|
app: app,
|
|
service: service,
|
|
stateChannel: make(chan string),
|
|
doorIdentifier: "binary_sensor.bedroom_door_opening",
|
|
}
|
|
}
|
|
|
|
var (
|
|
//go:embed "resources/*.ico"
|
|
icons embed.FS
|
|
)
|
|
|
|
func (ta *TrayApp) HandleState(newState string) {
|
|
switch newState {
|
|
case "on":
|
|
ta.stateChannel <- "open"
|
|
case "off":
|
|
ta.stateChannel <- "closed"
|
|
default:
|
|
slog.Error("unknown state encountered", "newState", newState)
|
|
ta.stateChannel <- "unknown"
|
|
}
|
|
}
|
|
|
|
func (ta *TrayApp) setupHomeAssistant() {
|
|
var err error
|
|
|
|
// Get the initial state
|
|
state, err := ta.app.GetState().Get(ta.doorIdentifier)
|
|
if err != nil {
|
|
slog.Error("Unable to get initial state", "error", err)
|
|
} else {
|
|
slog.Debug("Initial State Received")
|
|
ta.HandleState(state.State)
|
|
}
|
|
|
|
ta.app.RegisterEntityListeners(ga.
|
|
NewEntityListener().
|
|
EntityIds(ta.doorIdentifier).
|
|
Call(func(service *ga.Service, state ga.State, sensor ga.EntityData) {
|
|
slog.Debug("Event Received", "identifier", ta.doorIdentifier, "sensor", sensor)
|
|
ta.HandleState(sensor.ToState)
|
|
}).
|
|
Build())
|
|
|
|
ta.app.Start()
|
|
|
|
slog.Warn("Home Assistant thread died")
|
|
ta.stateChannel <- "unknown"
|
|
}
|
|
|
|
func (ta *TrayApp) Start() {
|
|
dotenv.Load()
|
|
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(
|
|
os.Stdout,
|
|
&slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
},
|
|
)))
|
|
// binfo, err := buildinfo.
|
|
slog.Info("Startup", "runtime", runtime.Version(), "os", runtime.GOOS, "arch", runtime.GOARCH, "pid", os.Getpid())
|
|
|
|
go ta.setupHomeAssistant()
|
|
systray.Run(ta.onReady, func() {})
|
|
}
|
|
|
|
func (ta *TrayApp) onReady() {
|
|
systray.SetTitle("door-tray")
|
|
systray.SetTooltip("Setting up...")
|
|
menuQuit := systray.AddMenuItem("Quit", "Stops the application")
|
|
menuOpenLogs := systray.AddMenuItem("Open Logs", "Opens the logs in the default editor")
|
|
menuOpenLogs.Disable()
|
|
|
|
// Load icons
|
|
systray.SetIcon(getIcon("unknown"))
|
|
|
|
// Handle Ctrl+C interrupt
|
|
interruptChannel := make(chan os.Signal, 1)
|
|
signal.Notify(interruptChannel, os.Interrupt)
|
|
signal.Notify(interruptChannel, os.Kill)
|
|
|
|
loop:
|
|
for {
|
|
select {
|
|
case signal := <-interruptChannel:
|
|
slog.Info("Received interrupt signal, quitting", "signal", signal)
|
|
break loop
|
|
case <-menuQuit.ClickedCh:
|
|
slog.Info("Quit clicked")
|
|
break loop
|
|
case <-menuOpenLogs.ClickedCh:
|
|
slog.Info("Open Logs clicked")
|
|
case newState := <-ta.stateChannel:
|
|
timeString := time.Now().Format("3:04 PM")
|
|
if newState != "unknown" {
|
|
systray.SetTooltip(fmt.Sprintf("%s as of %s", cases.Title(language.AmericanEnglish, cases.NoLower).String(newState), timeString))
|
|
switch newState {
|
|
case "open":
|
|
systray.SetIcon(getIcon("open_fault"))
|
|
case "closed":
|
|
systray.SetIcon(getIcon("closed"))
|
|
}
|
|
} else {
|
|
slog.Warn("Unknown state", "state", newState)
|
|
systray.SetTooltip(fmt.Sprintf("Unknown as of %s", timeString))
|
|
systray.SetIcon(getIcon("unknown"))
|
|
}
|
|
}
|
|
}
|
|
|
|
slog.Info("Cleaning up")
|
|
systray.Quit()
|
|
ta.app.Cleanup()
|
|
}
|