From 7714e262a70268c034e1d71e9a8d812f5270a26f Mon Sep 17 00:00:00 2001 From: jweigele Date: Sun, 14 Jan 2024 18:44:21 -0800 Subject: [PATCH] New features for ESP devices direct over MQTT * 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 | 51 ++++++++++++++++++++++++++++++++++++++- reprocess/main.go | 61 ++++++++++++++++++++++++++++++++++++++++------- wunder/main.go | 4 +++- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/lights/main.go b/lights/main.go index 7c768fe..3b2ef07 100644 --- a/lights/main.go +++ b/lights/main.go @@ -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) diff --git a/reprocess/main.go b/reprocess/main.go index d342c78..fc6d0c0 100644 --- a/reprocess/main.go +++ b/reprocess/main.go @@ -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", "")) diff --git a/wunder/main.go b/wunder/main.go index 9f9588f..42b44e0 100644 --- a/wunder/main.go +++ b/wunder/main.go @@ -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) -- 2.30.2