201 lines
5.5 KiB
Vue
201 lines
5.5 KiB
Vue
<template>
|
|
<DeviceModalBase
|
|
:id="id"
|
|
ref="deviceModal"
|
|
name="hems"
|
|
device-type="hems"
|
|
:modal-title="$t('config.hems.title')"
|
|
:provide-template-options="provideTemplateOptions"
|
|
:initial-values="initialValues"
|
|
:is-yaml-input-type="isYamlInputType"
|
|
:on-template-change="handleTemplateChange"
|
|
:hide-template-fields="fromYaml"
|
|
:hide-delete="true"
|
|
:hide-info="true"
|
|
:keep-open-on-remove="true"
|
|
@added="onAdded"
|
|
@updated="onUpdated"
|
|
@removed="onRemoved"
|
|
@open="loadSessions"
|
|
@close="$emit('close')"
|
|
>
|
|
<template v-if="id !== undefined && !fromYaml" #template-action>
|
|
<button
|
|
type="button"
|
|
class="btn btn-outline-secondary border-0"
|
|
:aria-label="$t('config.general.change')"
|
|
:title="$t('config.general.change')"
|
|
:disabled="changing"
|
|
@click="handleChange"
|
|
>
|
|
<shopicon-regular-edit size="s" class="flex-shrink-0"></shopicon-regular-edit>
|
|
</button>
|
|
</template>
|
|
<template #description>
|
|
<p class="mt-0 mb-4">
|
|
{{ $t("config.hems.description") }}
|
|
<a :href="docsLink" target="_blank" rel="noopener">
|
|
{{ $t("config.general.docsLink") }}
|
|
</a>
|
|
</p>
|
|
<div v-if="configured" class="mb-4" data-testid="grid-sessions">
|
|
<h6 class="mb-3">{{ $t("config.hems.recordedEvents") }}</h6>
|
|
<div class="events-box rounded p-3">
|
|
<p v-if="!sessionCount" class="mb-0 text-muted">
|
|
{{ $t("config.hems.noEvents") }}
|
|
</p>
|
|
<template v-else>
|
|
<p class="mb-3">
|
|
{{ $t("config.hems.eventsRecorded", { count: sessionCount }) }}<br />
|
|
<template v-if="lastEventTimeAgo">
|
|
{{ $t("config.hems.lastEvent", { timeAgo: lastEventTimeAgo }) }}
|
|
</template>
|
|
</p>
|
|
<DownloadButton :label="$t('general.download')" :href="downloadHref()" />
|
|
</template>
|
|
</div>
|
|
<hr class="mt-4 mb-0" />
|
|
</div>
|
|
<p v-if="fromYaml" class="text-muted">
|
|
{{ $t("config.general.fromYamlHint") }}
|
|
</p>
|
|
</template>
|
|
</DeviceModalBase>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, type PropType } from "vue";
|
|
import "@h2d2/shopicons/es/regular/edit";
|
|
import DeviceModalBase from "./DeviceModal/DeviceModalBase.vue";
|
|
import { ConfigType, type YamlSource } from "@/types/evcc";
|
|
import { type DeviceValues } from "./DeviceModal";
|
|
import { customTemplateOption, type TemplateGroup } from "./DeviceModal/TemplateSelector.vue";
|
|
import customHemsYaml from "./defaultYaml/customHems.yaml?raw";
|
|
import relayHemsYaml from "./defaultYaml/relayHems.yaml?raw";
|
|
import api from "../../api";
|
|
import { docsPrefix } from "@/i18n";
|
|
import DownloadButton from "../Helper/DownloadButton.vue";
|
|
import formatter from "../../mixins/formatter";
|
|
|
|
// selector value for the relay variant; both variants save as type custom
|
|
const RELAY_OPTION = "relay";
|
|
|
|
const initialValues = {
|
|
type: ConfigType.Template,
|
|
icon: undefined,
|
|
deviceProduct: undefined,
|
|
yaml: undefined,
|
|
template: null,
|
|
};
|
|
|
|
export default defineComponent({
|
|
name: "HemsModal",
|
|
components: { DeviceModalBase, DownloadButton },
|
|
mixins: [formatter],
|
|
props: {
|
|
yamlSource: String as PropType<YamlSource>,
|
|
id: Number as PropType<number | undefined>,
|
|
},
|
|
emits: ["changed", "close"],
|
|
data() {
|
|
return {
|
|
initialValues,
|
|
sessions: [] as Array<{ created: string }>,
|
|
changing: false,
|
|
};
|
|
},
|
|
computed: {
|
|
fromYaml(): boolean {
|
|
return this.yamlSource === "file";
|
|
},
|
|
configured(): boolean {
|
|
return this.id !== undefined || this.fromYaml;
|
|
},
|
|
sessionCount(): number {
|
|
return this.sessions.length;
|
|
},
|
|
lastEvent() {
|
|
return this.sessions[0] ?? null;
|
|
},
|
|
lastEventTimeAgo(): string {
|
|
const created = this.lastEvent?.created;
|
|
if (!created) return "";
|
|
const ms = new Date(created).getTime();
|
|
if (!Number.isFinite(ms)) return "";
|
|
return (this as any).fmtTimeAgo(ms - Date.now());
|
|
},
|
|
docsLink(): string {
|
|
return `${docsPrefix()}/docs/external-limit`;
|
|
},
|
|
},
|
|
methods: {
|
|
downloadHref(): string {
|
|
const params = new URLSearchParams({ lang: this.$i18n?.locale });
|
|
return `./api/gridsessions?${params.toString()}`;
|
|
},
|
|
async loadSessions() {
|
|
try {
|
|
const response = await api.get("gridsessions", {
|
|
validateStatus: (code: number) => [200, 404].includes(code),
|
|
});
|
|
this.sessions = response.data || [];
|
|
} catch (e) {
|
|
this.sessions = [];
|
|
console.error(e);
|
|
}
|
|
},
|
|
provideTemplateOptions(products: any[]): TemplateGroup[] {
|
|
if (this.fromYaml) {
|
|
return [];
|
|
}
|
|
return [
|
|
{
|
|
label: "generic",
|
|
options: [
|
|
customTemplateOption(this.$t("config.hems.type.custom")),
|
|
customTemplateOption(this.$t("config.hems.type.relay"), RELAY_OPTION),
|
|
],
|
|
},
|
|
{
|
|
label: "integrations",
|
|
options: products,
|
|
},
|
|
];
|
|
},
|
|
isYamlInputType(type: ConfigType): boolean {
|
|
return type === ConfigType.Custom || (type as string) === RELAY_OPTION;
|
|
},
|
|
handleTemplateChange(value: string, values: DeviceValues) {
|
|
if (this.isYamlInputType(value as ConfigType)) {
|
|
values.type = ConfigType.Custom;
|
|
values.yaml = value === RELAY_OPTION ? relayHemsYaml : customHemsYaml;
|
|
}
|
|
},
|
|
onAdded(name: string) {
|
|
this.$emit("changed", { action: "added", name });
|
|
},
|
|
onUpdated() {
|
|
this.$emit("changed", { action: "updated" });
|
|
},
|
|
onRemoved() {
|
|
this.$emit("changed", { action: "removed" });
|
|
},
|
|
async handleChange() {
|
|
if (this.id === undefined) return;
|
|
if (!window.confirm(this.$t("config.hems.changeConfirm"))) return;
|
|
this.changing = true;
|
|
try {
|
|
await (this.$refs["deviceModal"] as any).remove();
|
|
} finally {
|
|
this.changing = false;
|
|
}
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.events-box {
|
|
background: var(--evcc-background);
|
|
}
|
|
</style>
|