package main import ( "fmt" "log" "os" "os/signal" "syscall" "time" mqtt "github.com/eclipse/paho.mqtt.golang" "xengineering.eu/homematic-go/homematic" ) const ( OPENCCU = `http://127.0.0.1:8080` BROKER = `tcp://127.0.0.1:1883` CLIENT_ID = `sia-server` TOPIC_PREFIX = `sia` QOS = byte(0) RETAINED = false MQTT_CONNECT_TIMEOUT = time.Second * 5 MQTT_DISCONNECT_TIMEOUT_US = 500 POLLING_PERIOD = 500 * time.Millisecond ) func main() { log.Println("+++ Started Sia server +++") defer log.Println("--- Stopped Sia server ---") go func() { req, inventory, client, err := Start() defer Stop(client) if err != nil { log.Fatalf("Failed startup process: %v", err) } for { start := time.Now() err = Poll(req, inventory, client) if err != nil { log.Fatalf("Failed to poll states: %v", err) } WaitUntil(start.Add(POLLING_PERIOD)) } }() Await(syscall.SIGTERM, syscall.SIGINT) } func ConnectMQTT(broker string, id string) (mqtt.Client, error) { opts := mqtt.NewClientOptions() opts.AddBroker(broker) opts.SetClientID(id) opts.SetCleanSession(true) client := mqtt.NewClient(opts) token := client.Connect() success := token.WaitTimeout(MQTT_CONNECT_TIMEOUT) if !success { return client, fmt.Errorf("Timed out after %v.", MQTT_CONNECT_TIMEOUT) } err := token.Error() if err != nil { return client, err } return client, nil } func Start() (homematic.Requester, homematic.Devices, mqtt.Client, error) { var req homematic.Requester var inventory homematic.Devices var client mqtt.Client var err error req = homematic.NewRequester(OPENCCU) log.Printf("Created Homematic requester (%s).", OPENCCU) inventory, err = req.ListDevices() if err != nil { return req, inventory, client, fmt.Errorf("Failed getting initial device list: %w", err) } log.Printf("Retrieved Homematic inventory with %d devices.", len(inventory)) client, err = ConnectMQTT(BROKER, CLIENT_ID) if err != nil { return req, inventory, client, fmt.Errorf("Failed connecting to MQTT broker: %w", err) } log.Printf("Connected to MQTT broker (%s).", BROKER) return req, inventory, client, nil } func Stop(c mqtt.Client) { c.Disconnect(MQTT_DISCONNECT_TIMEOUT_US) log.Println("Disconnected from MQTT broker.") } func Poll(req homematic.Requester, inventory homematic.Devices, client mqtt.Client) error { for _, device := range inventory { if device.Type == `SHUTTER_CONTACT` { state, err := req.GetValue(device.Address) if err != nil { return fmt.Errorf("Failed to get value: %w", err) } topic := fmt.Sprintf("%s/contact/%s/state", TOPIC_PREFIX, device.Address) payload := []byte(fmt.Sprintf("%t", state)) _ = client.Publish(topic, QOS, RETAINED, payload) } } return nil } func Await(signals ...syscall.Signal) { listener := make(chan os.Signal) for _, s := range signals { signal.Notify(listener, s) } sig := <-listener log.Printf("Received OS signal '%v'\n", sig) } func WaitUntil(deadline time.Time) { duration := time.Until(deadline) if duration > 0 { time.Sleep(duration) } }