package pipeline import ( "bytes" "encoding/hex" "fmt" "regexp" "strconv" "strings" xj "github.com/basgys/goxml2json" "github.com/cenkalti/backoff/v4" "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/jq" "github.com/itchyny/gojq" "github.com/volkszaehler/mbmd/meters/rs485" ) type Pipeline struct { log *util.Logger re *regexp.Regexp jq *gojq.Query allowEmpty bool quote bool dflt string unpack string decode string } type Settings struct { AllowEmpty bool Quote bool Regex string Default string Jq string Unpack string Decode string } func New(log *util.Logger, cc Settings) (*Pipeline, error) { p := &Pipeline{ log: log, allowEmpty: cc.AllowEmpty, quote: cc.Quote, } var err error if err == nil && cc.Regex != "" { _, err = p.WithRegex(cc.Regex, cc.Default) } if err == nil && cc.Jq != "" { _, err = p.WithJq(cc.Jq) } if err == nil && cc.Unpack != "" { _, err = p.WithUnpack(cc.Unpack) } if err == nil && cc.Decode != "" { _, err = p.WithDecode(cc.Decode) } return p, err } // WithRegex adds a regex query applied to the mqtt listener payload func (p *Pipeline) WithRegex(regex, dflt string) (*Pipeline, error) { re, err := regexp.Compile(regex) if err != nil { return nil, fmt.Errorf("invalid regex '%s': %w", re, err) } p.re = re p.dflt = dflt return p, nil } // WithJq adds a jq query applied to the mqtt listener payload func (p *Pipeline) WithJq(jq string) (*Pipeline, error) { op, err := gojq.Parse(jq) if err != nil { return nil, fmt.Errorf("invalid jq query '%s': %w", jq, err) } p.jq = op return p, nil } // WithUnpack adds data unpacking func (p *Pipeline) WithUnpack(unpack string) (*Pipeline, error) { p.unpack = strings.ToLower(unpack) return p, nil } // WithDecode adds data decoding func (p *Pipeline) WithDecode(decode string) (*Pipeline, error) { p.decode = strings.ToLower(decode) return p, nil } // transform XML into JSON with attribute names getting 'attr' prefix func (p *Pipeline) transformXML(value []byte) []byte { value = bytes.TrimSpace(value) // only do a simple check, as some devices e.g. Kostal Piko MP plus don't seem to send proper XML if !bytes.HasPrefix(value, []byte(" 1 { b = m[1] // first submatch } else if len(p.dflt) > 0 { return []byte(p.dflt), nil } } if p.jq != nil { if p.quote { b = fmt.Appendf(nil, "%q", string(b)) } v, err := jq.Query(p.jq, b) if err != nil { return b, backoff.Permanent(err) } b = fmt.Appendf(nil, "%v", v) } if p.unpack != "" { v, err := p.unpackValue(b) if err != nil { return b, err } b = fmt.Appendf(nil, "%v", v) } if p.decode != "" { v, err := p.decodeValue(b) if err != nil { return b, err } b = []byte(strconv.FormatFloat(v, 'f', -1, 64)) } return b, nil }