Files
ShackMaster/internal/api/device_manager.go
2026-01-10 11:01:40 +01:00

301 lines
7.5 KiB
Go

package api
import (
"log"
"sync"
"time"
"git.rouggy.com/rouggy/ShackMaster/internal/config"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/antennagenius"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/powergenius"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/rotatorgenius"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/tunergenius"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/ultrabeam"
"git.rouggy.com/rouggy/ShackMaster/internal/devices/webswitch"
"git.rouggy.com/rouggy/ShackMaster/internal/services/solar"
"git.rouggy.com/rouggy/ShackMaster/internal/services/weather"
)
type DeviceManager struct {
config *config.Config
webSwitch *webswitch.Client
powerGenius *powergenius.Client
tunerGenius *tunergenius.Client
antennaGenius *antennagenius.Client
rotatorGenius *rotatorgenius.Client
ultrabeam *ultrabeam.Client
solarClient *solar.Client
weatherClient *weather.Client
hub *Hub
statusMu sync.RWMutex
lastStatus *SystemStatus
updateInterval time.Duration
stopChan chan struct{}
}
type SystemStatus struct {
WebSwitch *webswitch.Status `json:"webswitch"`
PowerGenius *powergenius.Status `json:"power_genius"`
TunerGenius *tunergenius.Status `json:"tuner_genius"`
AntennaGenius *antennagenius.Status `json:"antenna_genius"`
RotatorGenius *rotatorgenius.Status `json:"rotator_genius"`
Ultrabeam *ultrabeam.Status `json:"ultrabeam"`
Solar *solar.SolarData `json:"solar"`
Weather *weather.WeatherData `json:"weather"`
Timestamp time.Time `json:"timestamp"`
}
func NewDeviceManager(cfg *config.Config, hub *Hub) *DeviceManager {
return &DeviceManager{
config: cfg,
hub: hub,
updateInterval: 1 * time.Second, // Update status every second
stopChan: make(chan struct{}),
}
}
func (dm *DeviceManager) Initialize() error {
log.Println("Initializing device manager...")
// Initialize WebSwitch
dm.webSwitch = webswitch.New(dm.config.Devices.WebSwitch.Host)
// Initialize Power Genius
dm.powerGenius = powergenius.New(
dm.config.Devices.PowerGenius.Host,
dm.config.Devices.PowerGenius.Port,
)
// Initialize Tuner Genius
dm.tunerGenius = tunergenius.New(
dm.config.Devices.TunerGenius.Host,
dm.config.Devices.TunerGenius.Port,
)
// Initialize Antenna Genius
dm.antennaGenius = antennagenius.New(
dm.config.Devices.AntennaGenius.Host,
dm.config.Devices.AntennaGenius.Port,
)
// Initialize Rotator Genius
log.Printf("Initializing RotatorGenius: host=%s port=%d", dm.config.Devices.RotatorGenius.Host, dm.config.Devices.RotatorGenius.Port)
dm.rotatorGenius = rotatorgenius.New(
dm.config.Devices.RotatorGenius.Host,
dm.config.Devices.RotatorGenius.Port,
)
// Initialize Ultrabeam
log.Printf("Initializing Ultrabeam: host=%s port=%d", dm.config.Devices.Ultrabeam.Host, dm.config.Devices.Ultrabeam.Port)
dm.ultrabeam = ultrabeam.New(
dm.config.Devices.Ultrabeam.Host,
dm.config.Devices.Ultrabeam.Port,
)
// Initialize Solar data client
dm.solarClient = solar.New()
// Initialize Weather client
dm.weatherClient = weather.New(
dm.config.Weather.OpenWeatherMapAPIKey,
dm.config.Location.Latitude,
dm.config.Location.Longitude,
)
// Start device polling in background (non-blocking)
go func() {
if err := dm.powerGenius.Start(); err != nil {
log.Printf("Warning: Failed to start PowerGenius polling: %v", err)
}
}()
go func() {
if err := dm.tunerGenius.Start(); err != nil {
log.Printf("Warning: Failed to start TunerGenius polling: %v", err)
}
}()
go func() {
if err := dm.antennaGenius.Start(); err != nil {
log.Printf("Warning: Failed to start AntennaGenius polling: %v", err)
}
}()
log.Println("About to launch RotatorGenius goroutine...")
go func() {
log.Println("Starting RotatorGenius polling goroutine...")
if err := dm.rotatorGenius.Start(); err != nil {
log.Printf("Warning: Failed to start RotatorGenius polling: %v", err)
}
}()
log.Println("RotatorGenius goroutine launched")
log.Println("About to launch Ultrabeam goroutine...")
go func() {
log.Println("Starting Ultrabeam polling goroutine...")
if err := dm.ultrabeam.Start(); err != nil {
log.Printf("Warning: Failed to start Ultrabeam polling: %v", err)
}
}()
log.Println("Ultrabeam goroutine launched")
log.Println("Device manager initialized")
return nil
}
func (dm *DeviceManager) Start() error {
log.Println("Starting device monitoring...")
go dm.monitorDevices()
return nil
}
func (dm *DeviceManager) Stop() {
log.Println("Stopping device manager...")
close(dm.stopChan)
// Close all connections
if dm.powerGenius != nil {
dm.powerGenius.Close()
}
if dm.tunerGenius != nil {
dm.tunerGenius.Close()
}
if dm.antennaGenius != nil {
dm.antennaGenius.Close()
}
if dm.rotatorGenius != nil {
dm.rotatorGenius.Close()
}
if dm.ultrabeam != nil {
dm.ultrabeam.Stop()
}
}
func (dm *DeviceManager) monitorDevices() {
ticker := time.NewTicker(dm.updateInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
dm.updateStatus()
case <-dm.stopChan:
return
}
}
}
func (dm *DeviceManager) updateStatus() {
status := &SystemStatus{
Timestamp: time.Now(),
}
// Query all devices
// WebSwitch - get actual relay states
if wsStatus, err := dm.webSwitch.GetStatus(); err == nil {
status.WebSwitch = wsStatus
} else {
log.Printf("WebSwitch error: %v", err)
}
// Power Genius
if pgStatus, err := dm.powerGenius.GetStatus(); err == nil {
status.PowerGenius = pgStatus
} else {
log.Printf("Power Genius error: %v", err)
}
// Tuner Genius
if tgStatus, err := dm.tunerGenius.GetStatus(); err == nil {
status.TunerGenius = tgStatus
} else {
log.Printf("Tuner Genius error: %v", err)
}
// Antenna Genius
if agStatus, err := dm.antennaGenius.GetStatus(); err == nil {
status.AntennaGenius = agStatus
} else {
log.Printf("Antenna Genius error: %v", err)
}
// Rotator Genius
if rgStatus, err := dm.rotatorGenius.GetStatus(); err == nil {
status.RotatorGenius = rgStatus
} else {
log.Printf("Rotator Genius error: %v", err)
}
// Ultrabeam
if ubStatus, err := dm.ultrabeam.GetStatus(); err == nil {
status.Ultrabeam = ubStatus
} else {
log.Printf("Ultrabeam error: %v", err)
}
// Solar Data (fetched every 15 minutes, cached)
if solarData, err := dm.solarClient.GetSolarData(); err == nil {
status.Solar = solarData
} else {
log.Printf("Solar data error: %v", err)
}
// Weather Data (fetched every 10 minutes, cached)
if weatherData, err := dm.weatherClient.GetWeatherData(); err == nil {
status.Weather = weatherData
} else {
log.Printf("Weather data error: %v", err)
}
// Update cached status
dm.statusMu.Lock()
dm.lastStatus = status
dm.statusMu.Unlock()
// Broadcast to all connected clients
if dm.hub != nil {
dm.hub.BroadcastStatusUpdate(status)
}
}
func (dm *DeviceManager) GetStatus() *SystemStatus {
dm.statusMu.RLock()
defer dm.statusMu.RUnlock()
if dm.lastStatus == nil {
return &SystemStatus{
Timestamp: time.Now(),
}
}
return dm.lastStatus
}
// Device control methods
func (dm *DeviceManager) WebSwitch() *webswitch.Client {
return dm.webSwitch
}
func (dm *DeviceManager) PowerGenius() *powergenius.Client {
return dm.powerGenius
}
func (dm *DeviceManager) TunerGenius() *tunergenius.Client {
return dm.tunerGenius
}
func (dm *DeviceManager) AntennaGenius() *antennagenius.Client {
return dm.antennaGenius
}
func (dm *DeviceManager) RotatorGenius() *rotatorgenius.Client {
return dm.rotatorGenius
}
func (dm *DeviceManager) Ultrabeam() *ultrabeam.Client {
return dm.ultrabeam
}