New features for ESP devices direct over MQTT
authorjweigele <jweigele@local>
Mon, 15 Jan 2024 02:44:21 +0000 (18:44 -0800)
committerjweigele <jweigele@local>
Mon, 15 Jan 2024 02:44:21 +0000 (18:44 -0800)
 * Control ESP devices directly over a topic (rgb json floats)
 * Uptime tracking when ESP devices report it
 * Same for internal temperature sensor

lights/main.go
reprocess/main.go
wunder/main.go

index 7c768fe27dc66847e3f0534db9aba0a5d07328be..3b2ef07ade2cd838a2dc70ecbf75789a6b8e64d0 100644 (file)
@@ -51,6 +51,7 @@ var (
 // config superstructure
 type RGBConfigYaml struct {
        RGBRelays []RGBRelay `yaml:"RGBRelays"`
+       ESPRelays []ESPRelay `yaml:"ESPRelays"`
        RGBZigs   []RGBZig   `yaml:"RGBZigs"`
        RGBPicos  []RGBPico  `yaml:"RGBPicos"`
        Switches  []Switch   `yaml:"Switches"`
@@ -181,6 +182,7 @@ type IRGBRelay interface {
        addSwitch(newSwitch *Switch)
        setPWM(red, green, blue float64)
        getLocation() string
+       getRoutingKey() string
        getMetricValue() float64
 }
 
@@ -212,7 +214,26 @@ func (relayData RelayData) getRelayData(red, green, blue float64) (map[string]in
        retval["location"] = relayData.parent.Location
        relayData.parent.logger.V(2).Info("Final relay getRelayData output", "retval", retval)
 
-       return retval, "rgb_relay"
+       return retval, relayData.parent.getRoutingKey()
+
+}
+
+// just copies the previous one with a diff receiver and no location, almost certainly a cleaner way to do this
+func (relayData ESPRelayData) getRelayData(red, green, blue float64) (map[string]interface{}, string) {
+       relayData.parent.logger.V(3).Info("Entering getRelayData for relay")
+       retval := make(map[string]interface{}, 0)
+       if relayData.parent.LastState {
+               retval["red"] = red
+               retval["green"] = green
+               retval["blue"] = blue
+       } else {
+               retval["red"] = 0.0
+               retval["green"] = 0.0
+               retval["blue"] = 0.0
+       }
+       relayData.parent.logger.V(2).Info("Final relay getRelayData output", "retval", retval)
+
+       return retval, relayData.parent.getRoutingKey()
 
 }
 
@@ -413,6 +434,8 @@ func (relay *RGBRelay) sendUpdate(red, green, blue float64) {
        }
 
        rgbData, routingKey := relay.relayData.getRelayData(red, green, blue)
+       relay.logger.V(2).Info("Sending data to rabbitmq", "Data", rgbData, "routingKey", routingKey)
+
        sendItem := helper.RabbitSend{Data: rgbData, RoutingKey: routingKey, EmptySource: true}
        relay.sendChannel <- sendItem
 }
@@ -550,11 +573,31 @@ func (zig *RGBZig) getRoutingKey() string {
        return fmt.Sprintf("zigbee2mqtt.%s.set", zig.FriendlyName)
 }
 
+// ESPRelay defs
+type ESPRelay struct {
+       RGBRelay `yaml:"RGBRelay"`
+       Topic    string `yaml:"Topic"`
+}
+
+func (relay *ESPRelay) Init(sendChannel chan helper.RabbitSend) {
+       relay.RGBRelay.Init(sendChannel)
+       relay.logger = logger.WithValues("relay", relay)
+       relay.RGBRelay.relayData = ESPRelayData{parent: relay}
+}
+
+func (relay *ESPRelay) getRoutingKey() string {
+       return relay.Topic
+}
+
 // RGBPico defs
 type PicoRelayData struct {
        parent *RGBPico
 }
 
+type ESPRelayData struct {
+       parent *ESPRelay
+}
+
 func (relayData PicoRelayData) getRelayData(red, green, blue float64) (map[string]interface{}, string) {
        relayData.parent.logger.V(3).Info("Entering getRelayData for pico")
        // need to do this in two stages so the json encodes properly
@@ -873,6 +916,12 @@ func main() {
                manager.addRelay(&relayDefs.RGBRelays[i])
        }
 
+       for i, _ := range relayDefs.ESPRelays {
+               relayDefs.ESPRelays[i].Init(channel)
+               logger.V(1).Info("Adding relay to manager", "relay", relayDefs.ESPRelays[i])
+               manager.addRelay(&relayDefs.ESPRelays[i])
+       }
+
        // we want to use the index value, because we're adding by pointer rather than value
        for i, _ := range relayDefs.RGBZigs {
                relayDefs.RGBZigs[i].Init(channel)
index d342c782627182a5ade93a62d20e0d2788b34117..fc6d0c0353eeececdd7162b75154637bdbce2023 100644 (file)
@@ -75,6 +75,14 @@ var (
                []string{"location", "friendlyname"},
        )
 
+       internalTempGauge = promauto.NewGaugeVec(
+               prometheus.GaugeOpts{
+                       Name: "sensor_internal_temperature",
+                       Help: "Current remote sensor internal temperature",
+               },
+               []string{"location", "friendlyname"},
+       )
+
        doorOpenCounter = promauto.NewCounterVec(
                prometheus.CounterOpts{
                        Name: "sensor_door_opened_count",
@@ -91,12 +99,13 @@ var (
                []string{"location"},
        )
 
-       tempExpire   = make(map[string]time.Time)
-       powerExpire  = make(map[string]time.Time)
-       pm10Expire   = make(map[string]time.Time)
-       pm25Expire   = make(map[string]time.Time)
-       aqiExpire    = make(map[string]time.Time)
-       uptimeExpire = make(map[string]time.Time)
+       tempExpire         = make(map[string]time.Time)
+       powerExpire        = make(map[string]time.Time)
+       pm10Expire         = make(map[string]time.Time)
+       pm25Expire         = make(map[string]time.Time)
+       aqiExpire          = make(map[string]time.Time)
+       uptimeExpire       = make(map[string]time.Time)
+       internalTempExpire = make(map[string]time.Time)
 
        doorMap = make(map[string]map[string]interface{})
 
@@ -311,6 +320,18 @@ func processUptime(location string, friendlyName string, uptime float64, sendCha
        uptimeExpire[tempHash.getHash()] = now
 }
 
+func processInternalTemp(location string, friendlyName string, internal float64, sendChannel chan helper.RabbitSend) {
+       // we don't want to have to loop back around on rabbit processing, so just set the gauge here
+       now := time.Now().UTC()
+       // do the label update here
+       internalTempGauge.With(prometheus.Labels{"location": location, "friendlyname": friendlyName}).Set(internal)
+
+       tempHash := labelValHash{labelValues: []string{location, friendlyName}}
+       expireMutex.Lock()
+       defer expireMutex.Unlock()
+       internalTempExpire[tempHash.getHash()] = now
+}
+
 func (dev *diydevice) handleDIY(obj map[string]interface{}, sendChannel chan helper.RabbitSend) {
        logger.V(1).Info("DIY data received", "obj", obj)
        _, isAction := obj["action"]
@@ -398,6 +419,19 @@ func (dev *powerdevice) handlePower(obj map[string]interface{}, sendChannel chan
 }
 
 func hexCommon(location string, friendlyName string, obj map[string]interface{}, sendChannel chan helper.RabbitSend) {
+       var dataMap map[string]interface{}
+
+       motion, ok := obj["motion"].(bool)
+       if ok {
+               dataMap = make(map[string]interface{}, 0)
+               dataMap["motion_detected"] = motion
+               // copied from above, but it might change later so shrug
+               dataMap["location"] = location
+               logger.V(2).Info("Sending reprocessed motion")
+               sendThis := helper.RabbitSend{Data: dataMap, RoutingKey: "motion", IncludeDate: true}
+               sendChannel <- sendThis
+       }
+
        pm25, ok := obj["pm25"].(float64)
        if !ok {
                logger.V(3).Info("pm25 not found in data, ignoring", "obj", obj)
@@ -419,7 +453,7 @@ func hexCommon(location string, friendlyName string, obj map[string]interface{},
                if temperature < -55.0 || temperature > 125.0 {
                        logger.Error(nil, "temperature out of range, ignoring it", "temperature", temperature)
                } else {
-                       dataMap := make(map[string]interface{}, 0)
+                       dataMap = make(map[string]interface{}, 0)
                        // these should all exist properly
                        dataMap["fahrenheit"] = temperature*9/5 + 32
                        dataMap["celsius"] = temperature
@@ -431,6 +465,15 @@ func hexCommon(location string, friendlyName string, obj map[string]interface{},
                }
        }
 
+       internal, ok := obj["internal"].(float64)
+       if ok {
+               if internal < -55.0 || internal > 125.0 {
+                       logger.Error(nil, "internal out of range, ignoring it", "internal", internal)
+               } else {
+                       processInternalTemp(location, friendlyName, internal, sendChannel)
+               }
+       }
+
        uptime, ok := obj["uptime"].(float64)
        if !ok {
                logger.V(3).Info("uptime not found in data, ignoring", "obj", obj)
@@ -666,6 +709,7 @@ func expireStaleMetrics() {
        expireStaleMetric(pm25Expire, pm25Gauge)
        expireStaleMetric(aqiExpire, aqiGauge)
        expireStaleMetric(uptimeExpire, uptimeGauge)
+       expireStaleMetric(internalTempExpire, internalTempGauge)
 
 }
 
@@ -712,7 +756,8 @@ func main() {
 
        // hex devices (super custom)
        //devices = append(devices, newhexdevice("0x7e6af7fefff95560", "Downstairs Test"))
-       devices = append(devices, newhexdevice("0x526af7fefff95560", "Downstairs Test"))
+       //devices = append(devices, newhexdevice("0x526af7fefff95560", "Downstairs Test"))
+       devices = append(devices, newhexdevice("0x6cf640feffca4c40", "Indoor AQI Monitor"))
 
        devices = append(devices, newesp32wifidevice("esp32.sensor_info", ""))
 
index 9f9588ff624a40a2db4e715e1b986b7c9ee25802..42b44e0fe3e09f472b561b90ad00aa8f42b891a7 100644 (file)
@@ -215,7 +215,9 @@ func main() {
                logger.Error(err, "failed in rabbitmq setup")
                os.Exit(1)
        }
-       const weatherStation string = "KWASEATT2696"
+       // off, somehow?
+       //const weatherStation string = "KWASEATT2696"
+       const weatherStation string = "KWASEATT2613"
        const sleepTime time.Duration = 30 * time.Second
 
        //currentTemp, err := fetchTemp(weatherStation)