Compare commits

..

7 Commits

Author SHA1 Message Date
jomjol
c587ca3224 Update Changelog.md 2025-02-28 20:46:08 +01:00
CaCO3
76f45a5927 Remove html directory on update (#3584)
* delete HTML directory on an update

* delete HTML directory on an update

* rename html folder

* swap HTML folders after extracting

* .

* .

* .

* .

* .

* .

* move SD card check, SD card directories setup and update to before the PSRAM init.
The update should be as early as possible to allow updates even if the PSRAM or cam fails.

* .

* .

* Update Helper.cpp

* Update Helper.h

* .

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: SybexX <Heinrich-Tuning@web.de>
2025-02-26 23:56:49 +01:00
jomjol
063c4827d0 Merge branch 'main' of https://github.com/jomjol/AI-on-the-edge-device 2025-02-26 22:00:27 +01:00
jomjol
c6b8823417 Update Changelog.md 2025-02-26 22:00:20 +01:00
github-actions[bot]
bb5e693077 docs(contributor): contrib-readme-action has updated readme 2025-02-25 18:31:30 +00:00
fsck-block
424df641cc openmetrics endpoint extension (#3521)
* added pre-value and raw-value to openmetrics endpoint

* added flow_error to openmentrics endpoint
2025-02-24 23:07:29 +01:00
CaCO3
8ddbda16bf consolidated reboot and save buttons (#3581)
* config page: consolidated reboot and save button

* various pages: consolidated reboot and save button

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2025-02-24 23:05:32 +01:00
16 changed files with 411 additions and 243 deletions

View File

@@ -1,4 +1,4 @@
## [16.0.0-RC6] - 2024-xx-xx ## [16.0.0-RC6] - 2024-02-28
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0) For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0)
@@ -9,13 +9,25 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
#### Core Changes #### Core Changes
Only changes since RC5 are listed: Only changes since RC5 are listed:
- [#3436](https://github.com/jomjol/AI-on-the-edge-device/pull/3436) Added basic authentification of the Web Interface and the REST API, see https://jomjol.github.io/AI-on-the-edge-device-docs/Password-Protection - [#3436](https://github.com/jomjol/AI-on-the-edge-device/pull/3436) Added basic authentification of the Web Interface and the REST API, see https://jomjol.github.io/AI-on-the-edge-device-docs/Password-Protection
- xxx
- [#3520](https://github.com/jomjol/AI-on-the-edge-device/pull/3520) Rewrite InfluxDB Interfache
- [#3500](https://github.com/jomjol/AI-on-the-edge-device/pull/3500) Update smartled driver
- [#3454](https://github.com/jomjol/AI-on-the-edge-device/pull/3454) Add thermometer
- [#3538](https://github.com/jomjol/AI-on-the-edge-device/pull/3538) add .gz
- [#3565](https://github.com/jomjol/AI-on-the-edge-device/pull/3565) corrected enable/disable texts to make it more intuitive
- [#3568](https://github.com/jomjol/AI-on-the-edge-device/pull/3568) compress all HTML files
**:warning: Please check your Homeassistant instance to make sure it is handled correctly!** **:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
#### Bug Fixes #### Bug Fixes
Only changes since RC5 are listed: Only changes since RC5 are listed:
- xxx - [#3450](https://github.com/jomjol/AI-on-the-edge-device/pull/3450) fix crash fue to empty CAM parameters in migration
- [#3446](https://github.com/jomjol/AI-on-the-edge-device/pull/3446) fix for incorrect decimal shift
## [16.0.0-RC5] - 2024-12-05 ## [16.0.0-RC5] - 2024-12-05

View File

@@ -250,13 +250,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Frank Haverland</b></sub> <sub><b>Frank Haverland</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/Slider0007">
<img src="https://avatars.githubusercontent.com/u/115730895?v=4" width="100;" alt="Slider0007"/>
<br />
<sub><b>Slider0007</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/SybexX"> <a href="https://github.com/SybexX">
<img src="https://avatars.githubusercontent.com/u/587201?v=4" width="100;" alt="SybexX"/> <img src="https://avatars.githubusercontent.com/u/587201?v=4" width="100;" alt="SybexX"/>
@@ -264,6 +257,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>michael</b></sub> <sub><b>michael</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/Slider0007">
<img src="https://avatars.githubusercontent.com/u/115730895?v=4" width="100;" alt="Slider0007"/>
<br />
<sub><b>Slider0007</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/nliaudat"> <a href="https://github.com/nliaudat">
<img src="https://avatars.githubusercontent.com/u/6782613?v=4" width="100;" alt="nliaudat"/> <img src="https://avatars.githubusercontent.com/u/6782613?v=4" width="100;" alt="nliaudat"/>
@@ -368,6 +368,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>parhedberg</b></sub> <sub><b>parhedberg</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/fsck-block">
<img src="https://avatars.githubusercontent.com/u/58307481?v=4" width="100;" alt="fsck-block"/>
<br />
<sub><b>fsck-block</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/slovdahl"> <a href="https://github.com/slovdahl">
<img src="https://avatars.githubusercontent.com/u/1417619?v=4" width="100;" alt="slovdahl"/> <img src="https://avatars.githubusercontent.com/u/1417619?v=4" width="100;" alt="slovdahl"/>
@@ -389,13 +396,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>LordGuilly</b></sub> <sub><b>LordGuilly</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/bilalmirza74">
<img src="https://avatars.githubusercontent.com/u/84387676?v=4" width="100;" alt="bilalmirza74"/>
<br />
<sub><b>Bilal Mirza</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/muggenhor"> <a href="https://github.com/muggenhor">
<img src="https://avatars.githubusercontent.com/u/484066?v=4" width="100;" alt="muggenhor"/> <img src="https://avatars.githubusercontent.com/u/484066?v=4" width="100;" alt="muggenhor"/>
@@ -406,10 +406,17 @@ There are some ideas and feature requests which are not currently being pursued
</tr> </tr>
<tr> <tr>
<td align="center"> <td align="center">
<a href="https://github.com/ppisljar"> <a href="https://github.com/bilalmirza74">
<img src="https://avatars.githubusercontent.com/u/13629809?v=4" width="100;" alt="ppisljar"/> <img src="https://avatars.githubusercontent.com/u/84387676?v=4" width="100;" alt="bilalmirza74"/>
<br /> <br />
<sub><b>Peter Pisljar</b></sub> <sub><b>Bilal Mirza</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/AngryApostrophe">
<img src="https://avatars.githubusercontent.com/u/89547888?v=4" width="100;" alt="AngryApostrophe"/>
<br />
<sub><b>AngryApostrophe</b></sub>
</a> </a>
</td> </td>
<td align="center"> <td align="center">
@@ -426,6 +433,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Ranjana761</b></sub> <sub><b>Ranjana761</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/SURYANSH-RAI">
<img src="https://avatars.githubusercontent.com/u/79277130?v=4" width="100;" alt="SURYANSH-RAI"/>
<br />
<sub><b>SURYANSH RAI</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/SkylightXD"> <a href="https://github.com/SkylightXD">
<img src="https://avatars.githubusercontent.com/u/16561545?v=4" width="100;" alt="SkylightXD"/> <img src="https://avatars.githubusercontent.com/u/16561545?v=4" width="100;" alt="SkylightXD"/>
@@ -433,6 +447,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>SkylightXD</b></sub> <sub><b>SkylightXD</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/ottk3"> <a href="https://github.com/ottk3">
<img src="https://avatars.githubusercontent.com/u/5236802?v=4" width="100;" alt="ottk3"/> <img src="https://avatars.githubusercontent.com/u/5236802?v=4" width="100;" alt="ottk3"/>
@@ -447,8 +463,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Tobias Bieniek</b></sub> <sub><b>Tobias Bieniek</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/tkopczuk"> <a href="https://github.com/tkopczuk">
<img src="https://avatars.githubusercontent.com/u/101632?v=4" width="100;" alt="tkopczuk"/> <img src="https://avatars.githubusercontent.com/u/101632?v=4" width="100;" alt="tkopczuk"/>
@@ -477,6 +491,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>flox_x</b></sub> <sub><b>flox_x</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/gneluka"> <a href="https://github.com/gneluka">
<img src="https://avatars.githubusercontent.com/u/32097881?v=4" width="100;" alt="gneluka"/> <img src="https://avatars.githubusercontent.com/u/32097881?v=4" width="100;" alt="gneluka"/>
@@ -491,8 +507,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>kalwados</b></sub> <sub><b>kalwados</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/kub3let"> <a href="https://github.com/kub3let">
<img src="https://avatars.githubusercontent.com/u/95883234?v=4" width="100;" alt="kub3let"/> <img src="https://avatars.githubusercontent.com/u/95883234?v=4" width="100;" alt="kub3let"/>
@@ -521,13 +535,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>smartboart</b></sub> <sub><b>smartboart</b></sub>
</a> </a>
</td> </td>
<td align="center"> </tr>
<a href="https://github.com/AngryApostrophe"> <tr>
<img src="https://avatars.githubusercontent.com/u/89547888?v=4" width="100;" alt="AngryApostrophe"/>
<br />
<sub><b>AngryApostrophe</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/wetneb"> <a href="https://github.com/wetneb">
<img src="https://avatars.githubusercontent.com/u/309908?v=4" width="100;" alt="wetneb"/> <img src="https://avatars.githubusercontent.com/u/309908?v=4" width="100;" alt="wetneb"/>
@@ -535,8 +544,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Antonin Delpeuch</b></sub> <sub><b>Antonin Delpeuch</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/adarazs"> <a href="https://github.com/adarazs">
<img src="https://avatars.githubusercontent.com/u/6269603?v=4" width="100;" alt="adarazs"/> <img src="https://avatars.githubusercontent.com/u/6269603?v=4" width="100;" alt="adarazs"/>
@@ -572,6 +579,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Dave</b></sub> <sub><b>Dave</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/FarukhS52"> <a href="https://github.com/FarukhS52">
<img src="https://avatars.githubusercontent.com/u/129654632?v=4" width="100;" alt="FarukhS52"/> <img src="https://avatars.githubusercontent.com/u/129654632?v=4" width="100;" alt="FarukhS52"/>
@@ -579,8 +588,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Farookh Zaheer Siddiqui</b></sub> <sub><b>Farookh Zaheer Siddiqui</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/hex7c0"> <a href="https://github.com/hex7c0">
<img src="https://avatars.githubusercontent.com/u/4419146?v=4" width="100;" alt="hex7c0"/> <img src="https://avatars.githubusercontent.com/u/4419146?v=4" width="100;" alt="hex7c0"/>
@@ -616,6 +623,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Joerg Rosenkranz</b></sub> <sub><b>Joerg Rosenkranz</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/Innovatorcloudy"> <a href="https://github.com/Innovatorcloudy">
<img src="https://avatars.githubusercontent.com/u/183274513?v=4" width="100;" alt="Innovatorcloudy"/> <img src="https://avatars.githubusercontent.com/u/183274513?v=4" width="100;" alt="Innovatorcloudy"/>
@@ -623,8 +632,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>KrishCode</b></sub> <sub><b>KrishCode</b></sub>
</a> </a>
</td> </td>
</tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/myxor"> <a href="https://github.com/myxor">
<img src="https://avatars.githubusercontent.com/u/1397377?v=4" width="100;" alt="myxor"/> <img src="https://avatars.githubusercontent.com/u/1397377?v=4" width="100;" alt="myxor"/>
@@ -652,6 +659,13 @@ There are some ideas and feature requests which are not currently being pursued
<br /> <br />
<sub><b>Michael Geissler</b></sub> <sub><b>Michael Geissler</b></sub>
</a> </a>
</td>
<td align="center">
<a href="https://github.com/ppisljar">
<img src="https://avatars.githubusercontent.com/u/13629809?v=4" width="100;" alt="ppisljar"/>
<br />
<sub><b>Peter Pisljar</b></sub>
</a>
</td> </td>
</tr> </tr>
<tbody> <tbody>

View File

@@ -96,15 +96,15 @@ void task_do_Update_ZIP(void *pvParameter)
/* ZIP file got extracted, replace the old html folder with the new one */ /* ZIP file got extracted, replace the old html folder with the new one */
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtml + " to " + outHtmlOld + "..."); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtml + " to " + outHtmlOld + "...");
::rename(outHtml.c_str(), outHtmlOld.c_str()); RenameFolder(outHtml, outHtmlOld);
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtmlTmp + " to " + outHtml + "..."); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtmlTmp + " to " + outHtml + "...");
::rename(outHtmlTmp.c_str(), outHtml.c_str()); RenameFolder(outHtmlTmp, outHtml);
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting folder " + outHtmlOld + "..."); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting folder " + outHtmlOld + "...");
removeFolder(outHtmlOld.c_str(), TAG); removeFolder(outHtmlOld.c_str(), TAG);
if (retfirmware.length() > 0) if (retfirmware.length() > 0)
{ {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin");
ota_update_task(retfirmware); ota_update_task(retfirmware);
} }

View File

@@ -365,19 +365,35 @@ size_t findDelimiterPos(string input, string delimiter)
bool RenameFile(string from, string to) bool RenameFile(string from, string to)
{ {
// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); // ESP_LOGI(logTag, "Renaming File: %s", from.c_str());
/* Delete file */
FILE *fpSourceFile = fopen(from.c_str(), "rb"); FILE *fpSourceFile = fopen(from.c_str(), "rb");
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! // Sourcefile does not exist otherwise there is a mistake when renaming!
if (!fpSourceFile) if (!fpSourceFile)
{ {
ESP_LOGE(TAG, "DeleteFile: File %s existiert nicht!", from.c_str()); ESP_LOGE(TAG, "RenameFile: File %s does not exist!", from.c_str());
return false; return false;
} }
fclose(fpSourceFile); fclose(fpSourceFile);
rename(from.c_str(), to.c_str());
return true;
}
bool RenameFolder(string from, string to)
{
// ESP_LOGI(logTag, "Renaming Folder: %s", from.c_str());
DIR *fpSourceFolder = opendir(from.c_str());
// Sourcefolder does not exist otherwise there is a mistake when renaming!
if (!fpSourceFolder)
{
ESP_LOGE(TAG, "RenameFolder: Folder %s does not exist!", from.c_str());
return false;
}
closedir(fpSourceFolder);
rename(from.c_str(), to.c_str()); rename(from.c_str(), to.c_str());
return true; return true;
@@ -387,7 +403,7 @@ bool FileExists(string filename)
{ {
FILE *fpSourceFile = fopen(filename.c_str(), "rb"); FILE *fpSourceFile = fopen(filename.c_str(), "rb");
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! // Sourcefile does not exist
if (!fpSourceFile) if (!fpSourceFile)
{ {
return false; return false;
@@ -398,22 +414,36 @@ bool FileExists(string filename)
return true; return true;
} }
bool DeleteFile(string fn) bool FolderExists(string foldername)
{ {
// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); DIR *fpSourceFolder = opendir(foldername.c_str());
/* Delete file */
FILE *fpSourceFile = fopen(fn.c_str(), "rb");
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! // Sourcefolder does not exist
if (!fpSourceFolder)
{
return false;
}
closedir(fpSourceFolder);
return true;
}
bool DeleteFile(string filename)
{
// ESP_LOGI(logTag, "Deleting file: %s", filename.c_str());
/* Delete file */
FILE *fpSourceFile = fopen(filename.c_str(), "rb");
// Sourcefile does not exist otherwise there is a mistake in copying!
if (!fpSourceFile) if (!fpSourceFile)
{ {
ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", fn.c_str()); ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", filename.c_str());
return false; return false;
} }
fclose(fpSourceFile); fclose(fpSourceFile);
unlink(filename.c_str());
unlink(fn.c_str());
return true; return true;
} }

View File

@@ -16,10 +16,12 @@ std::size_t file_size(const std::string& file_name);
void FindReplace(std::string& line, std::string& oldString, std::string& newString); void FindReplace(std::string& line, std::string& oldString, std::string& newString);
bool CopyFile(string input, string output); bool CopyFile(string input, string output);
bool DeleteFile(string fn); bool DeleteFile(string filename);
bool RenameFile(string from, string to); bool RenameFile(string from, string to);
bool RenameFolder(string from, string to);
bool MakeDir(std::string _what); bool MakeDir(std::string _what);
bool FileExists(string filename); bool FileExists(string filename);
bool FolderExists(string foldername);
string RundeOutput(double _in, int _anzNachkomma); string RundeOutput(double _in, int _anzNachkomma);

View File

@@ -1,4 +1,6 @@
#include "openmetrics.h" #include "openmetrics.h"
#include "functional"
#include "esp_log.h"
/** /**
* create a singe metric from the given input * create a singe metric from the given input
@@ -10,10 +12,66 @@ std::string createMetric(const std::string &metricName, const std::string &help,
metricName + " " + value + "\n"; metricName + " " + value + "\n";
} }
typedef struct sequence_metric {
const char *name;
const char *help;
const char *type;
std::function<std::string(NumberPost *number)> valueFunc;
} sequence_metric_t;
sequence_metric_t sequenceMetrics[4] = {
{ "flow_value", "current value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnValue;} },
{ "flow_raw_value", "current raw value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnRawValue;} },
{ "flow_pre_value", "previous value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnPreValue;} },
{ "flow_error", "Error message text != 'no error'", "gauge", [](NumberPost *number)-> std::string {return number->ErrorMessageText.compare("no error") == 0 ? "0" : "1";} },
};
std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPost *> &numbers)
{
std::string result;
for (int i = 0; i<sizeof(sequenceMetrics)/sizeof(sequence_metric_t);i++)
{
std::string res;
for (const auto &number : numbers)
{
std::string value = sequenceMetrics[i].valueFunc(number);
if (value.find("N") != std::string::npos) {
value = "NaN";
}
ESP_LOGD("METRICS", "metric=%s, name=%s, value = %s ",sequenceMetrics[i].name,number->name.c_str(), value.c_str());
// only valid data is reported (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#missing-data)
if (value.length() > 0)
{
auto label = number->name;
// except newline, double quote, and backslash (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#abnf)
// to keep it simple, these characters are just removed from the label
replaceAll(label, "\\", "");
replaceAll(label, "\"", "");
replaceAll(label, "\n", "");
res += prefix + "_" + sequenceMetrics[i].name + "{sequence=\"" + label + "\"} " + value + "\n";
}
}
// prepend metadata if a valid metric was created
if (res.length() > 0)
{
res = "# HELP " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].help + "\n"
+ "# TYPE " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].type + "\n"
+ res;
}
result += res;
}
return result;
}
/** /**
* Generate the MetricFamily from all available sequences * Generate the MetricFamily from all available sequences
* @returns the string containing the text wire format of the MetricFamily * @returns the string containing the text wire format of the MetricFamily
**/ **/
/*
std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPost *> &numbers) std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPost *> &numbers)
{ {
std::string res; std::string res;
@@ -41,3 +99,4 @@ std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPo
} }
return res; return res;
} }
*/

View File

@@ -254,6 +254,35 @@ extern "C" void app_main(void)
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================");
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "================================================="); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
// SD card: basic R/W check
// ********************************************
int iSDCardStatus = SDCardCheckRW();
if (iSDCardStatus < 0) {
if (iSDCardStatus <= -1 && iSDCardStatus >= -2) { // write error
StatusLED(SDCARD_CHECK, 1, true);
}
else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) { // read error
StatusLED(SDCARD_CHECK, 2, true);
}
else if (iSDCardStatus == -6) { // delete error
StatusLED(SDCARD_CHECK, 3, true);
}
setSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD); // reduced web interface going to be loaded
}
// SD card: Create further mandatory directories (if not already existing)
// Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence"
// ********************************************
MakeDir("/sdcard/firmware"); // mandatory for OTA firmware update
MakeDir("/sdcard/img_tmp"); // mandatory for setting up alignment marks
MakeDir("/sdcard/demo"); // mandatory for demo mode
MakeDir("/sdcard/config/certs"); // mandatory for mqtt certificates
// Check for updates
// ********************************************
CheckOTAUpdate();
CheckUpdate();
// Init external PSRAM // Init external PSRAM
// ******************************************** // ********************************************
esp_err_t PSRAMStatus = esp_psram_init(); esp_err_t PSRAMStatus = esp_psram_init();
@@ -352,22 +381,6 @@ extern "C" void app_main(void)
} }
} }
// SD card: basic R/W check
// ********************************************
int iSDCardStatus = SDCardCheckRW();
if (iSDCardStatus < 0) {
if (iSDCardStatus <= -1 && iSDCardStatus >= -2) { // write error
StatusLED(SDCARD_CHECK, 1, true);
}
else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) { // read error
StatusLED(SDCARD_CHECK, 2, true);
}
else if (iSDCardStatus == -6) { // delete error
StatusLED(SDCARD_CHECK, 3, true);
}
setSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD); // reduced web interface going to be loaded
}
// Migrate parameter in config.ini to new naming (firmware 15.0 and newer) // Migrate parameter in config.ini to new naming (firmware 15.0 and newer)
// ******************************************** // ********************************************
migrateConfiguration(); migrateConfiguration();
@@ -380,19 +393,6 @@ extern "C" void app_main(void)
// ******************************************** // ********************************************
setCpuFrequency(); setCpuFrequency();
// SD card: Create further mandatory directories (if not already existing)
// Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence"
// ********************************************
MakeDir("/sdcard/firmware"); // mandatory for OTA firmware update
MakeDir("/sdcard/img_tmp"); // mandatory for setting up alignment marks
MakeDir("/sdcard/demo"); // mandatory for demo mode
MakeDir("/sdcard/config/certs"); // mandatory for mqtt certificates
// Check for updates
// ********************************************
CheckOTAUpdate();
CheckUpdate();
// Start SoftAP for initial remote setup // Start SoftAP for initial remote setup
// Note: Start AP if no wlan.ini and/or config.ini available, e.g. SD card empty; function does not exit anymore until reboot // Note: Start AP if no wlan.ini and/or config.ini available, e.g. SD card empty; function does not exit anymore until reboot
// ******************************************** // ********************************************

View File

@@ -37,23 +37,58 @@ void test_createSequenceMetrics()
NumberPost *number_1 = new NumberPost; NumberPost *number_1 = new NumberPost;
number_1->name = "main"; number_1->name = "main";
number_1->ReturnValue = "123.456"; number_1->ReturnValue = "123.456";
number_1->ReturnRawValue = "N23.456";
number_1->ReturnPreValue = "986.543";
number_1->ErrorMessageText = "";
NUMBERS.push_back(number_1); NUMBERS.push_back(number_1);
const std::string metricNamePrefix = "ai_on_the_edge_device"; const std::string metricNamePrefix = "ai_on_the_edge_device";
const std::string metricName = metricNamePrefix + "_flow_value"; const std::string metricName1 = metricNamePrefix + "_flow_value";
const std::string metricName2 = metricNamePrefix + "_flow_raw_value";
const std::string metricName3 = metricNamePrefix + "_flow_pre_value";
const std::string metricName4 = metricNamePrefix + "_flow_error";
std::string expected1 ;
expected1 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" +
metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n";
expected1 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" +
metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n";
expected1 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" +
metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n";
expected1 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" +
metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n";
std::string expected1 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" +
metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n";
TEST_ASSERT_EQUAL_STRING(expected1.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str()); TEST_ASSERT_EQUAL_STRING(expected1.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str());
NumberPost *number_2 = new NumberPost; NumberPost *number_2 = new NumberPost;
number_2->name = "secondary"; number_2->name = "secondary";
number_2->ReturnValue = "1.0"; number_2->ReturnValue = "1.0";
number_2->ReturnRawValue = "01.000";
number_2->ReturnPreValue = "0.987";
number_2->ErrorMessageText = "no error";
NUMBERS.push_back(number_2); NUMBERS.push_back(number_2);
std::string expected2 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" + std::string expected2 ;
metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" + expected2 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" +
metricName + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n"; metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" +
metricName1 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n";
expected2 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" +
metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n" +
metricName2 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnRawValue + "\n";
expected2 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" +
metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n" +
metricName3 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnPreValue + "\n";
expected2 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" +
metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n" +
metricName4 + "{sequence=\"" + number_2->name + "\"} " + "0" + "\n";
TEST_ASSERT_EQUAL_STRING(expected2.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str()); TEST_ASSERT_EQUAL_STRING(expected2.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str());
} }

View File

@@ -79,6 +79,18 @@
transform: translate(-50%,-50%); transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%);
} }
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style> </style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet"> <link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -188,13 +200,11 @@
param; param;
function doReboot() { function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save your changes?")) { var stringota = domainname + "/reboot";
var stringota = domainname + "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
}
} }
function ChangeSelection(){ function ChangeSelection(){
@@ -215,50 +225,45 @@
document.getElementById("overlay").style.display = "block"; document.getElementById("overlay").style.display = "block";
document.getElementById("overlaytext").innerHTML = "Save Alignment Marker..."; document.getElementById("overlaytext").innerHTML = "Save Alignment Marker...";
if (confirm("Are you sure you want to save the new alignment marker configuration?")) { function sleep(ms) {
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));
return new Promise(resolve => setTimeout(resolve, ms)); }
}
async function task() { async function task() {
while (true) { while (true) {
WriteConfigININew(); WriteConfigININew();
if (neueref1 == 1 && neueref2 == 1) { if (neueref1 == 1 && neueref2 == 1) {
UpdateConfigReferences(domainname); UpdateConfigReferences(domainname);
} }
else if (neueref1 == 1) { else if (neueref1 == 1) {
var anzneueref = 1; var anzneueref = 1;
UpdateConfigReference(anzneueref, domainname); UpdateConfigReference(anzneueref, domainname);
} }
else if (neueref2 == 1) { else if (neueref2 == 1) {
var anzneueref = 2; var anzneueref = 2;
UpdateConfigReference(anzneueref, domainname); UpdateConfigReference(anzneueref, domainname);
}
SaveConfigToServer(domainname);
document.getElementById("updatemarker").disabled = false;
// document.getElementById("savemarker").disabled = true;
// document.getElementById("enhancecontrast").disabled = true;
EnDisableItem(false, "savemarker", true);
EnDisableItem(false, "enhancecontrast", true);
document.getElementById("overlay").style.display = "none";
firework.launch('Alignment marker saved. They will get applied after next reboot', 'success', 5000);
return;
} }
}
setTimeout(function () { SaveConfigToServer(domainname);
// Delay so the overlay gets shown
task(); document.getElementById("updatemarker").disabled = false;
}, 1); // document.getElementById("savemarker").disabled = true;
} // document.getElementById("enhancecontrast").disabled = true;
else {
document.getElementById("overlay").style.display = "none"; EnDisableItem(false, "savemarker", true);
EnDisableItem(false, "enhancecontrast", true);
document.getElementById("overlay").style.display = "none";
firework.launch('Alignment marker saved. They will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
return;
}
} }
setTimeout(function () {
// Delay so the overlay gets shown
task();
}, 1);
} }
function EnhanceContrast() { function EnhanceContrast() {

View File

@@ -5,6 +5,20 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Analog ROI</title> <title>Analog ROI</title>
<style>
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="edit_style.css" rel="stylesheet"> <link href="edit_style.css" rel="stylesheet">
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet"> <link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -185,13 +199,11 @@ The following settings are only used for easier setup, they are <b>not</b> persi
domainname = getDomainname(); domainname = getDomainname();
function doReboot() { function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save your changes?")) { var stringota = getDomainname() + "/reboot";
var stringota = getDomainname() + "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
}
} }
function EnDisableAnalog() { function EnDisableAnalog() {
@@ -331,16 +343,14 @@ The following settings are only used for easier setup, they are <b>not</b> persi
} }
function SaveToConfig() { function SaveToConfig() {
if (confirm("Are you sure you want to save the new analog ROI configuration?")) { //_zwcat = getConfigCategory();
//_zwcat = getConfigCategory(); cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; WriteConfigININew();
WriteConfigININew(); SaveConfigToServer(domainname);
SaveConfigToServer(domainname); UpdateROIs();
UpdateROIs(); document.getElementById("saveroi").disabled = true;
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000); firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
}
} }
function ShowMultiplier() { function ShowMultiplier() {

View File

@@ -20,6 +20,18 @@
textarea { textarea {
font-size: 15px; font-size: 15px;
} }
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style> </style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet"> <link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -38,15 +50,8 @@
</td> </td>
</table> </table>
<table> <hr>
<td> <button class="button" onclick="saveTextAsFile()">Save Config</button>
<button class="button" onclick="saveTextAsFile()">Save Config</button>
</td>
<td>
<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button>
</td>
</table>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script> <script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script> <script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
@@ -64,23 +69,19 @@
function saveTextAsFile() function saveTextAsFile()
{ {
if (confirm("Are you sure you want to save the configuration?")) { FileDeleteOnServer("/config/config.ini", domainname);
FileDeleteOnServer("/config/config.ini", domainname); var textToSave = document.getElementById("inputTextToSave").value;
var textToSave = document.getElementById("inputTextToSave").value; FileSendContent(textToSave, "/config/config.ini", domainname);
FileSendContent(textToSave, "/config/config.ini", domainname);
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000); firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
}
} }
function doReboot() { function doReboot() {
if (confirm("Are you sure you want to reboot?")) { var stringota = "/reboot";
var stringota = "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
}
} }
LoadConfigNeu(); LoadConfigNeu();

View File

@@ -184,6 +184,18 @@
transform: translate(-50%,-50%); transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%);
} }
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style> </style>
<link rel="stylesheet" href="mkdocs_theme.css?v=$COMMIT_HASH" /> <link rel="stylesheet" href="mkdocs_theme.css?v=$COMMIT_HASH" />
@@ -2110,14 +2122,9 @@
</tr> </tr>
</table> </table>
<table style="padding-top:10px"> <hr>
<td> <button class="button" onclick="saveTextAsFile()">Save Config</button>
<button class="button" onclick="saveTextAsFile()">Save Config</button>
</td>
<td>
<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button>
</td>
</table>
</div> </div>
@@ -2673,18 +2680,16 @@ function saveTextAsFile() {
return; return;
} }
if (confirm("Are you sure you want to save the configuration?")) { ReadParameterAll();
ReadParameterAll(); WriteConfigININew();
WriteConfigININew(); SaveConfigToServer(domainname);
SaveConfigToServer(domainname);
firework.launch('Configuration saved. It will get applied after the next reboot!', 'success', 5000); firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
if (changeCamValue == 1) { if (changeCamValue == 1) {
camSettingsSet(); camSettingsSet();
firework.launch('You have changed the camera settings, so creating a new reference image and updating the alignment marks is mandatory!', 'success', 10000); firework.launch('You have changed the camera settings, creating a new reference image and updating the alignment marks is mandatory!', 'success', 5000);
} }
}
} }
function camSettingsSet(){ function camSettingsSet(){
@@ -2875,7 +2880,7 @@ function camSettingsSet(){
if (xhttp.responseText == "CamSettingsSet") { if (xhttp.responseText == "CamSettingsSet") {
document.getElementById("overlay").style.display = "none"; document.getElementById("overlay").style.display = "none";
firework.launch('Cam Settings saved', 'success', 2000); firework.launch('Cam Settings saved', 'success', 5000);
return; return;
} }
else { else {
@@ -2903,13 +2908,11 @@ function camSettingsSet(){
} }
function doReboot() { function doReboot() {
if (confirm("Are you sure you want to reboot?")) { var stringota = domainname + "/reboot";
var stringota = domainname + "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
}
} }
function FormatDecimalValue(_param, _cat, _name) { function FormatDecimalValue(_param, _cat, _name) {

View File

@@ -4,6 +4,19 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Digit ROI</title> <title>Digit ROI</title>
<style>
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="edit_style.css" rel="stylesheet"> <link href="edit_style.css" rel="stylesheet">
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet"> <link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -204,13 +217,11 @@
domainname = getDomainname(); domainname = getDomainname();
function doReboot() { function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save your changes?")) { var stringota = getDomainname() + "/reboot";
var stringota = getDomainname() + "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
}
} }
function EnDisableDigits() { function EnDisableDigits() {
@@ -359,16 +370,14 @@
} }
function SaveToConfig() { function SaveToConfig() {
if (confirm("Are you sure you want to save the new digit ROI configuration?")) { // _zwcat = getConfigCategory();
// _zwcat = getConfigCategory(); cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; WriteConfigININew();
WriteConfigININew(); SaveConfigToServer(domainname);
SaveConfigToServer(domainname); UpdateROIs();
UpdateROIs(); document.getElementById("saveroi").disabled = true;
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000); firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
}
} }
function ShowMultiplier() { function ShowMultiplier() {

View File

@@ -395,22 +395,12 @@
<script type="text/javascript"> <script type="text/javascript">
var canvas = document.getElementById('canvas'), var canvas = document.getElementById('canvas'),
domainname = getDomainname(), domainname = getDomainname(),
context = canvas.getContext('2d'), context = canvas.getContext('2d'),
imageObj = new Image(), imageObj = new Image(),
isActReference = false, isActReference = false,
param, param,
category; category;
function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
var stringota = domainname + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
function cameraParameterChanged() { function cameraParameterChanged() {
document.getElementById("savereferenceimage").disabled = true; document.getElementById("savereferenceimage").disabled = true;
@@ -738,7 +728,7 @@
if (xhttp.responseText == "CamSettingsSet") { if (xhttp.responseText == "CamSettingsSet") {
document.getElementById("overlay").style.display = "none"; document.getElementById("overlay").style.display = "none";
firework.launch('Cam Settings saved', 'success', 2000); firework.launch('Cam Settings saved', 'success', 5000);
return; return;
} }
else { else {

View File

@@ -34,13 +34,11 @@ p {font-size: 1em;}
<script> <script>
function doReboot() { function doReboot() {
// if (confirm("Are you sure you want to reboot the ESP32?")) { var stringota = getDomainname() + "/reboot";
var stringota = getDomainname() + "/reboot"; window.location = stringota;
window.location = stringota; window.location.href = stringota;
window.location.href = stringota; window.location.assign(stringota);
window.location.assign(stringota); window.location.replace(stringota);
window.location.replace(stringota);
// }
} }
</script> </script>

View File

@@ -29,7 +29,7 @@
// var xhttp = new XMLHttpRequest(); // var xhttp = new XMLHttpRequest();
// xhttp.onreadystatechange = function() {if (xhttp.readyState == 4) {if (xhttp.status == 200) {document.reload();}}}; // xhttp.onreadystatechange = function() {if (xhttp.readyState == 4) {if (xhttp.status == 200) {document.reload();}}};
if (!file.name.includes("remote-setup")){ if (!file.name.includes("remote-setup")){
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure that you have downloaded the correct file?")) if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure you have downloaded the correct file?"))
return; return;
} }
@@ -41,7 +41,7 @@ if (!file.name.includes("remote-setup")){
var file = document.getElementById("newfile").files[0]; var file = document.getElementById("newfile").files[0];
if (!file.name.includes("remote-setup")) if (!file.name.includes("remote-setup"))
{ {
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure that you have downloaded the correct file?")) if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure you have downloaded the correct file?"))
return; return;
} }