Files
go-telldus-matter/pkg/config/parser.go
Mats Tornberg b4f657e96c fix
2025-11-22 23:55:01 +01:00

290 lines
7.1 KiB
Go

package config
import (
"bufio"
"fmt"
"os"
"strings"
)
// Device represents a device configuration in tellstick.conf
type Device struct {
ID int `json:"id"`
Name string `json:"name"`
Protocol string `json:"protocol"`
Model string `json:"model"`
Parameters map[string]string `json:"parameters"`
}
// Controller represents a controller configuration
type Controller struct {
ID int `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Serial string `json:"serial,omitempty"`
}
// Config represents the entire tellstick.conf structure
type Config struct {
User string `json:"user,omitempty"`
Group string `json:"group,omitempty"`
DevicePath string `json:"devicePath,omitempty"`
IgnoreControllerConfirmation int `json:"ignoreControllerConfirmation,omitempty"`
Controllers []Controller `json:"controllers"`
Devices []Device `json:"devices"`
}
// Parser handles parsing and writing tellstick.conf files
type Parser struct {
filePath string
}
// NewParser creates a new config parser
func NewParser(filePath string) *Parser {
return &Parser{filePath: filePath}
}
// Parse reads and parses the tellstick.conf file
func (p *Parser) Parse() (*Config, error) {
file, err := os.Open(p.filePath)
if err != nil {
return nil, err
}
defer file.Close()
config := &Config{
Controllers: []Controller{},
Devices: []Device{},
}
scanner := bufio.NewScanner(file)
var currentSection string
var currentDevice *Device
var currentController *Controller
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// Skip empty lines and comments
if line == "" || strings.HasPrefix(line, "#") {
continue
}
// Check for section headers
if strings.HasPrefix(line, "user") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
config.User = strings.Trim(strings.TrimSpace(parts[1]), "\"")
}
continue
}
if strings.HasPrefix(line, "group") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
config.Group = strings.Trim(strings.TrimSpace(parts[1]), "\"")
}
continue
}
if strings.HasPrefix(line, "deviceNode") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
config.DevicePath = strings.Trim(strings.TrimSpace(parts[1]), "\"")
}
continue
}
if strings.HasPrefix(line, "ignoreControllerConfirmation") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
fmt.Sscanf(strings.TrimSpace(parts[1]), "%d", &config.IgnoreControllerConfirmation)
}
continue
}
// Section detection
if strings.HasPrefix(line, "controller {") || strings.HasPrefix(line, "controller{") {
currentSection = "controller"
currentController = &Controller{}
continue
}
if strings.HasPrefix(line, "device {") || strings.HasPrefix(line, "device{") {
currentSection = "device"
currentDevice = &Device{
Parameters: make(map[string]string),
}
continue
}
// End of section
if line == "}" {
if currentSection == "device" && currentDevice != nil {
config.Devices = append(config.Devices, *currentDevice)
currentDevice = nil
}
if currentSection == "controller" && currentController != nil {
config.Controllers = append(config.Controllers, *currentController)
currentController = nil
}
currentSection = ""
continue
}
// Parse device/controller properties
if currentSection == "device" && currentDevice != nil {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.Trim(strings.TrimSpace(parts[1]), "\"")
switch key {
case "id":
fmt.Sscanf(value, "%d", &currentDevice.ID)
case "name":
currentDevice.Name = value
case "protocol":
currentDevice.Protocol = value
case "model":
currentDevice.Model = value
default:
currentDevice.Parameters[key] = value
}
}
}
if currentSection == "controller" && currentController != nil {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.Trim(strings.TrimSpace(parts[1]), "\"")
switch key {
case "id":
fmt.Sscanf(value, "%d", &currentController.ID)
case "name":
currentController.Name = value
case "type":
currentController.Type = value
case "serial":
currentController.Serial = value
}
}
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
return config, nil
}
// Write writes the configuration to the tellstick.conf file
func (p *Parser) Write(config *Config) error {
file, err := os.Create(p.filePath)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
// Write global settings
if config.User != "" {
fmt.Fprintf(w, "user = \"%s\"\n", config.User)
}
if config.Group != "" {
fmt.Fprintf(w, "group = \"%s\"\n", config.Group)
}
if config.DevicePath != "" {
fmt.Fprintf(w, "deviceNode = \"%s\"\n", config.DevicePath)
}
if config.IgnoreControllerConfirmation > 0 {
fmt.Fprintf(w, "ignoreControllerConfirmation = %d\n", config.IgnoreControllerConfirmation)
}
w.WriteString("\n")
// Write controllers
for _, ctrl := range config.Controllers {
w.WriteString("controller {\n")
fmt.Fprintf(w, " id = %d\n", ctrl.ID)
if ctrl.Name != "" {
fmt.Fprintf(w, " name = \"%s\"\n", ctrl.Name)
}
if ctrl.Type != "" {
fmt.Fprintf(w, " type = \"%s\"\n", ctrl.Type)
}
if ctrl.Serial != "" {
fmt.Fprintf(w, " serial = \"%s\"\n", ctrl.Serial)
}
w.WriteString("}\n\n")
}
// Write devices
for _, dev := range config.Devices {
w.WriteString("device {\n")
fmt.Fprintf(w, " id = %d\n", dev.ID)
fmt.Fprintf(w, " name = \"%s\"\n", dev.Name)
fmt.Fprintf(w, " protocol = \"%s\"\n", dev.Protocol)
if dev.Model != "" {
fmt.Fprintf(w, " model = \"%s\"\n", dev.Model)
}
// Write parameters
for key, value := range dev.Parameters {
fmt.Fprintf(w, " %s = \"%s\"\n", key, value)
}
w.WriteString("}\n\n")
}
return w.Flush()
}
// GetDevice returns a device by ID
func (c *Config) GetDevice(id int) *Device {
for i := range c.Devices {
if c.Devices[i].ID == id {
return &c.Devices[i]
}
}
return nil
}
// AddDevice adds a new device to the configuration
func (c *Config) AddDevice(device Device) {
c.Devices = append(c.Devices, device)
}
// UpdateDevice updates an existing device
func (c *Config) UpdateDevice(device Device) bool {
for i := range c.Devices {
if c.Devices[i].ID == device.ID {
c.Devices[i] = device
return true
}
}
return false
}
// DeleteDevice removes a device by ID
func (c *Config) DeleteDevice(id int) bool {
for i := range c.Devices {
if c.Devices[i].ID == id {
c.Devices = append(c.Devices[:i], c.Devices[i+1:]...)
return true
}
}
return false
}
// GetNextDeviceID returns the next available device ID
func (c *Config) GetNextDeviceID() int {
maxID := 0
for _, dev := range c.Devices {
if dev.ID > maxID {
maxID = dev.ID
}
}
return maxID + 1
}