more refinements
This commit is contained in:
28
.devcontainer/Dockerfile
Normal file
28
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM golang:1.25.4-bookworm
|
||||
|
||||
# Install dependencies for telldus-core
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
libssl-dev \
|
||||
libavahi-client-dev \
|
||||
libglib2.0-dev \
|
||||
libftdi-dev \
|
||||
libconfuse-dev \
|
||||
nodejs \
|
||||
npm \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build telldus-core
|
||||
COPY ./telldus-core /usr/src/telldus-core
|
||||
WORKDIR /usr/src/telldus-core
|
||||
RUN cmake . \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig
|
||||
|
||||
# Set workdir for the project
|
||||
WORKDIR /workspaces/go-telldus-matter
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV CGO_ENABLED=1
|
||||
33
.devcontainer/devcontainer.json
Normal file
33
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "Go Telldus Matter Dev",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
"workspaceFolder": "/workspaces/go-telldus-matter",
|
||||
"runArgs": [
|
||||
"--privileged"
|
||||
],
|
||||
"mounts": [
|
||||
"source=/dev/bus/usb,target=/dev/bus/usb,type=bind",
|
||||
"source=${localWorkspaceFolder}/tellstick.conf,target=/etc/tellstick.conf,type=bind"
|
||||
],
|
||||
"containerEnv": {
|
||||
"MQTT_URL": "10.10.3.12:1883",
|
||||
"MQTT_USER": "telldus-dev",
|
||||
"MQTT_PASSWORD": ""
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-vscode.Go",
|
||||
"ms-vscode.vscode-typescript-next"
|
||||
],
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "go mod download && cd frontend && npm install"
|
||||
}
|
||||
14
Dockerfile
14
Dockerfile
@@ -50,17 +50,7 @@ RUN cp -r dist /go/src/app/
|
||||
# Set workdir back
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# Create startup script
|
||||
RUN echo '#!/bin/bash\n\
|
||||
/usr/local/sbin/telldusd --nodaemon &\n\
|
||||
TELLDUSD_PID=$!\n\
|
||||
./main &\n\
|
||||
MAIN_PID=$!\n\
|
||||
trap "kill $MAIN_PID $TELLDUSD_PID; exit" INT TERM\n\
|
||||
wait $MAIN_PID\n\
|
||||
kill $TELLDUSD_PID' > start.sh && chmod +x start.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Run the application
|
||||
CMD ["./start.sh"]
|
||||
# Run the application (it will manage telldusd internally)
|
||||
CMD ["./main"]
|
||||
1
chip
1
chip
Submodule chip deleted from 9548aca151
BIN
db/telldus.db
BIN
db/telldus.db
Binary file not shown.
15
go.mod
15
go.mod
@@ -3,9 +3,14 @@ module app
|
||||
go 1.25.4
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
|
||||
12
go.sum
12
go.sum
@@ -1,10 +1,14 @@
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
|
||||
189
main.go
189
main.go
@@ -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)
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user