Repository: evcc-io/evcc (go)

Issue:
format (target)soc as °C if icon: waterheater 
Hallo,

ich betreibe einen 3-phasigen Heizstab zur Warmwasserbereitung.
Dieser hängt pro Phase an einem Shelly und wird abhängig vom vorhandenen PV-Überschuss gesteuert (ca. 666, 1333, 2000 Watt).

ich habe gesehen, dass es die Formatierung des SOC in °C bereits gibt, jedoch nur für Plugins. 

Gibt es die Möglichkeit, die Formatierung des SOC in °C generell für Charger mit dem "Icon: Waterheater" zu implementieren?

Pull request description:
Add heating option to switchsockets
fixes #9987

Patch:
diff --git a/server/socket_helper.go b/server/socket_helper.go
index f1a8c2e9..0cb3c6c3 100644
--- a/server/socket_helper.go
+++ b/server/socket_helper.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"math"
+	"reflect"
 	"strings"
 	"time"
 
@@ -39,8 +40,33 @@ func encode(v interface{}) (string, error) {
 	return s, nil
 }
 
+func encodeSlice(v interface{}) (string, error) {
+	rv := reflect.ValueOf(v)
+	res := make([]string, rv.Len())
+
+	for i := 0; i < rv.Len(); i++ {
+		var err error
+		if res[i], err = encode(rv.Index(i).Interface()); err != nil {
+			return "", err
+		}
+	}
+
+	return fmt.Sprintf("[%s]", strings.Join(res, ",")), nil
+}
+
 func kv(p util.Param) string {
-	val, err := encode(p.Val)
+	var (
+		val string
+		err error
+	)
+
+	// unwrap slices
+	if p.Val != nil && reflect.TypeOf(p.Val).Kind() == reflect.Slice {
+		val, err = encodeSlice(p.Val)
+	} else {
+		val, err = encode(p.Val)
+	}
+
 	if err != nil {
 		panic(err)
 	}
diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml
index f31c73df..d6556e0a 100644
--- a/util/templates/defaults.yaml
+++ b/util/templates/defaults.yaml
@@ -331,9 +331,15 @@ presets:
       - name: integrateddevice
         type: bool
         advanced: true
-        description:
+        help:
           de: Integriertes Gerät (keine Fahrzeugverbindung)
           en: Integrated device (no vehicle connection)
+      - name: heating
+        type: bool
+        advanced: true
+        help:
+          de: Wärmeerzeuger (°C anstatt %)
+          en: Heating device (°C instead of %)
       - name: icon
         advanced: true
   ocpp:
diff --git a/util/templates/includes/switchsocket.tpl b/util/templates/includes/switchsocket.tpl
index 7f341943..e3776638 100644
--- a/util/templates/includes/switchsocket.tpl
+++ b/util/templates/includes/switchsocket.tpl
@@ -1,5 +1,11 @@
 {{ define "switchsocket" }}
 standbypower: {{ .standbypower }}
-{{ if .integrateddevice }}features: ["integrateddevice"]{{ end }}
+features:
+{{- if .integrateddevice }}
+- integrateddevice
+{{- end }}
+{{- if .heating }}
+- heating
+{{- end }}
 {{ if .icon }}icon: {{ .icon }}{{ end }}
 {{ end -}}


Test patch:
diff --git a/server/socket_test.go b/server/socket_test.go
index ffe7368f..dd40b945 100644
--- a/server/socket_test.go
+++ b/server/socket_test.go
@@ -31,3 +31,20 @@ func TestEncode(t *testing.T) {
 		assert.Equal(t, tc.out, out)
 	}
 }
+
+func TestEncodeSlice(t *testing.T) {
+	tc := []struct {
+		in  interface{}
+		out string
+	}{
+		{[]string{"a", "b"}, `["a","b"]`},
+		{[2]int64{1, 2}, `[1,2]`},
+		{[]float64{1, math.NaN()}, `[1,null]`},
+	}
+
+	for _, tc := range tc {
+		out, err := encodeSlice(tc.in)
+		assert.NoError(t, err)
+		assert.Equal(t, tc.out, out)
+	}
+}