Compare commits

..

5 Commits

Author SHA1 Message Date
a55f80b7d9 saerbeck query
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-11-11 12:44:02 +01:00
3d68aa0e61 prepare changes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-11-11 12:43:29 +01:00
f2f16c811a still sensor labels
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-07-31 16:50:54 +02:00
6f9327fdd6 sensor labels
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-07-31 16:36:17 +02:00
1d1942a4d3 sensor labels 2024-07-31 16:26:51 +02:00
6 changed files with 88 additions and 124 deletions

View File

@ -10,14 +10,34 @@ create or replace view badesee_temperature_v as
where application = 'de-hottis-saerbeck-monitoring' and where application = 'de-hottis-saerbeck-monitoring' and
device = 'eui-a84041318187ec13'; device = 'eui-a84041318187ec13';
create or replace view cubecell_threeway_temperature_v as create or replace view cubecell_threeway_temperature2_v as
select time, select time,
cast(values->'Temperature1'->>'value' as float) as Temp1, cast(values->'Temperature2'->>'value' as float) as value,
cast(values->'Temperature2'->>'value' as float) as Temp2, values->'Temperature2'->>'label' as label
cast(values->'Temperature3'->>'value' as float) as Temp3,
device
from measurements from measurements
where application = 'de-hottis-saerbeck-monitoring' and where application = 'de-hottis-saerbeck-monitoring' and
device = 'eui-70b3d57ed0068fa4'; device = 'eui-70b3d57ed0068fa4';
create or replace view cubecell_threeway_temperature1_v as
select time,
cast(values->'Temperature1'->>'value' as float) as value,
values->'Temperature1'->>'label' as label
from measurements
where application = 'de-hottis-saerbeck-monitoring' and
device = 'eui-70b3d57ed0068fa4';
create or replace view cubecell_threeway_temperature3_v as
select time,
cast(values->'Temperature3'->>'value' as float) as value,
values->'Temperature3'->>'label' as label
from measurements
where application = 'de-hottis-saerbeck-monitoring' and
device = 'eui-70b3d57ed0068fa4';
create or replace view cubecell_threeway_battery_v as
select time,
cast(values->'Battery'->>'value' as float) as value,
values->'Battery'->>'label' as label
from measurements
where application = 'de-hottis-saerbeck-monitoring' and
device = 'eui-70b3d57ed0068fa4';

View File

@ -1,6 +1,6 @@
PGUSER="uditest" PGUSER="udi-hottis"
PGHOST=`kubectl get services traefik -n system -o jsonpath="{.status.loadBalancer.ingress[0].ip}"` PGHOST=`kubectl get services traefik -n system -o jsonpath="{.status.loadBalancer.ingress[0].ip}"`
PGPASSWORD=`kubectl get secrets uditest-db-cred -n udi-test -o jsonpath="{.data.PGPASSWORD}" | base64 --decode` PGPASSWORD=`kubectl get secrets default-udi-db-cred -n udi -o jsonpath="{.data.PGPASSWORD}" | base64 --decode`
PGSSLMODE=require PGSSLMODE=require
PGDATABASE="uditest" PGDATABASE="uditest"
export PGUSER PGHOST PGPASSWORD PGSSLMODE PGDATABASE export PGUSER PGHOST PGPASSWORD PGSSLMODE PGDATABASE

View File

@ -4,72 +4,6 @@
"tlsEnable": "false" "tlsEnable": "false"
}, },
"topicMappings": [ "topicMappings": [
{
"topics": [ "IoT/PV/Values" ],
"handler": "PV",
"id": "PV",
"config": {
"attributes": {
}
}
},
{
"topics": [ "IoT/MBGW3/Measurement" ],
"handler": "MBGW3",
"id": "MBGW3",
"config": {
"attributes": {
}
}
},
{
"topics": [ "dt1/ai/periodic/1" ],
"handler": "DT1T",
"id": "DT1T.0",
"config": {
"attributes": {
"Application": "Temperature Wago",
"Device": "Freezer",
"HardLow": "-273",
"SoftLow": "-50",
"SoftHigh": "20",
"HardHigh": "100"
}
}
},
{
"topics": [ "dt1/ai/periodic/3" ],
"handler": "DT1T",
"id": "DT1T.1",
"config": {
"attributes": {
"Application": "Temperature Wago",
"Device": "Outdoor",
"HardLow": "-273",
"SoftLow": "-60",
"SoftHigh": "60",
"HardHigh": "100"
}
}
},
{
"topics": [ "IoT/OneWireGW/Bus 1/#" ],
"handler": "SVER",
"id": "SVER0",
"config": {
"databaseConnStr": "",
"attributes": {
"application": "Temperature Heating",
"payloadRegex": "(\\d+(\\.\\d+)?)\\s*([^0-9\\s]\\S*)",
"deviceFrom": "topic",
"devicePart": "3",
"valueFrom": "payload",
"valuePart": "1",
"unitFrom": "payload",
"unitPart": "3"
}
}
},
{ {
"topics": [ "NR/Multisensor/+/Temperatur" ], "topics": [ "NR/Multisensor/+/Temperatur" ],
"handler": "SVEJ", "handler": "SVEJ",
@ -85,46 +19,18 @@
} }
}, },
{ {
"topics": [ "NR/Multisensor/+/Feuchte" ], "topics": [ "zigbee2mqtt/+" ],
"handler": "SVEJ", "handler": "SVEJ",
"id": "SVEJ1", "id": "SVEJ1",
"config": { "config": {
"databaseConnStr": "", "databaseConnStr": "",
"attributes": { "attributes": {
"application": "Humidity Multisensor", "application": "Temperature Multisensor",
"deviceSelector": "T:2", "deviceSelector": "L:1",
"valueSelector": "J:$.CurrentRelativeHumidity", "valueSelector": "J:$.temperature",
"unitSelector": "C:%"
}
}
},
{
"topics": [ "shellyplusht/+/status/temperature:0" ],
"handler": "SVEJ",
"id": "SVEJ2",
"config": {
"databaseConnStr": "",
"attributes": {
"application": "Temperature Shelly Plus HT",
"deviceSelector": "T:1",
"valueSelector": "J:$.tC",
"unitSelector": "C:°C" "unitSelector": "C:°C"
} }
} }
},
{
"topics": [ "shellyplusht/+/status/humidity:0" ],
"handler": "SVEJ",
"id": "SVE4",
"config": {
"databaseConnStr": "",
"attributes": {
"application": "Humidity Shelly Plus HT",
"deviceSelector": "T:1",
"valueSelector": "J:$.rh",
"unitSelector": "C:%"
}
}
} }
], ],
"archiver": { "archiver": {

View File

@ -70,5 +70,27 @@ func (self *DatabaseHandle) GetDeviceByLabelAndApplication(applicationLabel stri
return &device, nil return &device, nil
} }
func (self *DatabaseHandle) GetDeviceByLabel(deviceLabel string) (*Device, error) {
if ! self.initialized {
err := fmt.Errorf("Database connection not initialized")
return nil, err
}
var device Device
result := self.dbh.
Preload("Application").
Preload("DeviceType").
Where("devices.label = ?", deviceLabel).
First(&device)
if result.Error != nil {
err := fmt.Errorf("Query failed: %s", result.Error)
return nil, err
}
return &device, nil
}

View File

@ -79,16 +79,18 @@ func New(id string, config config.HandlerConfigT) handler.Handler {
return t return t
} }
func extractionHelper(subTopics []string, jPayload interface{}, selector string, jp *jsonpath.Compiled) (string, error) { func (self *SingleValueExtractorJsonpathHandler) ExtractionHelper(subTopics []string, jPayload interface{}, selector string, jp *jsonpath.Compiled) (string, error) {
var res string var res string
switch selector[:2] { switch selector[:2] {
case "J:": case "J:":
// extract using jsonpath from payload
r, e := jp.Lookup(jPayload) r, e := jp.Lookup(jPayload)
if e != nil { if e != nil {
return "", fmt.Errorf("jp.Lookup failed with %s", e) return "", fmt.Errorf("jp.Lookup failed with %s", e)
} }
res = fmt.Sprint(r) res = fmt.Sprint(r)
case "T:": case "T:":
// T: extract from topic
i, e := strconv.Atoi(selector[2:]) i, e := strconv.Atoi(selector[2:])
if e != nil { if e != nil {
return "", fmt.Errorf("Atoi failed with %s", e) return "", fmt.Errorf("Atoi failed with %s", e)
@ -97,7 +99,26 @@ func extractionHelper(subTopics []string, jPayload interface{}, selector string,
return "", fmt.Errorf("not enough subtopics") return "", fmt.Errorf("not enough subtopics")
} }
res = subTopics[i] res = subTopics[i]
case "L:":
// L: extract from topic and later match against devices table in database
i, e := strconv.Atoi(selector[2:])
if e != nil {
return "", fmt.Errorf("Atoi failed with %s", e)
}
if i >= len(subTopics) {
return "", fmt.Errorf("not enough subtopics")
}
ext := subTopics[i]
lookup, err1b := self.dbh.GetDeviceByLabel(ext)
if err1b != nil {
log.Printf("ext lookup %s failed: %v", ext, err1b)
res = ext
} else {
log.Printf("ext: %s", lookup)
res = ext
}
case "C:": case "C:":
// use constant value
res = selector[2:] res = selector[2:]
default: default:
return "", fmt.Errorf("Invalid selector: %s", selector[:2]) return "", fmt.Errorf("Invalid selector: %s", selector[:2])
@ -111,14 +132,14 @@ func (self *SingleValueExtractorJsonpathHandler) Handle(message handler.MessageT
self.Lost("Handler is not marked as ready", nil, message) self.Lost("Handler is not marked as ready", nil, message)
return return
} }
//log.Printf("Handler SingleValueExtractorJsonpath %d processing %s -> %s", self.Id, message.Topic, message.Payload) log.Printf("Handler SingleValueExtractorJsonpath %d processing %s -> %s", self.Id, message.Topic, message.Payload)
var measurement database.Measurement var measurement database.Measurement
measurement.Time = time.Now() measurement.Time = time.Now()
measurement.Application = self.application measurement.Application = self.application
subTopics := strings.Split(message.Topic, "/") subTopics := strings.Split(message.Topic, "/")
//log.Printf("Subtopics: %s", strings.Join(subTopics, ", ")) log.Printf("Subtopics: %s", strings.Join(subTopics, ", "))
var jPayload interface{} var jPayload interface{}
err := json.Unmarshal([]byte(message.Payload), &jPayload) err := json.Unmarshal([]byte(message.Payload), &jPayload)
if err != nil { if err != nil {
@ -126,17 +147,20 @@ func (self *SingleValueExtractorJsonpathHandler) Handle(message handler.MessageT
return return
} }
device, err1 := extractionHelper(subTopics, jPayload, self.deviceSelector, self.deviceJsonpath) device, err1 := self.ExtractionHelper(subTopics, jPayload, self.deviceSelector, self.deviceJsonpath)
if err1 != nil { if err1 != nil {
self.Lost("Device extraction failed", err1, message) self.Lost("Device extraction failed", err1, message)
return return
} }
value, err2 := extractionHelper(subTopics, jPayload, self.valueSelector, self.valueJsonpath) log.Printf("device: %s", device)
value, err2 := self.ExtractionHelper(subTopics, jPayload, self.valueSelector, self.valueJsonpath)
if err2 != nil { if err2 != nil {
self.Lost("Value extraction failed", err2, message) self.Lost("Value extraction failed", err2, message)
return return
} }
unit, err3 := extractionHelper(subTopics, jPayload, self.unitSelector, self.unitJsonpath)
unit, err3 := self.ExtractionHelper(subTopics, jPayload, self.unitSelector, self.unitJsonpath)
if err3 != nil { if err3 != nil {
self.Lost("Unit extraction failed", err3, message) self.Lost("Unit extraction failed", err3, message)
return return
@ -152,7 +176,7 @@ func (self *SingleValueExtractorJsonpathHandler) Handle(message handler.MessageT
measurement.Values = make(map[string]database.VariableType) measurement.Values = make(map[string]database.VariableType)
measurement.Values["Value"] = variable measurement.Values["Value"] = variable
//log.Printf("Prepared measurement item: %s", measurement) log.Printf("Prepared measurement item: %s", measurement)
self.dbh.StoreMeasurement(&measurement) self.dbh.StoreMeasurement(&measurement)
self.S() self.S()
} }

View File

@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/json"
"udi/database" "udi/database"
) )
@ -22,24 +21,17 @@ type hottisThreeWayThermometerValues struct {
Value3 int32 Value3 int32
} }
func getSensorName(sensorsMap *map[string]string, sensorAddress uint64) string { func getSensorName(sensorsMap *map[string]interface{}, sensorAddress uint64) string {
key := strconv.FormatUint(sensorAddress, 10) key := strconv.FormatUint(sensorAddress, 10)
if sensorName, exists := (*sensorsMap)[key]; exists { if sensorName, exists := (*sensorsMap)[key].(string); exists {
return sensorName return sensorName
} }
return "Sensor" + key return "Sensor" + key
} }
func Parse(fPort int, _ []byte, frmPayload string, variables *map[string]database.VariableType, attributes *map[string]interface{}, device *database.Device) error { func Parse(fPort int, _ []byte, frmPayload string, variables *map[string]database.VariableType, attributes *map[string]interface{}, device *database.Device) error {
sensorsMap := make(map[string]string) deviceAttrs := (*device).Attributes
sensorsJSON, ok := (*device).Attributes["Sensors"].(string) sensorsMap := deviceAttrs["Sensors"].(map[string]interface{})
if !ok {
return fmt.Errorf("Unable to load sensor map from attributes")
}
errJ := json.Unmarshal([]byte(sensorsJSON), &sensorsMap)
if errJ != nil {
return fmt.Errorf("Unable to parse sensor map: %v", errJ)
}
if fPort != 2 { if fPort != 2 {
return fmt.Errorf("Unexpected fPort %d", fPort) return fmt.Errorf("Unexpected fPort %d", fPort)