EEBUS: configure by default (#26944)
This commit is contained in:
parent
ca7bbba0a4
commit
768656e0c3
52 changed files with 712 additions and 324 deletions
|
|
@ -18,6 +18,13 @@ import (
|
||||||
"github.com/evcc-io/evcc/util/modbus"
|
"github.com/evcc-io/evcc/util/modbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
type All struct {
|
type All struct {
|
||||||
Network Network
|
Network Network
|
||||||
Ocpp ocpp.Config
|
Ocpp ocpp.Config
|
||||||
|
|
@ -79,13 +86,6 @@ func (c Hems) Redacted() any {
|
||||||
|
|
||||||
var _ api.Redactor = (*Mqtt)(nil)
|
var _ api.Redactor = (*Mqtt)(nil)
|
||||||
|
|
||||||
func masked(s any) string {
|
|
||||||
if s != "" {
|
|
||||||
return "***"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mqtt struct {
|
type Mqtt struct {
|
||||||
mqtt.Config `mapstructure:",squash"`
|
mqtt.Config `mapstructure:",squash"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
|
|
@ -97,12 +97,12 @@ func (m Mqtt) Redacted() any {
|
||||||
Config: mqtt.Config{
|
Config: mqtt.Config{
|
||||||
Broker: m.Broker,
|
Broker: m.Broker,
|
||||||
User: m.User,
|
User: m.User,
|
||||||
Password: masked(m.Password),
|
Password: util.Masked(m.Password),
|
||||||
ClientID: m.ClientID,
|
ClientID: m.ClientID,
|
||||||
Insecure: m.Insecure,
|
Insecure: m.Insecure,
|
||||||
CaCert: masked(m.CaCert),
|
CaCert: util.Masked(m.CaCert),
|
||||||
ClientCert: masked(m.ClientCert),
|
ClientCert: util.Masked(m.ClientCert),
|
||||||
ClientKey: masked(m.ClientKey),
|
ClientKey: util.Masked(m.ClientKey),
|
||||||
},
|
},
|
||||||
Topic: m.Topic,
|
Topic: m.Topic,
|
||||||
}
|
}
|
||||||
|
|
@ -124,10 +124,10 @@ func (c Influx) Redacted() any {
|
||||||
return Influx{
|
return Influx{
|
||||||
URL: c.URL,
|
URL: c.URL,
|
||||||
Database: c.Database,
|
Database: c.Database,
|
||||||
Token: masked(c.Token),
|
Token: util.Masked(c.Token),
|
||||||
Org: c.Org,
|
Org: c.Org,
|
||||||
User: c.User,
|
User: c.User,
|
||||||
Password: masked(c.Password),
|
Password: util.Masked(c.Password),
|
||||||
Insecure: c.Insecure,
|
Insecure: c.Insecure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +147,7 @@ type MessagingEventTemplate struct {
|
||||||
Title, Msg string
|
Title, Msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Messaging) Configured() bool {
|
func (c Messaging) IsConfigured() bool {
|
||||||
return len(c.Services) > 0 || len(c.Events) > 0
|
return len(c.Services) > 0 || len(c.Events) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
'round-box--error': error,
|
'round-box--error': error,
|
||||||
'round-box--warning': warning,
|
'round-box--warning': warning,
|
||||||
'root--unconfigured': unconfigured,
|
'root--unconfigured': unconfigured,
|
||||||
|
'root--with-tags': $slots.tags,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="d-flex align-items-center mb-2">
|
<div class="d-flex align-items-center" :class="{ 'mb-2': $slots.tags }">
|
||||||
<div class="icon me-2">
|
<div class="icon me-2">
|
||||||
<slot name="icon" />
|
<slot name="icon" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -33,8 +34,10 @@
|
||||||
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
|
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-3 divide" />
|
<div v-if="$slots.tags">
|
||||||
<slot name="tags" />
|
<hr class="my-3 divide" />
|
||||||
|
<slot name="tags" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -67,7 +70,7 @@ export default {
|
||||||
}
|
}
|
||||||
let title = `${this.$t("config.main.name")}: <span class='font-monospace'>${this.name}</span>`;
|
let title = `${this.$t("config.main.name")}: <span class='font-monospace'>${this.name}</span>`;
|
||||||
if (!this.editable) {
|
if (!this.editable) {
|
||||||
title += `<div class="mt-1">${this.$t("config.main.yaml")}</div>`;
|
title += `<div class="mt-1">${this.$t("config.general.fromYamlHint")}</div>`;
|
||||||
}
|
}
|
||||||
return `<div class="text-start">${title}</div>`;
|
return `<div class="text-start">${title}</div>`;
|
||||||
},
|
},
|
||||||
|
|
@ -105,6 +108,8 @@ export default {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
.root--with-tags {
|
||||||
min-height: 8rem;
|
min-height: 8rem;
|
||||||
}
|
}
|
||||||
.root--unconfigured {
|
.root--unconfigured {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,153 @@
|
||||||
<template>
|
<template>
|
||||||
<YamlModal
|
<JsonModal
|
||||||
id="eebusModal"
|
id="eebusModal"
|
||||||
:title="$t('config.eebus.title')"
|
:title="$t('config.eebus.title')"
|
||||||
:description="$t('config.eebus.description')"
|
:description="$t('config.eebus.description')"
|
||||||
docs="/docs/reference/configuration/eebus"
|
docs="/docs/reference/configuration/eebus"
|
||||||
:defaultYaml="defaultYaml"
|
|
||||||
removeKey="eebus"
|
|
||||||
endpoint="/config/eebus"
|
endpoint="/config/eebus"
|
||||||
|
state-key="eebus.config"
|
||||||
|
data-testid="eebus-modal"
|
||||||
|
:no-buttons="fromYaml"
|
||||||
|
:confirm-remove="$t('config.eebus.removeConfirm')"
|
||||||
@changed="$emit('changed')"
|
@changed="$emit('changed')"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ values }: { values: EebusConfig }">
|
||||||
|
<p v-if="fromYaml" class="text-muted">
|
||||||
|
{{ $t("config.general.fromYamlHint") }}
|
||||||
|
</p>
|
||||||
|
<FormRow
|
||||||
|
v-if="values.shipid"
|
||||||
|
:id="formId('shipid-display')"
|
||||||
|
:label="$t('config.eebus.shipid')"
|
||||||
|
:help="$t('config.eebus.shipidExplain')"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
:id="formId('shipid-display')"
|
||||||
|
:value="values.shipid"
|
||||||
|
readonly
|
||||||
|
class="form-control text-muted"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow
|
||||||
|
v-if="status.ski"
|
||||||
|
:id="formId('ski-display')"
|
||||||
|
:label="$t('config.eebus.ski')"
|
||||||
|
:help="$t('config.eebus.skiExplain')"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
:id="formId('ski-display')"
|
||||||
|
:value="status.ski"
|
||||||
|
readonly
|
||||||
|
class="form-control text-muted"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<PropertyCollapsible v-if="!fromYaml">
|
||||||
|
<template #advanced>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ $t("config.eebus.descriptionAdvanced") }}
|
||||||
|
</div>
|
||||||
|
<FormRow
|
||||||
|
:id="formId('shipid')"
|
||||||
|
:label="$t('config.eebus.shipid')"
|
||||||
|
:help="$t('config.eebus.shipidHelp')"
|
||||||
|
>
|
||||||
|
<PropertyField
|
||||||
|
:id="formId('shipid')"
|
||||||
|
v-model="values.shipid"
|
||||||
|
type="String"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow
|
||||||
|
:id="formId('port')"
|
||||||
|
:label="$t('config.eebus.port')"
|
||||||
|
:help="$t('config.eebus.portHelp')"
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<PropertyField
|
||||||
|
:id="formId('port')"
|
||||||
|
v-model="values.port"
|
||||||
|
property="port"
|
||||||
|
type="Int"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow
|
||||||
|
:id="formId('interfaces')"
|
||||||
|
:label="$t('config.eebus.interfaces')"
|
||||||
|
:help="$t('config.eebus.interfacesHelp')"
|
||||||
|
optional
|
||||||
|
example="eth0"
|
||||||
|
>
|
||||||
|
<PropertyField
|
||||||
|
:id="formId('interfaces')"
|
||||||
|
v-model="values.interfaces"
|
||||||
|
type="List"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<h6>{{ $t("config.eebus.certificate.title") }}</h6>
|
||||||
|
<FormRow
|
||||||
|
:id="formId('certificate-public')"
|
||||||
|
:label="$t('config.eebus.certificate.public')"
|
||||||
|
>
|
||||||
|
<PropertyCertField
|
||||||
|
:id="formId('certificate-public')"
|
||||||
|
:model-value="values.certificate?.public"
|
||||||
|
required
|
||||||
|
@update:model-value="
|
||||||
|
values.certificate ? (values.certificate.public = $event) : ''
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow
|
||||||
|
:id="formId('certificate-private')"
|
||||||
|
:label="$t('config.eebus.certificate.private')"
|
||||||
|
>
|
||||||
|
<PropertyCertField
|
||||||
|
:id="formId('certificate-private')"
|
||||||
|
:model-value="values.certificate?.private"
|
||||||
|
required
|
||||||
|
@update:model-value="
|
||||||
|
values.certificate ? (values.certificate.private = $event) : ''
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
</template>
|
||||||
|
</PropertyCollapsible>
|
||||||
|
</template>
|
||||||
|
</JsonModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import YamlModal from "./YamlModal.vue";
|
import type { PropType } from "vue";
|
||||||
import defaultYaml from "./defaultYaml/eebus.yaml?raw";
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import type { EebusConfig, EebusStatus } from "@/types/evcc";
|
||||||
|
import JsonModal from "./JsonModal.vue";
|
||||||
|
import FormRow from "./FormRow.vue";
|
||||||
|
import PropertyField from "./PropertyField.vue";
|
||||||
|
import PropertyCertField from "./PropertyCertField.vue";
|
||||||
|
import PropertyCollapsible from "./PropertyCollapsible.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EebusModal",
|
name: "EebusModal",
|
||||||
components: { YamlModal },
|
components: {
|
||||||
|
JsonModal,
|
||||||
|
FormRow,
|
||||||
|
PropertyField,
|
||||||
|
PropertyCertField,
|
||||||
|
PropertyCollapsible,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
status: {
|
||||||
|
type: Object as PropType<EebusStatus>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
fromYaml: Boolean,
|
||||||
|
},
|
||||||
emits: ["changed"],
|
emits: ["changed"],
|
||||||
data() {
|
methods: {
|
||||||
return { defaultYaml: defaultYaml.trim() };
|
formId(s: string) {
|
||||||
|
return `eebus-${s}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="fromYaml" class="text-muted">
|
<p v-if="fromYaml" class="text-muted">
|
||||||
{{ $t("config.hems.yamlConfigured") }}
|
{{ $t("config.general.fromYamlHint") }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</YamlModal>
|
</YamlModal>
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export default {
|
||||||
saveMethod: { type: String, default: "post" },
|
saveMethod: { type: String, default: "post" },
|
||||||
storeValuesInArray: Boolean,
|
storeValuesInArray: Boolean,
|
||||||
size: { type: String },
|
size: { type: String },
|
||||||
|
confirmRemove: String,
|
||||||
},
|
},
|
||||||
emits: ["changed", "open"],
|
emits: ["changed", "open"],
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -121,7 +122,13 @@ export default {
|
||||||
await this.load();
|
await this.load();
|
||||||
},
|
},
|
||||||
async load() {
|
async load() {
|
||||||
this.serverValues = this.stateKey ? store.state[this.stateKey] : store.state;
|
if (this.stateKey) {
|
||||||
|
// Support nested keys like "eebus.config"
|
||||||
|
const keys = this.stateKey.split(".");
|
||||||
|
this.serverValues = keys.reduce((obj, key) => obj?.[key], store.state);
|
||||||
|
} else {
|
||||||
|
this.serverValues = store.state;
|
||||||
|
}
|
||||||
if (this.transformReadValues) {
|
if (this.transformReadValues) {
|
||||||
this.serverValues = this.transformReadValues(this.serverValues);
|
this.serverValues = this.transformReadValues(this.serverValues);
|
||||||
}
|
}
|
||||||
|
|
@ -155,6 +162,9 @@ export default {
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
},
|
},
|
||||||
async remove() {
|
async remove() {
|
||||||
|
if (this.confirmRemove && !window.confirm(this.confirmRemove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.removing = true;
|
this.removing = true;
|
||||||
this.error = "";
|
this.error = "";
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<textarea :id="id" v-model="value" class="form-control" rows="3" />
|
<textarea :id="id" v-model="value" class="form-control" rows="3" :required="required" />
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -26,6 +26,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
id: String,
|
id: String,
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
|
required: Boolean,
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue"],
|
emits: ["update:modelValue"],
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,9 @@ export default {
|
||||||
return this.property === "icon";
|
return this.property === "icon";
|
||||||
},
|
},
|
||||||
textarea() {
|
textarea() {
|
||||||
return ["accessToken", "refreshToken", "identifiers"].includes(this.property);
|
return (
|
||||||
|
this.array || ["accessToken", "refreshToken", "identifiers"].includes(this.property)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
boolean() {
|
boolean() {
|
||||||
return this.type === "Bool";
|
return this.type === "Bool";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
#shipid: EVCC-1234567890abcdef
|
|
||||||
#interfaces:
|
|
||||||
# - eth0
|
|
||||||
#certificate:
|
|
||||||
# public: |
|
|
||||||
# -----BEGIN CERTIFICATE-----
|
|
||||||
# 1234567890abcdef==
|
|
||||||
# -----END CERTIFICATE-----
|
|
||||||
# private: |
|
|
||||||
# -----BEGIN EC PRIVATE KEY-----
|
|
||||||
# 1234567890abcdef
|
|
||||||
# -----END EC PRIVATE KEY-----
|
|
||||||
|
|
@ -84,7 +84,7 @@ export interface State {
|
||||||
hems?: Hems;
|
hems?: Hems;
|
||||||
shm?: ShmConfig;
|
shm?: ShmConfig;
|
||||||
sponsor?: Sponsor;
|
sponsor?: Sponsor;
|
||||||
eebus?: any;
|
eebus?: Eebus;
|
||||||
modbusproxy?: ModbusProxy[];
|
modbusproxy?: ModbusProxy[];
|
||||||
messaging?: any;
|
messaging?: any;
|
||||||
interval?: number;
|
interval?: number;
|
||||||
|
|
@ -440,6 +440,29 @@ export enum MODBUS_PROTOCOL {
|
||||||
RTU = "rtu",
|
RTU = "rtu",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Certificate = {
|
||||||
|
public: string;
|
||||||
|
private: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Eebus = {
|
||||||
|
config: EebusConfig;
|
||||||
|
status: EebusStatus;
|
||||||
|
fromYaml?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EebusConfig = {
|
||||||
|
uri: string;
|
||||||
|
port: number;
|
||||||
|
shipid: string;
|
||||||
|
interfaces?: string[];
|
||||||
|
certificate?: Certificate;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EebusStatus = {
|
||||||
|
ski: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ModbusProxy = {
|
export type ModbusProxy = {
|
||||||
port: number;
|
port: number;
|
||||||
readonly: MODBUS_PROXY_READONLY;
|
readonly: MODBUS_PROXY_READONLY;
|
||||||
|
|
|
||||||
|
|
@ -210,33 +210,6 @@
|
||||||
<DeviceTags :tags="influxTags" />
|
<DeviceTags :tags="influxTags" />
|
||||||
</template>
|
</template>
|
||||||
</DeviceCard>
|
</DeviceCard>
|
||||||
<DeviceCard
|
|
||||||
:title="$t('config.eebus.title')"
|
|
||||||
editable
|
|
||||||
:error="hasClassError('eebus')"
|
|
||||||
:unconfigured="isUnconfigured(eebusTags)"
|
|
||||||
data-testid="eebus"
|
|
||||||
@edit="openModal('eebusModal')"
|
|
||||||
>
|
|
||||||
<template #icon><EebusIcon /></template>
|
|
||||||
<template #tags>
|
|
||||||
<DeviceTags :tags="eebusTags" />
|
|
||||||
</template>
|
|
||||||
</DeviceCard>
|
|
||||||
<DeviceCard
|
|
||||||
:title="$t('config.ocpp.title')"
|
|
||||||
editable
|
|
||||||
:error="hasClassError('ocpp')"
|
|
||||||
:unconfigured="isUnconfigured(ocppTags)"
|
|
||||||
data-testid="ocpp"
|
|
||||||
@edit="openModal('ocppModal')"
|
|
||||||
>
|
|
||||||
<template #icon><OcppIcon /></template>
|
|
||||||
<template #tags>
|
|
||||||
<DeviceTags :tags="ocppTags" />
|
|
||||||
</template>
|
|
||||||
</DeviceCard>
|
|
||||||
|
|
||||||
<DeviceCard
|
<DeviceCard
|
||||||
:title="`${$t('config.circuits.title')}`"
|
:title="`${$t('config.circuits.title')}`"
|
||||||
editable
|
editable
|
||||||
|
|
@ -278,19 +251,6 @@
|
||||||
<DeviceTags :tags="modbusproxyTags" />
|
<DeviceTags :tags="modbusproxyTags" />
|
||||||
</template>
|
</template>
|
||||||
</DeviceCard>
|
</DeviceCard>
|
||||||
<DeviceCard
|
|
||||||
:title="$t('config.shm.cardTitle')"
|
|
||||||
editable
|
|
||||||
:error="hasClassError('shm')"
|
|
||||||
:unconfigured="isUnconfigured(shmTags)"
|
|
||||||
data-testid="shm"
|
|
||||||
@edit="openModal('shmModal')"
|
|
||||||
>
|
|
||||||
<template #icon><ShmIcon /></template>
|
|
||||||
<template #tags>
|
|
||||||
<DeviceTags :tags="shmTags" />
|
|
||||||
</template>
|
|
||||||
</DeviceCard>
|
|
||||||
<DeviceCard
|
<DeviceCard
|
||||||
:title="$t('config.hems.title')"
|
:title="$t('config.hems.title')"
|
||||||
editable
|
editable
|
||||||
|
|
@ -306,6 +266,37 @@
|
||||||
</DeviceCard>
|
</DeviceCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 class="my-4 mt-5">{{ $t("config.section.services") }}</h2>
|
||||||
|
<div class="p-0 config-list">
|
||||||
|
<DeviceCard
|
||||||
|
:title="$t('config.ocpp.title')"
|
||||||
|
editable
|
||||||
|
:error="hasClassError('ocpp')"
|
||||||
|
data-testid="ocpp"
|
||||||
|
@edit="openModal('ocppModal')"
|
||||||
|
>
|
||||||
|
<template #icon><OcppIcon /></template>
|
||||||
|
</DeviceCard>
|
||||||
|
<DeviceCard
|
||||||
|
:title="$t('config.shm.cardTitle')"
|
||||||
|
editable
|
||||||
|
:error="hasClassError('shm')"
|
||||||
|
data-testid="shm"
|
||||||
|
@edit="openModal('shmModal')"
|
||||||
|
>
|
||||||
|
<template #icon><ShmIcon /></template>
|
||||||
|
</DeviceCard>
|
||||||
|
<DeviceCard
|
||||||
|
:title="$t('config.eebus.title')"
|
||||||
|
editable
|
||||||
|
:error="hasClassError('eebus')"
|
||||||
|
data-testid="eebus"
|
||||||
|
@edit="openModal('eebusModal')"
|
||||||
|
>
|
||||||
|
<template #icon><EebusIcon /></template>
|
||||||
|
</DeviceCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr class="my-5" />
|
<hr class="my-5" />
|
||||||
|
|
||||||
<h2 class="my-4 mt-5">{{ $t("config.section.system") }}</h2>
|
<h2 class="my-4 mt-5">{{ $t("config.section.system") }}</h2>
|
||||||
|
|
@ -387,7 +378,11 @@
|
||||||
:extMeters="extMeters"
|
:extMeters="extMeters"
|
||||||
@changed="yamlChanged"
|
@changed="yamlChanged"
|
||||||
/>
|
/>
|
||||||
<EebusModal @changed="yamlChanged" />
|
<EebusModal
|
||||||
|
:status="eebus?.status"
|
||||||
|
:from-yaml="eebus?.fromYaml"
|
||||||
|
@changed="yamlChanged"
|
||||||
|
/>
|
||||||
<OcppModal :ocpp="ocpp" />
|
<OcppModal :ocpp="ocpp" />
|
||||||
<BackupRestoreModal v-bind="backupRestoreProps" />
|
<BackupRestoreModal v-bind="backupRestoreProps" />
|
||||||
<PasswordModal update-mode />
|
<PasswordModal update-mode />
|
||||||
|
|
@ -656,12 +651,6 @@ export default defineComponent({
|
||||||
vehicleOptions(): VehicleOption[] {
|
vehicleOptions(): VehicleOption[] {
|
||||||
return this.vehicles.map((v) => ({ key: v.name, name: v.config?.title || v.name }));
|
return this.vehicles.map((v) => ({ key: v.name, name: v.config?.title || v.name }));
|
||||||
},
|
},
|
||||||
shmTags(): DeviceTags {
|
|
||||||
const { vendorId, deviceId } = store.state?.shm || {};
|
|
||||||
// TODO: use incoming SEMP connections to determin configured/active status
|
|
||||||
const value = !!vendorId || !!deviceId;
|
|
||||||
return { configured: { value } };
|
|
||||||
},
|
|
||||||
hems() {
|
hems() {
|
||||||
return store.state?.hems;
|
return store.state?.hems;
|
||||||
},
|
},
|
||||||
|
|
@ -698,30 +687,8 @@ export default defineComponent({
|
||||||
// @ts-expect-error: telemetry property exists but not in TypeScript definitions
|
// @ts-expect-error: telemetry property exists but not in TypeScript definitions
|
||||||
return store.state?.telemetry === true;
|
return store.state?.telemetry === true;
|
||||||
},
|
},
|
||||||
eebusTags(): DeviceTags {
|
eebus() {
|
||||||
return { configured: { value: store.state?.eebus || false } };
|
return store.state?.eebus;
|
||||||
},
|
|
||||||
ocppTags(): DeviceTags {
|
|
||||||
const ocpp = store.state?.ocpp;
|
|
||||||
const stations = ocpp?.status?.stations || [];
|
|
||||||
if (stations.length === 0) {
|
|
||||||
return { configured: { value: false } };
|
|
||||||
}
|
|
||||||
|
|
||||||
const connected = stations.filter((s) => s.status === "connected").length;
|
|
||||||
const configured = stations.filter((s) => s.status === "configured").length;
|
|
||||||
const detected = stations.filter((s) => s.status === "unknown").length;
|
|
||||||
const total = connected + configured;
|
|
||||||
|
|
||||||
const tags: Record<string, any> = {
|
|
||||||
connections: { value: `${connected}/${total}` },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (detected > 0) {
|
|
||||||
tags["detected"] = { value: detected };
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags;
|
|
||||||
},
|
},
|
||||||
modbusproxyTags(): DeviceTags {
|
modbusproxyTags(): DeviceTags {
|
||||||
const config = store.state?.modbusproxy || [];
|
const config = store.state?.modbusproxy || [];
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/evcc-io/evcc/cmd/shutdown"
|
"github.com/evcc-io/evcc/cmd/shutdown"
|
||||||
|
"github.com/evcc-io/evcc/server/db/settings"
|
||||||
"github.com/evcc-io/evcc/util"
|
"github.com/evcc-io/evcc/util"
|
||||||
"github.com/evcc-io/evcc/util/config"
|
"github.com/evcc-io/evcc/util/config"
|
||||||
"go.yaml.in/yaml/v4"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
@ -120,3 +121,18 @@ func deviceHeader[T any](dev config.Device[T]) string {
|
||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// migrateYamlToJson converts a settings value from yaml to json if needed
|
||||||
|
func migrateYamlToJson[T any](key string, res *T) error {
|
||||||
|
if settings.IsJson(key) {
|
||||||
|
return settings.Json(key, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.Yaml(key, new(T), res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.SetJson(key, res)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
34
cmd/helper_test.go
Normal file
34
cmd/helper_test.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/server/db"
|
||||||
|
"github.com/evcc-io/evcc/server/db/settings"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMigrateYaml(t *testing.T) {
|
||||||
|
require.NoError(t, db.NewInstance("sqlite", ":memory:"))
|
||||||
|
|
||||||
|
const key = "foo"
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
Key string
|
||||||
|
Val []string
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := T{
|
||||||
|
Key: "foo",
|
||||||
|
Val: []string{"a", "b"},
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, settings.SetYaml(key, expect))
|
||||||
|
require.False(t, settings.IsJson(key))
|
||||||
|
|
||||||
|
var res T
|
||||||
|
require.NoError(t, migrateYamlToJson(key, &res))
|
||||||
|
require.True(t, settings.IsJson(key))
|
||||||
|
|
||||||
|
require.Equal(t, expect, res)
|
||||||
|
}
|
||||||
34
cmd/root.go
34
cmd/root.go
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/evcc-io/evcc/push"
|
"github.com/evcc-io/evcc/push"
|
||||||
"github.com/evcc-io/evcc/server"
|
"github.com/evcc-io/evcc/server"
|
||||||
"github.com/evcc-io/evcc/server/db"
|
"github.com/evcc-io/evcc/server/db"
|
||||||
|
"github.com/evcc-io/evcc/server/eebus"
|
||||||
"github.com/evcc-io/evcc/server/mcp"
|
"github.com/evcc-io/evcc/server/mcp"
|
||||||
"github.com/evcc-io/evcc/server/network"
|
"github.com/evcc-io/evcc/server/network"
|
||||||
"github.com/evcc-io/evcc/server/updater"
|
"github.com/evcc-io/evcc/server/updater"
|
||||||
|
|
@ -176,10 +177,7 @@ func runRoot(cmd *cobra.Command, args []string) {
|
||||||
ocppCS := ocpp.Instance()
|
ocppCS := ocpp.Instance()
|
||||||
ocppCS.SetUpdated(func() {
|
ocppCS.SetUpdated(func() {
|
||||||
// republish when OCPP state updates
|
// republish when OCPP state updates
|
||||||
valueChan <- util.Param{Key: keys.Ocpp, Val: struct {
|
valueChan <- util.Param{Key: keys.Ocpp, Val: globalconfig.ConfigStatus{
|
||||||
Config ocpp.Config `json:"config"`
|
|
||||||
Status ocpp.Status `json:"status"`
|
|
||||||
}{
|
|
||||||
Config: conf.Ocpp,
|
Config: conf.Ocpp,
|
||||||
Status: ocpp.GetStatus(),
|
Status: ocpp.GetStatus(),
|
||||||
}}
|
}}
|
||||||
|
|
@ -329,35 +327,29 @@ func runRoot(cmd *cobra.Command, args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish initial settings
|
// publish initial settings
|
||||||
valueChan <- util.Param{Key: keys.EEBus, Val: conf.EEBus.Configured()}
|
valueChan <- util.Param{Key: keys.EEBus, Val: globalconfig.ConfigStatus{
|
||||||
|
Config: conf.EEBus.Redacted(),
|
||||||
|
Status: eebus.GetStatus(),
|
||||||
|
FromYaml: fromYaml.eebus,
|
||||||
|
}}
|
||||||
valueChan <- util.Param{Key: keys.Shm, Val: conf.SHM}
|
valueChan <- util.Param{Key: keys.Shm, Val: conf.SHM}
|
||||||
valueChan <- util.Param{Key: keys.Influx, Val: conf.Influx}
|
valueChan <- util.Param{Key: keys.Influx, Val: conf.Influx}
|
||||||
valueChan <- util.Param{Key: keys.Interval, Val: conf.Interval}
|
valueChan <- util.Param{Key: keys.Interval, Val: conf.Interval}
|
||||||
valueChan <- util.Param{Key: keys.Messaging, Val: conf.Messaging.Configured()}
|
valueChan <- util.Param{Key: keys.Messaging, Val: conf.Messaging.IsConfigured()}
|
||||||
valueChan <- util.Param{Key: keys.ModbusProxy, Val: conf.ModbusProxy}
|
valueChan <- util.Param{Key: keys.ModbusProxy, Val: conf.ModbusProxy}
|
||||||
valueChan <- util.Param{Key: keys.Mqtt, Val: conf.Mqtt}
|
valueChan <- util.Param{Key: keys.Mqtt, Val: conf.Mqtt}
|
||||||
valueChan <- util.Param{Key: keys.Network, Val: conf.Network}
|
valueChan <- util.Param{Key: keys.Network, Val: conf.Network}
|
||||||
valueChan <- util.Param{Key: keys.Ocpp, Val: struct {
|
valueChan <- util.Param{Key: keys.Ocpp, Val: globalconfig.ConfigStatus{
|
||||||
Config ocpp.Config `json:"config"`
|
|
||||||
Status ocpp.Status `json:"status"`
|
|
||||||
}{
|
|
||||||
Config: conf.Ocpp,
|
Config: conf.Ocpp,
|
||||||
Status: ocpp.GetStatus(),
|
Status: ocpp.GetStatus(),
|
||||||
}}
|
}}
|
||||||
valueChan <- util.Param{Key: keys.Sponsor, Val: struct {
|
valueChan <- util.Param{Key: keys.Sponsor, Val: globalconfig.ConfigStatus{
|
||||||
Status sponsor.Status `json:"status"`
|
Status: sponsor.RedactedStatus(),
|
||||||
FromYaml bool `json:"fromYaml"`
|
|
||||||
}{
|
|
||||||
Status: sponsor.GetStatus(),
|
|
||||||
FromYaml: fromYaml.sponsor,
|
FromYaml: fromYaml.sponsor,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
valueChan <- util.Param{Key: keys.Hems, Val: struct {
|
valueChan <- util.Param{Key: keys.Hems, Val: globalconfig.ConfigStatus{
|
||||||
Config globalconfig.Hems `json:"config"`
|
Config: conf.HEMS.Redacted(),
|
||||||
Status *hemsapi.Status `json:"status,omitempty"`
|
|
||||||
FromYaml bool `json:"fromYaml"`
|
|
||||||
}{
|
|
||||||
Config: conf.HEMS,
|
|
||||||
Status: hemsapi.GetStatus(hems),
|
Status: hemsapi.GetStatus(hems),
|
||||||
FromYaml: fromYaml.hems,
|
FromYaml: fromYaml.hems,
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
46
cmd/setup.go
46
cmd/setup.go
|
|
@ -73,7 +73,7 @@ var conf = globalconfig.All{
|
||||||
Topic: "evcc",
|
Topic: "evcc",
|
||||||
},
|
},
|
||||||
EEBus: eebus.Config{
|
EEBus: eebus.Config{
|
||||||
URI: ":4712",
|
Port: 4712,
|
||||||
},
|
},
|
||||||
Database: globalconfig.DB{
|
Database: globalconfig.DB{
|
||||||
Type: "sqlite",
|
Type: "sqlite",
|
||||||
|
|
@ -84,6 +84,7 @@ var conf = globalconfig.All{
|
||||||
var fromYaml struct {
|
var fromYaml struct {
|
||||||
sponsor bool
|
sponsor bool
|
||||||
hems bool
|
hems bool
|
||||||
|
eebus bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameRE = regexp.MustCompile(`^[a-zA-Z0-9_.:-]+$`)
|
var nameRE = regexp.MustCompile(`^[a-zA-Z0-9_.:-]+$`)
|
||||||
|
|
@ -164,7 +165,7 @@ func configureCircuits(conf *[]config.Named) error {
|
||||||
|
|
||||||
children := slices.Clone(*conf)
|
children := slices.Clone(*conf)
|
||||||
|
|
||||||
// TODO: check for circular references
|
// TODO check for circular references
|
||||||
NEXT:
|
NEXT:
|
||||||
for i, cc := range children {
|
for i, cc := range children {
|
||||||
if cc.Name == "" {
|
if cc.Name == "" {
|
||||||
|
|
@ -780,14 +781,23 @@ func configureOCPP(cfg *ocpp.Config, externalUrl string) {
|
||||||
func configureEEBus(conf *eebus.Config) error {
|
func configureEEBus(conf *eebus.Config) error {
|
||||||
// migrate settings
|
// migrate settings
|
||||||
if settings.Exists(keys.EEBus) {
|
if settings.Exists(keys.EEBus) {
|
||||||
*conf = eebus.Config{}
|
if err := migrateYamlToJson(keys.EEBus, conf); err != nil {
|
||||||
if err := settings.Yaml(keys.EEBus, new(map[string]any), &conf); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if conf.IsConfigured() {
|
||||||
|
fromYaml.eebus = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conf.Configured() {
|
if !conf.IsConfigured() {
|
||||||
return nil
|
cc, err := eebus.DefaultConfig(conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*conf = *cc
|
||||||
|
if err := settings.SetJson(keys.EEBus, conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -968,33 +978,11 @@ func configureDevices(conf globalconfig.All) error {
|
||||||
return joinErrors(errs...)
|
return joinErrors(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrateYamlToJson converts a settings value from yaml to json if needed
|
|
||||||
func migrateYamlToJson[T any](key string) error {
|
|
||||||
var err error
|
|
||||||
if settings.IsJson(key) {
|
|
||||||
// already JSON, nothing to do
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var data T
|
|
||||||
if err := settings.Yaml(key, new(T), &data); err == nil {
|
|
||||||
settings.SetJson(key, data)
|
|
||||||
log.INFO.Printf("migrated %s setting to JSON", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureModbusProxy(conf *[]globalconfig.ModbusProxy) error {
|
func configureModbusProxy(conf *[]globalconfig.ModbusProxy) error {
|
||||||
if settings.Exists(keys.ModbusProxy) {
|
if settings.Exists(keys.ModbusProxy) {
|
||||||
// TODO: delete if not needed any more
|
if err := migrateYamlToJson(keys.ModbusProxy, conf); err != nil {
|
||||||
if err := migrateYamlToJson[[]globalconfig.ModbusProxy](keys.ModbusProxy); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := settings.Json(keys.ModbusProxy, &conf); err != nil {
|
|
||||||
return fmt.Errorf("failed to read modbusproxy setting: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent panic
|
// prevent panic
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Списък за повторна поръчка",
|
"dragList": "Списък за повторна поръчка",
|
||||||
"experimental": "Експериментален",
|
"experimental": "Експериментален",
|
||||||
"forceSave": "Запази все пак",
|
"forceSave": "Запази все пак",
|
||||||
|
"fromYamlHint": "Забележка: Конфигурира се чрез evcc.yaml. Премахнете записа от файла, за да активирате редактирането тук.",
|
||||||
"hideAdvancedSettings": "Скрий разширените настройки",
|
"hideAdvancedSettings": "Скрий разширените настройки",
|
||||||
"invalidFileSelected": "Избраният файл е невалиден",
|
"invalidFileSelected": "Избраният файл е невалиден",
|
||||||
"noFileSelected": "Няма избран файл.",
|
"noFileSelected": "Няма избран файл.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Изтегли CSV",
|
"downloadCsv": "Изтегли CSV",
|
||||||
"eventsRecorded": "Записани {count} събития за ограничение на мрежата.",
|
"eventsRecorded": "Записани {count} събития за ограничение на мрежата.",
|
||||||
"lastEvent": "Най-скорошно {timeAgo}.",
|
"lastEvent": "Най-скорошно {timeAgo}.",
|
||||||
"title": "Външен лимит",
|
"title": "Външен лимит"
|
||||||
"yamlConfigured": "Забележка: Конфигурира се чрез evcc.yaml. Премахнете записа от файла, за да активирате редактирането тук."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "промени",
|
"change": "промени",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "не е конфигуриран",
|
"unconfigured": "не е конфигуриран",
|
||||||
"vehicles": "Моите Автомобили",
|
"vehicles": "Моите Автомобили",
|
||||||
"welcomeBannerText": "Започнете с създаването на поне един **зарядно устройство**, **нагревател**, **мрежа**, **соларна система**, **батерия** или **допълнителен електромер**. Ако искате само да тествате, изберете **демонстрационно устройство**..",
|
"welcomeBannerText": "Започнете с създаването на поне един **зарядно устройство**, **нагревател**, **мрежа**, **соларна система**, **батерия** или **допълнителен електромер**. Ако искате само да тествате, изберете **демонстрационно устройство**..",
|
||||||
"welcomeBannerTitle": "Да конфигурираме вашата система!",
|
"welcomeBannerTitle": "Да конфигурираме вашата система!"
|
||||||
"yaml": "Устройствата от evcc.yaml не могат да се редактират."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Получавайте съобщения за вашите сесии на зареждане.",
|
"description": "Получавайте съобщения за вашите сесии на зареждане.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Ponovo naručiva lista",
|
"dragList": "Ponovo naručiva lista",
|
||||||
"experimental": "Eksperimentalni",
|
"experimental": "Eksperimentalni",
|
||||||
"forceSave": "Sačuvaj svejedno",
|
"forceSave": "Sačuvaj svejedno",
|
||||||
|
"fromYamlHint": "Napomena: Konfigurisano putem evcc.yaml. Uklonite unos iz datoteke da biste ovdje omogućili uređivanje.",
|
||||||
"hideAdvancedSettings": "Sakrij napredne postavke",
|
"hideAdvancedSettings": "Sakrij napredne postavke",
|
||||||
"invalidFileSelected": "Odabrana neispravna datoteka",
|
"invalidFileSelected": "Odabrana neispravna datoteka",
|
||||||
"noFileSelected": "Nije odabrana nijedna datoteka.",
|
"noFileSelected": "Nije odabrana nijedna datoteka.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Preuzmi CSV",
|
"downloadCsv": "Preuzmi CSV",
|
||||||
"eventsRecorded": "Zabilježeno je {count} događaja ograničenja mreže.",
|
"eventsRecorded": "Zabilježeno je {count} događaja ograničenja mreže.",
|
||||||
"lastEvent": "Najnoviji prije {timeAgo}.",
|
"lastEvent": "Najnoviji prije {timeAgo}.",
|
||||||
"title": "Vanjski limit",
|
"title": "Vanjski limit"
|
||||||
"yamlConfigured": "Napomena: Konfigurisano putem evcc.yaml. Uklonite unos iz datoteke da biste ovdje omogućili uređivanje."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "promjena",
|
"change": "promjena",
|
||||||
|
|
@ -344,6 +344,7 @@
|
||||||
"thresholdDisableHelpZero": "Zaustavite kada se za {delay} ne može zadovoljiti minimalna potrebna snaga.",
|
"thresholdDisableHelpZero": "Zaustavite kada se za {delay} ne može zadovoljiti minimalna potrebna snaga.",
|
||||||
"thresholdEnableHelpZero": " Počnite kada se minimalna potrebna snaga može zadovoljiti za {delay}."
|
"thresholdEnableHelpZero": " Počnite kada se minimalna potrebna snaga može zadovoljiti za {delay}."
|
||||||
},
|
},
|
||||||
|
"main": {},
|
||||||
"meter": {
|
"meter": {
|
||||||
"save": "Sačuvaj"
|
"save": "Sačuvaj"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Seznam umožňující úpravy pořadí",
|
"dragList": "Seznam umožňující úpravy pořadí",
|
||||||
"experimental": "Experimentální nastavení",
|
"experimental": "Experimentální nastavení",
|
||||||
"forceSave": "Vynutit uložení",
|
"forceSave": "Vynutit uložení",
|
||||||
|
"fromYamlHint": "Poznámka: Konfigurace se provádí prostřednictvím souboru evcc.yaml. Chcete-li zde povolit úpravy, odstraňte záznam ze souboru.",
|
||||||
"hideAdvancedSettings": "Skrýt pokročilá nastavení",
|
"hideAdvancedSettings": "Skrýt pokročilá nastavení",
|
||||||
"invalidFileSelected": "Zvolen neplatný soubor",
|
"invalidFileSelected": "Zvolen neplatný soubor",
|
||||||
"noFileSelected": "Nebyl zvolen žádný soubor.",
|
"noFileSelected": "Nebyl zvolen žádný soubor.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Stáhnout CSV",
|
"downloadCsv": "Stáhnout CSV",
|
||||||
"eventsRecorded": "Zaznamenáno {count} událostí omezení mřížky.",
|
"eventsRecorded": "Zaznamenáno {count} událostí omezení mřížky.",
|
||||||
"lastEvent": "Nejnovější {timeAgo}.",
|
"lastEvent": "Nejnovější {timeAgo}.",
|
||||||
"title": "Vnější limit",
|
"title": "Vnější limit"
|
||||||
"yamlConfigured": "Poznámka: Konfigurace se provádí prostřednictvím souboru evcc.yaml. Chcete-li zde povolit úpravy, odstraňte záznam ze souboru."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "Změnit",
|
"change": "Změnit",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "není konfigurováno",
|
"unconfigured": "není konfigurováno",
|
||||||
"vehicles": "Má vozidla",
|
"vehicles": "Má vozidla",
|
||||||
"welcomeBannerText": "Začněte vytvořením alespoň jednoho **nabíječe**, **ohřívače**, **sítě**, **solárního panelu**, **baterie** nebo **dalšího měřiče**. Pokud chcete pouze otestovat, vyberte **demo zařízení**.",
|
"welcomeBannerText": "Začněte vytvořením alespoň jednoho **nabíječe**, **ohřívače**, **sítě**, **solárního panelu**, **baterie** nebo **dalšího měřiče**. Pokud chcete pouze otestovat, vyberte **demo zařízení**.",
|
||||||
"welcomeBannerTitle": "Pojďme nakonfigurovat váš systém!",
|
"welcomeBannerTitle": "Pojďme nakonfigurovat váš systém!"
|
||||||
"yaml": "Zařízení z evcc.yaml nelze upravovat."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Dostávejte notifikace o průběhu nabíjení.",
|
"description": "Dostávejte notifikace o průběhu nabíjení.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Rækkefølgen kan ændres",
|
"dragList": "Rækkefølgen kan ændres",
|
||||||
"experimental": "Eksperimentel",
|
"experimental": "Eksperimentel",
|
||||||
"forceSave": "Gem alligevel",
|
"forceSave": "Gem alligevel",
|
||||||
|
"fromYamlHint": "Bemærk: Er konfigureret via evcc.yaml. Fjern indtastningen fra filen for at aktivere redigering her.",
|
||||||
"hideAdvancedSettings": "Skjul avancerede indstillinger",
|
"hideAdvancedSettings": "Skjul avancerede indstillinger",
|
||||||
"invalidFileSelected": "Ugyldig fil er valgt",
|
"invalidFileSelected": "Ugyldig fil er valgt",
|
||||||
"noFileSelected": "Ingen fil valgt.",
|
"noFileSelected": "Ingen fil valgt.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Download CSV",
|
"downloadCsv": "Download CSV",
|
||||||
"eventsRecorded": "Registrerede {count} netbegrænsningshændelser.",
|
"eventsRecorded": "Registrerede {count} netbegrænsningshændelser.",
|
||||||
"lastEvent": "Sidst {timeAgo}.",
|
"lastEvent": "Sidst {timeAgo}.",
|
||||||
"title": "Ekstern grænse",
|
"title": "Ekstern grænse"
|
||||||
"yamlConfigured": "Bemærk: Er konfigureret via evcc.yaml. Fjern indtastningen fra filen for at aktivere redigering her."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "ændre",
|
"change": "ændre",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "ikke konfigureret",
|
"unconfigured": "ikke konfigureret",
|
||||||
"vehicles": "Mine køretøjer",
|
"vehicles": "Mine køretøjer",
|
||||||
"welcomeBannerText": "Start med at oprette mindst én **lader**, **varmer**, **elnet**, **sol**, **batteri** eller **ekstra måler**. Hvis du blot vil teste, kan du vælge en **demoenhed**.",
|
"welcomeBannerText": "Start med at oprette mindst én **lader**, **varmer**, **elnet**, **sol**, **batteri** eller **ekstra måler**. Hvis du blot vil teste, kan du vælge en **demoenhed**.",
|
||||||
"welcomeBannerTitle": "Lad os konfigurere dit system!",
|
"welcomeBannerTitle": "Lad os konfigurere dit system!"
|
||||||
"yaml": "Enheder fra evcc.yaml kan ikke ændres her."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Modtag beskeder om dine opladningssessioner.",
|
"description": "Modtag beskeder om dine opladningssessioner.",
|
||||||
|
|
|
||||||
26
i18n/de.json
26
i18n/de.json
|
|
@ -172,7 +172,23 @@
|
||||||
"loading": "Lade YAML-Editor …"
|
"loading": "Lade YAML-Editor …"
|
||||||
},
|
},
|
||||||
"eebus": {
|
"eebus": {
|
||||||
"description": "Grundkonfiguration für die Kommunikation mit anderen EEBus-Geräten.",
|
"certificate": {
|
||||||
|
"private": "Privater Schlüssel",
|
||||||
|
"public": "Öffentliches Zertifikat",
|
||||||
|
"title": "Zertifikate"
|
||||||
|
},
|
||||||
|
"description": "Konfiguration zur Kommunikation mit EEBus-kompatiblen Geräten wie Wallboxen oder dem Steuergerät des Netzbetreibers. Alle notwendigen Initialisierungen und die Zertifikatsgenerierung erfolgen automatisch beim ersten Start.",
|
||||||
|
"descriptionAdvanced": "Keine Änderungen erforderlich. Bei Änderung ist Neukoppelung aller Geräte erforderlich.",
|
||||||
|
"interfaces": "Schnittstellen",
|
||||||
|
"interfacesHelp": "Begrenzt die Netzwerkschnittstellen, die EEBus nutzen soll, um Kommunikationsprobleme zu vermeiden. Feld leer lassen, um alle Schnittstellen zu verwenden. Ein Eintrag pro Zeile.",
|
||||||
|
"port": "Port",
|
||||||
|
"portHelp": "Der zu verwendende Port.",
|
||||||
|
"removeConfirm": "Die gesamte EEBus-Konfiguration wird entfernt. Neue Zertifikate und Kennungen werden beim nächsten Start generiert. Bist du sicher?",
|
||||||
|
"shipid": "SHIP-ID",
|
||||||
|
"shipidExplain": "Permanente Gerätekennung zur Identifikation im EEBus-Netzwerk.",
|
||||||
|
"shipidHelp": "Diese SHIP-ID ist mit den unten stehenden Zertifikaten verknüpft.",
|
||||||
|
"ski": "SKI",
|
||||||
|
"skiExplain": "Eindeutige Sicherheitskennung zum Koppeln von EEBus-Geräten.",
|
||||||
"title": "EEBus"
|
"title": "EEBus"
|
||||||
},
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
|
|
@ -209,6 +225,7 @@
|
||||||
"dragList": "Sortierbare Liste",
|
"dragList": "Sortierbare Liste",
|
||||||
"experimental": "Experimentell",
|
"experimental": "Experimentell",
|
||||||
"forceSave": "Trotzdem speichern",
|
"forceSave": "Trotzdem speichern",
|
||||||
|
"fromYamlHint": "Hinweis: Konfiguriert über evcc.yaml. Entferne den Eintrag aus der Datei, um die Bearbeitung hier zu aktivieren.",
|
||||||
"hideAdvancedSettings": "Erweiterte Einstellungen ausblenden",
|
"hideAdvancedSettings": "Erweiterte Einstellungen ausblenden",
|
||||||
"invalidFileSelected": "Ungültige Datei ausgewählt",
|
"invalidFileSelected": "Ungültige Datei ausgewählt",
|
||||||
"noFileSelected": "Keine Datei ausgewählt.",
|
"noFileSelected": "Keine Datei ausgewählt.",
|
||||||
|
|
@ -247,8 +264,7 @@
|
||||||
"downloadCsv": "CSV herunterladen",
|
"downloadCsv": "CSV herunterladen",
|
||||||
"eventsRecorded": "{count} Netzbegrenzungen gespeichert.",
|
"eventsRecorded": "{count} Netzbegrenzungen gespeichert.",
|
||||||
"lastEvent": "Zuletzt {timeAgo}.",
|
"lastEvent": "Zuletzt {timeAgo}.",
|
||||||
"title": "Externe Begrenzung",
|
"title": "Externe Begrenzung"
|
||||||
"yamlConfigured": "Hinweis: Konfiguriert über evcc.yaml. Entferne den Eintrag aus der Datei, um die Bearbeitung hier zu aktivieren."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "ändern",
|
"change": "ändern",
|
||||||
|
|
@ -390,8 +406,7 @@
|
||||||
"unconfigured": "nicht konfiguriert",
|
"unconfigured": "nicht konfiguriert",
|
||||||
"vehicles": "Meine Fahrzeuge",
|
"vehicles": "Meine Fahrzeuge",
|
||||||
"welcomeBannerText": "Lege zunächst eine **Wallbox**, **Heizung**, **Netzzähler**, **Solar**, **Batterie** oder **zusätzlichen Zähler** an. Falls du nur testen möchtest, wähle ein **Demogerät**.",
|
"welcomeBannerText": "Lege zunächst eine **Wallbox**, **Heizung**, **Netzzähler**, **Solar**, **Batterie** oder **zusätzlichen Zähler** an. Falls du nur testen möchtest, wähle ein **Demogerät**.",
|
||||||
"welcomeBannerTitle": "Los geht's mit der Konfiguration!",
|
"welcomeBannerTitle": "Los geht's mit der Konfiguration!"
|
||||||
"yaml": "Geräte aus evcc.yaml sind nicht editierbar."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Benachrichtigungen über Ladevorgänge und andere Ereignisse erhalten.",
|
"description": "Benachrichtigungen über Ladevorgänge und andere Ereignisse erhalten.",
|
||||||
|
|
@ -543,6 +558,7 @@
|
||||||
"integrations": "Integrationen",
|
"integrations": "Integrationen",
|
||||||
"loadpoints": "Laden & Heizen",
|
"loadpoints": "Laden & Heizen",
|
||||||
"meter": "PV & Batterie",
|
"meter": "PV & Batterie",
|
||||||
|
"services": "Dienste",
|
||||||
"system": "System",
|
"system": "System",
|
||||||
"vehicles": "Fahrzeuge"
|
"vehicles": "Fahrzeuge"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Λίστα με αναδιάταξη",
|
"dragList": "Λίστα με αναδιάταξη",
|
||||||
"experimental": "Πειραματικό",
|
"experimental": "Πειραματικό",
|
||||||
"forceSave": "Αποθήκευση ούτως ή άλλως",
|
"forceSave": "Αποθήκευση ούτως ή άλλως",
|
||||||
|
"fromYamlHint": "Σημείωση: Διαμορφώνεται μέσω του αρχείου evcc.yaml. Διαγράψτε την καταχώριση από το αρχείο για να ενεργοποιήσετε την επεξεργασία εδώ.",
|
||||||
"hideAdvancedSettings": "Απόκρυψη ρυθμίσεων για προχωρημένους",
|
"hideAdvancedSettings": "Απόκρυψη ρυθμίσεων για προχωρημένους",
|
||||||
"invalidFileSelected": "Επιλέχθηκε μη έγκυρο αρχείο",
|
"invalidFileSelected": "Επιλέχθηκε μη έγκυρο αρχείο",
|
||||||
"noFileSelected": "Δεν έχει επιλεγεί αρχείο.",
|
"noFileSelected": "Δεν έχει επιλεγεί αρχείο.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Λήψη CSV",
|
"downloadCsv": "Λήψη CSV",
|
||||||
"eventsRecorded": "Καταγράφηκαν {count} συμβάντα περιορισμού πλέγματος.",
|
"eventsRecorded": "Καταγράφηκαν {count} συμβάντα περιορισμού πλέγματος.",
|
||||||
"lastEvent": "Πιο πρόσφατο {timeAgo}.",
|
"lastEvent": "Πιο πρόσφατο {timeAgo}.",
|
||||||
"title": "Εξωτερικό όριο",
|
"title": "Εξωτερικό όριο"
|
||||||
"yamlConfigured": "Σημείωση: Διαμορφώνεται μέσω του αρχείου evcc.yaml. Διαγράψτε την καταχώριση από το αρχείο για να ενεργοποιήσετε την επεξεργασία εδώ."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "αλλαγή",
|
"change": "αλλαγή",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "δεν έχει ρυθμιστεί",
|
"unconfigured": "δεν έχει ρυθμιστεί",
|
||||||
"vehicles": "Τα Οχήματά μου",
|
"vehicles": "Τα Οχήματά μου",
|
||||||
"welcomeBannerText": "Ξεκινήστε δημιουργώντας τουλάχιστον έναν **φορτιστή**, **θερμαντήρα**, **πλέγμα**, **ηλιακό**, **μπαταρία** ή **επιπλέον μετρητή**. Αν θέλετε απλώς να δοκιμάσετε, επιλέξτε μια **συσκευή επίδειξης**.",
|
"welcomeBannerText": "Ξεκινήστε δημιουργώντας τουλάχιστον έναν **φορτιστή**, **θερμαντήρα**, **πλέγμα**, **ηλιακό**, **μπαταρία** ή **επιπλέον μετρητή**. Αν θέλετε απλώς να δοκιμάσετε, επιλέξτε μια **συσκευή επίδειξης**.",
|
||||||
"welcomeBannerTitle": "Ας διαμορφώσουμε το σύστημά σας!",
|
"welcomeBannerTitle": "Ας διαμορφώσουμε το σύστημά σας!"
|
||||||
"yaml": "Συσκευές που ορίζονται στο evcc.yaml δεν είναι εδώ προς επεξεργασία."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Λάβετε μηνύματα σχετικά με τις συνεδρίες φόρτισης.",
|
"description": "Λάβετε μηνύματα σχετικά με τις συνεδρίες φόρτισης.",
|
||||||
|
|
|
||||||
26
i18n/en.json
26
i18n/en.json
|
|
@ -172,7 +172,23 @@
|
||||||
"loading": "Loading YAML editor…"
|
"loading": "Loading YAML editor…"
|
||||||
},
|
},
|
||||||
"eebus": {
|
"eebus": {
|
||||||
"description": "Configuration that enables evcc to communicate with other EEBus devices.",
|
"certificate": {
|
||||||
|
"private": "Private key",
|
||||||
|
"public": "Public certificate",
|
||||||
|
"title": "Certificates"
|
||||||
|
},
|
||||||
|
"description": "Configuration that enables evcc to communicate with EEBus compatible devices like chargers or a control unit of your grid operator. All relevant initialization and certificate generation is done automatically on first start.",
|
||||||
|
"descriptionAdvanced": "No changes required. Only perform changes if you really know what you're doing. If you change either the SHIP-id or the certificates, you'll need to pair your devices again.",
|
||||||
|
"interfaces": "Interfaces",
|
||||||
|
"interfacesHelp": "Limit the network interfaces that EEBus should use to avoid communication problems. Leave the field blank to use all interfaces. One entry per line.",
|
||||||
|
"port": "Port",
|
||||||
|
"portHelp": "The port to be used.",
|
||||||
|
"removeConfirm": "All EEBus configuration will be removed. New certificates and identifiers will be generated on next start. Are you sure?",
|
||||||
|
"shipid": "SHIP-ID",
|
||||||
|
"shipidExplain": "Permanent device identifier for identification in the EEBus network.",
|
||||||
|
"shipidHelp": "This SHIP-ID is linked to the certificates below.",
|
||||||
|
"ski": "SKI",
|
||||||
|
"skiExplain": "Unique security identifier for pairing EEBus devices.",
|
||||||
"title": "EEBus"
|
"title": "EEBus"
|
||||||
},
|
},
|
||||||
"experimental": {
|
"experimental": {
|
||||||
|
|
@ -209,6 +225,7 @@
|
||||||
"dragList": "Reorderable list",
|
"dragList": "Reorderable list",
|
||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"forceSave": "Save anyway",
|
"forceSave": "Save anyway",
|
||||||
|
"fromYamlHint": "Note: Configured via evcc.yaml. Remove the entry from the file to enable editing here.",
|
||||||
"hideAdvancedSettings": "Hide advanced settings",
|
"hideAdvancedSettings": "Hide advanced settings",
|
||||||
"invalidFileSelected": "Invalid file selected",
|
"invalidFileSelected": "Invalid file selected",
|
||||||
"noFileSelected": "No file selected.",
|
"noFileSelected": "No file selected.",
|
||||||
|
|
@ -247,8 +264,7 @@
|
||||||
"downloadCsv": "Download CSV",
|
"downloadCsv": "Download CSV",
|
||||||
"eventsRecorded": "Recorded {count} grid limitation events.",
|
"eventsRecorded": "Recorded {count} grid limitation events.",
|
||||||
"lastEvent": "Most recent {timeAgo}.",
|
"lastEvent": "Most recent {timeAgo}.",
|
||||||
"title": "External Limit",
|
"title": "External Limit"
|
||||||
"yamlConfigured": "Note: Configured via evcc.yaml. Remove the entry from the file to enable editing here."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "change",
|
"change": "change",
|
||||||
|
|
@ -390,8 +406,7 @@
|
||||||
"unconfigured": "not configured",
|
"unconfigured": "not configured",
|
||||||
"vehicles": "My Vehicles",
|
"vehicles": "My Vehicles",
|
||||||
"welcomeBannerText": "Start with creating at least one **charger**, **heater**, **grid**, **solar**, **battery** or **additional meter**. If you just want to test, pick a **demo device**.",
|
"welcomeBannerText": "Start with creating at least one **charger**, **heater**, **grid**, **solar**, **battery** or **additional meter**. If you just want to test, pick a **demo device**.",
|
||||||
"welcomeBannerTitle": "Let's configure your system!",
|
"welcomeBannerTitle": "Let's configure your system!"
|
||||||
"yaml": "Devices from evcc.yaml are not editable."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Receive messages about your charging sessions.",
|
"description": "Receive messages about your charging sessions.",
|
||||||
|
|
@ -543,6 +558,7 @@
|
||||||
"integrations": "Integrations",
|
"integrations": "Integrations",
|
||||||
"loadpoints": "Charging & Heating",
|
"loadpoints": "Charging & Heating",
|
||||||
"meter": "Solar & Battery",
|
"meter": "Solar & Battery",
|
||||||
|
"services": "Services",
|
||||||
"system": "System",
|
"system": "System",
|
||||||
"vehicles": "Vehicles"
|
"vehicles": "Vehicles"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Lista reordenable",
|
"dragList": "Lista reordenable",
|
||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"forceSave": "Guardar de todos modos",
|
"forceSave": "Guardar de todos modos",
|
||||||
|
"fromYamlHint": "Nota: Configurado a través de evcc.yaml. Elimina la entrada del archivo para poder editar aquí.",
|
||||||
"hideAdvancedSettings": "Ocultar configuración avanzada",
|
"hideAdvancedSettings": "Ocultar configuración avanzada",
|
||||||
"invalidFileSelected": "Se ha seleccionado un archivo no válido",
|
"invalidFileSelected": "Se ha seleccionado un archivo no válido",
|
||||||
"noFileSelected": "No hay ningún archivo seleccionado.",
|
"noFileSelected": "No hay ningún archivo seleccionado.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Descargar CSV",
|
"downloadCsv": "Descargar CSV",
|
||||||
"eventsRecorded": "Se registraron {count} eventos de limitación de la red.",
|
"eventsRecorded": "Se registraron {count} eventos de limitación de la red.",
|
||||||
"lastEvent": "Más reciente {timeAgo}.",
|
"lastEvent": "Más reciente {timeAgo}.",
|
||||||
"title": "Límite externo",
|
"title": "Límite externo"
|
||||||
"yamlConfigured": "Nota: Configurado a través de evcc.yaml. Elimina la entrada del archivo para poder editar aquí."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "cambio",
|
"change": "cambio",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "no configurado",
|
"unconfigured": "no configurado",
|
||||||
"vehicles": "Mis vehículos",
|
"vehicles": "Mis vehículos",
|
||||||
"welcomeBannerText": "Empiece por crear al menos un **cargador**, **calentador**, **red**, **solar**, **batería** o **contador adicional**. Si solo desea realizar una prueba, elija un **dispositivo de demostración**.",
|
"welcomeBannerText": "Empiece por crear al menos un **cargador**, **calentador**, **red**, **solar**, **batería** o **contador adicional**. Si solo desea realizar una prueba, elija un **dispositivo de demostración**.",
|
||||||
"welcomeBannerTitle": "¡Configuremos su sistema!",
|
"welcomeBannerTitle": "¡Configuremos su sistema!"
|
||||||
"yaml": "Los dispositivos de evcc.yaml no se pueden editar."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Recibe mensajes sobre tus sesiones de carga.",
|
"description": "Recibe mensajes sobre tus sesiones de carga.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Järjestettävä lista",
|
"dragList": "Järjestettävä lista",
|
||||||
"experimental": "Kokeellinen",
|
"experimental": "Kokeellinen",
|
||||||
"forceSave": "Tallenna joka tapauksessa",
|
"forceSave": "Tallenna joka tapauksessa",
|
||||||
|
"fromYamlHint": "Huomio: Asetus on evcc.yaml-tiedostosta. Poista asetus tiedostosta, jotta voisit muokata sitä täällä.",
|
||||||
"hideAdvancedSettings": "Piilota edistyneet asetukset",
|
"hideAdvancedSettings": "Piilota edistyneet asetukset",
|
||||||
"invalidFileSelected": "Valittu virheellinen tiedosto",
|
"invalidFileSelected": "Valittu virheellinen tiedosto",
|
||||||
"noFileSelected": "Ei valittua tiedostoa.",
|
"noFileSelected": "Ei valittua tiedostoa.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Lataa CSV-taulukko",
|
"downloadCsv": "Lataa CSV-taulukko",
|
||||||
"eventsRecorded": "Tallennettu {count} verkon rajoitustapahtumaa.",
|
"eventsRecorded": "Tallennettu {count} verkon rajoitustapahtumaa.",
|
||||||
"lastEvent": "Viimeisin {timeAgo}.",
|
"lastEvent": "Viimeisin {timeAgo}.",
|
||||||
"title": "Ulkoinen raja",
|
"title": "Ulkoinen raja"
|
||||||
"yamlConfigured": "Huomio: Asetus on evcc.yaml-tiedostosta. Poista asetus tiedostosta, jotta voisit muokata sitä täällä."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "vaihda",
|
"change": "vaihda",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "ei määritetty",
|
"unconfigured": "ei määritetty",
|
||||||
"vehicles": "Minun ajoneuvot",
|
"vehicles": "Minun ajoneuvot",
|
||||||
"welcomeBannerText": "Aloita luomalla vähintään yksi **laturi**, **lämmitin**, **sähköverkko**, **aurinkopaneeli**, **akku** tai **lisämittari**. Jos haluat vain testata, valitse **esittelylaite**.",
|
"welcomeBannerText": "Aloita luomalla vähintään yksi **laturi**, **lämmitin**, **sähköverkko**, **aurinkopaneeli**, **akku** tai **lisämittari**. Jos haluat vain testata, valitse **esittelylaite**.",
|
||||||
"welcomeBannerTitle": "Määritetään järjestelmäsi!",
|
"welcomeBannerTitle": "Määritetään järjestelmäsi!"
|
||||||
"yaml": "evcc.yaml-asetuksissa määritettyjä laitteita ei voi muokata."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Vastaanota viestejä koskien lataustapahtumiasi.",
|
"description": "Vastaanota viestejä koskien lataustapahtumiasi.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Liste à ordonner",
|
"dragList": "Liste à ordonner",
|
||||||
"experimental": "Expérimental",
|
"experimental": "Expérimental",
|
||||||
"forceSave": "Enregistrer quand même",
|
"forceSave": "Enregistrer quand même",
|
||||||
|
"fromYamlHint": "Remarque : configuré via evcc.yaml. Supprimez l'entrée du fichier pour pouvoir effectuer des modifications ici.",
|
||||||
"hideAdvancedSettings": "Masquer les paramètres avancés",
|
"hideAdvancedSettings": "Masquer les paramètres avancés",
|
||||||
"invalidFileSelected": "Fichier sélectionné non valide",
|
"invalidFileSelected": "Fichier sélectionné non valide",
|
||||||
"noFileSelected": "Aucun fichier sélectionné.",
|
"noFileSelected": "Aucun fichier sélectionné.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Télécharger CSV",
|
"downloadCsv": "Télécharger CSV",
|
||||||
"eventsRecorded": "Enregistrement de {count} événements liés à la limitation de la grille.",
|
"eventsRecorded": "Enregistrement de {count} événements liés à la limitation de la grille.",
|
||||||
"lastEvent": "Le plus récent {timeAgo}.",
|
"lastEvent": "Le plus récent {timeAgo}.",
|
||||||
"title": "Limite externe",
|
"title": "Limite externe"
|
||||||
"yamlConfigured": "Remarque : configuré via evcc.yaml. Supprimez l'entrée du fichier pour pouvoir effectuer des modifications ici."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "changer",
|
"change": "changer",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "non configuré",
|
"unconfigured": "non configuré",
|
||||||
"vehicles": "Mes véhicules",
|
"vehicles": "Mes véhicules",
|
||||||
"welcomeBannerText": "Commencez par créer au moins un **chargeur**, un **chauffage**, un **réseau**, un **système solaire**, une **batterie** ou un **compteur supplémentaire**. Si vous souhaitez simplement tester, choisissez un **appareil de démonstration**.",
|
"welcomeBannerText": "Commencez par créer au moins un **chargeur**, un **chauffage**, un **réseau**, un **système solaire**, une **batterie** ou un **compteur supplémentaire**. Si vous souhaitez simplement tester, choisissez un **appareil de démonstration**.",
|
||||||
"welcomeBannerTitle": "Configurons votre système !",
|
"welcomeBannerTitle": "Configurons votre système !"
|
||||||
"yaml": "Un appareil provenant du evcc.yaml n’est pas modifiable."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Recevoir des messages à propos de vos sessions de charge.",
|
"description": "Recevoir des messages à propos de vos sessions de charge.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Preurediv popis",
|
"dragList": "Preurediv popis",
|
||||||
"experimental": "Eksperimentalno",
|
"experimental": "Eksperimentalno",
|
||||||
"forceSave": "Svejedno spremi",
|
"forceSave": "Svejedno spremi",
|
||||||
|
"fromYamlHint": "Napomena: Konfigurirano putem evcc.yaml. Uklonite unos iz datoteke kako biste ovdje omogućili uređivanje.",
|
||||||
"hideAdvancedSettings": "Sakrij napredne postavke",
|
"hideAdvancedSettings": "Sakrij napredne postavke",
|
||||||
"invalidFileSelected": "Neispravna datoteka",
|
"invalidFileSelected": "Neispravna datoteka",
|
||||||
"noFileSelected": "Datoteka nije odabrana.",
|
"noFileSelected": "Datoteka nije odabrana.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Preuzmi CSV",
|
"downloadCsv": "Preuzmi CSV",
|
||||||
"eventsRecorded": "Zabilježeno je {count} događaja ograničenja mreže.",
|
"eventsRecorded": "Zabilježeno je {count} događaja ograničenja mreže.",
|
||||||
"lastEvent": "Najnoviji {timeAgo}",
|
"lastEvent": "Najnoviji {timeAgo}",
|
||||||
"title": "Vanjski limit",
|
"title": "Vanjski limit"
|
||||||
"yamlConfigured": "Napomena: Konfigurirano putem evcc.yaml. Uklonite unos iz datoteke kako biste ovdje omogućili uređivanje."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "promjeni",
|
"change": "promjeni",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "nije postavljeno",
|
"unconfigured": "nije postavljeno",
|
||||||
"vehicles": "Moja vozila",
|
"vehicles": "Moja vozila",
|
||||||
"welcomeBannerText": "Počnite s izradom barem jednog **punjača**, **grijača**, **mreže**, **solarne ploče**, **baterije** ili **dodatnog brojila**. Ako samo želite testirati, odaberite **demo uređaj**.",
|
"welcomeBannerText": "Počnite s izradom barem jednog **punjača**, **grijača**, **mreže**, **solarne ploče**, **baterije** ili **dodatnog brojila**. Ako samo želite testirati, odaberite **demo uređaj**.",
|
||||||
"welcomeBannerTitle": "Konfigurirajmo vaš sustav!",
|
"welcomeBannerTitle": "Konfigurirajmo vaš sustav!"
|
||||||
"yaml": "U evcc.yaml datoteci definirani uređaji se ne mogu uređivati."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Primajte poruke o svojim sesijama punjenja.",
|
"description": "Primajte poruke o svojim sesijama punjenja.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Átrendezhető lista",
|
"dragList": "Átrendezhető lista",
|
||||||
"experimental": "Kísérleti",
|
"experimental": "Kísérleti",
|
||||||
"forceSave": "Mentés mindenképpen",
|
"forceSave": "Mentés mindenképpen",
|
||||||
|
"fromYamlHint": "Megjegyzés: Az evcc.yaml fájlban konfigurálható. A szerkesztés engedélyezéséhez távolítsa el a bejegyzést a fájlból.",
|
||||||
"hideAdvancedSettings": "Speciális beállítások elrejtése",
|
"hideAdvancedSettings": "Speciális beállítások elrejtése",
|
||||||
"invalidFileSelected": "Érvénytelen fájl kiválasztva",
|
"invalidFileSelected": "Érvénytelen fájl kiválasztva",
|
||||||
"noFileSelected": "Nincs fájl kiválasztva.",
|
"noFileSelected": "Nincs fájl kiválasztva.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "CSV letöltése",
|
"downloadCsv": "CSV letöltése",
|
||||||
"eventsRecorded": "{count} rácskorlátozási esemény rögzítve.",
|
"eventsRecorded": "{count} rácskorlátozási esemény rögzítve.",
|
||||||
"lastEvent": "Legutóbbi {timeAgo}.",
|
"lastEvent": "Legutóbbi {timeAgo}.",
|
||||||
"title": "Külső határ",
|
"title": "Külső határ"
|
||||||
"yamlConfigured": "Megjegyzés: Az evcc.yaml fájlban konfigurálható. A szerkesztés engedélyezéséhez távolítsa el a bejegyzést a fájlból."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "változtatás",
|
"change": "változtatás",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "nincs konfigurálva",
|
"unconfigured": "nincs konfigurálva",
|
||||||
"vehicles": "Járműveim",
|
"vehicles": "Járműveim",
|
||||||
"welcomeBannerText": "Kezdje azzal, hogy létrehoz legalább egy **töltőt**, **fűtőt**, **hálózatot**, **napelemet**, **akkumulátort** vagy **kiegészítő mérőt**. Ha csak tesztelni szeretne, válasszon egy **demo eszközt**.",
|
"welcomeBannerText": "Kezdje azzal, hogy létrehoz legalább egy **töltőt**, **fűtőt**, **hálózatot**, **napelemet**, **akkumulátort** vagy **kiegészítő mérőt**. Ha csak tesztelni szeretne, válasszon egy **demo eszközt**.",
|
||||||
"welcomeBannerTitle": "Konfiguráljuk a rendszerét!",
|
"welcomeBannerTitle": "Konfiguráljuk a rendszerét!"
|
||||||
"yaml": "Az evcc.yaml fájlban szereplő eszközök nem szerkeszthetők."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Értesítési üzenetek beállítása a töltési folyamatokról.",
|
"description": "Értesítési üzenetek beállítása a töltési folyamatokról.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Elenco riordinabile",
|
"dragList": "Elenco riordinabile",
|
||||||
"experimental": "Sperimentale",
|
"experimental": "Sperimentale",
|
||||||
"forceSave": "Salva comunque",
|
"forceSave": "Salva comunque",
|
||||||
|
"fromYamlHint": "Nota: configurato tramite evcc.yaml. Rimuovere la voce dal file per abilitare la modifica qui.",
|
||||||
"hideAdvancedSettings": "Nascondi impostazioni avanzate",
|
"hideAdvancedSettings": "Nascondi impostazioni avanzate",
|
||||||
"invalidFileSelected": "File non valido selezionato",
|
"invalidFileSelected": "File non valido selezionato",
|
||||||
"noFileSelected": "Nessun file selezionato.",
|
"noFileSelected": "Nessun file selezionato.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Scarica CSV",
|
"downloadCsv": "Scarica CSV",
|
||||||
"eventsRecorded": "Registrati {count} eventi di limitazione della griglia.",
|
"eventsRecorded": "Registrati {count} eventi di limitazione della griglia.",
|
||||||
"lastEvent": "Più recente {timeAgo}.",
|
"lastEvent": "Più recente {timeAgo}.",
|
||||||
"title": "HEMSLimite esterno",
|
"title": "HEMSLimite esterno"
|
||||||
"yamlConfigured": "Nota: configurato tramite evcc.yaml. Rimuovere la voce dal file per abilitare la modifica qui."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "cambiamento",
|
"change": "cambiamento",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "non configurato",
|
"unconfigured": "non configurato",
|
||||||
"vehicles": "I miei veicoli",
|
"vehicles": "I miei veicoli",
|
||||||
"welcomeBannerText": "Inizia creando almeno un **caricabatterie**, un **riscaldatore**, una **rete**, un **impianto solare**, una **batteria** o un **contatore aggiuntivo**. Se desideri solo effettuare un test, scegli un **dispositivo demo**.",
|
"welcomeBannerText": "Inizia creando almeno un **caricabatterie**, un **riscaldatore**, una **rete**, un **impianto solare**, una **batteria** o un **contatore aggiuntivo**. Se desideri solo effettuare un test, scegli un **dispositivo demo**.",
|
||||||
"welcomeBannerTitle": "Configuriamo il tuo sistema!",
|
"welcomeBannerTitle": "Configuriamo il tuo sistema!"
|
||||||
"yaml": "I dispositivi di evcc.yaml non sono modificabili."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Ricevi messaggi sulle sessioni di ricarica.",
|
"description": "Ricevi messaggi sulle sessioni di ricarica.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Sortéirbar Lëscht",
|
"dragList": "Sortéirbar Lëscht",
|
||||||
"experimental": "Experimentell",
|
"experimental": "Experimentell",
|
||||||
"forceSave": "Trotzdem späicheren",
|
"forceSave": "Trotzdem späicheren",
|
||||||
|
"fromYamlHint": "Bemierkung: Konfiguréiert iwwer evcc.yaml. Läscht d'Astellungen aus der Datei fir d'Ännerung hei z'erméiglechen.",
|
||||||
"hideAdvancedSettings": "Fortgeschratten Astellunge verstoppen",
|
"hideAdvancedSettings": "Fortgeschratten Astellunge verstoppen",
|
||||||
"invalidFileSelected": "Ongülteg Datei ausgewielt",
|
"invalidFileSelected": "Ongülteg Datei ausgewielt",
|
||||||
"noFileSelected": "Keng Datei ausgewielt.",
|
"noFileSelected": "Keng Datei ausgewielt.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "CSV eroflueden",
|
"downloadCsv": "CSV eroflueden",
|
||||||
"eventsRecorded": "{count} gespäichert Limitatiounen vum Netz.",
|
"eventsRecorded": "{count} gespäichert Limitatiounen vum Netz.",
|
||||||
"lastEvent": "Méi rezent {timeAgo}.",
|
"lastEvent": "Méi rezent {timeAgo}.",
|
||||||
"title": "Äusserlech Grenz",
|
"title": "Äusserlech Grenz"
|
||||||
"yamlConfigured": "Bemierkung: Konfiguréiert iwwer evcc.yaml. Läscht d'Astellungen aus der Datei fir d'Ännerung hei z'erméiglechen."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "änneren",
|
"change": "änneren",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "net konfiguréiert",
|
"unconfigured": "net konfiguréiert",
|
||||||
"vehicles": "Meng Gefierer",
|
"vehicles": "Meng Gefierer",
|
||||||
"welcomeBannerText": "Fänkt un mat der Erstelle vun op d'mannst engem **Ladegerät**, **Heizer**, **Netz**, **Solarpanel**, **Batterie** oder **zousätzlechen Zähler**. Wann Dir just testen wëllt, wielt e **Demonstratiounsapparat**.",
|
"welcomeBannerText": "Fänkt un mat der Erstelle vun op d'mannst engem **Ladegerät**, **Heizer**, **Netz**, **Solarpanel**, **Batterie** oder **zousätzlechen Zähler**. Wann Dir just testen wëllt, wielt e **Demonstratiounsapparat**.",
|
||||||
"welcomeBannerTitle": "Loosst eis de System konfiguréieren!",
|
"welcomeBannerTitle": "Loosst eis de System konfiguréieren!"
|
||||||
"yaml": "Geräter aus der evcc.yaml Datei si net editiérbar."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Notifikatiounen iwwer Är Opluedsessioune kréien.",
|
"description": "Notifikatiounen iwwer Är Opluedsessioune kréien.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Galima sukeisti vietomis",
|
"dragList": "Galima sukeisti vietomis",
|
||||||
"experimental": "Eksperimentinis",
|
"experimental": "Eksperimentinis",
|
||||||
"forceSave": "Vistiek įrašyti",
|
"forceSave": "Vistiek įrašyti",
|
||||||
|
"fromYamlHint": "Pastaba: Sukonfigūruota naudojant evcc.yaml. Pašalinkite įrašą iš failo, kad būtų galima redaguoti čia.",
|
||||||
"hideAdvancedSettings": "Slėpti išplėstinius nustatymus",
|
"hideAdvancedSettings": "Slėpti išplėstinius nustatymus",
|
||||||
"invalidFileSelected": "Pasirinktas netinkamas failas",
|
"invalidFileSelected": "Pasirinktas netinkamas failas",
|
||||||
"noFileSelected": "Nepasirinktas failas.",
|
"noFileSelected": "Nepasirinktas failas.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Atsisiųsti CSV failą",
|
"downloadCsv": "Atsisiųsti CSV failą",
|
||||||
"eventsRecorded": "Užfiksuota {count} tinklo galios apribojimo įvykių.",
|
"eventsRecorded": "Užfiksuota {count} tinklo galios apribojimo įvykių.",
|
||||||
"lastEvent": "Naujausias {timeAgo}.",
|
"lastEvent": "Naujausias {timeAgo}.",
|
||||||
"title": "Išorinė riba",
|
"title": "Išorinė riba"
|
||||||
"yamlConfigured": "Pastaba: Sukonfigūruota naudojant evcc.yaml. Pašalinkite įrašą iš failo, kad būtų galima redaguoti čia."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "pakeisti",
|
"change": "pakeisti",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "nesukonfigūruota",
|
"unconfigured": "nesukonfigūruota",
|
||||||
"vehicles": "Mano automobiliai",
|
"vehicles": "Mano automobiliai",
|
||||||
"welcomeBannerText": "Pradžiai sukonfigūruokite bent vieną **įkroviklį**, **šildytuvą**, **tinklo skaitiklį**, **saulės jėgainę** ar **papildomą skaitiklį**. Jei norite tik pasibandyti, pasirinkite **demonstracinį įrenginį**.",
|
"welcomeBannerText": "Pradžiai sukonfigūruokite bent vieną **įkroviklį**, **šildytuvą**, **tinklo skaitiklį**, **saulės jėgainę** ar **papildomą skaitiklį**. Jei norite tik pasibandyti, pasirinkite **demonstracinį įrenginį**.",
|
||||||
"welcomeBannerTitle": "Sukonfigūruokime jūsų sistemą!",
|
"welcomeBannerTitle": "Sukonfigūruokime jūsų sistemą!"
|
||||||
"yaml": "Įtaisas iš evcc.yaml , čia konfigūruoti neina."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Gaukite pranešimus apie įkrovimo sesijas.",
|
"description": "Gaukite pranešimus apie įkrovimo sesijas.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Sorteerbare lijst",
|
"dragList": "Sorteerbare lijst",
|
||||||
"experimental": "Experimenteel",
|
"experimental": "Experimenteel",
|
||||||
"forceSave": "Toch opslaan",
|
"forceSave": "Toch opslaan",
|
||||||
|
"fromYamlHint": "Opmerking: Geconfigureerd via evcc.yaml. Verwijder de betreffende regels uit het bestand om hier bewerkingen mogelijk te maken.",
|
||||||
"hideAdvancedSettings": "Verberg geavanceerde instellingen",
|
"hideAdvancedSettings": "Verberg geavanceerde instellingen",
|
||||||
"invalidFileSelected": "Ongeldig bestand geselecteerd",
|
"invalidFileSelected": "Ongeldig bestand geselecteerd",
|
||||||
"noFileSelected": "Geen bestand geselecteerd.",
|
"noFileSelected": "Geen bestand geselecteerd.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Download CSV",
|
"downloadCsv": "Download CSV",
|
||||||
"eventsRecorded": "{count} gebeurtenissen met rasterbeperkingen geregistreerd.",
|
"eventsRecorded": "{count} gebeurtenissen met rasterbeperkingen geregistreerd.",
|
||||||
"lastEvent": "Meest recent {timeAgo}.",
|
"lastEvent": "Meest recent {timeAgo}.",
|
||||||
"title": "Externe limiet",
|
"title": "Externe limiet"
|
||||||
"yamlConfigured": "Opmerking: Geconfigureerd via evcc.yaml. Verwijder de betreffende regels uit het bestand om hier bewerkingen mogelijk te maken."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "wijzig",
|
"change": "wijzig",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "Niet geconfigureerd",
|
"unconfigured": "Niet geconfigureerd",
|
||||||
"vehicles": "Mijn voertuigen",
|
"vehicles": "Mijn voertuigen",
|
||||||
"welcomeBannerText": "Begin met het aanmaken van minstens één **oplader**, **verwarmer**, **netstroom**, **zonnepanelen**, **thuisbatterij** of **extra meter**. Als je gewoon wilt testen, kies dan een **demo-apparaat**.",
|
"welcomeBannerText": "Begin met het aanmaken van minstens één **oplader**, **verwarmer**, **netstroom**, **zonnepanelen**, **thuisbatterij** of **extra meter**. Als je gewoon wilt testen, kies dan een **demo-apparaat**.",
|
||||||
"welcomeBannerTitle": "Start met het configureren van uw systeem!",
|
"welcomeBannerTitle": "Start met het configureren van uw systeem!"
|
||||||
"yaml": "Apparaten geconfigureerd in evcc.yaml kunnen hier niet worden bewerkt."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Ontvang berichten over je oplaadsessies.",
|
"description": "Ontvang berichten over je oplaadsessies.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Omordningsbar liste",
|
"dragList": "Omordningsbar liste",
|
||||||
"experimental": "Eksperimentell",
|
"experimental": "Eksperimentell",
|
||||||
"forceSave": "Lagre likevel",
|
"forceSave": "Lagre likevel",
|
||||||
|
"fromYamlHint": "Merk: Konfigurert via evcc.yaml. Fjern oppføringen fra filen for å aktivere redigering her.",
|
||||||
"hideAdvancedSettings": "Skjul avanserte innstillinger",
|
"hideAdvancedSettings": "Skjul avanserte innstillinger",
|
||||||
"invalidFileSelected": "Ugyldig fil valgt",
|
"invalidFileSelected": "Ugyldig fil valgt",
|
||||||
"noFileSelected": "Ingen fil valgt.",
|
"noFileSelected": "Ingen fil valgt.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Last ned CSV",
|
"downloadCsv": "Last ned CSV",
|
||||||
"eventsRecorded": "Registrerte {count} hendelser med rutenettbegrensning.",
|
"eventsRecorded": "Registrerte {count} hendelser med rutenettbegrensning.",
|
||||||
"lastEvent": "Siste {timeAgo}.",
|
"lastEvent": "Siste {timeAgo}.",
|
||||||
"title": "Ekstern grense",
|
"title": "Ekstern grense"
|
||||||
"yamlConfigured": "Merk: Konfigurert via evcc.yaml. Fjern oppføringen fra filen for å aktivere redigering her."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "endring",
|
"change": "endring",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "ikke konfigurert",
|
"unconfigured": "ikke konfigurert",
|
||||||
"vehicles": "Mine kjøretøy",
|
"vehicles": "Mine kjøretøy",
|
||||||
"welcomeBannerText": "Begynn med å opprette minst én **lader**, **varmeapparat**, **nett**, **solcelle**, **batteri** eller **ekstra måler**. Hvis du bare vil teste, velger du en **demoenhet**.",
|
"welcomeBannerText": "Begynn med å opprette minst én **lader**, **varmeapparat**, **nett**, **solcelle**, **batteri** eller **ekstra måler**. Hvis du bare vil teste, velger du en **demoenhet**.",
|
||||||
"welcomeBannerTitle": "La oss konfigurere systemet ditt!",
|
"welcomeBannerTitle": "La oss konfigurere systemet ditt!"
|
||||||
"yaml": "Enheter fra evcc.yaml kan ikke redigeres."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Motta meldinger om ladingsøktene dine.",
|
"description": "Motta meldinger om ladingsøktene dine.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Lista do ponownego zamówienia",
|
"dragList": "Lista do ponownego zamówienia",
|
||||||
"experimental": "Eksperymentalny",
|
"experimental": "Eksperymentalny",
|
||||||
"forceSave": "Zapisz mimo to",
|
"forceSave": "Zapisz mimo to",
|
||||||
|
"fromYamlHint": "Uwaga: Konfiguracja odbywa się za pomocą pliku evcc.yaml. Aby umożliwić edycję w tym miejscu, usuń wpis z pliku.",
|
||||||
"hideAdvancedSettings": "Ukryj ustawienia zaawansowane",
|
"hideAdvancedSettings": "Ukryj ustawienia zaawansowane",
|
||||||
"invalidFileSelected": "Wybrano nieprawidłowy plik",
|
"invalidFileSelected": "Wybrano nieprawidłowy plik",
|
||||||
"noFileSelected": "Nie wybrano pliku.",
|
"noFileSelected": "Nie wybrano pliku.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Pobierz CSV",
|
"downloadCsv": "Pobierz CSV",
|
||||||
"eventsRecorded": "Zarejestrowano {count} zdarzeń związanych z ograniczeniami sieci.",
|
"eventsRecorded": "Zarejestrowano {count} zdarzeń związanych z ograniczeniami sieci.",
|
||||||
"lastEvent": "Najnowsze {timeAgo}.",
|
"lastEvent": "Najnowsze {timeAgo}.",
|
||||||
"title": "Limit zewnętrzny",
|
"title": "Limit zewnętrzny"
|
||||||
"yamlConfigured": "Uwaga: Konfiguracja odbywa się za pomocą pliku evcc.yaml. Aby umożliwić edycję w tym miejscu, usuń wpis z pliku."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "zmień",
|
"change": "zmień",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "nie skonfigurowane",
|
"unconfigured": "nie skonfigurowane",
|
||||||
"vehicles": "Moje pojazdy",
|
"vehicles": "Moje pojazdy",
|
||||||
"welcomeBannerText": "Zacznij od utworzenia co najmniej jednej **ładowarki**, **grzejnika**, **sieci**, **panelu słonecznego**, **baterii** lub **dodatkowego licznika**. Jeśli chcesz tylko przetestować, wybierz **urządzenie demonstracyjne**.",
|
"welcomeBannerText": "Zacznij od utworzenia co najmniej jednej **ładowarki**, **grzejnika**, **sieci**, **panelu słonecznego**, **baterii** lub **dodatkowego licznika**. Jeśli chcesz tylko przetestować, wybierz **urządzenie demonstracyjne**.",
|
||||||
"welcomeBannerTitle": "Skonfigurujmy Twój system!",
|
"welcomeBannerTitle": "Skonfigurujmy Twój system!"
|
||||||
"yaml": "Urządzenia z pliku evcc.yaml nie mogą być edytowane."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Otrzymuj wiadomości o sesjach ładowania.",
|
"description": "Otrzymuj wiadomości o sesjach ładowania.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Lista reordenável",
|
"dragList": "Lista reordenável",
|
||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"forceSave": "Guardar na mesma",
|
"forceSave": "Guardar na mesma",
|
||||||
|
"fromYamlHint": "Nota: Configurado através do evcc.yaml. Remova a entrada do ficheiro para permitir a edição aqui.",
|
||||||
"hideAdvancedSettings": "Ocultar definições avançadas",
|
"hideAdvancedSettings": "Ocultar definições avançadas",
|
||||||
"invalidFileSelected": "Ficheiro selecionado inválido",
|
"invalidFileSelected": "Ficheiro selecionado inválido",
|
||||||
"noFileSelected": "Nenhum ficheiro selecionado.",
|
"noFileSelected": "Nenhum ficheiro selecionado.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Descarregar CSV",
|
"downloadCsv": "Descarregar CSV",
|
||||||
"eventsRecorded": "Registou {count} eventos de limitação da rede.",
|
"eventsRecorded": "Registou {count} eventos de limitação da rede.",
|
||||||
"lastEvent": "Mais recente {timeAgo}.",
|
"lastEvent": "Mais recente {timeAgo}.",
|
||||||
"title": "Limite externo",
|
"title": "Limite externo"
|
||||||
"yamlConfigured": "Nota: Configurado através do evcc.yaml. Remova a entrada do ficheiro para permitir a edição aqui."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "alterar",
|
"change": "alterar",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "não configurado",
|
"unconfigured": "não configurado",
|
||||||
"vehicles": "Os meus Veículos",
|
"vehicles": "Os meus Veículos",
|
||||||
"welcomeBannerText": "Comece por criar pelo menos um **carregador**, **aquecedor**, **rede**, **solar**, **bateria** ou **medidor adicional**. Se quiser apenas testar, escolha um **dispositivo de demonstração**.",
|
"welcomeBannerText": "Comece por criar pelo menos um **carregador**, **aquecedor**, **rede**, **solar**, **bateria** ou **medidor adicional**. Se quiser apenas testar, escolha um **dispositivo de demonstração**.",
|
||||||
"welcomeBannerTitle": "Configuremos agora o seu sistema!",
|
"welcomeBannerTitle": "Configuremos agora o seu sistema!"
|
||||||
"yaml": "Os dispositivos de evcc.yaml não são editáveis."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Receber mensagens sobre as sessões de carregamento.",
|
"description": "Receber mensagens sobre as sessões de carregamento.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Listă reordonabilă",
|
"dragList": "Listă reordonabilă",
|
||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"forceSave": "Salvați oricum",
|
"forceSave": "Salvați oricum",
|
||||||
|
"fromYamlHint": "Notă: Configurat prin evcc.yaml. Ștergeți intrarea din fișier pentru a activa editarea aici.",
|
||||||
"hideAdvancedSettings": "Ascunde setările avansate",
|
"hideAdvancedSettings": "Ascunde setările avansate",
|
||||||
"invalidFileSelected": "Fișier invalid selectat",
|
"invalidFileSelected": "Fișier invalid selectat",
|
||||||
"noFileSelected": "Nu a fost selectat niciun fișier.",
|
"noFileSelected": "Nu a fost selectat niciun fișier.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Descărcați CSV",
|
"downloadCsv": "Descărcați CSV",
|
||||||
"eventsRecorded": "Au fost înregistrate {count} evenimente de limitare a grilei.",
|
"eventsRecorded": "Au fost înregistrate {count} evenimente de limitare a grilei.",
|
||||||
"lastEvent": "Cel mai recent {timeAgo}.",
|
"lastEvent": "Cel mai recent {timeAgo}.",
|
||||||
"title": "Limită externă",
|
"title": "Limită externă"
|
||||||
"yamlConfigured": "Notă: Configurat prin evcc.yaml. Ștergeți intrarea din fișier pentru a activa editarea aici."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "schimbare",
|
"change": "schimbare",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "neconfigurat",
|
"unconfigured": "neconfigurat",
|
||||||
"vehicles": "Masinile mele",
|
"vehicles": "Masinile mele",
|
||||||
"welcomeBannerText": "Începeți prin a crea cel puțin un **încărcător**, **încălzitor**, **rețea**, **sistem solar**, **baterie** sau **contor suplimentar**. Dacă doriți doar să testați, alegeți un **dispozitiv demo**.",
|
"welcomeBannerText": "Începeți prin a crea cel puțin un **încărcător**, **încălzitor**, **rețea**, **sistem solar**, **baterie** sau **contor suplimentar**. Dacă doriți doar să testați, alegeți un **dispozitiv demo**.",
|
||||||
"welcomeBannerTitle": "Să configurăm sistemul tău!",
|
"welcomeBannerTitle": "Să configurăm sistemul tău!"
|
||||||
"yaml": "Dispozitivele din evcc.yaml nu pot fi editate."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Primiți mesaje despre sesiunile dvs. de încărcare.",
|
"description": "Primiți mesaje despre sesiunile dvs. de încărcare.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Список, который можно переупорядочить",
|
"dragList": "Список, который можно переупорядочить",
|
||||||
"experimental": "Экспериментальный",
|
"experimental": "Экспериментальный",
|
||||||
"forceSave": "Сохранить в любом случае",
|
"forceSave": "Сохранить в любом случае",
|
||||||
|
"fromYamlHint": "Примечание: Настраивается через evcc.yaml. Удалите запись из файла, чтобы включить редактирование здесь.",
|
||||||
"hideAdvancedSettings": "Скрыть расширенные настройки",
|
"hideAdvancedSettings": "Скрыть расширенные настройки",
|
||||||
"invalidFileSelected": "Выбран недействительный файл",
|
"invalidFileSelected": "Выбран недействительный файл",
|
||||||
"noFileSelected": "Файл не выбран.",
|
"noFileSelected": "Файл не выбран.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Скачать CSV",
|
"downloadCsv": "Скачать CSV",
|
||||||
"eventsRecorded": "Зарегистрировано {count} событий ограничения сетки.",
|
"eventsRecorded": "Зарегистрировано {count} событий ограничения сетки.",
|
||||||
"lastEvent": "Последнее {timeAgo}.",
|
"lastEvent": "Последнее {timeAgo}.",
|
||||||
"title": "Внешний предел",
|
"title": "Внешний предел"
|
||||||
"yamlConfigured": "Примечание: Настраивается через evcc.yaml. Удалите запись из файла, чтобы включить редактирование здесь."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "изменение",
|
"change": "изменение",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "не настроен",
|
"unconfigured": "не настроен",
|
||||||
"vehicles": "Мои транспортные средства",
|
"vehicles": "Мои транспортные средства",
|
||||||
"welcomeBannerText": "Начните с создания хотя бы одного **зарядного устройства**, **нагревателя**, **сети**, **солнечной батареи**, **аккумулятора** или **дополнительного счетчика**. Если вы просто хотите протестировать, выберите **демонстрационное устройство**.",
|
"welcomeBannerText": "Начните с создания хотя бы одного **зарядного устройства**, **нагревателя**, **сети**, **солнечной батареи**, **аккумулятора** или **дополнительного счетчика**. Если вы просто хотите протестировать, выберите **демонстрационное устройство**.",
|
||||||
"welcomeBannerTitle": "Давайте настроим вашу систему!",
|
"welcomeBannerTitle": "Давайте настроим вашу систему!"
|
||||||
"yaml": "Устройства из evcc.yaml не подлежат редактированию."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Получайте сообщения о ваших сеансах зарядки.",
|
"description": "Получайте сообщения о ваших сеансах зарядки.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Seznam, ki ga je mogoče prerazporediti",
|
"dragList": "Seznam, ki ga je mogoče prerazporediti",
|
||||||
"experimental": "Eksperimentalno",
|
"experimental": "Eksperimentalno",
|
||||||
"forceSave": "Vseeno shrani",
|
"forceSave": "Vseeno shrani",
|
||||||
|
"fromYamlHint": "Opomba: Konfigurirano prek evcc.yaml. Odstranite vnos iz datoteke, da omogočite urejanje tukaj.",
|
||||||
"hideAdvancedSettings": "Skrij napredne nastavitve",
|
"hideAdvancedSettings": "Skrij napredne nastavitve",
|
||||||
"invalidFileSelected": "Izbrana neveljavna datoteka",
|
"invalidFileSelected": "Izbrana neveljavna datoteka",
|
||||||
"noFileSelected": "Izbrana ni nobena datoteka.",
|
"noFileSelected": "Izbrana ni nobena datoteka.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Prenesi CSV",
|
"downloadCsv": "Prenesi CSV",
|
||||||
"eventsRecorded": "Zabeleženo {count} dogodkov omejitve omrežja.",
|
"eventsRecorded": "Zabeleženo {count} dogodkov omejitve omrežja.",
|
||||||
"lastEvent": "Najnovejše {timeAgo}.",
|
"lastEvent": "Najnovejše {timeAgo}.",
|
||||||
"title": "Zunanja meja",
|
"title": "Zunanja meja"
|
||||||
"yamlConfigured": "Opomba: Konfigurirano prek evcc.yaml. Odstranite vnos iz datoteke, da omogočite urejanje tukaj."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "sprememba",
|
"change": "sprememba",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "ni konfigurirano",
|
"unconfigured": "ni konfigurirano",
|
||||||
"vehicles": "Moja vozila",
|
"vehicles": "Moja vozila",
|
||||||
"welcomeBannerText": "Začnite z ustvarjanjem vsaj enega **polnilnika**, **grelca**, **omrežja**, **sončne energije**, **baterije** ali **dodatnega merilnika**. Če želite samo preizkusiti, izberite **demo napravo**.",
|
"welcomeBannerText": "Začnite z ustvarjanjem vsaj enega **polnilnika**, **grelca**, **omrežja**, **sončne energije**, **baterije** ali **dodatnega merilnika**. Če želite samo preizkusiti, izberite **demo napravo**.",
|
||||||
"welcomeBannerTitle": "Konfigurirajmo vaš sistem!",
|
"welcomeBannerTitle": "Konfigurirajmo vaš sistem!"
|
||||||
"yaml": "Naprav iz datoteke evcc.yaml ni mogoče urejati."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Prejemanje sporočil o vaših sejah polnjenja.",
|
"description": "Prejemanje sporočil o vaših sejah polnjenja.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Sorterbar lista",
|
"dragList": "Sorterbar lista",
|
||||||
"experimental": "Experimentella funktioner",
|
"experimental": "Experimentella funktioner",
|
||||||
"forceSave": "Spara ändå",
|
"forceSave": "Spara ändå",
|
||||||
|
"fromYamlHint": "Obs: Konfigurerad via evcc.yaml. Ta bort posten från filen för att aktivera redigering här.",
|
||||||
"hideAdvancedSettings": "Dölj avancerade inställningar",
|
"hideAdvancedSettings": "Dölj avancerade inställningar",
|
||||||
"invalidFileSelected": "Ogiltig fil vald",
|
"invalidFileSelected": "Ogiltig fil vald",
|
||||||
"noFileSelected": "Ingen fil vald.",
|
"noFileSelected": "Ingen fil vald.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Hämta CSV",
|
"downloadCsv": "Hämta CSV",
|
||||||
"eventsRecorded": "Registrerade {count} händelser med nätbegränsning.",
|
"eventsRecorded": "Registrerade {count} händelser med nätbegränsning.",
|
||||||
"lastEvent": "Senaste {timeAgo}.",
|
"lastEvent": "Senaste {timeAgo}.",
|
||||||
"title": "Extern gräns",
|
"title": "Extern gräns"
|
||||||
"yamlConfigured": "Obs: Konfigurerad via evcc.yaml. Ta bort posten från filen för att aktivera redigering här."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "ändra",
|
"change": "ändra",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "ej konfigurerad",
|
"unconfigured": "ej konfigurerad",
|
||||||
"vehicles": "Mina fordon",
|
"vehicles": "Mina fordon",
|
||||||
"welcomeBannerText": "Börja med att skapa minst en **laddare**, **värmare**, **nät**, **solcell**, **batteri** eller **extra mätare**. Om du bara vill testa, välj en **demoenhet**.",
|
"welcomeBannerText": "Börja med att skapa minst en **laddare**, **värmare**, **nät**, **solcell**, **batteri** eller **extra mätare**. Om du bara vill testa, välj en **demoenhet**.",
|
||||||
"welcomeBannerTitle": "Låt oss konfigurera ditt system!",
|
"welcomeBannerTitle": "Låt oss konfigurera ditt system!"
|
||||||
"yaml": "Konfigurera i evcc.yaml. Kan inte ändras i UI."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Få notiser om dina laddningar.",
|
"description": "Få notiser om dina laddningar.",
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@
|
||||||
"delete": "நீக்கு",
|
"delete": "நீக்கு",
|
||||||
"docsLink": "ஆவணங்களைக் காண்க.",
|
"docsLink": "ஆவணங்களைக் காண்க.",
|
||||||
"experimental": "சோதனை",
|
"experimental": "சோதனை",
|
||||||
|
"fromYamlHint": "Evcc.yaml இல் கட்டமைக்கப்பட்டுள்ளது. இடைமுகம் இல் திருத்த முடியாது.",
|
||||||
"hideAdvancedSettings": "மேம்பட்ட அமைப்புகளை மறைக்க",
|
"hideAdvancedSettings": "மேம்பட்ட அமைப்புகளை மறைக்க",
|
||||||
"invalidFileSelected": "தவறான கோப்பு தேர்ந்தெடுக்கப்பட்டது",
|
"invalidFileSelected": "தவறான கோப்பு தேர்ந்தெடுக்கப்பட்டது",
|
||||||
"noFileSelected": "எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை.",
|
"noFileSelected": "எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை.",
|
||||||
|
|
@ -314,8 +315,7 @@
|
||||||
"name": "பெயர்",
|
"name": "பெயர்",
|
||||||
"title": "உள்ளமைவு",
|
"title": "உள்ளமைவு",
|
||||||
"unconfigured": "கட்டமைக்கப்படவில்லை",
|
"unconfigured": "கட்டமைக்கப்படவில்லை",
|
||||||
"vehicles": "என் வாகனங்கள்",
|
"vehicles": "என் வாகனங்கள்"
|
||||||
"yaml": "Evcc.yaml இல் கட்டமைக்கப்பட்டுள்ளது. இடைமுகம் இல் திருத்த முடியாது."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "உங்கள் சார்சிங் அமர்வுகள் பற்றிய செய்திகளைப் பெறுங்கள்.",
|
"description": "உங்கள் சார்சிங் அமர்வுகள் பற்றிய செய்திகளைப் பெறுங்கள்.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Yeniden sıralanabilir liste",
|
"dragList": "Yeniden sıralanabilir liste",
|
||||||
"experimental": "Deneysel",
|
"experimental": "Deneysel",
|
||||||
"forceSave": "Yine de kaydet",
|
"forceSave": "Yine de kaydet",
|
||||||
|
"fromYamlHint": "Not: evcc.yaml aracılığıyla yapılandırıldı. Burada düzenlemeyi etkinleştirmek için dosyadan öğeyi kaldır.",
|
||||||
"hideAdvancedSettings": "“Gelişmiş ayarları gizle”",
|
"hideAdvancedSettings": "“Gelişmiş ayarları gizle”",
|
||||||
"invalidFileSelected": "Geçersiz dosya seçildi",
|
"invalidFileSelected": "Geçersiz dosya seçildi",
|
||||||
"noFileSelected": "Seçili dosya yok.",
|
"noFileSelected": "Seçili dosya yok.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "CSV'yi indir",
|
"downloadCsv": "CSV'yi indir",
|
||||||
"eventsRecorded": "{count} şebeke sınırlama etkinliği kaydedildi.",
|
"eventsRecorded": "{count} şebeke sınırlama etkinliği kaydedildi.",
|
||||||
"lastEvent": "En son {timeAgo}.",
|
"lastEvent": "En son {timeAgo}.",
|
||||||
"title": "Dış Limit",
|
"title": "Dış Limit"
|
||||||
"yamlConfigured": "Not: evcc.yaml aracılığıyla yapılandırıldı. Burada düzenlemeyi etkinleştirmek için dosyadan öğeyi kaldır."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "değiştir",
|
"change": "değiştir",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "yapılandırılmadı",
|
"unconfigured": "yapılandırılmadı",
|
||||||
"vehicles": "Araçlarım",
|
"vehicles": "Araçlarım",
|
||||||
"welcomeBannerText": "Önce bir **doldurma cihazı**, **ısıtıcı**, **elektrik sayacı**, **güneş enerjisi**, **batarya** veya **ek sayaç** oluştur. Sadece denemek istiyorsan, bir **demo cihazı** seç.",
|
"welcomeBannerText": "Önce bir **doldurma cihazı**, **ısıtıcı**, **elektrik sayacı**, **güneş enerjisi**, **batarya** veya **ek sayaç** oluştur. Sadece denemek istiyorsan, bir **demo cihazı** seç.",
|
||||||
"welcomeBannerTitle": "Haydi sistemini yapılandıralım!",
|
"welcomeBannerTitle": "Haydi sistemini yapılandıralım!"
|
||||||
"yaml": "evcc.yaml'daki cihazlar düzenlenemez."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Dolum oturumları hakkında bildirimler al.",
|
"description": "Dolum oturumları hakkında bildirimler al.",
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@
|
||||||
"dragList": "Список, який можна перевпорядкувати",
|
"dragList": "Список, який можна перевпорядкувати",
|
||||||
"experimental": "Експериментальний",
|
"experimental": "Експериментальний",
|
||||||
"forceSave": "Зберегти все одно",
|
"forceSave": "Зберегти все одно",
|
||||||
|
"fromYamlHint": "Примітка: Налаштовано через evcc.yaml. Видаліть запис із файлу, щоб дозволити редагування тут.",
|
||||||
"hideAdvancedSettings": "Приховати розширені налаштування",
|
"hideAdvancedSettings": "Приховати розширені налаштування",
|
||||||
"invalidFileSelected": "Вибрано недійсний файл",
|
"invalidFileSelected": "Вибрано недійсний файл",
|
||||||
"noFileSelected": "Не вибрано файл.",
|
"noFileSelected": "Не вибрано файл.",
|
||||||
|
|
@ -247,8 +248,7 @@
|
||||||
"downloadCsv": "Завантажити CSV-файл",
|
"downloadCsv": "Завантажити CSV-файл",
|
||||||
"eventsRecorded": "Записано {count} подій обмеження мережі.",
|
"eventsRecorded": "Записано {count} подій обмеження мережі.",
|
||||||
"lastEvent": "Найновіші {timeAgo}.",
|
"lastEvent": "Найновіші {timeAgo}.",
|
||||||
"title": "Зовнішнє обмеження",
|
"title": "Зовнішнє обмеження"
|
||||||
"yamlConfigured": "Примітка: Налаштовано через evcc.yaml. Видаліть запис із файлу, щоб дозволити редагування тут."
|
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"change": "зміна",
|
"change": "зміна",
|
||||||
|
|
@ -390,8 +390,7 @@
|
||||||
"unconfigured": "не налаштовано",
|
"unconfigured": "не налаштовано",
|
||||||
"vehicles": "Мій транспорт",
|
"vehicles": "Мій транспорт",
|
||||||
"welcomeBannerText": "Почніть зі створення принаймні одного **зарядного пристрою**, **обігрівача**, **мережевої**, **сонячної батареї**, **акумулятора** або **додаткового лічильника**. Якщо ви просто хочете протестувати, виберіть **демонстраційний пристрій**.",
|
"welcomeBannerText": "Почніть зі створення принаймні одного **зарядного пристрою**, **обігрівача**, **мережевої**, **сонячної батареї**, **акумулятора** або **додаткового лічильника**. Якщо ви просто хочете протестувати, виберіть **демонстраційний пристрій**.",
|
||||||
"welcomeBannerTitle": "Давайте налаштуємо вашу систему!",
|
"welcomeBannerTitle": "Давайте налаштуємо вашу систему!"
|
||||||
"yaml": "Пристрої з evcc.yaml не можна редагувати."
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "Отримувати повідомлення про ваші сеанси зарядки.",
|
"description": "Отримувати повідомлення про ваші сеанси зарядки.",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"docsLink": "查看文档。",
|
"docsLink": "查看文档。",
|
||||||
"experimental": "实验性",
|
"experimental": "实验性",
|
||||||
|
"fromYamlHint": "evcc.yaml 中的设备不可编辑。",
|
||||||
"hideAdvancedSettings": "隐藏高级设置",
|
"hideAdvancedSettings": "隐藏高级设置",
|
||||||
"off": "关",
|
"off": "关",
|
||||||
"on": "开",
|
"on": "开",
|
||||||
|
|
@ -306,8 +307,7 @@
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"title": "配置",
|
"title": "配置",
|
||||||
"unconfigured": "未配置",
|
"unconfigured": "未配置",
|
||||||
"vehicles": "我的车辆",
|
"vehicles": "我的车辆"
|
||||||
"yaml": "evcc.yaml 中的设备不可编辑。"
|
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"description": "接收有关您充电会话及其他事件的消息。",
|
"description": "接收有关您充电会话及其他事件的消息。",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -34,7 +32,6 @@ import (
|
||||||
"github.com/enbility/spine-go/model"
|
"github.com/enbility/spine-go/model"
|
||||||
"github.com/enbility/spine-go/spine"
|
"github.com/enbility/spine-go/spine"
|
||||||
"github.com/evcc-io/evcc/util"
|
"github.com/evcc-io/evcc/util"
|
||||||
"github.com/evcc-io/evcc/util/machine"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device interface {
|
type Device interface {
|
||||||
|
|
@ -82,29 +79,33 @@ type EEBus struct {
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
log *util.Logger
|
log *util.Logger
|
||||||
|
|
||||||
Ski string
|
ski string
|
||||||
|
|
||||||
clients map[string][]Device
|
clients map[string][]Device
|
||||||
}
|
}
|
||||||
|
|
||||||
var Instance *EEBus
|
var Instance *EEBus
|
||||||
|
|
||||||
|
func GetStatus() any {
|
||||||
|
return struct {
|
||||||
|
Ski string `json:"ski"`
|
||||||
|
}{
|
||||||
|
Ski: Ski(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewServer(other Config) (*EEBus, error) {
|
func NewServer(other Config) (*EEBus, error) {
|
||||||
cc := Config{
|
cc := Config{
|
||||||
URI: ":4712",
|
Port: 4712,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mergo.Merge(&cc, other, mergo.WithOverride); err != nil {
|
if err := mergo.Merge(&cc, other, mergo.WithOverride); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log := util.NewLogger("eebus")
|
serial := cc.ShipID
|
||||||
|
if serial == "" {
|
||||||
protectedID := machine.ProtectedID("evcc-eebus")
|
serial = createShipID()
|
||||||
serial := fmt.Sprintf("%s-%0x", "EVCC", protectedID[:8])
|
|
||||||
|
|
||||||
if len(cc.ShipID) != 0 {
|
|
||||||
serial = cc.ShipID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate, err := tls.X509KeyPair([]byte(cc.Certificate.Public), []byte(cc.Certificate.Private))
|
certificate, err := tls.X509KeyPair([]byte(cc.Certificate.Public), []byte(cc.Certificate.Private))
|
||||||
|
|
@ -112,22 +113,11 @@ func NewServer(other Config) (*EEBus, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, portValue, err := net.SplitHostPort(cc.URI)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := strconv.Atoi(portValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: get the voltage from the site
|
|
||||||
configuration, err := eebusapi.NewConfiguration(
|
configuration, err := eebusapi.NewConfiguration(
|
||||||
BrandName, BrandName, Model, serial,
|
BrandName, BrandName, Model, serial,
|
||||||
model.DeviceTypeTypeEnergyManagementSystem,
|
model.DeviceTypeTypeEnergyManagementSystem,
|
||||||
[]model.EntityTypeType{model.EntityTypeTypeCEM},
|
[]model.EntityTypeType{model.EntityTypeTypeCEM},
|
||||||
port, certificate, time.Second*4,
|
cc.Port, certificate, time.Second*4,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -147,8 +137,8 @@ func NewServer(other Config) (*EEBus, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &EEBus{
|
c := &EEBus{
|
||||||
log: log,
|
log: util.NewLogger("eebus"),
|
||||||
Ski: ski,
|
ski: ski,
|
||||||
clients: make(map[string][]Device),
|
clients: make(map[string][]Device),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,7 +216,7 @@ func (c *EEBus) RegisterDevice(ski, ip string, device Device) error {
|
||||||
ski = shiputil.NormalizeSKI(ski)
|
ski = shiputil.NormalizeSKI(ski)
|
||||||
c.log.TRACE.Printf("registering ski: %s", ski)
|
c.log.TRACE.Printf("registering ski: %s", ski)
|
||||||
|
|
||||||
if ski == c.Ski {
|
if ski == c.ski {
|
||||||
return errors.New("device ski can not be identical to host ski")
|
return errors.New("device ski can not be identical to host ski")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,13 @@ func TestEEBus(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
require.NoError(t, err, "server")
|
require.NoError(t, err, "server")
|
||||||
require.NotEmpty(t, srv.Ski, "server ski")
|
|
||||||
|
|
||||||
server.Instance = srv
|
server.Instance = srv
|
||||||
go srv.Run()
|
go srv.Run()
|
||||||
|
|
||||||
box, err := createControlbox(t.Context(), server.Instance.Ski, remotePort)
|
require.NotEmpty(t, server.Ski(), "server ski")
|
||||||
|
|
||||||
|
box, err := createControlbox(t.Context(), server.Ski(), remotePort)
|
||||||
require.NoError(t, err, "controlbox")
|
require.NoError(t, err, "controlbox")
|
||||||
|
|
||||||
eventC := make(chan api.EventType, 1)
|
eventC := make(chan api.EventType, 1)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
package eebus
|
package eebus
|
||||||
|
|
||||||
import "github.com/evcc-io/evcc/util"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/util"
|
||||||
|
"github.com/evcc-io/evcc/util/machine"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BrandName string = "EVCC"
|
BrandName string = "EVCC"
|
||||||
|
|
@ -11,17 +16,69 @@ const (
|
||||||
var DeviceCode = util.Getenv("EEBUS_DEVICE_CODE", "EVCC_HEMS_01")
|
var DeviceCode = util.Getenv("EEBUS_DEVICE_CODE", "EVCC_HEMS_01")
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
Public, Private string
|
Public string `json:"public"`
|
||||||
|
Private string `json:"private"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
URI string
|
URI_ string `mapstructure:"uri" json:"uri"` // TODO deprecated
|
||||||
ShipID string
|
Port int `json:"port"`
|
||||||
Interfaces []string
|
ShipID string `json:"shipid"`
|
||||||
Certificate Certificate
|
Interfaces []string `json:"interfaces,omitempty"`
|
||||||
|
Certificate Certificate `json:"certificate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configured returns true if the EEbus server is configured
|
// IsConfigured returns true if the EEbus server is configured
|
||||||
func (c Config) Configured() bool {
|
func (c Config) IsConfigured() bool {
|
||||||
return len(c.Certificate.Public) > 0 && len(c.Certificate.Private) > 0
|
return len(c.Certificate.Public) > 0 && len(c.Certificate.Private) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redacted implements the redactor interface used by the tee publisher
|
||||||
|
func (c Config) Redacted() any {
|
||||||
|
return Config{
|
||||||
|
URI_: c.URI_,
|
||||||
|
Port: c.Port,
|
||||||
|
ShipID: c.ShipID,
|
||||||
|
Interfaces: c.Interfaces,
|
||||||
|
Certificate: Certificate{
|
||||||
|
Public: c.Certificate.Public,
|
||||||
|
Private: util.Masked(c.Certificate.Private),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createShipID() string {
|
||||||
|
protectedID := machine.ProtectedID("evcc-eebus")
|
||||||
|
return fmt.Sprintf("%s-%0x", "EVCC", protectedID[:8])
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultConfig(conf *Config) (*Config, error) {
|
||||||
|
cert, err := CreateCertificate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
public, private, err := GetX509KeyPair(cert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := Config{
|
||||||
|
Port: 4712,
|
||||||
|
ShipID: createShipID(),
|
||||||
|
Certificate: Certificate{
|
||||||
|
Public: public,
|
||||||
|
Private: private,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ski returns the EEbus server SKI
|
||||||
|
func Ski() string {
|
||||||
|
if Instance == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Instance.ski
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,6 @@ func (s *HTTPd) RegisterSystemHandler(site *core.Site, pub publisher, cache *uti
|
||||||
|
|
||||||
// yaml handlers
|
// yaml handlers
|
||||||
for key, fun := range map[string]func() (any, any){
|
for key, fun := range map[string]func() (any, any){
|
||||||
keys.EEBus: func() (any, any) { return map[string]any{}, eebus.Config{} },
|
|
||||||
keys.Hems: func() (any, any) { return map[string]any{}, config.Typed{} },
|
keys.Hems: func() (any, any) { return map[string]any{}, config.Typed{} },
|
||||||
keys.Tariffs: func() (any, any) { return map[string]any{}, globalconfig.Tariffs{} },
|
keys.Tariffs: func() (any, any) { return map[string]any{}, globalconfig.Tariffs{} },
|
||||||
keys.Messaging: func() (any, any) { return map[string]any{}, globalconfig.Messaging{} }, // has default
|
keys.Messaging: func() (any, any) { return map[string]any{}, globalconfig.Messaging{} }, // has default
|
||||||
|
|
@ -316,6 +315,7 @@ func (s *HTTPd) RegisterSystemHandler(site *core.Site, pub publisher, cache *uti
|
||||||
keys.ModbusProxy: func() any { return new([]globalconfig.ModbusProxy) }, // slice
|
keys.ModbusProxy: func() any { return new([]globalconfig.ModbusProxy) }, // slice
|
||||||
keys.Shm: func() any { return new(shm.Config) },
|
keys.Shm: func() any { return new(shm.Config) },
|
||||||
keys.Influx: func() any { return new(globalconfig.Influx) },
|
keys.Influx: func() any { return new(globalconfig.Influx) },
|
||||||
|
keys.EEBus: func() any { return new(eebus.Config) },
|
||||||
} {
|
} {
|
||||||
routes["update"+key] = route{Method: "POST", Pattern: "/" + key, HandlerFunc: settingsSetJsonHandler(key, pub, fun)}
|
routes["update"+key] = route{Method: "POST", Pattern: "/" + key, HandlerFunc: settingsSetJsonHandler(key, pub, fun)}
|
||||||
routes["delete"+key] = route{Method: "DELETE", Pattern: "/" + key, HandlerFunc: settingsDeleteJsonHandler(key, pub, fun())}
|
routes["delete"+key] = route{Method: "DELETE", Pattern: "/" + key, HandlerFunc: settingsDeleteJsonHandler(key, pub, fun())}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/api/globalconfig"
|
||||||
"github.com/evcc-io/evcc/core/keys"
|
"github.com/evcc-io/evcc/core/keys"
|
||||||
"github.com/evcc-io/evcc/server/db/settings"
|
"github.com/evcc-io/evcc/server/db/settings"
|
||||||
"github.com/evcc-io/evcc/util/sponsor"
|
"github.com/evcc-io/evcc/util/sponsor"
|
||||||
|
|
@ -26,11 +27,8 @@ func updateSponsortokenHandler(pub publisher) func(w http.ResponseWriter, r *htt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(keys.Sponsor, struct {
|
pub(keys.Sponsor, globalconfig.ConfigStatus{
|
||||||
Status sponsor.Status `json:"status"`
|
Status: sponsor.RedactedStatus(),
|
||||||
FromYaml bool `json:"fromYaml"`
|
|
||||||
}{
|
|
||||||
Status: sponsor.GetStatus(),
|
|
||||||
FromYaml: false,
|
FromYaml: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +37,7 @@ func updateSponsortokenHandler(pub publisher) func(w http.ResponseWriter, r *htt
|
||||||
settings.SetString(keys.SponsorToken, req.Token)
|
settings.SetString(keys.SponsorToken, req.Token)
|
||||||
setConfigDirty()
|
setConfigDirty()
|
||||||
|
|
||||||
jsonWrite(w, sponsor.GetStatus())
|
jsonWrite(w, sponsor.RedactedStatus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,10 +45,7 @@ func deleteSponsorTokenHandler(pub publisher) func(w http.ResponseWriter, r *htt
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
settings.SetString(keys.SponsorToken, "")
|
settings.SetString(keys.SponsorToken, "")
|
||||||
|
|
||||||
pub(keys.Sponsor, struct {
|
pub(keys.Sponsor, globalconfig.ConfigStatus{
|
||||||
Status sponsor.Status `json:"status"`
|
|
||||||
FromYaml bool `json:"fromYaml"`
|
|
||||||
}{
|
|
||||||
Status: sponsor.Status{},
|
Status: sponsor.Status{},
|
||||||
FromYaml: false,
|
FromYaml: false,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/core/keys"
|
||||||
"github.com/evcc-io/evcc/server/db/settings"
|
"github.com/evcc-io/evcc/server/db/settings"
|
||||||
"github.com/evcc-io/evcc/util/redact"
|
"github.com/evcc-io/evcc/util/redact"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
@ -80,6 +81,12 @@ func settingsSetYamlHandler(key string, other, struc any) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allowPub(key string) bool {
|
||||||
|
// don't publish on update - would overwrite globalconfig.Info struct with config
|
||||||
|
// TODO come up with a general solution once all endpoinds use Info
|
||||||
|
return key != keys.EEBus
|
||||||
|
}
|
||||||
|
|
||||||
func settingsSetJsonHandler(key string, pub publisher, newStruc func() any) http.HandlerFunc {
|
func settingsSetJsonHandler(key string, pub publisher, newStruc func() any) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
struc := newStruc()
|
struc := newStruc()
|
||||||
|
|
@ -104,7 +111,9 @@ func settingsSetJsonHandler(key string, pub publisher, newStruc func() any) http
|
||||||
settings.SetJson(key, struc)
|
settings.SetJson(key, struc)
|
||||||
setConfigDirty()
|
setConfigDirty()
|
||||||
|
|
||||||
pub(key, struc)
|
if allowPub(key) {
|
||||||
|
pub(key, struc)
|
||||||
|
}
|
||||||
|
|
||||||
jsonWrite(w, true)
|
jsonWrite(w, true)
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +124,9 @@ func settingsDeleteJsonHandler(key string, pub publisher, struc any) http.Handle
|
||||||
settings.SetString(key, "")
|
settings.SetString(key, "")
|
||||||
setConfigDirty()
|
setConfigDirty()
|
||||||
|
|
||||||
pub(key, struc)
|
if allowPub(key) {
|
||||||
|
pub(key, struc)
|
||||||
|
}
|
||||||
|
|
||||||
jsonWrite(w, true)
|
jsonWrite(w, true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
tests/config-eebus.evcc.yaml
Normal file
27
tests/config-eebus.evcc.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
interval: 0.1s
|
||||||
|
|
||||||
|
eebus:
|
||||||
|
shipid: EVCC_HEMS_01
|
||||||
|
certificate:
|
||||||
|
public: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBuzCCAWKgAwIBAgIQatuHkiaPT4obu8RHSmfOwzAKBggqhkjOPQQDAjA+MQsw
|
||||||
|
CQYDVQQGEwJERTENMAsGA1UEChMERVZDQzEJMAcGA1UECxMAMRUwEwYDVQQDDAxF
|
||||||
|
VkNDX0hFTVNfMDEwHhcNMjYwMTMwMTAzNjUzWhcNMzYwMTI4MTAzNjUzWjA+MQsw
|
||||||
|
CQYDVQQGEwJERTENMAsGA1UEChMERVZDQzEJMAcGA1UECxMAMRUwEwYDVQQDDAxF
|
||||||
|
VkNDX0hFTVNfMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ8Z40GPe8HzV17
|
||||||
|
ZhbBPkGBwhu8emdvzO5OWJUPSaX1R8rrWSaNwlh6COPG3fOVvUzCYcQbgmMFXK/o
|
||||||
|
nhuAslXTo0IwQDAOBgNVHQ8BAf8EBAMCB4AwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
|
||||||
|
HQ4EFgQUdQegutt1VxBfwDAlPdBpvrp7ztowCgYIKoZIzj0EAwIDRwAwRAIgS2ia
|
||||||
|
A+q0Tiy7NLyEjuKCV8QCVPjxt2OnfbSb9ZlDT3YCIDkwJoLc4+lsfvidDimUB+/I
|
||||||
|
ZvF1asyp3PVL7BDSFHil
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
private: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIIVZ2cLeTiENyCchgpj7eyJ5JKP5qpgj8eHUbLxn8Du1oAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEPGeNBj3vB81de2YWwT5BgcIbvHpnb8zuTliVD0ml9UfK61kmjcJY
|
||||||
|
egjjxt3zlb1MwmHEG4JjBVyv6J4bgLJV0w==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
circuits:
|
||||||
|
- name: main
|
||||||
137
tests/config-eebus.spec.ts
Normal file
137
tests/config-eebus.spec.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import { start, stop, baseUrl, restart } from "./evcc";
|
||||||
|
import { expectModalHidden, expectModalVisible } from "./utils";
|
||||||
|
|
||||||
|
test.use({ baseURL: baseUrl() });
|
||||||
|
|
||||||
|
test.afterEach(async () => {
|
||||||
|
await stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("eebus", async () => {
|
||||||
|
test("first start, auto-configured", async ({ page }) => {
|
||||||
|
await start();
|
||||||
|
await page.goto("/#/config");
|
||||||
|
|
||||||
|
await page.getByTestId("eebus").getByRole("button", { name: "edit" }).click();
|
||||||
|
const modal = page.getByTestId("eebus-modal");
|
||||||
|
await expectModalVisible(modal);
|
||||||
|
|
||||||
|
await expect(modal.getByLabel("SHIP-ID")).not.toBeEmpty();
|
||||||
|
await expect(modal.getByLabel("SKI")).not.toBeEmpty();
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Show advanced settings" }).click();
|
||||||
|
await expect(modal.getByLabel("Port")).toHaveValue("4712");
|
||||||
|
await expect(modal.getByLabel("Interfaces")).toBeVisible();
|
||||||
|
await expect(modal.getByLabel("Public certificate")).not.toBeEmpty();
|
||||||
|
await expect(modal.getByLabel("Private key")).toHaveValue("***");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("change values", async ({ page }) => {
|
||||||
|
await start();
|
||||||
|
await page.goto("/#/config");
|
||||||
|
|
||||||
|
const eebusCard = page.getByTestId("eebus");
|
||||||
|
|
||||||
|
await eebusCard.getByRole("button", { name: "edit" }).click();
|
||||||
|
const modal = page.getByTestId("eebus-modal");
|
||||||
|
await expectModalVisible(modal);
|
||||||
|
|
||||||
|
const shipidReadonly = modal.locator("#eebus-shipid-display");
|
||||||
|
const ski = modal.getByLabel("SKI");
|
||||||
|
const shipidEditable = modal.locator("#eebus-shipid");
|
||||||
|
const port = modal.getByLabel("Port");
|
||||||
|
const interfaces = modal.getByLabel("Interfaces");
|
||||||
|
const publicCertificate = modal.getByLabel("Public certificate");
|
||||||
|
const privateKey = modal.getByLabel("Private key");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Show advanced settings" }).click();
|
||||||
|
await shipidEditable.fill("EVCC-1234");
|
||||||
|
await port.fill("4321");
|
||||||
|
await interfaces.fill("eth0");
|
||||||
|
await publicCertificate.fill("public certificate");
|
||||||
|
await privateKey.fill("private key");
|
||||||
|
|
||||||
|
// validate connection
|
||||||
|
await modal.getByRole("button", { name: "Save" }).click();
|
||||||
|
await expectModalHidden(modal);
|
||||||
|
|
||||||
|
// restart button appears
|
||||||
|
const restartButton = page
|
||||||
|
.getByTestId("bottom-banner")
|
||||||
|
.getByRole("button", { name: "Restart" });
|
||||||
|
await expect(restartButton).toBeVisible();
|
||||||
|
|
||||||
|
await restart();
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await eebusCard.getByRole("button", { name: "edit" }).click();
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Show advanced settings" }).click();
|
||||||
|
await expect(shipidReadonly).toHaveValue("EVCC-1234");
|
||||||
|
await expect(ski).not.toBeVisible();
|
||||||
|
await expect(shipidEditable).toHaveValue("EVCC-1234");
|
||||||
|
await expect(port).toHaveValue("4321");
|
||||||
|
await expect(interfaces).toHaveValue("eth0");
|
||||||
|
await expect(publicCertificate).toHaveValue("public certificate");
|
||||||
|
await expect(privateKey).toHaveValue("***");
|
||||||
|
});
|
||||||
|
test("delete: ski changes", async ({ page }) => {
|
||||||
|
await start();
|
||||||
|
await page.goto("/#/config");
|
||||||
|
|
||||||
|
const eebusCard = page.getByTestId("eebus");
|
||||||
|
|
||||||
|
await page.getByTestId("eebus").getByRole("button", { name: "edit" }).click();
|
||||||
|
const modal = page.getByTestId("eebus-modal");
|
||||||
|
await expectModalVisible(modal);
|
||||||
|
|
||||||
|
// remember ski
|
||||||
|
const ski = modal.getByLabel("SKI");
|
||||||
|
const skiValue = await ski.inputValue();
|
||||||
|
|
||||||
|
page.on("dialog", async (dialog) => await dialog.accept());
|
||||||
|
await modal.getByRole("button", { name: "Remove" }).click();
|
||||||
|
|
||||||
|
// restart button appears
|
||||||
|
const restartButton = page
|
||||||
|
.getByTestId("bottom-banner")
|
||||||
|
.getByRole("button", { name: "Restart" });
|
||||||
|
await expect(restartButton).toBeVisible();
|
||||||
|
|
||||||
|
await restart();
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await eebusCard.getByRole("button", { name: "edit" }).click();
|
||||||
|
|
||||||
|
await expect(ski).not.toHaveValue(skiValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("configured in yaml", async ({ page }) => {
|
||||||
|
await start("config-eebus.evcc.yaml");
|
||||||
|
await page.goto("/#/config");
|
||||||
|
|
||||||
|
await page.getByTestId("eebus").getByRole("button", { name: "edit" }).click();
|
||||||
|
const modal = page.getByTestId("eebus-modal");
|
||||||
|
await expectModalVisible(modal);
|
||||||
|
|
||||||
|
// yaml configuration note is visible
|
||||||
|
await expect(modal.getByText("Configured via evcc.yaml", { exact: false })).toBeVisible();
|
||||||
|
|
||||||
|
// SHIP-ID readonly field is visible with configured value
|
||||||
|
await expect(modal.getByLabel("SHIP-ID")).toBeVisible();
|
||||||
|
await expect(modal.getByLabel("SHIP-ID")).toHaveValue("EVCC_HEMS_01");
|
||||||
|
await expect(modal.getByLabel("SHIP-ID")).toHaveAttribute("readonly");
|
||||||
|
|
||||||
|
// SKI readonly field is visible
|
||||||
|
await expect(modal.getByLabel("SKI")).toBeVisible();
|
||||||
|
await expect(modal.getByLabel("SKI")).toHaveAttribute("readonly");
|
||||||
|
|
||||||
|
// no advanced settings button
|
||||||
|
await expect(modal.getByRole("button", { name: "Show advanced settings" })).not.toBeVisible();
|
||||||
|
|
||||||
|
// no action buttons
|
||||||
|
await expect(modal.getByRole("button", { name: "Save" })).not.toBeVisible();
|
||||||
|
await expect(modal.getByRole("button", { name: "Remove" })).not.toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -25,7 +25,6 @@ test.describe("ocpp", () => {
|
||||||
|
|
||||||
// Open OCPP card
|
// Open OCPP card
|
||||||
const ocppCard = page.getByTestId("ocpp");
|
const ocppCard = page.getByTestId("ocpp");
|
||||||
await expect(ocppCard).toContainText(["Configured", "no"].join(""));
|
|
||||||
await ocppCard.getByRole("button", { name: "edit" }).click();
|
await ocppCard.getByRole("button", { name: "edit" }).click();
|
||||||
|
|
||||||
const ocppModal = page.getByTestId("ocpp-modal");
|
const ocppModal = page.getByTestId("ocpp-modal");
|
||||||
|
|
@ -44,7 +43,6 @@ test.describe("ocpp", () => {
|
||||||
|
|
||||||
// Navigate back to evcc config page
|
// Navigate back to evcc config page
|
||||||
await page.goto("/#/config");
|
await page.goto("/#/config");
|
||||||
await expect(ocppCard).toContainText(["Connections", "0/0", "Detected", "1"].join(""));
|
|
||||||
await ocppCard.getByRole("button", { name: "edit" }).click();
|
await ocppCard.getByRole("button", { name: "edit" }).click();
|
||||||
await expectModalVisible(ocppModal);
|
await expectModalVisible(ocppModal);
|
||||||
await expect(ocppModal).toContainText("Detected station IDs");
|
await expect(ocppModal).toContainText("Detected station IDs");
|
||||||
|
|
|
||||||
8
util/redact.go
Normal file
8
util/redact.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
func Masked(s any) string {
|
||||||
|
if s != "" {
|
||||||
|
return "***"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
@ -119,8 +119,8 @@ type Status struct {
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatus returns the sponsorship status
|
// RedactedStatus returns the sponsorship status
|
||||||
func GetStatus() Status {
|
func RedactedStatus() Status {
|
||||||
mu.RLock()
|
mu.RLock()
|
||||||
defer mu.RUnlock()
|
defer mu.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue