Skip to content

Commit

Permalink
Add schedule for planner (#16091)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maschga authored Dec 1, 2024
1 parent 35c9452 commit a35a705
Show file tree
Hide file tree
Showing 33 changed files with 1,771 additions and 421 deletions.
9 changes: 9 additions & 0 deletions api/plans.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package api

type RepeatingPlanStruct struct {
Weekdays []int `json:"weekdays"` // 0-6 (Sunday-Saturday)
Time string `json:"time"` // HH:MM
Tz string `json:"tz"` // timezone in IANA format
Soc int `json:"soc"`
Active bool `json:"active"`
}
4 changes: 4 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,7 @@ html.app .modal-dialog {
.alert-danger code {
color: var(--evcc-darkest-green);
}

input::-webkit-date-and-time-value {
text-align: left;
}
49 changes: 35 additions & 14 deletions assets/js/components/ChargingPlan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
data-testid="charging-plan"
>
<div class="value m-0 d-block align-items-baseline justify-content-center">
<button class="value-button p-0" :class="buttonColor" @click="openModal">
<button
class="value-button p-0"
:class="buttonColor"
data-testid="charging-plan-button"
@click="openModal"
>
<strong v-if="enabled">
<span class="targetTimeLabel"> {{ targetTimeLabel }}</span>
<div
Expand All @@ -33,6 +38,7 @@
tabindex="-1"
role="dialog"
aria-hidden="true"
data-testid="charging-plan-modal"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
Expand Down Expand Up @@ -74,11 +80,12 @@
</li>
</ul>
<div v-if="isModalVisible">
<ChargingPlanSettings
<ChargingPlansSettings
v-if="departureTabActive"
v-bind="chargingPlanSettingsProps"
@plan-updated="updatePlan"
@plan-removed="removePlan"
v-bind="chargingPlansSettingsProps"
@static-plan-updated="updateStaticPlan"
@static-plan-removed="removeStaticPlan"
@repeating-plans-updated="updateRepeatingPlans"
/>
<ChargingPlanArrival
v-if="arrivalTabActive"
Expand All @@ -98,7 +105,7 @@
<script>
import Modal from "bootstrap/js/dist/modal";
import LabelAndValue from "./LabelAndValue.vue";
import ChargingPlanSettings from "./ChargingPlanSettings.vue";
import ChargingPlansSettings from "./ChargingPlansSettings.vue";
import ChargingPlanArrival from "./ChargingPlanArrival.vue";
import formatter from "../mixins/formatter";
Expand All @@ -110,7 +117,7 @@ const ONE_MINUTE = 60 * 1000;
export default {
name: "ChargingPlan",
components: { LabelAndValue, ChargingPlanSettings, ChargingPlanArrival },
components: { LabelAndValue, ChargingPlansSettings, ChargingPlanArrival },
mixins: [formatter, collector],
props: {
currency: String,
Expand Down Expand Up @@ -162,12 +169,18 @@ export default {
limitSoc: function () {
return this.vehicle?.limitSoc;
},
plans: function () {
staticPlan: function () {
if (this.socBasedPlanning) {
return this.vehicle?.plans || [];
return this.vehicle?.plan;
}
if (this.planEnergy && this.planTime) {
return [{ energy: this.planEnergy, time: this.planTime }];
return { energy: this.planEnergy, time: this.planTime };
}
return null;
},
repeatingPlans: function () {
if (this.vehicle?.repeatingPlans.length > 0) {
return [...this.vehicle.repeatingPlans];
}
return [];
},
Expand All @@ -183,8 +196,8 @@ export default {
arrivalTabActive: function () {
return this.activeTab === "arrival";
},
chargingPlanSettingsProps: function () {
return this.collectProps(ChargingPlanSettings);
chargingPlansSettingsProps: function () {
return this.collectProps(ChargingPlansSettings);
},
chargingPlanArrival: function () {
return this.collectProps(ChargingPlanArrival);
Expand All @@ -211,6 +224,11 @@ export default {
effectivePlanTime() {
this.updateTargetTimeLabel();
},
"$i18n.locale": {
handler() {
this.updateTargetTimeLabel();
},
},
},
mounted() {
this.modal = Modal.getOrCreateInstance(this.$refs.modal);
Expand Down Expand Up @@ -263,21 +281,24 @@ export default {
showArrivalTab: function () {
this.activeTab = "arrival";
},
updatePlan: function ({ soc, time, energy }) {
updateStaticPlan: function ({ soc, time, energy }) {
const timeISO = time.toISOString();
if (this.socBasedPlanning) {
api.post(`${this.apiVehicle}plan/soc/${soc}/${timeISO}`);
} else {
api.post(`${this.apiLoadpoint}plan/energy/${energy}/${timeISO}`);
}
},
removePlan: function () {
removeStaticPlan: function () {
if (this.socBasedPlanning) {
api.delete(`${this.apiVehicle}plan/soc`);
} else {
api.delete(`${this.apiLoadpoint}plan/energy`);
}
},
updateRepeatingPlans: function (plans) {
api.post(`${this.apiVehicle}plan/repeating`, { plans });
},
setMinSoc: function (soc) {
api.post(`${this.apiVehicle}minsoc/${soc}`);
},
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/ChargingPlanPreview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ describe("basics", () => {
result = wrapper.vm.slots;
});

test("should return 42 slots", () => {
expect(result.length).eq(42);
test("should return 39 slots", () => {
expect(result.length).eq(39);
});

test("slots should be an hour apart", () => {
Expand Down
37 changes: 34 additions & 3 deletions assets/js/components/ChargingPlanPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@
</div>
</div>
</div>
<TariffChart :slots="slots" @slot-hovered="slotHovered" />
<TariffChart
:slots="slots"
:target-text="targetText"
:target-offset="targetHourOffset"
@slot-hovered="slotHovered"
/>
</div>
</template>

Expand Down Expand Up @@ -114,12 +119,28 @@ export default {
}
return null;
},
targetHourOffset() {
if (!this.targetTime) {
return null;
}
const start = new Date(this.startTime);
start.setMinutes(0);
start.setSeconds(0);
start.setMilliseconds(0);
return (this.targetTime.getTime() - start.getTime()) / (60 * 60 * 1000);
},
targetText() {
if (!this.targetTime) {
return null;
}
return this.fmtWeekdayTime(this.targetTime);
},
slots() {
const result = [];
const rates = this.convertDates(this.rates);
const plan = this.convertDates(this.plan);
const oneHour = 60 * 60 * 1000;
for (let i = 0; i < 42; i++) {
for (let i = 0; i < 39; i++) {
const start = new Date(this.startTime.getTime() + oneHour * i);
const startHour = start.getHours();
start.setMinutes(0);
Expand All @@ -132,13 +153,23 @@ export default {
const toLate = this.targetTime && this.targetTime <= start;
// TODO: handle multiple matching time slots
const price = this.findSlotInRange(start, end, rates)?.price;
const isTarget = start <= this.targetTime && end > this.targetTime;
const charging = this.findSlotInRange(start, end, plan) != null;
const warning =
charging &&
this.targetTime &&
end > this.targetTime &&
this.targetTime < this.endTime;
result.push({ day, price, startHour, endHour, charging, toLate, warning });
result.push({
day,
price,
startHour,
endHour,
charging,
toLate,
warning,
isTarget,
});
}
return result;
},
Expand Down
Loading

0 comments on commit a35a705

Please sign in to comment.