Vehicles: move more properties to /vehicles (BC) (#11181)
This commit is contained in:
parent
02214bcdea
commit
bc0343ab7d
26 changed files with 182 additions and 162 deletions
76
api/api.go
76
api/api.go
|
|
@ -2,88 +2,13 @@ package api
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:generate mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,CurrentLimiter,PhaseSwitcher,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery,Tariff,BatteryController
|
||||
|
||||
// ChargeMode is the charge operation mode. Valid values are off, now, minpv and pv
|
||||
type ChargeMode string
|
||||
|
||||
// Charge modes
|
||||
const (
|
||||
ModeEmpty ChargeMode = ""
|
||||
ModeOff ChargeMode = "off"
|
||||
ModeNow ChargeMode = "now"
|
||||
ModeMinPV ChargeMode = "minpv"
|
||||
ModePV ChargeMode = "pv"
|
||||
)
|
||||
|
||||
// String implements Stringer
|
||||
func (c ChargeMode) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// ChargeStatus is the EV's charging status from A to F
|
||||
type ChargeStatus string
|
||||
|
||||
// Charging states
|
||||
const (
|
||||
StatusNone ChargeStatus = ""
|
||||
StatusA ChargeStatus = "A" // Fzg. angeschlossen: nein Laden aktiv: nein Ladestation betriebsbereit, Fahrzeug getrennt
|
||||
StatusB ChargeStatus = "B" // Fzg. angeschlossen: ja Laden aktiv: nein Fahrzeug verbunden, Netzspannung liegt nicht an
|
||||
StatusC ChargeStatus = "C" // Fzg. angeschlossen: ja Laden aktiv: ja Fahrzeug lädt, Netzspannung liegt an
|
||||
StatusD ChargeStatus = "D" // Fzg. angeschlossen: ja Laden aktiv: ja Fahrzeug lädt mit externer Belüfungsanforderung (für Blei-Säure-Batterien)
|
||||
StatusE ChargeStatus = "E" // Fzg. angeschlossen: ja Laden aktiv: nein Fehler Fahrzeug / Kabel (CP-Kurzschluss, 0V)
|
||||
StatusF ChargeStatus = "F" // Fzg. angeschlossen: ja Laden aktiv: nein Fehler EVSE oder Abstecken simulieren (CP-Wake-up, -12V)
|
||||
)
|
||||
|
||||
var StatusEasA = map[ChargeStatus]ChargeStatus{StatusE: StatusA}
|
||||
|
||||
// ChargeStatusString converts a string to ChargeStatus
|
||||
func ChargeStatusString(status string) (ChargeStatus, error) {
|
||||
s := strings.ToUpper(strings.Trim(status, "\x00 "))
|
||||
|
||||
if len(s) == 0 {
|
||||
return StatusNone, fmt.Errorf("invalid status: %s", status)
|
||||
}
|
||||
|
||||
switch s1 := s[:1]; s1 {
|
||||
case "A", "B":
|
||||
return ChargeStatus(s1), nil
|
||||
|
||||
case "C", "D":
|
||||
if s == "C1" || s == "D1" {
|
||||
return StatusB, nil
|
||||
}
|
||||
return StatusC, nil
|
||||
|
||||
case "E", "F":
|
||||
return ChargeStatus(s1), fmt.Errorf("invalid status: %s", s)
|
||||
|
||||
default:
|
||||
return StatusNone, fmt.Errorf("invalid status: %s", status)
|
||||
}
|
||||
}
|
||||
|
||||
// ChargeStatusStringWithMapping converts a string to ChargeStatus. In case of error, mapping is applied.
|
||||
func ChargeStatusStringWithMapping(s string, m map[ChargeStatus]ChargeStatus) (ChargeStatus, error) {
|
||||
status, err := ChargeStatusString(s)
|
||||
if mappedStatus, ok := m[status]; ok && err != nil {
|
||||
return mappedStatus, nil
|
||||
}
|
||||
return status, err
|
||||
}
|
||||
|
||||
// String implements Stringer
|
||||
func (c ChargeStatus) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// Meter provides total active power in W
|
||||
type Meter interface {
|
||||
CurrentPower() (float64, error)
|
||||
|
|
@ -187,6 +112,7 @@ type Vehicle interface {
|
|||
Battery
|
||||
BatteryCapacity
|
||||
IconDescriber
|
||||
FeatureDescriber
|
||||
Title() string
|
||||
SetTitle(string)
|
||||
Phases() int
|
||||
|
|
|
|||
79
api/chargemodestatus.go
Normal file
79
api/chargemodestatus.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ChargeMode is the charge operation mode. Valid values are off, now, minpv and pv
|
||||
type ChargeMode string
|
||||
|
||||
// Charge modes
|
||||
const (
|
||||
ModeEmpty ChargeMode = ""
|
||||
ModeOff ChargeMode = "off"
|
||||
ModeNow ChargeMode = "now"
|
||||
ModeMinPV ChargeMode = "minpv"
|
||||
ModePV ChargeMode = "pv"
|
||||
)
|
||||
|
||||
// String implements Stringer
|
||||
func (c ChargeMode) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// ChargeStatus is the EV's charging status from A to F
|
||||
type ChargeStatus string
|
||||
|
||||
// Charging states
|
||||
const (
|
||||
StatusNone ChargeStatus = ""
|
||||
StatusA ChargeStatus = "A" // Fzg. angeschlossen: nein Laden aktiv: nein Ladestation betriebsbereit, Fahrzeug getrennt
|
||||
StatusB ChargeStatus = "B" // Fzg. angeschlossen: ja Laden aktiv: nein Fahrzeug verbunden, Netzspannung liegt nicht an
|
||||
StatusC ChargeStatus = "C" // Fzg. angeschlossen: ja Laden aktiv: ja Fahrzeug lädt, Netzspannung liegt an
|
||||
StatusD ChargeStatus = "D" // Fzg. angeschlossen: ja Laden aktiv: ja Fahrzeug lädt mit externer Belüfungsanforderung (für Blei-Säure-Batterien)
|
||||
StatusE ChargeStatus = "E" // Fzg. angeschlossen: ja Laden aktiv: nein Fehler Fahrzeug / Kabel (CP-Kurzschluss, 0V)
|
||||
StatusF ChargeStatus = "F" // Fzg. angeschlossen: ja Laden aktiv: nein Fehler EVSE oder Abstecken simulieren (CP-Wake-up, -12V)
|
||||
)
|
||||
|
||||
var StatusEasA = map[ChargeStatus]ChargeStatus{StatusE: StatusA}
|
||||
|
||||
// ChargeStatusString converts a string to ChargeStatus
|
||||
func ChargeStatusString(status string) (ChargeStatus, error) {
|
||||
s := strings.ToUpper(strings.Trim(status, "\x00 "))
|
||||
|
||||
if len(s) == 0 {
|
||||
return StatusNone, fmt.Errorf("invalid status: %s", status)
|
||||
}
|
||||
|
||||
switch s1 := s[:1]; s1 {
|
||||
case "A", "B":
|
||||
return ChargeStatus(s1), nil
|
||||
|
||||
case "C", "D":
|
||||
if s == "C1" || s == "D1" {
|
||||
return StatusB, nil
|
||||
}
|
||||
return StatusC, nil
|
||||
|
||||
case "E", "F":
|
||||
return ChargeStatus(s1), fmt.Errorf("invalid status: %s", s)
|
||||
|
||||
default:
|
||||
return StatusNone, fmt.Errorf("invalid status: %s", status)
|
||||
}
|
||||
}
|
||||
|
||||
// ChargeStatusStringWithMapping converts a string to ChargeStatus. In case of error, mapping is applied.
|
||||
func ChargeStatusStringWithMapping(s string, m map[ChargeStatus]ChargeStatus) (ChargeStatus, error) {
|
||||
status, err := ChargeStatusString(s)
|
||||
if mappedStatus, ok := m[status]; ok && err != nil {
|
||||
return mappedStatus, nil
|
||||
}
|
||||
return status, err
|
||||
}
|
||||
|
||||
// String implements Stringer
|
||||
func (c ChargeStatus) String() string {
|
||||
return string(c)
|
||||
}
|
||||
14
api/mock.go
14
api/mock.go
|
|
@ -361,6 +361,20 @@ func (mr *MockVehicleMockRecorder) Capacity() *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capacity", reflect.TypeOf((*MockVehicle)(nil).Capacity))
|
||||
}
|
||||
|
||||
// Features mocks base method.
|
||||
func (m *MockVehicle) Features() []Feature {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Features")
|
||||
ret0, _ := ret[0].([]Feature)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Features indicates an expected call of Features.
|
||||
func (mr *MockVehicleMockRecorder) Features() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Features", reflect.TypeOf((*MockVehicle)(nil).Features))
|
||||
}
|
||||
|
||||
// Icon mocks base method.
|
||||
func (m *MockVehicle) Icon() string {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export default {
|
|||
socBasedCharging: Boolean,
|
||||
socPerKwh: Number,
|
||||
vehicle: Object,
|
||||
vehicleCapacity: Number,
|
||||
capacity: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
},
|
||||
|
|
@ -208,7 +208,7 @@ export default {
|
|||
}
|
||||
return fmtEnergy(
|
||||
this.planEnergy,
|
||||
optionStep(this.vehicleCapacity || 100),
|
||||
optionStep(this.capacity || 100),
|
||||
this.fmtKWh,
|
||||
this.$t("main.targetEnergy.noLimit")
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
:id="`${id}_0`"
|
||||
class="mb-2"
|
||||
v-bind="plans[0] || {}"
|
||||
:vehicle-capacity="vehicleCapacity"
|
||||
:capacity="capacity"
|
||||
:range-per-soc="rangePerSoc"
|
||||
:soc-per-kwh="socPerKwh"
|
||||
:soc-based-planning="socBasedPlanning"
|
||||
|
|
@ -58,7 +58,7 @@ export default {
|
|||
smartCostType: String,
|
||||
currency: String,
|
||||
mode: String,
|
||||
vehicleCapacity: Number,
|
||||
capacity: Number,
|
||||
vehicle: Object,
|
||||
vehicleTargetSoc: Number,
|
||||
},
|
||||
|
|
@ -161,7 +161,7 @@ export default {
|
|||
this.$emit("plan-updated", {
|
||||
time: this.defaultDate(),
|
||||
soc: 100,
|
||||
energy: this.vehicleCapacity || 10,
|
||||
energy: this.capacity || 10,
|
||||
});
|
||||
},
|
||||
removePlan: function (index) {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export default {
|
|||
time: String,
|
||||
rangePerSoc: Number,
|
||||
socPerKwh: Number,
|
||||
vehicleCapacity: Number,
|
||||
capacity: Number,
|
||||
socBasedPlanning: Boolean,
|
||||
},
|
||||
emits: ["plan-updated", "plan-removed", "plan-preview"],
|
||||
|
|
@ -173,7 +173,7 @@ export default {
|
|||
energyOptions: function () {
|
||||
const options = energyOptions(
|
||||
0,
|
||||
this.vehicleCapacity || 100,
|
||||
this.capacity || 100,
|
||||
this.socPerKwh,
|
||||
this.fmtKWh,
|
||||
"-"
|
||||
|
|
@ -241,7 +241,7 @@ export default {
|
|||
}
|
||||
if (!this.selectedEnergy) {
|
||||
this.selectedEnergy =
|
||||
window.localStorage[LAST_ENERGY_GOAL_KEY] || this.vehicleCapacity || 10;
|
||||
window.localStorage[LAST_ENERGY_GOAL_KEY] || this.capacity || 10;
|
||||
}
|
||||
|
||||
let time = this.time;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
:id="session.vehicle"
|
||||
class="options"
|
||||
:vehicles="vehicles"
|
||||
:is-unknown="false"
|
||||
connected
|
||||
@change-vehicle="changeVehicle"
|
||||
@remove-vehicle="removeVehicle"
|
||||
|
|
|
|||
|
|
@ -47,21 +47,21 @@ export default {
|
|||
limitEnergy: Number,
|
||||
socPerKwh: Number,
|
||||
chargedEnergy: Number,
|
||||
vehicleCapacity: Number,
|
||||
capacity: Number,
|
||||
},
|
||||
emits: ["limit-energy-updated"],
|
||||
computed: {
|
||||
options: function () {
|
||||
return energyOptions(
|
||||
this.chargedEnergy,
|
||||
this.vehicleCapacity || 100,
|
||||
this.capacity || 100,
|
||||
this.socPerKwh,
|
||||
this.fmtKWh,
|
||||
this.$t("main.targetEnergy.noLimit")
|
||||
);
|
||||
},
|
||||
step() {
|
||||
return optionStep(this.vehicleCapacity || 100);
|
||||
return optionStep(this.capacity || 100);
|
||||
},
|
||||
estimated: function () {
|
||||
return estimatedSoc(this.limitEnergy, this.socPerKwh);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ const state = reactive({
|
|||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehiclePresent: true,
|
||||
vehicleTitle: "Mein Auto",
|
||||
vehicleName: "meinauto",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
mode: "pv",
|
||||
|
|
@ -29,14 +29,14 @@ const state = reactive({
|
|||
<Loadpoint v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="without soc">
|
||||
<Loadpoint v-bind="state" vehicleTitle="" :vehiclePresent="false" />
|
||||
<Loadpoint v-bind="state" vehicleTitle="" vehicleName="" />
|
||||
</Variant>
|
||||
<Variant title="idle">
|
||||
<Loadpoint
|
||||
v-bind="state"
|
||||
:enabled="false"
|
||||
:connected="false"
|
||||
:vehiclePresent="false"
|
||||
vehicleName=""
|
||||
mode="off"
|
||||
:charging="false"
|
||||
:chargeCurrent="0"
|
||||
|
|
|
|||
|
|
@ -156,8 +156,6 @@ export default {
|
|||
vehicleName: String,
|
||||
vehicleIcon: String,
|
||||
vehicleTargetSoc: Number,
|
||||
vehicleCapacity: Number,
|
||||
vehicleFeatureOffline: Boolean,
|
||||
vehicles: Array,
|
||||
planActive: Boolean,
|
||||
planProjectedStart: String,
|
||||
|
|
@ -247,13 +245,13 @@ export default {
|
|||
return !!this.vehicleName;
|
||||
},
|
||||
vehicleHasSoc: function () {
|
||||
return this.vehicleKnown && !this.vehicleFeatureOffline;
|
||||
return this.vehicleKnown && !this.vehicle?.features?.includes("Offline");
|
||||
},
|
||||
socBasedCharging: function () {
|
||||
return this.vehicleHasSoc || this.vehicleSoc > 0;
|
||||
},
|
||||
socBasedPlanning: function () {
|
||||
return this.socBasedCharging && this.vehicleCapacity > 0;
|
||||
return this.socBasedCharging && this.vehicle?.capacity > 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ function loadpoint(opts) {
|
|||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehiclePresent: true,
|
||||
vehicleName: "tesla",
|
||||
vehicleTitle: "Tesla Model 3",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ export default {
|
|||
},
|
||||
loadpointsCompact: function () {
|
||||
return this.loadpoints.map((lp) => {
|
||||
const icon = lp.chargerIcon || lp.vehicleIcon || "car";
|
||||
const vehicleIcon = this.vehicles?.[lp.vehicleName]?.icon;
|
||||
const icon = lp.chargerIcon || vehicleIcon || "car";
|
||||
const charging = lp.charging;
|
||||
const power = lp.chargePower || 0;
|
||||
return { icon, charging, power };
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ import { reactive } from "vue";
|
|||
import Vehicle from "./Vehicle.vue";
|
||||
|
||||
const state = reactive({
|
||||
vehicleTitle: "Mein Auto",
|
||||
vehicle: { title: "Mein Auto", icon: "car", capacity: 72, features: [] },
|
||||
enabled: false,
|
||||
connected: true,
|
||||
vehiclePresent: true,
|
||||
vehicleName: "meinauto",
|
||||
vehicleSoc: 42.742,
|
||||
vehicleRange: 231,
|
||||
limitSoc: 90,
|
||||
vehicleCapacity: 72,
|
||||
chargedEnergy: 14123,
|
||||
socBasedCharging: true,
|
||||
id: 0,
|
||||
|
|
@ -45,9 +44,9 @@ const hoursFromNow = function (hours) {
|
|||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
:vehiclePresent="false"
|
||||
vehicleName=""
|
||||
:socBasedCharging="false"
|
||||
:vehicleCapacity="null"
|
||||
:vehicle="{ ...state.vehicle, capacity: null }"
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -56,10 +55,13 @@ const hoursFromNow = function (hours) {
|
|||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
vehicleTitle="Opel Corsa-e"
|
||||
:socBasedCharging="false"
|
||||
:vehicleCapacity="72"
|
||||
vehicleFeatureOffline
|
||||
:vehicle="{
|
||||
...state.vehicle,
|
||||
title: 'Opel Corsa-e',
|
||||
capacity: 72,
|
||||
features: ['Offline'],
|
||||
}"
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -68,11 +70,14 @@ const hoursFromNow = function (hours) {
|
|||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
vehicleTitle="Opel Corsa-e"
|
||||
:socBasedCharging="false"
|
||||
:vehicleCapacity="72"
|
||||
:vehicle="{
|
||||
...state.vehicle,
|
||||
title: 'Opel Corsa-e',
|
||||
capacity: 72,
|
||||
features: ['Offline'],
|
||||
}"
|
||||
:targetEnergy="30"
|
||||
vehicleFeatureOffline
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
:limit-energy="limitEnergy"
|
||||
:soc-per-kwh="socPerKwh"
|
||||
:charged-energy="chargedEnergy"
|
||||
:vehicle-capacity="vehicleCapacity"
|
||||
:capacity="capacity"
|
||||
@limit-energy-updated="limitEnergyUpdated"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -117,16 +117,12 @@ export default {
|
|||
tariffCo2: Number,
|
||||
tariffGrid: Number,
|
||||
vehicle: Object,
|
||||
vehicleCapacity: Number,
|
||||
vehicleDetectionActive: Boolean,
|
||||
vehicleIcon: String,
|
||||
vehicleName: String,
|
||||
vehiclePresent: Boolean,
|
||||
vehicleRange: Number,
|
||||
vehicles: Array,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
vehicleTitle: String,
|
||||
},
|
||||
emits: ["limit-soc-updated", "limit-energy-updated", "change-vehicle", "remove-vehicle"],
|
||||
data() {
|
||||
|
|
@ -135,6 +131,15 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
title: function () {
|
||||
return this.vehicle?.title || "";
|
||||
},
|
||||
capacity: function () {
|
||||
return this.vehicle?.capacity || 0;
|
||||
},
|
||||
icon: function () {
|
||||
return this.vehicle?.icon || "";
|
||||
},
|
||||
minSoc: function () {
|
||||
return this.vehicle?.minSoc || 0;
|
||||
},
|
||||
|
|
@ -178,8 +183,8 @@ export default {
|
|||
return null;
|
||||
},
|
||||
socPerKwh: function () {
|
||||
if (this.vehicleCapacity > 0) {
|
||||
return 100 / this.vehicleCapacity;
|
||||
if (this.capacity > 0) {
|
||||
return 100 / this.capacity;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
{{ vehicle.title }}
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="!isUnknown">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item" @click="removeVehicle()">
|
||||
<span v-if="connected">{{ $t("main.vehicle.unknown") }}</span>
|
||||
<span v-else>{{ $t("main.vehicle.none") }}</span>
|
||||
|
|
@ -39,7 +39,6 @@ export default {
|
|||
connected: Boolean,
|
||||
id: [String, Number],
|
||||
vehicles: Array,
|
||||
isUnknown: Boolean,
|
||||
},
|
||||
emits: ["change-vehicle", "remove-vehicle"],
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ export default {
|
|||
name: "VehicleSoc",
|
||||
props: {
|
||||
connected: Boolean,
|
||||
vehiclePresent: Boolean,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
enabled: Boolean,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
<div class="d-flex justify-content-between mb-3 align-items-center" data-testid="vehicle-title">
|
||||
<h4 class="d-flex align-items-center m-0 flex-grow-1 overflow-hidden">
|
||||
<shopicon-regular-refresh
|
||||
v-if="icon === 'refresh'"
|
||||
v-if="iconType === 'refresh'"
|
||||
ref="refresh"
|
||||
data-bs-toggle="tooltip"
|
||||
:title="$t('main.vehicle.detectionActive')"
|
||||
class="me-2 flex-shrink-0 spin"
|
||||
></shopicon-regular-refresh>
|
||||
<VehicleIcon
|
||||
v-else-if="icon === 'vehicle'"
|
||||
:name="vehicleIcon"
|
||||
v-else-if="iconType === 'vehicle'"
|
||||
:name="icon"
|
||||
class="me-2 flex-shrink-0"
|
||||
/>
|
||||
<shopicon-regular-cablecharge
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
:id="id"
|
||||
class="options"
|
||||
:vehicles="otherVehicles"
|
||||
:is-unknown="isUnknown"
|
||||
@change-vehicle="changeVehicle"
|
||||
@remove-vehicle="removeVehicle"
|
||||
>
|
||||
|
|
@ -54,14 +53,14 @@ export default {
|
|||
connected: Boolean,
|
||||
id: [String, Number],
|
||||
vehicleDetectionActive: Boolean,
|
||||
vehicleIcon: String,
|
||||
icon: String,
|
||||
vehicleName: String,
|
||||
vehicles: { type: Array, default: () => [] },
|
||||
vehicleTitle: String,
|
||||
title: String,
|
||||
},
|
||||
emits: ["change-vehicle", "remove-vehicle"],
|
||||
computed: {
|
||||
icon() {
|
||||
iconType() {
|
||||
if (this.vehicleDetectionActive) {
|
||||
return "refresh";
|
||||
}
|
||||
|
|
@ -71,8 +70,8 @@ export default {
|
|||
return null;
|
||||
},
|
||||
name() {
|
||||
if (this.vehicleTitle) {
|
||||
return this.vehicleTitle;
|
||||
if (this.title) {
|
||||
return this.title;
|
||||
}
|
||||
if (this.connected) {
|
||||
return this.$t("main.vehicle.unknown");
|
||||
|
|
@ -93,7 +92,7 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
icon: function () {
|
||||
iconType: function () {
|
||||
this.tooltip();
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -60,15 +60,12 @@ const (
|
|||
RemoteDisabledSource = "remoteDisabledSource" // remote disabled source
|
||||
|
||||
// vehicle
|
||||
VehicleTitle = "vehicleTitle" // vehicle title
|
||||
VehicleName = "vehicleName" // vehicle name
|
||||
VehicleIdentity = "vehicleIdentity" // vehicle identity
|
||||
VehicleCapacity = "vehicleCapacity" // vehicle battery capacity
|
||||
VehicleDetectionActive = "vehicleDetectionActive" // vehicle detection active
|
||||
VehicleIcon = "vehicleIcon" // vehicle icon for ui
|
||||
VehicleOdometer = "vehicleOdometer" // vehicle odometer
|
||||
VehicleRange = "vehicleRange" // vehicle range
|
||||
VehicleSoc = "vehicleSoc" // vehicle soc
|
||||
VehicleTargetSoc = "vehicleTargetSoc" // vehicle soc limit
|
||||
VehicleTargetSoc = "vehicleTargetSoc" // vehicle api soc limit
|
||||
VehicleClimaterActive = "vehicleClimaterActive" // vehicle climater active
|
||||
)
|
||||
|
|
|
|||
|
|
@ -591,9 +591,7 @@ func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
|
|||
}
|
||||
|
||||
// vehicle
|
||||
lp.publish(keys.VehicleIcon, "")
|
||||
lp.publish(keys.VehicleName, "")
|
||||
lp.publish(keys.VehicleCapacity, 0.0)
|
||||
lp.publish(keys.VehicleOdometer, 0.0)
|
||||
|
||||
// assign and publish default vehicle
|
||||
|
|
|
|||
|
|
@ -386,10 +386,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
|||
vehicle := api.NewMockVehicle(ctrl)
|
||||
|
||||
// wrap vehicle with estimator
|
||||
vehicle.EXPECT().Capacity().Return(float64(10)).AnyTimes()
|
||||
vehicle.EXPECT().Phases().Return(0).AnyTimes()
|
||||
vehicle.EXPECT().Title().Return("foo").AnyTimes() // TODO remove when settings always available for vehicle
|
||||
vehicle.EXPECT().OnIdentified().Return(api.ActionConfig{}).AnyTimes()
|
||||
expectVehiclePublish(vehicle)
|
||||
|
||||
socEstimator := soc.NewEstimator(util.NewLogger("foo"), charger, vehicle, false)
|
||||
|
||||
|
|
|
|||
|
|
@ -132,8 +132,6 @@ func (lp *Loadpoint) setActiveVehicle(v api.Vehicle) {
|
|||
lp.socEstimator = soc.NewEstimator(lp.log, lp.charger, v, estimate)
|
||||
|
||||
lp.publish(keys.VehicleName, vehicle.Settings(lp.log, v).Name())
|
||||
lp.publish(keys.VehicleIcon, v.Icon())
|
||||
lp.publish(keys.VehicleCapacity, v.Capacity())
|
||||
|
||||
if mode, ok := v.OnIdentified().GetMode(); ok {
|
||||
lp.SetMode(mode)
|
||||
|
|
@ -146,8 +144,6 @@ func (lp *Loadpoint) setActiveVehicle(v api.Vehicle) {
|
|||
lp.socEstimator = nil
|
||||
lp.publish(keys.VehicleSoc, 0)
|
||||
lp.publish(keys.VehicleName, "")
|
||||
lp.publish(keys.VehicleIcon, "")
|
||||
lp.publish(keys.VehicleCapacity, 0.0)
|
||||
lp.publish(keys.VehicleOdometer, 0.0)
|
||||
}
|
||||
|
||||
|
|
@ -197,8 +193,6 @@ func (lp *Loadpoint) unpublishVehicle() {
|
|||
|
||||
lp.setRemainingEnergy(0)
|
||||
lp.setRemainingDuration(0)
|
||||
|
||||
lp.publishVehicleFeature(api.Offline)
|
||||
}
|
||||
|
||||
// vehicleHasFeature checks availability of vehicle feature
|
||||
|
|
@ -210,11 +204,6 @@ func (lp *Loadpoint) vehicleHasFeature(f api.Feature) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
// publishVehicleFeature availability of vehicle features
|
||||
func (lp *Loadpoint) publishVehicleFeature(f api.Feature) {
|
||||
lp.publish("vehicleFeature"+f.String(), lp.vehicleHasFeature(f))
|
||||
}
|
||||
|
||||
// vehicleUnidentified returns true if there are associated vehicles and detection is running.
|
||||
// It will also reset the api cache at regular intervals.
|
||||
// Detection is stopped after maximum duration and the "guest vehicle" message dispatched.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ import (
|
|||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func expectVehiclePublish(vehicle *api.MockVehicle) {
|
||||
vehicle.EXPECT().Title().Return("target").AnyTimes()
|
||||
vehicle.EXPECT().Capacity().AnyTimes()
|
||||
vehicle.EXPECT().Icon().AnyTimes()
|
||||
vehicle.EXPECT().Features().AnyTimes()
|
||||
vehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
}
|
||||
|
||||
func TestPublishSocAndRange(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
clck := clock.NewMock()
|
||||
|
|
@ -24,10 +33,7 @@ func TestPublishSocAndRange(t *testing.T) {
|
|||
charger.EXPECT().Enabled().Return(true, nil).AnyTimes()
|
||||
|
||||
vehicle := api.NewMockVehicle(ctrl)
|
||||
vehicle.EXPECT().Title().Return("target").AnyTimes()
|
||||
vehicle.EXPECT().Capacity().AnyTimes()
|
||||
vehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
expectVehiclePublish(vehicle)
|
||||
|
||||
log := util.NewLogger("foo")
|
||||
lp := &Loadpoint{
|
||||
|
|
@ -227,12 +233,9 @@ func TestReconnectVehicle(t *testing.T) {
|
|||
*api.MockChargeState
|
||||
}
|
||||
|
||||
vehicle := &vehicleT{api.NewMockVehicle(ctrl), api.NewMockChargeState(ctrl)}
|
||||
vehicle.MockVehicle.EXPECT().Title().Return("vehicle").AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().Icon().Return("").AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().Capacity().AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
v := api.NewMockVehicle(ctrl)
|
||||
vehicle := &vehicleT{v, api.NewMockChargeState(ctrl)}
|
||||
expectVehiclePublish(v)
|
||||
vehicle.MockVehicle.EXPECT().Identifiers().AnyTimes().Return(tc.vehicleId)
|
||||
vehicle.MockVehicle.EXPECT().Soc().Return(0.0, nil).AnyTimes()
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/evcc-io/evcc/core/vehicle"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type planStruct struct {
|
||||
|
|
@ -18,8 +19,11 @@ type planStruct struct {
|
|||
|
||||
type vehicleStruct struct {
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Capacity float64 `json:"capacity,omitempty"`
|
||||
MinSoc int `json:"minSoc,omitempty"`
|
||||
LimitSoc int `json:"limitSoc,omitempty"`
|
||||
Features []string `json:"features,omitempty"`
|
||||
Plans []planStruct `json:"plans,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -36,14 +40,19 @@ func (site *Site) publishVehicles() {
|
|||
plans = append(plans, planStruct{Soc: soc, Time: time})
|
||||
}
|
||||
|
||||
instance := v.Instance()
|
||||
|
||||
res[v.Name()] = vehicleStruct{
|
||||
Title: v.Instance().Title(),
|
||||
Title: instance.Title(),
|
||||
Icon: instance.Icon(),
|
||||
Capacity: instance.Capacity(),
|
||||
MinSoc: v.GetMinSoc(),
|
||||
LimitSoc: v.GetLimitSoc(),
|
||||
Features: lo.Map(instance.Features(), func(f api.Feature, _ int) string { return f.String() }),
|
||||
Plans: plans,
|
||||
}
|
||||
|
||||
if lp := site.coordinator.Owner(v.Instance()); lp != nil {
|
||||
if lp := site.coordinator.Owner(instance); lp != nil {
|
||||
lp.PublishEffectiveValues()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,10 @@ func (h *Hub) apply(ev Event, tmpl string) (string, error) {
|
|||
// add missing attributes
|
||||
if name, ok := attr["vehicleName"].(string); ok {
|
||||
if v, err := h.vehicles.ByName(name); err == nil {
|
||||
attr["vehicleTitle"] = v.Instance().Title()
|
||||
instance := v.Instance()
|
||||
attr["vehicleTitle"] = instance.Title()
|
||||
attr["vehicleIcon"] = instance.Icon()
|
||||
attr["vehicleCapacity"] = instance.Capacity()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
)
|
||||
|
||||
// TODO align phases with OnIdentify
|
||||
// TODO remove vehicle settings
|
||||
type embed struct {
|
||||
Title_ string `mapstructure:"title"`
|
||||
Icon_ string `mapstructure:"icon"`
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type Wrapper struct {
|
|||
// New creates a new Vehicle
|
||||
func New(name string, other map[string]interface{}, err error) api.Vehicle {
|
||||
var cc struct {
|
||||
// add manually since embed is not available here
|
||||
Title string
|
||||
Icon string
|
||||
Phases int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue