This commit is contained in:
Florian Wetzel 2025-01-15 07:34:39 +01:00 committed by GitHub
commit d18d818e1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 444 additions and 6 deletions

View File

@ -35,6 +35,8 @@
#define DEV_MAX_MAPPING_NAME_STRLEN 63 #define DEV_MAX_MAPPING_NAME_STRLEN 63
#define LOCALE_STRLEN 2 #define LOCALE_STRLEN 2
#define INTEGRATIONS_GOE_MAX_HOSTNAME_STRLEN 128
struct CHANNEL_CONFIG_T { struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower; uint16_t MaxChannelPower;
char Name[CHAN_MAX_NAME_STRLEN]; char Name[CHAN_MAX_NAME_STRLEN];
@ -161,6 +163,14 @@ struct CONFIG_T {
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT]; INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1]; char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
struct {
// go-e Controller
bool GoeControllerEnabled;
bool GoeControllerPublishHomeCategory;
char GoeControllerHostname[INTEGRATIONS_GOE_MAX_HOSTNAME_STRLEN + 1];
uint32_t GoeControllerUpdateInterval;
} Integrations;
}; };
class ConfigurationClass { class ConfigurationClass {

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <HTTPClient.h>
#include "NetworkSettings.h"
#include <TaskSchedulerDeclarations.h>
class IntegrationsGoeControllerClass {
public:
IntegrationsGoeControllerClass();
void init(Scheduler& scheduler);
private:
void loop();
void NetworkEvent(network_event event);
Task _loopTask;
bool _networkConnected = false;
HTTPClient _http;
};
extern IntegrationsGoeControllerClass IntegrationsGoeController;

View File

@ -10,6 +10,7 @@
#include "WebApi_firmware.h" #include "WebApi_firmware.h"
#include "WebApi_gridprofile.h" #include "WebApi_gridprofile.h"
#include "WebApi_i18n.h" #include "WebApi_i18n.h"
#include "WebApi_integrations.h"
#include "WebApi_inverter.h" #include "WebApi_inverter.h"
#include "WebApi_limit.h" #include "WebApi_limit.h"
#include "WebApi_maintenance.h" #include "WebApi_maintenance.h"
@ -68,6 +69,7 @@ private:
WebApiWebappClass _webApiWebapp; WebApiWebappClass _webApiWebapp;
WebApiWsConsoleClass _webApiWsConsole; WebApiWsConsoleClass _webApiWsConsole;
WebApiWsLiveClass _webApiWsLive; WebApiWsLiveClass _webApiWsLive;
WebApiIntegrationsClass _webApiIntegrations;
}; };
extern WebApiClass WebApi; extern WebApiClass WebApi;

View File

@ -94,4 +94,8 @@ enum WebApiError {
HardwareBase = 12000, HardwareBase = 12000,
HardwarePinMappingLength, HardwarePinMappingLength,
IntegrationsBase = 13000,
IntegrationsGoeControllerHostnameLength,
IntegrationsGoeControllerUpdateInterval,
}; };

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiIntegrationsClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
private:
void onIntegrationsAdminGet(AsyncWebServerRequest* request);
void onIntegrationsAdminPost(AsyncWebServerRequest* request);
};

View File

@ -110,3 +110,8 @@
#define MAX_INVERTER_LIMIT 2250 #define MAX_INVERTER_LIMIT 2250
#define LANG_PACK_SUFFIX ".lang.json" #define LANG_PACK_SUFFIX ".lang.json"
#define INTEGRATIONS_GOE_CTRL_HOSTNAME ""
#define INTEGRATIONS_GOE_CTRL_ENABLED false
#define INTEGRATIONS_GOE_CTRL_ENABLE_HOME_CATEGORY false
#define INTEGRATIONS_GOE_CTRL_UPDATE_INTERVAL 3U

View File

@ -151,6 +151,12 @@ bool ConfigurationClass::write()
} }
} }
JsonObject integrations = doc["integrations"].to<JsonObject>();
integrations["goe_ctrl_hostname"] = config.Integrations.GoeControllerHostname;
integrations["goe_ctrl_enabled"] = config.Integrations.GoeControllerEnabled;
integrations["goe_ctrl_publish_home_category"] = config.Integrations.GoeControllerPublishHomeCategory;
integrations["goe_ctrl_update_interval"] = config.Integrations.GoeControllerUpdateInterval;
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) { if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
return false; return false;
} }
@ -327,6 +333,12 @@ bool ConfigurationClass::read()
} }
} }
JsonObject integrations = doc["integrations"];
strlcpy(config.Integrations.GoeControllerHostname, integrations["goe_ctrl_hostname"] | INTEGRATIONS_GOE_CTRL_HOSTNAME, sizeof(config.Integrations.GoeControllerHostname));
config.Integrations.GoeControllerEnabled = integrations["goe_ctrl_enabled"] | INTEGRATIONS_GOE_CTRL_ENABLED;
config.Integrations.GoeControllerPublishHomeCategory = integrations["goe_ctrl_publish_home_category"] | INTEGRATIONS_GOE_CTRL_ENABLE_HOME_CATEGORY;
config.Integrations.GoeControllerUpdateInterval = integrations["goe_ctrl_update_interval"] | INTEGRATIONS_GOE_CTRL_UPDATE_INTERVAL;
f.close(); f.close();
// Check for default DTU serial // Check for default DTU serial

View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023-2024 Thomas Basler and others
*/
#include "IntegrationsGoeController.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MessageOutput.h"
#include "NetworkSettings.h"
#include <Hoymiles.h>
IntegrationsGoeControllerClass IntegrationsGoeController;
IntegrationsGoeControllerClass::IntegrationsGoeControllerClass()
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&IntegrationsGoeControllerClass::loop, this))
{
}
void IntegrationsGoeControllerClass::init(Scheduler& scheduler)
{
using std::placeholders::_1;
NetworkSettings.onEvent(std::bind(&IntegrationsGoeControllerClass::NetworkEvent, this, _1));
scheduler.addTask(_loopTask);
_loopTask.setInterval(Configuration.get().Integrations.GoeControllerUpdateInterval * TASK_SECOND);
_loopTask.enable();
}
void IntegrationsGoeControllerClass::NetworkEvent(network_event event)
{
switch (event) {
case network_event::NETWORK_GOT_IP:
_networkConnected = true;
break;
case network_event::NETWORK_DISCONNECTED:
_networkConnected = false;
break;
default:
break;
}
}
void IntegrationsGoeControllerClass::loop()
{
const auto& integrationsConfig = Configuration.get().Integrations;
const bool reachable = Datastore.getIsAllEnabledReachable();
_loopTask.setInterval((reachable ? integrationsConfig.GoeControllerUpdateInterval : std::min(integrationsConfig.GoeControllerUpdateInterval, 5U)) * TASK_SECOND);
if (!integrationsConfig.GoeControllerEnabled) {
return;
}
if (!_networkConnected || !Hoymiles.isAllRadioIdle()) {
_loopTask.forceNextIteration();
return;
}
const auto value = reachable ? Datastore.getTotalAcPowerEnabled() : 0;
// home, grid, car, relais, solar
// ecp is an array of numbers or null: [{power}, null, null, null, {power}]
// setting the home category to the power should be configurable
// url is this: http://{hostname}/api/set?ecp=
auto url = "http://" + String(integrationsConfig.GoeControllerHostname) + "/api/set?ecp=";
url += "[";
url += integrationsConfig.GoeControllerPublishHomeCategory ? String(value) : "null";
url += ",null,null,null,";
url += value;
url += "]";
const auto timeout = std::max(2U, std::min(integrationsConfig.GoeControllerUpdateInterval-1, 3U)) * 1000U;
_http.setConnectTimeout(timeout);
_http.setTimeout(timeout);
_http.setReuse(true);
_http.begin(url);
int httpCode = _http.GET();
if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK) {
MessageOutput.println("go-e Controller updated");
} else {
MessageOutput.printf("HTTP error: %d\n", httpCode);
}
} else {
MessageOutput.println("HTTP error");
}
}

View File

@ -292,7 +292,7 @@ void NetworkSettingsClass::applyConfig()
MessageOutput.print("existing credentials... "); MessageOutput.print("existing credentials... ");
WiFi.begin(); WiFi.begin();
} }
MessageOutput.println("done"); MessageOutput.println("done. Connecting to " + String(Configuration.get().WiFi.Ssid));
setStaticIp(); setStaticIp();
} }

View File

@ -36,6 +36,7 @@ void WebApiClass::init(Scheduler& scheduler)
_webApiWebapp.init(_server, scheduler); _webApiWebapp.init(_server, scheduler);
_webApiWsConsole.init(_server, scheduler); _webApiWsConsole.init(_server, scheduler);
_webApiWsLive.init(_server, scheduler); _webApiWsLive.init(_server, scheduler);
_webApiIntegrations.init(_server, scheduler);
_server.begin(); _server.begin();
} }

View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022-2024 Thomas Basler and others
*/
#include "WebApi_integrations.h"
#include "Configuration.h"
#include "WebApi.h"
#include "WebApi_errors.h"
#include "helper.h"
#include <AsyncJson.h>
void WebApiIntegrationsClass::init(AsyncWebServer& server, Scheduler& scheduler)
{
using std::placeholders::_1;
server.on("/api/integrations/config", HTTP_GET, std::bind(&WebApiIntegrationsClass::onIntegrationsAdminGet, this, _1));
server.on("/api/integrations/config", HTTP_POST, std::bind(&WebApiIntegrationsClass::onIntegrationsAdminPost, this, _1));
}
void WebApiIntegrationsClass::onIntegrationsAdminGet(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentials(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["goe_ctrl_hostname"] = config.Integrations.GoeControllerHostname;
root["goe_ctrl_enabled"] = config.Integrations.GoeControllerEnabled;
root["goe_ctrl_publish_home_category"] = config.Integrations.GoeControllerPublishHomeCategory;
root["goe_ctrl_update_interval"] = config.Integrations.GoeControllerUpdateInterval;
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}
void WebApiIntegrationsClass::onIntegrationsAdminPost(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentials(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonDocument root;
if (!WebApi.parseRequestData(request, response, root)) {
return;
}
auto& retMsg = response->getRoot();
if (!(root["goe_ctrl_hostname"].is<String>()
&& root["goe_ctrl_enabled"].is<bool>()
&& root["goe_ctrl_publish_home_category"].is<bool>()
&& root["goe_ctrl_update_interval"].is<uint32_t>())) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["goe_ctrl_enabled"].as<bool>()) {
if (root["goe_ctrl_hostname"].as<String>().length() == 0 || root["goe_ctrl_hostname"].as<String>().length() > INTEGRATIONS_GOE_MAX_HOSTNAME_STRLEN) {
retMsg["message"] = "go-e Controller hostname must between 1 and " STR(INTEGRATIONS_GOE_MAX_HOSTNAME_STRLEN) " characters long!";
retMsg["code"] = WebApiError::IntegrationsGoeControllerHostnameLength;
retMsg["param"]["max"] = INTEGRATIONS_GOE_MAX_HOSTNAME_STRLEN;
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
if (root["goe_ctrl_update_interval"].as<uint32_t>() < 3 || root["goe_ctrl_update_interval"].as<uint32_t>() > 65535) {
retMsg["message"] = "go-e Controller update interval must between 3 and 65535!";
retMsg["code"] = WebApiError::IntegrationsGoeControllerUpdateInterval;
retMsg["param"]["min"] = 3;
retMsg["param"]["max"] = 65535;
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
return;
}
}
{
auto guard = Configuration.getWriteGuard();
auto& config = guard.getConfig();
config.Integrations.GoeControllerEnabled = root["goe_ctrl_enabled"].as<bool>();
config.Integrations.GoeControllerPublishHomeCategory = root["goe_ctrl_publish_home_category"].as<bool>();
config.Integrations.GoeControllerUpdateInterval = root["goe_ctrl_update_interval"].as<uint32_t>();
strlcpy(config.Integrations.GoeControllerHostname, root["goe_ctrl_hostname"].as<String>().c_str(), sizeof(config.Integrations.GoeControllerHostname));
}
WebApi.writeConfig(retMsg);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
}

View File

@ -5,6 +5,7 @@
#include "Configuration.h" #include "Configuration.h"
#include "Datastore.h" #include "Datastore.h"
#include "Display_Graphic.h" #include "Display_Graphic.h"
#include "IntegrationsGoeController.h"
#include "I18n.h" #include "I18n.h"
#include "InverterSettings.h" #include "InverterSettings.h"
#include "Led_Single.h" #include "Led_Single.h"
@ -121,6 +122,11 @@ void setup()
MqttHandleHass.init(scheduler); MqttHandleHass.init(scheduler);
MessageOutput.println("done"); MessageOutput.println("done");
// Initialize go-e Integration
MessageOutput.print("Initialize go-e Integration... ");
IntegrationsGoeController.init(scheduler);
MessageOutput.println("done");
// Initialize WebApi // Initialize WebApi
MessageOutput.print("Initialize WebApi... "); MessageOutput.print("Initialize WebApi... ");
WebApi.init(scheduler); WebApi.init(scheduler);

1
webapp/.nvmrc Normal file
View File

@ -0,0 +1 @@
22.9.0

View File

@ -47,5 +47,8 @@
"vite-plugin-css-injected-by-js": "^3.5.2", "vite-plugin-css-injected-by-js": "^3.5.2",
"vue-tsc": "^2.2.0" "vue-tsc": "^2.2.0"
}, },
"engines": {
"node": ">=21.1.0"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
} }

View File

@ -5,7 +5,7 @@
:class="[wide ? 'col-sm-4' : 'col-sm-2', isCheckbox ? 'form-check-label' : 'col-form-label']" :class="[wide ? 'col-sm-4' : 'col-sm-2', isCheckbox ? 'form-check-label' : 'col-form-label']"
> >
{{ label }} {{ label }}
<BIconInfoCircle v-if="tooltip !== undefined" v-tooltip :title="tooltip" /> <BIconInfoCircle v-if="tooltip !== undefined" v-tooltip :title="tooltip" class="ms-1" />
</label> </label>
<div :class="[wide ? 'col-sm-8' : 'col-sm-10']"> <div :class="[wide ? 'col-sm-8' : 'col-sm-10']">
<div v-if="!isTextarea" :class="{ 'form-check form-switch': isCheckbox, 'input-group': postfix || prefix }"> <div v-if="!isTextarea" :class="{ 'form-check form-switch': isCheckbox, 'input-group': postfix || prefix }">

View File

@ -73,6 +73,11 @@
$t('menu.DeviceManager') $t('menu.DeviceManager')
}}</router-link> }}</router-link>
</li> </li>
<li>
<router-link @click="onClick" class="dropdown-item" to="/settings/integrations">{{
$t('menu.Integrations')
}}</router-link>
</li>
<li> <li>
<hr class="dropdown-divider" /> <hr class="dropdown-divider" />
</li> </li>

View File

@ -113,7 +113,9 @@
"10002": "Authentifizierung erfolgreich!", "10002": "Authentifizierung erfolgreich!",
"11001": "@:apiresponse.2001", "11001": "@:apiresponse.2001",
"11002": "@:apiresponse:5004", "11002": "@:apiresponse:5004",
"12001": "Profil muss zwischen 1 und {max} Zeichen lang sein!" "12001": "Profil muss zwischen 1 und {max} Zeichen lang sein!",
"13001": "Hostname muss zwischen 1 und {max} Zeichen lang sein!",
"13002": "Aktualisierungsintervall muss zwischen {min} und {max} Sekunden liegen!"
}, },
"home": { "home": {
"LiveData": "Live-Daten", "LiveData": "Live-Daten",
@ -668,6 +670,18 @@
"EqualBrightness": "Gleiche Helligkeit", "EqualBrightness": "Gleiche Helligkeit",
"LedBrightness": "LED {led} Helligkeit ({brightness})" "LedBrightness": "LED {led} Helligkeit ({brightness})"
}, },
"integrationsadmin": {
"IntegrationSettings": "Integrationseinstellungen",
"Save": "@:base.Save",
"Cancel": "@:base.Cancel",
"goecontroller": "go-e Controller",
"goecontrollerEnabled": "go-e Controller aktivieren",
"goecontrollerEnabledHint": "Aktiviere die go-e Controller-Integration. Dadurch wird der 'ecp' API-Key gesetzt, um dem Controller mitzuteilen, wie viel Solarenergie produziert wird.",
"goecontrollerHostname": "Hostname",
"goecontrollerUpdateInterval": "Aktualisierungsintervall",
"goecontrollerEnableHomeCategory": "Heim-Kategorie aktivieren",
"goecontrollerEnableHomeCategoryHint": "Setze auch den Wert für die Heim-Kategorie. Auf diese Weise kannst du den korrekten Verbrauch deines Hauses haben, wenn dein Wechselrichter auch in der Heim-Kategorie enthalten ist."
},
"pininfo": { "pininfo": {
"Category": "Kategorie", "Category": "Kategorie",
"Name": "Name", "Name": "Name",

View File

@ -9,6 +9,7 @@
"SecuritySettings": "Security Settings", "SecuritySettings": "Security Settings",
"DTUSettings": "DTU Settings", "DTUSettings": "DTU Settings",
"DeviceManager": "Device-Manager", "DeviceManager": "Device-Manager",
"Integrations": "Integrations",
"ConfigManagement": "Config Management", "ConfigManagement": "Config Management",
"FirmwareUpgrade": "Firmware Upgrade", "FirmwareUpgrade": "Firmware Upgrade",
"DeviceReboot": "Device Reboot", "DeviceReboot": "Device Reboot",
@ -113,7 +114,9 @@
"10002": "Authentication successful!", "10002": "Authentication successful!",
"11001": "@:apiresponse.2001", "11001": "@:apiresponse.2001",
"11002": "@:apiresponse:5004", "11002": "@:apiresponse:5004",
"12001": "Profil must between 1 and {max} characters long!" "12001": "Profil must between 1 and {max} characters long!",
"13001": "Hostname must between 1 and {max} characters long!",
"13002": "Update interval must between {min} and {max} seconds!"
}, },
"home": { "home": {
"LiveData": "Live Data", "LiveData": "Live Data",
@ -668,6 +671,18 @@
"EqualBrightness": "Equal brightness", "EqualBrightness": "Equal brightness",
"LedBrightness": "LED {led} brightness ({brightness})" "LedBrightness": "LED {led} brightness ({brightness})"
}, },
"integrationsadmin": {
"IntegrationSettings": "Integration Settings",
"Save": "@:base.Save",
"Cancel": "@:base.Cancel",
"goecontroller": "go-e Controller",
"goecontrollerEnabled": "Enable go-e Controller",
"goecontrollerEnabledHint": "Enable the go-e controller integration. This will set the 'ecp' api-key to let the controller know how much solar energy is produced",
"goecontrollerHostname": "Hostname",
"goecontrollerUpdateInterval": "Update Interval",
"goecontrollerEnableHomeCategory": "Enable Home Category",
"goecontrollerEnableHomeCategoryHint": "Also set the value for the home category. This way you can have the correct consumption of your home if your inverter is also included in the home category."
},
"pininfo": { "pininfo": {
"Category": "Category", "Category": "Category",
"Name": "Name", "Name": "Name",

View File

@ -113,7 +113,9 @@
"10002": "Authentification réussie !", "10002": "Authentification réussie !",
"11001": "@:apiresponse.2001", "11001": "@:apiresponse.2001",
"11002": "@:apiresponse:5004", "11002": "@:apiresponse:5004",
"12001": "Le profil doit comporter entre 1 et {max} caractères !" "12001": "Le profil doit comporter entre 1 et {max} caractères !",
"13001": "Hostname must between 1 and {max} characters long!",
"13002": "Update interval must between {min} and {max} seconds!"
}, },
"home": { "home": {
"LiveData": "Données en direct", "LiveData": "Données en direct",
@ -650,6 +652,18 @@
"EqualBrightness": "Même luminosité", "EqualBrightness": "Même luminosité",
"LedBrightness": "LED {led} luminosité ({brightness})" "LedBrightness": "LED {led} luminosité ({brightness})"
}, },
"integrationsadmin": {
"IntegrationSettings": "Integration Settings",
"Save": "@:base.Save",
"Cancel": "@:base.Cancel",
"goecontroller": "go-e Controller",
"goecontrollerEnabled": "Enable go-e Controller",
"goecontrollerEnabledHint": "Enable the go-e controller integration. This will set the 'ecp' api-key to let the controller know how much solar energy is produced",
"goecontrollerHostname": "Hostname",
"goecontrollerUpdateInterval": "Update Interval",
"goecontrollerEnableHomeCategory": "Enable Home Category",
"goecontrollerEnableHomeCategoryHint": "Also set the value for the home category. This way you can have the correct consumption of your home if your inverter is also included in the home category."
},
"pininfo": { "pininfo": {
"Category": "Catégorie", "Category": "Catégorie",
"Name": "Nom", "Name": "Nom",

View File

@ -18,6 +18,7 @@ import NtpInfoView from '@/views/NtpInfoView.vue';
import SecurityAdminView from '@/views/SecurityAdminView.vue'; import SecurityAdminView from '@/views/SecurityAdminView.vue';
import SystemInfoView from '@/views/SystemInfoView.vue'; import SystemInfoView from '@/views/SystemInfoView.vue';
import WaitRestartView from '@/views/WaitRestartView.vue'; import WaitRestartView from '@/views/WaitRestartView.vue';
import IntegrationsAdminView from '@/views/IntegrationsAdminView.vue';
import { createRouter, createWebHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({ const router = createRouter({
@ -106,6 +107,11 @@ const router = createRouter({
name: 'Device Manager', name: 'Device Manager',
component: DeviceAdminView, component: DeviceAdminView,
}, },
{
path: '/settings/integrations',
name: 'Integrations',
component: IntegrationsAdminView,
},
{ {
path: '/firmware/upgrade', path: '/firmware/upgrade',
name: 'Firmware Upgrade', name: 'Firmware Upgrade',

View File

@ -0,0 +1,6 @@
export interface IntegrationsConfig {
goe_ctrl_enabled: boolean;
goe_ctrl_hostname: string;
goe_ctrl_publish_home_category: boolean;
goe_ctrl_update_interval: number;
}

View File

@ -0,0 +1,109 @@
<template>
<BasePage :title="$t('integrationsadmin.IntegrationSettings')" :isLoading="dataLoading">
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
{{ alertMessage }}
</BootstrapAlert>
<form @submit="saveIntegrationsConfig">
<CardElement :text="$t('integrationsadmin.goecontroller')" textVariant="text-bg-primary">
<InputElement
:label="$t('integrationsadmin.goecontrollerEnabled')"
v-model="integrationsConfig.goe_ctrl_enabled"
type="checkbox"
wide
:tooltip="$t('integrationsadmin.goecontrollerEnabledHint')"
/>
<InputElement
v-show="integrationsConfig.goe_ctrl_enabled"
:label="$t('integrationsadmin.goecontrollerEnableHomeCategory')"
v-model="integrationsConfig.goe_ctrl_publish_home_category"
type="checkbox"
wide
:tooltip="$t('integrationsadmin.goecontrollerEnableHomeCategoryHint')"
/>
<InputElement
v-show="integrationsConfig.goe_ctrl_enabled"
:label="$t('integrationsadmin.goecontrollerHostname')"
v-model="integrationsConfig.goe_ctrl_hostname"
type="text"
placeholder="go-econtroller_XXXXXX"
/>
<InputElement
v-show="integrationsConfig.goe_ctrl_enabled"
:label="$t('integrationsadmin.goecontrollerUpdateInterval')"
v-model="integrationsConfig.goe_ctrl_update_interval"
type="number"
min="3"
max="65535"
:postfix="$t('mqttadmin.Seconds')"
/>
</CardElement>
<FormFooter @reload="getIntegrationsConfig" />
</form>
</BasePage>
</template>
<script lang="ts">
import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from '@/components/BootstrapAlert.vue';
import CardElement from '@/components/CardElement.vue';
import FormFooter from '@/components/FormFooter.vue';
import InputElement from '@/components/InputElement.vue';
import { authHeader, handleResponse } from '@/utils/authentication';
import { defineComponent } from 'vue';
import type { IntegrationsConfig } from '@/types/IntegrationsConfig';
export default defineComponent({
components: {
BasePage,
BootstrapAlert,
CardElement,
FormFooter,
InputElement,
},
data() {
return {
dataLoading: true,
integrationsConfig: {} as IntegrationsConfig,
alertMessage: '',
alertType: 'info',
showAlert: false,
};
},
created() {
this.getIntegrationsConfig();
},
methods: {
getIntegrationsConfig() {
this.dataLoading = true;
fetch('/api/integrations/config', { headers: authHeader() })
.then((response) => handleResponse(response, this.$emitter, this.$router))
.then((data) => {
this.integrationsConfig = data;
this.dataLoading = false;
});
},
saveIntegrationsConfig(e: Event) {
e.preventDefault();
const formData = new FormData();
formData.append('data', JSON.stringify(this.integrationsConfig));
fetch('/api/integrations/config', {
method: 'POST',
headers: authHeader(),
body: formData,
})
.then((response) => handleResponse(response, this.$emitter, this.$router))
.then((response) => {
this.alertMessage = this.$t('apiresponse.' + response.code, response.param);
this.alertType = response.type;
this.showAlert = true;
});
},
},
});
</script>

View File

@ -80,7 +80,7 @@
v-model="mqttConfigList.mqtt_publish_interval" v-model="mqttConfigList.mqtt_publish_interval"
type="number" type="number"
min="5" min="5"
max="86400" max="65535"
:postfix="$t('mqttadmin.Seconds')" :postfix="$t('mqttadmin.Seconds')"
/> />