package mqtt import ( "encoding/json" "fmt" "log" "git.k7n.net/mats/go-telldus/pkg/datastore" ) // HADevice represents a device in Home Assistant MQTT discovery type HADevice struct { Identifiers []string `json:"identifiers"` Name string `json:"name"` Manufacturer string `json:"manufacturer"` Model string `json:"model,omitempty"` } // SwitchDiscovery represents Home Assistant MQTT switch discovery config // Reference: https://www.home-assistant.io/integrations/switch.mqtt/ type SwitchDiscovery struct { Platform string `json:"platform"` Name string `json:"name"` CommandTopic string `json:"command_topic"` StateTopic string `json:"state_topic"` UniqueID string `json:"unique_id"` Device HADevice `json:"device"` PayloadOn string `json:"payload_on,omitempty"` PayloadOff string `json:"payload_off,omitempty"` StateOn string `json:"state_on,omitempty"` StateOff string `json:"state_off,omitempty"` OptimisticMode bool `json:"optimistic,omitempty"` Qos int `json:"qos,omitempty"` Retain bool `json:"retain,omitempty"` AvailabilityTopic string `json:"availability_topic,omitempty"` } // SensorDiscovery represents Home Assistant MQTT sensor discovery config // Reference: https://www.home-assistant.io/integrations/sensor.mqtt/ type SensorDiscovery struct { Platform string `json:"platform"` Name string `json:"name"` StateTopic string `json:"state_topic"` UniqueID string `json:"unique_id"` Device HADevice `json:"device"` UnitOfMeasurement string `json:"unit_of_measurement,omitempty"` DeviceClass string `json:"device_class,omitempty"` StateClass string `json:"state_class,omitempty"` ValueTemplate string `json:"value_template,omitempty"` Qos int `json:"qos,omitempty"` AvailabilityTopic string `json:"availability_topic,omitempty"` } // PublishAllDiscovery publishes Home Assistant discovery messages for all devices and sensors func (c *Client) PublishAllDiscovery() error { if err := c.publishDeviceDiscovery(); err != nil { return err } if err := c.publishSensorDiscovery(); err != nil { return err } return nil } // publishDeviceDiscovery publishes discovery for all devices func (c *Client) publishDeviceDiscovery() error { for device := range c.store.ListDevices() { if err := c.PublishDeviceDiscovery(device.ID); err != nil { log.Printf("Error publishing discovery for device %d: %v", device.ID, err) } } return nil } // PublishDeviceDiscovery publishes Home Assistant discovery for a single device func (c *Client) PublishDeviceDiscovery(deviceID int) error { device, err := c.store.GetDevice(deviceID) if err != nil { return fmt.Errorf("device %d not found: %w", deviceID, err) } discovery := SwitchDiscovery{ Platform: "mqtt", Name: device.Name, CommandTopic: fmt.Sprintf("telldus/device/%d/set", deviceID), StateTopic: fmt.Sprintf("telldus/device/%d/state", deviceID), UniqueID: device.UniqueID, PayloadOn: "ON", PayloadOff: "OFF", StateOn: "ON", StateOff: "OFF", Device: HADevice{ Identifiers: []string{fmt.Sprintf("telldus_%d", deviceID)}, Name: device.Name, Manufacturer: "Telldus", Model: fmt.Sprintf("%s %s", device.Protocol, device.Model), }, } payload, err := json.Marshal(discovery) if err != nil { return fmt.Errorf("failed to marshal discovery: %w", err) } topic := fmt.Sprintf("homeassistant/switch/%s/config", device.UniqueID) c.client.Publish(topic, 0, true, payload) return nil } // publishSensorDiscovery publishes discovery for all sensors func (c *Client) publishSensorDiscovery() error { for sensor := range c.store.ListSensors() { if sensor.Hidden { log.Printf("Sensor %s %s %d is hidden, skipping", sensor.Protocol, sensor.Model, sensor.ID) continue } if err := c.PublishSensorDiscovery(sensor); err != nil { log.Printf("Error publishing discovery for sensor %s %s %d: %v", sensor.Protocol, sensor.Model, sensor.ID, err) } } return nil } // PublishSensorDiscovery publishes Home Assistant discovery for a single sensor func (c *Client) PublishSensorDiscovery(sensor *datastore.Sensor) error { return c.publishSensorDiscoveryForSensor(sensor) // return fmt.Errorf("sensor %s %s %d not found in telldus", protocol, model, id) } // publishSensorDiscoveryForSensor publishes discovery messages for a sensor's data types func (c *Client) publishSensorDiscoveryForSensor(sensor *datastore.Sensor) error { sensorDevice := HADevice{ Identifiers: []string{fmt.Sprintf("telldus_sensor_%s_%s_%d", sensor.Protocol, sensor.Model, sensor.ID)}, Name: sensor.Name, Manufacturer: "Telldus", Model: fmt.Sprintf("%s %s", sensor.Protocol, sensor.Model), } if sensor.TemperatureUniqueID != "" { discovery := SensorDiscovery{ Platform: "mqtt", Name: fmt.Sprintf("%s Temperature", sensor.Name), StateTopic: fmt.Sprintf("telldus/sensor/%s/%s/%d/temperature", sensor.Protocol, sensor.Model, sensor.ID), UniqueID: sensor.TemperatureUniqueID, UnitOfMeasurement: "°C", DeviceClass: "temperature", StateClass: "measurement", Device: sensorDevice, } payload, err := json.Marshal(discovery) if err != nil { return fmt.Errorf("failed to marshal temperature discovery: %w", err) } topic := fmt.Sprintf("homeassistant/sensor/%s/config", sensor.TemperatureUniqueID) c.client.Publish(topic, 0, true, payload) } if sensor.HumidityUniqueID != "" { discovery := SensorDiscovery{ Platform: "mqtt", Name: fmt.Sprintf("%s Humidity", sensor.Name), StateTopic: fmt.Sprintf("telldus/sensor/%s/%s/%d/humidity", sensor.Protocol, sensor.Model, sensor.ID), UniqueID: sensor.HumidityUniqueID, UnitOfMeasurement: "%", DeviceClass: "humidity", StateClass: "measurement", Device: sensorDevice, } payload, err := json.Marshal(discovery) if err != nil { return fmt.Errorf("failed to marshal humidity discovery: %w", err) } topic := fmt.Sprintf("homeassistant/sensor/%s/config", sensor.HumidityUniqueID) c.client.Publish(topic, 0, true, payload) } return nil }