Skip to content

Commit 4d58374

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 4584afb + 6cefd83 commit 4d58374

File tree

111 files changed

+2388
-1446
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+2388
-1446
lines changed

CONTRIBUTING.md

+7
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ make ui build
115115

116116
evcc already includes many translations for the UI. We're using [Weblate](https://hosted.weblate.org/projects/evcc/evcc/) to maintain translations. Feel free to add more languages or verify and edit existing translations. Weblate will automatically push all modifications to the evcc repository where they get reviewed and merged.
117117

118+
If you find a text that is not yet translatable in [Weblate](https://hosted.weblate.org/projects/evcc/evcc/), you can help us by making it translatable. To do this, you can simply find the missing translation text in the code and apply similar changes as in these two Pull Requests:
119+
120+
- [UI: Add missing translation for Error during startup](https://github.com/evcc-io/evcc/pull/14695)
121+
- [Translation: kein Plan, keine Grenze](https://github.com/evcc-io/evcc/pull/7461/)
122+
123+
Note: To ensure the build succeeds after creating new translations, make sure to include your new translations in both the [de.toml](i18n/de.toml) and [en.toml](i18n/en.toml) files.
124+
118125
[![Languages](https://hosted.weblate.org/widgets/evcc/-/evcc/multi-auto.svg)](https://hosted.weblate.org/engage/evcc/)
119126

120127
[1]: https://go.dev

Dockerfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ COPY packaging/docker/bin/* /app/
8484

8585
# mDNS
8686
EXPOSE 5353/udp
87+
# EEBus
88+
EXPOSE 4712/tcp
8789
# UI and /api
8890
EXPOSE 7070/tcp
8991
# KEBA charger
9092
EXPOSE 7090/udp
9193
# OCPP charger
9294
EXPOSE 8887/tcp
93-
# GoodWe Wifi Inverter
95+
# Modbus UDP
9496
EXPOSE 8899/udp
9597
# SMA Energy Manager
9698
EXPOSE 9522/udp

api/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ type Circuit interface {
222222
SetTitle(string)
223223
GetParent() Circuit
224224
RegisterChild(child Circuit)
225+
Wrap(parent Circuit) error
225226
HasMeter() bool
226227
GetMaxPower() float64
227228
GetMaxCurrent() float64

api/feature.go

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ const (
1010
IntegratedDevice
1111
Heating
1212
Retryable
13+
WelcomeCharge
1314
)

api/feature_enumer.go

+8-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/globalconfig/types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import (
66
"strconv"
77
"time"
88

9-
"github.com/evcc-io/evcc/charger/eebus"
109
"github.com/evcc-io/evcc/provider/mqtt"
1110
"github.com/evcc-io/evcc/push"
11+
"github.com/evcc-io/evcc/server/eebus"
1212
"github.com/evcc-io/evcc/util/config"
1313
"github.com/evcc-io/evcc/util/modbus"
1414
)

api/mock.go

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/app.css

+4
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ a:hover {
250250
--bs-btn-hover-border-color: var(--bs-gray-medium);
251251
}
252252

253+
.btn-link:hover {
254+
color: var(--bs-primary);
255+
}
256+
253257
.dark .btn-outline-secondary {
254258
--bs-btn-color: var(--bs-gray-bright);
255259
--bs-btn-border-color: var(--bs-gray-bright);

assets/js/components/ChargingPlan.vue

+15-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<div class="value m-0 d-block align-items-baseline justify-content-center">
1010
<button class="value-button p-0" :class="buttonColor" @click="openModal">
1111
<strong v-if="enabled">
12-
<span class="targetTimeLabel"> {{ targetTimeLabel() }}</span>
12+
<span class="targetTimeLabel"> {{ targetTimeLabel }}</span>
1313
<div
1414
class="extraValue text-nowrap"
1515
:class="{ 'text-warning': planTimeUnreachable }"
@@ -106,6 +106,8 @@ import collector from "../mixins/collector";
106106
import api from "../api";
107107
import { optionStep, fmtEnergy } from "../utils/energyOptions";
108108
109+
const ONE_MINUTE = 60 * 1000;
110+
109111
export default {
110112
name: "ChargingPlan",
111113
components: { LabelAndValue, ChargingPlanSettings, ChargingPlanArrival },
@@ -140,6 +142,8 @@ export default {
140142
modal: null,
141143
isModalVisible: false,
142144
activeTab: "departure",
145+
targetTimeLabel: "",
146+
interval: null,
143147
};
144148
},
145149
computed: {
@@ -203,16 +207,24 @@ export default {
203207
return `loadpoints/${this.id}/`;
204208
},
205209
},
210+
watch: {
211+
effectivePlanTime() {
212+
this.updateTargetTimeLabel();
213+
},
214+
},
206215
mounted() {
207216
this.modal = Modal.getOrCreateInstance(this.$refs.modal);
208217
this.$refs.modal.addEventListener("show.bs.modal", this.modalVisible);
209218
this.$refs.modal.addEventListener("hidden.bs.modal", this.modalInvisible);
210219
this.$refs.modal.addEventListener("hide.bs.modal", this.checkUnsavedOnClose);
220+
this.interval = setInterval(this.updateTargetTimeLabel, ONE_MINUTE);
221+
this.updateTargetTimeLabel();
211222
},
212223
unmounted() {
213224
this.$refs.modal?.removeEventListener("show.bs.modal", this.modalVisible);
214225
this.$refs.modal?.removeEventListener("hidden.bs.modal", this.modalInvisible);
215226
this.$refs.modal?.removeEventListener("hide.bs.modal", this.checkUnsavedOnClose);
227+
clearInterval(this.interval);
216228
},
217229
methods: {
218230
checkUnsavedOnClose: function () {
@@ -241,10 +253,9 @@ export default {
241253
}
242254
this.modal.show();
243255
},
244-
// not computed because it needs to update over time
245-
targetTimeLabel: function () {
256+
updateTargetTimeLabel: function () {
246257
const targetDate = new Date(this.effectivePlanTime);
247-
return this.fmtAbsoluteDate(targetDate);
258+
this.targetTimeLabel = this.fmtAbsoluteDate(targetDate);
248259
},
249260
showDeatureTab: function () {
250261
this.activeTab = "departure";

assets/js/components/Config/CircuitsModal.vue

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
docs="/docs/features/loadmanagement"
77
:defaultYaml="defaultYaml"
88
endpoint="/config/circuits"
9-
size="md"
109
@changed="$emit('changed')"
1110
/>
1211
</template>

assets/js/components/Config/MeterModal.vue

+44-33
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,25 @@
8080
:defaultPort="modbus.Port"
8181
:capabilities="modbusCapabilities"
8282
/>
83-
<FormRow
84-
v-for="param in templateParams"
85-
:id="`meterParam${param.Name}`"
83+
<PropertyEntry
84+
v-for="param in normalParams"
8685
:key="param.Name"
87-
:optional="!param.Required"
88-
:label="param.Description || `[${param.Name}]`"
89-
:help="param.Description === param.Help ? undefined : param.Help"
90-
:example="param.Example"
91-
>
92-
<PropertyField
93-
:id="`meterParam${param.Name}`"
94-
v-model="values[param.Name]"
95-
:masked="param.Mask"
96-
:property="param.Name"
97-
:type="param.Type"
98-
class="me-2"
99-
:required="param.Required"
100-
:validValues="param.ValidValues"
101-
/>
102-
</FormRow>
86+
:id="`meterParam${param.Name}`"
87+
v-bind="param"
88+
v-model="values[param.Name]"
89+
/>
90+
91+
<PropertyCollapsible>
92+
<template v-if="advancedParams.length" #advanced>
93+
<PropertyEntry
94+
v-for="param in advancedParams"
95+
:key="param.Name"
96+
:id="`meterParam${param.Name}`"
97+
v-bind="param"
98+
v-model="values[param.Name]"
99+
/>
100+
</template>
101+
</PropertyCollapsible>
103102

104103
<TestResult
105104
v-if="templateName"
@@ -158,7 +157,8 @@
158157

159158
<script>
160159
import FormRow from "./FormRow.vue";
161-
import PropertyField from "./PropertyField.vue";
160+
import PropertyEntry from "./PropertyEntry.vue";
161+
import PropertyCollapsible from "./PropertyCollapsible.vue";
162162
import TestResult from "./TestResult.vue";
163163
import api from "../../api";
164164
import test from "./mixins/test";
@@ -171,9 +171,18 @@ function sleep(ms) {
171171
return new Promise((resolve) => setTimeout(resolve, ms));
172172
}
173173
174+
const CUSTOM_FIELDS = ["usage", "modbus"];
175+
174176
export default {
175177
name: "MeterModal",
176-
components: { FormRow, PropertyField, Modbus, TestResult, AddDeviceButton },
178+
components: {
179+
FormRow,
180+
PropertyEntry,
181+
Modbus,
182+
TestResult,
183+
AddDeviceButton,
184+
PropertyCollapsible,
185+
},
177186
mixins: [test],
178187
props: {
179188
id: Number,
@@ -215,18 +224,20 @@ export default {
215224
return this.products.filter((p) => p.group === "generic");
216225
},
217226
templateParams() {
218-
const params = this.template?.Params || [];
219-
return (
220-
params
221-
// deprecated fields
222-
.filter((p) => !p.Deprecated)
223-
// remove usage option
224-
.filter((p) => p.Name !== "usage")
225-
// remove modbus, handles separately
226-
.filter((p) => p.Name !== "modbus")
227-
// capacity only for battery meters
228-
.filter((p) => this.meterType === "battery" || p.Name !== "capacity")
229-
);
227+
return (this.template?.Params || [])
228+
.filter((p) => !CUSTOM_FIELDS.includes(p.Name))
229+
.map((p) => {
230+
if (this.meterType === "battery" && p.Name === "capacity") {
231+
p.Advanced = false;
232+
}
233+
return p;
234+
});
235+
},
236+
normalParams() {
237+
return this.templateParams.filter((p) => !p.Advanced);
238+
},
239+
advancedParams() {
240+
return this.templateParams.filter((p) => p.Advanced);
230241
},
231242
modbus() {
232243
const params = this.template?.Params || [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<template>
2+
<div v-if="$slots.advanced || $slots.more">
3+
<button
4+
class="btn btn-link btn-sm text-gray px-0 border-0 d-flex align-items-center mb-2"
5+
:class="open ? 'text-primary' : ''"
6+
type="button"
7+
@click="toggle"
8+
>
9+
<span v-if="open">Hide advanced settings</span>
10+
<span v-else>Show advanced settings</span>
11+
<DropdownIcon class="icon" :class="{ iconUp: open }" />
12+
</button>
13+
14+
<Transition>
15+
<div v-if="open" class="pt-2">
16+
<slot name="advanced"></slot>
17+
<hr v-if="$slots.advanced && $slots.more" class="my-5" />
18+
<slot name="more"></slot>
19+
</div>
20+
</Transition>
21+
</div>
22+
</template>
23+
24+
<script>
25+
import DropdownIcon from "../MaterialIcon/Dropdown.vue";
26+
27+
export default {
28+
name: "PropertyCollapsible",
29+
components: { DropdownIcon },
30+
data() {
31+
return { open: false };
32+
},
33+
34+
methods: {
35+
toggle() {
36+
this.open = !this.open;
37+
},
38+
},
39+
};
40+
</script>
41+
<style scoped>
42+
.icon {
43+
transform: rotate(0deg);
44+
transition: transform var(--evcc-transition-medium) ease;
45+
}
46+
.iconUp {
47+
transform: rotate(-180deg);
48+
}
49+
.v-enter-active,
50+
.v-leave-active {
51+
transition:
52+
transform var(--evcc-transition-medium) ease,
53+
opacity var(--evcc-transition-medium) ease;
54+
transform: translateY(0);
55+
}
56+
57+
.v-enter-from,
58+
.v-leave-to {
59+
opacity: 0;
60+
transform: translateY(-0.5rem);
61+
}
62+
</style>

0 commit comments

Comments
 (0)