Skip to content

Commit

Permalink
Improved Charge Progress UI (evcc-io#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis authored Mar 26, 2021
1 parent 9d2d314 commit df19f23
Show file tree
Hide file tree
Showing 19 changed files with 362 additions and 116 deletions.
8 changes: 5 additions & 3 deletions assets/js/components/Loadpoint.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
/>
<fa-icon
icon="clock"
v-bind:class="{ fas: socTimerActive, far: !socTimerActive }"
v-bind:class="{ fas: timerActive, far: !timerActive }"
></fa-icon>
</label>
</div> -->
Expand Down Expand Up @@ -164,11 +164,13 @@ export default {
// vehicle
connected: Boolean,
// charging: Boolean,
enabled: Boolean,
socTitle: String,
socCharge: Number,
minSoC: Number,
socTimerSet: Boolean,
socTimerActive: Boolean,
timerSet: Boolean,
timerActive: Boolean,
targetTime: String,
// details
chargePower: Number,
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/LoadpointDetails.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ VehicleTimer.args = {
hasVehicle: true,
range: 240.123,
chargeEstimate: 5 * 3600,
socTimerSet: true,
socTimerActive: true,
timerSet: true,
timerActive: true,
};
5 changes: 0 additions & 5 deletions assets/js/components/LoadpointDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
<div class="col-6 col-md-3 mt-3">
<div class="mb-2 value">
Leistung
<fa-icon class="text-primary ml-1" icon="clock" v-if="socTimerActive"></fa-icon>
<fa-icon class="text-secondary ml-1" icon="clock" v-else-if="socTimerSet"></fa-icon>

<fa-icon
class="text-primary ml-1"
icon="temperature-low"
Expand Down Expand Up @@ -75,8 +72,6 @@ export default {
climater: String,
hasVehicle: Boolean,
range: Number,
socTimerActive: Boolean,
socTimerSet: Boolean,
},
mixins: [formatter],
};
Expand Down
122 changes: 113 additions & 9 deletions assets/js/components/Vehicle.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,128 @@ const Template = (args, { argTypes }) => ({

export const Base = Template.bind({});
Base.args = {
soc: false,
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: true,
socCharge: 42,
};

export const Car = Template.bind({});
Car.args = {
soc: true,
export const Connected = Template.bind({});
Connected.args = {
socTitle: "Mein Auto",
enabled: false,
connected: true,
socCharge: 15,
hasVehicle: true,
charging: false,
socCharge: 66,
};

export const CarWithMin = Template.bind({});
CarWithMin.args = {
soc: true,
export const ReadyToCharge = Template.bind({});
ReadyToCharge.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
socCharge: 15,
hasVehicle: true,
charging: false,
socCharge: 66,
};

export const Charging = Template.bind({});
Charging.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: true,
charging: true,
socCharge: 66,
};

const hoursFromNow = function (hours) {
const now = new Date();
now.setHours(now.getHours() + hours);
return now.toISOString();
};

export const TargetChargePlanned = Template.bind({});
TargetChargePlanned.args = {
socTitle: "Mein Auto",
enabled: false,
connected: true,
hasVehicle: true,
socCharge: 31,
minSoC: 20,
charging: false,
timerSet: true,
timerActive: false,
targetSoC: 45,
targetTime: hoursFromNow(14),
};

export const TargetChargeActive = Template.bind({});
TargetChargeActive.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: true,
socCharge: 66,
minSoC: 30,
charging: true,
timerSet: true,
timerActive: true,
targetSoC: 80,
targetTime: hoursFromNow(2),
};

export const MinCharge = Template.bind({});
MinCharge.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: true,
socCharge: 17,
minSoC: 20,
charging: true,
};

export const UnknownVehicleConnected = Template.bind({});
UnknownVehicleConnected.args = {
socTitle: "Mein Auto",
enabled: false,
connected: true,
hasVehicle: false,
};

export const UnknownVehicleReadyToCharge = Template.bind({});
UnknownVehicleReadyToCharge.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: false,
charging: false,
};

export const UnknownVehicleCharging = Template.bind({});
UnknownVehicleCharging.args = {
socTitle: "Mein Auto",
enabled: true,
connected: true,
hasVehicle: false,
charging: true,
};

export const Disconnected = Template.bind({});
Disconnected.args = {
socTitle: "Mein Auto",
connected: false,
hasVehicle: false,
};

export const DisconnectedKnownSoc = Template.bind({});
DisconnectedKnownSoc.args = {
socTitle: "Mein Auto",
connected: false,
enabled: false,
hasVehicle: true,
socCharge: 17,
};
130 changes: 117 additions & 13 deletions assets/js/components/Vehicle.vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
<template>
<div>
<div class="mb-2">{{ socTitle || "Fahrzeug" }}</div>
<div class="progress" style="height: 24px; font-size: 100%; margin-top: 16px">
<div class="mb-3">
{{ socTitle || "Fahrzeug" }}
</div>
<div class="progress" style="height: 28px; font-size: 100%">
<div
class="progress-bar"
role="progressbar"
:class="{
'progress-bar-striped': charging,
'progress-bar-animated': charging,
'bg-light': !connected,
'text-secondary': !connected,
'bg-warning': connected && minSoCActive,
[progressColor]: true,
}"
:style="{ width: socChargeDisplayWidth + '%' }"
>
{{ socChargeDisplayValue }}
</div>
<div
v-if="remainingSoCWidth"
class="progress-bar"
role="progressbar"
:class="{
'progress-bar-striped': charging,
'progress-bar-animated': charging,
'bg-warning': true,
[progressColor]: true,
'bg-muted': true,
}"
:style="{ width: minSoCRemainingDisplayWidth + '%' }"
v-if="minSoCActive && socChargeDisplayWidth < 100"
:style="{ width: remainingSoCWidth + '%' }"
></div>
</div>
<small v-if="markerLabel()" class="subline my-2 text-secondary">
<fa-icon
v-if="minSoCActive"
class="text-muted mr-1"
icon="exclamation-circle"
></fa-icon>
<fa-icon v-else-if="targetChargeEnabled" class="text-muted mr-1" icon="clock"></fa-icon>
{{ markerLabel() }}
</small>
</div>
</template>

<script>
import formatter from "../mixins/formatter";
export default {
name: "Vehicle",
props: {
socTitle: String,
connected: Boolean,
charging: Boolean,
hasVehicle: Boolean,
socCharge: Number,
enabled: Boolean,
charging: Boolean,
minSoC: Number,
timerActive: Boolean,
timerSet: Boolean,
targetTime: String,
targetSoC: Number,
},
computed: {
socChargeDisplayWidth: function () {
Expand All @@ -52,10 +68,12 @@ export default {
},
socChargeDisplayValue: function () {
// no soc or no soc value
if (!this.hasVehicle || this.socCharge < 0) {
if (!this.hasVehicle || !this.socCharge || this.socCharge < 0) {
let chargeStatus = "getrennt";
if (this.charging) {
chargeStatus = "laden";
chargeStatus = "lädt";
} else if (this.enabled) {
chargeStatus = "bereit";
} else if (this.connected) {
chargeStatus = "verbunden";
}
Expand All @@ -69,12 +87,98 @@ export default {
}
return socCharge;
},
socMarker: function () {
if (!this.connected || !this.hasVehicle) {
return null;
}
if (this.minSoCActive) {
return this.minSoC;
}
if (this.targetChargeEnabled) {
return this.targetSoC;
}
return null;
},
progressColor: function () {
if (!this.connected) {
return "bg-light border";
}
if (this.minSoCActive) {
return "bg-danger";
}
if (this.enabled) {
return "bg-primary";
}
return "bg-secondary";
},
minSoCActive: function () {
return this.minSoC > 0 && this.socCharge < this.minSoC;
},
minSoCRemainingDisplayWidth: function () {
return this.minSoC - this.socCharge;
targetChargeEnabled: function () {
return this.targetTime && this.timerSet;
},
remainingSoCWidth: function () {
if (!this.connected || this.socCharge === 100) {
return null;
}
if (this.minSoCActive) {
return this.minSoC - this.socCharge;
}
if (this.targetChargeEnabled) {
return this.targetSoC - this.socCharge;
}
return null;
},
},
methods: {
// not computed because it needs to update over time
markerLabel: function () {
if (!this.connected) {
return null;
}
if (this.minSoCActive) {
return `Mindestladung bis ${this.socMarker}%`;
}
if (this.targetChargeEnabled) {
const targetDate = Date.parse(this.targetTime);
if (this.timerActive) {
return `Lädt ${this.fmtRelativeTime(targetDate)} bis ${this.socMarker}%`;
} else {
return `Geplant bis ${this.fmtAbsoluteDate(targetDate)} bis ${this.socMarker}%`;
}
}
return null;
},
},
mixins: [formatter],
};
</script>
<style scoped>
.subline {
display: flex;
align-items: center;
}
.progress {
overflow: visible;
}
.progress-bar.bg-muted {
position: relative;
overflow: visible;
color: var(--white);
}
.progress-bar.bg-muted::after {
position: absolute;
right: 0;
top: -5px;
height: calc(100% + 10px);
width: 2px;
background: var(--dark);
content: "";
}
.bg-disabled {
background-color: var(--gray);
}
.bg-light {
color: var(--dark);
}
</style>
Loading

0 comments on commit df19f23

Please sign in to comment.