From 212822e85d261f6321524e7ac0432fa97aa1e8e3 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:28:34 +0100 Subject: [PATCH] feat(inputs.knx_listener): Allow usage of DPT string representation (#15069) --- plugins/inputs/knx_listener/README.md | 3 + plugins/inputs/knx_listener/knx_listener.go | 45 +++++----- .../inputs/knx_listener/knx_listener_test.go | 87 ++++++++++++------- plugins/inputs/knx_listener/sample.conf | 3 + 4 files changed, 85 insertions(+), 53 deletions(-) diff --git a/plugins/inputs/knx_listener/README.md b/plugins/inputs/knx_listener/README.md index 09af5adcaa233..ac38e191b7196 100644 --- a/plugins/inputs/knx_listener/README.md +++ b/plugins/inputs/knx_listener/README.md @@ -43,6 +43,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. # name = "temperature" # ## Datapoint-Type (DPT) of the KNX messages # dpt = "9.001" + # ## Use the string representation instead of the numerical value for the + # ## datapoint-type and the addresses below + # # as_string = false # ## List of Group-Addresses (GAs) assigned to the measurement # addresses = ["5/5/1"] diff --git a/plugins/inputs/knx_listener/knx_listener.go b/plugins/inputs/knx_listener/knx_listener.go index b455e142247ed..f013fe638829c 100644 --- a/plugins/inputs/knx_listener/knx_listener.go +++ b/plugins/inputs/knx_listener/knx_listener.go @@ -26,13 +26,15 @@ type KNXInterface interface { type addressTarget struct { measurement string - datapoint dpt.DatapointValue + asstring bool + datapoint dpt.Datapoint } type Measurement struct { - Name string - Dpt string - Addresses []string + Name string `toml:"name"` + Dpt string `toml:"dpt"` + AsString bool `toml:"as_string"` + Addresses []string `toml:"addresses"` } type KNXListener struct { @@ -83,7 +85,7 @@ func (kl *KNXListener) Init() error { if !ok { return fmt.Errorf("cannot create datapoint-type %q for address %q", m.Dpt, ga) } - kl.gaTargetMap[ga] = addressTarget{m.Name, d} + kl.gaTargetMap[ga] = addressTarget{measurement: m.Name, asstring: m.AsString, datapoint: d} } } @@ -164,8 +166,7 @@ func (kl *KNXListener) listen(acc telegraf.Accumulator) { } // Extract the value from the data-frame - err := target.datapoint.Unpack(msg.Data) - if err != nil { + if err := target.datapoint.Unpack(msg.Data); err != nil { kl.Log.Errorf("Unpacking data failed: %v", err) continue } @@ -175,19 +176,23 @@ func (kl *KNXListener) listen(acc telegraf.Accumulator) { // as otherwise telegraf will not push out the metrics and eat it // silently. var value interface{} - vi := reflect.Indirect(reflect.ValueOf(target.datapoint)) - switch vi.Kind() { - case reflect.Bool: - value = vi.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - value = vi.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - value = vi.Uint() - case reflect.Float32, reflect.Float64: - value = vi.Float() - default: - kl.Log.Errorf("Type conversion %v failed for address %q", vi.Kind(), ga) - continue + if !target.asstring { + vi := reflect.Indirect(reflect.ValueOf(target.datapoint)) + switch vi.Kind() { + case reflect.Bool: + value = vi.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + value = vi.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + value = vi.Uint() + case reflect.Float32, reflect.Float64: + value = vi.Float() + default: + kl.Log.Errorf("Type conversion %v failed for address %q", vi.Kind(), ga) + continue + } + } else { + value = target.datapoint.String() } // Compose the actual data to be pushed out diff --git a/plugins/inputs/knx_listener/knx_listener_test.go b/plugins/inputs/knx_listener/knx_listener_test.go index afe52f8c9f835..d7fd90ff24b13 100644 --- a/plugins/inputs/knx_listener/knx_listener_test.go +++ b/plugins/inputs/knx_listener/knx_listener_test.go @@ -60,41 +60,62 @@ func produceKnxEvent(t *testing.T, address string, datapoint string, value inter func TestRegularReceives_DPT(t *testing.T) { // Define the test-cases - var testcases = []message{ - {"1/0/1", "1.001", true}, - {"1/0/2", "1.002", false}, - {"1/0/3", "1.003", true}, - {"1/0/9", "1.009", false}, - {"1/1/0", "1.010", true}, - {"5/0/1", "5.001", 12.157}, - {"5/0/3", "5.003", 121.412}, - {"5/0/4", "5.004", uint64(25)}, - {"9/0/1", "9.001", 18.56}, - {"9/0/4", "9.004", 243.84}, - {"9/0/5", "9.005", 12.01}, - {"9/0/7", "9.007", 59.32}, - {"13/0/1", "13.001", int64(-15)}, - {"13/0/2", "13.002", int64(183)}, - {"13/1/0", "13.010", int64(-141)}, - {"13/1/1", "13.011", int64(277)}, - {"13/1/2", "13.012", int64(-4096)}, - {"13/1/3", "13.013", int64(8192)}, - {"13/1/4", "13.014", int64(-65536)}, - {"13/1/5", "13.015", int64(2147483647)}, - {"14/0/0", "14.000", -1.31}, - {"14/0/1", "14.001", 0.44}, - {"14/0/2", "14.002", 32.08}, - // {"14/0/3", "14.003", 92.69}, - // {"14/0/4", "14.004", 1.00794}, - {"14/1/0", "14.010", 5963.78}, - {"14/1/1", "14.011", 150.95}, + var testcases = []struct { + address string + dpt string + asstring bool + value interface{} + expected interface{} + }{ + {"1/0/1", "1.001", false, true, true}, + {"1/0/2", "1.002", false, false, false}, + {"1/0/3", "1.003", false, true, true}, + {"1/0/9", "1.009", false, false, false}, + {"1/1/0", "1.010", false, true, true}, + {"1/1/1", "1.001", true, true, "On"}, + {"1/1/2", "1.001", true, false, "Off"}, + {"1/1/3", "1.002", true, true, "True"}, + {"1/1/4", "1.002", true, false, "False"}, + {"1/1/5", "1.003", true, true, "Enable"}, + {"1/1/6", "1.003", true, false, "Disable"}, + {"1/1/7", "1.009", true, true, "Close"}, + {"1/1/8", "1.009", true, false, "Open"}, + {"1/2/0", "1.010", true, true, "Start"}, + {"1/2/1", "1.010", true, false, "Stop"}, + {"5/0/1", "5.001", false, 12.157, 12.157}, + {"5/0/3", "5.003", false, 121.412, 121.412}, + {"5/0/4", "5.004", false, uint64(25), uint64(25)}, + {"9/0/1", "9.001", false, 18.56, 18.56}, + {"9/0/4", "9.004", false, 243.84, 243.84}, + {"9/0/5", "9.005", false, 12.01, 12.01}, + {"9/0/7", "9.007", false, 59.32, 59.32}, + {"13/0/1", "13.001", false, int64(-15), int64(-15)}, + {"13/0/2", "13.002", false, int64(183), int64(183)}, + {"13/1/0", "13.010", false, int64(-141), int64(-141)}, + {"13/1/1", "13.011", false, int64(277), int64(277)}, + {"13/1/2", "13.012", false, int64(-4096), int64(-4096)}, + {"13/1/3", "13.013", false, int64(8192), int64(8192)}, + {"13/1/4", "13.014", false, int64(-65536), int64(-65536)}, + {"13/1/5", "13.015", false, int64(2147483647), int64(2147483647)}, + {"14/0/0", "14.000", false, -1.31, -1.31}, + {"14/0/1", "14.001", false, 0.44, 0.44}, + {"14/0/2", "14.002", false, 32.08, 32.08}, + {"14/0/3", "14.003", false, 92.69, 92.69}, + {"14/0/4", "14.004", false, 1.00794, 1.00794}, + {"14/1/0", "14.010", false, 5963.78, 5963.78}, + {"14/1/1", "14.011", false, 150.95, 150.95}, } acc := &testutil.Accumulator{} // Setup the unit-under-test measurements := make([]Measurement, 0, len(testcases)) for _, testcase := range testcases { - measurements = append(measurements, Measurement{"test", testcase.dpt, []string{testcase.address}}) + measurements = append(measurements, Measurement{ + Name: "test", + Dpt: testcase.dpt, + AsString: testcase.asstring, + Addresses: []string{testcase.address}, + }) } listener := KNXListener{ ServiceType: "dummy", @@ -129,8 +150,8 @@ func TestRegularReceives_DPT(t *testing.T) { require.Equal(t, "test", m.Measurement) require.Equal(t, testcases[i].address, m.Tags["groupaddress"]) require.Len(t, m.Fields, 1) - switch v := testcases[i].value.(type) { - case bool, int64, uint64: + switch v := testcases[i].expected.(type) { + case string, bool, int64, uint64: require.Equal(t, v, m.Fields["value"]) case float64: require.InDelta(t, v, m.Fields["value"], epsilon) @@ -144,7 +165,7 @@ func TestRegularReceives_MultipleMessages(t *testing.T) { listener := KNXListener{ ServiceType: "dummy", Measurements: []Measurement{ - {"temperature", "1.001", []string{"1/1/1"}}, + {Name: "temperature", Dpt: "1.001", Addresses: []string{"1/1/1"}}, }, Log: testutil.Logger{Name: "knx_listener"}, } @@ -197,7 +218,7 @@ func TestReconnect(t *testing.T) { listener := KNXListener{ ServiceType: "dummy", Measurements: []Measurement{ - {"temperature", "1.001", []string{"1/1/1"}}, + {Name: "temperature", Dpt: "1.001", Addresses: []string{"1/1/1"}}, }, Log: testutil.Logger{Name: "knx_listener"}, } diff --git a/plugins/inputs/knx_listener/sample.conf b/plugins/inputs/knx_listener/sample.conf index 436ff234db1d3..770eadbfb306a 100644 --- a/plugins/inputs/knx_listener/sample.conf +++ b/plugins/inputs/knx_listener/sample.conf @@ -13,6 +13,9 @@ # name = "temperature" # ## Datapoint-Type (DPT) of the KNX messages # dpt = "9.001" + # ## Use the string representation instead of the numerical value for the + # ## datapoint-type and the addresses below + # # as_string = false # ## List of Group-Addresses (GAs) assigned to the measurement # addresses = ["5/5/1"]