1249 lines
49 KiB
C++
1249 lines
49 KiB
C++
/*
|
|
; Project: Open Vehicle Monitor System
|
|
; Date: 14th March 2017
|
|
;
|
|
; Changes:
|
|
; 1.0 Initial release
|
|
;
|
|
; (C) 2018 Michael Balzer
|
|
;
|
|
; Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
; of this software and associated documentation files (the "Software"), to deal
|
|
; in the Software without restriction, including without limitation the rights
|
|
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
; copies of the Software, and to permit persons to whom the Software is
|
|
; furnished to do so, subject to the following conditions:
|
|
;
|
|
; The above copyright notice and this permission notice shall be included in
|
|
; all copies or substantial portions of the Software.
|
|
;
|
|
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
; THE SOFTWARE.
|
|
*/
|
|
|
|
#include "ovms_log.h"
|
|
static const char *TAG = "webserver";
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <dirent.h>
|
|
#include "ovms_webserver.h"
|
|
#include "ovms_config.h"
|
|
#include "ovms_metrics.h"
|
|
#include "metrics_standard.h"
|
|
#include "vehicle.h"
|
|
#include "ovms_boot.h"
|
|
#include "ovms_housekeeping.h"
|
|
#include "ovms_peripherals.h"
|
|
|
|
#ifdef CONFIG_OVMS_COMP_OTA
|
|
#include "ovms_ota.h"
|
|
#endif
|
|
|
|
#if defined(CONFIG_OVMS_COMP_SERVER) && defined(CONFIG_OVMS_COMP_SERVER_V2)
|
|
#include "ovms_server_v2.h"
|
|
#endif
|
|
|
|
#define _attr(text) (c.encode_html(text).c_str())
|
|
#define _html(text) (c.encode_html(text).c_str())
|
|
|
|
|
|
#ifdef WEBSRV_HAVE_SETUPWIZARD
|
|
|
|
/**
|
|
* HandleCfgInit: /cfg/init: setup wizard dispatcher
|
|
*/
|
|
void OvmsWebServer::HandleCfgInit(PageEntry_t& p, PageContext_t& c)
|
|
{
|
|
std::string step = MyConfig.GetParamValue("module", "init");
|
|
if (step.empty())
|
|
step = "1";
|
|
ESP_LOGI(TAG, "CfgInit enter %s", step.c_str());
|
|
|
|
c.head(200);
|
|
|
|
if (c.method == "POST") {
|
|
if (c.getvar("action") == "abort") {
|
|
ESP_LOGI(TAG, "CfgInit abort");
|
|
CfgInitSetStep("done");
|
|
c.alert("info",
|
|
"<p class=\"lead\">Setup wizard aborted.</p>"
|
|
"<p>Use the configuration menu to do a manual setup. Check the user guide for instructions.</p>");
|
|
OutputHome(p, c);
|
|
c.done();
|
|
return;
|
|
}
|
|
else if (c.getvar("action") == "skip") {
|
|
char sc = step[0];
|
|
if (sc == '5')
|
|
step = "done";
|
|
else
|
|
step = sc+1;
|
|
ESP_LOGI(TAG, "CfgInit skip to %s", step.c_str());
|
|
CfgInitSetStep(step);
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
|
|
// todo… use function pointers, esp. if we need 'back' option
|
|
if (startsWith(step, "1")) {
|
|
if (!(step = MyWebServer.CfgInit1(p, c, step)).empty()) {
|
|
ESP_LOGI(TAG, "CfgInit next: %s", step.c_str());
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
if (startsWith(step, "2")) {
|
|
if (!(step = MyWebServer.CfgInit2(p, c, step)).empty()) {
|
|
ESP_LOGI(TAG, "CfgInit next: %s", step.c_str());
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
if (startsWith(step, "3")) {
|
|
if (!(step = MyWebServer.CfgInit3(p, c, step)).empty()) {
|
|
ESP_LOGI(TAG, "CfgInit next: %s", step.c_str());
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
if (startsWith(step, "4")) {
|
|
if (!(step = MyWebServer.CfgInit4(p, c, step)).empty()) {
|
|
ESP_LOGI(TAG, "CfgInit next: %s", step.c_str());
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
if (startsWith(step, "5")) {
|
|
if (!(step = MyWebServer.CfgInit5(p, c, step)).empty()) {
|
|
ESP_LOGI(TAG, "CfgInit next: %s", step.c_str());
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
|
|
if (step == "done") {
|
|
c.alert("success",
|
|
"<p class=\"lead\">Setup wizard completed.</p>"
|
|
"<p>Your OVMS module is now ready to use. Please have a look at the menus for more options.</p>");
|
|
OutputHome(p, c);
|
|
}
|
|
|
|
c.done();
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit state process
|
|
*/
|
|
|
|
void OvmsWebServer::CfgInitStartup()
|
|
{
|
|
std::string step = MyConfig.GetParamValue("module", "init");
|
|
if (!step.empty() && step != "done") {
|
|
if (MyBoot.GetEarlyCrashCount() > 0) {
|
|
// early crash: restore default access
|
|
ESP_LOGE(TAG, "CfgInit: early crash detected at step %s", step.c_str());
|
|
CfgInitSetStep("restore", 1);
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInit: continue at step %s after reboot", step.c_str());
|
|
m_init_timeout = 5;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string OvmsWebServer::CfgInitSetStep(std::string step, int timeout /*=0*/)
|
|
{
|
|
ESP_LOGI(TAG, "CfgInitSetStep: step %s timeout %d", step.c_str(), timeout);
|
|
MyConfig.SetParamValue("module", "init", step);
|
|
MyWebServer.m_init_timeout = timeout;
|
|
return step;
|
|
}
|
|
|
|
void OvmsWebServer::CfgInitTicker()
|
|
{
|
|
if (m_init_timeout == 0)
|
|
return;
|
|
std::string step = MyConfig.GetParamValue("module", "init");
|
|
if (step.empty() || step == "done") {
|
|
m_init_timeout = 0;
|
|
return;
|
|
}
|
|
if (m_init_timeout % 5 == 0)
|
|
ESP_LOGI(TAG, "CfgInitTicker: step %s timeout in %d seconds", step.c_str(), m_init_timeout);
|
|
if (--m_init_timeout != 0)
|
|
return;
|
|
ESP_LOGI(TAG, "CfgInitTicker: step %s timeout", step.c_str());
|
|
|
|
if (step == "1.test.start") {
|
|
// reconfigure wifi AP:
|
|
std::string ap_ssid = MyConfig.GetParamValue("vehicle", "id");
|
|
std::string ap_pass = MyConfig.GetParamValue("wifi.ap", ap_ssid);
|
|
if (ap_ssid.empty() || ap_pass.length() < 8) {
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 1: wifi AP config invalid");
|
|
CfgInitSetStep("restore", 1);
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 1: starting wifi AP '%s'", ap_ssid.c_str());
|
|
MyPeripherals->m_esp32wifi->StartAccessPointMode(ap_ssid, ap_pass);
|
|
CfgInitSetStep("1.test.connect", 300);
|
|
}
|
|
}
|
|
else if (step == "1.test.connect") {
|
|
// timeout waiting for user:
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 1: timeout waiting for user at new AP");
|
|
CfgInitSetStep("restore", 1);
|
|
}
|
|
else if (step == "restore") {
|
|
// revert to factory default AP & password:
|
|
ESP_LOGE(TAG, "CfgInitTicker: restoring default access, AP 'OVMS'/'OVMSinit'");
|
|
MyConfig.DeleteInstance("password", "module");
|
|
MyConfig.DeleteInstance("auto", "wifi.mode");
|
|
MyConfig.DeleteInstance("auto", "wifi.ssid.ap");
|
|
MyConfig.DeleteInstance("auto", "wifi.ssid.client");
|
|
MyConfig.DeleteInstance("wifi.ap", "OVMS");
|
|
MyConfig.DeleteInstance("network", "dns");
|
|
std::string ssid = MyConfig.GetParamValue("vehicle", "id");
|
|
MyConfig.DeleteInstance("wifi.ap", ssid);
|
|
MyPeripherals->m_esp32wifi->StartAccessPointMode("OVMS", "OVMSinit");
|
|
CfgInitSetStep("1.fail");
|
|
}
|
|
|
|
else if (step == "2.test.start") {
|
|
// reconfigure wifi to APCLIENT:
|
|
std::string ap_ssid = MyConfig.GetParamValue("vehicle", "id");
|
|
std::string ap_pass = MyConfig.GetParamValue("wifi.ap", ap_ssid);
|
|
if (ap_ssid.empty() || ap_pass.length() < 8) {
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 2: wifi AP config invalid, return to step 1");
|
|
CfgInitSetStep("restore", 1);
|
|
return;
|
|
}
|
|
std::string cl_ssid = MyConfig.GetParamValue("auto", "wifi.ssid.client");
|
|
std::string cl_pass = MyConfig.GetParamValue("wifi.ssid", cl_ssid);
|
|
if (cl_ssid.empty() || cl_pass.empty()) {
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 2: wifi client config invalid");
|
|
MyConfig.DeleteInstance("auto", "wifi.mode");
|
|
MyConfig.DeleteInstance("auto", "wifi.ssid.client");
|
|
MyConfig.DeleteInstance("network", "dns");
|
|
CfgInitSetStep("2.fail.connect");
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 2: starting wifi APCLIENT '%s' / '%s'", ap_ssid.c_str(), cl_ssid.c_str());
|
|
MyPeripherals->m_esp32wifi->StartAccessPointClientMode(ap_ssid, ap_pass, cl_ssid, cl_pass);
|
|
CfgInitSetStep("2.test.connect", 20);
|
|
}
|
|
}
|
|
else if (step == "2.test.connect") {
|
|
// test wifi client connection:
|
|
std::string ssid = MyConfig.GetParamValue("auto", "wifi.ssid.client");
|
|
if (!MyNetManager.m_connected_wifi || MyPeripherals->m_esp32wifi->GetSSID() != ssid) {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 2: wifi connection to '%s' failed, reverting to AP mode", ssid.c_str());
|
|
MyConfig.DeleteInstance("auto", "wifi.mode");
|
|
MyConfig.DeleteInstance("auto", "wifi.ssid.client");
|
|
MyConfig.DeleteInstance("network", "dns");
|
|
std::string ap_ssid = MyConfig.GetParamValue("vehicle", "id");
|
|
std::string ap_pass = MyConfig.GetParamValue("wifi.ap", ap_ssid);
|
|
MyPeripherals->m_esp32wifi->StartAccessPointMode(ap_ssid, ap_pass);
|
|
CfgInitSetStep("2.fail.connect");
|
|
}
|
|
#ifdef CONFIG_OVMS_COMP_OTA
|
|
else {
|
|
ota_info info;
|
|
MyOTA.GetStatus(info, true);
|
|
if (info.version_server.empty()) {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 2: server connection failed");
|
|
CfgInitSetStep("2.fail.server");
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 2: success, proceeding to step 3");
|
|
CfgInitSetStep("3");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
else if (step == "4.test.start") {
|
|
std::string vehicletype = MyConfig.GetParamValue("auto", "vehicle.type");
|
|
std::string server = MyConfig.GetParamValue("server.v2", "server");
|
|
if (StdMetrics.ms_v_type->AsString() != vehicletype) {
|
|
// stage 1: configure vehicle type:
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 4: setting vehicle type '%s'", vehicletype.c_str());
|
|
MyVehicleFactory.SetVehicle(vehicletype.c_str());
|
|
if (!MyVehicleFactory.ActiveVehicle()) {
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 4: cannot set vehicle type '%s'", vehicletype.c_str());
|
|
MyConfig.DeleteInstance("auto", "vehicle.type");
|
|
CfgInitSetStep("4.fail.vehicle");
|
|
return;
|
|
}
|
|
CfgInitSetStep("4.test.start", 3); // stage 2 after 3 seconds
|
|
}
|
|
#if defined(CONFIG_OVMS_COMP_SERVER) && defined(CONFIG_OVMS_COMP_SERVER_V2)
|
|
else {
|
|
// stage 2: start/stop V2 server:
|
|
if (MyOvmsServerV2) {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 4: stop server v2");
|
|
delete MyOvmsServerV2;
|
|
MyOvmsServerV2 = NULL;
|
|
}
|
|
if (!server.empty()) {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 4: start server v2 for host '%s'", server.c_str());
|
|
MyOvmsServerV2 = new OvmsServerV2("oscv2");
|
|
}
|
|
CfgInitSetStep("4.test.connect");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_OVMS_COMP_CELLULAR
|
|
else if (step == "5.test.start") {
|
|
if (!MyPeripherals || !MyPeripherals->m_cellular_modem) {
|
|
ESP_LOGE(TAG, "CfgInitTicker: step 5: cellular modem not available");
|
|
CfgInitSetStep("done");
|
|
}
|
|
if (MyPeripherals->m_cellular_modem->GetPowerMode() != On) {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 5: cellular modem power on");
|
|
MyPeripherals->m_cellular_modem->SetPowerMode(On);
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInitTicker: step 5: cellular modem enter state NetStart");
|
|
MyPeripherals->m_cellular_modem->SendSetState1(modem::NetStart);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit1: Secure module
|
|
* - enter AP SSID & password
|
|
* - set AP network, restart AP
|
|
* - if user doesn't come back within 5 minutes: revert AP net to OVMS/OVMSinit
|
|
* - else: configure autostart, module password & vehicle auth
|
|
*/
|
|
std::string OvmsWebServer::CfgInit1(PageEntry_t& p, PageContext_t& c, std::string step)
|
|
{
|
|
std::string error;
|
|
std::string moduleid, newpass1, newpass2;
|
|
|
|
if (c.method == "POST") {
|
|
|
|
// check input:
|
|
moduleid = c.getvar("moduleid");
|
|
newpass1 = c.getvar("newpass1");
|
|
newpass2 = c.getvar("newpass2");
|
|
|
|
if (moduleid.empty())
|
|
error += "<li data-input=\"moduleid\">The module ID must not be empty</li>";
|
|
if (moduleid.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-") != std::string::npos)
|
|
error += "<li data-input=\"moduleid\">Module ID may only contain ASCII letters, digits and '-'</li>";
|
|
if (newpass1.length() < 8)
|
|
error += "<li data-input=\"newpass1\">New password must have at least 8 characters</li>";
|
|
if (newpass2 != newpass1)
|
|
error += "<li data-input=\"newpass2\">Passwords do not match</li>";
|
|
|
|
if (error == "") {
|
|
// OK, start test:
|
|
ESP_LOGI(TAG, "CfgInit1: starting test: AP '%s'", moduleid.c_str());
|
|
MyConfig.SetParamValue("vehicle", "id", moduleid);
|
|
MyConfig.SetParamValue("password", "module", newpass1);
|
|
MyConfig.SetParamValue("wifi.ap", moduleid, newpass1);
|
|
MyConfig.SetParamValue("auto", "wifi.mode", "ap");
|
|
MyConfig.SetParamValue("auto", "wifi.ssid.ap", moduleid);
|
|
step = CfgInitSetStep("1.test.start", 5);
|
|
}
|
|
else {
|
|
// output error, return to form:
|
|
error = "<p class=\"lead\">Error!</p><ul class=\"errorlist\">" + error + "</ul>";
|
|
c.alert("danger", error.c_str());
|
|
step = CfgInitSetStep("1");
|
|
}
|
|
}
|
|
else {
|
|
|
|
moduleid = MyConfig.GetParamValue("vehicle", "id");
|
|
|
|
if (step == "1.test.connect") {
|
|
if (MyPeripherals->m_esp32wifi->GetAPSSID() == moduleid) {
|
|
// OK, user connected to new AP: proceed to step 2
|
|
ESP_LOGI(TAG, "CfgInit1: AP test succeeded");
|
|
CfgInitSetStep("2");
|
|
return "2";
|
|
}
|
|
step = CfgInitSetStep("1.fail");
|
|
}
|
|
}
|
|
|
|
// display:
|
|
|
|
if (startsWith(step, "1.test")) {
|
|
c.printf(
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What happens now?</p>"
|
|
"<p>The OVMS Wifi access point now gets <strong>reconfigured</strong> to the new module ID and password,"
|
|
" so you will now lose connection to the module and need to reconnect using the new ID and password.</p>"
|
|
"<p>Open your Wifi configuration now and connect to the new Wifi network <code>%s</code>,"
|
|
" then reload this page.</p>"
|
|
"<p><a class=\"btn btn-default\" href=\"javascript:location.reload(true)\">Reload page</a></p>"
|
|
"<p>If you can't reconnect, <strong>don't panic</strong>.</p>"
|
|
"<p>The module will revert to the factory default configuration if you don't reconnect within 5 minutes.</p>"
|
|
"<p>Alternatively, you may simply unplug the module and start again.</p>"
|
|
"</div>"
|
|
, moduleid.c_str());
|
|
return "";
|
|
}
|
|
|
|
if (step == "1.fail") {
|
|
ESP_LOGI(TAG, "CfgInit1: AP test failed");
|
|
if (MyBoot.GetEarlyCrashCount() > 0) {
|
|
c.alert("warning",
|
|
"<p class=\"lead\">Crash reboot detected!</p>"
|
|
"<p><strong>Sorry, this really should not happen.</strong></p>"
|
|
"<p>Please check if the problem persists, if so note your configuration inputs"
|
|
" and ask for support on the OpenVehicles forum.</p>"
|
|
"<p>You can also try a different confiuration, as the problem is most probably"
|
|
" related to your specific setup.</p>");
|
|
}
|
|
else {
|
|
c.alert("warning",
|
|
"<p class=\"lead\">Something went wrong.</p>"
|
|
"<p>You did not connect to the new access point. Please check your ID and password and try again.</p>");
|
|
}
|
|
}
|
|
|
|
// Wizard status:
|
|
c.alert("info",
|
|
"<p class=\"lead\">Quick setup wizard</p>"
|
|
"<ol>"
|
|
"<li><strong>Secure module access</strong></li>"
|
|
"<li>Connect module to internet (via Wifi)</li>"
|
|
"<li>Update to latest firmware version</li>"
|
|
"<li>Configure vehicle type and server</li>"
|
|
"<li>Configure cellular modem (if equipped)</li>"
|
|
"</ol>");
|
|
|
|
// create some random passwords:
|
|
std::ostringstream pwsugg;
|
|
srand48(StdMetrics.ms_m_monotonic->AsInt() * StdMetrics.ms_m_freeram->AsInt());
|
|
pwsugg << "<p>Inspiration:";
|
|
for (int i=0; i<5; i++)
|
|
pwsugg << " <code class=\"autoselect\">" << c.encode_html(pwgen(12)) << "</code>";
|
|
pwsugg << "</p>";
|
|
|
|
// generate form:
|
|
c.panel_start("primary", "Step 1/5: Secure Module");
|
|
|
|
c.print(
|
|
"<p>Your module may currently be accessed by anyone in Wifi range using the default"
|
|
" password, so we first should do something about that.</p>"
|
|
"<p>This setup step will change both the Wifi access point and the module admin"
|
|
" password, so the module is secured for both local and remote access.</p>");
|
|
|
|
c.form_start(p.uri);
|
|
c.input_text("Module ID", "moduleid", moduleid.c_str(), "Use ASCII letters, digits and '-'",
|
|
"<p>Enter a unique personal module/vehicle ID, e.g. your nickname or vehicle license plate number.</p>"
|
|
"<p>If already registered at an OVMS server, use the vehicle ID as registered.</p>"
|
|
"<p>The ID will be used for the Wifi access point, check if it is free to use within your Wifi range.</p>",
|
|
"autocomplete=\"section-module username\"");
|
|
c.input_password("Module password", "newpass1", "",
|
|
"Enter a password, min. 8 characters", pwsugg.str().c_str(), "autocomplete=\"section-module new-password\"");
|
|
c.input_password("Repeat password", "newpass2", "",
|
|
"Repeat password to verify entry", NULL, "autocomplete=\"section-module new-password\"");
|
|
c.print(
|
|
"<div class=\"form-group\">"
|
|
"<div class=\"col-sm-offset-3 col-sm-9\">"
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"proceed\">Proceed</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"skip\">Skip step</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"abort\">Abort setup</button> "
|
|
"</div>"
|
|
"</div>");
|
|
c.form_end();
|
|
|
|
c.panel_end();
|
|
|
|
c.alert("info",
|
|
"<p class=\"lead\">What will happen next?</p>"
|
|
"<p>The OVMS Wifi access point will be <strong>reconfigured</strong> to the new module ID and password,"
|
|
" so you will lose connection to the module and need to reconnect using the new ID and password.</p>"
|
|
"<p>If you can't reconnect, <strong>don't panic</strong>.</p>"
|
|
"<p>The module will revert to the old configuration if you don't reconnect within 5 minutes.</p>"
|
|
"<p>Alternatively, you may simply unplug the module and start again.</p>");
|
|
|
|
return "";
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit2: Connect to Wifi network
|
|
* - TODO: show list of SSIDs from scan
|
|
* - Select/input SSID & password
|
|
* - Test connection
|
|
*/
|
|
std::string OvmsWebServer::CfgInit2(PageEntry_t& p, PageContext_t& c, std::string step)
|
|
{
|
|
std::string error;
|
|
std::string ssid, pass, dns;
|
|
|
|
if (c.method == "POST") {
|
|
|
|
// check input:
|
|
ssid = c.getvar("ssid");
|
|
pass = c.getvar("pass");
|
|
if (pass.empty())
|
|
pass = MyConfig.GetParamValue("wifi.ssid", ssid);
|
|
dns = c.getvar("dns");
|
|
|
|
if (ssid.empty())
|
|
error += "<li data-input=\"ssid\">The Wifi network ID must not be empty</li>";
|
|
if (pass.empty())
|
|
error += "<li data-input=\"pass\">The Wifi network password must not be empty</li>";
|
|
|
|
if (error == "") {
|
|
// OK, start test:
|
|
MyConfig.SetParamValue("wifi.ssid", ssid, pass);
|
|
MyConfig.SetParamValue("auto", "wifi.mode", "apclient");
|
|
MyConfig.SetParamValue("auto", "wifi.ssid.client", ssid);
|
|
MyConfig.SetParamValue("network", "dns", dns);
|
|
c.alert("success", "<p class=\"lead\">OK.</p>");
|
|
step = CfgInitSetStep("2.test.start", 5);
|
|
}
|
|
else {
|
|
// output error, return to form:
|
|
error = "<p class=\"lead\">Error!</p><ul class=\"errorlist\">" + error + "</ul>";
|
|
c.alert("danger", error.c_str());
|
|
step = CfgInitSetStep("2");
|
|
}
|
|
}
|
|
else {
|
|
ssid = MyConfig.GetParamValue("auto", "wifi.ssid.client");
|
|
pass = MyConfig.GetParamValue("wifi.ssid", ssid);
|
|
dns = MyConfig.GetParamValue("network", "dns");
|
|
}
|
|
|
|
// display:
|
|
|
|
if (startsWith(step, "2.test")) {
|
|
c.printf(
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What happens now?</p>"
|
|
"<p>The OVMS now tries to connect to the Wifi network <code>%s</code>, if successful"
|
|
" then tries to contact the OpenVehicles server to check the internet access.</p>"
|
|
"<p>Please be patient, this can take up to 2 minutes.</p>"
|
|
"<p><strong>Note:</strong> the OVMS Wifi access point will continue serving network <code>%s</code>, but you"
|
|
" may <strong>lose connection</strong> to the module and <strong>need to reconnect manually</strong>.</p>"
|
|
"<p>Have an eye on your Wifi association to see if/when you need to reconnect manually.</p>"
|
|
"</div>"
|
|
, ssid.c_str()
|
|
, MyConfig.GetParamValue("auto", "wifi.ssid.ap").c_str());
|
|
OutputReconnect(p, c, "Testing internet connection…");
|
|
return "";
|
|
}
|
|
|
|
if (step == "2.fail.connect") {
|
|
c.printf(
|
|
"<div class=\"alert alert-warning\">"
|
|
"<p class=\"lead\">Wifi connection failed!</p>"
|
|
"<p>The module could not connect to the Wifi network <code>%s</code>.</p>"
|
|
"<p>Please check SSID & password, then try again.</p>"
|
|
"</div>"
|
|
, ssid.c_str());
|
|
}
|
|
else if (step == "2.fail.server") {
|
|
c.printf(
|
|
"<div class=\"alert alert-warning\">"
|
|
"<p class=\"lead\">Server access failed!</p>"
|
|
"<p>The module could not retrieve the latest version info from the OpenVehicles"
|
|
" server using Wifi network <code>%s</code>.</p>"
|
|
"<p>This may be due to a network or a temporary server problem.</p>"
|
|
"<p>Please check the network and try again.</p>"
|
|
"</div>"
|
|
, ssid.c_str());
|
|
}
|
|
|
|
// Wizard status:
|
|
c.alert("info",
|
|
"<p class=\"lead\">Quick setup wizard</p>"
|
|
"<ol>"
|
|
"<li>Secure module access <span class=\"checkmark\">✔</span></li>"
|
|
"<li><strong>Connect module to internet (via Wifi)</strong></li>"
|
|
"<li>Update to latest firmware version</li>"
|
|
"<li>Configure vehicle type and server</li>"
|
|
"<li>Configure cellular modem (if equipped)</li>"
|
|
"</ol>");
|
|
|
|
// generate form:
|
|
c.panel_start("primary", "Step 2/5: Connect to Internet");
|
|
|
|
c.print(
|
|
"<p>Your module now needs an internet connection to download the latest firmware"
|
|
" version from the OpenVehicles server.</p>");
|
|
|
|
c.form_start(p.uri);
|
|
|
|
c.printf(
|
|
"<div class=\"form-group\">\n"
|
|
"<label class=\"control-label col-sm-3\" for=\"input-ssid\">Wifi network SSID:</label>\n"
|
|
"<div class=\"col-sm-9\">\n"
|
|
"<div class=\"input-group\">\n"
|
|
"<input type=\"text\" class=\"form-control\" placeholder=\"Enter Wifi SSID\" name=\"ssid\"\n"
|
|
"id=\"input-ssid\" value=\"%s\" autocomplete=\"section-wifi-client username\">\n"
|
|
"<div class=\"input-group-btn\">\n"
|
|
"<button type=\"button\" class=\"btn btn-default action-wifiscan\" data-target=\"#input-ssid\">"
|
|
"<span class=\"hidden-xs\">Select</span><span class=\"hidden-sm hidden-md hidden-lg\">⊙</span>"
|
|
"</button>\n"
|
|
"</div>\n"
|
|
"</div>\n"
|
|
"<span class=\"help-block\">\n"
|
|
"<p>Please enter or select a Wifi network that provides access to the internet.</p>\n"
|
|
"<p>Note: you may later configure the OVMS to select among multiple Wifi networks.</p>\n"
|
|
"</span>\n"
|
|
"</div>\n"
|
|
"</div>\n"
|
|
, ssid.c_str());
|
|
/*
|
|
c.input_text("Wifi network SSID", "ssid", ssid.c_str(), "Enter Wifi SSID",
|
|
"<p>Please enter a Wifi network that provides access to the internet.</p>"
|
|
"<p>Note: you may later configure the OVMS to select among multiple Wifi networks.</p>",
|
|
"autocomplete=\"section-wifi-client username\"");
|
|
*/
|
|
c.input_password("Wifi network password", "pass", "",
|
|
(pass.empty()) ? "Enter Wifi password" : "Empty = no change (retry)",
|
|
NULL, "autocomplete=\"section-wifi-client current-password\"");
|
|
|
|
c.input_radio_start("DNS server", "dns");
|
|
c.input_radio_option("dns", "Use default DNS of the Wifi network", "" , dns == "");
|
|
c.input_radio_option("dns", "Use Google public DNS (<code>8.8.8.8 8.8.4.4</code>)", "8.8.8.8 8.8.4.4" , dns == "8.8.8.8 8.8.4.4");
|
|
c.input_radio_option("dns", "Use Cloudflare public DNS (<code>1.1.1.1</code>)", "1.1.1.1" , dns == "1.1.1.1");
|
|
c.input_radio_end(
|
|
"<p>If the server connection fails with your default DNS, try one of the other options.</p>");
|
|
|
|
c.print(
|
|
"<div class=\"form-group\">"
|
|
"<div class=\"col-sm-offset-3 col-sm-9\">"
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"proceed\">Proceed</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"skip\">Skip step</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"abort\">Abort setup</button> "
|
|
"</div>"
|
|
"</div>");
|
|
c.form_end();
|
|
|
|
c.panel_end();
|
|
|
|
c.print(
|
|
"<style>\n"
|
|
".table>tbody>tr.active>td, .table>tbody>tr.active:hover>td {\n"
|
|
"background-color: #bed2e3;\n"
|
|
"cursor: pointer;\n"
|
|
"}\n"
|
|
".night .table>tbody>tr.active>td {\n"
|
|
"background-color: #293746;\n"
|
|
"cursor: pointer;\n"
|
|
"}\n"
|
|
"</style>\n"
|
|
"<script>"
|
|
"$('.action-wifiscan').on('click', function(){\n"
|
|
"var tgt = $(this).data(\"target\");\n"
|
|
"var $tgt = $(tgt);\n"
|
|
"var ssid_sel = \"\";\n"
|
|
"var $dlg = $('<div id=\"wifiscan-dialog\" />').dialog({\n"
|
|
"size: 'lg',\n"
|
|
"title: 'Select Wifi Network',\n"
|
|
"body:\n"
|
|
"'<p id=\"wifiscan-info\">Scanning, please wait…</p>'+\n"
|
|
"'<table id=\"wifiscan-list\" class=\"table table-condensed table-border table-striped table-hover\">'+\n"
|
|
"'<thead><tr>'+\n"
|
|
"'<th class=\"col-xs-4\">AP SSID</th>'+\n"
|
|
"'<th class=\"col-xs-4 hidden-xs\">MAC Address</th>'+\n"
|
|
"'<th class=\"col-xs-1 text-center\">Chan</th>'+\n"
|
|
"'<th class=\"col-xs-1 text-center\">RSSI</th>'+\n"
|
|
"'<th class=\"col-xs-2 hidden-xs\">Auth</th>'+\n"
|
|
"'</tr></thead><tbody/></table>',\n"
|
|
"buttons: [\n"
|
|
"{ label: 'Cancel' },\n"
|
|
"{ label: 'Select', btnClass: 'primary', action: function(input) { $tgt.val(ssid_sel); } },\n"
|
|
"],\n"
|
|
"});\n"
|
|
"var $tb = $dlg.find('#wifiscan-list > tbody'), $info = $dlg.find('#wifiscan-info');\n"
|
|
"$tb.on(\"click\", \"tr\", function(ev) {\n"
|
|
"var $tr = $(this);\n"
|
|
"$tr.addClass(\"active\").siblings().removeClass(\"active\");\n"
|
|
"ssid_sel = $tr.children().first().text();\n"
|
|
"if (ssid_sel == '<HIDDEN>') ssid_sel = '';\n"
|
|
"}).on(\"dblclick\", \"tr\", function(ev) {\n"
|
|
"$dlg.modal(\"hide\");\n"
|
|
"$tgt.val(ssid_sel);\n"
|
|
"});\n"
|
|
"$dlg.addClass(\"loading\");\n"
|
|
"loadcmd(\"wifi scan -j\").then(function(output) {\n"
|
|
"$dlg.removeClass(\"loading\");\n"
|
|
"var res = JSON.parse(output);\n"
|
|
"if (res.error) {\n"
|
|
"$info.text(res.error);\n"
|
|
"} else if (res.list.length == 0) {\n"
|
|
"$info.text(\"Sorry, no networks found.\");\n"
|
|
"} else {\n"
|
|
"var i, ap;\n"
|
|
"$info.text(\"Available networks sorted by signal strength:\");\n"
|
|
"for (i = 0; i < res.list.length; i++) {\n"
|
|
"ap = res.list[i];\n"
|
|
"$('<tr><td>'+encode_html(ap.ssid)+'</td><td class=\"hidden-xs\">'+ap.bssid+\n"
|
|
"'</td><td class=\"text-center\">'+ap.chan+'</td><td class=\"text-center\">'+ap.rssi+\n"
|
|
"'</td><td class=\"hidden-xs\">'+ap.auth+'</td></tr>').appendTo($tb);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"});\n"
|
|
"});\n"
|
|
"</script>");
|
|
|
|
c.printf(
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What will happen next?</p>"
|
|
"<p>The OVMS will <strong>try to connect</strong> to the Wifi network and then try to"
|
|
" contact the OpenVehicles server to check the internet access.</p>"
|
|
"<p>The OVMS access point will continue serving network <code>%s</code>, but you may"
|
|
" <strong>temporarily lose connection</strong> to the network and need to reconnect manually.</p>"
|
|
"<p>If you can't reconnect, <strong>don't panic</strong>.</p>"
|
|
"<p>The module will revert to the old configuration if you don't reconnect within 5 minutes.</p>"
|
|
"<p>Alternatively, you may simply unplug the module and start again.</p>"
|
|
"</div>"
|
|
, MyConfig.GetParamValue("auto", "wifi.ssid.ap").c_str());
|
|
|
|
return "";
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit3: Update firmware
|
|
*/
|
|
std::string OvmsWebServer::CfgInit3(PageEntry_t& p, PageContext_t& c, std::string step)
|
|
{
|
|
#ifdef CONFIG_OVMS_COMP_OTA
|
|
std::string error;
|
|
std::string action;
|
|
std::string server;
|
|
ota_info info;
|
|
|
|
if (c.method == "POST") {
|
|
action = c.getvar("action");
|
|
if (action == "reboot") {
|
|
// reboot, then proceed to step 4:
|
|
CfgInitSetStep("4");
|
|
OutputReboot(p, c);
|
|
return "";
|
|
}
|
|
else if (action == "change") {
|
|
step = CfgInitSetStep("3");
|
|
}
|
|
else {
|
|
// set server, start update:
|
|
server = c.getvar("server");
|
|
if (!server.empty())
|
|
MyConfig.SetParamValue("ota", "server", server);
|
|
step = CfgInitSetStep("3.update");
|
|
}
|
|
}
|
|
|
|
if (server.empty())
|
|
server = MyConfig.GetParamValue("ota", "server");
|
|
if (server.empty())
|
|
server = "https://ovms-ota.bit-cloud.de";
|
|
|
|
MyOTA.GetStatus(info, true);
|
|
|
|
// check if update has been successful:
|
|
if (step == "3.update") {
|
|
if (!info.version_server.empty() && !info.update_available) {
|
|
ESP_LOGI(TAG, "CfgInit3: firmware up to date, proceeding to step 4");
|
|
c.alert("success", "<p class=\"lead\">Firmware up to date.</p>");
|
|
return CfgInitSetStep("4");
|
|
}
|
|
}
|
|
|
|
// output:
|
|
|
|
if (info.version_server.empty()) {
|
|
c.printf(
|
|
"<div class=\"alert alert-danger\">"
|
|
"<p class=\"lead\">Update server currently unavailable!</p>"
|
|
"<p>Retrieving the firmware version from server <code>%s</code> failed.</p>"
|
|
"<p>Please select another server or try again later.</p>"
|
|
"</div>"
|
|
, server.c_str());
|
|
// back to input:
|
|
step = CfgInitSetStep("3");
|
|
}
|
|
|
|
if (step == "3.update") {
|
|
// Flash & reboot:
|
|
c.print(
|
|
"<div class=\"modal fade\" id=\"flash-dialog\" role=\"dialog\" data-backdrop=\"static\" data-keyboard=\"false\">"
|
|
"<div class=\"modal-dialog modal-lg\">"
|
|
"<div class=\"modal-content\">"
|
|
"<div class=\"modal-header\">"
|
|
"<h4 class=\"modal-title\">Flashing…</h4>"
|
|
"</div>"
|
|
"<div class=\"modal-body\">"
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What happens now?</p>"
|
|
"<p>The module now downloads and installs the latest firmware version from the update server.</p>"
|
|
"<p>This will normally take about 30-60 seconds, depending on your internet speed.</p>"
|
|
"<p>When finished (\"flash was successful\"), click \"Reboot now\" to restart the module"
|
|
" using the new firmware.</p>"
|
|
"<p>After reboot and relogin, the setup will automatically continue.</p>"
|
|
"<p><strong>Don't panic</strong> if something goes wrong and the module gets stuck in the update.</p>"
|
|
"<p>You can simply unplug and restart the module, it will show the update form again so you can choose to"
|
|
" retry, try another server or skip the update.</p>"
|
|
"</div>"
|
|
"<pre id=\"output\"></pre>"
|
|
"</div>"
|
|
"<div class=\"modal-footer\">"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"reboot\">Reboot now</button>"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"change\">Change server</button>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"<script>"
|
|
"function setloading(sel, on){"
|
|
"$(sel+\" button\").prop(\"disabled\", on);"
|
|
"if (on) $(sel).addClass(\"loading\");"
|
|
"else $(sel).removeClass(\"loading\");"
|
|
"}"
|
|
"after(0.1, function(){"
|
|
"$(\"#output\").text(\"Processing… (do not interrupt, may take some minutes)\\n\");"
|
|
"setloading(\"#flash-dialog\", true);"
|
|
"$(\"#flash-dialog\").modal(\"show\");"
|
|
"loadcmd(\"ota flash http\", \"+#output\").done(function(resp){"
|
|
"setloading(\"#flash-dialog\", false);"
|
|
"});"
|
|
"});"
|
|
"$(\"#flash-dialog button[name=action]\").on(\"click\", function(ev){"
|
|
"$(\"#flash-dialog\").removeClass(\"fade\").modal(\"hide\");"
|
|
"loaduri(\"#main\", \"post\", \"/cfg/init\", { \"action\": $(this).val() });"
|
|
"});"
|
|
"</script>");
|
|
return "";
|
|
}
|
|
|
|
// Wizard status:
|
|
c.alert("info",
|
|
"<p class=\"lead\">Quick setup wizard</p>"
|
|
"<ol>"
|
|
"<li>Secure module access <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Connect module to internet (via Wifi) <span class=\"checkmark\">✔</span></li>"
|
|
"<li><strong>Update to latest firmware version</strong></li>"
|
|
"<li>Configure vehicle type and server</li>"
|
|
"<li>Configure cellular modem (if equipped)</li>"
|
|
"</ol>");
|
|
|
|
// form:
|
|
c.panel_start("primary", "Step 3/5: Update Firmware");
|
|
c.form_start(p.uri);
|
|
c.input_radio_start("Update server", "server");
|
|
c.input_radio_option("server", "Europe, Germany (ovms-ota.bit-cloud.de)",
|
|
"https://ovms-ota.bit-cloud.de" , server == "https://ovms-ota.bit-cloud.de");
|
|
c.input_radio_option("server", "Asia-Pacific (openvehicles.com)",
|
|
"https://api.openvehicles.com/firmware/ota" , server == "https://api.openvehicles.com/firmware/ota");
|
|
c.input_radio_option("server", "Europe (dexters-web.de)",
|
|
"https://ovms.dexters-web.de/firmware/ota", server == "https://ovms.dexters-web.de/firmware/ota");
|
|
c.input_radio_end();
|
|
c.print(
|
|
"<div class=\"form-group\">"
|
|
"<div class=\"col-sm-offset-3 col-sm-9\">"
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"proceed\">Proceed</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"skip\">Skip step</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"abort\">Abort setup</button> "
|
|
"</div>"
|
|
"</div>");
|
|
c.form_end();
|
|
c.panel_end();
|
|
|
|
c.alert("info",
|
|
"<p class=\"lead\">What will happen next?</p>"
|
|
"<p>The module will check the update server for a new firmware version, and automatically download"
|
|
" and install it if an update is available.</p>"
|
|
"<p><strong>Don't panic</strong> if something goes wrong and the module gets stuck in the update.</p>"
|
|
"<p>You can simply unplug and restart the module, it will show this form again so you can choose to"
|
|
" retry or try another server.</p>");
|
|
|
|
return "";
|
|
#else
|
|
ESP_LOGI(TAG, "CfgInit3: OTA disabled, proceeding to step 4");
|
|
c.alert("danger", "<p class=\"lead\">Firmware cannot be updated, OTA is disabled.</p>");
|
|
return CfgInitSetStep("4");
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit4: vehicle & server
|
|
* - TODO: server account auto create API (should only need an email address)
|
|
*/
|
|
std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::string step)
|
|
{
|
|
std::string error, info;
|
|
std::string vehicletype, units_distance;
|
|
std::string server, vehicleid, password;
|
|
|
|
if (c.method == "POST") {
|
|
if (startsWith(step, "4.test")) {
|
|
// action from test dialog:
|
|
if (c.getvar("action") == "keep") {
|
|
if (!MyConfig.GetParamValue("server.v2", "server").empty())
|
|
MyConfig.SetParamValueBool("auto", "server.v2", true);
|
|
return CfgInitSetStep("5");
|
|
}
|
|
else if (c.getvar("action") == "change") {
|
|
step = CfgInitSetStep("4");
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
else {
|
|
// process form input:
|
|
vehicletype = c.getvar("vehicletype");
|
|
units_distance = c.getvar("units_distance");
|
|
server = c.getvar("server");
|
|
vehicleid = c.getvar("vehicleid");
|
|
password = c.getvar("password");
|
|
if (password.empty())
|
|
password = MyConfig.GetParamValue("password", "module");
|
|
|
|
if (vehicleid.length() == 0)
|
|
error += "<li data-input=\"vehicleid\">Vehicle ID must not be empty</li>";
|
|
if (vehicleid.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-") != std::string::npos)
|
|
error += "<li data-input=\"vehicleid\">Vehicle ID may only contain ASCII letters, digits and '-'</li>";
|
|
|
|
// configure vehicle:
|
|
MyConfig.SetParamValue("vehicle", "units.distance", units_distance);
|
|
MyConfig.SetParamValue("auto", "vehicle.type", vehicletype);
|
|
|
|
// configure server:
|
|
MyConfig.SetParamValue("server.v2", "server", server);
|
|
MyConfig.SetParamValueBool("server.v2", "tls", true);
|
|
if (error == "" && !server.empty()) {
|
|
MyConfig.SetParamValue("vehicle", "id", vehicleid);
|
|
MyConfig.SetParamValue("password","server.v2", password);
|
|
}
|
|
|
|
if (error == "") {
|
|
// start test:
|
|
step = CfgInitSetStep("4.test.start", 3);
|
|
}
|
|
else {
|
|
// output error, return to form:
|
|
error = "<p class=\"lead\">Error!</p><ul class=\"errorlist\">" + error + "</ul>";
|
|
c.alert("danger", error.c_str());
|
|
step = CfgInitSetStep("4");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c.method == "GET") {
|
|
// read configuration:
|
|
vehicleid = MyConfig.GetParamValue("vehicle", "id");
|
|
vehicletype = MyConfig.GetParamValue("auto", "vehicle.type");
|
|
units_distance = MyConfig.GetParamValue("vehicle", "units.distance", "K");
|
|
server = MyConfig.GetParamValue("server.v2", "server");
|
|
password = MyConfig.GetParamValue("password","server.v2");
|
|
|
|
// default data server = ota server:
|
|
if (server.empty()) {
|
|
server = MyConfig.GetParamValue("ota", "server");
|
|
if (startsWith(server, "ovms-ota.bit-cloud.de"))
|
|
server = "ovms-server.bit-cloud.de";
|
|
else
|
|
server = "ovms.dexters-web.de";
|
|
}
|
|
}
|
|
|
|
// output:
|
|
|
|
if (startsWith(step, "4.test")) {
|
|
c.print(
|
|
"<div class=\"modal fade\" id=\"test-dialog\" role=\"dialog\" data-backdrop=\"static\" data-keyboard=\"false\">"
|
|
"<div class=\"modal-dialog modal-lg\">"
|
|
"<div class=\"modal-content\">"
|
|
"<div class=\"modal-header\">"
|
|
"<h4 class=\"modal-title\">Testing Vehicle & Server</h4>"
|
|
"</div>"
|
|
"<div class=\"modal-body\">"
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What happens now?</p>"
|
|
"<p>The module now configures the vehicle type, then tries to connect to the OVMS data server.</p>"
|
|
"<p>This may take a few seconds, the progress is shown below.</p>"
|
|
"</div>"
|
|
"<pre class=\"monitor\" data-updcmd=\"vehicle status\" data-events=\"vehicle.type\" data-updcnt=\"-1\" data-updint=\"5\"></pre>"
|
|
"<pre class=\"monitor\" data-updcmd=\"server v2 status\" data-events=\"server.v2\" data-updcnt=\"-1\" data-updint=\"5\"></pre>"
|
|
"</div>"
|
|
"<div class=\"modal-footer\">"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"keep\">Keep & proceed</button>"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"change\">Change configuration</button>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"<script>"
|
|
"after(0.1, function(){"
|
|
"monitorInit();"
|
|
"$(\"#test-dialog\").modal(\"show\");"
|
|
"});"
|
|
"$(\"#test-dialog button[name=action]\").on(\"click\", function(ev){"
|
|
"$(\"#test-dialog\").removeClass(\"fade\").modal(\"hide\");"
|
|
"loaduri(\"#main\", \"post\", \"/cfg/init\", { \"action\": $(this).val() });"
|
|
"});"
|
|
"</script>");
|
|
return "";
|
|
}
|
|
|
|
if (step == "4.fail.vehicle") {
|
|
c.printf(
|
|
"<div class=\"alert alert-warning\">"
|
|
"<p class=\"lead\">Vehicle type setup failed!</p>"
|
|
"<p><strong>Sorry, this really should not happen.</strong></p>"
|
|
"<p>Please use a generic vehicle type like the demonstration vehicle or OBDII for the moment"
|
|
" and ask for support on the OpenVehicles forum.</p>"
|
|
"</div>");
|
|
}
|
|
|
|
// Wizard status:
|
|
c.alert("info",
|
|
"<p class=\"lead\">Quick setup wizard</p>"
|
|
"<ol>"
|
|
"<li>Secure module access <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Connect module to internet (via Wifi) <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Update to latest firmware version <span class=\"checkmark\">✔</span></li>"
|
|
"<li><strong>Configure vehicle type and server</strong></li>"
|
|
"<li>Configure cellular modem (if equipped)</li>"
|
|
"</ol>");
|
|
|
|
// form:
|
|
c.panel_start("primary", "Step 4/5: Vehicle & Server");
|
|
c.form_start(p.uri);
|
|
|
|
c.input_select_start("Vehicle type", "vehicletype");
|
|
c.input_select_option("—", "", vehicletype.empty());
|
|
for (OvmsVehicleFactory::map_vehicle_t::iterator k=MyVehicleFactory.m_vmap.begin(); k!=MyVehicleFactory.m_vmap.end(); ++k)
|
|
c.input_select_option(k->second.name, k->first, (vehicletype == k->first));
|
|
c.input_select_end();
|
|
|
|
c.input_radiobtn_start("Distance units", "units_distance");
|
|
c.input_radiobtn_option("units_distance", "Kilometers", "K", units_distance == "K");
|
|
c.input_radiobtn_option("units_distance", "Miles", "M", units_distance == "M");
|
|
c.input_radiobtn_end();
|
|
|
|
c.input_radio_start("OVMS data server", "server");
|
|
c.input_radio_option("server", "No server connection", "" , server == "");
|
|
c.input_radio_option("server",
|
|
"Europe, Germany (ovms-server.bit-cloud.de) <a href=\"https://ovms.bit-cloud.de/user/register\" target=\"_blank\">Registration</a>",
|
|
"ovms-server.bit-cloud.de" , server == "ovms-server.bit-cloud.de");
|
|
c.input_radio_option("server",
|
|
"Asia-Pacific (openvehicles.com) <a href=\"https://www.openvehicles.com/user/register\" target=\"_blank\">Registration</a>",
|
|
"api.openvehicles.com" , server == "api.openvehicles.com");
|
|
c.input_radio_option("server",
|
|
"Europe (dexters-web.de) <a href=\"https://dexters-web.de/?action=NewAccount\" target=\"_blank\">Registration</a>",
|
|
"ovms.dexters-web.de", server == "ovms.dexters-web.de");
|
|
c.input_radio_end();
|
|
|
|
c.input_text("Vehicle ID", "vehicleid", vehicleid.c_str(), "Use ASCII letters, digits and '-'",
|
|
NULL, "autocomplete=\"section-serverv2 username\"");
|
|
c.input_password("Server password", "password", "", "empty = use module password",
|
|
"<p>Note: enter the password for the <strong>vehicle ID</strong>, <em>not</em> your user account password</p>",
|
|
"autocomplete=\"section-serverv2 current-password\"");
|
|
|
|
c.print(
|
|
"<div class=\"form-group\">"
|
|
"<div class=\"col-sm-offset-3 col-sm-9\">"
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"proceed\">Proceed</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"skip\">Skip step</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"abort\">Abort setup</button> "
|
|
"</div>"
|
|
"</div>");
|
|
c.form_end();
|
|
c.panel_end();
|
|
|
|
c.alert("info",
|
|
"<p class=\"lead\">What will happen next?</p>"
|
|
"<p>The module will configure itself for your vehicle type.</p>"
|
|
"<p>If you have selected a data server, it will try to connect to the server using the"
|
|
" login (ID) and password and display the results.</p>"
|
|
"<p>The progress is shown live and can be cancelled any time.</p>");
|
|
|
|
return "";
|
|
}
|
|
|
|
|
|
/**
|
|
* CfgInit5: modem
|
|
*/
|
|
std::string OvmsWebServer::CfgInit5(PageEntry_t& p, PageContext_t& c, std::string step)
|
|
{
|
|
std::string error, info;
|
|
bool modem = false, gps = false;
|
|
std::string apn, apn_user, apn_pass;
|
|
|
|
if (c.method == "POST") {
|
|
if (startsWith(step, "5.test")) {
|
|
// action from test dialog:
|
|
if (c.getvar("action") == "keep") {
|
|
MyConfig.SetParamValueBool("auto", "modem", true);
|
|
return CfgInitSetStep("done");
|
|
}
|
|
else if (c.getvar("action") == "change") {
|
|
step = CfgInitSetStep("5");
|
|
c.method = "GET";
|
|
}
|
|
}
|
|
else {
|
|
// process form input:
|
|
modem = c.getvar("modem") == "yes";
|
|
apn = c.getvar("apn");
|
|
apn_user = c.getvar("apn_user");
|
|
apn_pass = c.getvar("apn_pass");
|
|
gps = c.getvar("gps") == "yes";
|
|
|
|
MyConfig.SetParamValue("modem", "apn", apn);
|
|
MyConfig.SetParamValue("modem", "apn.user", apn_user);
|
|
MyConfig.SetParamValue("modem", "apn.password", apn_pass);
|
|
|
|
MyConfig.SetParamValueBool("modem", "enable.gps", gps);
|
|
MyConfig.SetParamValueBool("modem", "enable.gpstime", gps);
|
|
|
|
if (modem) {
|
|
// start test:
|
|
ESP_LOGI(TAG, "CfgInit5: starting test");
|
|
step = CfgInitSetStep("5.test.start", 3);
|
|
}
|
|
else {
|
|
ESP_LOGI(TAG, "CfgInit5: cellular modem disabled");
|
|
MyConfig.SetParamValueBool("auto", "modem", false);
|
|
return CfgInitSetStep("done");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c.method == "GET") {
|
|
// read configuration:
|
|
modem = MyConfig.GetParamValueBool("auto", "modem", false);
|
|
apn = MyConfig.GetParamValue("modem", "apn");
|
|
apn_user = MyConfig.GetParamValue("modem", "apn.user");
|
|
apn_pass = MyConfig.GetParamValue("modem", "apn.password");
|
|
gps = MyConfig.GetParamValueBool("modem", "enable.gps", false);
|
|
|
|
// default: hologram
|
|
if (apn.empty())
|
|
apn = "hologram";
|
|
}
|
|
|
|
// output:
|
|
|
|
if (startsWith(step, "5.test")) {
|
|
c.print(
|
|
"<div class=\"modal fade\" id=\"test-dialog\" role=\"dialog\" data-backdrop=\"static\" data-keyboard=\"false\">"
|
|
"<div class=\"modal-dialog modal-lg\">"
|
|
"<div class=\"modal-content\">"
|
|
"<div class=\"modal-header\">"
|
|
"<h4 class=\"modal-title\">Testing cellular modem APN</h4>"
|
|
"</div>"
|
|
"<div class=\"modal-body\">"
|
|
"<div class=\"alert alert-info\">"
|
|
"<p class=\"lead\">What happens now?</p>"
|
|
"<p>The cellular modem now tries to register with the mobile network.</p>"
|
|
"<p>This may take up to a few minutes depending on your mobile provider and the"
|
|
" SIM activation status. The progress is shown below.</p>"
|
|
"<p>The cellular modem is connected when <strong>State: NetMode</strong> and <strong>PPP connected</strong>"
|
|
" have been established.</p>"
|
|
"</div>"
|
|
"<pre class=\"monitor\" data-updcmd=\"cellular status\" data-events=\"system.modem\" data-updcnt=\"-1\" data-updint=\"5\"></pre>"
|
|
"</div>"
|
|
"<div class=\"modal-footer\">"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"keep\">Keep & proceed</button>"
|
|
"<button type=\"button\" class=\"btn btn-default\" name=\"action\" value=\"change\">Change configuration</button>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
"<script>"
|
|
"after(0.1, function(){"
|
|
"monitorInit();"
|
|
"$(\"#test-dialog\").modal(\"show\");"
|
|
"});"
|
|
"$(\"#test-dialog button[name=action]\").on(\"click\", function(ev){"
|
|
"$(\"#test-dialog\").removeClass(\"fade\").modal(\"hide\");"
|
|
"loaduri(\"#main\", \"post\", \"/cfg/init\", { \"action\": $(this).val() });"
|
|
"});"
|
|
"</script>");
|
|
return "";
|
|
}
|
|
|
|
// Wizard status:
|
|
c.alert("info",
|
|
"<p class=\"lead\">Quick setup wizard</p>"
|
|
"<ol>"
|
|
"<li>Secure module access <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Connect module to internet (via Wifi) <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Update to latest firmware version <span class=\"checkmark\">✔</span></li>"
|
|
"<li>Configure vehicle type and server <span class=\"checkmark\">✔</span></li>"
|
|
"<li><strong>Configure cellular modem (if equipped)</strong></li>"
|
|
"</ol>");
|
|
|
|
// form:
|
|
c.panel_start("primary", "Step 5/5: Cellular Modem");
|
|
c.form_start(p.uri);
|
|
|
|
std::string iccid = StdMetrics.ms_m_net_mdm_iccid->AsString();
|
|
if (!iccid.empty()) {
|
|
info = "<code class=\"autoselect\">" + iccid + "</code>";
|
|
} else {
|
|
info =
|
|
"<div class=\"receiver\">"
|
|
"<code class=\"autoselect\" data-metric=\"m.net.mdm.iccid\">(power cellular modem on to read)</code>"
|
|
" "
|
|
"<button class=\"btn btn-default\" data-cmd=\"power cellular on\" data-target=\"#pso\">Power cellular modem on</button>"
|
|
" "
|
|
"<samp id=\"pso\" class=\"samp-inline\"></samp>"
|
|
"</div>"
|
|
"<script>"
|
|
"$(\".receiver\").on(\"msg:metrics\", function(e, update){"
|
|
"$(this).find(\"[data-metric]\").each(function(){"
|
|
"if (metrics[$(this).data(\"metric\")] != \"\" && $(this).text() != metrics[$(this).data(\"metric\")])"
|
|
"$(this).text(metrics[$(this).data(\"metric\")]);"
|
|
"});"
|
|
"}).trigger(\"msg:metrics\");"
|
|
"</script>";
|
|
}
|
|
c.input_info("SIM ICCID", info.c_str());
|
|
|
|
c.input_checkbox("Enable cellular modem", "modem", modem);
|
|
c.input_text("APN", "apn", apn.c_str(), NULL,
|
|
"<p>For Hologram, use APN <code>hologram</code> with empty username & password</p>");
|
|
c.input_text("APN username", "apn_user", apn_user.c_str());
|
|
c.input_text("APN password", "apn_pass", apn_pass.c_str());
|
|
|
|
c.input_checkbox("Enable GPS", "gps", gps,
|
|
"<p>This enables the cellular modem GPS receiver. Use this if your car does not provide GPS.</p>");
|
|
|
|
c.print(
|
|
"<div class=\"form-group\">"
|
|
"<div class=\"col-sm-offset-3 col-sm-9\">"
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"proceed\">Proceed</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"skip\">Skip step</button> "
|
|
"<button type=\"submit\" class=\"btn btn-default\" name=\"action\" value=\"abort\">Abort setup</button> "
|
|
"</div>"
|
|
"</div>");
|
|
c.form_end();
|
|
c.panel_end();
|
|
|
|
c.alert("info",
|
|
"<p class=\"lead\">What will happen next?</p>"
|
|
"<p>The cellular modem will try to register with the mobile network using the APN data you enter.</p>"
|
|
"<p>This may take up to a few minutes depending on your mobile provider and the SIM activation"
|
|
" status — if you haven't activated your SIM yet, do so before starting the test.</p>"
|
|
"<p>The progress is shown live and can be cancelled any time.</p>");
|
|
|
|
return "";
|
|
}
|
|
|
|
#endif // WEBSRV_HAVE_SETUPWIZARD
|