This commit is contained in:
Mats Tornberg
2025-11-22 17:35:24 +01:00
parent 0596fe60fa
commit 87660c7d1d
12 changed files with 34 additions and 39 deletions

169
pkg/devices/manager.go Normal file
View File

@@ -0,0 +1,169 @@
package devices
import (
"app/pkg/datastore"
"app/pkg/mqtt"
"app/pkg/telldus"
"fmt"
"log"
"strconv"
"strings"
"sync"
"time"
)
// EventManager handles telldus events and callbacks
type EventManager struct {
store *datastore.DataStore
mqttClient *mqtt.Client
rawEvents []datastore.RawEvent
sensorEvents []datastore.SensorEvent
mu sync.Mutex
maxEvents int
}
// NewEventManager creates a new event manager
func NewEventManager(store *datastore.DataStore, mqttClient *mqtt.Client, maxEvents int) *EventManager {
return &EventManager{
store: store,
mqttClient: mqttClient,
maxEvents: maxEvents,
}
}
// GetRawEvents returns a copy of raw events
func (em *EventManager) GetRawEvents() []datastore.RawEvent {
em.mu.Lock()
defer em.mu.Unlock()
events := make([]datastore.RawEvent, len(em.rawEvents))
copy(events, em.rawEvents)
return events
}
// GetSensorEvents returns a copy of sensor events
func (em *EventManager) GetSensorEvents() []datastore.SensorEvent {
em.mu.Lock()
defer em.mu.Unlock()
events := make([]datastore.SensorEvent, len(em.sensorEvents))
copy(events, em.sensorEvents)
return events
}
// HandleDeviceEvent handles device state change events
func (em *EventManager) HandleDeviceEvent(deviceID, method int, data string, callbackID int) {
fmt.Printf("Device event: ID=%d, Method=%d, Data=%s\n", deviceID, method, data)
var state string
switch method {
case telldus.MethodTurnOn:
state = "ON"
case telldus.MethodTurnOff:
state = "OFF"
}
if state != "" {
em.mqttClient.PublishDeviceState(deviceID, state)
}
}
// HandleSensorEvent handles sensor data events
func (em *EventManager) HandleSensorEvent(protocol, model string, id, dataType int, value string, timestamp, callbackID int) {
fmt.Printf("Sensor event: Protocol=%s, Model=%s, ID=%d, Type=%d, Value=%s, Timestamp=%d\n",
protocol, model, id, dataType, value, timestamp)
// Publish to MQTT
em.mqttClient.PublishSensorValue(protocol, model, id, dataType, value)
// Store in history
em.mu.Lock()
em.sensorEvents = append(em.sensorEvents, datastore.SensorEvent{
Timestamp: time.Now(),
Protocol: protocol,
Model: model,
ID: id,
DataType: dataType,
Value: value,
})
if len(em.sensorEvents) > em.maxEvents {
em.sensorEvents = em.sensorEvents[1:]
}
em.mu.Unlock()
// Update last value in DB
if err := em.store.UpdateSensorValue(protocol, model, id, dataType, value); err != nil {
log.Printf("Error updating sensor %s %s %d: %v", protocol, model, id, err)
}
}
// HandleRawDeviceEvent handles raw device detection events
func (em *EventManager) HandleRawDeviceEvent(data string, controllerID, callbackID int) {
fmt.Printf("Raw device event: ControllerID=%d, Data=%s\n", controllerID, data)
// Parse data
fields := strings.Split(data, ";")
var class, protocol, model, deviceID string
for _, field := range fields {
kv := strings.SplitN(field, ":", 2)
if len(kv) == 2 {
key, val := kv[0], kv[1]
switch key {
case "class":
class = val
case "protocol":
protocol = val
case "model":
model = val
case "id":
deviceID = val
}
}
}
// Store in potential_devices
potentialDev := &datastore.PotentialDevice{
Class: class,
Protocol: protocol,
Model: model,
DeviceID: deviceID,
LastData: data,
LastSeen: time.Now().Unix(),
}
if err := em.store.UpsertPotentialDevice(potentialDev); err != nil {
log.Printf("Error storing potential device: %v", err)
}
// If sensor, ensure in sensors table
if class == "sensor" {
idInt, _ := strconv.Atoi(deviceID)
sensor := &datastore.Sensor{
Protocol: protocol,
Model: model,
ID: idInt,
Name: fmt.Sprintf("%s %s %s", protocol, model, deviceID),
TemperatureUniqueID: fmt.Sprintf("telldus_sensor_%s_%s_%s_temperature", protocol, model, deviceID),
HumidityUniqueID: fmt.Sprintf("telldus_sensor_%s_%s_%s_humidity", protocol, model, deviceID),
}
if err := em.store.UpsertSensor(sensor); err != nil {
log.Printf("Error inserting sensor from raw: %v", err)
}
}
// Log the raw event data
em.mu.Lock()
em.rawEvents = append(em.rawEvents, datastore.RawEvent{
Timestamp: time.Now(),
ControllerID: controllerID,
Data: data,
})
if len(em.rawEvents) > em.maxEvents {
em.rawEvents = em.rawEvents[1:]
}
em.mu.Unlock()
}
// RegisterCallbacks registers all event callbacks with telldus
func (em *EventManager) RegisterCallbacks() {
telldus.RegisterDeviceEvent(em.HandleDeviceEvent)
telldus.RegisterSensorEvent(em.HandleSensorEvent)
telldus.RegisterRawDeviceEvent(em.HandleRawDeviceEvent)
}

110
pkg/devices/sync.go Normal file
View File

@@ -0,0 +1,110 @@
package devices
import (
"app/pkg/datastore"
"app/pkg/telldus"
"fmt"
"log"
)
// Syncer handles synchronization of devices and sensors to the database
type Syncer struct {
store *datastore.DataStore
}
// NewSyncer creates a new device/sensor syncer
func NewSyncer(store *datastore.DataStore) *Syncer {
return &Syncer{store: store}
}
// SyncDevices synchronizes all telldus devices to the database
func (s *Syncer) SyncDevices() error {
numDevices := telldus.GetNumberOfDevices()
for i := 0; i < numDevices; i++ {
deviceID := telldus.GetDeviceId(i)
name := telldus.GetName(deviceID)
device := &datastore.Device{
ID: deviceID,
Name: name,
UniqueID: fmt.Sprintf("telldus_device_%d", deviceID),
}
if err := s.store.UpsertDevice(device); err != nil {
log.Printf("Error upserting device %d: %v", deviceID, err)
}
}
return nil
}
// SyncSensors synchronizes all telldus sensors to the database
func (s *Syncer) SyncSensors() error {
var protocol, model string
var id, dataTypes int
ret := telldus.Sensor(&protocol, &model, &id, &dataTypes)
for ret == 0 {
sensor := &datastore.Sensor{
Protocol: protocol,
Model: model,
ID: id,
Name: fmt.Sprintf("%s %s %d", protocol, model, id),
}
if dataTypes&telldus.DataTypeTemperature != 0 {
sensor.TemperatureUniqueID = fmt.Sprintf("telldus_sensor_%s_%s_%d_temperature", protocol, model, id)
}
if dataTypes&telldus.DataTypeHumidity != 0 {
sensor.HumidityUniqueID = fmt.Sprintf("telldus_sensor_%s_%s_%d_humidity", protocol, model, id)
}
if err := s.store.UpsertSensor(sensor); err != nil {
log.Printf("Error upserting sensor %s %s %d: %v", protocol, model, id, err)
}
ret = telldus.Sensor(&protocol, &model, &id, &dataTypes)
}
return nil
}
// ListDevices prints all devices to stdout
func (s *Syncer) ListDevices() {
numDevices := telldus.GetNumberOfDevices()
if numDevices < 0 {
errStr := telldus.GetErrorString(numDevices)
fmt.Printf("Error fetching devices: %s\n", errStr)
return
}
fmt.Printf("Number of devices: %d\n", numDevices)
for i := 0; i < numDevices; i++ {
deviceID := telldus.GetDeviceId(i)
name := telldus.GetName(deviceID)
protocol := telldus.GetProtocol(deviceID)
model := telldus.GetModel(deviceID)
fmt.Printf("%d\t%s\tProtocol: %s\tModel: %s\n", deviceID, name, protocol, model)
}
}
// ListSensors prints all sensors to stdout
func (s *Syncer) ListSensors() {
fmt.Println("\nSENSORS:")
var protocol, model string
var id, dataTypes int
ret := telldus.Sensor(&protocol, &model, &id, &dataTypes)
if ret == 0 {
for {
fmt.Printf("Protocol: %s, Model: %s, ID: %d, DataTypes: %d\n", protocol, model, id, dataTypes)
// Fetch values if available
if dataTypes&telldus.DataTypeTemperature != 0 {
value, timestamp, _ := telldus.SensorValue(protocol, model, id, telldus.DataTypeTemperature)
fmt.Printf(" Temperature: %s°C at %d\n", value, timestamp)
}
if dataTypes&telldus.DataTypeHumidity != 0 {
value, timestamp, _ := telldus.SensorValue(protocol, model, id, telldus.DataTypeHumidity)
fmt.Printf(" Humidity: %s%% at %d\n", value, timestamp)
}
ret = telldus.Sensor(&protocol, &model, &id, &dataTypes)
if ret != 0 {
break
}
}
} else if ret != -6 { // Assuming -6 is TELLSTICK_ERROR_DEVICE_NOT_FOUND
errStr := telldus.GetErrorString(ret)
fmt.Printf("Error fetching sensors: %s\n", errStr)
}
}