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
|
# Set workdir back
|
||||||
WORKDIR /go/src/app
|
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
|
EXPOSE 8080
|
||||||
|
|
||||||
# Run the application
|
# Run the application (it will manage telldusd internally)
|
||||||
CMD ["./start.sh"]
|
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
|
go 1.25.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.1 // indirect
|
github.com/eclipse/paho.mqtt.golang v1.5.1
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
golang.org/x/net v0.44.0 // indirect
|
)
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
|
||||||
|
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 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
|
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 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
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 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
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 (
|
import (
|
||||||
"app/telldus"
|
"app/telldus"
|
||||||
|
"bufio"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -17,6 +20,7 @@ import (
|
|||||||
|
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -25,6 +29,8 @@ const (
|
|||||||
|
|
||||||
var mqttDev *mqttDevice
|
var mqttDev *mqttDevice
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
|
var telldusCmd *exec.Cmd
|
||||||
|
var telldusMu sync.Mutex
|
||||||
|
|
||||||
type mqttDevice struct {
|
type mqttDevice struct {
|
||||||
client mqtt.Client
|
client mqtt.Client
|
||||||
@@ -216,7 +222,189 @@ var mu sync.Mutex
|
|||||||
|
|
||||||
const maxEvents = 1000
|
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() {
|
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
|
// Initialize Telldus
|
||||||
telldus.Init()
|
telldus.Init()
|
||||||
defer telldus.Close()
|
defer telldus.Close()
|
||||||
@@ -328,6 +516,7 @@ func main() {
|
|||||||
log.Println("Shutting down gracefully...")
|
log.Println("Shutting down gracefully...")
|
||||||
mqttDev.client.Disconnect(250)
|
mqttDev.client.Disconnect(250)
|
||||||
telldus.Close()
|
telldus.Close()
|
||||||
|
stopTelldusd()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user