Added device reboot functionality

This commit is contained in:
Thomas Basler 2022-11-21 23:28:53 +01:00
parent 8ba0a654a3
commit e60619b867
7 changed files with 207 additions and 0 deletions

View File

@ -8,6 +8,7 @@
#include "WebApi_firmware.h"
#include "WebApi_inverter.h"
#include "WebApi_limit.h"
#include "WebApi_maintenance.h"
#include "WebApi_mqtt.h"
#include "WebApi_network.h"
#include "WebApi_ntp.h"
@ -38,6 +39,7 @@ private:
WebApiFirmwareClass _webApiFirmware;
WebApiInverterClass _webApiInverter;
WebApiLimitClass _webApiLimit;
WebApiMaintenanceClass _webApiMaintenance;
WebApiMqttClass _webApiMqtt;
WebApiNetworkClass _webApiNetwork;
WebApiNtpClass _webApiNtp;

View 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;
};

View File

@ -25,6 +25,7 @@ void WebApiClass::init()
_webApiFirmware.init(&_server);
_webApiInverter.init(&_server);
_webApiLimit.init(&_server);
_webApiMaintenance.init(&_server);
_webApiMqtt.init(&_server);
_webApiNetwork.init(&_server);
_webApiNtp.init(&_server);
@ -47,6 +48,7 @@ void WebApiClass::loop()
_webApiFirmware.loop();
_webApiInverter.loop();
_webApiLimit.loop();
_webApiMaintenance.loop();
_webApiMqtt.loop();
_webApiNetwork.loop();
_webApiNtp.loop();

View 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);
}
}

View File

@ -46,6 +46,9 @@
<li>
<router-link @click="onClick" class="dropdown-item" to="/firmware/upgrade">Firmware Upgrade</router-link>
</li>
<li>
<router-link @click="onClick" class="dropdown-item" to="/maintenance/reboot">Device Reboot</router-link>
</li>
</ul>
</li>
<li class="nav-item dropdown">

View File

@ -14,6 +14,7 @@ import FirmwareUpgradeView from '@/views/FirmwareUpgradeView.vue'
import ConfigAdminView from '@/views/ConfigAdminView.vue'
import SecurityAdminView from '@/views/SecurityAdminView.vue'
import LoginView from '@/views/LoginView.vue'
import MaintenanceRebootView from '@/views/MaintenanceRebootView.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@ -93,6 +94,11 @@ const router = createRouter({
path: '/settings/security',
name: 'Security',
component: SecurityAdminView
},
{
path: '/maintenance/reboot',
name: 'Device Reboot',
component: MaintenanceRebootView
}
]
});

View 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>