more refinements

This commit is contained in:
Mats Tornberg
2025-11-22 15:57:36 +00:00
parent 00f236cac7
commit 790b174a9e
8 changed files with 270 additions and 22 deletions

189
main.go
View File

@@ -2,13 +2,16 @@ package main
import (
"app/telldus"
"bufio"
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"strings"
"sync"
@@ -17,6 +20,7 @@ import (
mqtt "github.com/eclipse/paho.mqtt.golang"
_ "github.com/mattn/go-sqlite3"
"github.com/fsnotify/fsnotify"
)
const (
@@ -25,6 +29,8 @@ const (
var mqttDev *mqttDevice
var db *sql.DB
var telldusCmd *exec.Cmd
var telldusMu sync.Mutex
type mqttDevice struct {
client mqtt.Client
@@ -216,7 +222,189 @@ var mu sync.Mutex
const maxEvents = 1000
// startTelldusd starts the telldusd daemon and captures its output
func startTelldusd() error {
telldusMu.Lock()
defer telldusMu.Unlock()
if telldusCmd != nil && telldusCmd.Process != nil {
log.Println("Telldusd already running")
return nil
}
log.Println("Starting telldusd...")
cmd := exec.Command("/usr/local/sbin/telldusd", "--nodaemon")
// Capture stdout
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed to get stdout pipe: %v", err)
}
// Capture stderr
stderr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("failed to get stderr pipe: %v", err)
}
// Start the command
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start telldusd: %v", err)
}
telldusCmd = cmd
// Log stdout in a goroutine
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
log.Printf("[telldusd] %s", scanner.Text())
}
}()
// Log stderr in a goroutine
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
log.Printf("[telldusd] ERROR: %s", scanner.Text())
}
}()
// Monitor process in a goroutine
go func() {
err := cmd.Wait()
telldusMu.Lock()
telldusCmd = nil
telldusMu.Unlock()
if err != nil {
log.Printf("Telldusd exited with error: %v", err)
} else {
log.Println("Telldusd exited normally")
}
}()
// Give telldusd a moment to start
time.Sleep(500 * time.Millisecond)
log.Println("Telldusd started successfully")
return nil
}
// stopTelldusd stops the telldusd daemon
func stopTelldusd() error {
telldusMu.Lock()
defer telldusMu.Unlock()
if telldusCmd == nil || telldusCmd.Process == nil {
log.Println("Telldusd not running")
return nil
}
log.Println("Stopping telldusd...")
// Send SIGTERM
if err := telldusCmd.Process.Signal(syscall.SIGTERM); err != nil {
log.Printf("Failed to send SIGTERM to telldusd: %v", err)
// Try SIGKILL as fallback
if err := telldusCmd.Process.Kill(); err != nil {
return fmt.Errorf("failed to kill telldusd: %v", err)
}
}
// Wait for process to exit (with timeout)
done := make(chan error, 1)
go func() {
done <- telldusCmd.Wait()
}()
select {
case <-done:
log.Println("Telldusd stopped successfully")
case <-time.After(5 * time.Second):
log.Println("Telldusd did not stop gracefully, killing...")
telldusCmd.Process.Kill()
}
telldusCmd = nil
return nil
}
// restartTelldusd restarts the telldusd daemon
func restartTelldusd() error {
log.Println("Restarting telldusd due to configuration change...")
if err := stopTelldusd(); err != nil {
log.Printf("Error stopping telldusd: %v", err)
}
// Give it a moment to fully stop
time.Sleep(1 * time.Second)
// Close and reinitialize telldus library
telldus.Close()
time.Sleep(500 * time.Millisecond)
if err := startTelldusd(); err != nil {
return err
}
// Reinitialize telldus library
telldus.Init()
log.Println("Telldusd restarted successfully")
return nil
}
// watchConfigFile watches for changes to the tellstick.conf file
func watchConfigFile(configPath string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Printf("Failed to create file watcher: %v", err)
return
}
defer watcher.Close()
// Watch the parent directory since file operations might replace the file
configDir := filepath.Dir(configPath)
if err := watcher.Add(configDir); err != nil {
log.Printf("Failed to watch config directory: %v", err)
return
}
log.Printf("Watching for changes to %s", configPath)
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
// Check if the event is for our config file
if event.Name == configPath && (event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create) {
log.Printf("Configuration file changed: %s", event.Op.String())
if err := restartTelldusd(); err != nil {
log.Printf("Failed to restart telldusd: %v", err)
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Printf("File watcher error: %v", err)
}
}
}
func main() {
// Start telldusd daemon
if err := startTelldusd(); err != nil {
log.Fatalf("Failed to start telldusd: %v", err)
}
defer stopTelldusd()
// Start watching config file
configPath := "/etc/tellstick.conf"
go watchConfigFile(configPath)
// Initialize Telldus
telldus.Init()
defer telldus.Close()
@@ -328,6 +516,7 @@ func main() {
log.Println("Shutting down gracefully...")
mqttDev.client.Disconnect(250)
telldus.Close()
stopTelldusd()
os.Exit(0)
}()