Add configurable device aliases
This commit is contained in:
@@ -3,6 +3,8 @@ package pipeline
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -11,19 +13,22 @@ import (
|
||||
"mqqt-scrubber/internal/parser"
|
||||
)
|
||||
|
||||
var invalidDeviceAliasCharacters = regexp.MustCompile(`[^a-z0-9_]+`)
|
||||
|
||||
type writer interface {
|
||||
Write(ctx context.Context, records []model.Record) error
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
config config.Config
|
||||
influxClient writer
|
||||
input chan model.RawMessage
|
||||
received atomic.Uint64
|
||||
parsed atomic.Uint64
|
||||
written atomic.Uint64
|
||||
dropped atomic.Uint64
|
||||
failed atomic.Uint64
|
||||
config config.Config
|
||||
deviceAliases map[string]string
|
||||
influxClient writer
|
||||
input chan model.RawMessage
|
||||
received atomic.Uint64
|
||||
parsed atomic.Uint64
|
||||
written atomic.Uint64
|
||||
dropped atomic.Uint64
|
||||
failed atomic.Uint64
|
||||
}
|
||||
|
||||
type Snapshot struct {
|
||||
@@ -36,9 +41,10 @@ type Snapshot struct {
|
||||
|
||||
func NewService(cfg config.Config, influxClient writer) *Service {
|
||||
return &Service{
|
||||
config: cfg,
|
||||
influxClient: influxClient,
|
||||
input: make(chan model.RawMessage, cfg.App.BufferSize),
|
||||
config: cfg,
|
||||
deviceAliases: normalizeDeviceAliases(cfg.DeviceAliases),
|
||||
influxClient: influxClient,
|
||||
input: make(chan model.RawMessage, cfg.App.BufferSize),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +92,8 @@ func (service *Service) Run(ctx context.Context) error {
|
||||
continue
|
||||
}
|
||||
|
||||
service.applyDeviceAliases(records)
|
||||
|
||||
service.parsed.Add(uint64(len(records)))
|
||||
batch = append(batch, records...)
|
||||
|
||||
@@ -150,3 +158,49 @@ func (service *Service) Snapshot() Snapshot {
|
||||
Failed: service.failed.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) applyDeviceAliases(records []model.Record) {
|
||||
if len(service.deviceAliases) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for index := range records {
|
||||
device := normalizeDeviceKey(records[index].Tags["device"])
|
||||
alias, ok := service.deviceAliases[device]
|
||||
if !ok || alias == "" {
|
||||
continue
|
||||
}
|
||||
records[index].Fields["device_alias"] = alias
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeDeviceAliases(aliases map[string]string) map[string]string {
|
||||
if len(aliases) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
normalized := make(map[string]string, len(aliases))
|
||||
for device, alias := range aliases {
|
||||
cleanDevice := normalizeDeviceKey(device)
|
||||
cleanAlias := strings.TrimSpace(alias)
|
||||
if cleanDevice == "" || cleanAlias == "" {
|
||||
continue
|
||||
}
|
||||
normalized[cleanDevice] = cleanAlias
|
||||
}
|
||||
|
||||
if len(normalized) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
func normalizeDeviceKey(value string) string {
|
||||
normalized := strings.ToLower(strings.TrimSpace(value))
|
||||
normalized = strings.ReplaceAll(normalized, "-", "_")
|
||||
normalized = strings.ReplaceAll(normalized, " ", "_")
|
||||
normalized = invalidDeviceAliasCharacters.ReplaceAllString(normalized, "_")
|
||||
normalized = strings.Trim(normalized, "_")
|
||||
return normalized
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ func (writer *fakeWriter) firstBatch() []model.Record {
|
||||
func TestServiceFlushesParsedRecords(t *testing.T) {
|
||||
fake := newFakeWriter()
|
||||
service := NewService(config.Config{
|
||||
DeviceAliases: map[string]string{
|
||||
"tasmota-896001": "Garage Plug",
|
||||
"Tasmota C88994": "Office Plug",
|
||||
},
|
||||
App: config.AppConfig{
|
||||
BatchSize: 2,
|
||||
BufferSize: 8,
|
||||
@@ -90,6 +94,9 @@ func TestServiceFlushesParsedRecords(t *testing.T) {
|
||||
if batch[0].Fields["online"] != true {
|
||||
t.Fatalf("unexpected lwt online field: %#v", batch[0].Fields["online"])
|
||||
}
|
||||
if batch[0].Fields["device_alias"] != "Garage Plug" {
|
||||
t.Fatalf("unexpected lwt device_alias field: %#v", batch[0].Fields["device_alias"])
|
||||
}
|
||||
|
||||
if batch[1].Measurement != "tasmota_sensor" {
|
||||
t.Fatalf("unexpected second measurement: %s", batch[1].Measurement)
|
||||
@@ -97,6 +104,9 @@ func TestServiceFlushesParsedRecords(t *testing.T) {
|
||||
if batch[1].Fields["energy_total"] != float64(41.385) {
|
||||
t.Fatalf("unexpected sensor energy_total field: %#v", batch[1].Fields["energy_total"])
|
||||
}
|
||||
if batch[1].Fields["device_alias"] != "Office Plug" {
|
||||
t.Fatalf("unexpected sensor device_alias field: %#v", batch[1].Fields["device_alias"])
|
||||
}
|
||||
|
||||
cancel()
|
||||
select {
|
||||
|
||||
Reference in New Issue
Block a user