From 26f149571c841a073f2d29269078f89f353b225c Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 4 Oct 2022 20:08:57 +0200 Subject: [PATCH] Add target energy API & UI for vehicles without SoC --- api/api.go | 6 + api/feature.go | 17 ++ api/feature_enumer.go | 75 ++++++ assets/js/components/AnimatedNumber.vue | 8 +- assets/js/components/Loadpoint.stories.js | 20 ++ assets/js/components/Loadpoint.vue | 12 +- assets/js/components/TargetEnergySelect.vue | 119 ++++++++++ assets/js/components/Vehicle.stories.js | 57 ++++- assets/js/components/Vehicle.vue | 94 +++++--- assets/js/components/VehicleSoc.vue | 48 ++-- assets/js/components/VehicleTitle.vue | 7 +- assets/js/i18n/de.js | 4 + assets/js/i18n/en.js | 4 + cmd/demo.yaml | 5 + core/loadpoint.go | 52 +++- core/loadpoint/api.go | 4 + core/loadpoint_api.go | 32 +++ .../{index.a6e7d28f.js => index.019e1c40.js} | 42 ++-- ...{index.682a2576.css => index.5b6a2832.css} | 2 +- dist/index.html | 4 +- package-lock.json | 224 +++++++++--------- server/http.go | 1 + templates/definition/vehicle/offline.yaml | 1 + vehicle/embed.go | 18 +- 24 files changed, 664 insertions(+), 192 deletions(-) create mode 100644 api/feature.go create mode 100644 api/feature_enumer.go create mode 100644 assets/js/components/TargetEnergySelect.vue rename dist/assets/{index.a6e7d28f.js => index.019e1c40.js} (55%) rename dist/assets/{index.682a2576.css => index.5b6a2832.css} (95%) diff --git a/api/api.go b/api/api.go index aa299e34b..c6f5f8827 100644 --- a/api/api.go +++ b/api/api.go @@ -221,3 +221,9 @@ type AuthProvider interface { LoginHandler() http.HandlerFunc LogoutHandler() http.HandlerFunc } + +// FeatureDescriber optionally provides a list of supported non-api features +type FeatureDescriber interface { + Features() []Feature + Has(Feature) bool +} diff --git a/api/feature.go b/api/feature.go new file mode 100644 index 000000000..9d615d73a --- /dev/null +++ b/api/feature.go @@ -0,0 +1,17 @@ +package api + +type Feature int + +func (f *Feature) UnmarshalText(text []byte) error { + feat, err := FeatureString(string(text)) + if err == nil { + *f = feat + } + return err +} + +//go:generate enumer -type Feature +const ( + _ Feature = iota + Offline +) diff --git a/api/feature_enumer.go b/api/feature_enumer.go new file mode 100644 index 000000000..55293ab84 --- /dev/null +++ b/api/feature_enumer.go @@ -0,0 +1,75 @@ +// Code generated by "enumer -type Feature"; DO NOT EDIT. + +package api + +import ( + "fmt" + "strings" +) + +const _FeatureName = "Offline" + +var _FeatureIndex = [...]uint8{0, 7} + +const _FeatureLowerName = "offline" + +func (i Feature) String() string { + i -= 1 + if i < 0 || i >= Feature(len(_FeatureIndex)-1) { + return fmt.Sprintf("Feature(%d)", i+1) + } + return _FeatureName[_FeatureIndex[i]:_FeatureIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _FeatureNoOp() { + var x [1]struct{} + _ = x[Offline-(1)] +} + +var _FeatureValues = []Feature{Offline} + +var _FeatureNameToValueMap = map[string]Feature{ + _FeatureName[0:7]: Offline, + _FeatureLowerName[0:7]: Offline, +} + +var _FeatureNames = []string{ + _FeatureName[0:7], +} + +// FeatureString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func FeatureString(s string) (Feature, error) { + if val, ok := _FeatureNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _FeatureNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Feature values", s) +} + +// FeatureValues returns all values of the enum +func FeatureValues() []Feature { + return _FeatureValues +} + +// FeatureStrings returns a slice of all String values of the enum +func FeatureStrings() []string { + strs := make([]string, len(_FeatureNames)) + copy(strs, _FeatureNames) + return strs +} + +// IsAFeature returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Feature) IsAFeature() bool { + for _, v := range _FeatureValues { + if i == v { + return true + } + } + return false +} diff --git a/assets/js/components/AnimatedNumber.vue b/assets/js/components/AnimatedNumber.vue index 96f7e4242..3879062da 100644 --- a/assets/js/components/AnimatedNumber.vue +++ b/assets/js/components/AnimatedNumber.vue @@ -1,5 +1,5 @@ + + diff --git a/assets/js/components/Vehicle.stories.js b/assets/js/components/Vehicle.stories.js index e9613d93f..0cf6fc77b 100644 --- a/assets/js/components/Vehicle.stories.js +++ b/assets/js/components/Vehicle.stories.js @@ -24,6 +24,7 @@ Base.args = { vehicleSoC: 42, vehicleRange: 231, targetSoC: 90, + socBasedCharging: true, id: 0, }; @@ -36,6 +37,7 @@ Connected.args = { charging: false, vehicleSoC: 66, targetSoC: 90, + socBasedCharging: true, id: 0, }; @@ -48,6 +50,7 @@ ReadyToCharge.args = { charging: false, vehicleSoC: 66, targetSoC: 90, + socBasedCharging: true, id: 0, }; @@ -60,6 +63,7 @@ Charging.args = { charging: true, vehicleSoC: 66, targetSoC: 90, + socBasedCharging: true, id: 0, }; @@ -81,6 +85,7 @@ TargetChargePlanned.args = { targetTimeActive: false, targetSoC: 45, targetTime: hoursFromNow(14), + socBasedCharging: true, id: 0, }; @@ -96,6 +101,7 @@ TargetChargeActive.args = { targetTimeActive: true, targetSoC: 80, targetTime: hoursFromNow(2), + socBasedCharging: true, id: 0, }; @@ -109,6 +115,21 @@ MinCharge.args = { minSoC: 20, charging: true, targetSoC: 90, + socBasedCharging: true, + id: 0, +}; + +export const VehicleTargetSoc = Template.bind({}); +VehicleTargetSoc.args = { + vehicleTitle: "Mein Auto", + enabled: true, + connected: true, + vehiclePresent: true, + vehicleTargetSoC: 80, + vehicleSoC: 66, + charging: true, + targetSoC: 90, + socBasedCharging: true, id: 0, }; @@ -125,6 +146,7 @@ TimerPvEnable.args = { phaseRemainingInterpolated: 0, pvAction: "enable", pvRemainingInterpolated: 32, + socBasedCharging: true, id: 0, }; @@ -141,16 +163,17 @@ TimerPvDisable.args = { phaseRemainingInterpolated: 0, pvAction: "disable", pvRemainingInterpolated: 155, + socBasedCharging: true, id: 0, }; export const UnknownVehicleConnected = Template.bind({}); UnknownVehicleConnected.args = { - vehicleTitle: "Mein Auto", enabled: false, connected: true, vehiclePresent: false, targetSoC: 90, + socBasedCharging: false, id: 0, }; @@ -162,6 +185,7 @@ UnknownVehicleReadyToCharge.args = { vehiclePresent: false, charging: false, targetSoC: 100, + socBasedCharging: false, id: 0, }; @@ -173,6 +197,37 @@ UnknownVehicleCharging.args = { vehiclePresent: false, charging: true, targetSoC: 90, + socBasedCharging: false, + id: 0, +}; + +export const OfflineVehicleCharging = Template.bind({}); +OfflineVehicleCharging.args = { + vehicleTitle: "Polestar 2", + enabled: true, + connected: true, + vehiclePresent: true, + vehicleFeatureOffline: true, + vehicleCapacity: 72, + chargedEnergy: 14123, + charging: true, + targetSoC: 90, + socBasedCharging: false, + id: 0, +}; + +export const OfflineVehicleTargetEnergy = Template.bind({}); +OfflineVehicleTargetEnergy.args = { + vehicleTitle: "Polestar 2", + enabled: true, + connected: true, + vehiclePresent: true, + vehicleFeatureOffline: true, + vehicleCapacity: 72, + chargedEnergy: 14123, + targetEnergy: 30, + charging: true, + socBasedCharging: false, id: 0, }; diff --git a/assets/js/components/Vehicle.vue b/assets/js/components/Vehicle.vue index f17701b14..d34fbe19e 100644 --- a/assets/js/components/Vehicle.vue +++ b/assets/js/components/Vehicle.vue @@ -12,44 +12,65 @@ @target-soc-updated="targetSocUpdated" @target-soc-drag="targetSocDrag" /> -
-
- - - -
-
- vor 5 Stunden -
+ +
+ + + + + +
+
+ vor 5 Stunden
- + + diff --git a/package-lock.json b/package-lock.json index aae1729e0..1a38e7c39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,30 +78,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.3.tgz", + "integrity": "sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", + "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", + "@babel/generator": "^7.19.3", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.3", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/traverse": "^7.19.3", + "@babel/types": "^7.19.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -117,12 +117,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz", + "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==", "dev": true, "dependencies": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.19.3", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -170,14 +170,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.3", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "engines": { @@ -263,13 +263,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -312,9 +312,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -322,9 +322,9 @@ "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -431,9 +431,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -464,14 +464,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -492,9 +492,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz", + "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1923,19 +1923,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.3.tgz", + "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", + "@babel/generator": "^7.19.3", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/parser": "^7.19.3", + "@babel/types": "^7.19.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1944,13 +1944,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.3.tgz", + "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -24603,27 +24603,27 @@ } }, "@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.3.tgz", + "integrity": "sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==", "dev": true }, "@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", + "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", + "@babel/generator": "^7.19.3", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.3", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/traverse": "^7.19.3", + "@babel/types": "^7.19.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -24632,12 +24632,12 @@ } }, "@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz", + "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==", "dev": true, "requires": { - "@babel/types": "^7.18.13", + "@babel/types": "^7.19.3", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -24675,14 +24675,14 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.3", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" } }, @@ -24741,13 +24741,13 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -24778,9 +24778,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", @@ -24788,9 +24788,9 @@ "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helper-optimise-call-expression": { @@ -24867,9 +24867,9 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/helper-validator-option": { @@ -24891,14 +24891,14 @@ } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/highlight": { @@ -24913,9 +24913,9 @@ } }, "@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==" + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz", + "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -25861,31 +25861,31 @@ } }, "@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.3.tgz", + "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", + "@babel/generator": "^7.19.3", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", + "@babel/parser": "^7.19.3", + "@babel/types": "^7.19.3", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.3.tgz", + "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, diff --git a/server/http.go b/server/http.go index b40f0f54c..9f65cabc0 100644 --- a/server/http.go +++ b/server/http.go @@ -109,6 +109,7 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, cache *util.Cache) { routes := map[string]route{ "mode": {[]string{"POST", "OPTIONS"}, "/mode/{value:[a-z]+}", chargeModeHandler(lp)}, + "targetenergy": {[]string{"POST", "OPTIONS"}, "/targetenergy/{value:[0-9]+}", intHandler(pass(lp.SetTargetEnergy), lp.GetTargetEnergy)}, "targetsoc": {[]string{"POST", "OPTIONS"}, "/targetsoc/{value:[0-9]+}", intHandler(pass(lp.SetTargetSoC), lp.GetTargetSoC)}, "minsoc": {[]string{"POST", "OPTIONS"}, "/minsoc/{value:[0-9]+}", intHandler(pass(lp.SetMinSoC), lp.GetMinSoC)}, "mincurrent": {[]string{"POST", "OPTIONS"}, "/mincurrent/{value:[0-9]+}", floatHandler(pass(lp.SetMinCurrent), lp.GetMinCurrent)}, diff --git a/templates/definition/vehicle/offline.yaml b/templates/definition/vehicle/offline.yaml index 6e7b5f1e4..6cf099fde 100644 --- a/templates/definition/vehicle/offline.yaml +++ b/templates/definition/vehicle/offline.yaml @@ -20,6 +20,7 @@ params: valuetype: stringlist render: | type: custom + features: ["offline"] {{- if ne .title "" }} title: {{ .title }} {{- end }} diff --git a/vehicle/embed.go b/vehicle/embed.go index 71f9d2a10..633def331 100644 --- a/vehicle/embed.go +++ b/vehicle/embed.go @@ -1,12 +1,16 @@ package vehicle -import "github.com/evcc-io/evcc/api" +import ( + "github.com/evcc-io/evcc/api" + "golang.org/x/exp/slices" +) type embed struct { Title_ string `mapstructure:"title"` Capacity_ int64 `mapstructure:"capacity"` Phases_ int `mapstructure:"phases"` Identifiers_ []string `mapstructure:"identifiers"` + Features_ []api.Feature `mapstructure:"features"` OnIdentify api.ActionConfig `mapstructure:"onIdentify"` } @@ -34,3 +38,15 @@ func (v *embed) Identifiers() []string { func (v *embed) OnIdentified() api.ActionConfig { return v.OnIdentify } + +var _ api.FeatureDescriber = (*embed)(nil) + +// Features implements the api.Describer interface +func (v *embed) Features() []api.Feature { + return v.Features_ +} + +// Features implements the api.Describer interface +func (v *embed) Has(f api.Feature) bool { + return slices.Contains(v.Features_, f) +}