mirror of
https://github.com/Xevion/HATray.git
synced 2025-12-06 03:15:16 -06:00
feat!: complete rearrangement for multi-module platform-specific modules
This commit is contained in:
190
internal/app.go
Normal file
190
internal/app.go
Normal file
@@ -0,0 +1,190 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user