Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
e395532
Config UI: make messengers configurable devices
andig Jan 18, 2026
4a2fa20
wip
andig Jan 18, 2026
6d7c00b
add api
andig Jan 18, 2026
76b83f0
Apply suggestion from @sourcery-ai[bot]
andig Jan 18, 2026
cc99630
chore: simplify device creation
andig Jan 18, 2026
9b8555d
wip
andig Jan 18, 2026
6998251
Merge branch 'chore/create-device' into feat/config-messengers
andig Jan 18, 2026
4cb87de
wip
andig Jan 18, 2026
e8b9bcf
Apply suggestion from @andig
andig Jan 18, 2026
f735397
Apply suggestion from @andig
andig Jan 18, 2026
ab01bf3
wip
andig Jan 18, 2026
beb94b0
Add templates
andig Jan 19, 2026
a4b256c
wip
andig Jan 19, 2026
ec0f142
Merge branch 'master' into feat/config-messengers
andig Jan 19, 2026
15af96b
wip
andig Jan 19, 2026
37178dc
Messaging: add tests (#26803)
Maschga Jan 19, 2026
33596ff
Messaging: add tests (2) (#26823)
Maschga Jan 19, 2026
9f5efb4
wip
andig Jan 21, 2026
30fc702
wip
Maschga Jan 23, 2026
368a310
improve templates
Maschga Jan 24, 2026
519a96d
fix backend
Maschga Jan 24, 2026
70b205b
basic ui, i18n strings
Maschga Jan 24, 2026
69d1878
fix templates
Maschga Jan 25, 2026
763ba4d
wip
Maschga Jan 25, 2026
af797d5
revert ui changes
Maschga Jan 31, 2026
bfe636c
wip
Maschga Jan 31, 2026
5ce4ad0
wip
Maschga Jan 31, 2026
6294cdb
Merge branch 'master' into feat/config-messengers-ui
Maschga Jan 31, 2026
9b2d64b
skiptest
Maschga Jan 31, 2026
2915098
wip: add ui
Maschga Jan 31, 2026
0031d68
wip
Maschga Feb 1, 2026
8747f04
wip
Maschga Feb 1, 2026
4181c51
wip
Maschga Feb 1, 2026
f7bf1a4
wip
Maschga Feb 1, 2026
229f284
wip
Maschga Feb 1, 2026
8d73924
Merge branch 'master' into feat/config-messengers-ui
Maschga Feb 1, 2026
52d8c20
merge
Maschga Feb 1, 2026
005a8ee
lint
Maschga Feb 1, 2026
7c01c64
wip
Maschga Feb 1, 2026
67990e2
tags
Maschga Feb 1, 2026
3febcfd
Merge branch 'master' into feat/config-messengers-ui
naltatis Feb 9, 2026
e6ed128
wip
Maschga Feb 9, 2026
7a78edb
add tests
Maschga Feb 9, 2026
37d512c
Merge branch 'master' into feat/config-messengers-ui
Maschga Feb 9, 2026
700f1ae
remove id()
Maschga Feb 10, 2026
c0199ae
Merge branch 'feat/config-messengers-ui' of https://github.com/Maschg…
Maschga Feb 10, 2026
788fda3
rename to id
Maschga Feb 10, 2026
a812001
move testid; add aria-label
Maschga Feb 10, 2026
5b96c08
remove event count
Maschga Feb 10, 2026
90b9fed
lint
Maschga Feb 10, 2026
cd653d9
fix i18n, use computed
Maschga Feb 10, 2026
59f758a
add role group, aria-label
Maschga Feb 10, 2026
3ae4e9e
rename
Maschga Feb 10, 2026
db47b68
remove event name from i18n
Maschga Feb 10, 2026
ff18d97
Merge branch 'feat/config-messengers-ui' of https://github.com/Maschg…
Maschga Feb 10, 2026
cd137b1
remove aria-label
Maschga Feb 10, 2026
d67a497
reusable collapsible, toggle events
naltatis Feb 10, 2026
e57801c
merge
naltatis Feb 11, 2026
8ae9864
optional yamlSource
naltatis Feb 11, 2026
ef31a65
fix modal size class
naltatis Feb 11, 2026
502533f
empty validation result
naltatis Feb 11, 2026
c087271
ui tweaks; show db:xx in device modal instead of tooltip on openener;…
naltatis Feb 11, 2026
89d0c82
e2e stability; translation
naltatis Feb 11, 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
5 changes: 5 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,8 @@ type Circuit interface {
type Redactor interface {
Redacted() any
}

// Messenger implements message sending
type Messenger interface {
Send(title, msg string)
}
80 changes: 47 additions & 33 deletions api/globalconfig/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,50 @@ import (

// ConfigStatus for publishing config, status and source to UI and external systems
type ConfigStatus struct {
Config any `json:"config,omitempty"`
Status any `json:"status,omitempty"`
FromYaml bool `json:"fromYaml,omitempty"`
Config any `json:"config,omitempty"`
Status any `json:"status,omitempty"`
FromYaml bool `json:"fromYaml,omitempty"`
YamlSource YamlSource `json:"yamlSource,omitempty"`
}

type YamlSource string

const (
YamlSourceFs YamlSource = "fs"
YamlSourceDb YamlSource = "db"
YamlSourceNone YamlSource = ""
)

type All struct {
Network Network
Ocpp ocpp.Config
Log string
SponsorToken string
Plant string // telemetry plant id
Telemetry bool
Mcp bool
Metrics bool
Profile bool
Levels map[string]string
Interval time.Duration
Database DB
Mqtt Mqtt
ModbusProxy []ModbusProxy
Javascript []Javascript
Go []Go
Influx Influx
EEBus eebus.Config
HEMS Hems
SHM shm.Config
Messaging Messaging
Meters []config.Named
Chargers []config.Named
Vehicles []config.Named
Tariffs Tariffs
Site map[string]any
Loadpoints []config.Named
Circuits []config.Named
Network Network
Ocpp ocpp.Config
Log string
SponsorToken string
Plant string // telemetry plant id
Telemetry bool
Mcp bool
Metrics bool
Profile bool
Levels map[string]string
Interval time.Duration
Database DB
Mqtt Mqtt
ModbusProxy []ModbusProxy
Javascript []Javascript
Go []Go
Influx Influx
EEBus eebus.Config
HEMS Hems
SHM shm.Config
Messaging Messaging
MessagingEvents MessagingEvents
Meters []config.Named
Chargers []config.Named
Vehicles []config.Named
Tariffs Tariffs
Site map[string]any
Loadpoints []config.Named
Circuits []config.Named
}

type Javascript struct {
Expand Down Expand Up @@ -138,13 +148,17 @@ type DB struct {
}

type Messaging struct {
Events map[string]MessagingEventTemplate
Events MessagingEvents
Services []config.Typed
}

type MessagingEvents = map[string]MessagingEventTemplate

// MessagingEventTemplate is the push message configuration for an event
type MessagingEventTemplate struct {
Title, Msg string
Title string `json:"title"`
Msg string `json:"msg"`
Disabled bool `json:"disabled"`
}

func (c Messaging) IsConfigured() bool {
Expand Down
23 changes: 23 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -773,3 +773,26 @@ input::-webkit-date-and-time-value {
.tabular {
font-variant-numeric: tabular-nums;
}

/* Collapsible wrapper pattern with choreographed animation */
.collapsible-wrapper {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows var(--evcc-transition-fast) ease 0.2s;
}

.collapsible-wrapper.open {
grid-template-rows: 1fr;
transition: grid-template-rows var(--evcc-transition-fast) ease;
}

.collapsible-content {
overflow: hidden;
opacity: 0;
transition: opacity 0.2s ease;
}

.collapsible-wrapper.open .collapsible-content {
opacity: 1;
transition: opacity var(--evcc-transition-fast) ease 0.2s;
}
34 changes: 2 additions & 32 deletions assets/js/components/ChargingPlans/PlanStrategy.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="strategy-wrapper" :class="{ open: show }">
<div class="strategy-content">
<div class="collapsible-wrapper" :class="{ open: show }">
<div class="collapsible-content pb-3">
<div v-if="disabled" class="row mb-4">
<div class="small text-muted">
<strong class="text-primary">{{ $t("general.note") }}</strong>
Expand Down Expand Up @@ -142,33 +142,3 @@ export default defineComponent({
},
});
</script>

<style scoped>
.strategy-wrapper {
display: grid;
grid-template-rows: 0fr;
margin-bottom: 0;
transition:
grid-template-rows 0.3s ease 0.2s,
margin-bottom 0.3s ease 0.2s;
}

.strategy-wrapper.open {
grid-template-rows: 1fr;
margin-bottom: 1rem;
transition:
grid-template-rows 0.3s ease,
margin-bottom 0.3s ease;
}

.strategy-content {
overflow: hidden;
opacity: 0;
transition: opacity 0.2s ease;
}

.open .strategy-content {
opacity: 1;
transition: opacity 0.3s ease 0.2s;
}
</style>
74 changes: 9 additions & 65 deletions assets/js/components/Config/DeviceCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,12 @@
:title="name"
>{{ title }}</strong
>
<button
ref="tooltip"
type="button"
class="btn btn-sm btn-outline-secondary position-relative border-0 p-2 edit-button"
:class="{ 'opacity-25': !editable, invisible: noEditButton }"
data-bs-toggle="tooltip"
data-bs-html="true"
:title="tooltipTitle"
:aria-label="editable ? $t('config.main.edit') : null"
:disabled="!editable || noEditButton"
@click="edit"
>
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
</button>
<DeviceCardEditIcon
:editable="editable"
:noEditButton="noEditButton"
:badge="badge"
@edit="$emit('edit')"
/>
</div>
<div v-if="$slots.tags">
<hr class="my-3 divide" />
Expand All @@ -42,12 +34,11 @@
</template>

<script>
import "@h2d2/shopicons/es/regular/adjust";
import "@h2d2/shopicons/es/regular/invoice";
import Tooltip from "bootstrap/js/dist/tooltip";
import DeviceCardEditIcon from "./DeviceCardEditIcon.vue";

export default {
name: "DeviceCard",
components: { DeviceCardEditIcon },
props: {
name: String,
title: String,
Expand All @@ -56,49 +47,9 @@ export default {
unconfigured: Boolean,
warning: Boolean,
noEditButton: Boolean,
badge: Boolean,
},
emits: ["edit"],
data() {
return {
tooltip: null,
};
},
computed: {
tooltipTitle() {
if (!this.name) {
return "";
}
let title = `${this.$t("config.main.name")}: <span class='font-monospace'>${this.name}</span>`;
if (!this.editable) {
title += `<div class="mt-1">${this.$t("config.general.fromYamlHint")}</div>`;
}
return `<div class="text-start">${title}</div>`;
},
},
watch: {
tooltipTitle() {
this.initTooltip();
},
},
mounted() {
this.initTooltip();
},
methods: {
edit() {
if (this.editable) {
this.tooltip?.hide();
this.$emit("edit");
}
},
initTooltip() {
this.$nextTick(() => {
this.tooltip?.dispose();
if (this.$refs.tooltip) {
this.tooltip = new Tooltip(this.$refs.tooltip);
}
});
},
},
};
</script>

Expand Down Expand Up @@ -129,15 +80,8 @@ export default {
.icon:empty {
display: none;
}
.edit-button {
/* transparent button, right align icon */
margin-right: -0.5rem;
}
.divide {
margin-left: -1.5rem;
margin-right: -1.5rem;
}
button:disabled {
pointer-events: auto;
}
</style>
84 changes: 84 additions & 0 deletions assets/js/components/Config/DeviceCardEditIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<button
ref="tooltip"
type="button"
class="btn btn-sm btn-outline-secondary position-relative border-0 p-2 edit-button"
:class="{ 'opacity-25': !editable, invisible: noEditButton }"
data-bs-toggle="tooltip"
data-bs-html="true"
:title="tooltipTitle"
:aria-label="editable ? $t('config.main.edit') : null"
:disabled="!editable || noEditButton"
@click="edit"
>
<span
v-if="badge"
class="position-absolute top-0 start-100 translate-middle p-2 rounded-circle bg-warning"
>
<span class="visually-hidden">new</span>
</span>
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
</button>
</template>

<script>
import "@h2d2/shopicons/es/regular/adjust";
import Tooltip from "bootstrap/js/dist/tooltip";

export default {
name: "DeviceCardEditIcon",
props: {
editable: Boolean,
noEditButton: Boolean,
badge: Boolean,
},
emits: ["edit"],
data() {
return {
tooltip: null,
};
},
computed: {
tooltipTitle() {
if (!this.editable) {
return `<div class="text-start mt-1">${this.$t("config.general.fromYamlHint")}</div>`;
}
return "";
},
},
watch: {
tooltipTitle() {
this.initTooltip();
},
},
mounted() {
this.initTooltip();
},
methods: {
edit() {
if (this.editable) {
this.tooltip?.hide();
this.$emit("edit");
}
},
initTooltip() {
this.$nextTick(() => {
this.tooltip?.dispose();
if (this.$refs.tooltip && this.tooltipTitle) {
this.tooltip = new Tooltip(this.$refs.tooltip);
}
});
},
},
};
</script>

<style scoped>
.edit-button {
/* transparent button, right align icon */
margin-right: -0.5rem;
}
button:disabled {
pointer-events: auto;
}
</style>
2 changes: 1 addition & 1 deletion assets/js/components/Config/DeviceModal/Actions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@test="$emit('test')"
/>

<div class="my-4 d-flex justify-content-between">
<div class="mt-4 d-flex justify-content-between">
<button
v-if="isDeletable"
type="button"
Expand Down
Loading
Loading