diff --git a/meter/fritz/smarthome/service.go b/meter/fritz/smarthome/service.go new file mode 100644 index 000000000..367ae8885 --- /dev/null +++ b/meter/fritz/smarthome/service.go @@ -0,0 +1,83 @@ +package smarthome + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "slices" + "strings" + + "github.com/evcc-io/evcc/meter/fritz" + "github.com/evcc-io/evcc/server/service" + "github.com/evcc-io/evcc/util" + "github.com/evcc-io/evcc/util/request" + "github.com/evcc-io/evcc/util/transport" + "github.com/samber/lo" +) + +func init() { + mux := http.NewServeMux() + mux.HandleFunc("GET /devices", getDevices) + + service.Register("fritz", mux) +} + +func getDevices(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + uri := strings.TrimRight(q.Get("uri"), "/") + user := q.Get("user") + password := q.Get("password") + + if uri == "" || user == "" || password == "" { + jsonError(w, http.StatusBadRequest, errors.New("missing uri, user or password")) + return + } + + log := util.NewLogger("fritz").Redact(password) + helper := request.NewHelper(log) + helper.Client.Transport = request.NewTripper(log, transport.Insecure()) + + settings := fritz.Settings{URI: uri, User: user, Password: password} + + sid, err := settings.GetSessionID(helper) + if err != nil { + jsonError(w, http.StatusBadRequest, err) + return + } + + if sid == "0000000000000000" { + jsonError(w, http.StatusUnauthorized, errors.New("invalid user or password")) + return + } + + req, _ := request.New(http.MethodGet, + fmt.Sprintf("%s/api/v0/smarthome/overview/devices", uri), nil, map[string]string{ + "Authorization": "AVM-SID " + sid, + }, request.AcceptJSON, + ) + + var devices []Device + if err := helper.DoJSON(req, &devices); err != nil { + jsonError(w, http.StatusBadRequest, err) + return + } + + ains := lo.Map(devices, func(d Device, _ int) string { + return d.AIN + }) + slices.Sort(ains) + + w.Header().Set("Cache-control", "max-age=60") + jsonWrite(w, ains) +} + +func jsonWrite(w http.ResponseWriter, data any) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(data) +} + +func jsonError(w http.ResponseWriter, status int, err error) { + w.WriteHeader(status) + jsonWrite(w, util.ErrorAsJson(err)) +} diff --git a/meter/fritz/smarthome/types.go b/meter/fritz/smarthome/types.go index 9b74a48cc..ecf901d45 100644 --- a/meter/fritz/smarthome/types.go +++ b/meter/fritz/smarthome/types.go @@ -1,5 +1,15 @@ package smarthome +// Device represents a smarthome device from the /devices endpoint +type Device struct { + UID string `json:"UID"` + AIN string `json:"ain"` + Name string `json:"name"` + ProductName string `json:"productName"` + ProductCategory string `json:"productCategory"` + IsConnected bool `json:"isConnected"` +} + // Unit represents a smarthome unit with its interfaces type Unit struct { GroupUID string `json:"groupUid,omitempty"` diff --git a/server/http_site_handler.go b/server/http_site_handler.go index 03c0a3f3d..8a89323ad 100644 --- a/server/http_site_handler.go +++ b/server/http_site_handler.go @@ -83,6 +83,7 @@ func jsonHandler(h http.Handler) http.Handler { } func jsonWrite(w http.ResponseWriter, data any) { + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) } diff --git a/templates/definition/charger/fritzdect.yaml b/templates/definition/charger/fritzdect.yaml index 922f4d127..3d66840ac 100644 --- a/templates/definition/charger/fritzdect.yaml +++ b/templates/definition/charger/fritzdect.yaml @@ -25,6 +25,7 @@ params: required: true - name: ain required: true + service: fritz/devices?uri={uri}&user={user}&password={password} - name: firmware82 advanced: true type: bool diff --git a/templates/definition/meter/fritzdect.yaml b/templates/definition/meter/fritzdect.yaml index caab3a1b8..aae5f2470 100644 --- a/templates/definition/meter/fritzdect.yaml +++ b/templates/definition/meter/fritzdect.yaml @@ -27,6 +27,7 @@ params: required: true - name: ain required: true + service: fritz/devices?uri={uri}&user={user}&password={password} - name: firmware82 advanced: true type: bool diff --git a/templates/definition/meter/fritzgrid.yaml b/templates/definition/meter/fritzgrid.yaml index f67125749..ab0c2f790 100644 --- a/templates/definition/meter/fritzgrid.yaml +++ b/templates/definition/meter/fritzgrid.yaml @@ -14,6 +14,7 @@ params: required: true - name: ain required: true + service: fritz/devices?uri={uri}&user={user}&password={password} render: | type: fritzdect uri: {{ .uri }} diff --git a/util/homeassistant/service.go b/util/homeassistant/service.go index 13f8853ff..72f0d4837 100644 --- a/util/homeassistant/service.go +++ b/util/homeassistant/service.go @@ -128,6 +128,7 @@ func getServices(w http.ResponseWriter, req *http.Request) { // jsonWrite writes a JSON response func jsonWrite(w http.ResponseWriter, data any) { + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) }