Easee: add cmd for (de)activating local ocpp (#29868)
This commit is contained in:
parent
8ce8d6d9f9
commit
603979167c
3 changed files with 254 additions and 0 deletions
|
|
@ -3,6 +3,9 @@ package easee
|
||||||
// API is the Easee API endpoint
|
// API is the Easee API endpoint
|
||||||
const API = "https://api.easee.com/api"
|
const API = "https://api.easee.com/api"
|
||||||
|
|
||||||
|
// LocalOcppAPI is the Easee local OCPP configuration API endpoint
|
||||||
|
const LocalOcppAPI = "https://api.easee.com/local-ocpp"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ChargeStart = "start_charging"
|
ChargeStart = "start_charging"
|
||||||
ChargeStop = "stop_charging"
|
ChargeStop = "stop_charging"
|
||||||
|
|
|
||||||
201
cmd/easee_ocpp.go
Normal file
201
cmd/easee_ocpp.go
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/charger/easee"
|
||||||
|
"github.com/evcc-io/evcc/util"
|
||||||
|
"github.com/evcc-io/evcc/util/request"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var easeeOcppCmd = &cobra.Command{
|
||||||
|
Use: "easee-ocpp",
|
||||||
|
Short: "Manage Easee local OCPP configuration",
|
||||||
|
}
|
||||||
|
|
||||||
|
var easeeOcppEnableCmd = &cobra.Command{
|
||||||
|
Use: "enable",
|
||||||
|
Short: "Enable local OCPP on Easee charger",
|
||||||
|
Run: runEaseeOcppEnable,
|
||||||
|
}
|
||||||
|
|
||||||
|
var easeeOcppDisableCmd = &cobra.Command{
|
||||||
|
Use: "disable",
|
||||||
|
Short: "Disable local OCPP on Easee charger",
|
||||||
|
Run: runEaseeOcppDisable,
|
||||||
|
}
|
||||||
|
|
||||||
|
var easeeOcppStatusCmd = &cobra.Command{
|
||||||
|
Use: "status",
|
||||||
|
Short: "Show Easee local OCPP configuration",
|
||||||
|
Run: runEaseeOcppStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(easeeOcppCmd)
|
||||||
|
easeeOcppCmd.AddCommand(easeeOcppEnableCmd)
|
||||||
|
easeeOcppCmd.AddCommand(easeeOcppDisableCmd)
|
||||||
|
easeeOcppCmd.AddCommand(easeeOcppStatusCmd)
|
||||||
|
|
||||||
|
easeeOcppCmd.PersistentFlags().String("user", "", "Easee account email")
|
||||||
|
easeeOcppCmd.PersistentFlags().String("password", "", "Easee account password")
|
||||||
|
easeeOcppCmd.PersistentFlags().String("charger", "", "Easee charger serial number")
|
||||||
|
|
||||||
|
_ = easeeOcppCmd.MarkPersistentFlagRequired("user")
|
||||||
|
_ = easeeOcppCmd.MarkPersistentFlagRequired("password")
|
||||||
|
_ = easeeOcppCmd.MarkPersistentFlagRequired("charger")
|
||||||
|
|
||||||
|
easeeOcppEnableCmd.Flags().String("url", "", "OCPP Central System URL (e.g. ws://192.168.1.100:8887/)")
|
||||||
|
_ = easeeOcppEnableCmd.MarkFlagRequired("url")
|
||||||
|
}
|
||||||
|
|
||||||
|
type localOcppConnectionDetails struct {
|
||||||
|
ConnectivityMode string `json:"connectivityMode"`
|
||||||
|
WebsocketConnectionArgs *localOcppWebsocketArgs `json:"websocketConnectionArgs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type localOcppWebsocketArgs struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type localOcppVersionResponse struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type localOcppApplyRequest struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type localOcppStatusResponse struct {
|
||||||
|
ConnectivityMode string `json:"connectivityMode"`
|
||||||
|
ChargePointID string `json:"chargePointId"`
|
||||||
|
WebsocketConnectionArgs *localOcppWebsocketArgs `json:"websocketConnectionArgs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func easeeOcppClient(user, password string) (*request.Helper, error) {
|
||||||
|
log := util.NewLogger("easee-ocpp")
|
||||||
|
log.Redact(user, password)
|
||||||
|
|
||||||
|
ts, err := easee.TokenSource(log, user, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("authentication failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := request.NewHelper(log)
|
||||||
|
client.Client.Transport = &oauth2.Transport{
|
||||||
|
Source: ts,
|
||||||
|
Base: client.Client.Transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func easeeOcppApplyConfig(client *request.Helper, serial string, details localOcppConnectionDetails) error {
|
||||||
|
uri := fmt.Sprintf("%s/v1/connection-details/%s", easee.LocalOcppAPI, serial)
|
||||||
|
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(details), request.JSONEncoding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var version localOcppVersionResponse
|
||||||
|
if err := client.DoJSON(req, &version); err != nil {
|
||||||
|
return fmt.Errorf("storing config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply := localOcppApplyRequest{Version: version.Version}
|
||||||
|
uri = fmt.Sprintf("%s/v1/connections/chargers/%s", easee.LocalOcppAPI, serial)
|
||||||
|
req, err = request.New(http.MethodPost, uri, request.MarshalJSON(apply), request.JSONEncoding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := client.DoBody(req); err != nil {
|
||||||
|
return fmt.Errorf("applying config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEaseeOcppEnable(cmd *cobra.Command, args []string) {
|
||||||
|
user, _ := cmd.Flags().GetString("user")
|
||||||
|
password, _ := cmd.Flags().GetString("password")
|
||||||
|
charger, _ := cmd.Flags().GetString("charger")
|
||||||
|
|
||||||
|
client, err := easeeOcppClient(user, password)
|
||||||
|
if err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ocppURL, _ := cmd.Flags().GetString("url")
|
||||||
|
|
||||||
|
details := localOcppConnectionDetails{
|
||||||
|
ConnectivityMode: "DualProtocol",
|
||||||
|
WebsocketConnectionArgs: &localOcppWebsocketArgs{URL: ocppURL},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := easeeOcppApplyConfig(client, charger, details); err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Local OCPP enabled for charger %s\n", charger)
|
||||||
|
fmt.Printf("OCPP Central System URL: %s\n", ocppURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEaseeOcppDisable(cmd *cobra.Command, args []string) {
|
||||||
|
user, _ := cmd.Flags().GetString("user")
|
||||||
|
password, _ := cmd.Flags().GetString("password")
|
||||||
|
charger, _ := cmd.Flags().GetString("charger")
|
||||||
|
|
||||||
|
client, err := easeeOcppClient(user, password)
|
||||||
|
if err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET current config, strip charger-appended serial from URL, set OcppOff
|
||||||
|
uri := fmt.Sprintf("%s/v1/connection-details/%s", easee.LocalOcppAPI, charger)
|
||||||
|
|
||||||
|
var current localOcppConnectionDetails
|
||||||
|
if err := client.GetJSON(uri, ¤t); err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
current.ConnectivityMode = "OcppOff"
|
||||||
|
if current.WebsocketConnectionArgs != nil {
|
||||||
|
current.WebsocketConnectionArgs.URL = strings.TrimSuffix(current.WebsocketConnectionArgs.URL, charger)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := easeeOcppApplyConfig(client, charger, current); err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Local OCPP disabled for charger %s\n", charger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEaseeOcppStatus(cmd *cobra.Command, args []string) {
|
||||||
|
user, _ := cmd.Flags().GetString("user")
|
||||||
|
password, _ := cmd.Flags().GetString("password")
|
||||||
|
charger, _ := cmd.Flags().GetString("charger")
|
||||||
|
|
||||||
|
client, err := easeeOcppClient(user, password)
|
||||||
|
if err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := fmt.Sprintf("%s/v1/connection-details/%s", easee.LocalOcppAPI, charger)
|
||||||
|
|
||||||
|
var status localOcppStatusResponse
|
||||||
|
if err := client.GetJSON(uri, &status); err != nil {
|
||||||
|
log.FATAL.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Charger: %s\n", charger)
|
||||||
|
fmt.Printf("Connectivity mode: %s\n", status.ConnectivityMode)
|
||||||
|
fmt.Printf("Charge point ID: %s\n", status.ChargePointID)
|
||||||
|
if status.WebsocketConnectionArgs != nil {
|
||||||
|
fmt.Printf("OCPP URL: %s\n", status.WebsocketConnectionArgs.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
templates/definition/charger/ocpp-easee.yaml
Normal file
50
templates/definition/charger/ocpp-easee.yaml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
template: ocpp-easee
|
||||||
|
products:
|
||||||
|
- brand: Easee
|
||||||
|
description:
|
||||||
|
generic: Home (OCPP)
|
||||||
|
- brand: Easee
|
||||||
|
description:
|
||||||
|
generic: Charge (OCPP)
|
||||||
|
- brand: Easee
|
||||||
|
description:
|
||||||
|
generic: Charge Lite (OCPP)
|
||||||
|
- brand: Easee
|
||||||
|
description:
|
||||||
|
generic: Charge Core (OCPP)
|
||||||
|
capabilities: ["rfid"]
|
||||||
|
requirements:
|
||||||
|
description:
|
||||||
|
de: |
|
||||||
|
Voraussetzungen:
|
||||||
|
* Firmware Version >= 328 mit aktiviertem nativem OCPP
|
||||||
|
* Für Stromsteuerung über OCPP ist Beta-Firmware erforderlich (getestet mit v9012)
|
||||||
|
* OCPP muss über die Easee API aktiviert werden:
|
||||||
|
* `evcc easee-ocpp --user <email> --password <passwort> --charger <seriennummer> enable --url ws://<evcc-host>:8887/`
|
||||||
|
* Zum Deaktivieren: `evcc easee-ocpp ... disable`
|
||||||
|
* Status prüfen: `evcc easee-ocpp ... status`
|
||||||
|
* Die Seriennummer des Chargers wird automatisch als Station-ID verwendet
|
||||||
|
* Protokoll: OCPP-J v1.6, Websocket, ws://
|
||||||
|
* Verbindung über das lokale Netzwerk
|
||||||
|
* Minimales Meter-Intervall: 30s
|
||||||
|
en: |
|
||||||
|
Requirements:
|
||||||
|
* Firmware version >= 328 with native OCPP enabled
|
||||||
|
* Beta firmware required for current control via OCPP (tested with v9012)
|
||||||
|
* OCPP must be enabled via the Easee API:
|
||||||
|
* `evcc easee-ocpp --user <email> --password <password> --charger <serial> enable --url ws://<evcc-host>:8887/`
|
||||||
|
* To disable: `evcc easee-ocpp ... disable`
|
||||||
|
* Check status: `evcc easee-ocpp ... status`
|
||||||
|
* The charger serial number is automatically used as station ID
|
||||||
|
* Protocol: OCPP-J v1.6, Websocket, ws://
|
||||||
|
* Local network connection
|
||||||
|
* Minimum meter interval: 30s
|
||||||
|
evcc: ["sponsorship", "skiptest"]
|
||||||
|
params:
|
||||||
|
- preset: ocpp
|
||||||
|
- name: metervalues
|
||||||
|
default: Energy.Active.Import.Register,Power.Active.Import,Current.Import
|
||||||
|
render: |
|
||||||
|
{{ include "ocpp" . }}
|
||||||
|
remotestart: true
|
||||||
|
meterinterval: 30s
|
||||||
Loading…
Add table
Add a link
Reference in a new issue