Target Charge: fix timezones (#3382) (#3386)

This commit is contained in:
Michael Geers 2022-05-18 06:35:26 +02:00 • committed by GitHub
parent ee8d3db356
commit aad8166b5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 92 additions and 25 deletions

View file

@ -202,8 +202,7 @@ export default {
api.post(this.apiPath("targetsoc") + "/" + soc);
},
setTargetTime: function (date) {
const formattedDate = `${this.fmtDayString(date)}T${this.fmtTimeString(date)}:00`;
api.post(this.apiPath("targetcharge") + "/" + this.targetSoC + "/" + formattedDate);
api.post(`${this.apiPath("targetcharge")}/${this.targetSoC}/${date.toISOString()}`);
},
removeTargetTime: function () {
api.delete(this.apiPath("targetcharge"));

View file

@ -103,7 +103,7 @@ export default {
minCharge: "minimum charging to {soc}%.",
waitForVehicle: "Ready. Waiting for vehicle.",
charging: "Charging.",
targetChargePlanned: "Target charge planned. Starting {start}.",
targetChargePlanned: "Target charge planned. Starting {time}.",
targetChargeWaitForVehicle: "Target charge ready. Wait for vehicle.",
targetChargeActive: "Target charge active.",
connected: "Connected.",

View file

@ -102,7 +102,7 @@ export default {
minCharge: "minimalus įkrovimas iki {soc}%.",
waitForVehicle: "Įkrovimas leidžiamas. Laukiama automobilio signalo.",
charging: "Įkraunama.",
targetChargePlanned: "Suplanuotas įkrovimas, prasidės {start}.",
targetChargePlanned: "Suplanuotas įkrovimas, prasidės {time}.",
targetChargeWaitForVehicle: "Suplanuotas įkrovimas leidžiamas. Laukiama automobilio signalo.",
targetChargeActive: "Suplanuotas įkrovimas aktyvuotas.",
connected: "Prijungtas.",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View file

@ -20,8 +20,8 @@
<meta name="theme-color" content="#28293e" />
<title>evcc</title>
<script type="module" crossorigin src="./assets/index.ab79ab45.js"></script>
<link rel="stylesheet" href="./assets/index.d3e49242.css">
<script type="module" crossorigin src="./assets/index.1062968a.js"></script>
<link rel="stylesheet" href="./assets/index.d9d5300d.css">
</head>
<body>
<script>

View file

@ -86,7 +86,7 @@ func NewHTTPd(addr string, site site.API, hub *SocketHub, cache *util.Cache) *HT
"mincurrent": {[]string{"POST", "OPTIONS"}, "/mincurrent/{value:[0-9]+}", minCurrentHandler(lp)},
"maxcurrent": {[]string{"POST", "OPTIONS"}, "/maxcurrent/{value:[0-9]+}", maxCurrentHandler(lp)},
"phases": {[]string{"POST", "OPTIONS"}, "/phases/{value:[0-9]+}", phasesHandler(lp)},
"targetcharge": {[]string{"POST", "OPTIONS"}, "/targetcharge/{soc:[0-9]+}/{time:[0-9TZ:-]+}", targetChargeHandler(lp)},
"targetcharge": {[]string{"POST", "OPTIONS"}, "/targetcharge/{soc:[0-9]+}/{time:[0-9TZ:.-]+}", targetChargeHandler(lp)},
"targetcharge2": {[]string{"DELETE", "OPTIONS"}, "/targetcharge", targetChargeRemoveHandler(lp)},
"vehicle": {[]string{"DELETE", "OPTIONS"}, "/vehicle", vehicleRemoveHandler(lp)},
"remotedemand": {[]string{"POST", "OPTIONS"}, "/remotedemand/{demand:[a-z]+}/{source::[0-9a-zA-Z_-]+}", remoteDemandHandler(lp)},

View file

@ -5,7 +5,6 @@ import (
"fmt"
"io/fs"
"net/http"
"os"
"strconv"
"text/template"
"time"
@ -222,18 +221,8 @@ func remoteDemandHandler(lp loadpoint.API) http.HandlerFunc {
}
}
func timezone() *time.Location {
tz := os.Getenv("TZ")
if tz == "" {
tz = "Local"
}
loc, _ := time.LoadLocation(tz)
return loc
}
// targetChargeHandler updates target soc
func targetChargeHandler(loadpoint loadpoint.API) http.HandlerFunc {
func targetChargeHandler(loadpoint targetCharger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
@ -246,7 +235,7 @@ func targetChargeHandler(loadpoint loadpoint.API) http.HandlerFunc {
}
timeS, ok := vars["time"]
timeV, err := time.ParseInLocation("2006-01-02T15:04:05", timeS, timezone())
timeV, err := time.Parse(time.RFC3339, timeS)
if !ok || err != nil {
jsonError(w, http.StatusBadRequest, err)
@ -291,3 +280,9 @@ func socketHandler(hub *SocketHub) http.HandlerFunc {
ServeWebsocket(hub, w, r)
}
}
// TargetCharger defines target charge related loadpoint operations
type targetCharger interface {
// SetTargetCharge sets the charge targetSoC
SetTargetCharge(time.Time, int)
}

View file

@ -0,0 +1,73 @@
package server
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gorilla/mux"
)
type mockLoadpoint struct {
SoC int
TargetTime time.Time
}
func (lp *mockLoadpoint) SetTargetCharge(time time.Time, soc int) {
lp.SoC = soc
lp.TargetTime = time
}
func TestTargetChargeHandler(t *testing.T) {
tc := []struct {
inSoC string
inTime string
statusCode int
outSoC int
outTime time.Time
}{
{"70", "2022-05-17T06:20:59.509Z", http.StatusOK,
70, time.Date(2022, 05, 17, 06, 20, 59, 509000000, time.UTC)},
{"foo", "2022-05-17T06:20:59.509Z", http.StatusBadRequest, 0, time.Time{}},
{"70", "2022-05-17 06:20:59", http.StatusBadRequest, 0, time.Time{}},
}
for _, tc := range tc {
mockLp := &mockLoadpoint{}
handler := http.HandlerFunc(targetChargeHandler(mockLp))
req, err := http.NewRequest("GET", fmt.Sprintf("/targetcharge/%s/%s", tc.inSoC, tc.inTime), nil)
if err != nil {
t.Fatal(err)
}
vars := map[string]string{
"soc": tc.inSoC,
"time": tc.inTime,
}
req = mux.SetURLVars(req, vars)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if status := rr.Code; status != tc.statusCode {
t.Errorf("handler returned wrong status code: got %v want %v",
status, tc.statusCode)
}
if mockLp.SoC != tc.outSoC {
t.Errorf("wrong target soc: got %v want %v", mockLp.SoC, tc.outSoC)
}
isoFormat := "2006-01-02T15:04:05.999Z07:00"
if !mockLp.TargetTime.Equal(tc.outTime) {
t.Errorf("wrong target time year: got %v want %v", mockLp.TargetTime.UTC().Format(isoFormat), tc.outTime.Format(isoFormat))
}
}
}