Add configurable device aliases

This commit is contained in:
2026-03-14 22:42:27 +01:00
parent b52ad9caee
commit 110388c363
8 changed files with 259 additions and 17 deletions
+65 -11
View File
@@ -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
}
+10
View File
@@ -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 {