package main import ( "app/telldus" "database/sql" "encoding/json" "fmt" "log" "net/http" "os" "os/signal" "strconv" "strings" "sync" "syscall" "time" mqtt "github.com/eclipse/paho.mqtt.golang" _ "github.com/mattn/go-sqlite3" ) const ( httpPort = ":8080" ) var mqttDev *mqttDevice var db *sql.DB type mqttDevice struct { client mqtt.Client } func newMqttDevice() *mqttDevice { opts := mqtt.NewClientOptions().AddBroker(os.Getenv("MQTT_URL")) opts.SetUsername(os.Getenv("MQTT_USER")) opts.SetPassword(os.Getenv("MQTT_PASSWORD")) client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { log.Fatal(token.Error()) } return &mqttDevice{client: client} } func (m *mqttDevice) publishDiscovery() { // Publish discovery for devices numDevices := telldus.GetNumberOfDevices() for i := 0; i < numDevices; i++ { deviceId := telldus.GetDeviceId(i) var name, uniqueId string err := db.QueryRow("SELECT name, unique_id FROM devices WHERE id = ?", deviceId).Scan(&name, &uniqueId) if err != nil { log.Printf("Device %d not in DB, skipping", deviceId) continue } topic := fmt.Sprintf("homeassistant/switch/%s/config", uniqueId) payload := fmt.Sprintf(`{ "name": "%s", "command_topic": "telldus/device/%d/set", "state_topic": "telldus/device/%d/state", "unique_id": "%s", "device": { "identifiers": ["telldus_%d"], "name": "%s", "manufacturer": "Telldus" } }`, name, deviceId, deviceId, uniqueId, deviceId, name) m.client.Publish(topic, 0, true, payload) } // Publish discovery for sensors var protocol, model string var id, dataTypes int ret := telldus.Sensor(&protocol, &model, &id, &dataTypes) for ret == 0 { var sensorName, tempUniqueId, humUniqueId string err := db.QueryRow("SELECT name, temperature_unique_id, humidity_unique_id FROM sensors WHERE protocol = ? AND model = ? AND id = ? AND hidden = 0", protocol, model, id).Scan(&sensorName, &tempUniqueId, &humUniqueId) if err != nil { log.Printf("Sensor %s %s %d not in DB, skipping", protocol, model, id) ret = telldus.Sensor(&protocol, &model, &id, &dataTypes) continue } if dataTypes&telldus.DataTypeTemperature != 0 && tempUniqueId != "" { topic := fmt.Sprintf("homeassistant/sensor/%s/config", tempUniqueId) payload := fmt.Sprintf(`{ "name": "%s Temperature", "state_topic": "telldus/sensor/%s/%s/%d/temperature", "unit_of_measurement": "°C", "device_class": "temperature", "unique_id": "%s", "device": { "identifiers": ["telldus_sensor_%s_%s_%d"], "name": "%s", "manufacturer": "Telldus" } }`, sensorName, protocol, model, id, tempUniqueId, protocol, model, id, sensorName) m.client.Publish(topic, 0, true, payload) } if dataTypes&telldus.DataTypeHumidity != 0 && humUniqueId != "" { topic := fmt.Sprintf("homeassistant/sensor/%s/config", humUniqueId) payload := fmt.Sprintf(`{ "name": "%s Humidity", "state_topic": "telldus/sensor/%s/%s/%d/humidity", "unit_of_measurement": "%%", "device_class": "humidity", "unique_id": "%s", "device": { "identifiers": ["telldus_sensor_%s_%s_%d"], "name": "%s", "manufacturer": "Telldus" } }`, sensorName, protocol, model, id, humUniqueId, protocol, model, id, sensorName) m.client.Publish(topic, 0, true, payload) } // Add other sensor types if needed ret = telldus.Sensor(&protocol, &model, &id, &dataTypes) } } func (m *mqttDevice) subscribeCommands() { numDevices := telldus.GetNumberOfDevices() for i := 0; i < numDevices; i++ { deviceId := telldus.GetDeviceId(i) topic := fmt.Sprintf("telldus/device/%d/set", deviceId) m.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) { payload := string(msg.Payload()) if payload == "ON" { telldus.TurnOn(deviceId) } else if payload == "OFF" { telldus.TurnOff(deviceId) } }) } } func (m *mqttDevice) republishDevice(deviceId int) { var name, uniqueId string err := db.QueryRow("SELECT name, unique_id FROM devices WHERE id = ?", deviceId).Scan(&name, &uniqueId) if err != nil { log.Printf("Error querying device %d: %v", deviceId, err) return } topic := fmt.Sprintf("homeassistant/switch/%s/config", uniqueId) payload := fmt.Sprintf(`{ "name": "%s", "command_topic": "telldus/device/%d/set", "state_topic": "telldus/device/%d/state", "unique_id": "%s", "device": { "identifiers": ["telldus_%d"], "name": "%s", "manufacturer": "Telldus" } }`, name, deviceId, deviceId, uniqueId, deviceId, name) m.client.Publish(topic, 0, true, payload) } func (m *mqttDevice) republishSensor(protocol, model string, id int) { var sensorName, tempUniqueId, humUniqueId string err := db.QueryRow("SELECT name, temperature_unique_id, humidity_unique_id FROM sensors WHERE protocol = ? AND model = ? AND id = ?", protocol, model, id).Scan(&sensorName, &tempUniqueId, &humUniqueId) if err != nil { log.Printf("Error querying sensor %s %s %d: %v", protocol, model, id, err) return } if tempUniqueId != "" { topic := fmt.Sprintf("homeassistant/sensor/%s/config", tempUniqueId) payload := fmt.Sprintf(`{ "name": "%s Temperature", "state_topic": "telldus/sensor/%s/%s/%d/temperature", "unit_of_measurement": "°C", "device_class": "temperature", "unique_id": "%s", "device": { "identifiers": ["telldus_sensor_%s_%s_%d"], "name": "%s", "manufacturer": "Telldus" } }`, sensorName, protocol, model, id, tempUniqueId, protocol, model, id, sensorName) m.client.Publish(topic, 0, true, payload) } if humUniqueId != "" { topic := fmt.Sprintf("homeassistant/sensor/%s/config", humUniqueId) payload := fmt.Sprintf(`{ "name": "%s Humidity", "state_topic": "telldus/sensor/%s/%s/%d/humidity", "unit_of_measurement": "%%", "device_class": "humidity", "unique_id": "%s", "device": { "identifiers": ["telldus_sensor_%s_%s_%d"], "name": "%s", "manufacturer": "Telldus" } }`, sensorName, protocol, model, id, humUniqueId, protocol, model, id, sensorName) m.client.Publish(topic, 0, true, payload) } } type RawEvent struct { Timestamp time.Time `json:"timestamp"` ControllerId int `json:"controller_id"` Data string `json:"data"` } type SensorEvent struct { Timestamp time.Time `json:"timestamp"` Protocol string `json:"protocol"` Model string `json:"model"` Id int `json:"id"` DataType int `json:"data_type"` Value string `json:"value"` } var rawEvents []RawEvent var sensorEvents []SensorEvent var mu sync.Mutex const maxEvents = 1000 func main() { // Initialize Telldus telldus.Init() defer telldus.Close() // Initialize SQLite DB var err error db, err = sql.Open("sqlite3", "./db/telldus.db") if err != nil { log.Fatal(err) } defer db.Close() // Create tables _, err = db.Exec(`CREATE TABLE IF NOT EXISTS devices ( id INTEGER PRIMARY KEY, name TEXT, unique_id TEXT UNIQUE )`) if err != nil { log.Fatal(err) } _, err = db.Exec(`CREATE TABLE IF NOT EXISTS sensors ( sensor_id INTEGER PRIMARY KEY AUTOINCREMENT, protocol TEXT, model TEXT, id INTEGER, name TEXT, temperature_unique_id TEXT, humidity_unique_id TEXT, last_temperature TEXT, last_humidity TEXT, last_timestamp INTEGER, hidden INTEGER DEFAULT 0, UNIQUE(protocol, model, id) )`) if err != nil { log.Fatal(err) } _, err = db.Exec(`CREATE TABLE IF NOT EXISTS potential_devices ( id INTEGER PRIMARY KEY AUTOINCREMENT, class TEXT, protocol TEXT, model TEXT, device_id TEXT, last_data TEXT, last_seen INTEGER )`) if err != nil { log.Fatal(err) } // Sync devices to DB numDevices := telldus.GetNumberOfDevices() for i := 0; i < numDevices; i++ { deviceId := telldus.GetDeviceId(i) name := telldus.GetName(deviceId) uniqueId := fmt.Sprintf("telldus_device_%d", deviceId) _, err = db.Exec("INSERT OR IGNORE INTO devices (id, name, unique_id) VALUES (?, ?, ?)", deviceId, name, uniqueId) if err != nil { log.Printf("Error inserting device %d: %v", deviceId, err) } } // Sync sensors to DB var protocol, model string var id, dataTypes int ret := telldus.Sensor(&protocol, &model, &id, &dataTypes) for ret == 0 { sensorName := fmt.Sprintf("%s %s %d", protocol, model, id) tempUniqueId := "" humUniqueId := "" if dataTypes&telldus.DataTypeTemperature != 0 { tempUniqueId = fmt.Sprintf("telldus_sensor_%s_%s_%d_temperature", protocol, model, id) } if dataTypes&telldus.DataTypeHumidity != 0 { humUniqueId = fmt.Sprintf("telldus_sensor_%s_%s_%d_humidity", protocol, model, id) } _, err = db.Exec("INSERT OR IGNORE INTO sensors (protocol, model, id, name, temperature_unique_id, humidity_unique_id) VALUES (?, ?, ?, ?, ?, ?)", protocol, model, id, sensorName, tempUniqueId, humUniqueId) if err != nil { log.Printf("Error inserting sensor %s %s %d: %v", protocol, model, id, err) } ret = telldus.Sensor(&protocol, &model, &id, &dataTypes) } // Initialize MQTT mqttDev = newMqttDevice() defer mqttDev.client.Disconnect(250) // Publish Home Assistant discovery mqttDev.publishDiscovery() // Subscribe to command topics mqttDev.subscribeCommands() // List devices and sensors listDevices() listSensors() // Register callbacks telldus.RegisterDeviceEvent(deviceEventHandler) telldus.RegisterSensorEvent(sensorEventHandler) telldus.RegisterRawDeviceEvent(rawDeviceEventHandler) // Setup graceful shutdown c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c log.Println("Shutting down gracefully...") mqttDev.client.Disconnect(250) telldus.Close() os.Exit(0) }() // Start HTTP server log.Println("Server starting on", httpPort) log.Fatal(http.ListenAndServe(httpPort, setupRoutes())) } func deviceEventHandler(deviceId, method int, data string, callbackId int) { fmt.Printf("Device event: ID=%d, Method=%d, Data=%s\n", deviceId, method, data) // Publish state to MQTT var state string switch method { case telldus.MethodTurnOn: state = "ON" case telldus.MethodTurnOff: state = "OFF" } if state != "" { topic := fmt.Sprintf("telldus/device/%d/state", deviceId) mqttDev.client.Publish(topic, 0, false, state) } // Log or handle locally, no MQTT } func sensorEventHandler(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 var topic string switch dataType { case telldus.DataTypeTemperature: topic = fmt.Sprintf("telldus/sensor/%s/%s/%d/temperature", protocol, model, id) case telldus.DataTypeHumidity: topic = fmt.Sprintf("telldus/sensor/%s/%s/%d/humidity", protocol, model, id) } if topic != "" { mqttDev.client.Publish(topic, 0, false, value) } // Store in history mu.Lock() sensorEvents = append(sensorEvents, SensorEvent{time.Now(), protocol, model, id, dataType, value}) if len(sensorEvents) > maxEvents { sensorEvents = sensorEvents[1:] } mu.Unlock() // Update last value in DB var column string switch dataType { case telldus.DataTypeTemperature: column = "last_temperature" case telldus.DataTypeHumidity: column = "last_humidity" } if column != "" { _, err := db.Exec(fmt.Sprintf("UPDATE sensors SET %s = ?, last_timestamp = ? WHERE protocol = ? AND model = ? AND id = ?", column), value, time.Now().Unix(), protocol, model, id) if err != nil { log.Printf("Error updating sensor %s %s %d: %v", protocol, model, id, err) } } // Log or handle locally, no MQTT } func rawDeviceEventHandler(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 _, err := db.Exec("INSERT OR REPLACE INTO potential_devices (class, protocol, model, device_id, last_data, last_seen) VALUES (?, ?, ?, ?, ?, ?)", class, protocol, model, deviceId, data, time.Now().Unix()) if err != nil { log.Printf("Error storing potential device: %v", err) } // If sensor, ensure in sensors table if class == "sensor" { idInt, _ := strconv.Atoi(deviceId) sensorName := fmt.Sprintf("%s %s %s", protocol, model, deviceId) tempUniqueId := fmt.Sprintf("telldus_sensor_%s_%s_%s_temperature", protocol, model, deviceId) humUniqueId := fmt.Sprintf("telldus_sensor_%s_%s_%s_humidity", protocol, model, deviceId) _, err = db.Exec("INSERT OR IGNORE INTO sensors (protocol, model, id, name, temperature_unique_id, humidity_unique_id) VALUES (?, ?, ?, ?, ?, ?)", protocol, model, idInt, sensorName, tempUniqueId, humUniqueId) if err != nil { log.Printf("Error inserting sensor from raw: %v", err) } } // Log the raw event data mu.Lock() rawEvents = append(rawEvents, RawEvent{time.Now(), controllerId, data}) if len(rawEvents) > maxEvents { rawEvents = rawEvents[1:] } mu.Unlock() } func getRawEvents(w http.ResponseWriter, r *http.Request) { mu.Lock() defer mu.Unlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(rawEvents) } func getSensorEvents(w http.ResponseWriter, r *http.Request) { mu.Lock() defer mu.Unlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(sensorEvents) } func getPotentialDevices(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT class, protocol, model, device_id, last_data, last_seen FROM potential_devices ORDER BY last_seen DESC") if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } defer rows.Close() devices := []map[string]interface{}{} for rows.Next() { var class, protocol, model, deviceId, lastData string var lastSeen int err := rows.Scan(&class, &protocol, &model, &deviceId, &lastData, &lastSeen) if err != nil { http.Error(w, "Scan error", http.StatusInternalServerError) return } devices = append(devices, map[string]interface{}{ "class": class, "protocol": protocol, "model": model, "device_id": deviceId, "last_data": lastData, "last_seen": lastSeen, }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(devices) } func renameDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid device ID", http.StatusBadRequest) return } var req struct { Name string `json:"name"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return } _, err = db.Exec("UPDATE devices SET name = ? WHERE id = ?", req.Name, id) if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } // Republish discovery for this device mqttDev.republishDevice(id) w.WriteHeader(http.StatusOK) } func renameSensor(w http.ResponseWriter, r *http.Request) { sensorIdStr := r.PathValue("sensor_id") sensorId, err := strconv.Atoi(sensorIdStr) if err != nil { http.Error(w, "Invalid sensor ID", http.StatusBadRequest) return } var req struct { Name string `json:"name"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return } var protocol, model string var id int err = db.QueryRow("SELECT protocol, model, id FROM sensors WHERE sensor_id = ?", sensorId).Scan(&protocol, &model, &id) if err != nil { http.Error(w, "Sensor not found", http.StatusNotFound) return } _, err = db.Exec("UPDATE sensors SET name = ? WHERE sensor_id = ?", req.Name, sensorId) if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } // Republish discovery for this sensor mqttDev.republishSensor(protocol, model, id) w.WriteHeader(http.StatusOK) } func hideSensor(w http.ResponseWriter, r *http.Request) { sensorIdStr := r.PathValue("sensor_id") sensorId, err := strconv.Atoi(sensorIdStr) if err != nil { http.Error(w, "Invalid sensor ID", http.StatusBadRequest) return } _, err = db.Exec("UPDATE sensors SET hidden = 1 WHERE sensor_id = ?", sensorId) if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } func unhideSensor(w http.ResponseWriter, r *http.Request) { sensorIdStr := r.PathValue("sensor_id") sensorId, err := strconv.Atoi(sensorIdStr) if err != nil { http.Error(w, "Invalid sensor ID", http.StatusBadRequest) return } _, err = db.Exec("UPDATE sensors SET hidden = 0 WHERE sensor_id = ?", sensorId) if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } func setupRoutes() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("/api/devices", getDevices) mux.HandleFunc("POST /api/devices/{id}/turnon", turnOnDevice) mux.HandleFunc("POST /api/devices/{id}/learn", learnDevice) mux.HandleFunc("POST /api/devices/{id}/turnoff", turnOffDevice) mux.HandleFunc("/api/sensors", getSensors) mux.HandleFunc("/api/devices/{id}", getDevice) mux.HandleFunc("PUT /api/devices/{id}", renameDevice) mux.HandleFunc("PUT /api/sensors/{sensor_id}", renameSensor) mux.HandleFunc("PUT /api/sensors/{sensor_id}/hide", hideSensor) mux.HandleFunc("PUT /api/sensors/{sensor_id}/unhide", unhideSensor) mux.HandleFunc("/api/sensors/{sensor_id}", getSensor) mux.HandleFunc("/api/events/raw", getRawEvents) mux.HandleFunc("/api/events/sensor", getSensorEvents) mux.HandleFunc("/api/potential_devices", getPotentialDevices) // Serve static files for the frontend mux.Handle("/", http.FileServer(http.Dir("./dist"))) return mux } func getDevices(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT id, name FROM devices ORDER BY id") if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } defer rows.Close() devices := []map[string]interface{}{} for rows.Next() { var id int var name string err := rows.Scan(&id, &name) if err != nil { http.Error(w, "Scan error", http.StatusInternalServerError) return } devices = append(devices, map[string]interface{}{ "id": id, "name": name, }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(devices) } type CommandResult struct { Success bool `json:"success"` Message string `json:"message,omitempty"` Code int `json:"code,omitempty"` } func turnOnDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid device ID", http.StatusBadRequest) return } status := telldus.TurnOn(id) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(CommandResult{Success: status == 0, Message: "turned on", Code: status}) } func learnDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid device ID", http.StatusBadRequest) return } status := telldus.Learn(id) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(CommandResult{Success: status == 0, Message: "learning started", Code: status}) } func turnOffDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid device ID", http.StatusBadRequest) return } status := telldus.TurnOff(id) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(CommandResult{Success: status == 0, Message: "turned off", Code: status}) } func getSensors(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT sensor_id, protocol, model, id, name, last_temperature, last_humidity, last_timestamp, hidden FROM sensors ORDER BY sensor_id") if err != nil { http.Error(w, "Database error", http.StatusInternalServerError) return } defer rows.Close() sensors := []map[string]interface{}{} for rows.Next() { var sensorId int var protocol, model, name, lastTemp, lastHum string var id, lastTs, hidden int err := rows.Scan(&sensorId, &protocol, &model, &id, &name, &lastTemp, &lastHum, &lastTs, &hidden) if err != nil { http.Error(w, "Scan error", http.StatusInternalServerError) return } sensor := map[string]interface{}{ "sensor_id": sensorId, "protocol": protocol, "model": model, "id": id, "name": name, "hidden": hidden != 0, } if lastTemp != "" { sensor["last_temperature"] = lastTemp } if lastHum != "" { sensor["last_humidity"] = lastHum } if lastTs > 0 { sensor["last_timestamp"] = lastTs } sensors = append(sensors, sensor) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(sensors) } func getDevice(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid device ID", http.StatusBadRequest) return } var name string err = db.QueryRow("SELECT name FROM devices WHERE id = ?", id).Scan(&name) if err != nil { http.Error(w, "Device not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "id": id, "name": name, }) } func getSensor(w http.ResponseWriter, r *http.Request) { sensorIdStr := r.PathValue("sensor_id") sensorId, err := strconv.Atoi(sensorIdStr) if err != nil { http.Error(w, "Invalid sensor ID", http.StatusBadRequest) return } var protocol, model, name string var id int err = db.QueryRow("SELECT protocol, model, id, name FROM sensors WHERE sensor_id = ?", sensorId).Scan(&protocol, &model, &id, &name) if err != nil { http.Error(w, "Sensor not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "sensor_id": sensorId, "protocol": protocol, "model": model, "id": id, "name": name, }) } func 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) } } func 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) } // Add other types as needed 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) } }