Added device reboot functionality
This commit is contained in:
parent
8ba0a654a3
commit
e60619b867
@ -8,6 +8,7 @@
|
|||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
#include "WebApi_inverter.h"
|
#include "WebApi_inverter.h"
|
||||||
#include "WebApi_limit.h"
|
#include "WebApi_limit.h"
|
||||||
|
#include "WebApi_maintenance.h"
|
||||||
#include "WebApi_mqtt.h"
|
#include "WebApi_mqtt.h"
|
||||||
#include "WebApi_network.h"
|
#include "WebApi_network.h"
|
||||||
#include "WebApi_ntp.h"
|
#include "WebApi_ntp.h"
|
||||||
@ -38,6 +39,7 @@ private:
|
|||||||
WebApiFirmwareClass _webApiFirmware;
|
WebApiFirmwareClass _webApiFirmware;
|
||||||
WebApiInverterClass _webApiInverter;
|
WebApiInverterClass _webApiInverter;
|
||||||
WebApiLimitClass _webApiLimit;
|
WebApiLimitClass _webApiLimit;
|
||||||
|
WebApiMaintenanceClass _webApiMaintenance;
|
||||||
WebApiMqttClass _webApiMqtt;
|
WebApiMqttClass _webApiMqtt;
|
||||||
WebApiNetworkClass _webApiNetwork;
|
WebApiNetworkClass _webApiNetwork;
|
||||||
WebApiNtpClass _webApiNtp;
|
WebApiNtpClass _webApiNtp;
|
||||||
|
|||||||
15
include/WebApi_maintenance.h
Normal file
15
include/WebApi_maintenance.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
class WebApiMaintenanceClass {
|
||||||
|
public:
|
||||||
|
void init(AsyncWebServer* server);
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onRebootPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
|
AsyncWebServer* _server;
|
||||||
|
};
|
||||||
@ -25,6 +25,7 @@ void WebApiClass::init()
|
|||||||
_webApiFirmware.init(&_server);
|
_webApiFirmware.init(&_server);
|
||||||
_webApiInverter.init(&_server);
|
_webApiInverter.init(&_server);
|
||||||
_webApiLimit.init(&_server);
|
_webApiLimit.init(&_server);
|
||||||
|
_webApiMaintenance.init(&_server);
|
||||||
_webApiMqtt.init(&_server);
|
_webApiMqtt.init(&_server);
|
||||||
_webApiNetwork.init(&_server);
|
_webApiNetwork.init(&_server);
|
||||||
_webApiNtp.init(&_server);
|
_webApiNtp.init(&_server);
|
||||||
@ -47,6 +48,7 @@ void WebApiClass::loop()
|
|||||||
_webApiFirmware.loop();
|
_webApiFirmware.loop();
|
||||||
_webApiInverter.loop();
|
_webApiInverter.loop();
|
||||||
_webApiLimit.loop();
|
_webApiLimit.loop();
|
||||||
|
_webApiMaintenance.loop();
|
||||||
_webApiMqtt.loop();
|
_webApiMqtt.loop();
|
||||||
_webApiNetwork.loop();
|
_webApiNetwork.loop();
|
||||||
_webApiNtp.loop();
|
_webApiNtp.loop();
|
||||||
|
|||||||
82
src/WebApi_maintenance.cpp
Normal file
82
src/WebApi_maintenance.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WebApi_maintenance.h"
|
||||||
|
#include "AsyncJson.h"
|
||||||
|
#include "WebApi.h"
|
||||||
|
|
||||||
|
void WebApiMaintenanceClass::init(AsyncWebServer* server)
|
||||||
|
{
|
||||||
|
using std::placeholders::_1;
|
||||||
|
|
||||||
|
_server = server;
|
||||||
|
|
||||||
|
_server->on("/api/maintenance/reboot", HTTP_POST, std::bind(&WebApiMaintenanceClass::onRebootPost, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiMaintenanceClass::loop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
||||||
|
{
|
||||||
|
if (!WebApi.checkCredentials(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||||
|
JsonObject retMsg = response->getRoot();
|
||||||
|
retMsg[F("type")] = F("warning");
|
||||||
|
|
||||||
|
if (!request->hasParam("data", true)) {
|
||||||
|
retMsg[F("message")] = F("No values found!");
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String json = request->getParam("data", true)->value();
|
||||||
|
|
||||||
|
if (json.length() > MQTT_JSON_DOC_SIZE) {
|
||||||
|
retMsg[F("message")] = F("Data too large!");
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument root(MQTT_JSON_DOC_SIZE);
|
||||||
|
DeserializationError error = deserializeJson(root, json);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
retMsg[F("message")] = F("Failed to parse data!");
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(root.containsKey("reboot"))) {
|
||||||
|
retMsg[F("message")] = F("Values are missing!");
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root[F("reboot")].as<bool>()) {
|
||||||
|
retMsg[F("type")] = F("success");
|
||||||
|
retMsg[F("message")] = F("Reboot triggered!");
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
yield();
|
||||||
|
delay(1000);
|
||||||
|
yield();
|
||||||
|
ESP.restart();
|
||||||
|
} else {
|
||||||
|
retMsg[F("message")] = F("Reboot cancled!");
|
||||||
|
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,6 +46,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<router-link @click="onClick" class="dropdown-item" to="/firmware/upgrade">Firmware Upgrade</router-link>
|
<router-link @click="onClick" class="dropdown-item" to="/firmware/upgrade">Firmware Upgrade</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<router-link @click="onClick" class="dropdown-item" to="/maintenance/reboot">Device Reboot</router-link>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import FirmwareUpgradeView from '@/views/FirmwareUpgradeView.vue'
|
|||||||
import ConfigAdminView from '@/views/ConfigAdminView.vue'
|
import ConfigAdminView from '@/views/ConfigAdminView.vue'
|
||||||
import SecurityAdminView from '@/views/SecurityAdminView.vue'
|
import SecurityAdminView from '@/views/SecurityAdminView.vue'
|
||||||
import LoginView from '@/views/LoginView.vue'
|
import LoginView from '@/views/LoginView.vue'
|
||||||
|
import MaintenanceRebootView from '@/views/MaintenanceRebootView.vue';
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@ -93,6 +94,11 @@ const router = createRouter({
|
|||||||
path: '/settings/security',
|
path: '/settings/security',
|
||||||
name: 'Security',
|
name: 'Security',
|
||||||
component: SecurityAdminView
|
component: SecurityAdminView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/maintenance/reboot',
|
||||||
|
name: 'Device Reboot',
|
||||||
|
component: MaintenanceRebootView
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
97
webapp/src/views/MaintenanceRebootView.vue
Normal file
97
webapp/src/views/MaintenanceRebootView.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<BasePage :title="'Device Reboot'" :isLoading="dataLoading">
|
||||||
|
<BootstrapAlert v-model="showAlert" dismissible :variant="alertType">
|
||||||
|
{{ alertMessage }}
|
||||||
|
</BootstrapAlert>
|
||||||
|
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-header text-bg-primary">Perform Reboot</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
|
||||||
|
<button class="btn btn-danger" @click="onOpenModal(performReboot)">Reboot!
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="alert alert-danger mt-3" role="alert">
|
||||||
|
<b>Note:</b> A manual reboot does not normally have to be performed. OpenDTU performs any required
|
||||||
|
reboot (e.g. after a firmware update) automatically. Settings are also adopted without rebooting. If
|
||||||
|
you need to reboot due to an error, please consider reporting it at <a
|
||||||
|
href="https://github.com/tbnobody/OpenDTU/issues" class="alert-link"
|
||||||
|
target="_blank">https://github.com/tbnobody/OpenDTU/issues</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BasePage>
|
||||||
|
|
||||||
|
<div class="modal" id="performReboot" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Reboot OpenDTU</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Do you really want to reboot the device?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" @click="onCloseModal(performReboot)"
|
||||||
|
data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-danger" @click="onReboot">Reboot</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import * as bootstrap from 'bootstrap';
|
||||||
|
import BasePage from '@/components/BasePage.vue';
|
||||||
|
import BootstrapAlert from "@/components/BootstrapAlert.vue";
|
||||||
|
import { handleResponse, authHeader } from '@/utils/authentication';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
BasePage,
|
||||||
|
BootstrapAlert,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
performReboot: {} as bootstrap.Modal,
|
||||||
|
|
||||||
|
dataLoading: false,
|
||||||
|
|
||||||
|
alertMessage: "",
|
||||||
|
alertType: "info",
|
||||||
|
showAlert: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.performReboot = new bootstrap.Modal('#performReboot');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onReboot() {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("data", JSON.stringify({ reboot: true }));
|
||||||
|
|
||||||
|
fetch("/api/maintenance/reboot", {
|
||||||
|
method: "POST",
|
||||||
|
headers: authHeader(),
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then((response) => handleResponse(response, this.$emitter))
|
||||||
|
.then((data) => {
|
||||||
|
this.alertMessage = data.message;
|
||||||
|
this.alertType = data.type;
|
||||||
|
this.showAlert = true;
|
||||||
|
});
|
||||||
|
this.onCloseModal(this.performReboot);
|
||||||
|
},
|
||||||
|
onOpenModal(modal: bootstrap.Modal) {
|
||||||
|
modal.show();
|
||||||
|
},
|
||||||
|
onCloseModal(modal: bootstrap.Modal) {
|
||||||
|
modal.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue
Block a user