Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a299f71
feat: configurable loadpoint priority strategy (soc, deficit)
Jun 20, 2026
8b2f970
feat: add priorityHysteresis deadband to loadpoint priority sub-ordering
Jun 20, 2026
c156d76
feat: wire priorityStrategy and priorityHysteresis as runtime loadpoi…
Jun 20, 2026
4d8d964
feat(openapi): add priorityStrategy and priorityHysteresis loadpoint …
Jun 20, 2026
ba2ee38
feat(ui): add priorityStrategy and priorityHysteresis loadpoint controls
Jun 20, 2026
55a0572
feat: include priorityStrategy/priorityHysteresis in loadpoint Dynami…
Jun 20, 2026
2ce7052
feat(ui): expose priorityStrategy/priorityHysteresis in loadpoint con…
Jun 20, 2026
4e9f364
fix(test): gci import order + disambiguate Priority locator
Jun 20, 2026
89e09d7
feat: add priorityBasis (percent/energy) to loadpoint priority strategy
Jun 21, 2026
df69798
feat(ui): expose priorityBasis in both loadpoint modals
Jun 21, 2026
6f17f60
chore: regenerate mock + gofmt for priority basis
Jun 21, 2026
697d443
chore: fix gofmt comment alignment on capacity-unknown row
Jun 21, 2026
88a8662
fix: rank a priority tier on one basis to avoid mixing kWh and percent
Jun 21, 2026
4617906
refactor(loadpoint): address review — TextUnmarshaler guard, drop Str…
Jun 21, 2026
16abf9f
refactor(loadpoint): enumer-based priority enums, rename static->none
Jun 21, 2026
d7c0f4f
fix(ui): default new loadpoint priority strategy/basis to none/percent
Jun 21, 2026
216274c
chore(mcp): regenerate openapi.json for none enum (porcelain check)
Jun 21, 2026
a32c09c
Merge branch 'master' into feat/loadpoint-priority-strategy
Alexxtheonly Jun 21, 2026
4fe8836
feat: publish effectivePriorityScore via API
Jun 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions api/prioritybasis_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions api/prioritystrategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package api

// PriorityStrategy determines how a loadpoint is ranked against other loadpoints
// of the same priority when distributing surplus power.
type PriorityStrategy int

//go:generate go tool enumer -type PriorityStrategy -trimprefix Priority -transform=lower -text
const (
PriorityNone PriorityStrategy = iota // no sub-ordering (default)
PrioritySoc // prefer the lower vehicle soc
PriorityDeficit // prefer the larger gap to limit soc
)

// PriorityBasis determines whether a priority strategy ranks loadpoints by soc
// percentage or by absolute energy (kWh).
type PriorityBasis int

//go:generate go tool enumer -type PriorityBasis -trimprefix PriorityBasis -transform=lower -text
const (
PriorityBasisPercent PriorityBasis = iota // rank by soc-% (default)
PriorityBasisEnergy // rank by absolute energy (kWh)
)
94 changes: 94 additions & 0 deletions api/prioritystrategy_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 108 additions & 0 deletions assets/js/components/Config/LoadpointModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,57 @@
/>
</FormRow>

<FormRow
v-if="showPriority"
id="loadpointParamPriorityStrategy"
:label="$t('config.loadpoint.priorityStrategyLabel')"
:help="$t('config.loadpoint.priorityStrategyHelp')"
>
<PropertyField
id="loadpointParamPriorityStrategy"
v-model="priorityStrategy"
type="Choice"
size="w-100"
class="me-2"
required
:choice="priorityStrategyOptions"
/>
</FormRow>

<FormRow
v-if="showPriority && priorityHysteresisAvailable"
id="loadpointParamPriorityBasis"
:label="$t('config.loadpoint.priorityBasisLabel')"
:help="$t('config.loadpoint.priorityBasisHelp')"
>
<PropertyField
id="loadpointParamPriorityBasis"
v-model="priorityBasis"
type="Choice"
size="w-100"
class="me-2"
required
:choice="priorityBasisOptions"
/>
</FormRow>

<FormRow
v-if="showPriority && priorityHysteresisAvailable"
id="loadpointParamPriorityHysteresis"
:label="$t('config.loadpoint.priorityHysteresisLabel')"
:help="$t('config.loadpoint.priorityHysteresisHelp')"
>
<PropertyField
id="loadpointParamPriorityHysteresis"
v-model="values.priorityHysteresis"
type="Float"
:unit="priorityHysteresisUnit"
size="w-25 w-min-200"
class="me-2"
required
/>
</FormRow>

<h6 v-if="!chargerIsSwitchDevice">
{{ $t("config.loadpoint.electricalTitle") }}
<small class="text-muted">{{
Expand Down Expand Up @@ -634,6 +685,8 @@ import { getModal, openModal, replaceModal, closeModal } from "@/configModal";
import {
CHARGE_MODE,
LOADPOINT_TYPE,
PRIORITY_STRATEGY,
PRIORITY_BASIS,
type DeviceType,
type LoadpointType,
type ConfigCharger,
Expand All @@ -654,6 +707,9 @@ const defaultValues = {
minCurrent: 6,
maxCurrent: 16,
priority: 0,
priorityStrategy: PRIORITY_STRATEGY.NONE,
priorityBasis: PRIORITY_BASIS.PERCENT,
priorityHysteresis: 0,
defaultMode: "",
thresholds: {
enable: { delay: 1 * nsPerMin, threshold: 0 },
Expand Down Expand Up @@ -795,6 +851,58 @@ export default {
result[10]!.name = "10 (highest)";
return result;
},
priorityStrategy: {
// fall back to the none (default) strategy
get(): PRIORITY_STRATEGY {
return this.values.priorityStrategy || PRIORITY_STRATEGY.NONE;
},
set(value: PRIORITY_STRATEGY) {
this.values.priorityStrategy = value;
},
},
priorityStrategyOptions(): { key: PRIORITY_STRATEGY; name: string }[] {
return [
{
key: PRIORITY_STRATEGY.NONE,
name: this.$t("config.loadpoint.priorityStrategyNone"),
},
{
key: PRIORITY_STRATEGY.SOC,
name: this.$t("config.loadpoint.priorityStrategySoc"),
},
{
key: PRIORITY_STRATEGY.DEFICIT,
name: this.$t("config.loadpoint.priorityStrategyDeficit"),
},
];
},
priorityBasis: {
// backend returns "" for the percent basis; map to/from the explicit "percent" choice
get(): PRIORITY_BASIS {
return this.values.priorityBasis || PRIORITY_BASIS.PERCENT;
},
set(value: PRIORITY_BASIS) {
this.values.priorityBasis = value;
},
},
priorityBasisOptions(): { key: PRIORITY_BASIS; name: string }[] {
return [
{
key: PRIORITY_BASIS.PERCENT,
name: this.$t("config.loadpoint.priorityBasisPercent"),
},
{
key: PRIORITY_BASIS.ENERGY,
name: this.$t("config.loadpoint.priorityBasisEnergy"),
},
];
},
priorityHysteresisAvailable(): boolean {
return this.priorityStrategy !== PRIORITY_STRATEGY.NONE;
},
priorityHysteresisUnit(): string {
return this.priorityBasis === PRIORITY_BASIS.ENERGY ? "kWh" : "%";
},
phasesOptions() {
return [
{ value: 1, name: this.$t("config.loadpoint.phases1p") },
Expand Down
Loading
Loading