Compare commits
112 Commits
v0.8.0
...
v1.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
446cb61e1c | |||
7a84042781 | |||
14a73b011e | |||
3d4c04333e | |||
2e3820ca73 | |||
d52615f8e3 | |||
786b8e9b19 | |||
10fa71d7ca | |||
aeda821396 | |||
3db3968e07 | |||
430f01943a | |||
ca97a586c1 | |||
5c80b780a0 | |||
2e42b86444 | |||
acb53bf611 | |||
5b99304d7f | |||
d94028a7b9 | |||
f4ca86e8ff | |||
2c9f5c72f2 | |||
f5aae4f2ea | |||
51daa23327 | |||
a5fb4f40b9 | |||
17e9b0d51b | |||
2877f91ef8 | |||
8dd509214b | |||
223ab7f81e | |||
b6ba01f6e0 | |||
75e9d178a3 | |||
e8740364cf | |||
53f1373432 | |||
40e7a37aa5 | |||
24b2446521 | |||
82f48604a2 | |||
f5b627616e | |||
dc4eb4f4a1 | |||
52785871b9 | |||
9c92bb9214 | |||
96295f161a | |||
50116e8b45 | |||
29b243f115 | |||
c779a45ea9 | |||
2d67a44ad0 | |||
8d44a5dcea | |||
3d3293138f | |||
cf3e683137 | |||
f2b926cce6 | |||
1494b2b3b2 | |||
474f987e73 | |||
294b7a7fdd | |||
fda5c6ff27 | |||
58144a5187 | |||
877afbc26a | |||
98a4c3650f | |||
dcbedf5899 | |||
4fff5ad185 | |||
d1f1e926e7 | |||
a20baa6b27 | |||
42ca66555a | |||
13d5280d76 | |||
ae4d5eb8a2 | |||
d6227b6dad | |||
07fefe41fb | |||
dab4d0ed22 | |||
faba3d7619 | |||
8637b0f72d | |||
169798e0eb | |||
04b2721c5d | |||
14909b241b | |||
f8e72a50e9 | |||
5d03a33253 | |||
65983e638a | |||
aae29786bb | |||
b28797a79b | |||
d4452e1d59 | |||
3717561466 | |||
4cad6f888f | |||
6e9d562977 | |||
3e6d698a40 | |||
dc70b250d8 | |||
276b311194 | |||
2609bf840c | |||
fe3ca247b9 | |||
37a42aa2a1 | |||
68dfacb07c | |||
a60abdbaa1 | |||
ef3cc5b523 | |||
99a57978fa | |||
a2aaeb3f84 | |||
2f8a324bfc | |||
16e91ec4f5 | |||
3407567568 | |||
83aa1b2202 | |||
6193f422e8 | |||
a657f698b8 | |||
3d497a1acc | |||
4e190af499 | |||
f07f845dfb | |||
f1936b6b0d | |||
aa4e3b5e8d | |||
7cafedd9bf | |||
1e44d9bd01 | |||
1bc3abc9f0 | |||
8d51c5ad12 | |||
d9c467d54f | |||
07e7cbee1c | |||
f5fcf42fbe | |||
036e10cd5d | |||
2d1317af0d | |||
21fba0481c | |||
6dfe5a80fd | |||
c681619be8 | |||
02cb91e918 |
4
.github/workflows/pio-build.yaml
vendored
@ -36,9 +36,7 @@ jobs:
|
||||
git config --global advice.detachedHead false
|
||||
|
||||
- name: Run PlatformIO
|
||||
#run: pio run -e gravity-release -e gravity-perf -e gravity-debug
|
||||
run: pio run -e gravity-release -e gravity-perf
|
||||
#run: pio run -e gravity-release
|
||||
run: pio run -e gravity-release -e gravity-perf -e gravity32-release -e gravity32-perf
|
||||
|
||||
- uses: EndBug/add-and-commit@v7 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
|
||||
with:
|
||||
|
29
README.md
@ -8,24 +8,31 @@
|
||||
|
||||
# Gravity Monitor for Beer Brewing
|
||||
|
||||
GravityMon is a replacement firmware for the iSpindle firmware. It's 100% compatible with the iSpindle hardware design so it does not require any hardware changes.
|
||||
GravityMon is a replacement firmware for the iSpindle firmware. It's 100% compatible with the iSpindle hardware design so it does not require any hardware changes.
|
||||
|
||||
Now also works with ESP32 (use ESP32 d1 mini which is compatible with ESP8266)
|
||||
|
||||
Installation can be made using https://www.brewflasher.com
|
||||
|
||||
Note! If its being flagged as malware, try the older version.
|
||||
|
||||
The main differences:
|
||||
---------------------
|
||||
|
||||
* Operates in two modes gravity monitoring and configuration mode (simplify calibration)
|
||||
* Modern web based UI for configuration (in config mode)
|
||||
* REST API
|
||||
* Send data to multiple endpoints when pushing data (2xhttp, brewfather, influxdb v2, mqtt supported)
|
||||
* Automatic temperature adjustment of gravity reading
|
||||
* OTA support from local webserver
|
||||
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity.
|
||||
* Visual graph showing how formula will be interpreted
|
||||
* Using the temperature sensor in gyro instead of DS18B20 (faster)
|
||||
* Built in performance measurements (used to optimise code)
|
||||
* Modern web based user interface for configuration when connected to WIFI
|
||||
* Efficient software, long lifespan (+45 days with 5min update frequencey)
|
||||
* Send data to multiple endpoints (http-post, http-get, influxdb v2, mqtt)
|
||||
* Instructions for service such as; Brewfather, Fermentrack, Ubidots, Home Assistant, Brewers Friend, Brewspy, Thingspeak, Blynk.
|
||||
* SSL support in standard HTTP and MQTT connections.
|
||||
* ESP32 support with Bluetooth push
|
||||
* Customize data format to be pushed
|
||||
* Automatic temperature adjustment of gravity when enabled
|
||||
* Use the temperature sensor in gyro instead of DS18B20
|
||||
* Built in function to create gravity formulas, no need for additional software, just enter tilt/gravity.
|
||||
* Visual graph showing how gravity formula will be interpreted
|
||||
* OTA support
|
||||
* Built in performance measurements (used to optimise code)
|
||||
* REST API for scripting
|
||||
|
||||
No code has been reused from the iSpindle project.
|
||||
|
||||
|
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>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 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.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>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 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.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -1 +0,0 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Average runtime:</div><div class="col-md-4 themed-grid-col bg-light" id="runtime">Loading...</div></div><div class="row mb-3"><a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">View error log</a></div><script>function loadLog(){$("#logContent").load("/log")}$("#log-btn").click(function(o){loadLog()}),setInterval(function(){loadLog()},3e3)</script><div class="collapse" id="collapseLog"><div class="card card-body"><pre><code id="logContent"></code></pre></div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var e="/api/device";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#app-ver").text(e["app-ver"]+" (html 0.8.0)"),$("#mdns").text(e.mdns),$("#id").text(e.id),$("#runtime").text(e["runtime-average"]+" seconds")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
BIN
bin/firmware.bin
BIN
bin/firmware32.bin
Normal file
@ -1,2 +1,2 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/config.htm">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div class="accordion" id="accordion"><div class="card"><div class="card-header" id="headingOne"><h2 class="mb-0"><button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">Push Format Templates</button></h2></div><div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"><div class="card-body"><input type="text" name="id" id="id" hidden> <input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-2" id="http-2" hidden><!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="mqtt" id="mqtt" hidden><div class="form-group row"><label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <select class="custom-select col-sm-4" required name="push-target" id="push-target"><option value="http-1">HTTP option 1</option><option value="http-2">HTTP option 2</option><!--<option value="brewfather">Brewfather</option>--><option value="influxdb">Influx DB</option><option value="mqtt">MQTT</option></select></div><div class="form-group row"><div class="col-sm-12"><textarea rows="5" class="form-control" name="format" id="format">
|
||||
</textarea></div></div><div class="form-group row"><div class="col-sm-8 offset-sm-2"><button class="btn btn-primary" id="format-btn">Save</button> <button class="btn btn-secondary" id="test-btn">Test</button></div></div><pre class="card-preview" id="preview" name="preview"></pre></div></div></div><hr class="my-4"></div><script type="text/javascript">function setButtonDisabled(l){$("#format-btn").prop("disabled",l),$("#test-btn").prop("disabled",l)}function selectFormat(){var l="#"+$("#push-target").val();console.log(l),l=decodeURIComponent($(l).val()),console.log(l),l=l.replaceAll("|","|\n"),console.log(l),$("#format").val(l),$("#preview").text("")}function getConfig(){setButtonDisabled(!0);var l="/api/config/format";$("#spinner").show(),$.getJSON(l,function(l){console.log(l),$("#id").val(l.id),$("#http-1").val(l["http-1"]),$("#http-2").val(l["http-2"]),$("#influxdb").val(l.influxdb),$("#mqtt").val(l.mqtt),selectFormat()}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide(),setButtonDisabled(!1)})}window.onload=getConfig,setButtonDisabled(!0),$(document).ready(function(){null!=location.hash&&""!=location.hash&&($(".collapse").removeClass("in"),$(location.hash+".collapse").collapse("show"))}),$("#push-target").change(function(l){console.log(l),selectFormat()}),$("#format-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("\n","");var t="id="+$("#id").val()+"&"+$("#push-target").val()+"="+encodeURIComponent(e);console.log(t),$.ajax({type:"POST",url:"/api/config/format",data:t,success:function(l){showSuccess("Format stored successfully."),getConfig()},error:function(l){showError("Unable to store format.")}})}),$("#test-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("${mdns}","testing"),e=e.replaceAll("${id}","e4a344"),e=e.replaceAll("${sleep-interval}","300"),e=e.replaceAll("${temp}","21.1"),e=e.replaceAll("${token}","a-token"),e=e.replaceAll("${temp-c}","21.1"),e=e.replaceAll("${temp-f}","51.3"),e=e.replaceAll("${temp-unit}","C"),e=e.replaceAll("${battery}","3.86"),e=e.replaceAll("${rssi}","-76"),e=e.replaceAll("${run-time}","4.32"),e=e.replaceAll("${gravity}","1.044"),e=e.replaceAll("${gravity-sg}","1.044"),e=e.replaceAll("${gravity-plato}","9.5"),e=e.replaceAll("${gravity-unit}","G"),e=e.replaceAll("${corr-gravity}","1.044"),e=e.replaceAll("${corr-gravity-sg}","1.044"),e=e.replaceAll("${corr-gravity-plato}","9.5"),e=e.replaceAll("${angle}","54.5"),e=e.replaceAll("${tilt}","54.5");try{var t=JSON.parse(e);e=JSON.stringify(t,null,2)}catch(l){console.log("Not a javascript object!")}$("#preview").text(e)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div class="accordion" id="accordion"><div class="card"><div class="card-header" id="headingFormat"><h2 class="mb-0"><button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseFormat" aria-expanded="true" aria-controls="collapseFormat">Push Format Templates</button></h2></div><div id="collapseFormat" class="collapse show" aria-labelledby="headingFormat" data-parent="#accordion"><div class="card-body"><input type="text" name="id" id="id" hidden> <input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-2" id="http-2" hidden> <input type="text" name="http-3" id="http-3" hidden><!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="mqtt" id="mqtt" hidden><div class="form-group row"><label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <select class="custom-select col-sm-4" required name="push-target" id="push-target"><option value="http-1">HTTP option 1 (post)</option><option value="http-2">HTTP option 2 (post)</option><option value="http-3">HTTP option 3 (get)</option><!--<option value="brewfather">Brewfather</option>--><option value="influxdb">Influx DB</option><option value="mqtt">MQTT</option></select></div><div class="form-group row"><div class="col-sm-12"><textarea rows="5" class="form-control" name="format" id="format">
|
||||
</textarea></div></div><div class="form-group row"><div class="col-sm-8 offset-sm-2"><button class="btn btn-primary" id="format-btn">Save</button> <button class="btn btn-secondary" id="test-btn">Test</button></div></div><pre class="card-preview" id="preview" name="preview"></pre></div></div></div><hr class="my-4"></div><script type="text/javascript">function setButtonDisabled(e){$("#format-btn").prop("disabled",e),$("#test-btn").prop("disabled",e)}function selectFormat(){var e="#"+$("#push-target").val();console.log(e),e=decodeURIComponent($(e).val()),console.log(e),e=e.replaceAll("|","|\n"),console.log(e),$("#format").val(e),$("#preview").text("")}function getConfig(){setButtonDisabled(!0);var e="/api/config/format";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").val(e.id),$("#http-1").val(e["http-1"]),$("#http-2").val(e["http-2"]),$("#http-3").val(e["http-3"]),$("#influxdb").val(e.influxdb),$("#mqtt").val(e.mqtt),selectFormat()}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide(),setButtonDisabled(!1)})}window.onload=getConfig,setButtonDisabled(!0),$(document).ready(function(){null!=location.hash&&""!=location.hash&&($(".collapse").removeClass("in"),$(location.hash+".collapse").collapse("show"))}),$("#push-target").change(function(e){console.log(e),selectFormat()}),$("#format-btn").click(function(e){var l=$("#format").val();l=l.replaceAll("\n","");var t="id="+$("#id").val()+"&"+$("#push-target").val()+"="+encodeURIComponent(l);console.log(t),$.ajax({type:"POST",url:"/api/config/format",data:t,success:function(e){showSuccess("Format stored successfully."),getConfig()},error:function(e){showError("Unable to store format.")}})}),$("#test-btn").click(function(e){var l="/api/status";$("#spinner").show(),$.getJSON(l,function(e){console.log(e);var l=$("#format").val();if(l="C"==e["temp-format"]?l.replaceAll("${temp}",e["temp-c"]):l.replaceAll("${temp}",e["temp-f"]),"G"==e["gravity-format"]){var t=e.gravity;l=l.replaceAll("${gravity-sg}",t),l=l.replaceAll("${corr-gravity-sg}",t);var a=259-(259-t);l=l.replaceAll("${gravity-plato}",a),l=l.replaceAll("${corr-gravity-plato}",a)}else{a=e.gravity;l=l.replaceAll("${gravity-plato}",a),l=l.replaceAll("${corr-gravity-plato}",a);t=259/(259-a);l=l.replaceAll("${gravity-sg}",t),l=l.replaceAll("${corr-gravity-sg}",t)}l=l.replaceAll("${mdns}",e.mdns),l=l.replaceAll("${id}",e.id),l=l.replaceAll("${sleep-interval}",e["sleep-interval"]),l=l.replaceAll("${token}",e.token),l=l.replaceAll("${token2}",e.token2),l=l.replaceAll("${temp-c}",e["temp-c"]),l=l.replaceAll("${temp-f}",e["temp-f"]),l=l.replaceAll("${temp-unit}",e["temp-format"]),l=l.replaceAll("${battery}",e.battery),l=l.replaceAll("${rssi}",e.rssi),l=l.replaceAll("${run-time}",e["runtime-average"]),l=l.replaceAll("${gravity}",e.gravity),l=l.replaceAll("${gravity-unit}",e["gravity-format"]),l=l.replaceAll("${corr-gravity}",e.gravity),l=l.replaceAll("${angle}",e.angle),l=l.replaceAll("${tilt}",e.angle);try{var r=JSON.parse(l);l=JSON.stringify(r,null,2)}catch(e){console.log("Not a javascript object!")}$("#preview").text(l)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
1
bin/test.min.htm
Normal file
@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div><div class="card"><div class="card-body"><pre class="card-preview" id="preview" name="preview"></pre></div></div><div class="form-group row"></div><div class="form-group row"><div class="col-sm-8"><button class="btn btn-primary" id="test-btn">Test</button></div></div><hr class="my-4"></div><script type="text/javascript">function clearLog(){$("#preview").text("")}function appendLog(t){doc=$("#preview").text(),doc+=t+"\n",$("#preview").text(doc)}function testMqtt(t){var e="/api/test/push";e+="?id="+t+"&format=mqtt",$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target 'mqtt' successful":-3==e?"Push target 'mqtt' failed to connect":-4==e?"Push target 'mqtt' failed with error timeout":-10==e?"Push target 'mqtt' failed with error denied":"Push target 'mqtt' failed with error code "+e:"Push target 'mqtt' is not configured/used")}).fail(function(){appendLog("Failed to test push target 'influxdb'")})}function testInfluxdb(t){var e="/api/test/push";e+="?id="+t+"&format=influxdb",$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target 'influxdb' successful":400==e?"Push target 'influxdb' failed with error code 400, bad request":401==e?"Push target 'influxdb' failed with error code 401, unauthorized":404==e?"Push target 'influxdb' failed with error code 404, url not found":"Push target 'influxdb' failed with error code "+e:"Push target 'influxdb' is not configured/used")}).fail(function(){appendLog("Failed to test push target 'influxdb'")})}function testHttp(t,s){var e="/api/test/push";e+="?id="+t+"&format="+s,$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target '"+s+"' successful":400==e?"Push target '"+s+"' failed with error code 400, bad request":401==e?"Push target '"+s+"' failed with error code 401, unauthorized":404==e?"Push target '"+s+"' failed with error code 404, url not found":"Push target '"+s+"' failed with error code "+e:"Push target '"+s+"' is not configured/used")}).fail(function(){appendLog("Failed to test push target '"+s+"'")})}$("#spinner").hide(),$("#test-btn").click(function(t){clearLog(),appendLog("Starting test of push targets");var e="/api/status";$("#test-btn").prop("disabled",!0),$("#spinner").show(),$.getJSON(e,function(t){var e=t.id;console.log(e),testHttp(e,"http-1"),testHttp(e,"http-2"),testHttp(e,"http-3"),testHttp(e,"brewfather"),testInfluxdb(e),testMqtt(e),$("#spinner").hide(),$("#test-btn").prop("disabled",!1)}).fail(function(){showError("Unable to get data from the device."),$("#spinner").hide(),$("#test-btn").prop("disabled",!1)}).always(function(){})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
@ -1 +1 @@
|
||||
{ "project":"gravmon", "version":"0.8.0", "html": [ "index.min.htm", "device.min.htm", "config.min.htm", "calibration.min.htm", "format.min.htm", "about.min.htm" ] }
|
||||
{ "project":"gravmon", "version":"0.9.0", "html": [ ] }
|
@ -25,9 +25,6 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/device.htm">Device</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||
</li>
|
||||
|
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>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 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.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item active"><a class="nav-link" href="/about.htm">About</a></li></ul></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="row mb-3"><h3>Beer Gravity Monitor</h3>This is a piece of software for the iSpindle hardware and will work in a similar way. No part of this software is copied from the iSpindle project.</div><div class="row mb-3"><h3>MIT License</h3>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 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.</div><hr class="my-4"></div><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
@ -26,9 +26,6 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/device.htm">Device</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||
</li>
|
||||
@ -77,13 +74,13 @@
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingOne">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseCalibration" aria-expanded="true" aria-controls="collapseCalibration">
|
||||
Formula calculation
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div id="collapseCalibration" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/formula" method="post">
|
||||
<input type="text" name="gravity-format" id="gravity-format" hidden>
|
||||
|
167
html/config.htm
@ -25,9 +25,6 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/device.htm">Device</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||
</li>
|
||||
@ -96,14 +93,14 @@
|
||||
<input type="text" name="runtime-average" id="runtime-average" hidden>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingOne">
|
||||
<div class="card-header" id="headingDevice">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
<button class="btn btn-link btn-block text-left" onclick="window.location.href = '#collapseDevice'" type="button" data-toggle="collapse" data-target="#collapseDevice" aria-expanded="true" aria-controls="collapseDevice">
|
||||
Device settings
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div id="collapseDevice" class="collapse show" aria-labelledby="headingDevice" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/config/device" method="post">
|
||||
<input type="text" name="id" id="id1" hidden>
|
||||
@ -135,7 +132,7 @@
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="10" max="3600" class="form-control" name="sleep-interval" id="sleep-interval">
|
||||
</div>
|
||||
<label for="sleep-interval" class="col-sm-7 col-form-label" id="sleep-interval-info"></label>
|
||||
<label for="sleep-interval" class="col-sm-4 col-form-label" id="sleep-interval-info"></label>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-3">
|
||||
@ -160,24 +157,25 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingTwo">
|
||||
<div class="card-header" id="headingPush">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
<button class="btn btn-link btn-block text-left collapsed" onclick="window.location.href = '#collapsePush'" type="button" data-toggle="collapse" data-target="#collapsePush" aria-expanded="false" aria-controls="collapsePush">
|
||||
Push settings
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
|
||||
<div id="collapsePush" class="collapse" aria-labelledby="headingPush" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/config/push" method="post">
|
||||
<input type="text" name="id" id="id2" hidden>
|
||||
<input type="text" name="section" value="collapsePush" hidden>
|
||||
<input type="text" name="http-push-h1" id="http-push-h1" hidden>
|
||||
<input type="text" name="http-push-h2" id="http-push-h2" hidden>
|
||||
<input type="text" name="http-push2-h1" id="http-push2-h1" hidden>
|
||||
<input type="text" name="http-push2-h2" id="http-push2-h2" hidden>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="http-push" class="col-sm-2 col-form-label">Http URL 1:</label>
|
||||
<label for="http-push" class="col-sm-2 col-form-label">HTTP 1 (POST):</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="120" class="form-control" name="http-push" id="http-push">
|
||||
</div>
|
||||
@ -186,7 +184,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="http-push2" class="col-sm-2 col-form-label">Http URL 2:</label>
|
||||
<label for="http-push2" class="col-sm-2 col-form-label">HTTP 2 (POST):</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="120" class="form-control" name="http-push2" id="http-push2">
|
||||
</div>
|
||||
@ -195,8 +193,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="token" class="col-sm-2 col-form-label">Token:</label>
|
||||
<div class="col-sm-4">
|
||||
@ -206,6 +202,22 @@
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="http-push3" class="col-sm-2 col-form-label">HTTP 3 (GET):</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="url" maxlength="120" class="form-control" name="http-push3" id="http-push3">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="token2" class="col-sm-2 col-form-label">Token 2:</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" maxlength="50" class="form-control" name="token2" id="token2">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="inputBrewfatherPush" class="col-sm-2 col-form-label">Brewfather URL:</label>
|
||||
<div class="col-sm-10">
|
||||
@ -213,7 +225,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary" id="push-btn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button class="btn btn-info" id="format-btn">Format editor</button>
|
||||
<button class="btn btn-info" id="test-btn">Test Push</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingPush2">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left collapsed" onclick="window.location.href = '#collapsePush2'" type="button" data-toggle="collapse" data-target="#collapsePush2" aria-expanded="false" aria-controls="collapsePush2">
|
||||
Push settings (2)
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapsePush2" class="collapse" aria-labelledby="headingPush2" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/config/push" method="post">
|
||||
<input type="text" name="id" id="id5" hidden>
|
||||
<input type="text" name="section" value="collapsePush2" hidden>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="influxdb2-push" class="col-sm-2 col-form-label">InfluxDB v2 URL:</label>
|
||||
@ -269,14 +311,15 @@
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary" id="push-btn">Save</button>
|
||||
<button type="submit" class="btn btn-primary" id="push-btn2">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button class="btn btn-info" id="format-btn">Format editor</button>
|
||||
<button class="btn btn-info" id="format-btn2">Format editor</button>
|
||||
<button class="btn btn-info" id="test-btn2">Test Push</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -285,14 +328,14 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingThree">
|
||||
<div class="card-header" id="headingGravity">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
<button class="btn btn-link btn-block text-left collapsed" onclick="window.location.href = '#collapseGravity'" type="button" data-toggle="collapse" data-target="#collapseGravity" aria-expanded="false" aria-controls="collapseGravity">
|
||||
Gravity
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapseThree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion">
|
||||
<div id="collapseGravity" class="collapse" aria-labelledby="headingGravity" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/config/gravity" method="post">
|
||||
<input type="text" name="id" id="id3" hidden>
|
||||
@ -340,14 +383,14 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingFour">
|
||||
<div class="card-header" id="headingHardware">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
<button class="btn btn-link btn-block text-left collapsed" onclick="window.location.href = '#collapseHardware'" type="button" data-toggle="collapse" data-target="#collapseHardware" aria-expanded="false" aria-controls="collapseHardware">
|
||||
Hardware settings
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapseFour" class="collapse" aria-labelledby="headingFour" data-parent="#accordion">
|
||||
<div id="collapseHardware" class="collapse" aria-labelledby="headingHardware" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<form action="/api/config/hardware" method="post">
|
||||
<input type="text" name="id" id="id4" hidden>
|
||||
@ -374,6 +417,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label" for="ble">Bluetooth tilt color:</label>
|
||||
<div class="col-sm-2">
|
||||
<select class="form-control" id="ble" name="ble" disabled>
|
||||
<option value="">-not active-</option>
|
||||
<option value="red">red</option>
|
||||
<option value="green">green</option>
|
||||
<option value="black">black</option>
|
||||
<option value="purple">purple</option>
|
||||
<option value="orange">orange</option>
|
||||
<option value="blue">blue</option>
|
||||
<option value="yellow">yellow</option>
|
||||
<option value="pink">pink</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="ota-url" class="col-sm-2 col-form-label">OTA base URL:</label>
|
||||
<div class="col-sm-10">
|
||||
@ -386,6 +445,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8 offset-sm-2">
|
||||
<button class="btn btn-info" id="firmware-btn">Upload firmware</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -450,7 +514,7 @@ function checkHeader(input) {
|
||||
|
||||
setButtonDisabled( true );
|
||||
|
||||
// Opens the targetet according (if URL has #collapseOne to #collapseFour)
|
||||
// Opens the targetet according (if URL has #collapseXXX)
|
||||
$(document).ready(function () {
|
||||
if(location.hash != null && location.hash != ""){
|
||||
$('.collapse').removeClass('in');
|
||||
@ -470,21 +534,37 @@ function checkHeader(input) {
|
||||
} );
|
||||
});
|
||||
|
||||
$("#firmware-btn").click(function(e){
|
||||
window.location.href = "/firmware.htm";
|
||||
});
|
||||
|
||||
// Open the format editor
|
||||
$("#format-btn").click(function(e){
|
||||
window.location.href = "/format.htm";
|
||||
});
|
||||
|
||||
function estimateBatteryLife(interval) {
|
||||
// Open the format editor
|
||||
$("#format-btn2").click(function(e){
|
||||
window.location.href = "/format.htm";
|
||||
});
|
||||
|
||||
$("#test-btn").click(function(e){
|
||||
window.location.href = "/test.htm";
|
||||
});
|
||||
|
||||
$("#test-btn2").click(function(e){
|
||||
window.location.href = "/test.htm";
|
||||
});
|
||||
|
||||
function estimateBatteryLife(interval, rt) {
|
||||
// ESP8266 consumes between 140-170mA when WIFI is on. Deep sleep is 20uA.
|
||||
// MPU-6050 consumes 4mA
|
||||
// DS18B20 consumes 1mA
|
||||
// For this estimation we use an average of 160mA
|
||||
|
||||
var pwrActive = 160; // mA per hour
|
||||
var pwrSleep = 5; // mA per day
|
||||
var batt = 2200; // mA
|
||||
var rt = parseInt($("#runtime-average").val());
|
||||
var pwrActive = 170; // mA per hour
|
||||
var pwrSleep = 15; // mA per day
|
||||
var batt = 2000; // mA
|
||||
|
||||
if(rt<1) rt = 2;
|
||||
|
||||
@ -495,15 +575,19 @@ function checkHeader(input) {
|
||||
|
||||
function updateSleepInfo() {
|
||||
var i = parseInt($("#sleep-interval").val());
|
||||
var rt = parseInt($("#runtime-average").val());
|
||||
var j = 0;
|
||||
|
||||
var j = estimateBatteryLife(i);
|
||||
if( rt>0 )
|
||||
j = estimateBatteryLife(i, rt);
|
||||
|
||||
var t1 = Math.floor(i/60) + " m " + (i%60) + " s";
|
||||
var t2 = Math.floor(j/7) + " weeks " + (i%7) + " days";
|
||||
var t2 = Math.floor(j/7) + " weeks " + Math.floor(j%7) + " days";
|
||||
|
||||
$("#sleep-interval-info").text(t1);
|
||||
//$("#sleep-interval-info").text( t1 + " - Estimated life: " + t2);
|
||||
console.log( "Estimated life: " + t2);
|
||||
if( j )
|
||||
$("#sleep-interval-info").text( t1 + " - Estimated runtime: " + t2);
|
||||
else
|
||||
$("#sleep-interval-info").text( t1 );
|
||||
|
||||
hideWarningGyro();
|
||||
if(i>0 && i<300) {
|
||||
@ -523,9 +607,13 @@ function checkHeader(input) {
|
||||
$("#device-btn").prop("disabled", b);
|
||||
$("#calibrate-btn").prop("disabled", b);
|
||||
$("#push-btn").prop("disabled", b);
|
||||
$("#format-btn").prop("disabled", b);
|
||||
$("#test-btn").prop("disabled", b);
|
||||
$("#gravity-btn").prop("disabled", b);
|
||||
$("#hardware-btn").prop("disabled", b);
|
||||
$("#format-btn").prop("disabled", b);
|
||||
$("#push-btn2").prop("disabled", b);
|
||||
$("#format-btn2").prop("disabled", b);
|
||||
$("#test-btn2").prop("disabled", b);
|
||||
}
|
||||
|
||||
// Get the configuration values from the API
|
||||
@ -537,10 +625,17 @@ function checkHeader(input) {
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
|
||||
if(cfg["platform"]=="esp32") {
|
||||
$('#ble').prop('disabled', false);
|
||||
$("#ble").val(cfg["ble"]);
|
||||
}
|
||||
|
||||
$("#id1").val(cfg["id"]);
|
||||
$("#id2").val(cfg["id"]);
|
||||
$("#id3").val(cfg["id"]);
|
||||
$("#id4").val(cfg["id"]);
|
||||
$("#id5").val(cfg["id"]);
|
||||
$("#mdns").val(cfg["mdns"]);
|
||||
if( cfg["temp-format"] == "C" ) $("#temp-format-c").click();
|
||||
else $("#temp-format-f").click();
|
||||
@ -548,12 +643,14 @@ function checkHeader(input) {
|
||||
else $("#gravity-format-p").click();
|
||||
$("#ota-url").val(cfg["ota-url"]);
|
||||
$("#token").val(cfg["token"]);
|
||||
$("#token2").val(cfg["token2"]);
|
||||
$("#http-push").val(cfg["http-push"]);
|
||||
$("#http-push-h1").val(cfg["http-push-h1"]);
|
||||
$("#http-push-h2").val(cfg["http-push-h2"]);
|
||||
$("#http-push2").val(cfg["http-push2"]);
|
||||
$("#http-push2-h1").val(cfg["http-push2-h1"]);
|
||||
$("#http-push2-h2").val(cfg["http-push2-h2"]);
|
||||
$("#http-push3").val(cfg["http-push3"]);
|
||||
$("#brewfather-push").val(cfg["brewfather-push"]);
|
||||
$("#influxdb2-push").val(cfg["influxdb2-push"]);
|
||||
$("#influxdb2-org").val(cfg["influxdb2-org"]);
|
||||
|
153
html/device.htm
@ -1,153 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<title>Beer Gravity Monitor</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
<body class="py-4">
|
||||
|
||||
<!-- START MENU -->
|
||||
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="/device.htm">Device</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/calibration.htm">Calibration</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/about.htm">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||
</nav>
|
||||
|
||||
<!-- START MAIN INDEX -->
|
||||
|
||||
<div class="container">
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
|
||||
<div id="alert-msg">...</div>
|
||||
<button type="button" id="alert-btn" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function showError( msg ) {
|
||||
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
function showSuccess( msg ) {
|
||||
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
$("#alert-btn").click(function(e){
|
||||
$('.alert').addClass('d-none').removeClass('show')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Current version:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3" id="h-app-ver-new" hidden>
|
||||
<div class="col-md-8 themed-grid-col bg-light">New version:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Host name:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Device ID:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Average runtime:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="runtime">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">
|
||||
View error log
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#log-btn").click(function(e){
|
||||
loadLog();
|
||||
});
|
||||
setInterval(function() {
|
||||
loadLog();
|
||||
}, 3000); //5 seconds
|
||||
|
||||
function loadLog() {
|
||||
$("#logContent").load("/log");
|
||||
//$("#logContent").load("/test/log");
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="collapse" id="collapseLog">
|
||||
<div class="card card-body">
|
||||
<pre><code id="logContent"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = getConfig;
|
||||
|
||||
function getConfig() {
|
||||
var url = "/api/device";
|
||||
//var url = "/test/device.json";
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
$("#app-ver").text(cfg["app-ver"] + " (html 0.8.0)");
|
||||
$("#mdns").text(cfg["mdns"]);
|
||||
$("#id").text(cfg["id"]);
|
||||
$("#runtime").text(cfg["runtime-average"] + " seconds");
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
})
|
||||
.always(function () {
|
||||
$('#spinner').hide();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
|
||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||
</body>
|
||||
</html>
|
@ -1 +0,0 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a></li><li class="nav-item active"><a class="nav-link" href="/device.htm">Device</a></li><li class="nav-item"><a class="nav-link" href="/config.htm">Configuration</a></li><li class="nav-item"><a class="nav-link" href="/calibration.htm">Calibration</a></li><li class="nav-item"><a class="nav-link" href="/about.htm">About</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3" id="h-app-ver-new" hidden><div class="col-md-8 themed-grid-col bg-light">New version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Host name:</div><div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Device ID:</div><div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Average runtime:</div><div class="col-md-4 themed-grid-col bg-light" id="runtime">Loading...</div></div><div class="row mb-3"><a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">View error log</a></div><script>function loadLog(){$("#logContent").load("/log")}$("#log-btn").click(function(o){loadLog()}),setInterval(function(){loadLog()},3e3)</script><div class="collapse" id="collapseLog"><div class="card card-body"><pre><code id="logContent"></code></pre></div></div><hr class="my-4"></div><script type="text/javascript">function getConfig(){var e="/api/device";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#app-ver").text(e["app-ver"]+" (html 0.8.0)"),$("#mdns").text(e.mdns),$("#id").text(e.id),$("#runtime").text(e["runtime-average"]+" seconds")}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})}window.onload=getConfig</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
188
html/firmware.htm
Normal file
@ -0,0 +1,188 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<title>Beer Gravity Monitor</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
<body class="py-4">
|
||||
|
||||
<!-- START MENU -->
|
||||
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="/firmware.htm">Beer Gravity Monitor - Firmware upgrade</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- START MAIN INDEX -->
|
||||
|
||||
<div class="container">
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
|
||||
<div id="alert-msg">...</div>
|
||||
<button type="button" id="alert-btn" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function showError( msg ) {
|
||||
$('.alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
function showSuccess( msg ) {
|
||||
$('.alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show')
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
$("#alert-btn").click(function(e){
|
||||
$('.alert').addClass('d-none').removeClass('show')
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Current version:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Platform:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="platform">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<!--
|
||||
<form action="/api/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="col-md-8 custom-file">
|
||||
<input type="file" accept=".bin" class="custom-file-input" name="name" id="name">
|
||||
<label class="custom-file-label" for="name">Choose file</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Flash firmware</button>
|
||||
</form>
|
||||
-->
|
||||
<form id="uploadForm" enctype="multipart/form-data">
|
||||
<div class="col-md-8 custom-file">
|
||||
<input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()">
|
||||
<label class="custom-file-label" for="name">Choose file</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Flash firmware</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = getStatus;
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#uploadForm").on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax( {
|
||||
xhr: function() {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
xhr.upload.addEventListener("progress", function(evt) {
|
||||
if (evt.lengthComputable) {
|
||||
progressHandler(evt);
|
||||
}
|
||||
}, false);
|
||||
return xhr;
|
||||
},
|
||||
type: 'POST',
|
||||
url: '/api/upload',
|
||||
data: new FormData(this),
|
||||
contentType: false,
|
||||
cache: false,
|
||||
processData:false,
|
||||
beforeSend: function() {
|
||||
setProgress(0);
|
||||
},
|
||||
error:function() {
|
||||
showError("Upload failed");
|
||||
},
|
||||
success: function(resp) {
|
||||
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser.");
|
||||
setTimeout(() => {
|
||||
window.location = "/";
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkName() {
|
||||
setButtonDisabled( $("#name").val()!="" ? false : true );
|
||||
}
|
||||
|
||||
function progressHandler(event) {
|
||||
var percent = (event.loaded / event.total) * 100;
|
||||
setProgress(Math.round(percent));
|
||||
}
|
||||
|
||||
function setProgress(val) {
|
||||
$('.progress-bar').css('width', val+'%').attr('aria-valuenow', val).text(val + "%");
|
||||
}
|
||||
|
||||
function setButtonDisabled( b ) {
|
||||
$("#upload-btn").prop("disabled", b);
|
||||
}
|
||||
|
||||
function getStatus() {
|
||||
|
||||
setButtonDisabled( true );
|
||||
|
||||
var url = "/api/status";
|
||||
//var url = "/test/status.json";
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
|
||||
$("#app-ver").text(cfg["app-ver"]);
|
||||
$("#platform").text(cfg["platform"]);
|
||||
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
})
|
||||
.always(function() {
|
||||
$('#spinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
setInterval(getStatus, 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
|
||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||
</body>
|
||||
</html>
|
87
html/firmware.min.htm
Normal file
@ -0,0 +1,87 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/firmware.htm">Beer Gravity Monitor - Firmware upgrade</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul><div class="spinner-border text-light" id="spinner" role="status"></div></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-4"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$(".alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$(".alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$(".alert").addClass("d-none").removeClass("show")})</script><div class="row mb-3"><div class="col-md-12 themed-grid-col bg-light">Here you can upload a new firmware version, it will not check the version number so you can also downgrade the firmware here.</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Current version:</div><div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div></div><div class="row mb-3"><div class="col-md-8 themed-grid-col bg-light">Platform:</div><div class="col-md-4 themed-grid-col bg-light" id="platform">Loading...</div></div><div class="row mb-3"><!--
|
||||
<form action="/api/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="col-md-8 custom-file">
|
||||
<input type="file" accept=".bin" class="custom-file-input" name="name" id="name">
|
||||
<label class="custom-file-label" for="name">Choose file</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Flash firmware</button>
|
||||
</form>
|
||||
--><form id="uploadForm" enctype="multipart/form-data"><div class="col-md-8 custom-file"><input type="file" accept=".bin" class="custom-file-input" name="name" id="name" onchange="checkName()"> <label class="custom-file-label" for="name">Choose file</label></div><button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Flash firmware</button></form></div><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div><hr class="my-4"></div><script type="text/javascript">window.onload = getStatus;
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#uploadForm").on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax( {
|
||||
xhr: function() {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
xhr.upload.addEventListener("progress", function(evt) {
|
||||
if (evt.lengthComputable) {
|
||||
progressHandler(evt);
|
||||
}
|
||||
}, false);
|
||||
return xhr;
|
||||
},
|
||||
type: 'POST',
|
||||
url: '/api/upload',
|
||||
data: new FormData(this),
|
||||
contentType: false,
|
||||
cache: false,
|
||||
processData:false,
|
||||
beforeSend: function() {
|
||||
setProgress(0);
|
||||
},
|
||||
error:function() {
|
||||
showError("Upload failed");
|
||||
},
|
||||
success: function(resp) {
|
||||
showSuccess("Upload completed, device is restarting. Waiting 10 seconds to refresh browser.");
|
||||
setTimeout(() => {
|
||||
window.location = "/";
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkName() {
|
||||
setButtonDisabled( $("#name").val()!="" ? false : true );
|
||||
}
|
||||
|
||||
function progressHandler(event) {
|
||||
var percent = (event.loaded / event.total) * 100;
|
||||
setProgress(Math.round(percent));
|
||||
}
|
||||
|
||||
function setProgress(val) {
|
||||
$('.progress-bar').css('width', val+'%').attr('aria-valuenow', val).text(val + "%");
|
||||
}
|
||||
|
||||
function setButtonDisabled( b ) {
|
||||
$("#upload-btn").prop("disabled", b);
|
||||
}
|
||||
|
||||
function getStatus() {
|
||||
|
||||
setButtonDisabled( true );
|
||||
|
||||
var url = "/api/status";
|
||||
//var url = "/test/status.json";
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
|
||||
$("#app-ver").text(cfg["app-ver"]);
|
||||
$("#platform").text(cfg["platform"]);
|
||||
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
})
|
||||
.always(function() {
|
||||
$('#spinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
setInterval(getStatus, 3000);
|
||||
}</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></body></html>
|
107
html/format.htm
@ -23,7 +23,7 @@
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config.htm">Back to configuration</a>
|
||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -62,18 +62,19 @@
|
||||
<div class="accordion" id="accordion">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingOne">
|
||||
<div class="card-header" id="headingFormat">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseFormat" aria-expanded="true" aria-controls="collapseFormat">
|
||||
Push Format Templates
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
|
||||
<div id="collapseFormat" class="collapse show" aria-labelledby="headingFormat" data-parent="#accordion">
|
||||
<div class="card-body">
|
||||
<input type="text" name="id" id="id" hidden>
|
||||
<input type="text" name="http-1" id="http-1" hidden>
|
||||
<input type="text" name="http-2" id="http-2" hidden>
|
||||
<input type="text" name="http-3" id="http-3" hidden>
|
||||
<!--<input type="text" name="brewfather" id="brewfather" hidden>-->
|
||||
<input type="text" name="influxdb" id="influxdb" hidden>
|
||||
<input type="text" name="mqtt" id="mqtt" hidden>
|
||||
@ -81,8 +82,9 @@
|
||||
<div class="form-group row">
|
||||
<label for="push-target" class="col-sm-2 col-form-label">Push target:</label>
|
||||
<select class="custom-select col-sm-4" required name="push-target" id="push-target">
|
||||
<option value="http-1">HTTP option 1</option>
|
||||
<option value="http-2">HTTP option 2</option>
|
||||
<option value="http-1">HTTP option 1 (post)</option>
|
||||
<option value="http-2">HTTP option 2 (post)</option>
|
||||
<option value="http-3">HTTP option 3 (get)</option>
|
||||
<!--<option value="brewfather">Brewfather</option>-->
|
||||
<option value="influxdb">Influx DB</option>
|
||||
<option value="mqtt">MQTT</option>
|
||||
@ -147,38 +149,72 @@
|
||||
|
||||
// Test the calibration
|
||||
$("#test-btn").click(function(e) {
|
||||
var doc = $("#format").val();
|
||||
doc = doc.replaceAll("${mdns}", "testing");
|
||||
doc = doc.replaceAll("${id}", "e4a344");
|
||||
doc = doc.replaceAll("${sleep-interval}", "300");
|
||||
doc = doc.replaceAll("${temp}", "21.1");
|
||||
doc = doc.replaceAll("${token}", "a-token");
|
||||
doc = doc.replaceAll("${temp-c}", "21.1");
|
||||
doc = doc.replaceAll("${temp-f}", "51.3");
|
||||
doc = doc.replaceAll("${temp-unit}", "C");
|
||||
doc = doc.replaceAll("${battery}", "3.86");
|
||||
doc = doc.replaceAll("${rssi}", "-76");
|
||||
doc = doc.replaceAll("${run-time}", "4.32");
|
||||
doc = doc.replaceAll("${gravity}", "1.044");
|
||||
doc = doc.replaceAll("${gravity-sg}", "1.044");
|
||||
doc = doc.replaceAll("${gravity-plato}", "9.5");
|
||||
doc = doc.replaceAll("${gravity-unit}", "G");
|
||||
doc = doc.replaceAll("${corr-gravity}", "1.044");
|
||||
doc = doc.replaceAll("${corr-gravity-sg}", "1.044");
|
||||
doc = doc.replaceAll("${corr-gravity-plato}", "9.5");
|
||||
doc = doc.replaceAll("${angle}", "54.5");
|
||||
doc = doc.replaceAll("${tilt}", "54.5");
|
||||
var url = "/api/status";
|
||||
//var url = "/test/status.json";
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
|
||||
// Format in a readable json string.
|
||||
try {
|
||||
var json = JSON.parse(doc);
|
||||
doc = JSON.stringify(json, null, 2);
|
||||
} catch(e) {
|
||||
console.log("Not a javascript object!")
|
||||
}
|
||||
var doc = $("#format").val();
|
||||
|
||||
$("#preview").text(doc);
|
||||
if (cfg["temp-format"]=="C")
|
||||
doc = doc.replaceAll("${temp}", cfg["temp-c"]);
|
||||
else
|
||||
doc = doc.replaceAll("${temp}", cfg["temp-f"]);
|
||||
|
||||
if (cfg["gravity-format"]=="G") {
|
||||
var sg = cfg["gravity"];
|
||||
doc = doc.replaceAll("${gravity-sg}", sg);
|
||||
doc = doc.replaceAll("${corr-gravity-sg}", sg);
|
||||
var plato = 259 - (259 - sg);
|
||||
doc = doc.replaceAll("${gravity-plato}", plato);
|
||||
doc = doc.replaceAll("${corr-gravity-plato}", plato);
|
||||
}
|
||||
else {
|
||||
var plato = cfg["gravity"];
|
||||
doc = doc.replaceAll("${gravity-plato}", plato);
|
||||
doc = doc.replaceAll("${corr-gravity-plato}", plato);
|
||||
var sg = 259 / (259 - plato);
|
||||
doc = doc.replaceAll("${gravity-sg}", sg);
|
||||
doc = doc.replaceAll("${corr-gravity-sg}", sg);
|
||||
}
|
||||
|
||||
doc = doc.replaceAll("${mdns}", cfg["mdns"]);
|
||||
doc = doc.replaceAll("${id}", cfg["id"]);
|
||||
doc = doc.replaceAll("${sleep-interval}", cfg["sleep-interval"]);
|
||||
doc = doc.replaceAll("${token}", cfg["token"]);
|
||||
doc = doc.replaceAll("${token2}", cfg["token2"]);
|
||||
doc = doc.replaceAll("${temp-c}", cfg["temp-c"]);
|
||||
doc = doc.replaceAll("${temp-f}", cfg["temp-f"]);
|
||||
doc = doc.replaceAll("${temp-unit}", cfg["temp-format"]);
|
||||
doc = doc.replaceAll("${battery}", cfg["battery"]);
|
||||
doc = doc.replaceAll("${rssi}", cfg["rssi"]);
|
||||
doc = doc.replaceAll("${run-time}", cfg["runtime-average"]);
|
||||
doc = doc.replaceAll("${gravity}", cfg["gravity"]);
|
||||
doc = doc.replaceAll("${gravity-unit}", cfg["gravity-format"]);
|
||||
doc = doc.replaceAll("${corr-gravity}", cfg["gravity"]);
|
||||
doc = doc.replaceAll("${angle}", cfg["angle"]);
|
||||
doc = doc.replaceAll("${tilt}", cfg["angle"]);
|
||||
|
||||
// Format in a readable json string.
|
||||
try {
|
||||
var json = JSON.parse(doc);
|
||||
doc = JSON.stringify(json, null, 2);
|
||||
} catch(e) {
|
||||
console.log("Not a javascript object!")
|
||||
}
|
||||
|
||||
$("#preview").text(doc);
|
||||
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
})
|
||||
.always(function() {
|
||||
$('#spinner').hide();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function setButtonDisabled( b ) {
|
||||
$("#format-btn").prop("disabled", b);
|
||||
@ -209,6 +245,7 @@
|
||||
|
||||
$("#http-1").val(cfg["http-1"]);
|
||||
$("#http-2").val(cfg["http-2"]);
|
||||
$("#http-3").val(cfg["http-3"]);
|
||||
//$("#brewfather").val(cfg["brewfather"]);
|
||||
$("#influxdb").val(cfg["influxdb"]);
|
||||
$("#mqtt").val(cfg["mqtt"]);
|
||||
|
@ -1,2 +1,2 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="/config.htm">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div class="accordion" id="accordion"><div class="card"><div class="card-header" id="headingOne"><h2 class="mb-0"><button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">Push Format Templates</button></h2></div><div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"><div class="card-body"><input type="text" name="id" id="id" hidden> <input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-2" id="http-2" hidden><!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="mqtt" id="mqtt" hidden><div class="form-group row"><label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <select class="custom-select col-sm-4" required name="push-target" id="push-target"><option value="http-1">HTTP option 1</option><option value="http-2">HTTP option 2</option><!--<option value="brewfather">Brewfather</option>--><option value="influxdb">Influx DB</option><option value="mqtt">MQTT</option></select></div><div class="form-group row"><div class="col-sm-12"><textarea rows="5" class="form-control" name="format" id="format">
|
||||
</textarea></div></div><div class="form-group row"><div class="col-sm-8 offset-sm-2"><button class="btn btn-primary" id="format-btn">Save</button> <button class="btn btn-secondary" id="test-btn">Test</button></div></div><pre class="card-preview" id="preview" name="preview"></pre></div></div></div><hr class="my-4"></div><script type="text/javascript">function setButtonDisabled(l){$("#format-btn").prop("disabled",l),$("#test-btn").prop("disabled",l)}function selectFormat(){var l="#"+$("#push-target").val();console.log(l),l=decodeURIComponent($(l).val()),console.log(l),l=l.replaceAll("|","|\n"),console.log(l),$("#format").val(l),$("#preview").text("")}function getConfig(){setButtonDisabled(!0);var l="/api/config/format";$("#spinner").show(),$.getJSON(l,function(l){console.log(l),$("#id").val(l.id),$("#http-1").val(l["http-1"]),$("#http-2").val(l["http-2"]),$("#influxdb").val(l.influxdb),$("#mqtt").val(l.mqtt),selectFormat()}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide(),setButtonDisabled(!1)})}window.onload=getConfig,setButtonDisabled(!0),$(document).ready(function(){null!=location.hash&&""!=location.hash&&($(".collapse").removeClass("in"),$(location.hash+".collapse").collapse("show"))}),$("#push-target").change(function(l){console.log(l),selectFormat()}),$("#format-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("\n","");var t="id="+$("#id").val()+"&"+$("#push-target").val()+"="+encodeURIComponent(e);console.log(t),$.ajax({type:"POST",url:"/api/config/format",data:t,success:function(l){showSuccess("Format stored successfully."),getConfig()},error:function(l){showError("Unable to store format.")}})}),$("#test-btn").click(function(l){var e=$("#format").val();e=e.replaceAll("${mdns}","testing"),e=e.replaceAll("${id}","e4a344"),e=e.replaceAll("${sleep-interval}","300"),e=e.replaceAll("${temp}","21.1"),e=e.replaceAll("${token}","a-token"),e=e.replaceAll("${temp-c}","21.1"),e=e.replaceAll("${temp-f}","51.3"),e=e.replaceAll("${temp-unit}","C"),e=e.replaceAll("${battery}","3.86"),e=e.replaceAll("${rssi}","-76"),e=e.replaceAll("${run-time}","4.32"),e=e.replaceAll("${gravity}","1.044"),e=e.replaceAll("${gravity-sg}","1.044"),e=e.replaceAll("${gravity-plato}","9.5"),e=e.replaceAll("${gravity-unit}","G"),e=e.replaceAll("${corr-gravity}","1.044"),e=e.replaceAll("${corr-gravity-sg}","1.044"),e=e.replaceAll("${corr-gravity-plato}","9.5"),e=e.replaceAll("${angle}","54.5"),e=e.replaceAll("${tilt}","54.5");try{var t=JSON.parse(e);e=JSON.stringify(t,null,2)}catch(l){console.log("Not a javascript object!")}$("#preview").text(e)})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div class="accordion" id="accordion"><div class="card"><div class="card-header" id="headingFormat"><h2 class="mb-0"><button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseFormat" aria-expanded="true" aria-controls="collapseFormat">Push Format Templates</button></h2></div><div id="collapseFormat" class="collapse show" aria-labelledby="headingFormat" data-parent="#accordion"><div class="card-body"><input type="text" name="id" id="id" hidden> <input type="text" name="http-1" id="http-1" hidden> <input type="text" name="http-2" id="http-2" hidden> <input type="text" name="http-3" id="http-3" hidden><!--<input type="text" name="brewfather" id="brewfather" hidden>--> <input type="text" name="influxdb" id="influxdb" hidden> <input type="text" name="mqtt" id="mqtt" hidden><div class="form-group row"><label for="push-target" class="col-sm-2 col-form-label">Push target:</label> <select class="custom-select col-sm-4" required name="push-target" id="push-target"><option value="http-1">HTTP option 1 (post)</option><option value="http-2">HTTP option 2 (post)</option><option value="http-3">HTTP option 3 (get)</option><!--<option value="brewfather">Brewfather</option>--><option value="influxdb">Influx DB</option><option value="mqtt">MQTT</option></select></div><div class="form-group row"><div class="col-sm-12"><textarea rows="5" class="form-control" name="format" id="format">
|
||||
</textarea></div></div><div class="form-group row"><div class="col-sm-8 offset-sm-2"><button class="btn btn-primary" id="format-btn">Save</button> <button class="btn btn-secondary" id="test-btn">Test</button></div></div><pre class="card-preview" id="preview" name="preview"></pre></div></div></div><hr class="my-4"></div><script type="text/javascript">function setButtonDisabled(e){$("#format-btn").prop("disabled",e),$("#test-btn").prop("disabled",e)}function selectFormat(){var e="#"+$("#push-target").val();console.log(e),e=decodeURIComponent($(e).val()),console.log(e),e=e.replaceAll("|","|\n"),console.log(e),$("#format").val(e),$("#preview").text("")}function getConfig(){setButtonDisabled(!0);var e="/api/config/format";$("#spinner").show(),$.getJSON(e,function(e){console.log(e),$("#id").val(e.id),$("#http-1").val(e["http-1"]),$("#http-2").val(e["http-2"]),$("#http-3").val(e["http-3"]),$("#influxdb").val(e.influxdb),$("#mqtt").val(e.mqtt),selectFormat()}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide(),setButtonDisabled(!1)})}window.onload=getConfig,setButtonDisabled(!0),$(document).ready(function(){null!=location.hash&&""!=location.hash&&($(".collapse").removeClass("in"),$(location.hash+".collapse").collapse("show"))}),$("#push-target").change(function(e){console.log(e),selectFormat()}),$("#format-btn").click(function(e){var l=$("#format").val();l=l.replaceAll("\n","");var t="id="+$("#id").val()+"&"+$("#push-target").val()+"="+encodeURIComponent(l);console.log(t),$.ajax({type:"POST",url:"/api/config/format",data:t,success:function(e){showSuccess("Format stored successfully."),getConfig()},error:function(e){showError("Unable to store format.")}})}),$("#test-btn").click(function(e){var l="/api/status";$("#spinner").show(),$.getJSON(l,function(e){console.log(e);var l=$("#format").val();if(l="C"==e["temp-format"]?l.replaceAll("${temp}",e["temp-c"]):l.replaceAll("${temp}",e["temp-f"]),"G"==e["gravity-format"]){var t=e.gravity;l=l.replaceAll("${gravity-sg}",t),l=l.replaceAll("${corr-gravity-sg}",t);var a=259-(259-t);l=l.replaceAll("${gravity-plato}",a),l=l.replaceAll("${corr-gravity-plato}",a)}else{a=e.gravity;l=l.replaceAll("${gravity-plato}",a),l=l.replaceAll("${corr-gravity-plato}",a);t=259/(259-a);l=l.replaceAll("${gravity-sg}",t),l=l.replaceAll("${corr-gravity-sg}",t)}l=l.replaceAll("${mdns}",e.mdns),l=l.replaceAll("${id}",e.id),l=l.replaceAll("${sleep-interval}",e["sleep-interval"]),l=l.replaceAll("${token}",e.token),l=l.replaceAll("${token2}",e.token2),l=l.replaceAll("${temp-c}",e["temp-c"]),l=l.replaceAll("${temp-f}",e["temp-f"]),l=l.replaceAll("${temp-unit}",e["temp-format"]),l=l.replaceAll("${battery}",e.battery),l=l.replaceAll("${rssi}",e.rssi),l=l.replaceAll("${run-time}",e["runtime-average"]),l=l.replaceAll("${gravity}",e.gravity),l=l.replaceAll("${gravity-unit}",e["gravity-format"]),l=l.replaceAll("${corr-gravity}",e.gravity),l=l.replaceAll("${angle}",e.angle),l=l.replaceAll("${tilt}",e.angle);try{var r=JSON.parse(l);l=JSON.stringify(r,null,2)}catch(e){console.log("Not a javascript object!")}$("#preview").text(l)}).fail(function(){showError("Unable to get data from the device.")}).always(function(){$("#spinner").hide()})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
@ -25,9 +25,6 @@
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="/index.htm">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/device.htm">Device</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/config.htm">Configuration</a>
|
||||
</li>
|
||||
@ -72,7 +69,54 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="" id="id" hidden></div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Current version:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="app-ver">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3" id="h-app-ver-new" hidden>
|
||||
<div class="col-md-8 themed-grid-col bg-light">New version:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="app-ver-new">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Host name:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="mdns">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Device ID:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="id">Loading...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Platform:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="platform">Loading...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#log-btn").click(function(e){
|
||||
loadLog();
|
||||
});
|
||||
setInterval(function() {
|
||||
loadLog();
|
||||
}, 3000); //5 seconds
|
||||
|
||||
function loadLog() {
|
||||
$("#logContent").load("/log");
|
||||
//$("#logContent").load("/test/status.json");
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="row mb-3">
|
||||
<a class="badge badge-primary" data-toggle="collapse" href="#collapseLog" role="button" aria-expanded="false" aria-controls="collapseLog" id="log-btn">
|
||||
View error log
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse" id="collapseLog">
|
||||
<div class="card card-body">
|
||||
<pre><code id="logContent"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Gravity:</div>
|
||||
@ -91,6 +135,11 @@
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="battery">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col bg-light">Average runtime:</div>
|
||||
<div class="col-md-4 themed-grid-col bg-light" id="runtime">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12 px-md-5 themed-grid-col bg-light custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" name="sleep-mode" id="sleep-mode" disabled>
|
||||
@ -99,6 +148,7 @@
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -121,8 +171,14 @@
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
console.log( cfg );
|
||||
|
||||
//$("#app-ver").text(cfg["app-ver"] + " (html 0.8.0)");
|
||||
$("#app-ver").text(cfg["app-ver"] + " (" + cfg["app-build"] + ")");
|
||||
$("#mdns").text(cfg["mdns"]);
|
||||
$("#id").text(cfg["id"]);
|
||||
|
||||
$("#platform").text(cfg["platform"]);
|
||||
$("#runtime").text(cfg["runtime-average"] + " seconds");
|
||||
|
||||
var angle = cfg["angle"];
|
||||
|
||||
if(angle==0) {
|
||||
|
217
html/test.htm
Normal file
@ -0,0 +1,217 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<title>Beer Gravity Monitor</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body class="py-4">
|
||||
|
||||
<!-- START MENU -->
|
||||
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-primary">
|
||||
|
||||
<a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="javascript:history.back()">Back to configuration</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="spinner-border text-light" id="spinner" role="status"></div>
|
||||
</nav>
|
||||
|
||||
<!-- START MAIN INDEX -->
|
||||
|
||||
<div class="container">
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert">
|
||||
<div id="alert-msg">...</div>
|
||||
<button type="button" id="alert-btn" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function showError( msg ) {
|
||||
$('#alert').removeClass('alert-success').addClass('alert-danger').removeClass('d-none').addClass('show');
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
function showSuccess( msg ) {
|
||||
$('#alert').addClass('alert-success').removeClass('alert-danger').removeClass('d-none').addClass('show');
|
||||
$('#alert-msg').text( msg );
|
||||
}
|
||||
|
||||
$("#alert-btn").click(function(e){
|
||||
$('#alert').addClass('d-none').removeClass('show');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<pre class="card-preview" id="preview" name="preview"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8">
|
||||
<button class="btn btn-primary" id="test-btn">Test</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#spinner').hide();
|
||||
|
||||
function clearLog() {
|
||||
$("#preview").text("");
|
||||
}
|
||||
|
||||
function appendLog(log) {
|
||||
doc = $("#preview").text();
|
||||
doc += log + "\n";
|
||||
$("#preview").text(doc);
|
||||
}
|
||||
|
||||
// Get the configuration values from the API
|
||||
$("#test-btn").click(function(e) {
|
||||
clearLog();
|
||||
appendLog( "Starting test of push targets" );
|
||||
|
||||
var url = "/api/status";
|
||||
//var url = "/test/status.json";
|
||||
$("#test-btn").prop("disabled", true);
|
||||
$('#spinner').show();
|
||||
$.getJSON(url, function (cfg) {
|
||||
var id = cfg["id"];
|
||||
console.log( id );
|
||||
|
||||
testHttp( id, "http-1" );
|
||||
testHttp( id, "http-2" );
|
||||
testHttp( id, "http-3" );
|
||||
testHttp( id, "brewfather" );
|
||||
testInfluxdb( id );
|
||||
testMqtt( id );
|
||||
$('#spinner').hide();
|
||||
$("#test-btn").prop("disabled", false);
|
||||
})
|
||||
.fail(function () {
|
||||
showError('Unable to get data from the device.');
|
||||
$('#spinner').hide();
|
||||
$("#test-btn").prop("disabled", false);
|
||||
})
|
||||
.always(function() {
|
||||
});
|
||||
});
|
||||
|
||||
function testMqtt(id) {
|
||||
var url = "/api/test/push";
|
||||
url += "?id=" + id + "&format=mqtt";
|
||||
//var url = "/test/push.json";
|
||||
$.getJSON(url, function (cfg) {
|
||||
var code = cfg["code"];
|
||||
var success = cfg["success"];
|
||||
var enabled = cfg["enabled"];
|
||||
|
||||
if(!enabled) {
|
||||
appendLog( "Push target 'mqtt' is not configured/used" );
|
||||
} else if(success) {
|
||||
appendLog( "Push target 'mqtt' successful" );
|
||||
} else{
|
||||
if(code==-3)
|
||||
appendLog( "Push target 'mqtt' failed to connect" );
|
||||
else if(code==-4)
|
||||
appendLog( "Push target 'mqtt' failed with error timeout" );
|
||||
else if(code==-10)
|
||||
appendLog( "Push target 'mqtt' failed with error denied" );
|
||||
else
|
||||
appendLog( "Push target 'mqtt' failed with error code " + code );
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
appendLog( "Failed to test push target 'influxdb'");
|
||||
})
|
||||
}
|
||||
|
||||
function testInfluxdb(id) {
|
||||
var url = "/api/test/push";
|
||||
url += "?id=" + id + "&format=influxdb";
|
||||
//var url = "/test/push.json";
|
||||
$.getJSON(url, function (cfg) {
|
||||
var code = cfg["code"];
|
||||
var success = cfg["success"];
|
||||
var enabled = cfg["enabled"];
|
||||
|
||||
if(!enabled) {
|
||||
appendLog( "Push target 'influxdb' is not configured/used" );
|
||||
} else if(success) {
|
||||
appendLog( "Push target 'influxdb' successful" );
|
||||
} else{
|
||||
if(code==400)
|
||||
appendLog( "Push target 'influxdb' failed with error code 400, bad request" );
|
||||
else if(code==401)
|
||||
appendLog( "Push target 'influxdb' failed with error code 401, unauthorized" );
|
||||
else if(code==404)
|
||||
appendLog( "Push target 'influxdb' failed with error code 404, url not found" );
|
||||
else
|
||||
appendLog( "Push target 'influxdb' failed with error code " + code );
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
appendLog( "Failed to test push target 'influxdb'");
|
||||
})
|
||||
}
|
||||
|
||||
function testHttp(id, target) {
|
||||
var url = "/api/test/push";
|
||||
url += "?id=" + id + "&format=" + target;
|
||||
//var url = "/test/push.json";
|
||||
$.getJSON(url, function (cfg) {
|
||||
var code = cfg["code"];
|
||||
var success = cfg["success"];
|
||||
var enabled = cfg["enabled"];
|
||||
|
||||
if(!enabled) {
|
||||
appendLog( "Push target '" + target + "' is not configured/used" );
|
||||
} else if(success) {
|
||||
appendLog( "Push target '" + target + "' successful" );
|
||||
} else{
|
||||
if(code==400)
|
||||
appendLog( "Push target '" + target + "' failed with error code 400, bad request" );
|
||||
else if(code==401)
|
||||
appendLog( "Push target '" + target + "' failed with error code 401, unauthorized" );
|
||||
else if(code==404)
|
||||
appendLog( "Push target '" + target + "' failed with error code 404, url not found" );
|
||||
else
|
||||
appendLog( "Push target '" + target + "' failed with error code " + code );
|
||||
}
|
||||
})
|
||||
.fail(function () {
|
||||
appendLog( "Failed to test push target '" + target + "'");
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
|
||||
<div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div>
|
||||
</body>
|
||||
</html>
|
1
html/test.min.htm
Normal file
@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content=""><title>Beer Gravity Monitor</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script></head><body class="py-4"><!-- START MENU --><nav class="navbar navbar-expand-sm navbar-dark bg-primary"><a class="navbar-brand" href="/index.htm">Beer Gravity Monitor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbar"><ul class="navbar-nav mr-auto"><li class="nav-item"><a class="nav-link" href="javascript:history.back()">Back to configuration</a></li></ul></div><div class="spinner-border text-light" id="spinner" role="status"></div></nav><!-- START MAIN INDEX --><div class="container"><hr class="my-2"><div class="alert alert-success alert-dismissible fade hide show d-none" role="alert" id="alert"><div id="alert-msg">...</div><button type="button" id="alert-btn" class="close" aria-label="Close"><span aria-hidden="true">×</span></button></div><script type="text/javascript">function showError(s){$("#alert").removeClass("alert-success").addClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}function showSuccess(s){$("#alert").addClass("alert-success").removeClass("alert-danger").removeClass("d-none").addClass("show"),$("#alert-msg").text(s)}$("#alert-btn").click(function(s){$("#alert").addClass("d-none").removeClass("show")})</script><div><div class="card"><div class="card-body"><pre class="card-preview" id="preview" name="preview"></pre></div></div><div class="form-group row"></div><div class="form-group row"><div class="col-sm-8"><button class="btn btn-primary" id="test-btn">Test</button></div></div><hr class="my-4"></div><script type="text/javascript">function clearLog(){$("#preview").text("")}function appendLog(t){doc=$("#preview").text(),doc+=t+"\n",$("#preview").text(doc)}function testMqtt(t){var e="/api/test/push";e+="?id="+t+"&format=mqtt",$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target 'mqtt' successful":-3==e?"Push target 'mqtt' failed to connect":-4==e?"Push target 'mqtt' failed with error timeout":-10==e?"Push target 'mqtt' failed with error denied":"Push target 'mqtt' failed with error code "+e:"Push target 'mqtt' is not configured/used")}).fail(function(){appendLog("Failed to test push target 'influxdb'")})}function testInfluxdb(t){var e="/api/test/push";e+="?id="+t+"&format=influxdb",$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target 'influxdb' successful":400==e?"Push target 'influxdb' failed with error code 400, bad request":401==e?"Push target 'influxdb' failed with error code 401, unauthorized":404==e?"Push target 'influxdb' failed with error code 404, url not found":"Push target 'influxdb' failed with error code "+e:"Push target 'influxdb' is not configured/used")}).fail(function(){appendLog("Failed to test push target 'influxdb'")})}function testHttp(t,s){var e="/api/test/push";e+="?id="+t+"&format="+s,$.getJSON(e,function(t){var e=t.code,r=t.success,i=t.enabled;appendLog(i?r?"Push target '"+s+"' successful":400==e?"Push target '"+s+"' failed with error code 400, bad request":401==e?"Push target '"+s+"' failed with error code 401, unauthorized":404==e?"Push target '"+s+"' failed with error code 404, url not found":"Push target '"+s+"' failed with error code "+e:"Push target '"+s+"' is not configured/used")}).fail(function(){appendLog("Failed to test push target '"+s+"'")})}$("#spinner").hide(),$("#test-btn").click(function(t){clearLog(),appendLog("Starting test of push targets");var e="/api/status";$("#test-btn").prop("disabled",!0),$("#spinner").show(),$.getJSON(e,function(t){var e=t.id;console.log(e),testHttp(e,"http-1"),testHttp(e,"http-2"),testHttp(e,"http-3"),testHttp(e,"brewfather"),testInfluxdb(e),testMqtt(e),$("#spinner").hide(),$("#test-btn").prop("disabled",!1)}).fail(function(){showError("Unable to get data from the device."),$("#spinner").hide(),$("#test-btn").prop("disabled",!1)}).always(function(){})})</script><!-- START FOOTER --><div class="container-fluid themed-container bg-primary text-light">(C) Copyright 2021-22 Magnus Persson</div></div></body></html>
|
@ -66,10 +66,6 @@
|
||||
<div class="col-md-2 themed-grid-col bg-light">index.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="index">Checking...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 themed-grid-col bg-light">device.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="device">Checking...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 themed-grid-col bg-light">config.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="config">Checking...</div>
|
||||
@ -82,6 +78,10 @@
|
||||
<div class="col-md-2 themed-grid-col bg-light">format.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="format">Checking...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 themed-grid-col bg-light">test.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="test">Checking...</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 themed-grid-col bg-light">about.min.htm</div>
|
||||
<div class="col-md-6 themed-grid-col bg-light" id="about">Checking...</div>
|
||||
@ -90,7 +90,7 @@
|
||||
<div class="row mb-3">
|
||||
<form action="/api/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="col-md-8 custom-file">
|
||||
<input type="file" class="custom-file-input" name="name" id="name">
|
||||
<input type="file" accept=".min.htm" class="custom-file-input" name="name" id="name">
|
||||
<label class="custom-file-label" for="name">Choose file</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="upload-btn" value="upload">Upload</button>
|
||||
@ -121,21 +121,21 @@ function getUpload() {
|
||||
else
|
||||
$("#index").text("File is missing.");
|
||||
|
||||
if( cfg["device"] )
|
||||
$("#device").text("Completed.");
|
||||
else
|
||||
$("#device").text("File is missing.");
|
||||
|
||||
if( cfg["config"] )
|
||||
$("#config").text("Completed.");
|
||||
else
|
||||
$("#config").text("File is missing.");
|
||||
|
||||
if( cfg["calibration"] )
|
||||
if( cfg["calibration"] )
|
||||
$("#calibration").text("Completed.");
|
||||
else
|
||||
$("#calibration").text("File is missing.");
|
||||
|
||||
if( cfg["test"] )
|
||||
$("#test").text("Completed.");
|
||||
else
|
||||
$("#test").text("File is missing.");
|
||||
|
||||
if( cfg["format"] )
|
||||
$("#format").text("Completed.");
|
||||
else
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
Built by Khoi Hoang https://github.com/khoih-prog/ESP_DoubleResetDetector
|
||||
Licensed under MIT license
|
||||
Version: 1.2.1
|
||||
Version: 1.3.1
|
||||
|
||||
Version Modified By Date Comments
|
||||
------- ----------- ---------- -----------
|
||||
@ -22,6 +22,8 @@
|
||||
1.1.2 K Hoang 10/10/2021 Update `platform.ini` and `library.json`
|
||||
1.2.0 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
|
||||
1.2.1 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
|
||||
1.3.0 K Hoang 10/02/2022 Add support to new ESP32-S3
|
||||
1.3.1 K Hoang 04/03/2022 Add waitingForDRD() function to signal in DRD wating period
|
||||
*****************************************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
@ -29,13 +31,26 @@
|
||||
#ifndef ESP_DoubleResetDetector_H
|
||||
#define ESP_DoubleResetDetector_H
|
||||
|
||||
#ifndef DOUBLERESETDETECTOR_DEBUG
|
||||
#define DOUBLERESETDETECTOR_DEBUG false
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO) && (ARDUINO >= 100)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.2.1"
|
||||
#ifndef ESP_DOUBLE_RESET_DETECTOR_VERSION
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION "ESP_DoubleResetDetector v1.3.1"
|
||||
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MAJOR 1
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_MINOR 3
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_PATCH 1
|
||||
|
||||
#define ESP_DOUBLE_RESET_DETECTOR_VERSION_INT 1003001
|
||||
#endif
|
||||
|
||||
#define ESP_DOUBLERESETDETECTOR_VERSION ESP_DOUBLE_RESET_DETECTOR_VERSION
|
||||
|
||||
//#define ESP_DRD_USE_EEPROM false
|
||||
@ -45,7 +60,11 @@
|
||||
|
||||
#ifdef ESP32
|
||||
#if (!ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
|
||||
#warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM
|
||||
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
#warning Neither EEPROM, SPIFFS nor LittleFS selected. Default to EEPROM
|
||||
#endif
|
||||
|
||||
#ifdef ESP_DRD_USE_EEPROM
|
||||
#undef ESP_DRD_USE_EEPROM
|
||||
#define ESP_DRD_USE_EEPROM true
|
||||
@ -55,7 +74,10 @@
|
||||
|
||||
#ifdef ESP8266
|
||||
#if (!ESP8266_DRD_USE_RTC && !ESP_DRD_USE_EEPROM && !ESP_DRD_USE_SPIFFS && !ESP_DRD_USE_LITTLEFS)
|
||||
#warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
#warning Neither RTC, EEPROM, LITTLEFS nor SPIFFS selected. Default to EEPROM
|
||||
#endif
|
||||
|
||||
#ifdef ESP_DRD_USE_EEPROM
|
||||
#undef ESP_DRD_USE_EEPROM
|
||||
#define ESP_DRD_USE_EEPROM true
|
||||
@ -87,14 +109,20 @@
|
||||
// Check cores/esp32/esp_arduino_version.h and cores/esp32/core_version.h
|
||||
//#if ( ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) ) //(ESP_ARDUINO_VERSION_MAJOR >= 2)
|
||||
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
|
||||
#warning Using ESP32 Core 1.0.6 or 2.0.0+
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
#warning Using ESP32 Core 1.0.6 or 2.0.0+
|
||||
#endif
|
||||
|
||||
// The library has been merged into esp32 core from release 1.0.6
|
||||
#include <LittleFS.h>
|
||||
|
||||
#define FileFS LittleFS
|
||||
#define FS_Name "LittleFS"
|
||||
#else
|
||||
#warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
|
||||
#if (DOUBLERESETDETECTOR_DEBUG)
|
||||
#warning Using ESP32 Core 1.0.5-. You must install LITTLEFS library
|
||||
#endif
|
||||
|
||||
// The library has been merged into esp32 core from release 1.0.6
|
||||
#include <LITTLEFS.h> // https://github.com/lorol/LITTLEFS
|
||||
|
||||
@ -125,9 +153,7 @@
|
||||
|
||||
#endif //#if ESP_DRD_USE_EEPROM
|
||||
|
||||
#ifndef DOUBLERESETDETECTOR_DEBUG
|
||||
#define DOUBLERESETDETECTOR_DEBUG false
|
||||
#endif
|
||||
|
||||
|
||||
#define DOUBLERESETDETECTOR_FLAG_SET 0xD0D01234
|
||||
#define DOUBLERESETDETECTOR_FLAG_CLEAR 0xD0D04321
|
||||
@ -194,6 +220,11 @@ class DoubleResetDetector
|
||||
return doubleResetDetected;
|
||||
|
||||
};
|
||||
|
||||
bool waitingForDRD()
|
||||
{
|
||||
return waitingForDoubleReset;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
|
||||
Licensed under MIT license
|
||||
|
||||
Version: 1.8.0
|
||||
Version: 1.10.1
|
||||
|
||||
Version Modified By Date Comments
|
||||
------- ----------- ---------- -----------
|
||||
@ -56,7 +56,10 @@
|
||||
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
|
||||
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
|
||||
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
|
||||
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
|
||||
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
|
||||
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
|
||||
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
|
||||
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
|
||||
*****************************************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
740
lib/ESP_WiFiManager/ESP_WiFiManager.hpp
Normal file
@ -16,7 +16,7 @@
|
||||
Built by Khoi Hoang https://github.com/khoih-prog/ESP_WiFiManager
|
||||
Licensed under MIT license
|
||||
|
||||
Version: 1.8.0
|
||||
Version: 1.10.1
|
||||
|
||||
Version Modified By Date Comments
|
||||
------- ----------- ---------- -----------
|
||||
@ -56,7 +56,10 @@
|
||||
1.7.6 K Hoang 26/11/2021 Auto detect ESP32 core and use either built-in LittleFS or LITTLEFS library
|
||||
1.7.7 K Hoang 26/11/2021 Fix compile error for ESP32 core v1.0.5-
|
||||
1.7.8 K Hoang 30/11/2021 Fix bug to permit using HTTP port different from 80. Fix bug
|
||||
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
|
||||
1.8.0 K Hoang 29/12/2021 Fix `multiple-definitions` linker error and weird bug related to src_cpp
|
||||
1.9.0 K Hoang 17/01/2022 Enable compatibility with old code to include only ESP_WiFiManager.h
|
||||
1.10.0 K Hoang 10/02/2022 Add support to new ESP32-S3
|
||||
1.10.1 K Hoang 11/02/2022 Add LittleFS support to ESP32-C3. Use core LittleFS instead of Lorol's LITTLEFS for v2.0.0+
|
||||
*****************************************************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
@ -71,8 +71,6 @@
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
#if 1
|
||||
|
||||
#define TZ_Africa_Abidjan ("GMT0")
|
||||
#define TZ_Africa_Accra ("GMT0")
|
||||
#define TZ_Africa_Addis_Ababa ("EAT-3")
|
||||
@ -534,479 +532,11 @@
|
||||
#define TZ_Etc_Universal ("UTC0")
|
||||
#define TZ_Etc_Zulu ("UTC0")
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#define TZ_Africa_Abidjan PSTR("GMT0")
|
||||
#define TZ_Africa_Accra PSTR("GMT0")
|
||||
#define TZ_Africa_Addis_Ababa PSTR("EAT-3")
|
||||
#define TZ_Africa_Algiers PSTR("CET-1")
|
||||
#define TZ_Africa_Asmara PSTR("EAT-3")
|
||||
#define TZ_Africa_Bamako PSTR("GMT0")
|
||||
#define TZ_Africa_Bangui PSTR("WAT-1")
|
||||
#define TZ_Africa_Banjul PSTR("GMT0")
|
||||
#define TZ_Africa_Bissau PSTR("GMT0")
|
||||
#define TZ_Africa_Blantyre PSTR("CAT-2")
|
||||
#define TZ_Africa_Brazzaville PSTR("WAT-1")
|
||||
#define TZ_Africa_Bujumbura PSTR("CAT-2")
|
||||
#define TZ_Africa_Cairo PSTR("EET-2")
|
||||
#define TZ_Africa_Casablanca PSTR("<+01>-1")
|
||||
#define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Africa_Conakry PSTR("GMT0")
|
||||
#define TZ_Africa_Dakar PSTR("GMT0")
|
||||
#define TZ_Africa_Dar_es_Salaam PSTR("EAT-3")
|
||||
#define TZ_Africa_Djibouti PSTR("EAT-3")
|
||||
#define TZ_Africa_Douala PSTR("WAT-1")
|
||||
#define TZ_Africa_El_Aaiun PSTR("<+01>-1")
|
||||
#define TZ_Africa_Freetown PSTR("GMT0")
|
||||
#define TZ_Africa_Gaborone PSTR("CAT-2")
|
||||
#define TZ_Africa_Harare PSTR("CAT-2")
|
||||
#define TZ_Africa_Johannesburg PSTR("SAST-2")
|
||||
#define TZ_Africa_Juba PSTR("EAT-3")
|
||||
#define TZ_Africa_Kampala PSTR("EAT-3")
|
||||
#define TZ_Africa_Khartoum PSTR("CAT-2")
|
||||
#define TZ_Africa_Kigali PSTR("CAT-2")
|
||||
#define TZ_Africa_Kinshasa PSTR("WAT-1")
|
||||
#define TZ_Africa_Lagos PSTR("WAT-1")
|
||||
#define TZ_Africa_Libreville PSTR("WAT-1")
|
||||
#define TZ_Africa_Lome PSTR("GMT0")
|
||||
#define TZ_Africa_Luanda PSTR("WAT-1")
|
||||
#define TZ_Africa_Lubumbashi PSTR("CAT-2")
|
||||
#define TZ_Africa_Lusaka PSTR("CAT-2")
|
||||
#define TZ_Africa_Malabo PSTR("WAT-1")
|
||||
#define TZ_Africa_Maputo PSTR("CAT-2")
|
||||
#define TZ_Africa_Maseru PSTR("SAST-2")
|
||||
#define TZ_Africa_Mbabane PSTR("SAST-2")
|
||||
#define TZ_Africa_Mogadishu PSTR("EAT-3")
|
||||
#define TZ_Africa_Monrovia PSTR("GMT0")
|
||||
#define TZ_Africa_Nairobi PSTR("EAT-3")
|
||||
#define TZ_Africa_Ndjamena PSTR("WAT-1")
|
||||
#define TZ_Africa_Niamey PSTR("WAT-1")
|
||||
#define TZ_Africa_Nouakchott PSTR("GMT0")
|
||||
#define TZ_Africa_Ouagadougou PSTR("GMT0")
|
||||
#define TZ_Africa_PortomNovo PSTR("WAT-1")
|
||||
#define TZ_Africa_Sao_Tome PSTR("GMT0")
|
||||
#define TZ_Africa_Tripoli PSTR("EET-2")
|
||||
#define TZ_Africa_Tunis PSTR("CET-1")
|
||||
#define TZ_Africa_Windhoek PSTR("CAT-2")
|
||||
#define TZ_America_Adak PSTR("HST10HDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Anchorage PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Anguilla PSTR("AST4")
|
||||
#define TZ_America_Antigua PSTR("AST4")
|
||||
#define TZ_America_Araguaina PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Catamarca PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Cordoba PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Jujuy PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_La_Rioja PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Mendoza PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Rio_Gallegos PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Salta PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_San_Juan PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_San_Luis PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Tucuman PSTR("<-03>3")
|
||||
#define TZ_America_Argentina_Ushuaia PSTR("<-03>3")
|
||||
#define TZ_America_Aruba PSTR("AST4")
|
||||
#define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0")
|
||||
#define TZ_America_Atikokan PSTR("EST5")
|
||||
#define TZ_America_Bahia PSTR("<-03>3")
|
||||
#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Barbados PSTR("AST4")
|
||||
#define TZ_America_Belem PSTR("<-03>3")
|
||||
#define TZ_America_Belize PSTR("CST6")
|
||||
#define TZ_America_BlancmSablon PSTR("AST4")
|
||||
#define TZ_America_Boa_Vista PSTR("<-04>4")
|
||||
#define TZ_America_Bogota PSTR("<-05>5")
|
||||
#define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Campo_Grande PSTR("<-04>4")
|
||||
#define TZ_America_Cancun PSTR("EST5")
|
||||
#define TZ_America_Caracas PSTR("<-04>4")
|
||||
#define TZ_America_Cayenne PSTR("<-03>3")
|
||||
#define TZ_America_Cayman PSTR("EST5")
|
||||
#define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Costa_Rica PSTR("CST6")
|
||||
#define TZ_America_Creston PSTR("MST7")
|
||||
#define TZ_America_Cuiaba PSTR("<-04>4")
|
||||
#define TZ_America_Curacao PSTR("AST4")
|
||||
#define TZ_America_Danmarkshavn PSTR("GMT0")
|
||||
#define TZ_America_Dawson PSTR("MST7")
|
||||
#define TZ_America_Dawson_Creek PSTR("MST7")
|
||||
#define TZ_America_Denver PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Detroit PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Dominica PSTR("AST4")
|
||||
#define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Eirunepe PSTR("<-05>5")
|
||||
#define TZ_America_El_Salvador PSTR("CST6")
|
||||
#define TZ_America_Fortaleza PSTR("<-03>3")
|
||||
#define TZ_America_Fort_Nelson PSTR("MST7")
|
||||
#define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1")
|
||||
#define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Grenada PSTR("AST4")
|
||||
#define TZ_America_Guadeloupe PSTR("AST4")
|
||||
#define TZ_America_Guatemala PSTR("CST6")
|
||||
#define TZ_America_Guayaquil PSTR("<-05>5")
|
||||
#define TZ_America_Guyana PSTR("<-04>4")
|
||||
#define TZ_America_Halifax PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Havana PSTR("CST5CDT,M3.2.0/0,M11.1.0/1")
|
||||
#define TZ_America_Hermosillo PSTR("MST7")
|
||||
#define TZ_America_Indiana_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Knox PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Marengo PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Petersburg PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Tell_City PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Jamaica PSTR("EST5")
|
||||
#define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kralendijk PSTR("AST4")
|
||||
#define TZ_America_La_Paz PSTR("<-04>4")
|
||||
#define TZ_America_Lima PSTR("<-05>5")
|
||||
#define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Lower_Princes PSTR("AST4")
|
||||
#define TZ_America_Maceio PSTR("<-03>3")
|
||||
#define TZ_America_Managua PSTR("CST6")
|
||||
#define TZ_America_Manaus PSTR("<-04>4")
|
||||
#define TZ_America_Marigot PSTR("AST4")
|
||||
#define TZ_America_Martinique PSTR("AST4")
|
||||
#define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0")
|
||||
#define TZ_America_Montevideo PSTR("<-03>3")
|
||||
#define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Montserrat PSTR("AST4")
|
||||
#define TZ_America_Nassau PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_New_York PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Nipigon PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Nome PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Noronha PSTR("<-02>2")
|
||||
#define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Panama PSTR("EST5")
|
||||
#define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Paramaribo PSTR("<-03>3")
|
||||
#define TZ_America_Phoenix PSTR("MST7")
|
||||
#define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Port_of_Spain PSTR("AST4")
|
||||
#define TZ_America_Porto_Velho PSTR("<-04>4")
|
||||
#define TZ_America_Puerto_Rico PSTR("AST4")
|
||||
#define TZ_America_Punta_Arenas PSTR("<-03>3")
|
||||
#define TZ_America_Rainy_River PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Rankin_Inlet PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Recife PSTR("<-03>3")
|
||||
#define TZ_America_Regina PSTR("CST6")
|
||||
#define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Rio_Branco PSTR("<-05>5")
|
||||
#define TZ_America_Santarem PSTR("<-03>3")
|
||||
#define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24")
|
||||
#define TZ_America_Santo_Domingo PSTR("AST4")
|
||||
#define TZ_America_Sao_Paulo PSTR("<-03>3")
|
||||
#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||
#define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_St_Barthelemy PSTR("AST4")
|
||||
#define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_St_Kitts PSTR("AST4")
|
||||
#define TZ_America_St_Lucia PSTR("AST4")
|
||||
#define TZ_America_St_Thomas PSTR("AST4")
|
||||
#define TZ_America_St_Vincent PSTR("AST4")
|
||||
#define TZ_America_Swift_Current PSTR("CST6")
|
||||
#define TZ_America_Tegucigalpa PSTR("CST6")
|
||||
#define TZ_America_Thule PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Thunder_Bay PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Tijuana PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Tortola PSTR("AST4")
|
||||
#define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Whitehorse PSTR("MST7")
|
||||
#define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_Antarctica_Casey PSTR("<+11>-11")
|
||||
#define TZ_Antarctica_Davis PSTR("<+07>-7")
|
||||
#define TZ_Antarctica_DumontDUrville PSTR("<+10>-10")
|
||||
#define TZ_Antarctica_Macquarie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Antarctica_Mawson PSTR("<+05>-5")
|
||||
#define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||
#define TZ_Antarctica_Palmer PSTR("<-03>3")
|
||||
#define TZ_Antarctica_Rothera PSTR("<-03>3")
|
||||
#define TZ_Antarctica_Syowa PSTR("<+03>-3")
|
||||
#define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3")
|
||||
#define TZ_Antarctica_Vostok PSTR("<+06>-6")
|
||||
#define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Asia_Aden PSTR("<+03>-3")
|
||||
#define TZ_Asia_Almaty PSTR("<+06>-6")
|
||||
#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1")
|
||||
#define TZ_Asia_Anadyr PSTR("<+12>-12")
|
||||
#define TZ_Asia_Aqtau PSTR("<+05>-5")
|
||||
#define TZ_Asia_Aqtobe PSTR("<+05>-5")
|
||||
#define TZ_Asia_Ashgabat PSTR("<+05>-5")
|
||||
#define TZ_Asia_Atyrau PSTR("<+05>-5")
|
||||
#define TZ_Asia_Baghdad PSTR("<+03>-3")
|
||||
#define TZ_Asia_Bahrain PSTR("<+03>-3")
|
||||
#define TZ_Asia_Baku PSTR("<+04>-4")
|
||||
#define TZ_Asia_Bangkok PSTR("<+07>-7")
|
||||
#define TZ_Asia_Barnaul PSTR("<+07>-7")
|
||||
#define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0")
|
||||
#define TZ_Asia_Bishkek PSTR("<+06>-6")
|
||||
#define TZ_Asia_Brunei PSTR("<+08>-8")
|
||||
#define TZ_Asia_Chita PSTR("<+09>-9")
|
||||
#define TZ_Asia_Choibalsan PSTR("<+08>-8")
|
||||
#define TZ_Asia_Colombo PSTR("<+0530>-5:30")
|
||||
#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0")
|
||||
#define TZ_Asia_Dhaka PSTR("<+06>-6")
|
||||
#define TZ_Asia_Dili PSTR("<+09>-9")
|
||||
#define TZ_Asia_Dubai PSTR("<+04>-4")
|
||||
#define TZ_Asia_Dushanbe PSTR("<+05>-5")
|
||||
#define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49")
|
||||
#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49")
|
||||
#define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7")
|
||||
#define TZ_Asia_Hong_Kong PSTR("HKT-8")
|
||||
#define TZ_Asia_Hovd PSTR("<+07>-7")
|
||||
#define TZ_Asia_Irkutsk PSTR("<+08>-8")
|
||||
#define TZ_Asia_Jakarta PSTR("WIB-7")
|
||||
#define TZ_Asia_Jayapura PSTR("WIT-9")
|
||||
#define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0")
|
||||
#define TZ_Asia_Kabul PSTR("<+0430>-4:30")
|
||||
#define TZ_Asia_Kamchatka PSTR("<+12>-12")
|
||||
#define TZ_Asia_Karachi PSTR("PKT-5")
|
||||
#define TZ_Asia_Kathmandu PSTR("<+0545>-5:45")
|
||||
#define TZ_Asia_Khandyga PSTR("<+09>-9")
|
||||
#define TZ_Asia_Kolkata PSTR("IST-5:30")
|
||||
#define TZ_Asia_Krasnoyarsk PSTR("<+07>-7")
|
||||
#define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8")
|
||||
#define TZ_Asia_Kuching PSTR("<+08>-8")
|
||||
#define TZ_Asia_Kuwait PSTR("<+03>-3")
|
||||
#define TZ_Asia_Macau PSTR("CST-8")
|
||||
#define TZ_Asia_Magadan PSTR("<+11>-11")
|
||||
#define TZ_Asia_Makassar PSTR("WITA-8")
|
||||
#define TZ_Asia_Manila PSTR("PST-8")
|
||||
#define TZ_Asia_Muscat PSTR("<+04>-4")
|
||||
#define TZ_Asia_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Asia_Novokuznetsk PSTR("<+07>-7")
|
||||
#define TZ_Asia_Novosibirsk PSTR("<+07>-7")
|
||||
#define TZ_Asia_Omsk PSTR("<+06>-6")
|
||||
#define TZ_Asia_Oral PSTR("<+05>-5")
|
||||
#define TZ_Asia_Phnom_Penh PSTR("<+07>-7")
|
||||
#define TZ_Asia_Pontianak PSTR("WIB-7")
|
||||
#define TZ_Asia_Pyongyang PSTR("KST-9")
|
||||
#define TZ_Asia_Qatar PSTR("<+03>-3")
|
||||
#define TZ_Asia_Qyzylorda PSTR("<+05>-5")
|
||||
#define TZ_Asia_Riyadh PSTR("<+03>-3")
|
||||
#define TZ_Asia_Sakhalin PSTR("<+11>-11")
|
||||
#define TZ_Asia_Samarkand PSTR("<+05>-5")
|
||||
#define TZ_Asia_Seoul PSTR("KST-9")
|
||||
#define TZ_Asia_Shanghai PSTR("CST-8")
|
||||
#define TZ_Asia_Singapore PSTR("<+08>-8")
|
||||
#define TZ_Asia_Srednekolymsk PSTR("<+11>-11")
|
||||
#define TZ_Asia_Taipei PSTR("CST-8")
|
||||
#define TZ_Asia_Tashkent PSTR("<+05>-5")
|
||||
#define TZ_Asia_Tbilisi PSTR("<+04>-4")
|
||||
#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
|
||||
#define TZ_Asia_Thimphu PSTR("<+06>-6")
|
||||
#define TZ_Asia_Tokyo PSTR("JST-9")
|
||||
#define TZ_Asia_Tomsk PSTR("<+07>-7")
|
||||
#define TZ_Asia_Ulaanbaatar PSTR("<+08>-8")
|
||||
#define TZ_Asia_Urumqi PSTR("<+06>-6")
|
||||
#define TZ_Asia_UstmNera PSTR("<+10>-10")
|
||||
#define TZ_Asia_Vientiane PSTR("<+07>-7")
|
||||
#define TZ_Asia_Vladivostok PSTR("<+10>-10")
|
||||
#define TZ_Asia_Yakutsk PSTR("<+09>-9")
|
||||
#define TZ_Asia_Yangon PSTR("<+0630>-6:30")
|
||||
#define TZ_Asia_Yekaterinburg PSTR("<+05>-5")
|
||||
#define TZ_Asia_Yerevan PSTR("<+04>-4")
|
||||
#define TZ_Atlantic_Azores PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||
#define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Cape_Verde PSTR("<-01>1")
|
||||
#define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Reykjavik PSTR("GMT0")
|
||||
#define TZ_Atlantic_South_Georgia PSTR("<-02>2")
|
||||
#define TZ_Atlantic_Stanley PSTR("<-03>3")
|
||||
#define TZ_Atlantic_St_Helena PSTR("GMT0")
|
||||
#define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Brisbane PSTR("AEST-10")
|
||||
#define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Darwin PSTR("ACST-9:30")
|
||||
#define TZ_Australia_Eucla PSTR("<+0845>-8:45")
|
||||
#define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Lindeman PSTR("AEST-10")
|
||||
#define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
|
||||
#define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Perth PSTR("AWST-8")
|
||||
#define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Astrakhan PSTR("<+04>-4")
|
||||
#define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Brussels PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Bucharest PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Budapest PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Busingen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Chisinau PSTR("EET-2EEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Copenhagen PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Dublin PSTR("IST-1GMT0,M10.5.0,M3.5.0/1")
|
||||
#define TZ_Europe_Gibraltar PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Guernsey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Helsinki PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Isle_of_Man PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Istanbul PSTR("<+03>-3")
|
||||
#define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Kaliningrad PSTR("EET-2")
|
||||
#define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Kirov PSTR("<+03>-3")
|
||||
#define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Luxembourg PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Madrid PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Malta PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Mariehamn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Minsk PSTR("<+03>-3")
|
||||
#define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Moscow PSTR("MSK-3")
|
||||
#define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Prague PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Riga PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Rome PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Samara PSTR("<+04>-4")
|
||||
#define TZ_Europe_San_Marino PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Sarajevo PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Saratov PSTR("<+04>-4")
|
||||
#define TZ_Europe_Simferopol PSTR("MSK-3")
|
||||
#define TZ_Europe_Skopje PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Sofia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Ulyanovsk PSTR("<+04>-4")
|
||||
#define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Volgograd PSTR("<+04>-4")
|
||||
#define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Indian_Antananarivo PSTR("EAT-3")
|
||||
#define TZ_Indian_Chagos PSTR("<+06>-6")
|
||||
#define TZ_Indian_Christmas PSTR("<+07>-7")
|
||||
#define TZ_Indian_Cocos PSTR("<+0630>-6:30")
|
||||
#define TZ_Indian_Comoro PSTR("EAT-3")
|
||||
#define TZ_Indian_Kerguelen PSTR("<+05>-5")
|
||||
#define TZ_Indian_Mahe PSTR("<+04>-4")
|
||||
#define TZ_Indian_Maldives PSTR("<+05>-5")
|
||||
#define TZ_Indian_Mauritius PSTR("<+04>-4")
|
||||
#define TZ_Indian_Mayotte PSTR("EAT-3")
|
||||
#define TZ_Indian_Reunion PSTR("<+04>-4")
|
||||
#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
|
||||
#define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||
#define TZ_Pacific_Bougainville PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
|
||||
#define TZ_Pacific_Chuuk PSTR("<+10>-10")
|
||||
#define TZ_Pacific_Easter PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
|
||||
#define TZ_Pacific_Efate PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Enderbury PSTR("<+13>-13")
|
||||
#define TZ_Pacific_Fakaofo PSTR("<+13>-13")
|
||||
#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.2.0,M1.2.3/99")
|
||||
#define TZ_Pacific_Funafuti PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Galapagos PSTR("<-06>6")
|
||||
#define TZ_Pacific_Gambier PSTR("<-09>9")
|
||||
#define TZ_Pacific_Guadalcanal PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Guam PSTR("ChST-10")
|
||||
#define TZ_Pacific_Honolulu PSTR("HST10")
|
||||
#define TZ_Pacific_Kiritimati PSTR("<+14>-14")
|
||||
#define TZ_Pacific_Kosrae PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Kwajalein PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Majuro PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Marquesas PSTR("<-0930>9:30")
|
||||
#define TZ_Pacific_Midway PSTR("SST11")
|
||||
#define TZ_Pacific_Nauru PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Niue PSTR("<-11>11")
|
||||
#define TZ_Pacific_Norfolk PSTR("<+11>-11<+12>,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Pacific_Noumea PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Pago_Pago PSTR("SST11")
|
||||
#define TZ_Pacific_Palau PSTR("<+09>-9")
|
||||
#define TZ_Pacific_Pitcairn PSTR("<-08>8")
|
||||
#define TZ_Pacific_Pohnpei PSTR("<+11>-11")
|
||||
#define TZ_Pacific_Port_Moresby PSTR("<+10>-10")
|
||||
#define TZ_Pacific_Rarotonga PSTR("<-10>10")
|
||||
#define TZ_Pacific_Saipan PSTR("ChST-10")
|
||||
#define TZ_Pacific_Tahiti PSTR("<-10>10")
|
||||
#define TZ_Pacific_Tarawa PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Tongatapu PSTR("<+13>-13")
|
||||
#define TZ_Pacific_Wake PSTR("<+12>-12")
|
||||
#define TZ_Pacific_Wallis PSTR("<+12>-12")
|
||||
#define TZ_Etc_GMT PSTR("GMT0")
|
||||
#define TZ_Etc_GMTm0 PSTR("GMT0")
|
||||
#define TZ_Etc_GMTm1 PSTR("<+01>-1")
|
||||
#define TZ_Etc_GMTm2 PSTR("<+02>-2")
|
||||
#define TZ_Etc_GMTm3 PSTR("<+03>-3")
|
||||
#define TZ_Etc_GMTm4 PSTR("<+04>-4")
|
||||
#define TZ_Etc_GMTm5 PSTR("<+05>-5")
|
||||
#define TZ_Etc_GMTm6 PSTR("<+06>-6")
|
||||
#define TZ_Etc_GMTm7 PSTR("<+07>-7")
|
||||
#define TZ_Etc_GMTm8 PSTR("<+08>-8")
|
||||
#define TZ_Etc_GMTm9 PSTR("<+09>-9")
|
||||
#define TZ_Etc_GMTm10 PSTR("<+10>-10")
|
||||
#define TZ_Etc_GMTm11 PSTR("<+11>-11")
|
||||
#define TZ_Etc_GMTm12 PSTR("<+12>-12")
|
||||
#define TZ_Etc_GMTm13 PSTR("<+13>-13")
|
||||
#define TZ_Etc_GMTm14 PSTR("<+14>-14")
|
||||
#define TZ_Etc_GMT0 PSTR("GMT0")
|
||||
#define TZ_Etc_GMTp0 PSTR("GMT0")
|
||||
#define TZ_Etc_GMTp1 PSTR("<-01>1")
|
||||
#define TZ_Etc_GMTp2 PSTR("<-02>2")
|
||||
#define TZ_Etc_GMTp3 PSTR("<-03>3")
|
||||
#define TZ_Etc_GMTp4 PSTR("<-04>4")
|
||||
#define TZ_Etc_GMTp5 PSTR("<-05>5")
|
||||
#define TZ_Etc_GMTp6 PSTR("<-06>6")
|
||||
#define TZ_Etc_GMTp7 PSTR("<-07>7")
|
||||
#define TZ_Etc_GMTp8 PSTR("<-08>8")
|
||||
#define TZ_Etc_GMTp9 PSTR("<-09>9")
|
||||
#define TZ_Etc_GMTp10 PSTR("<-10>10")
|
||||
#define TZ_Etc_GMTp11 PSTR("<-11>11")
|
||||
#define TZ_Etc_GMTp12 PSTR("<-12>12")
|
||||
#define TZ_Etc_UCT PSTR("UTC0")
|
||||
#define TZ_Etc_UTC PSTR("UTC0")
|
||||
#define TZ_Etc_Greenwich PSTR("GMT0")
|
||||
#define TZ_Etc_Universal PSTR("UTC0")
|
||||
#define TZ_Etc_Zulu PSTR("UTC0")
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#define TIMEZONE_MAX_LEN 50
|
||||
|
||||
#if 1
|
||||
|
||||
const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
static const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
{
|
||||
#if USING_AFRICA
|
||||
"Africa/Abidjan", //PSTR("GMT0")
|
||||
@ -1503,7 +1033,7 @@ const char TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
static const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
{
|
||||
#if USING_AFRICA
|
||||
TZ_Africa_Abidjan, //PSTR("GMT0")
|
||||
@ -1999,152 +1529,4 @@ const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] /*PROGMEM*/ =
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
#else
|
||||
const char TZ_NAME[][TIMEZONE_MAX_LEN] =
|
||||
{
|
||||
"Pacific/Pago_Pago",
|
||||
"America/Adak",
|
||||
"Pacific/Honolulu",
|
||||
"Pacific/Marquesas",
|
||||
"Pacific/Gambier",
|
||||
"America/Anchorage",
|
||||
"America/Los_Angeles",
|
||||
"Pacific/Pitcairn",
|
||||
"America/Phoenix",
|
||||
"America/Denver",
|
||||
"America/Guatemala",
|
||||
"America/Chicago",
|
||||
"Pacific/Easter",
|
||||
"America/Bogota",
|
||||
"America/New_York",
|
||||
"America/Caracas",
|
||||
"America/Halifax",
|
||||
"America/Santo_Domingo",
|
||||
"America/Santiago",
|
||||
"America/St_Johns",
|
||||
"America/Godthab",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"America/Montevideo",
|
||||
"Etc/GMT+2",
|
||||
"Atlantic/Azores",
|
||||
"Atlantic/Cape_Verde",
|
||||
"Etc/UTC",
|
||||
"Europe/London",
|
||||
"Europe/Berlin",
|
||||
"Africa/Lagos",
|
||||
"Africa/Windhoek",
|
||||
"Asia/Beirut",
|
||||
"Africa/Johannesburg",
|
||||
"Asia/Baghdad",
|
||||
"Europe/Moscow",
|
||||
"Asia/Tehran",
|
||||
"Asia/Dubai",
|
||||
"Asia/Baku",
|
||||
"Asia/Kabul",
|
||||
"Asia/Yekaterinburg",
|
||||
"Asia/Karachi",
|
||||
"Asia/Kolkata",
|
||||
"Asia/Kathmandu",
|
||||
"Asia/Dhaka",
|
||||
"Asia/Omsk",
|
||||
"Asia/Krasnoyarsk",
|
||||
"Asia/Jakarta",
|
||||
"Asia/Shanghai",
|
||||
"Asia/Irkutsk",
|
||||
"Australia/Eucla",
|
||||
"Asia/Yakutsk",
|
||||
"Asia/Tokyo",
|
||||
"Australia/Darwin",
|
||||
"Australia/Adelaide",
|
||||
"Australia/Brisbane",
|
||||
"Asia/Vladivostok",
|
||||
"Australia/Sydney",
|
||||
"Australia/Lord_Howe",
|
||||
"Asia/Kamchatka",
|
||||
"Pacific/Noumea",
|
||||
"Pacific/Norfolk",
|
||||
"Pacific/Auckland",
|
||||
"Pacific/Tarawa",
|
||||
"Pacific/Chatham",
|
||||
"Pacific/Tongatapu",
|
||||
"Pacific/Apia",
|
||||
"Pacific/Kiritimati",
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
const char ESP_TZ_NAME[][TIMEZONE_MAX_LEN] =
|
||||
{
|
||||
TZ_Pacific_Pago_Pago, //PSTR("SST11")
|
||||
TZ_America_Adak, //PSTR("HST10HDT,M3.2.0,M11.1.0")
|
||||
TZ_Pacific_Honolulu, //PSTR("HST10")
|
||||
TZ_Pacific_Marquesas, //PSTR("<-0930>9:30")
|
||||
TZ_Pacific_Gambier, //PSTR("<-09>9")
|
||||
TZ_America_Anchorage, //PSTR("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
TZ_America_Los_Angeles, //"PST8PDT,M3.2.0,M11.1.0", //"America/Los_Angeles",
|
||||
TZ_Pacific_Pitcairn, //PSTR("<-08>8")
|
||||
TZ_America_Phoenix, //PSTR("MST7")
|
||||
TZ_America_Denver, //"MST7MDT,M3.2.0,M11.1.0", //"America/Denver",
|
||||
TZ_America_Guatemala, //PSTR("CST6")
|
||||
TZ_America_Chicago, //"CST6CDT,M3.2.0,M11.1.0", //"America/Chicago",
|
||||
TZ_Pacific_Easter, //PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
|
||||
TZ_America_Bogota, //PSTR("<-05>5")
|
||||
TZ_America_New_York, //"EST5EDT,M3.2.0,M11.1.0", //"America/New_York",
|
||||
TZ_America_Caracas, //PSTR("<-04>4")
|
||||
TZ_America_Halifax, //"AST4ADT,M3.2.0,M11.1.0", //"America/Halifax",
|
||||
TZ_America_Santo_Domingo, //PSTR("AST4")
|
||||
TZ_America_Santiago, //"<-04>4<-03>,M9.1.6/24,M4.1.6/24", //"America/Santiago",
|
||||
TZ_America_St_Johns, //PSTR("NST3:30NDT,M3.2.0,M11.1.0")
|
||||
TZ_America_Godthab, //"<-03>3<-02>,M3.5.0/-2,M10.5.0/-1", //"America/Godthab",
|
||||
TZ_America_Argentina_Buenos_Aires, //PSTR("<-03>3")
|
||||
TZ_America_Montevideo, //"<-03>3", //"America/Montevideo",
|
||||
TZ_Etc_GMTp2, //PSTR("<-02>2")
|
||||
TZ_Atlantic_Azores, //PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||
TZ_Atlantic_Cape_Verde, //PSTR("<-01>1")
|
||||
TZ_Etc_UTC, //PSTR("UTC0")
|
||||
TZ_Europe_London, //PSTR("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
TZ_Europe_Berlin, //PSTR("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
TZ_Africa_Lagos, //PSTR("WAT-1")
|
||||
TZ_Africa_Windhoek, //PSTR("CAT-2")
|
||||
TZ_Asia_Beirut, //"EET-2EEST,M3.5.0/0,M10.5.0/0" //"Asia/Beirut",
|
||||
TZ_Africa_Johannesburg, //"SAST-2", //"Africa/Johannesburg",
|
||||
TZ_Asia_Baghdad, //"<+03>-3", //"Asia/Baghdad",
|
||||
TZ_Europe_Moscow, //PSTR("MSK-3")
|
||||
TZ_Asia_Tehran, //PSTR("<+0330>-3:30<+0430>,J79/24,J263/24")
|
||||
TZ_Asia_Dubai, //"<+04>-4", //"Asia/Dubai",
|
||||
TZ_Asia_Baku, //PSTR("<+04>-4")
|
||||
TZ_Asia_Kabul, //PSTR("<+0430>-4:30")
|
||||
TZ_Asia_Yekaterinburg, //PSTR("<+05>-5")
|
||||
TZ_Asia_Karachi, //PSTR("PKT-5")
|
||||
TZ_Asia_Kolkata, //PSTR("IST-5:30")
|
||||
TZ_Asia_Kathmandu, //PSTR("<+0545>-5:45")
|
||||
TZ_Asia_Dhaka, //"<+06>-6", //"Asia/Dhaka",
|
||||
TZ_Asia_Omsk, //PSTR("<+06>-6")
|
||||
TZ_Asia_Krasnoyarsk, //PSTR("<+07>-7")
|
||||
TZ_Asia_Jakarta, //"WIB-7", //"Asia/Jakarta",
|
||||
TZ_Asia_Shanghai, //"CST-8", //"Asia/Shanghai",
|
||||
TZ_Asia_Irkutsk, //PSTR("<+08>-8")
|
||||
TZ_Australia_Eucla, //PSTR("<+0845>-8:45")
|
||||
TZ_Asia_Yakutsk, //PSTR("<+09>-9")
|
||||
TZ_Asia_Tokyo, //"JST-9", //"Asia/Tokyo",
|
||||
TZ_Australia_Darwin, //PSTR("ACST-9:30")
|
||||
TZ_Australia_Adelaide, //PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||
TZ_Australia_Brisbane, //"AEST-10", //"Australia/Brisbane",
|
||||
TZ_Asia_Vladivostok, //PSTR("<+10>-10")
|
||||
TZ_Australia_Sydney, //PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
TZ_Australia_Lord_Howe, //PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
|
||||
TZ_Asia_Kamchatka, //PSTR("<+12>-12")
|
||||
TZ_Pacific_Noumea, //"<+11>-11", //"Pacific/Noumea",
|
||||
TZ_Pacific_Norfolk, //PSTR("<+11>-11<+12>,M10.1.0,M4.1.0/3")
|
||||
TZ_Pacific_Auckland, //"NZST-12NZDT,M9.5.0,M4.1.0/3", //"Pacific/Auckland",
|
||||
TZ_Pacific_Tarawa, //"<+12>-12", //"Pacific/Tarawa",
|
||||
TZ_Pacific_Chatham, //PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
|
||||
TZ_Pacific_Tongatapu, //PSTR("<+13>-13")
|
||||
TZ_Pacific_Apia, //PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4")
|
||||
TZ_Pacific_Kiritimati, //PSTR("<+14>-14")
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // TZDB_H
|
||||
|
13
part32.csv
Normal file
@ -0,0 +1,13 @@
|
||||
# Name,Type,SubType,Offset,Size,Flags
|
||||
nvs,data,nvs,0x9000,0x5000
|
||||
otadata,data,ota,0xe000,0x2000
|
||||
app0,app,ota_0,0x10000,0x1c0000
|
||||
app1,app,ota_1,0x1d0000,0x1c0000
|
||||
spiffs,data,spiffs,0x390000,0x70000
|
||||
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
#nvs, data, nvs, 0x9000, 0x5000,
|
||||
#otadata, data, ota, 0xe000, 0x2000,
|
||||
#app0, app, ota_0, 0x10000, 0x140000, # 1310720
|
||||
#app1, app, ota_1, 0x150000,0x140000, # 1310720
|
||||
#spiffs, data, spiffs, 0x290000,0x170000, # 1507328
|
|
@ -20,9 +20,8 @@ framework = arduino
|
||||
board = d1_mini
|
||||
build_unflags =
|
||||
build_flags =
|
||||
-Wl,-Map,output.map
|
||||
-D BAUD=${common_env_data.monitor_speed}
|
||||
-D ACTIVATE_OTA
|
||||
-D ACTIVATE_OTA
|
||||
#-D DEBUG_ESP_HTTP_CLIENT
|
||||
#-D DEBUG_ESP_HTTP_SERVER
|
||||
#-D DEBUG_ESP_PORT=Serial
|
||||
@ -34,7 +33,8 @@ build_flags =
|
||||
-D EMBED_HTML # If this is not used the html files needs to be on the file system (can be uploaded)
|
||||
-D USER_SSID=\""\"" # =\""myssid\""
|
||||
-D USER_SSID_PWD=\""\"" # =\""mypwd\""
|
||||
-D CFG_APPVER="\"0.8.0\""
|
||||
-D CFG_APPVER="\"0.9.0\""
|
||||
!python script/git_rev.py
|
||||
lib_deps = # Switched to forks for better version control.
|
||||
# Using local copy of these libraries
|
||||
#https://github.com/jrowberg/i2cdevlib.git#<document>
|
||||
@ -61,6 +61,7 @@ extra_scripts =
|
||||
build_unflags =
|
||||
${common_env_data.build_unflags}
|
||||
build_flags =
|
||||
-Wl,-Map,output.map
|
||||
${common_env_data.build_flags}
|
||||
#-D PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||
#-D SKIP_SLEEPMODE
|
||||
@ -114,7 +115,7 @@ extra_scripts =
|
||||
script/create_versionjson.py
|
||||
build_unflags = ${common_env_data.build_unflags}
|
||||
build_flags =
|
||||
${common_env_data.build_flags}
|
||||
${common_env_data.build_flags}
|
||||
-D COLLECT_PERFDATA
|
||||
-D LOG_LEVEL=5
|
||||
lib_deps =
|
||||
@ -123,12 +124,9 @@ board = ${common_env_data.board}
|
||||
build_type = release
|
||||
board_build.filesystem = littlefs
|
||||
|
||||
[env:gravity32-perf]
|
||||
[env:gravity32-release]
|
||||
framework = arduino
|
||||
# platformio only supports v1.0.6 of the esp32 libs.
|
||||
platform = espressif32
|
||||
# tasmota port of v2.0.2
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.1/platform-tasmota-espressif32-2.0.2.1.zip
|
||||
upload_speed = ${common_env_data.upload_speed}
|
||||
monitor_speed = ${common_env_data.monitor_speed}
|
||||
extra_scripts =
|
||||
@ -138,14 +136,75 @@ extra_scripts =
|
||||
build_unflags =
|
||||
${common_env_data.build_unflags}
|
||||
build_flags =
|
||||
${common_env_data.build_flags}
|
||||
-Wl,-Map,output.map
|
||||
${common_env_data.build_flags}
|
||||
#-D COLLECT_PERFDATA
|
||||
-D LOG_LEVEL=5
|
||||
-D CFG_DISABLE_LOGGING # Turn off verbose/notice logging to reduce size and dont overload uart (applies to LOG_LEVEL6)
|
||||
-D GYRO_DISABLE_LOGGING
|
||||
-D CALC_DISABLE_LOGGING
|
||||
-D HELPER_DISABLE_LOGGING
|
||||
-D PUSH_DISABLE_LOGGING
|
||||
-D TSEN_DISABLE_LOGGING
|
||||
-D WIFI_DISABLE_LOGGING
|
||||
-D WEB_DISABLE_LOGGING
|
||||
-D MAIN_DISABLE_LOGGING
|
||||
lib_deps =
|
||||
${common_env_data.lib_deps}
|
||||
https://github.com/lorol/LITTLEFS#1.0.6
|
||||
https://github.com/h2zero/NimBLE-Arduino#1.3.7
|
||||
board = featheresp32
|
||||
build_type = release
|
||||
board_build.partitions = part32.csv
|
||||
board_build.filesystem = littlefs
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:gravity32-perf]
|
||||
framework = arduino
|
||||
platform = espressif32
|
||||
upload_speed = ${common_env_data.upload_speed}
|
||||
monitor_speed = ${common_env_data.monitor_speed}
|
||||
extra_scripts =
|
||||
script/copy_html.py
|
||||
script/copy_firmware.py
|
||||
script/create_versionjson.py
|
||||
build_unflags =
|
||||
${common_env_data.build_unflags}
|
||||
build_flags =
|
||||
-Wl,-Map,output.map
|
||||
${common_env_data.build_flags}
|
||||
-D COLLECT_PERFDATA
|
||||
-D LOG_LEVEL=5
|
||||
lib_deps =
|
||||
${common_env_data.lib_deps}
|
||||
board = nodemcu-32s
|
||||
https://github.com/lorol/LITTLEFS#1.0.6
|
||||
https://github.com/h2zero/NimBLE-Arduino#1.3.7
|
||||
board = featheresp32
|
||||
build_type = release
|
||||
#build_type = debug
|
||||
#board_build.filesystem = littlefs
|
||||
board_build.filesystem = spiffs
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = part32.csv
|
||||
board_build.filesystem = littlefs
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:gravity32-release2]
|
||||
framework = arduino
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip # build fails
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip
|
||||
upload_speed = ${common_env_data.upload_speed}
|
||||
monitor_speed = ${common_env_data.monitor_speed}
|
||||
extra_scripts =
|
||||
script/copy_html.py
|
||||
script/copy_firmware.py
|
||||
script/create_versionjson.py
|
||||
build_unflags =
|
||||
${common_env_data.build_unflags}
|
||||
build_flags =
|
||||
${common_env_data.build_flags}
|
||||
-D ESPRESSIF32_20 # v2.0 framework
|
||||
-D LOG_LEVEL=5
|
||||
lib_deps =
|
||||
${common_env_data.lib_deps}
|
||||
https://github.com/h2zero/NimBLE-Arduino#1.3.7
|
||||
board = featheresp32
|
||||
build_type = release
|
||||
board_build.partitions = part32.csv
|
||||
board_build.filesystem = littlefs
|
@ -18,8 +18,12 @@ def after_build(source, target, env):
|
||||
target = dir + "/bin/firmware.bin"
|
||||
if name == "gravity-perf" :
|
||||
target = dir + "/bin/firmware-perf.bin"
|
||||
if name == "gravity32-release" :
|
||||
target = dir + "/bin/firmware32.bin"
|
||||
if name == "gravity32-perf" :
|
||||
target = dir + "/bin/firmware32-perf.bin"
|
||||
if name == "gravity32-release2" :
|
||||
target = dir + "/bin/firmware32_2.bin"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
|
@ -17,9 +17,6 @@ shutil.copyfile( source + file, target + file )
|
||||
file = "config.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
file = "device.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
file = "index.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
@ -29,3 +26,9 @@ shutil.copyfile( source + file, target + file )
|
||||
file = "format.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
file = "test.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
file = "firmware.min.htm"
|
||||
#print( "Copy file: " + source + file + "->" + target + file)
|
||||
shutil.copyfile( source + file, target + file )
|
||||
|
@ -19,42 +19,43 @@ def after_build(source, target, env):
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 2
|
||||
source = dir + "/data/device.min.htm"
|
||||
target = dir + "/bin/device.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 3
|
||||
source = dir + "/data/config.min.htm"
|
||||
target = dir + "/bin/config.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 4
|
||||
# Copy file 3
|
||||
source = dir + "/data/about.min.htm"
|
||||
target = dir + "/bin/about.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 5
|
||||
# Copy file 4
|
||||
source = dir + "/data/calibration.min.htm"
|
||||
target = dir + "/bin/calibration.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 6
|
||||
# Copy file 5
|
||||
source = dir + "/data/format.min.htm"
|
||||
target = dir + "/bin/format.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
# Copy file 6
|
||||
source = dir + "/data/test.min.htm"
|
||||
target = dir + "/bin/test.min.htm"
|
||||
print( "Copy file : " + source + " -> " + target )
|
||||
shutil.copyfile( source, target )
|
||||
|
||||
target = dir + "/bin/version.json"
|
||||
ver = get_build_flag_value("CFG_APPVER")
|
||||
|
||||
print( "Creating version.json" )
|
||||
f = open( target, "w" )
|
||||
f.write( "{ \"project\":\"gravmon\", \"version\":" + ver + ", " )
|
||||
f.write( " \"html\": [ \"index.min.htm\", \"device.min.htm\", \"config.min.htm\", \"calibration.min.htm\", \"format.min.htm\", \"about.min.htm\" ] }" )
|
||||
#f.write( " \"html\": [ \"index.min.htm\", \"config.min.htm\", \"calibration.min.htm\", \"test.min.htm\", \"format.min.htm\", \"about.min.htm\" ] }" )
|
||||
f.write( " \"html\": [ ] }" )
|
||||
f.close()
|
||||
|
||||
|
||||
|
9
script/git_rev.py
Normal file
@ -0,0 +1,9 @@
|
||||
import subprocess
|
||||
|
||||
revision = (
|
||||
subprocess.check_output(["git", "rev-parse", "HEAD"])
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
revision = revision[-6:]
|
||||
print("-D CFG_GITREV='\"..%s\"'" % revision)
|
102
src/ble.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
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.
|
||||
*/
|
||||
#if defined(ESP32)
|
||||
|
||||
#include <ble.hpp>
|
||||
#include <string>
|
||||
|
||||
// Tilt UUID variants and data format, based on tilt-sim
|
||||
//
|
||||
// https://github.com/spouliot/tilt-sim
|
||||
//
|
||||
// Tilt data format is described here. Only SG and Temp is transmitted over BLE.
|
||||
// https://kvurd.com/blog/tilt-hydrometer-ibeacon-data-format/
|
||||
|
||||
//
|
||||
// Create ble sender
|
||||
//
|
||||
BleSender::BleSender(const char* color) {
|
||||
BLEDevice::init("");
|
||||
|
||||
// boost power to maximum, these might be changed once battery life using BLE
|
||||
// has been tested.
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
|
||||
esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9);
|
||||
|
||||
_advertising = BLEDevice::getAdvertising();
|
||||
_color = color;
|
||||
|
||||
if (!_color.compareTo("red"))
|
||||
_uuid = BLEUUID::fromString("A495BB10-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("green"))
|
||||
_uuid = BLEUUID::fromString("A495BB20-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("black"))
|
||||
_uuid = BLEUUID::fromString("A495BB30-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("purple"))
|
||||
_uuid = BLEUUID::fromString("A495BB40-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("orange"))
|
||||
_uuid = BLEUUID::fromString("A495BB50-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("blue"))
|
||||
_uuid = BLEUUID::fromString("A495BB60-C5B1-4B44-B512-1370F02D74DE");
|
||||
else if (!_color.compareTo("yellow"))
|
||||
_uuid = BLEUUID::fromString("A495BB70-C5B1-4B44-B512-1370F02D74DE");
|
||||
else // if (_color.compareTo("pink"))
|
||||
_uuid = BLEUUID::fromString("A495BB80-C5B1-4B44-B512-1370F02D74DE");
|
||||
}
|
||||
|
||||
//
|
||||
// Send temp and gravity via BLE
|
||||
//
|
||||
void BleSender::sendData(float tempF, float gravSG) {
|
||||
uint16_t gravity = gravSG * 1000; // SG * 1000 or SG * 10000 for Tilt Pro/HD
|
||||
uint16_t temperature = tempF; // Deg F _or_ Deg F * 10 for Tilt Pro/HD
|
||||
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setManufacturerId(
|
||||
0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
|
||||
oBeacon.setProximityUUID(_uuid);
|
||||
oBeacon.setMajor(temperature);
|
||||
oBeacon.setMinor(gravity);
|
||||
std::string strServiceData = "";
|
||||
strServiceData += static_cast<char>(26); // Len
|
||||
strServiceData += static_cast<char>(0xFF); // Type
|
||||
strServiceData += oBeacon.getData();
|
||||
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
|
||||
oAdvertisementData.addData(strServiceData);
|
||||
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
_advertising->setAdvertisementData(oAdvertisementData);
|
||||
_advertising->setScanResponseData(oScanResponseData);
|
||||
_advertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON);
|
||||
|
||||
_advertising->start();
|
||||
delay(100);
|
||||
_advertising->stop();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
#endif // ESP32
|
47
src/ble.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-22 Magnus
|
||||
|
||||
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.
|
||||
*/
|
||||
#ifndef SRC_BLE_HPP_
|
||||
#define SRC_BLE_HPP_
|
||||
|
||||
#if defined(ESP32)
|
||||
|
||||
#include <NimBLEBeacon.h>
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
#include <config.hpp>
|
||||
#include <main.hpp>
|
||||
|
||||
class BleSender {
|
||||
private:
|
||||
BLEAdvertising* _advertising;
|
||||
String _color;
|
||||
BLEUUID _uuid;
|
||||
|
||||
public:
|
||||
explicit BleSender(const char* color);
|
||||
void sendData(float tempF, float gravSG);
|
||||
};
|
||||
|
||||
#endif // ESP32
|
||||
#endif // SRC_BLE_HPP_
|
@ -35,13 +35,13 @@ Config::Config() {
|
||||
// Assiging default values
|
||||
char buf[30];
|
||||
#if defined(ESP8266)
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", (unsigned int)ESP.getChipId());
|
||||
snprintf(&buf[0], sizeof(buf), "%06x", (unsigned int)ESP.getChipId());
|
||||
#else // defined (ESP32)
|
||||
uint32_t chipId = 0;
|
||||
for (int i = 0; i < 17; i = i + 8) {
|
||||
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
||||
}
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", chipId);
|
||||
snprintf(&buf[0], sizeof(buf), "%06x", chipId);
|
||||
#endif
|
||||
_id = String(&buf[0]);
|
||||
snprintf(&buf[0], sizeof(buf), "" WIFI_MDNS "%s", getID());
|
||||
@ -51,12 +51,6 @@ Config::Config() {
|
||||
Log.verbose(F("CFG : Created config for %s (%s)." CR), _id.c_str(),
|
||||
_mDNS.c_str());
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
setVoltageFactor(1.59); // Conversion factor for battery on ESP8266
|
||||
#else // defined (ESP32)
|
||||
setVoltageFactor(1.43); // Conversion factor for battery on ESP32
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
@ -65,20 +59,23 @@ Config::Config() {
|
||||
//
|
||||
void Config::createJson(DynamicJsonDocument& doc) {
|
||||
doc[PARAM_MDNS] = getMDNS();
|
||||
//doc[PARAM_CONFIG_VER] = getConfigVersion();
|
||||
// doc[PARAM_CONFIG_VER] = getConfigVersion();
|
||||
doc[PARAM_ID] = getID();
|
||||
doc[PARAM_OTA] = getOtaURL();
|
||||
doc[PARAM_SSID] = getWifiSSID();
|
||||
doc[PARAM_PASS] = getWifiPass();
|
||||
doc[PARAM_BLE] = getColorBLE();
|
||||
doc[PARAM_TEMPFORMAT] = String(getTempFormat());
|
||||
doc[PARAM_PUSH_BREWFATHER] = getBrewfatherPushUrl();
|
||||
doc[PARAM_TOKEN] = getToken();
|
||||
doc[PARAM_TOKEN2] = getToken2();
|
||||
doc[PARAM_PUSH_HTTP] = getHttpUrl();
|
||||
doc[PARAM_PUSH_HTTP_H1] = getHttpHeader(0);
|
||||
doc[PARAM_PUSH_HTTP_H2] = getHttpHeader(1);
|
||||
doc[PARAM_PUSH_HTTP2] = getHttp2Url();
|
||||
doc[PARAM_PUSH_HTTP2_H1] = getHttp2Header(0);
|
||||
doc[PARAM_PUSH_HTTP2_H2] = getHttp2Header(1);
|
||||
doc[PARAM_PUSH_HTTP3] = getHttp3Url();
|
||||
doc[PARAM_PUSH_INFLUXDB2] = getInfluxDb2PushUrl();
|
||||
doc[PARAM_PUSH_INFLUXDB2_ORG] = getInfluxDb2PushOrg();
|
||||
doc[PARAM_PUSH_INFLUXDB2_BUCKET] = getInfluxDb2PushBucket();
|
||||
@ -203,6 +200,7 @@ bool Config::loadFile() {
|
||||
if (!doc[PARAM_MDNS].isNull()) setMDNS(doc[PARAM_MDNS]);
|
||||
if (!doc[PARAM_SSID].isNull()) setWifiSSID(doc[PARAM_SSID]);
|
||||
if (!doc[PARAM_PASS].isNull()) setWifiPass(doc[PARAM_PASS]);
|
||||
if (!doc[PARAM_BLE].isNull()) setColorBLE(doc[PARAM_BLE]);
|
||||
|
||||
if (!doc[PARAM_TEMPFORMAT].isNull()) {
|
||||
String s = doc[PARAM_TEMPFORMAT];
|
||||
@ -213,6 +211,7 @@ bool Config::loadFile() {
|
||||
setBrewfatherPushUrl(doc[PARAM_PUSH_BREWFATHER]);
|
||||
|
||||
if (!doc[PARAM_TOKEN].isNull()) setToken(doc[PARAM_TOKEN]);
|
||||
if (!doc[PARAM_TOKEN2].isNull()) setToken2(doc[PARAM_TOKEN2]);
|
||||
if (!doc[PARAM_PUSH_HTTP].isNull()) setHttpUrl(doc[PARAM_PUSH_HTTP]);
|
||||
if (!doc[PARAM_PUSH_HTTP_H1].isNull())
|
||||
setHttpHeader(doc[PARAM_PUSH_HTTP_H1], 0);
|
||||
@ -223,6 +222,7 @@ bool Config::loadFile() {
|
||||
setHttp2Header(doc[PARAM_PUSH_HTTP2_H1], 0);
|
||||
if (!doc[PARAM_PUSH_HTTP2_H2].isNull())
|
||||
setHttp2Header(doc[PARAM_PUSH_HTTP2_H2], 1);
|
||||
if (!doc[PARAM_PUSH_HTTP3].isNull()) setHttp3Url(doc[PARAM_PUSH_HTTP3]);
|
||||
|
||||
if (!doc[PARAM_PUSH_INFLUXDB2].isNull())
|
||||
setInfluxDb2PushUrl(doc[PARAM_PUSH_INFLUXDB2]);
|
||||
@ -294,10 +294,11 @@ bool Config::loadFile() {
|
||||
_formulaData.g[4] = doc[PARAM_FORMULA_DATA]["g5"].as<double>();
|
||||
|
||||
/*if( doc[PARAM_CONFIG_VER].isNull() ) {
|
||||
// If this parameter is missing we need to reset the gyrocalibaration due to bug #29
|
||||
_gyroCalibration.ax = _gyroCalibration.ay = _gyroCalibration.az = 0;
|
||||
_gyroCalibration.gx = _gyroCalibration.gy = _gyroCalibration.gz = 0;
|
||||
Log.warning(F("CFG : Old configuration format, clearing gyro calibration." CR));
|
||||
// If this parameter is missing we need to reset the gyrocalibaration due to
|
||||
bug #29 _gyroCalibration.ax = _gyroCalibration.ay = _gyroCalibration.az = 0;
|
||||
_gyroCalibration.gx = _gyroCalibration.gy = _gyroCalibration.gz = 0;
|
||||
Log.warning(F("CFG : Old configuration format, clearing gyro calibration."
|
||||
CR));
|
||||
}*/
|
||||
|
||||
_saveNeeded = false; // Reset save flag
|
||||
|
@ -60,7 +60,7 @@ class HardwareConfig {
|
||||
int _gyroSensorMovingThreashold = 500;
|
||||
int _gyroReadCount = 50;
|
||||
int _gyroReadDelay = 3150; // us, empirical, to hold sampling to 200 Hz
|
||||
int _pushTimeout = 10; // seconds
|
||||
int _pushTimeout = 10; // seconds
|
||||
|
||||
public:
|
||||
int getWifiPortalTimeout() { return _wifiPortalTimeout; }
|
||||
@ -96,7 +96,7 @@ class Config {
|
||||
String _mDNS = "";
|
||||
String _otaURL = "";
|
||||
char _tempFormat = 'C';
|
||||
float _voltageFactor = 0;
|
||||
float _voltageFactor = 1.59;
|
||||
float _tempSensorAdjC = 0;
|
||||
int _sleepInterval = 900;
|
||||
bool _gyroTemp = false;
|
||||
@ -109,11 +109,13 @@ class Config {
|
||||
String _brewfatherPushUrl = "";
|
||||
|
||||
String _token = "";
|
||||
String _token2 = "";
|
||||
|
||||
String _httpUrl = "";
|
||||
String _httpHeader[2] = {"Content-Type: application/json", ""};
|
||||
String _http2Url = "";
|
||||
String _http2Header[2] = {"Content-Type: application/json", ""};
|
||||
String _http3Url = "";
|
||||
|
||||
String _influxDb2Url = "";
|
||||
String _influxDb2Org = "";
|
||||
@ -130,6 +132,9 @@ class Config {
|
||||
bool _gravityTempAdj = false;
|
||||
char _gravityFormat = 'G';
|
||||
|
||||
// BLE (ESP32 only)
|
||||
String _colorBLE;
|
||||
|
||||
// Gyro calibration and formula calculation data
|
||||
RawGyroData _gyroCalibration = {0, 0, 0, 0, 0, 0};
|
||||
RawFormulaData _formulaData = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}};
|
||||
@ -189,6 +194,11 @@ class Config {
|
||||
_token = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
const char* getToken2() { return _token2.c_str(); }
|
||||
void setToken2(String s) {
|
||||
_token2 = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
|
||||
// Standard HTTP
|
||||
const char* getHttpUrl() { return _httpUrl.c_str(); }
|
||||
@ -217,6 +227,14 @@ class Config {
|
||||
bool isHttp2Active() { return _http2Url.length() ? true : false; }
|
||||
bool isHttp2SSL() { return _http2Url.startsWith("https://"); }
|
||||
|
||||
const char* getHttp3Url() { return _http3Url.c_str(); }
|
||||
void setHttp3Url(String s) {
|
||||
_http3Url = s;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isHttp3Active() { return _http3Url.length() ? true : false; }
|
||||
bool isHttp3SSL() { return _http3Url.startsWith("https://"); }
|
||||
|
||||
// InfluxDB2
|
||||
const char* getInfluxDb2PushUrl() { return _influxDb2Url.c_str(); }
|
||||
void setInfluxDb2PushUrl(String s) {
|
||||
@ -304,12 +322,12 @@ class Config {
|
||||
_tempSensorAdjC = f;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
void setTempSensorAdjC(String s) {
|
||||
_tempSensorAdjC = s.toFloat();
|
||||
void setTempSensorAdjC(String s, float adjustC = 0) {
|
||||
_tempSensorAdjC = s.toFloat() + adjustC;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
void setTempSensorAdjF(String s) {
|
||||
_tempSensorAdjC = convertFtoC(s.toFloat());
|
||||
void setTempSensorAdjF(String s, float adjustF = 0) {
|
||||
_tempSensorAdjC = convertFtoC(s.toFloat() + adjustF);
|
||||
_saveNeeded = true;
|
||||
}
|
||||
|
||||
@ -335,6 +353,19 @@ class Config {
|
||||
bool isGravitySG() { return _gravityFormat == 'G'; }
|
||||
bool isGravityPlato() { return _gravityFormat == 'P'; }
|
||||
|
||||
const char* getColorBLE() { return _colorBLE.c_str(); }
|
||||
void setColorBLE(String c) {
|
||||
_colorBLE = c;
|
||||
_saveNeeded = true;
|
||||
}
|
||||
bool isBLEActive() { return _colorBLE.length() ? true : false; }
|
||||
bool isWifiPushActive() {
|
||||
return (isHttpActive() || isHttp2Active() || isHttp3Active() ||
|
||||
isBrewfatherActive() || isInfluxDb2Active() || isMqttActive())
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
const RawGyroData& getGyroCalibration() { return _gyroCalibration; }
|
||||
void setGyroCalibration(const RawGyroData& r) {
|
||||
_gyroCalibration = r;
|
||||
|
@ -197,12 +197,13 @@ float GyroSensor::calculateAngle(RawGyroData &raw) {
|
||||
|
||||
// Source: https://www.nxp.com/docs/en/application-note/AN3461.pdf
|
||||
float vY = (acos(abs(ay) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
|
||||
//float vZ = (acos(abs(az) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
|
||||
//float vX = (acos(abs(ax) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 / PI);
|
||||
// float vZ = (acos(abs(az) / sqrt(ax * ax + ay * ay + az * az)) * 180.0 /
|
||||
// PI); float vX = (acos(abs(ax) / sqrt(ax * ax + ay * ay + az * az)) * 180.0
|
||||
// / PI);
|
||||
#if LOG_LEVEL == 6 && !defined(GYRO_DISABLE_LOGGING)
|
||||
//Log.notice(F("GYRO: angleX= %F." CR), vX);
|
||||
// Log.notice(F("GYRO: angleX= %F." CR), vX);
|
||||
Log.notice(F("GYRO: angleY= %F." CR), vY);
|
||||
//Log.notice(F("GYRO: angleZ= %F." CR), vZ);
|
||||
// Log.notice(F("GYRO: angleZ= %F." CR), vZ);
|
||||
#endif
|
||||
return vY;
|
||||
}
|
||||
|
@ -50,7 +50,10 @@ void tcp_cleanup() {
|
||||
//
|
||||
// Convert sg to plato
|
||||
//
|
||||
double convertToPlato(double sg) { return 259 - (259 / sg); }
|
||||
double convertToPlato(double sg) {
|
||||
if (sg) return 259 - (259 / sg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Convert plato to sg
|
||||
@ -92,7 +95,8 @@ void ErrorFileLog::addEntry(String err) {
|
||||
_errors[i] = _errors[i - 1];
|
||||
}
|
||||
_errors[0] = err;
|
||||
Log.errorln(err.c_str());
|
||||
err += String(CR);
|
||||
Log.error(err.c_str());
|
||||
save();
|
||||
}
|
||||
|
||||
@ -186,7 +190,7 @@ void deepSleep(int t) {
|
||||
// Print the build options used
|
||||
//
|
||||
void printBuildOptions() {
|
||||
Log.notice(F("Build options: %s LOGLEVEL %d "
|
||||
Log.notice(F("Build options: %s (%s) LOGLEVEL %d "
|
||||
#ifdef SKIP_SLEEPMODE
|
||||
"SKIP_SLEEP "
|
||||
#endif
|
||||
@ -200,7 +204,7 @@ void printBuildOptions() {
|
||||
"OTA "
|
||||
#endif
|
||||
CR),
|
||||
CFG_APPVER, LOG_LEVEL);
|
||||
CFG_APPVER, CFG_GITREV, LOG_LEVEL);
|
||||
}
|
||||
|
||||
//
|
||||
@ -232,7 +236,7 @@ void BatteryVoltage::read() {
|
||||
// The analog pin can only handle 3.3V maximum voltage so we need to reduce
|
||||
// the voltage (from max 5V)
|
||||
float factor = myConfig.getVoltageFactor(); // Default value is 1.63
|
||||
int v = analogRead(A0);
|
||||
int v = analogRead(PIN_A0);
|
||||
|
||||
// An ESP8266 has a ADC range of 0-1023 and a maximum voltage of 3.3V
|
||||
// An ESP32 has an ADC range of 0-4095 and a maximum voltage of 3.3V
|
||||
@ -352,20 +356,25 @@ void PerfLogging::pushInflux() {
|
||||
snprintf(&buf[0], sizeof(buf), "\ndebug,host=%s,device=%s ",
|
||||
myConfig.getMDNS(), myConfig.getID());
|
||||
body += &buf[0];
|
||||
#if defined (ESP8266)
|
||||
snprintf(
|
||||
&buf[0], sizeof(buf),
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f,heap=%d,heap-frag=%d,heap-max=%d,stack=%d",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(), myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(), ESP.getHeapFragmentation(), ESP.getMaxFreeBlockSize(), ESP.getFreeContStack());
|
||||
#else // defined (ESP32)
|
||||
snprintf(
|
||||
&buf[0], sizeof(buf),
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp=%.2f,heap=%d,heap-frag=%d,heap-max=%d",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(), myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(), 0, ESP.getMaxAllocHeap());
|
||||
#if defined(ESP8266)
|
||||
snprintf(&buf[0], sizeof(buf),
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp="
|
||||
"%.2f,heap=%d,heap-frag=%d,heap-max=%d,stack=%d",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(),
|
||||
myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(),
|
||||
ESP.getHeapFragmentation(), ESP.getMaxFreeBlockSize(),
|
||||
ESP.getFreeContStack());
|
||||
#else // defined (ESP32)
|
||||
snprintf(&buf[0], sizeof(buf),
|
||||
"angle=%.4f,gyro-ax=%d,gyro-ay=%d,gyro-az=%d,gyro-temp=%.2f,ds-temp="
|
||||
"%.2f,heap=%d,heap-frag=%d,heap-max=%d",
|
||||
myGyro.getAngle(), myGyro.getLastGyroData().ax,
|
||||
myGyro.getLastGyroData().ay, myGyro.getLastGyroData().az,
|
||||
myGyro.getSensorTempC(),
|
||||
myTempSensor.getTempC(myConfig.isGyroTemp()), ESP.getFreeHeap(), 0,
|
||||
ESP.getMaxAllocHeap());
|
||||
#endif
|
||||
|
||||
body += &buf[0];
|
||||
@ -424,7 +433,7 @@ float reduceFloatPrecision(float f, int dec) {
|
||||
//
|
||||
String urlencode(String str) {
|
||||
String encodedString;
|
||||
encodedString.reserve(str.length()*2);
|
||||
encodedString.reserve(str.length() * 2);
|
||||
encodedString = "";
|
||||
char c;
|
||||
char code0;
|
||||
|
61
src/main.cpp
@ -21,6 +21,7 @@ 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 <ble.hpp>
|
||||
#include <calc.hpp>
|
||||
#include <config.hpp>
|
||||
#include <gyro.hpp>
|
||||
@ -128,6 +129,8 @@ void setup() {
|
||||
}
|
||||
snprintf(&buf[0], sizeof(buf), "%6x", chipId);
|
||||
Log.notice(F("Main: Started setup for %s." CR), &buf[0]);
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
#endif
|
||||
printBuildOptions();
|
||||
|
||||
@ -158,6 +161,9 @@ void setup() {
|
||||
runMode = RunMode::wifiSetupMode;
|
||||
}
|
||||
|
||||
bool needWifi = true; // Under ESP32 we dont need wifi if only BLE is active
|
||||
// in gravityMode
|
||||
|
||||
// Do this setup for all modes exect wifi setup
|
||||
switch (runMode) {
|
||||
case RunMode::wifiSetupMode:
|
||||
@ -165,14 +171,6 @@ void setup() {
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect();
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
|
||||
if (!myGyro.setup()) {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
@ -185,6 +183,25 @@ void setup() {
|
||||
|
||||
myBatteryVoltage.read();
|
||||
checkSleepMode(myGyro.getAngle(), myBatteryVoltage.getVoltage());
|
||||
|
||||
#if defined(ESP32)
|
||||
if (!myConfig.isWifiPushActive() && runMode == RunMode::gravityMode) {
|
||||
Log.notice(
|
||||
F("Main: Wifi is not needed in gravity mode, skipping "
|
||||
"connection." CR));
|
||||
needWifi = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (needWifi) {
|
||||
LOG_PERF_START("main-wifi-connect");
|
||||
myWifi.connect();
|
||||
LOG_PERF_STOP("main-wifi-connect");
|
||||
}
|
||||
|
||||
LOG_PERF_START("main-temp-setup");
|
||||
myTempSensor.setup();
|
||||
LOG_PERF_STOP("main-temp-setup");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -245,7 +262,7 @@ bool loopReadGravity() {
|
||||
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
|
||||
Log.verbose(F("Main: Sensor values gyro angle=%F, temp=%FC, gravity=%F, "
|
||||
"corr_gravity=%F." CR),
|
||||
angle, tempC, gravity, corrGravity);
|
||||
angle, tempC, gravitySG, corrGravitySG);
|
||||
#endif
|
||||
|
||||
bool pushExpired = (abs((int32_t)(millis() - pushMillis)) >
|
||||
@ -254,10 +271,24 @@ bool loopReadGravity() {
|
||||
if (pushExpired || runMode == RunMode::gravityMode) {
|
||||
pushMillis = millis();
|
||||
LOG_PERF_START("loop-push");
|
||||
PushTarget push;
|
||||
push.send(angle, gravitySG, corrGravitySG, tempC,
|
||||
(millis() - runtimeMillis) / 1000);
|
||||
|
||||
#if defined(ESP32)
|
||||
if (myConfig.isBLEActive()) {
|
||||
BleSender ble(myConfig.getColorBLE());
|
||||
ble.sendData(convertCtoF(tempC), gravitySG);
|
||||
Log.notice(F("MAIN: Broadcast data over bluetooth." CR));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (myWifi.isConnected()) { // no need to try if there is no wifi
|
||||
// connection.
|
||||
PushTarget push;
|
||||
push.sendAll(angle, gravitySG, corrGravitySG, tempC,
|
||||
(millis() - runtimeMillis) / 1000);
|
||||
}
|
||||
|
||||
LOG_PERF_STOP("loop-push");
|
||||
|
||||
// Send stats to influx after each push run.
|
||||
if (runMode == RunMode::configurationMode) {
|
||||
LOG_PERF_PUSH();
|
||||
@ -265,7 +296,7 @@ bool loopReadGravity() {
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Log.error(F("MAIN: No gyro value found, the device might be moving."));
|
||||
Log.error(F("MAIN: No gyro value found, the device might be moving." CR));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -329,7 +360,9 @@ void loop() {
|
||||
case RunMode::gravityMode:
|
||||
// If we didnt get a wifi connection, we enter sleep for a short time to
|
||||
// conserve battery.
|
||||
if (!myWifi.isConnected()) { // no connection to wifi
|
||||
if (!myWifi.isConnected() &&
|
||||
myConfig.isWifiPushActive()) { // no connection to wifi and we have
|
||||
// defined push targets.
|
||||
Log.notice(
|
||||
F("MAIN: No connection to wifi established, sleeping for 60s." CR));
|
||||
myWifi.stopDoubleReset();
|
||||
|
17
src/main.hpp
@ -29,21 +29,30 @@ SOFTWARE.
|
||||
#include <ArduinoLog.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#include <LittleFS.h>
|
||||
#define ESP_RESET ESP.reset
|
||||
#define PIN_SDA D3
|
||||
#define PIN_SCL D4
|
||||
#define PIN_DS D6
|
||||
#else // defined (ESP32)
|
||||
#define LittleFS SPIFFS
|
||||
#define PIN_LED 2
|
||||
// #define PIN_A0 A0
|
||||
#else // defined (ESP32)
|
||||
#if defined(ESPRESSIF32_20)
|
||||
#include <LittleFS.h>
|
||||
#else
|
||||
#include <LITTLEFS.h>
|
||||
#define LittleFS LITTLEFS
|
||||
#endif
|
||||
#include <FS.h>
|
||||
#define ESPhttpUpdate httpUpdate
|
||||
#define ESP_RESET ESP.restart
|
||||
#define ESP8266WebServer WebServer
|
||||
#include <spiffs.h>
|
||||
#define PIN_SDA 17
|
||||
#define PIN_SCL 16
|
||||
#define PIN_DS 19
|
||||
#define PIN_A0 36
|
||||
#define PIN_LED 2
|
||||
#endif
|
||||
|
||||
#define PIN_LED 2
|
||||
|
@ -35,11 +35,11 @@ SOFTWARE.
|
||||
//
|
||||
// Send the data to targets
|
||||
//
|
||||
void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
float tempC, float runTime) {
|
||||
void PushTarget::sendAll(float angle, float gravitySG, float corrGravitySG,
|
||||
float tempC, float runTime) {
|
||||
printHeap("PUSH");
|
||||
http.setReuse(false);
|
||||
httpSecure.setReuse(false);
|
||||
_http.setReuse(false);
|
||||
_httpSecure.setReuse(false);
|
||||
|
||||
TemplatingEngine engine;
|
||||
engine.initialize(angle, gravitySG, corrGravitySG, tempC, runTime);
|
||||
@ -52,16 +52,22 @@ void PushTarget::send(float angle, float gravitySG, float corrGravitySG,
|
||||
|
||||
if (myConfig.isHttpActive()) {
|
||||
LOG_PERF_START("push-http");
|
||||
sendHttp(engine, myConfig.isHttpSSL(), 0);
|
||||
sendHttpPost(engine, myConfig.isHttpSSL(), 0);
|
||||
LOG_PERF_STOP("push-http");
|
||||
}
|
||||
|
||||
if (myConfig.isHttp2Active()) {
|
||||
LOG_PERF_START("push-http2");
|
||||
sendHttp(engine, myConfig.isHttp2SSL(), 1);
|
||||
sendHttpPost(engine, myConfig.isHttp2SSL(), 1);
|
||||
LOG_PERF_STOP("push-http2");
|
||||
}
|
||||
|
||||
if (myConfig.isHttp3Active()) {
|
||||
LOG_PERF_START("push-http3");
|
||||
sendHttpGet(engine, myConfig.isHttp3SSL());
|
||||
LOG_PERF_STOP("push-http3");
|
||||
}
|
||||
|
||||
if (myConfig.isInfluxDb2Active()) {
|
||||
LOG_PERF_START("push-influxdb2");
|
||||
sendInfluxDb2(engine);
|
||||
@ -82,6 +88,8 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to influxdb2." CR));
|
||||
#endif
|
||||
_lastCode = 0;
|
||||
_lastSuccess = false;
|
||||
|
||||
String serverPath =
|
||||
String(myConfig.getInfluxDb2PushUrl()) +
|
||||
@ -89,8 +97,8 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
"&bucket=" + String(myConfig.getInfluxDb2PushBucket());
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_INFLUX);
|
||||
|
||||
http.begin(wifi, serverPath);
|
||||
http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_http.begin(_wifi, serverPath);
|
||||
_http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
@ -98,20 +106,19 @@ void PushTarget::sendInfluxDb2(TemplatingEngine& engine) {
|
||||
#endif
|
||||
|
||||
String auth = "Token " + String(myConfig.getInfluxDb2PushToken());
|
||||
http.addHeader(F("Authorization"), auth.c_str());
|
||||
int httpResponseCode = http.POST(doc);
|
||||
_http.addHeader(F("Authorization"), auth.c_str());
|
||||
_lastCode = _http.POST(doc);
|
||||
|
||||
if (httpResponseCode == 204) {
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
if (_lastCode == 204) {
|
||||
_lastSuccess = true;
|
||||
Log.notice(F("PUSH: InfluxDB2 push successful, response=%d" CR), _lastCode);
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: Influxdb push failed response=" +
|
||||
String(httpResponseCode));
|
||||
errLog.addEntry("PUSH: Influxdb push failed response=" + String(_lastCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
wifi.stop();
|
||||
_http.end();
|
||||
_wifi.stop();
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
@ -122,32 +129,35 @@ void PushTarget::sendBrewfather(TemplatingEngine& engine) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to brewfather" CR));
|
||||
#endif
|
||||
_lastCode = 0;
|
||||
_lastSuccess = false;
|
||||
|
||||
String serverPath = myConfig.getBrewfatherPushUrl();
|
||||
String doc = engine.create(TemplatingEngine::TEMPLATE_BREWFATHER);
|
||||
|
||||
http.begin(wifi, serverPath);
|
||||
http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_http.begin(_wifi, serverPath);
|
||||
_http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
|
||||
#endif
|
||||
|
||||
http.addHeader(F("Content-Type"), F("application/json"));
|
||||
int httpResponseCode = http.POST(doc);
|
||||
_http.addHeader(F("Content-Type"), F("application/json"));
|
||||
_lastCode = _http.POST(doc);
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
if (_lastCode == 200) {
|
||||
_lastSuccess = true;
|
||||
Log.notice(F("PUSH: Brewfather push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
_lastCode);
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: Brewfather push failed response=" +
|
||||
String(httpResponseCode));
|
||||
String(_lastCode));
|
||||
}
|
||||
|
||||
http.end();
|
||||
wifi.stop();
|
||||
_http.end();
|
||||
_wifi.stop();
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
@ -172,13 +182,17 @@ void PushTarget::addHttpHeader(HTTPClient& http, String header) {
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
// Send data to http target using POST
|
||||
//
|
||||
void PushTarget::sendHttp(TemplatingEngine& engine, bool isSecure, int index) {
|
||||
void PushTarget::sendHttpPost(TemplatingEngine& engine, bool isSecure,
|
||||
int index) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to http (%s)" CR),
|
||||
index ? "http2" : "http");
|
||||
#endif
|
||||
_lastCode = 0;
|
||||
_lastSuccess = false;
|
||||
|
||||
String serverPath, doc;
|
||||
|
||||
if (index == 0) {
|
||||
@ -189,8 +203,6 @@ void PushTarget::sendHttp(TemplatingEngine& engine, bool isSecure, int index) {
|
||||
doc = engine.create(TemplatingEngine::TEMPLATE_HTTP2);
|
||||
}
|
||||
|
||||
int httpResponseCode;
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
Log.verbose(F("PUSH: json %s." CR), doc.c_str());
|
||||
@ -198,74 +210,139 @@ void PushTarget::sendHttp(TemplatingEngine& engine, bool isSecure, int index) {
|
||||
|
||||
if (isSecure) {
|
||||
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
|
||||
wifiSecure.setInsecure();
|
||||
_wifiSecure.setInsecure();
|
||||
|
||||
#if defined (ESP8266)
|
||||
String host = serverPath.substring(8); // remove the prefix or the probe will fail, it needs a pure host name.
|
||||
#if defined(ESP8266)
|
||||
String host =
|
||||
serverPath.substring(8); // remove the prefix or the probe will fail,
|
||||
// it needs a pure host name.
|
||||
int idx = host.indexOf("/");
|
||||
if (idx!=-1)
|
||||
host = host.substring(0, idx);
|
||||
if (idx != -1) host = host.substring(0, idx);
|
||||
|
||||
if (wifiSecure.probeMaxFragmentLength(host, 443, 512)) {
|
||||
if (_wifiSecure.probeMaxFragmentLength(host, 443, 512)) {
|
||||
Log.notice(F("PUSH: HTTP server supports smaller SSL buffer." CR));
|
||||
wifiSecure.setBufferSizes(512, 512);
|
||||
_wifiSecure.setBufferSizes(512, 512);
|
||||
}
|
||||
#endif
|
||||
|
||||
httpSecure.begin(wifiSecure, serverPath);
|
||||
httpSecure.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_httpSecure.begin(_wifiSecure, serverPath);
|
||||
_httpSecure.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
|
||||
if (index == 0) {
|
||||
addHttpHeader(httpSecure, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(httpSecure, myConfig.getHttpHeader(1));
|
||||
addHttpHeader(_httpSecure, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(_httpSecure, myConfig.getHttpHeader(1));
|
||||
} else {
|
||||
addHttpHeader(httpSecure, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(httpSecure, myConfig.getHttp2Header(1));
|
||||
addHttpHeader(_httpSecure, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(_httpSecure, myConfig.getHttp2Header(1));
|
||||
}
|
||||
|
||||
httpResponseCode = httpSecure.POST(doc);
|
||||
_lastCode = _httpSecure.POST(doc);
|
||||
} else {
|
||||
http.begin(wifi, serverPath);
|
||||
http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_http.begin(_wifi, serverPath);
|
||||
_http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
|
||||
if (index == 0) {
|
||||
addHttpHeader(http, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(http, myConfig.getHttpHeader(1));
|
||||
addHttpHeader(_http, myConfig.getHttpHeader(0));
|
||||
addHttpHeader(_http, myConfig.getHttpHeader(1));
|
||||
} else {
|
||||
addHttpHeader(http, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(http, myConfig.getHttp2Header(1));
|
||||
addHttpHeader(_http, myConfig.getHttp2Header(0));
|
||||
addHttpHeader(_http, myConfig.getHttp2Header(1));
|
||||
}
|
||||
|
||||
httpResponseCode = http.POST(doc);
|
||||
_lastCode = _http.POST(doc);
|
||||
}
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
Log.notice(F("PUSH: HTTP push successful, response=%d" CR),
|
||||
httpResponseCode);
|
||||
if (_lastCode == 200) {
|
||||
_lastSuccess = true;
|
||||
Log.notice(F("PUSH: HTTP post successful, response=%d" CR), _lastCode);
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
"PUSH: HTTP push failed response=" + String(httpResponseCode) +
|
||||
String(index == 0 ? " (http)" : " (http2)"));
|
||||
errLog.addEntry("PUSH: HTTP post failed response=" + String(_lastCode) +
|
||||
String(index == 0 ? " (http)" : " (http2)"));
|
||||
}
|
||||
|
||||
if (isSecure) {
|
||||
httpSecure.end();
|
||||
wifiSecure.stop();
|
||||
_httpSecure.end();
|
||||
_wifiSecure.stop();
|
||||
} else {
|
||||
http.end();
|
||||
wifi.stop();
|
||||
_http.end();
|
||||
_wifi.stop();
|
||||
}
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to http target
|
||||
// Send data to http target using GET
|
||||
//
|
||||
void PushTarget::sendHttpGet(TemplatingEngine& engine, bool isSecure) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to http3" CR));
|
||||
#endif
|
||||
_lastCode = 0;
|
||||
_lastSuccess = false;
|
||||
|
||||
String serverPath;
|
||||
|
||||
serverPath = myConfig.getHttp3Url();
|
||||
serverPath += engine.create(TemplatingEngine::TEMPLATE_HTTP3);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.verbose(F("PUSH: url %s." CR), serverPath.c_str());
|
||||
#endif
|
||||
|
||||
if (isSecure) {
|
||||
Log.notice(F("PUSH: HTTP, SSL enabled without validation." CR));
|
||||
_wifiSecure.setInsecure();
|
||||
|
||||
#if defined(ESP8266)
|
||||
String host =
|
||||
serverPath.substring(8); // remove the prefix or the probe will fail,
|
||||
// it needs a pure host name.
|
||||
int idx = host.indexOf("/");
|
||||
if (idx != -1) host = host.substring(0, idx);
|
||||
|
||||
if (_wifiSecure.probeMaxFragmentLength(host, 443, 512)) {
|
||||
Log.notice(F("PUSH: HTTP server supports smaller SSL buffer." CR));
|
||||
_wifiSecure.setBufferSizes(512, 512);
|
||||
}
|
||||
#endif
|
||||
|
||||
_httpSecure.begin(_wifiSecure, serverPath);
|
||||
_httpSecure.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_lastCode = _httpSecure.GET();
|
||||
} else {
|
||||
_http.begin(_wifi, serverPath);
|
||||
_http.setTimeout(myHardwareConfig.getPushTimeout() * 1000);
|
||||
_lastCode = _http.GET();
|
||||
}
|
||||
|
||||
if (_lastCode == 200) {
|
||||
_lastSuccess = true;
|
||||
Log.notice(F("PUSH: HTTP get successful, response=%d" CR), _lastCode);
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: HTTP get failed response=" + String(_lastCode));
|
||||
}
|
||||
|
||||
if (isSecure) {
|
||||
_httpSecure.end();
|
||||
_wifiSecure.stop();
|
||||
} else {
|
||||
_http.end();
|
||||
_wifi.stop();
|
||||
}
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
||||
//
|
||||
// Send data to mqtt target
|
||||
//
|
||||
void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
||||
#if !defined(PUSH_DISABLE_LOGGING)
|
||||
Log.notice(F("PUSH: Sending values to mqtt." CR));
|
||||
#endif
|
||||
_lastCode = 0;
|
||||
_lastSuccess = false;
|
||||
|
||||
MQTTClient mqtt(512);
|
||||
String host = myConfig.getMqttUrl();
|
||||
@ -274,18 +351,18 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
||||
|
||||
if (myConfig.isMqttSSL()) {
|
||||
Log.notice(F("PUSH: MQTT, SSL enabled without validation." CR));
|
||||
wifiSecure.setInsecure();
|
||||
_wifiSecure.setInsecure();
|
||||
|
||||
#if defined (ESP8266)
|
||||
if (wifiSecure.probeMaxFragmentLength(host, port, 512)) {
|
||||
#if defined(ESP8266)
|
||||
if (_wifiSecure.probeMaxFragmentLength(host, port, 512)) {
|
||||
Log.notice(F("PUSH: MQTT server supports smaller SSL buffer." CR));
|
||||
wifiSecure.setBufferSizes(512, 512);
|
||||
_wifiSecure.setBufferSizes(512, 512);
|
||||
}
|
||||
#endif
|
||||
|
||||
mqtt.begin(host.c_str(), port, wifiSecure);
|
||||
mqtt.begin(host.c_str(), port, _wifiSecure);
|
||||
} else {
|
||||
mqtt.begin(host.c_str(), port, wifi);
|
||||
mqtt.begin(host.c_str(), port, _wifi);
|
||||
}
|
||||
|
||||
mqtt.connect(myConfig.getMDNS(), myConfig.getMqttUser(),
|
||||
@ -319,8 +396,11 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
||||
value.c_str());
|
||||
#endif
|
||||
if (mqtt.publish(topic, value)) {
|
||||
_lastSuccess = true;
|
||||
Log.notice(F("PUSH: MQTT publish successful on %s" CR), topic.c_str());
|
||||
_lastCode = 0;
|
||||
} else {
|
||||
_lastCode = mqtt.lastError();
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("PUSH: MQTT push on " + topic +
|
||||
" failed error=" + String(mqtt.lastError()));
|
||||
@ -332,9 +412,9 @@ void PushTarget::sendMqtt(TemplatingEngine& engine, bool isSecure) {
|
||||
|
||||
mqtt.disconnect();
|
||||
if (isSecure) {
|
||||
wifiSecure.stop();
|
||||
_wifiSecure.stop();
|
||||
} else {
|
||||
wifi.stop();
|
||||
_wifi.stop();
|
||||
}
|
||||
tcp_cleanup();
|
||||
}
|
||||
|
@ -35,20 +35,35 @@ SOFTWARE.
|
||||
|
||||
class PushTarget {
|
||||
private:
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPClient http;
|
||||
HTTPClient httpSecure;
|
||||
WiFiClient _wifi;
|
||||
WiFiClientSecure _wifiSecure;
|
||||
HTTPClient _http;
|
||||
HTTPClient _httpSecure;
|
||||
int _lastCode;
|
||||
bool _lastSuccess;
|
||||
|
||||
void sendBrewfather(TemplatingEngine& engine);
|
||||
void sendHttp(TemplatingEngine& engine, bool isSecure, int index);
|
||||
void sendInfluxDb2(TemplatingEngine& engine);
|
||||
void sendMqtt(TemplatingEngine& engine, bool isSecure);
|
||||
void sendHttpPost(TemplatingEngine& engine, bool isSecure, int index);
|
||||
void sendHttpGet(TemplatingEngine& engine, bool isSecure);
|
||||
void addHttpHeader(HTTPClient& http, String header);
|
||||
|
||||
public:
|
||||
void send(float angle, float gravitySG, float corrGravitySG, float tempC,
|
||||
float runTime);
|
||||
void sendAll(float angle, float gravitySG, float corrGravitySG, float tempC,
|
||||
float runTime);
|
||||
|
||||
void sendBrewfather(TemplatingEngine& engine);
|
||||
void sendHttp1(TemplatingEngine& engine, bool isSecure) {
|
||||
sendHttpPost(engine, isSecure, 0);
|
||||
}
|
||||
void sendHttp2(TemplatingEngine& engine, bool isSecure) {
|
||||
sendHttpPost(engine, isSecure, 1);
|
||||
}
|
||||
void sendHttp3(TemplatingEngine& engine, bool isSecure) {
|
||||
sendHttpGet(engine, isSecure);
|
||||
}
|
||||
void sendInfluxDb2(TemplatingEngine& engine);
|
||||
void sendMqtt(TemplatingEngine& engine, bool isSecure);
|
||||
int getLastCode() { return _lastCode; }
|
||||
bool getLastSuccess() { return _lastSuccess; }
|
||||
};
|
||||
|
||||
#endif // SRC_PUSHTARGET_HPP_
|
||||
|
@ -21,7 +21,7 @@ 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.
|
||||
*/
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
#endif
|
||||
#include <incbin.h>
|
||||
@ -31,14 +31,15 @@ SOFTWARE.
|
||||
#if defined(EMBED_HTML)
|
||||
// Using minify to reduce memory usage. Reducing RAM memory usage with about 7%
|
||||
INCBIN(IndexHtm, "data/index.min.htm");
|
||||
INCBIN(DeviceHtm, "data/device.min.htm");
|
||||
INCBIN(ConfigHtm, "data/config.min.htm");
|
||||
INCBIN(CalibrationHtm, "data/calibration.min.htm");
|
||||
INCBIN(FormatHtm, "data/format.min.htm");
|
||||
INCBIN(TestHtm, "data/test.min.htm");
|
||||
INCBIN(AboutHtm, "data/about.min.htm");
|
||||
#else
|
||||
// Minium web interface for uploading htm files
|
||||
INCBIN(UploadHtm, "data/upload.min.htm");
|
||||
#endif
|
||||
INCBIN(FirmwareHtm, "data/firmware.min.htm");
|
||||
|
||||
// EOF
|
||||
|
@ -34,12 +34,14 @@ SOFTWARE.
|
||||
#define PARAM_RUNTIME_AVERAGE "runtime-average"
|
||||
#define PARAM_PUSH_BREWFATHER "brewfather-push"
|
||||
#define PARAM_TOKEN "token"
|
||||
#define PARAM_TOKEN2 "token2"
|
||||
#define PARAM_PUSH_HTTP "http-push"
|
||||
#define PARAM_PUSH_HTTP_H1 "http-push-h1"
|
||||
#define PARAM_PUSH_HTTP_H2 "http-push-h2"
|
||||
#define PARAM_PUSH_HTTP2 "http-push2"
|
||||
#define PARAM_PUSH_HTTP2_H1 "http-push2-h1"
|
||||
#define PARAM_PUSH_HTTP2_H2 "http-push2-h2"
|
||||
#define PARAM_PUSH_HTTP3 "http-push3"
|
||||
#define PARAM_PUSH_INFLUXDB2 "influxdb2-push"
|
||||
#define PARAM_PUSH_INFLUXDB2_ORG "influxdb2-org"
|
||||
#define PARAM_PUSH_INFLUXDB2_BUCKET "influxdb2-bucket"
|
||||
@ -61,8 +63,8 @@ SOFTWARE.
|
||||
#define PARAM_FILES "files"
|
||||
#define PARAM_FILE_NAME "file-name"
|
||||
#define PARAM_FILE_SIZE "file-size"
|
||||
#define PARAM_APP_NAME "app-name"
|
||||
#define PARAM_APP_VER "app-ver"
|
||||
#define PARAM_APP_BUILD "app-build"
|
||||
#define PARAM_ANGLE "angle"
|
||||
#define PARAM_GRAVITY "gravity"
|
||||
#define PARAM_TEMP_C "temp-c"
|
||||
@ -71,6 +73,8 @@ SOFTWARE.
|
||||
#define PARAM_SLEEP_MODE "sleep-mode"
|
||||
#define PARAM_RSSI "rssi"
|
||||
#define PARAM_ERROR "error"
|
||||
#define PARAM_PLATFORM "platform"
|
||||
#define PARAM_BLE "ble"
|
||||
#define PARAM_HW_GYRO_READ_COUNT "gyro-read-count"
|
||||
#define PARAM_HW_GYRO_READ_DELAY "gyro-read-delay"
|
||||
#define PARAM_HW_GYRO_MOVING_THREASHOLD "gyro-moving-threashold"
|
||||
@ -80,8 +84,13 @@ SOFTWARE.
|
||||
#define PARAM_HW_PUSH_TIMEOUT "push-timeout"
|
||||
#define PARAM_FORMAT_HTTP1 "http-1"
|
||||
#define PARAM_FORMAT_HTTP2 "http-2"
|
||||
#define PARAM_FORMAT_HTTP3 "http-3"
|
||||
#define PARAM_FORMAT_BREWFATHER "brewfather"
|
||||
#define PARAM_FORMAT_INFLUXDB "influxdb"
|
||||
#define PARAM_FORMAT_MQTT "mqtt"
|
||||
#define PARAM_PUSH_FORMAT "format"
|
||||
#define PARAM_PUSH_SUCCESS "success"
|
||||
#define PARAM_PUSH_CODE "code"
|
||||
#define PARAM_PUSH_ENABLED "enabled"
|
||||
|
||||
#endif // SRC_RESOURCES_HPP_
|
||||
|
@ -21,35 +21,51 @@ 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 <templating.hpp>
|
||||
#include <config.hpp>
|
||||
#include <templating.hpp>
|
||||
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#else // defined (ESP32)
|
||||
#else // defined (ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
// Use iSpindle format for compatibility
|
||||
const char iSpindleFormat[] PROGMEM =
|
||||
"{"
|
||||
// Use iSpindle format for compatibility, HTTP POST
|
||||
const char iSpindleFormat[] PROGMEM =
|
||||
"{"
|
||||
"\"name\" : \"${mdns}\", "
|
||||
"\"ID\": \"${id}\", "
|
||||
"\"token\" : \"${token}\", "
|
||||
"\"interval\": ${sleep-interval}, "
|
||||
"\"temperature\": ${temp}, "
|
||||
"\"temp-units\": \"${temp-unit}\", "
|
||||
"\"gravity\": ${gravity}, "
|
||||
"\"angle\": ${angle}, "
|
||||
"\"battery\": ${battery}, "
|
||||
"\"rssi\": ${rssi}, "
|
||||
"\"corr-gravity\": ${corr-gravity}, "
|
||||
"\"gravity-unit\": \"${gravity-unit}\", "
|
||||
"\"run-time\": ${run-time} "
|
||||
"}";
|
||||
"\"ID\": \"${id}\", "
|
||||
"\"token\" : \"${token}\", "
|
||||
"\"interval\": ${sleep-interval}, "
|
||||
"\"temperature\": ${temp}, "
|
||||
"\"temp_units\": \"${temp-unit}\", "
|
||||
"\"gravity\": ${gravity}, "
|
||||
"\"angle\": ${angle}, "
|
||||
"\"battery\": ${battery}, "
|
||||
"\"rssi\": ${rssi}, "
|
||||
"\"corr-gravity\": ${corr-gravity}, "
|
||||
"\"gravity-unit\": \"${gravity-unit}\", "
|
||||
"\"run-time\": ${run-time} "
|
||||
"}";
|
||||
|
||||
const char brewfatherFormat[] PROGMEM =
|
||||
"{"
|
||||
// Format for an HTTP GET
|
||||
const char iHttpGetFormat[] PROGMEM =
|
||||
"?name=${mdns}"
|
||||
"&id=${id}"
|
||||
"&token=${token2}"
|
||||
"&interval=${sleep-interval}"
|
||||
"&temperature=${temp}"
|
||||
"&temp-units=${temp-unit}"
|
||||
"&gravity=${gravity}"
|
||||
"&angle=${angle}"
|
||||
"&battery=${battery}"
|
||||
"&rssi=${rssi}"
|
||||
"&corr-gravity=${corr-gravity}"
|
||||
"&gravity-unit=${gravity-unit}"
|
||||
"&run-time=${run-time}";
|
||||
|
||||
const char brewfatherFormat[] PROGMEM =
|
||||
"{"
|
||||
"\"name\": \"${mdns}\","
|
||||
"\"temp\": ${temp}, "
|
||||
"\"aux_temp\": 0, "
|
||||
@ -64,31 +80,35 @@ const char brewfatherFormat[] PROGMEM =
|
||||
"\"comment\": \"\", "
|
||||
"\"beer\": \"\", "
|
||||
"\"battery\": ${battery}"
|
||||
"}";
|
||||
"}";
|
||||
|
||||
const char influxDbFormat[] PROGMEM =
|
||||
"measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-format=${gravity-unit} "
|
||||
"gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${temp},battery=${battery},"
|
||||
"rssi=${rssi}\n";
|
||||
const char influxDbFormat[] PROGMEM =
|
||||
"measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-"
|
||||
"format=${gravity-unit} "
|
||||
"gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${"
|
||||
"temp},battery=${battery},"
|
||||
"rssi=${rssi}\n";
|
||||
|
||||
const char mqttFormat[] PROGMEM =
|
||||
"ispindel/${mdns}/tilt:${angle}|"
|
||||
"ispindel/${mdns}/temperature:${temp}|"
|
||||
"ispindel/${mdns}/temp_units:${temp-unit}|"
|
||||
"ispindel/${mdns}/battery:${battery}|"
|
||||
"ispindel/${mdns}/gravity:${gravity}|"
|
||||
"ispindel/${mdns}/interval:${sleep-interval}|"
|
||||
"ispindel/${mdns}/RSSI:${rssi}|";
|
||||
const char mqttFormat[] PROGMEM =
|
||||
"ispindel/${mdns}/tilt:${angle}|"
|
||||
"ispindel/${mdns}/temperature:${temp}|"
|
||||
"ispindel/${mdns}/temp_units:${temp-unit}|"
|
||||
"ispindel/${mdns}/battery:${battery}|"
|
||||
"ispindel/${mdns}/gravity:${gravity}|"
|
||||
"ispindel/${mdns}/interval:${sleep-interval}|"
|
||||
"ispindel/${mdns}/RSSI:${rssi}|";
|
||||
|
||||
//
|
||||
// Initialize the variables
|
||||
//
|
||||
void TemplatingEngine::initialize(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime) {
|
||||
|
||||
void TemplatingEngine::initialize(float angle, float gravitySG,
|
||||
float corrGravitySG, float tempC,
|
||||
float runTime) {
|
||||
// Names
|
||||
setVal(TPL_MDNS, myConfig.getMDNS());
|
||||
setVal(TPL_ID, myConfig.getID());
|
||||
setVal(TPL_TOKEN, myConfig.getToken());
|
||||
setVal(TPL_TOKEN2, myConfig.getToken2());
|
||||
|
||||
// Temperature
|
||||
if (myConfig.isTempC()) {
|
||||
@ -117,8 +137,7 @@ void TemplatingEngine::initialize(float angle, float gravitySG, float corrGravit
|
||||
if (myConfig.isGravitySG()) {
|
||||
setVal(TPL_GRAVITY, gravitySG, 4);
|
||||
setVal(TPL_GRAVITY_CORR, corrGravitySG, 4);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setVal(TPL_GRAVITY, convertToPlato(gravitySG), 1);
|
||||
setVal(TPL_GRAVITY_CORR, convertToPlato(corrGravitySG), 1);
|
||||
}
|
||||
@ -146,30 +165,34 @@ const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
||||
case TEMPLATE_HTTP1:
|
||||
baseTemplate = String(iSpindleFormat);
|
||||
fname = TPL_FNAME_HTTP1;
|
||||
break;
|
||||
break;
|
||||
case TEMPLATE_HTTP2:
|
||||
baseTemplate = String(iSpindleFormat);
|
||||
fname = TPL_FNAME_HTTP2;
|
||||
break;
|
||||
break;
|
||||
case TEMPLATE_HTTP3:
|
||||
baseTemplate = String(iHttpGetFormat);
|
||||
fname = TPL_FNAME_HTTP3;
|
||||
break;
|
||||
case TEMPLATE_BREWFATHER:
|
||||
baseTemplate = String(brewfatherFormat);
|
||||
//fname = TPL_FNAME_BREWFATHER;
|
||||
break;
|
||||
// fname = TPL_FNAME_BREWFATHER;
|
||||
break;
|
||||
case TEMPLATE_INFLUX:
|
||||
baseTemplate = String(influxDbFormat);
|
||||
fname = TPL_FNAME_INFLUXDB;
|
||||
break;
|
||||
break;
|
||||
case TEMPLATE_MQTT:
|
||||
baseTemplate = String(mqttFormat);
|
||||
fname = TPL_FNAME_MQTT;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Add code to load templates from disk if they exist.
|
||||
File file = LittleFS.open(fname, "r");
|
||||
if (file) {
|
||||
char buf[file.size()+1];
|
||||
memset(&buf[0], 0, file.size()+1);
|
||||
char buf[file.size() + 1];
|
||||
memset(&buf[0], 0, file.size() + 1);
|
||||
file.readBytes(&buf[0], file.size());
|
||||
baseTemplate = String(&buf[0]);
|
||||
file.close();
|
||||
@ -177,14 +200,14 @@ const String& TemplatingEngine::create(TemplatingEngine::Templates idx) {
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
//Log.verbose(F("TPL : Base '%s'." CR), baseTemplate.c_str());
|
||||
// Log.verbose(F("TPL : Base '%s'." CR), baseTemplate.c_str());
|
||||
#endif
|
||||
|
||||
// Insert data into template.
|
||||
transform(baseTemplate);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
//Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str());
|
||||
// Log.verbose(F("TPL : Transformed '%s'." CR), baseTemplate.c_str());
|
||||
#endif
|
||||
|
||||
return baseTemplate;
|
||||
|
@ -35,6 +35,7 @@ SOFTWARE.
|
||||
#define TPL_MDNS "${mdns}"
|
||||
#define TPL_ID "${id}"
|
||||
#define TPL_TOKEN "${token}"
|
||||
#define TPL_TOKEN2 "${token2}"
|
||||
#define TPL_SLEEP_INTERVAL "${sleep-interval}"
|
||||
#define TPL_TEMP "${temp}"
|
||||
#define TPL_TEMP_C "${temp-c}"
|
||||
@ -55,11 +56,13 @@ SOFTWARE.
|
||||
|
||||
#define TPL_FNAME_HTTP1 "/http-1.tpl"
|
||||
#define TPL_FNAME_HTTP2 "/http-2.tpl"
|
||||
#define TPL_FNAME_HTTP3 "/http-3.tpl"
|
||||
// #define TPL_FNAME_BREWFATHER "/brewfather.tpl"
|
||||
#define TPL_FNAME_INFLUXDB "/influxdb.tpl"
|
||||
#define TPL_FNAME_MQTT "/mqtt.tpl"
|
||||
|
||||
extern const char iSpindleFormat[] PROGMEM;
|
||||
extern const char iHttpGetFormat[] PROGMEM;
|
||||
extern const char brewfatherFormat[] PROGMEM;
|
||||
extern const char influxDbFormat[] PROGMEM;
|
||||
extern const char mqttFormat[] PROGMEM;
|
||||
@ -72,7 +75,7 @@ class TemplatingEngine {
|
||||
String val;
|
||||
};
|
||||
|
||||
KeyVal items[20] = {{TPL_MDNS, ""}, {TPL_ID, ""},
|
||||
KeyVal items[21] = {{TPL_MDNS, ""}, {TPL_ID, ""},
|
||||
{TPL_SLEEP_INTERVAL, ""}, {TPL_TEMP, ""},
|
||||
{TPL_TEMP_C, ""}, {TPL_TEMP_F, ""},
|
||||
{TPL_TEMP_UNITS, ""}, {TPL_BATTERY, ""},
|
||||
@ -81,7 +84,8 @@ class TemplatingEngine {
|
||||
{TPL_GRAVITY, ""}, {TPL_GRAVITY_G, ""},
|
||||
{TPL_GRAVITY_P, ""}, {TPL_GRAVITY_CORR, ""},
|
||||
{TPL_GRAVITY_CORR_G, ""}, {TPL_GRAVITY_CORR_P, ""},
|
||||
{TPL_GRAVITY_UNIT, ""}, {TPL_TOKEN, ""}};
|
||||
{TPL_GRAVITY_UNIT, ""}, {TPL_TOKEN, ""},
|
||||
{TPL_TOKEN2, ""}};
|
||||
|
||||
char buffer[20];
|
||||
String baseTemplate;
|
||||
@ -128,9 +132,10 @@ class TemplatingEngine {
|
||||
enum Templates {
|
||||
TEMPLATE_HTTP1 = 0,
|
||||
TEMPLATE_HTTP2 = 1,
|
||||
TEMPLATE_BREWFATHER = 2,
|
||||
TEMPLATE_INFLUX = 3,
|
||||
TEMPLATE_MQTT = 4
|
||||
TEMPLATE_HTTP3 = 2,
|
||||
TEMPLATE_BREWFATHER = 3,
|
||||
TEMPLATE_INFLUX = 4,
|
||||
TEMPLATE_MQTT = 5
|
||||
};
|
||||
|
||||
void initialize(float angle, float gravitySG, float corrGravitySG,
|
||||
|
@ -26,6 +26,7 @@ SOFTWARE.
|
||||
#include <gyro.hpp>
|
||||
#include <helper.hpp>
|
||||
#include <main.hpp>
|
||||
#include <pushtarget.hpp>
|
||||
#include <resources.hpp>
|
||||
#include <templating.hpp>
|
||||
#include <tempsensor.hpp>
|
||||
@ -36,36 +37,6 @@ WebServerHandler myWebServerHandler; // My wrapper class fr webserver functions
|
||||
extern bool sleepModeActive;
|
||||
extern bool sleepModeAlwaysSkip;
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
void WebServerHandler::webHandleDevice() {
|
||||
LOG_PERF_START("webserver-api-device");
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.verbose(F("WEB : webServer callback for /api/device(get)." CR));
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(100);
|
||||
doc[PARAM_ID] = myConfig.getID();
|
||||
doc[PARAM_APP_NAME] = CFG_APPNAME;
|
||||
doc[PARAM_APP_VER] = CFG_APPVER;
|
||||
doc[PARAM_MDNS] = myConfig.getMDNS();
|
||||
|
||||
FloatHistoryLog runLog(RUNTIME_FILENAME);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
|
||||
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
|
||||
|
||||
#if LOG_LEVEL == 6
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
String out;
|
||||
out.reserve(100);
|
||||
serializeJson(doc, out);
|
||||
_server->send(200, "application/json", out.c_str());
|
||||
LOG_PERF_STOP("webserver-api-device");
|
||||
}
|
||||
|
||||
//
|
||||
// Callback from webServer when / has been accessed.
|
||||
//
|
||||
@ -80,15 +51,22 @@ void WebServerHandler::webHandleConfig() {
|
||||
|
||||
double angle = 0;
|
||||
|
||||
if (myGyro.hasValue())
|
||||
angle = myGyro.getAngle();
|
||||
|
||||
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
||||
|
||||
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
double gravity = calculateGravity(angle, tempC);
|
||||
|
||||
doc[PARAM_ANGLE] = reduceFloatPrecision(angle);
|
||||
doc[PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
|
||||
|
||||
// Format the adjustment so we get rid of rounding errors
|
||||
if (myConfig.isTempF())
|
||||
// We want the delta value (32F = 0C).
|
||||
doc[PARAM_TEMP_ADJ] =
|
||||
reduceFloatPrecision(convertCtoF(myConfig.getTempSensorAdjC())-32, 1);
|
||||
else
|
||||
doc[PARAM_TEMP_ADJ] = reduceFloatPrecision(myConfig.getTempSensorAdjC(), 1);
|
||||
|
||||
if (myConfig.isGravityTempAdj()) {
|
||||
gravity =
|
||||
gravityTemperatureCorrectionC(gravity, tempC, myConfig.getTempFormat());
|
||||
@ -106,6 +84,12 @@ void WebServerHandler::webHandleConfig() {
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
|
||||
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
|
||||
|
||||
#if defined(ESP8266)
|
||||
doc[PARAM_PLATFORM] = "esp8266";
|
||||
#else
|
||||
doc[PARAM_PLATFORM] = "esp32";
|
||||
#endif
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
@ -123,15 +107,15 @@ void WebServerHandler::webHandleConfig() {
|
||||
//
|
||||
void WebServerHandler::webHandleUpload() {
|
||||
LOG_PERF_START("webserver-api-upload");
|
||||
Log.notice(F("WEB : webServer callback for /api/upload." CR));
|
||||
Log.notice(F("WEB : webServer callback for /api/upload(get)." CR));
|
||||
DynamicJsonDocument doc(300);
|
||||
|
||||
doc["index"] = checkHtmlFile(WebServerHandler::HTML_INDEX);
|
||||
doc["device"] = checkHtmlFile(WebServerHandler::HTML_DEVICE);
|
||||
doc["config"] = checkHtmlFile(WebServerHandler::HTML_CONFIG);
|
||||
doc["calibration"] = checkHtmlFile(WebServerHandler::HTML_CALIBRATION);
|
||||
doc["format"] = checkHtmlFile(WebServerHandler::HTML_FORMAT);
|
||||
doc["about"] = checkHtmlFile(WebServerHandler::HTML_ABOUT);
|
||||
doc["test"] = checkHtmlFile(WebServerHandler::HTML_TEST);
|
||||
|
||||
#if defined(ESP8266)
|
||||
JsonArray files = doc.createNestedArray(PARAM_FILES);
|
||||
@ -145,8 +129,26 @@ void WebServerHandler::webHandleUpload() {
|
||||
obj[PARAM_FILE_NAME] = dir.fileName();
|
||||
obj[PARAM_FILE_SIZE] = dir.fileSize();
|
||||
}
|
||||
#else // defined(ESP32)
|
||||
#warning "TODO: Implement file listing for ESP32"
|
||||
#else // defined(ESP32)
|
||||
JsonArray files = doc.createNestedArray(PARAM_FILES);
|
||||
|
||||
File dir = LittleFS.open("/");
|
||||
|
||||
while (true) {
|
||||
File entry = dir.openNextFile();
|
||||
if (!entry) {
|
||||
// no more files
|
||||
break;
|
||||
}
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
JsonObject obj = files.createNestedObject();
|
||||
obj[PARAM_FILE_NAME] = entry.name();
|
||||
obj[PARAM_FILE_SIZE] = entry.size();
|
||||
}
|
||||
entry.close();
|
||||
}
|
||||
dir.close();
|
||||
#endif
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
@ -166,45 +168,106 @@ void WebServerHandler::webHandleUpload() {
|
||||
//
|
||||
void WebServerHandler::webHandleUploadFile() {
|
||||
LOG_PERF_START("webserver-api-upload-file");
|
||||
Log.notice(F("WEB : webServer callback for /api/upload/file." CR));
|
||||
Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR));
|
||||
HTTPUpload& upload = _server->upload();
|
||||
String f = upload.filename;
|
||||
bool validFilename = false;
|
||||
bool firmware = false;
|
||||
|
||||
if (f.equalsIgnoreCase("index.min.htm") ||
|
||||
f.equalsIgnoreCase("device.min.htm") ||
|
||||
f.equalsIgnoreCase("calibration.min.htm") ||
|
||||
f.equalsIgnoreCase("config.min.htm") ||
|
||||
f.equalsIgnoreCase("format.min.htm") ||
|
||||
f.equalsIgnoreCase("test.min.htm") ||
|
||||
f.equalsIgnoreCase("about.min.htm")) {
|
||||
validFilename = true;
|
||||
}
|
||||
|
||||
if (f.endsWith(".bin")) {
|
||||
validFilename = true;
|
||||
firmware = true;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.verbose(F("WEB : webServer callback for /api/upload, receiving file %s, "
|
||||
"valid=%s." CR),
|
||||
f.c_str(), validFilename ? "yes" : "no");
|
||||
Log.verbose(
|
||||
F("WEB : webServer callback for /api/upload, receiving file %s, %d(%d) "
|
||||
"valid=%s, firmware=%s." CR),
|
||||
f.c_str(), upload.currentSize, upload.totalSize,
|
||||
validFilename ? "yes" : "no", firmware ? "yes" : "no");
|
||||
#endif
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
Log.notice(F("WEB : Start upload." CR));
|
||||
if (validFilename) _uploadFile = LittleFS.open(f, "w");
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
Log.notice(F("WEB : Writing upload." CR));
|
||||
if (_uploadFile)
|
||||
_uploadFile.write(
|
||||
upload.buf,
|
||||
upload.currentSize); // Write the received bytes to the file
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
Log.notice(F("WEB : Finish upload." CR));
|
||||
if (_uploadFile) {
|
||||
_uploadFile.close();
|
||||
Log.notice(F("WEB : File uploaded %d bytes." CR), upload.totalSize);
|
||||
if (firmware) {
|
||||
// Handle firmware update, hardcode since function return wrong value.
|
||||
// (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
uint32_t maxSketchSpace = MAX_SKETCH_SPACE;
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
_uploadReturn = 200;
|
||||
Log.notice(F("WEB : Start firmware upload, max sketch size %d kb." CR),
|
||||
maxSketchSpace / 1024);
|
||||
|
||||
if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry(
|
||||
F("WEB : Not enough space to store for this firmware."));
|
||||
_uploadReturn = 500;
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
Log.notice(F("WEB : Writing firmware upload %d (%d)." CR),
|
||||
upload.totalSize, maxSketchSpace);
|
||||
|
||||
if (upload.totalSize > maxSketchSpace) {
|
||||
Log.error(F("WEB : Firmware file is to large." CR));
|
||||
_uploadReturn = 500;
|
||||
} else if (Update.write(upload.buf, upload.currentSize) !=
|
||||
upload.currentSize) {
|
||||
Log.warning(F("WEB : Firmware write was unsuccessful." CR));
|
||||
_uploadReturn = 500;
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
Log.notice(F("WEB : Finish firmware upload." CR));
|
||||
if (Update.end(true)) {
|
||||
_server->send(200);
|
||||
delay(500);
|
||||
ESP_RESET();
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("WEB : Failed to finish firmware flashing error=" +
|
||||
String(Update.getError()));
|
||||
_uploadReturn = 500;
|
||||
}
|
||||
} else {
|
||||
Update.end();
|
||||
Log.notice(F("WEB : Firmware flashing aborted." CR));
|
||||
_uploadReturn = 500;
|
||||
}
|
||||
_server->sendHeader("Location", "/");
|
||||
_server->send(303);
|
||||
|
||||
delay(0);
|
||||
|
||||
} else {
|
||||
_server->send(500, "text/plain", "Couldn't create file.");
|
||||
// Handle HTML file upload
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
_uploadReturn = 200;
|
||||
Log.notice(F("WEB : Start html upload." CR));
|
||||
|
||||
if (validFilename) _uploadFile = LittleFS.open(f, "w");
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
Log.notice(F("WEB : Writing html upload." CR));
|
||||
if (_uploadFile) _uploadFile.write(upload.buf, upload.currentSize);
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
Log.notice(F("WEB : Finish html upload." CR));
|
||||
if (_uploadFile) {
|
||||
_uploadFile.close();
|
||||
Log.notice(F("WEB : Html file uploaded %d bytes." CR),
|
||||
upload.totalSize);
|
||||
}
|
||||
_server->sendHeader("Location", "/");
|
||||
_server->send(303);
|
||||
} else {
|
||||
_server->send(500, "text/plain", "Couldn't upload html file.");
|
||||
}
|
||||
}
|
||||
|
||||
LOG_PERF_STOP("webserver-api-upload-file");
|
||||
}
|
||||
|
||||
@ -261,12 +324,11 @@ void WebServerHandler::webHandleStatus() {
|
||||
LOG_PERF_START("webserver-api-status");
|
||||
Log.notice(F("WEB : webServer callback for /api/status(get)." CR));
|
||||
|
||||
DynamicJsonDocument doc(256);
|
||||
DynamicJsonDocument doc(300);
|
||||
|
||||
double angle = 0;
|
||||
|
||||
if (myGyro.hasValue())
|
||||
angle = myGyro.getAngle();
|
||||
if (myGyro.hasValue()) angle = myGyro.getAngle();
|
||||
|
||||
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
double gravity = calculateGravity(angle, tempC);
|
||||
@ -274,7 +336,7 @@ void WebServerHandler::webHandleStatus() {
|
||||
doc[PARAM_ID] = myConfig.getID();
|
||||
doc[PARAM_ANGLE] = reduceFloatPrecision(angle);
|
||||
if (myConfig.isGravityTempAdj()) {
|
||||
gravity = gravityTemperatureCorrectionC(gravity, tempC); //
|
||||
gravity = gravityTemperatureCorrectionC(gravity, tempC);
|
||||
}
|
||||
if (myConfig.isGravityPlato()) {
|
||||
doc[PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
|
||||
@ -288,6 +350,23 @@ void WebServerHandler::webHandleStatus() {
|
||||
doc[PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
|
||||
doc[PARAM_SLEEP_MODE] = sleepModeAlwaysSkip;
|
||||
doc[PARAM_RSSI] = WiFi.RSSI();
|
||||
doc[PARAM_SLEEP_INTERVAL] = myConfig.getSleepInterval();
|
||||
doc[PARAM_TOKEN] = myConfig.getToken();
|
||||
doc[PARAM_TOKEN2] = myConfig.getToken2();
|
||||
|
||||
doc[PARAM_APP_VER] = CFG_APPVER;
|
||||
doc[PARAM_APP_BUILD] = CFG_GITREV;
|
||||
doc[PARAM_MDNS] = myConfig.getMDNS();
|
||||
|
||||
FloatHistoryLog runLog(RUNTIME_FILENAME);
|
||||
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
|
||||
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
|
||||
|
||||
#if defined(ESP8266)
|
||||
doc[PARAM_PLATFORM] = "esp8266";
|
||||
#else
|
||||
doc[PARAM_PLATFORM] = "esp32";
|
||||
#endif
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
@ -311,6 +390,9 @@ void WebServerHandler::webHandleClearWIFI() {
|
||||
if (!id.compareTo(myConfig.getID())) {
|
||||
_server->send(200, "text/plain",
|
||||
"Clearing WIFI credentials and doing reset...");
|
||||
myConfig.setWifiPass("");
|
||||
myConfig.setWifiSSID("");
|
||||
myConfig.saveFile();
|
||||
delay(1000);
|
||||
WiFi.disconnect(); // Clear credentials
|
||||
ESP_RESET();
|
||||
@ -374,7 +456,7 @@ void WebServerHandler::webHandleConfigDevice() {
|
||||
if (_server->hasArg(PARAM_SLEEP_INTERVAL))
|
||||
myConfig.setSleepInterval(_server->arg(PARAM_SLEEP_INTERVAL).c_str());
|
||||
myConfig.saveFile();
|
||||
_server->sendHeader("Location", "/config.htm#collapseOne", true);
|
||||
_server->sendHeader("Location", "/config.htm#collapseDevice", true);
|
||||
_server->send(302, "text/plain", "Device config updated");
|
||||
LOG_PERF_STOP("webserver-api-config-device");
|
||||
}
|
||||
@ -400,6 +482,8 @@ void WebServerHandler::webHandleConfigPush() {
|
||||
|
||||
if (_server->hasArg(PARAM_TOKEN))
|
||||
myConfig.setToken(_server->arg(PARAM_TOKEN).c_str());
|
||||
if (_server->hasArg(PARAM_TOKEN2))
|
||||
myConfig.setToken2(_server->arg(PARAM_TOKEN2).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_HTTP))
|
||||
myConfig.setHttpUrl(_server->arg(PARAM_PUSH_HTTP).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_HTTP_H1))
|
||||
@ -412,6 +496,8 @@ void WebServerHandler::webHandleConfigPush() {
|
||||
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H1).c_str(), 0);
|
||||
if (_server->hasArg(PARAM_PUSH_HTTP2_H2))
|
||||
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H2).c_str(), 1);
|
||||
if (_server->hasArg(PARAM_PUSH_HTTP3))
|
||||
myConfig.setHttp3Url(_server->arg(PARAM_PUSH_HTTP3).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_BREWFATHER))
|
||||
myConfig.setBrewfatherPushUrl(_server->arg(PARAM_PUSH_BREWFATHER).c_str());
|
||||
if (_server->hasArg(PARAM_PUSH_INFLUXDB2))
|
||||
@ -434,7 +520,9 @@ void WebServerHandler::webHandleConfigPush() {
|
||||
if (_server->hasArg(PARAM_PUSH_MQTT_PASS))
|
||||
myConfig.setMqttPass(_server->arg(PARAM_PUSH_MQTT_PASS).c_str());
|
||||
myConfig.saveFile();
|
||||
_server->sendHeader("Location", "/config.htm#collapseTwo", true);
|
||||
String section("/config.htm#");
|
||||
section += _server->arg("section");
|
||||
_server->sendHeader("Location", section.c_str(), true);
|
||||
_server->send(302, "text/plain", "Push config updated");
|
||||
LOG_PERF_STOP("webserver-api-config-push");
|
||||
}
|
||||
@ -487,7 +575,7 @@ void WebServerHandler::webHandleConfigGravity() {
|
||||
_server->arg(PARAM_GRAVITY_TEMP_ADJ).equalsIgnoreCase("on") ? true
|
||||
: false);
|
||||
myConfig.saveFile();
|
||||
_server->sendHeader("Location", "/config.htm#collapseThree", true);
|
||||
_server->sendHeader("Location", "/config.htm#collapseGravity", true);
|
||||
_server->send(302, "text/plain", "Gravity config updated");
|
||||
LOG_PERF_STOP("webserver-api-config-gravity");
|
||||
}
|
||||
@ -518,16 +606,19 @@ void WebServerHandler::webHandleConfigHardware() {
|
||||
if (myConfig.isTempC()) {
|
||||
myConfig.setTempSensorAdjC(_server->arg(PARAM_TEMP_ADJ));
|
||||
} else {
|
||||
myConfig.setTempSensorAdjF(_server->arg(PARAM_TEMP_ADJ));
|
||||
// Data is delta so we add 32 in order to conver to C.
|
||||
myConfig.setTempSensorAdjF(_server->arg(PARAM_TEMP_ADJ), 32);
|
||||
}
|
||||
}
|
||||
if (_server->hasArg(PARAM_BLE))
|
||||
myConfig.setColorBLE(_server->arg(PARAM_BLE).c_str());
|
||||
if (_server->hasArg(PARAM_OTA))
|
||||
myConfig.setOtaURL(_server->arg(PARAM_OTA).c_str());
|
||||
if (_server->hasArg(PARAM_GYRO_TEMP))
|
||||
myConfig.setGyroTemp(
|
||||
_server->arg(PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
|
||||
myConfig.saveFile();
|
||||
_server->sendHeader("Location", "/config.htm#collapseFour", true);
|
||||
_server->sendHeader("Location", "/config.htm#collapseHardware", true);
|
||||
_server->send(302, "text/plain", "Hardware config updated");
|
||||
LOG_PERF_STOP("webserver-api-config-hardware");
|
||||
}
|
||||
@ -693,6 +784,8 @@ void WebServerHandler::webHandleConfigFormatWrite() {
|
||||
success = writeFile(TPL_FNAME_HTTP1, _server->arg(PARAM_FORMAT_HTTP1));
|
||||
} else if (_server->hasArg(PARAM_FORMAT_HTTP2)) {
|
||||
success = writeFile(TPL_FNAME_HTTP2, _server->arg(PARAM_FORMAT_HTTP2));
|
||||
} else if (_server->hasArg(PARAM_FORMAT_HTTP3)) {
|
||||
success = writeFile(TPL_FNAME_HTTP3, _server->arg(PARAM_FORMAT_HTTP3));
|
||||
} else if (_server->hasArg(PARAM_FORMAT_INFLUXDB)) {
|
||||
success =
|
||||
writeFile(TPL_FNAME_INFLUXDB, _server->arg(PARAM_FORMAT_INFLUXDB));
|
||||
@ -716,6 +809,78 @@ void WebServerHandler::webHandleConfigFormatWrite() {
|
||||
LOG_PERF_STOP("webserver-api-config-format-write");
|
||||
}
|
||||
|
||||
//
|
||||
// Get format with real data
|
||||
//
|
||||
void WebServerHandler::webHandleTestPush() {
|
||||
LOG_PERF_START("webserver-api-test-push");
|
||||
String id = _server->arg(PARAM_ID);
|
||||
Log.notice(F("WEB : webServer callback for /api/test/push." CR));
|
||||
|
||||
if (!id.equalsIgnoreCase(myConfig.getID())) {
|
||||
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
|
||||
myConfig.getID());
|
||||
_server->send(400, "text/plain", "Invalid ID.");
|
||||
LOG_PERF_STOP("webserver-api-test-push");
|
||||
return;
|
||||
}
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
|
||||
#endif
|
||||
|
||||
float angle = myGyro.getAngle();
|
||||
float tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
|
||||
float gravitySG = calculateGravity(angle, tempC);
|
||||
float corrGravitySG = gravityTemperatureCorrectionC(gravitySG, tempC);
|
||||
|
||||
TemplatingEngine engine;
|
||||
engine.initialize(angle, gravitySG, corrGravitySG, tempC, 2.1);
|
||||
|
||||
const String& type = _server->arg(PARAM_PUSH_FORMAT);
|
||||
PushTarget push;
|
||||
bool enabled = false;
|
||||
|
||||
if (!type.compareTo(PARAM_FORMAT_BREWFATHER) &&
|
||||
myConfig.isBrewfatherActive()) {
|
||||
push.sendBrewfather(engine);
|
||||
enabled = true;
|
||||
} else if (!type.compareTo(PARAM_FORMAT_HTTP1) && myConfig.isHttpActive()) {
|
||||
push.sendHttp1(engine, myConfig.isHttpSSL());
|
||||
enabled = true;
|
||||
} else if (!type.compareTo(PARAM_FORMAT_HTTP2) && myConfig.isHttp2Active()) {
|
||||
push.sendHttp2(engine, myConfig.isHttp2SSL());
|
||||
enabled = true;
|
||||
} else if (!type.compareTo(PARAM_FORMAT_HTTP3) && myConfig.isHttp3Active()) {
|
||||
push.sendHttp3(engine, myConfig.isHttp3SSL());
|
||||
enabled = true;
|
||||
} else if (!type.compareTo(PARAM_FORMAT_INFLUXDB) &&
|
||||
myConfig.isInfluxDb2Active()) {
|
||||
push.sendInfluxDb2(engine);
|
||||
enabled = true;
|
||||
} else if (!type.compareTo(PARAM_FORMAT_MQTT) && myConfig.isMqttActive()) {
|
||||
push.sendMqtt(engine, myConfig.isMqttSSL());
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(100);
|
||||
doc[PARAM_PUSH_ENABLED] = enabled;
|
||||
doc[PARAM_PUSH_SUCCESS] = push.getLastSuccess();
|
||||
doc[PARAM_PUSH_CODE] = push.getLastCode();
|
||||
|
||||
String out;
|
||||
out.reserve(100);
|
||||
serializeJson(doc, out);
|
||||
|
||||
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(CR);
|
||||
#endif
|
||||
|
||||
_server->send(200, "application/json", out.c_str());
|
||||
LOG_PERF_STOP("webserver-api-test-push");
|
||||
}
|
||||
|
||||
//
|
||||
// Write file to disk, if there is no data then delete the current file (if it
|
||||
// exists) = reset to default.
|
||||
@ -784,6 +949,12 @@ void WebServerHandler::webHandleConfigFormatRead() {
|
||||
else
|
||||
doc[PARAM_FORMAT_HTTP2] = urlencode(String(&iSpindleFormat[0]));
|
||||
|
||||
s = readFile(TPL_FNAME_HTTP3);
|
||||
if (s.length())
|
||||
doc[PARAM_FORMAT_HTTP3] = urlencode(s);
|
||||
else
|
||||
doc[PARAM_FORMAT_HTTP3] = urlencode(String(&iHttpGetFormat[0]));
|
||||
|
||||
/*s = readFile(TPL_FNAME_BREWFATHER);
|
||||
if (s.length())
|
||||
doc[PARAM_FORMAT_BREWFATHER] = urlencode(s);
|
||||
@ -902,8 +1073,6 @@ const char* WebServerHandler::getHtmlFileName(HtmlFile item) {
|
||||
switch (item) {
|
||||
case HTML_INDEX:
|
||||
return "index.min.htm";
|
||||
case HTML_DEVICE:
|
||||
return "device.min.htm";
|
||||
case HTML_CONFIG:
|
||||
return "config.min.htm";
|
||||
case HTML_CALIBRATION:
|
||||
@ -912,6 +1081,8 @@ const char* WebServerHandler::getHtmlFileName(HtmlFile item) {
|
||||
return "format.min.htm";
|
||||
case HTML_ABOUT:
|
||||
return "about.min.htm";
|
||||
case HTML_TEST:
|
||||
return "test.min.htm";
|
||||
}
|
||||
|
||||
return "";
|
||||
@ -957,8 +1128,6 @@ bool WebServerHandler::setupWebServer() {
|
||||
_server->on("/", std::bind(&WebServerHandler::webReturnIndexHtm, this));
|
||||
_server->on("/index.htm",
|
||||
std::bind(&WebServerHandler::webReturnIndexHtm, this));
|
||||
_server->on("/device.htm",
|
||||
std::bind(&WebServerHandler::webReturnDeviceHtm, this));
|
||||
_server->on("/config.htm",
|
||||
std::bind(&WebServerHandler::webReturnConfigHtm, this));
|
||||
_server->on("/calibration.htm",
|
||||
@ -967,6 +1136,8 @@ bool WebServerHandler::setupWebServer() {
|
||||
std::bind(&WebServerHandler::webReturnFormatHtm, this));
|
||||
_server->on("/about.htm",
|
||||
std::bind(&WebServerHandler::webReturnAboutHtm, this));
|
||||
_server->on("/test.htm",
|
||||
std::bind(&WebServerHandler::webReturnTestHtm, this));
|
||||
#else
|
||||
// Show files in the filessytem at startup
|
||||
|
||||
@ -982,16 +1153,16 @@ bool WebServerHandler::setupWebServer() {
|
||||
|
||||
// Check if the html files exist, if so serve them, else show the static
|
||||
// upload page.
|
||||
if (checkHtmlFile(HTML_INDEX) && checkHtmlFile(HTML_DEVICE) &&
|
||||
checkHtmlFile(HTML_CONFIG) && checkHtmlFile(HTML_CALIBRATION) &&
|
||||
checkHtmlFile(HTML_FORMAT) && checkHtmlFile(HTML_ABOUT)) {
|
||||
if (checkHtmlFile(HTML_INDEX) && checkHtmlFile(HTML_CONFIG) &&
|
||||
checkHtmlFile(HTML_CALIBRATION) && checkHtmlFile(HTML_FORMAT) &&
|
||||
checkHtmlFile(HTML_ABOUT) && checkHtmlFile(HTML_TEST)) {
|
||||
Log.notice(F("WEB : All html files exist, starting in normal mode." CR));
|
||||
|
||||
_server->serveStatic("/", LittleFS, "/index.min.htm");
|
||||
_server->serveStatic("/index.htm", LittleFS, "/index.min.htm");
|
||||
_server->serveStatic("/device.htm", LittleFS, "/device.min.htm");
|
||||
_server->serveStatic("/config.htm", LittleFS, "/config.min.htm");
|
||||
_server->serveStatic("/about.htm", LittleFS, "/about.min.htm");
|
||||
_server->serveStatic("/test.htm", LittleFS, "/test.min.htm");
|
||||
_server->serveStatic("/calibration.htm", LittleFS, "/calibration.min.htm");
|
||||
_server->serveStatic("/format.htm", LittleFS, "/format.min.htm");
|
||||
|
||||
@ -1004,6 +1175,8 @@ bool WebServerHandler::setupWebServer() {
|
||||
_server->on("/", std::bind(&WebServerHandler::webReturnUploadHtm, this));
|
||||
}
|
||||
#endif
|
||||
_server->on("/firmware.htm",
|
||||
std::bind(&WebServerHandler::webReturnFirmwareHtm, this));
|
||||
_server->serveStatic("/log", LittleFS, ERR_FILENAME);
|
||||
_server->serveStatic("/runtime", LittleFS, RUNTIME_FILENAME);
|
||||
|
||||
@ -1011,9 +1184,6 @@ bool WebServerHandler::setupWebServer() {
|
||||
_server->on(
|
||||
"/api/config", HTTP_GET,
|
||||
std::bind(&WebServerHandler::webHandleConfig, this)); // Get config.json
|
||||
_server->on(
|
||||
"/api/device", HTTP_GET,
|
||||
std::bind(&WebServerHandler::webHandleDevice, this)); // Get device.json
|
||||
_server->on("/api/formula", HTTP_GET,
|
||||
std::bind(&WebServerHandler::webHandleFormulaRead,
|
||||
this)); // Get formula.json (calibration page)
|
||||
@ -1064,6 +1234,9 @@ bool WebServerHandler::setupWebServer() {
|
||||
_server->on("/api/device/param", HTTP_GET,
|
||||
std::bind(&WebServerHandler::webHandleDeviceParam,
|
||||
this)); // Change device params
|
||||
_server->on("/api/test/push", HTTP_GET,
|
||||
std::bind(&WebServerHandler::webHandleTestPush,
|
||||
this)); //
|
||||
|
||||
_server->onNotFound(
|
||||
std::bind(&WebServerHandler::webHandlePageNotFound, this));
|
||||
|
@ -24,33 +24,38 @@ SOFTWARE.
|
||||
#ifndef SRC_WEBSERVER_HPP_
|
||||
#define SRC_WEBSERVER_HPP_
|
||||
|
||||
#if defined (ESP8266)
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#else // defined (ESP32)
|
||||
#define MAX_SKETCH_SPACE 1044464
|
||||
#else // defined (ESP32)
|
||||
#include <ESPmDNS.h>
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <Update.h>
|
||||
#define MAX_SKETCH_SPACE 1835008
|
||||
#endif
|
||||
#include <incbin.h>
|
||||
|
||||
#if defined(EMBED_HTML)
|
||||
INCBIN_EXTERN(IndexHtm);
|
||||
INCBIN_EXTERN(DeviceHtm);
|
||||
INCBIN_EXTERN(ConfigHtm);
|
||||
INCBIN_EXTERN(CalibrationHtm);
|
||||
INCBIN_EXTERN(FormatHtm);
|
||||
INCBIN_EXTERN(TestHtm);
|
||||
INCBIN_EXTERN(AboutHtm);
|
||||
#else
|
||||
INCBIN_EXTERN(UploadHtm);
|
||||
#endif
|
||||
INCBIN_EXTERN(FirmwareHtm);
|
||||
|
||||
class WebServerHandler {
|
||||
private:
|
||||
ESP8266WebServer* _server = 0;
|
||||
File _uploadFile;
|
||||
int _lastFormulaCreateError = 0;
|
||||
int _uploadReturn = 200;
|
||||
|
||||
void webHandleConfig();
|
||||
void webHandleFormulaWrite();
|
||||
@ -61,6 +66,7 @@ class WebServerHandler {
|
||||
void webHandleConfigDevice();
|
||||
void webHandleConfigFormatRead();
|
||||
void webHandleConfigFormatWrite();
|
||||
void webHandleTestPush();
|
||||
void webHandleStatusSleepmode();
|
||||
void webHandleClearWIFI();
|
||||
void webHandleStatus();
|
||||
@ -68,7 +74,6 @@ class WebServerHandler {
|
||||
void webHandleCalibrate();
|
||||
void webHandleUploadFile();
|
||||
void webHandleUpload();
|
||||
void webHandleDevice();
|
||||
void webHandleDeviceParam();
|
||||
void webHandlePageNotFound();
|
||||
|
||||
@ -78,16 +83,12 @@ class WebServerHandler {
|
||||
String getRequestArguments();
|
||||
|
||||
// Inline functions.
|
||||
void webReturnOK() { _server->send(200); }
|
||||
void webReturnOK() { _server->send(_uploadReturn); }
|
||||
#if defined(EMBED_HTML)
|
||||
void webReturnIndexHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gIndexHtmData,
|
||||
gIndexHtmSize);
|
||||
}
|
||||
void webReturnDeviceHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gDeviceHtmData,
|
||||
gDeviceHtmSize);
|
||||
}
|
||||
void webReturnConfigHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gConfigHtmData,
|
||||
gConfigHtmSize);
|
||||
@ -104,21 +105,28 @@ class WebServerHandler {
|
||||
_server->send_P(200, "text/html", (const char*)gAboutHtmData,
|
||||
gAboutHtmSize);
|
||||
}
|
||||
void webReturnTestHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gTestHtmData, gTestHtmSize);
|
||||
}
|
||||
#else
|
||||
void webReturnUploadHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gUploadHtmData,
|
||||
gUploadHtmSize);
|
||||
}
|
||||
#endif
|
||||
void webReturnFirmwareHtm() {
|
||||
_server->send_P(200, "text/html", (const char*)gFirmwareHtmData,
|
||||
gFirmwareHtmSize);
|
||||
}
|
||||
|
||||
public:
|
||||
enum HtmlFile {
|
||||
HTML_INDEX = 0,
|
||||
HTML_DEVICE = 1,
|
||||
HTML_CONFIG = 2,
|
||||
HTML_ABOUT = 3,
|
||||
HTML_CALIBRATION = 4,
|
||||
HTML_FORMAT = 5
|
||||
HTML_CONFIG = 1,
|
||||
HTML_ABOUT = 2,
|
||||
HTML_CALIBRATION = 3,
|
||||
HTML_FORMAT = 4,
|
||||
HTML_TEST = 5
|
||||
};
|
||||
|
||||
bool setupWebServer();
|
||||
|
67
src/wifi.cpp
@ -22,11 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#include <HTTPUpdate.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
@ -39,13 +37,8 @@ SOFTWARE.
|
||||
#include <wifi.hpp>
|
||||
|
||||
// Settings for DRD
|
||||
#if defined(ESP8266)
|
||||
#define ESP_DRD_USE_LITTLEFS true
|
||||
#define ESP_DRD_USE_SPIFFS false
|
||||
#else // defined (ESP32)
|
||||
#define ESP_DRD_USE_LITTLEFS false
|
||||
#define ESP_DRD_USE_SPIFFS true
|
||||
#endif
|
||||
#define ESP_DRD_USE_EEPROM false
|
||||
#include <ESP_DoubleResetDetector.h>
|
||||
#define DRD_TIMEOUT 3
|
||||
@ -57,6 +50,7 @@ SOFTWARE.
|
||||
#define USING_CORS_FEATURE false
|
||||
#define NUM_WIFI_CREDENTIALS 1
|
||||
#define USE_STATIC_IP_CONFIG_IN_CP false
|
||||
#define _WIFIMGR_LOGLEVEL_ 3
|
||||
#include <ESP_WiFiManager.h>
|
||||
// Override the look and feel of the standard ui (hide secondary forms)
|
||||
const char WM_HTTP_FORM_START[] PROGMEM =
|
||||
@ -69,7 +63,6 @@ const char WM_HTTP_FORM_START[] PROGMEM =
|
||||
"placeholder='SSID1'><div></div></div><div "
|
||||
"hidden><label>Password</label><input id='p1' name='p1' length=64 "
|
||||
"placeholder='password1'><div></div></div></fieldset>";
|
||||
#include <ESP_WiFiManager-Impl.h>
|
||||
ESP_WiFiManager *myWifiManager;
|
||||
DoubleResetDetector *myDRD;
|
||||
|
||||
@ -96,8 +89,11 @@ bool WifiConnection::hasConfig() {
|
||||
String ssid = WiFi.SSID();
|
||||
if (ssid.length()) {
|
||||
Log.notice(F("WIFI: Found credentials in EEPORM." CR));
|
||||
myConfig.setWifiSSID(WiFi.SSID());
|
||||
myConfig.setWifiPass(WiFi.psk());
|
||||
myConfig.setWifiSSID(ssid);
|
||||
|
||||
if (WiFi.psk().length())
|
||||
myConfig.setWifiPass(WiFi.psk());
|
||||
|
||||
myConfig.saveFile();
|
||||
return true;
|
||||
}
|
||||
@ -143,10 +139,21 @@ void WifiConnection::startPortal() {
|
||||
myWifiManager->setConfigPortalTimeout(
|
||||
myHardwareConfig.getWifiPortalTimeout());
|
||||
|
||||
String mdns("<p>Default mDNS name is: http://");
|
||||
mdns += myConfig.getMDNS();
|
||||
mdns += ".local<p>";
|
||||
ESP_WMParameter deviceName(mdns.c_str());
|
||||
myWifiManager->addParameter(&deviceName);
|
||||
|
||||
if (myWifiManager->startConfigPortal(WIFI_DEFAULT_SSID, WIFI_DEFAULT_PWD)) {
|
||||
Log.notice(F("WIFI: Exited portal, connected to wifi. Rebooting..." CR));
|
||||
myConfig.setWifiSSID(myWifiManager->getSSID());
|
||||
myConfig.setWifiPass(myWifiManager->getPW());
|
||||
|
||||
if (myWifiManager->getSSID().length())
|
||||
myConfig.setWifiSSID(myWifiManager->getSSID());
|
||||
|
||||
if (myWifiManager->getPW().length())
|
||||
myConfig.setWifiPass(myWifiManager->getPW());
|
||||
|
||||
myConfig.saveFile();
|
||||
} else {
|
||||
Log.notice(
|
||||
@ -245,7 +252,11 @@ bool WifiConnection::updateFirmware() {
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPUpdateResult ret;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
#if defined(ESP8266)
|
||||
serverPath += "firmware.bin";
|
||||
#else // defined (ESP32)
|
||||
serverPath += "firmware32.bin";
|
||||
#endif
|
||||
|
||||
if (serverPath.startsWith("https://")) {
|
||||
wifiSecure.setInsecure();
|
||||
@ -277,37 +288,22 @@ bool WifiConnection::updateFirmware() {
|
||||
//
|
||||
// Download and save file
|
||||
//
|
||||
void WifiConnection::downloadFile(const char *fname) {
|
||||
void WifiConnection::downloadFile(HTTPClient &http, String &fname) {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Download file %s." CR), fname);
|
||||
#endif
|
||||
WiFiClient wifi;
|
||||
WiFiClientSecure wifiSecure;
|
||||
HTTPClient http;
|
||||
String serverPath = myConfig.getOtaURL();
|
||||
serverPath += fname;
|
||||
|
||||
if (myConfig.isOtaSSL()) {
|
||||
wifiSecure.setInsecure();
|
||||
Log.notice(F("WIFI: OTA, SSL enabled without validation." CR));
|
||||
http.begin(wifiSecure, serverPath);
|
||||
} else {
|
||||
http.begin(wifi, serverPath);
|
||||
}
|
||||
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
File f = LittleFS.open(fname, "w");
|
||||
http.writeToStream(&f);
|
||||
f.close();
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname);
|
||||
Log.notice(F("WIFI: Downloaded file %s." CR), fname.c_str());
|
||||
} else {
|
||||
ErrorFileLog errLog;
|
||||
errLog.addEntry("WIFI: Failed to download html-file " +
|
||||
String(httpResponseCode));
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
//
|
||||
@ -333,6 +329,7 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
}
|
||||
|
||||
// Send HTTP GET request
|
||||
DynamicJsonDocument ver(300);
|
||||
int httpResponseCode = http.GET();
|
||||
|
||||
if (httpResponseCode == 200) {
|
||||
@ -342,7 +339,6 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
#if LOG_LEVEL == 6 && !defined(WIFI_DISABLE_LOGGING)
|
||||
Log.verbose(F("WIFI: Payload %s." CR), payload.c_str());
|
||||
#endif
|
||||
DynamicJsonDocument ver(300);
|
||||
DeserializationError err = deserializeJson(ver, payload);
|
||||
if (err) {
|
||||
ErrorFileLog errLog;
|
||||
@ -365,25 +361,26 @@ bool WifiConnection::checkFirmwareVersion() {
|
||||
// Compare major version
|
||||
if (newVer[0] > curVer[0]) _newFirmware = true;
|
||||
// Compare minor version
|
||||
if (newVer[0] == curVer[0] && newVer[1] > curVer[1])
|
||||
else if (newVer[0] == curVer[0] && newVer[1] > curVer[1])
|
||||
_newFirmware = true;
|
||||
// Compare patch version
|
||||
if (newVer[0] == curVer[0] && newVer[1] == curVer[1] &&
|
||||
newVer[2] > curVer[2])
|
||||
else if (newVer[0] == curVer[0] && newVer[1] == curVer[1] &&
|
||||
newVer[2] > curVer[2])
|
||||
_newFirmware = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Download new html files to filesystem if they are present.
|
||||
if (!ver["html"].isNull() && _newFirmware) {
|
||||
Log.notice(F("WIFI: OTA downloading new html files." CR));
|
||||
Log.notice(
|
||||
F("WIFI: OTA checking if html files should be downloaded." CR));
|
||||
JsonArray htmlFiles = ver["html"].as<JsonArray>();
|
||||
for (JsonVariant v : htmlFiles) {
|
||||
String s = v;
|
||||
#if LOG_LEVEL == 6
|
||||
Log.verbose(F("WIFI: OTA listed html file %s" CR), s.c_str());
|
||||
#endif
|
||||
downloadFile(s.c_str());
|
||||
downloadFile(http, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,12 @@ SOFTWARE.
|
||||
#ifndef SRC_WIFI_HPP_
|
||||
#define SRC_WIFI_HPP_
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#else // defined (ESP32)
|
||||
#include <HTTPClient.h>
|
||||
#endif
|
||||
|
||||
#define WIFI_DEFAULT_SSID "GravityMon" // Name of created SSID
|
||||
#define WIFI_DEFAULT_PWD "password" // Password for created SSID
|
||||
#define WIFI_MDNS "gravitymon" // Prefix for MDNS name
|
||||
@ -35,7 +41,7 @@ class WifiConnection {
|
||||
// OTA
|
||||
bool _newFirmware = false;
|
||||
bool parseFirmwareVersionString(int (&num)[3], const char* version);
|
||||
void downloadFile(const char* fname);
|
||||
void downloadFile(HTTPClient& http, String& fname);
|
||||
void connectAsync();
|
||||
bool waitForConnection(int maxTime = 20);
|
||||
|
||||
|
@ -35,6 +35,12 @@ These are the format keys available for use in the format.
|
||||
* - ${mdns}
|
||||
- Name of the device
|
||||
- gravmon2
|
||||
* - ${token}
|
||||
- Token
|
||||
- any value
|
||||
* - ${token2}
|
||||
- Token 2
|
||||
- any value
|
||||
* - ${id}
|
||||
- Unique id of the device
|
||||
- e422a3
|
||||
|
@ -12,6 +12,7 @@ Retrive the current configuation of the device via an HTTP GET command. Payload
|
||||
|
||||
* ``temp-format`` can be either ``C`` or ``F``
|
||||
* ``gravity-format`` is always ``G`` (plato is not yet supported)
|
||||
* ``ble`` is used to enable ble data transmission (only on esp32) simulating a tilt. Valid color names are; red, green, black, purple, orange, blue, yellow, pink
|
||||
|
||||
Other parameters are the same as in the configuration guide.
|
||||
|
||||
@ -22,14 +23,17 @@ Other parameters are the same as in the configuration guide.
|
||||
"id": "ee1bfc",
|
||||
"ota-url": "http://192.168.1.50:80/firmware/gravmon/",
|
||||
"temp-format": "C",
|
||||
"ble": "color",
|
||||
"brewfather-push": "http://log.brewfather.net/stream?id=Qwerty",
|
||||
"token": "token",
|
||||
"token2": "token2",
|
||||
"http-push": "http://192.168.1.50:9090/api/v1/Qwerty/telemetry",
|
||||
"http-push-h1": "header: value",
|
||||
"http-push-h2": "header: value",
|
||||
"http-push2": "http://192.168.1.50/ispindel",
|
||||
"http-push2-h1": "header: value",
|
||||
"http-push2-h2": "header: value",
|
||||
"http-push3": "http://192.168.1.50/ispindel",
|
||||
"influxdb2-push": "http://192.168.1.50:8086",
|
||||
"influxdb2-org": "org",
|
||||
"influxdb2-bucket": "bucket_id",
|
||||
@ -53,9 +57,22 @@ Other parameters are the same as in the configuration guide.
|
||||
"gy": -6,
|
||||
"gz": 4
|
||||
},
|
||||
"formula-calculation-data": {
|
||||
"a1":25,
|
||||
"a2":30,
|
||||
"a3":35,
|
||||
"a4":40,
|
||||
"a5":45,
|
||||
"g1":1,
|
||||
"g2":1.01,
|
||||
"g3":1.02,
|
||||
"g4":1.03,
|
||||
"g5":1.04
|
||||
},
|
||||
"angle": 90.93,
|
||||
"gravity": 1.105,
|
||||
"battery": 0.04,
|
||||
"platform": "esp8266",
|
||||
"runtime-average": 3.12
|
||||
}
|
||||
|
||||
@ -63,17 +80,7 @@ Other parameters are the same as in the configuration guide.
|
||||
GET: /api/device
|
||||
================
|
||||
|
||||
Retrive the current device settings via an HTTP GET command. Payload is in JSON format.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"app-name": "GravityMon",
|
||||
"app-ver": "0.0.0",
|
||||
"id": "ee1bfc",
|
||||
"mdns": "gravmon",
|
||||
"runtime-average": 3.12
|
||||
}
|
||||
This API has been removed from 0.9 and merged with /api/status
|
||||
|
||||
|
||||
GET: /api/status
|
||||
@ -82,6 +89,7 @@ GET: /api/status
|
||||
Retrive the current device status via an HTTP GET command. Payload is in JSON format.
|
||||
|
||||
* ``temp-format`` can be either ``C`` or ``F``
|
||||
* ``platform`` can be either ``esp8266`` or ``esp32``
|
||||
|
||||
Other parameters are the same as in the configuration guide.
|
||||
|
||||
@ -91,13 +99,19 @@ Other parameters are the same as in the configuration guide.
|
||||
"id": "ee1bfc",
|
||||
"angle": 89.86,
|
||||
"gravity": 1.1052,
|
||||
"gravity-tempcorr": 1.1031,
|
||||
"temp-c": 0,
|
||||
"temp-f": 32,
|
||||
"battery": 0,
|
||||
"temp-format": "C",
|
||||
"sleep-mode": false,
|
||||
"rssi": -56
|
||||
"token": "token",
|
||||
"token2": "token2",
|
||||
"rssi": -56,
|
||||
"app-ver": "0.0.0",
|
||||
"mdns": "gravmon",
|
||||
"sleep-interval": 30,
|
||||
"platform": "esp8266",
|
||||
"runtime-average": 3.12
|
||||
}
|
||||
|
||||
|
||||
@ -128,8 +142,20 @@ Retrive the data used for formula calculation data via an HTTP GET command. Payl
|
||||
}
|
||||
|
||||
|
||||
GET: /api/clearwifi
|
||||
===================
|
||||
|
||||
Will reset the wifi settings both in the configuration file and eeprom, leaving the rest of the configuration.
|
||||
|
||||
For this to work you will need to supply the device id as a parameter in the request:
|
||||
|
||||
::
|
||||
|
||||
http://mygravity.local/api/clearwifi?id=<mydeviceid>
|
||||
|
||||
|
||||
GET: /api/factory
|
||||
================
|
||||
=================
|
||||
|
||||
Will do a reset to factory defaults and delete all data except wifi settings.
|
||||
|
||||
@ -140,6 +166,27 @@ For this to work you will need to supply the device id as a parameter in the req
|
||||
http://mygravity.local/api/factory?id=<mydeviceid>
|
||||
|
||||
|
||||
GET: /api/test/push
|
||||
===================
|
||||
|
||||
Trigger a push on one of the targets, used to validate the configuration from the UI.
|
||||
|
||||
Requires to parameters to function /api/test/push?id=<deviceid>&format=<format>
|
||||
|
||||
* ``format`` defines which endpoint to test, valid values are; http-1, http-2, brewfather, influxdb, mqtt
|
||||
|
||||
The response is an json message with the following values.
|
||||
|
||||
* ``code`` is the return code from the push function, typically http responsecode or error code from mqtt library.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"success": false,
|
||||
"enabled": true,
|
||||
"code": -3
|
||||
}
|
||||
|
||||
POST: /api/config/device
|
||||
========================
|
||||
|
||||
@ -167,8 +214,11 @@ Payload should be in standard format used for posting a form. Such as as: `id=va
|
||||
.. code-block::
|
||||
|
||||
id=ee1bfc
|
||||
token=
|
||||
token2=
|
||||
http-push=http://192.168.1.50/ispindel
|
||||
http-push2=
|
||||
http-push3=
|
||||
http-push-h1=
|
||||
http-push-h2=
|
||||
http-push2-h1=
|
||||
@ -222,6 +272,7 @@ Payload should be in standard format used for posting a form. Such as as: `id=va
|
||||
id=ee1bfc
|
||||
voltage-factor=1.59
|
||||
temp-adjustment=0
|
||||
ble=red
|
||||
gyro-temp=off
|
||||
ota-url=http://192.168.1.50/firmware/gravmon/
|
||||
|
||||
@ -286,15 +337,17 @@ The requests package converts the json to standard form post format.
|
||||
|
||||
url = "http://" + host + "/api/config/push"
|
||||
json = { "id": id,
|
||||
"token": "",
|
||||
"token": "",
|
||||
"token2": "",
|
||||
"http-push": "http://192.168.1.1/ispindel",
|
||||
"http-push2": "",
|
||||
"http-push-h1": "",
|
||||
"http-push-h2": "",
|
||||
"http-push2-h1": "",
|
||||
"http-push2-h2": "",
|
||||
"brewfather-push": "",
|
||||
"influxdb2-push": "",
|
||||
"http-push2": "",
|
||||
"http-push3": "",
|
||||
"http-push-h1": "",
|
||||
"http-push-h2": "",
|
||||
"http-push2-h1": ""
|
||||
"http-push2-h2": "",
|
||||
"brewfather-push": "",
|
||||
"influxdb2-push": "",
|
||||
"influxdb2-org": "",
|
||||
"influxdb2-bucket": "",
|
||||
"influxdb2-auth": "",
|
||||
@ -318,6 +371,7 @@ The requests package converts the json to standard form post format.
|
||||
"voltage-factor": 1.59, # Default value for voltage calculation
|
||||
"temp-adjustment": 0, # If temp sensor needs to be corrected
|
||||
"gyro-temp": "on", # Use the temp sensor in the gyro instead (on/off)
|
||||
"ble": "red", # Enable ble on esp32
|
||||
"ota-url": "" # if the device should seach for a new update when active
|
||||
}
|
||||
set_config( url, json )
|
||||
|
@ -22,7 +22,7 @@ copyright = '2021-2022, Magnus Persson'
|
||||
author = 'Magnus Persson'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.8.0'
|
||||
release = '0.9.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
@ -22,49 +22,24 @@ URL: (http://gravmon.local)
|
||||
:width: 800
|
||||
:alt: Index page
|
||||
|
||||
|
||||
Configuration is accessed by entering the URL for the device, this will be the mDNS name *device.local* or the IP adress. The following chapter assumes the device name is *gravmon*.
|
||||
|
||||
The main page shows the device readings; gravity, angle, temperature and battery charge. If the checkbox is active then the device will never go into sleep mode. This is useful if
|
||||
you are collecting angle/tilt for calibration. If this is unchecked the device will change mode as explained before.
|
||||
|
||||
You can also view the average time a gravity measurement takes. Under optimal setting this should be around 1.5 - 2.0 seconds. If this is higher than 2 seconds this is most likley connected to slow wifi
|
||||
connection. It will show 0 if data has not been collected yet.
|
||||
|
||||
.. tip::
|
||||
|
||||
If you are connected to the device via a serial console (speed: 115200) you can see the connection sequence and get the Unique ID and IP adress from there.
|
||||
|
||||
|
||||
Device
|
||||
======
|
||||
|
||||
URL: (http://gravmon.local/device)
|
||||
|
||||
.. image:: images/device.png
|
||||
:width: 800
|
||||
:alt: Device Settings
|
||||
|
||||
.. tip::
|
||||
|
||||
The button `view error log` will show the last 15 errors on the device. This can be useful for checking errors without
|
||||
the need to connect to the serial port or to check what errors has occured while in `gravity mode`.
|
||||
|
||||
* **Version:**
|
||||
|
||||
Installed version of the code and html files.
|
||||
|
||||
* **Device name:**
|
||||
|
||||
This is unique name of the device which is set in the configuration, also known as MDNS name.
|
||||
|
||||
* **Device ID:**
|
||||
|
||||
This is unique identifier for the device (ESP8266 id), this is required when using the API as an API Key to safeguard
|
||||
against faulty requests. This is the ESP8266 chip ID, so it should be unique.
|
||||
|
||||
* **Average runtime:**
|
||||
|
||||
This shows the average time a gravity measurement takes. Under optimal setting this should be
|
||||
around 1.5 - 2.0 seconds. If this is higher than 2 seconds this is most likley connected to slow wifi
|
||||
connection. It will show 0 if data has not been collected yet.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
@ -80,27 +55,28 @@ Device Setting
|
||||
|
||||
* **Device name:**
|
||||
|
||||
This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local)
|
||||
This is unique name for the device. It will be used in pushing data as well as mDNS name on the network (<name>.local)
|
||||
|
||||
* **Temperature format:**
|
||||
|
||||
Choose between Celsius and Farenheight when displaying temperature.
|
||||
Choose between Celsius and Farenheight when displaying temperature.
|
||||
|
||||
* **Interval:**
|
||||
|
||||
This defines how long the device should be sleeping between the readings when in `gravity monitoring` mode. You will also see
|
||||
the values in minutes/seconds to easier set the interval. 900s is a recommended interval. The sleep interval can
|
||||
be set between 10 - 3600 seconds (60 minutes).
|
||||
This defines how long the device should be sleeping between the readings when in `gravity monitoring` mode. You will also see
|
||||
the values in minutes/seconds to easier set the interval. 900s is a recommended interval. The sleep interval can
|
||||
be set between 10 - 3600 seconds (60 minutes).
|
||||
|
||||
.. note::
|
||||
|
||||
A low value such as 30s will give a lifespan of 1-2 weeks and 300s (5 min) would last for 3+ weeks. This assumes that
|
||||
there is good wifi connection that takes less than 1s to reconnect. Poor wifi connection is the main reason for battery drain.
|
||||
there is good wifi connection that takes less than 1s to reconnect. Poor wifi connection is the main reason for battery drain.
|
||||
The device will show the estimated lifespan based on the average connection time, if no data exist it will not be shown.
|
||||
|
||||
|
||||
* **Calibration values:**
|
||||
|
||||
These are calibration data for the gyro. Place the device flat on a table and press the button to save the default orientation values. Without this calibration we cannot calculate the correct angle/tilt.
|
||||
These are calibration data for the gyro. Place the device flat on a table and press the button to save the default orientation values. Without this calibration we cannot calculate the correct angle/tilt.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -118,85 +94,102 @@ Push Settings
|
||||
When enabling SSL this will not validate the root CA of the remote service, this is a design decision based on two aspects. Enabling CA validation will take 3-4s extra on each connection which means way less
|
||||
battery life, so the decision is to prioritize battery life over security. The data transmitted is not really that sensitive anyway so I belive this is a good balance.
|
||||
|
||||
* **HTTP 1 (POST):**
|
||||
|
||||
* **HTTP URL 1:**
|
||||
Endpoint to send data via http. Default format used Format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
Endpoint to send data via http. Default format used Format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
|
||||
If you add the prefix `https://` then the device will use SSL when sending data.
|
||||
|
||||
If you add the prefix `https://` then the device will use SSL when sending data.
|
||||
* **HTTP 2 (POST):**
|
||||
|
||||
* **HTTP URL 2:**
|
||||
Endpoint to send data via http. Default format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
Endpoint to send data via http. Default format used :ref:`data-formats-ispindle`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
If you add the prefix `https://` then the device will use SSL when sending data.
|
||||
If you add the prefix `https://` then the device will use SSL when sending data.
|
||||
|
||||
* **Token:**
|
||||
|
||||
The token is included in the iSpindle JSON format and will be used for both HTTP targets. If you
|
||||
need to have 2 different tokens please use the :ref:`format-editor` to customize the data format.
|
||||
The token is included in the iSpindle JSON format and will be used for both HTTP targets. If you
|
||||
need to have 2 different tokens please use the :ref:`format-editor` to customize the data format.
|
||||
|
||||
* **HTTP 3 (GET):**
|
||||
|
||||
Endpoint to send data via http. This is using an HTTP GET request instead of a post. This means that the values are appended to the URL like; http://endpoint?param=value¶m2=value2. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
If you add the prefix `https://` then the device will use SSL when sending data.
|
||||
|
||||
* **Token 2:**
|
||||
|
||||
The token is included in the default format for the HTTP GET url but can be used for any of the formats. For HTTP GET use can use this for an authorization token with for instance ubidots or blynk http api.
|
||||
|
||||
* **Brewfather URL:**
|
||||
|
||||
Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather`
|
||||
Endpoint to send data via http to brewfather. Format used :ref:`data-formats-brewfather`
|
||||
|
||||
SSL is not supported for this target.
|
||||
|
||||
* **Influx DB v2 URL:**
|
||||
|
||||
Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
SSL is not supported for this target. Raise a issue on github if this is wanted.
|
||||
|
||||
* **Influx DB v2 Organisation:**
|
||||
|
||||
Name of organisation in Influx.
|
||||
|
||||
* **Influx DB v2 Bucket:**
|
||||
|
||||
Identifier for bucket.
|
||||
|
||||
* **Influx DB v2 Token:**
|
||||
|
||||
Token with write access to bucket.
|
||||
|
||||
* **MQTT server:**
|
||||
|
||||
IP or name of server to send data to. Default format used :ref:`data-formats-mqtt`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
* **MQTT Port:**
|
||||
|
||||
Which port should be used for communication, default is 1883 (standard port). For SSL use 8883 (any port over 8000 is treated as SSL).
|
||||
|
||||
* **MQTT user:**
|
||||
|
||||
Username or blank if anonymous is accepted
|
||||
|
||||
* **MQTT password:**
|
||||
|
||||
Password or blank if anonymous is accepted
|
||||
SSL is not supported for this target.
|
||||
|
||||
* **HTTP Headers**
|
||||
|
||||
.. image:: images/config-popup1.png
|
||||
:width: 300
|
||||
:alt: HTTP Headers
|
||||
.. image:: images/config-popup1.png
|
||||
:width: 300
|
||||
:alt: HTTP Headers
|
||||
|
||||
You can define 2 http headers per push target. This is available via a pop-up window but dont forget
|
||||
to press the save buttons on the post section to save the values. One common header is content type which is the
|
||||
default setting for http targets.
|
||||
You can define 2 http headers per push target. This is available via a pop-up window but dont forget
|
||||
to press the save buttons on the post section to save the values. One common header is content type which is the
|
||||
default setting for http targets.
|
||||
|
||||
The input must have the format **'<header>: <value>'** for it to work. The UI will accept any value so errors
|
||||
will not show until the device tries to push data.
|
||||
The input must have the format **'<header>: <value>'** for it to work. The UI will accept any value so errors
|
||||
will not show until the device tries to push data.
|
||||
|
||||
|
||||
::
|
||||
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: <api-token>
|
||||
::
|
||||
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: <api-token>
|
||||
|
||||
|
||||
Mozilla has a good guide on what headers are valid; `HTTP Headers <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers>`_
|
||||
Mozilla has a good guide on what headers are valid; `HTTP Headers <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers>`_
|
||||
|
||||
|
||||
Push Settings (2)
|
||||
+++++++++++++++++
|
||||
|
||||
.. image:: images/config2b.png
|
||||
:width: 800
|
||||
:alt: Push Settings
|
||||
|
||||
* **Influx DB v2 URL:**
|
||||
|
||||
Endpoint to send data via http to InfluxDB. Format used :ref:`data-formats-influxdb2`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
SSL is not supported for this target. Raise a issue on github if this is wanted.
|
||||
|
||||
* **Influx DB v2 Organisation:**
|
||||
|
||||
Name of organisation in Influx.
|
||||
|
||||
* **Influx DB v2 Bucket:**
|
||||
|
||||
Identifier for bucket.
|
||||
|
||||
* **Influx DB v2 Token:**
|
||||
|
||||
Token with write access to bucket.
|
||||
|
||||
* **MQTT server:**
|
||||
|
||||
IP or name of server to send data to. Default format used :ref:`data-formats-mqtt`. You can customize the format using :ref:`format-editor`.
|
||||
|
||||
* **MQTT Port:**
|
||||
|
||||
Which port should be used for communication, default is 1883 (standard port). For SSL use 8883 (any port over 8000 is treated as SSL).
|
||||
|
||||
* **MQTT user:**
|
||||
|
||||
Username or blank if anonymous is accepted
|
||||
|
||||
* **MQTT password:**
|
||||
|
||||
Password or blank if anonymous is accepted
|
||||
|
||||
|
||||
Gravity Settings
|
||||
@ -208,25 +201,26 @@ Gravity Settings
|
||||
|
||||
* **Gravity format:**
|
||||
|
||||
Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data.
|
||||
Gravity format can be eihter `SG` or `Plato`. The device will use SG Internally and convert to Plato when displaying or sending data.
|
||||
|
||||
* **Gravity formula:**
|
||||
|
||||
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
|
||||
the feature to create the formula by supplying the raw data. See :ref:`create-formula`
|
||||
Gravity formula is compatible with standard iSpindle formulas so any existing calculation option can be used. You can also use
|
||||
the feature to create the formula by supplying the raw data. See :ref:`create-formula`
|
||||
|
||||
The gravity formula accepts to paramaters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula
|
||||
will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier.
|
||||
The gravity formula accepts to paramaters, **tilt** for the angle or **temp** for temperature (temperature inserted into the formula
|
||||
will be in celsius). I would recommend to use the formula calculation feature instead since this is much easier.
|
||||
|
||||
* **Temperature correct gravity:**
|
||||
|
||||
Will apply a temperature calibration formula to the gravity as a second step after gravity has been calculated. It's also possible to
|
||||
build this into the gravity formula.
|
||||
Will apply a temperature calibration formula to the gravity as a second step after gravity has been calculated. It's also possible to
|
||||
build this into the gravity formula.
|
||||
|
||||
.. warning::
|
||||
|
||||
This formula assumes that the calibration has been done at 20°C / 68°F.
|
||||
|
||||
Formula used in temperature correction.
|
||||
Formula used in temperature correction.
|
||||
|
||||
::
|
||||
|
||||
@ -243,30 +237,34 @@ Hardware Settings
|
||||
|
||||
* **Voltage factor:**
|
||||
|
||||
Factor used to calcualate the battery voltage. If you get a too low/high voltage you can adjust this value.
|
||||
Factor used to calcualate the battery voltage. If you get a too low/high voltage you can adjust this value.
|
||||
|
||||
* **Temperature correction:**
|
||||
|
||||
This value will be added to the temperature reading (negative value will reduce temperature reading). This is applied
|
||||
when the device starts. So changing this will not take affect until the device is restarted.
|
||||
This value will be added to the temperature reading (negative value will reduce temperature reading). This is applied
|
||||
when the device starts. So changing this will not take affect until the device is restarted.
|
||||
|
||||
* **Gyro Temperature:**
|
||||
|
||||
Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and
|
||||
longer battery life (this is an experimental feature). The value used is the first temperature reading from when the
|
||||
device is activated, since the gyro should be cool this is reflecting the surronding temperature. After it has
|
||||
been running the value would be totally off.
|
||||
Enable this feature will use the temp sensor i the gyro instead of the DS18B20, the benefit is shorter run time and
|
||||
longer battery life (this is an experimental feature). The value used is the first temperature reading from when the
|
||||
device is activated, since the gyro should be cool this is reflecting the surronding temperature. After it has
|
||||
been running the value would be totally off.
|
||||
|
||||
* **Bluetooth: (Only ESP32)**
|
||||
|
||||
If the build is using an ESP32 then you can send data over BLE, simulating a Tilt device. Choose the color that you want the device to simulate.
|
||||
|
||||
* **OTA URL:**
|
||||
|
||||
Should point to a URL where the firmware.bin file + version.json file are located.
|
||||
Should point to a URL where the firmware.bin file + version.json file are located. For an ESP32 target the firmware should be named firmware32.bin.
|
||||
|
||||
For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the
|
||||
code the update will be done during startup.
|
||||
For the OTA to work, place the following files (version.json + firmware.bin) at the location that you pointed out in OTA URL. If the version number in the json file is newer than in the
|
||||
code the update will be done during startup.
|
||||
|
||||
If you have the previx `https://` then the device will use secure transfer without CA validation.
|
||||
If you have the previx `https://` then the device will use secure transfer without CA validation.
|
||||
|
||||
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
|
||||
Example; OTA URL (don't forget trailing dash), the name of the file should be firmware.bin
|
||||
|
||||
.. code-block::
|
||||
|
||||
@ -274,3 +272,12 @@ Hardware Settings
|
||||
https://192.168.1.1/firmware/gravmon/
|
||||
|
||||
|
||||
* **Upload Firmware**
|
||||
|
||||
This option gives you the possibility to install an new version of the firmware (or any firmware that uses the standard flash layout).
|
||||
|
||||
.. image:: images/firmware.png
|
||||
:width: 600
|
||||
:alt: Update firmware
|
||||
|
||||
|
||||
|
@ -5,8 +5,8 @@ Data Formats
|
||||
|
||||
.. _data-formats-ispindle:
|
||||
|
||||
iSpindle format
|
||||
===============
|
||||
HTTP Post, iSpindle format
|
||||
==========================
|
||||
|
||||
This is the format used for standard http posts.
|
||||
|
||||
@ -22,7 +22,7 @@ This is the format used for standard http posts.
|
||||
"token" : "gravmon",
|
||||
"interval": 900,
|
||||
"temperature": 20.5,
|
||||
"temp-units": "C",
|
||||
"temp_units": "C",
|
||||
"gravity": 1.0050,
|
||||
"angle": 45.34,
|
||||
"battery": 3.67,
|
||||
@ -43,7 +43,7 @@ This is the format template used to create the json above.
|
||||
"token" : "gravmon",
|
||||
"interval": ${sleep-interval},
|
||||
"temperature": ${temp},
|
||||
"temp-units": "${temp-unit}",
|
||||
"temp_units": "${temp-unit}",
|
||||
"gravity": ${gravity},
|
||||
"angle": ${angle},
|
||||
"battery": ${battery},
|
||||
@ -75,6 +75,25 @@ This is the format for Brewfather. See: `Brewfather API docs <https://docs.brewf
|
||||
|
||||
.. _data-formats-influxdb2:
|
||||
|
||||
HTTP Get
|
||||
========
|
||||
|
||||
This is the format added to the URL when using HTTP get
|
||||
|
||||
.. code-block::
|
||||
|
||||
?name=<mdns>,id=<id>,token=<token>&interval=300&temperature=20.1&temp-units=<C|F>&
|
||||
gravity=$1.004&angle=45.5&battery=3.96&rssi=-18&corr-gravity=1.004&gravity-unit=<G|P>&run-time=2.1
|
||||
|
||||
This is the format template used to create the data above.
|
||||
|
||||
.. code-block::
|
||||
|
||||
?name=${mdns}&id=${id}&token=${token2}&interval=${sleep-interval}&temperature=${temp}&
|
||||
temp-units=${temp-unit}&gravity=${gravity}&angle=${angle}&battery=${battery}&rssi=${rssi}&
|
||||
corr-gravity=${corr-gravity}&gravity-unit=${gravity-unit}&run-time=${run-time}
|
||||
|
||||
|
||||
Influx DB v2
|
||||
============
|
||||
|
||||
@ -82,14 +101,17 @@ This is the format for InfluxDB v2
|
||||
|
||||
.. code-block::
|
||||
|
||||
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
||||
measurement,host=<mdns>,device=<id>,temp-format=<C|F>,gravity-format=SG,
|
||||
gravity=1.0004,corr-gravity=1.0004,angle=45.45,temp=20.1,battery=3.96,rssi=-18
|
||||
|
||||
|
||||
This is the format template used to create the json above.
|
||||
|
||||
.. code-block::
|
||||
|
||||
measurement,host=${mdns},device=${id},temp-format=${temp-unit},gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},angle=${angle},temp=${temp},battery=${battery},rssi=${rssi}
|
||||
measurement,host=${mdns},device=${id},temp-format=${temp-unit},
|
||||
gravity-format=${gravity-unit} gravity=${gravity},corr-gravity=${corr-gravity},
|
||||
angle=${angle},temp=${temp},battery=${battery},rssi=${rssi}
|
||||
|
||||
|
||||
.. _data-formats-mqtt:
|
||||
@ -130,7 +152,10 @@ This is a format template that is compatible with v0.6. Just replace the `topic`
|
||||
|
||||
.. code-block::
|
||||
|
||||
topic:{"name":"gravmon","ID":"${id}","token":"gravmon","interval": ${sleep-interval},"temperature": ${temp},"temp-units": "${temp-unit}","gravity":${gravity},"angle": ${angle},"battery":${battery},"rssi": ${rssi},"corr-gravity":${corr-gravity},"gravity-unit": "${gravity-unit}","run-time": ${run-time}}|
|
||||
topic:{"name":"gravmon", "ID":"${id}", "token":"gravmon", "interval": ${sleep-interval},
|
||||
"temperature": ${temp}, "temp_units": "${temp-unit}", "gravity":${gravity},
|
||||
"angle": ${angle}, "battery":${battery}, "rssi": ${rssi}, "corr-gravity":${corr-gravity},
|
||||
"gravity-unit": "${gravity-unit}", "run-time": ${run-time}}|
|
||||
|
||||
|
||||
version.json
|
||||
@ -147,7 +172,7 @@ they can be uploaded manually afterwards.
|
||||
"version":"0.7.0",
|
||||
"html": [
|
||||
"index.min.htm",
|
||||
"device.min.htm",
|
||||
"test.min.htm",
|
||||
"config.min.htm",
|
||||
"format.min.htm",
|
||||
"calibration.min.htm",
|
||||
|
26
src_docs/source/hardware.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. _hardware:
|
||||
|
||||
Hardware
|
||||
########
|
||||
|
||||
There are lots of resouces out there on how to build the hardware for an iSpindle so I will not go into details on that part. Here are two of my builds using the iSpindle PCB v4.
|
||||
|
||||
.. image:: images/ispindel.jpg
|
||||
:width: 500
|
||||
:alt: Builds of iSpindel
|
||||
|
||||
It's possible to use this PCB and mount an ESP32 on top of that. It must be an pin compatible ESP32 and the one I used was called *ESP32 d1 mini*. Since this is the same width as the PCB you need to
|
||||
mount it really close to the PCB in order for it to fit in the PET tube/container. I also had to smooth the edge of the ESP32 in order for it to fit.
|
||||
|
||||
I would suggest that you try how it fits into the PET tube before soldering it to the PCB. Make sure that the battery is attached since this will be a really tight fit.
|
||||
|
||||
You also need to desolder (remove) the RED ON LED from the ESP32 or the battery power will be reduced a lot.
|
||||
|
||||
Final thing is to add a resistor between A0 (Analog PIN) and ground of 470k. The reason is that the esp8266 has a build in resistor which
|
||||
the esp32 does not have. So in order to get a valid voltage (less than 3.2V) on the A0 pin this is needed. Once the modification is done you might
|
||||
need to adjust the voltage factor so the battery reading is correct.
|
||||
|
||||
.. image:: images/esp32.jpg
|
||||
:width: 500
|
||||
:alt: Mounting esp32
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 28 KiB |
BIN
src_docs/source/images/config2b.png
Normal file
After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 16 KiB |
BIN
src_docs/source/images/esp32.jpg
Normal file
After Width: | Height: | Size: 931 KiB |
BIN
src_docs/source/images/firmware.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
src_docs/source/images/gravitymon.gif
Normal file
After Width: | Height: | Size: 4.5 MiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 75 KiB |
BIN
src_docs/source/images/ispindel.jpg
Normal file
After Width: | Height: | Size: 2.5 MiB |
@ -7,8 +7,12 @@ Welcome to GravityMon's documentation!
|
||||
######################################
|
||||
|
||||
.. note::
|
||||
This documentation reflects **v0.8**. Last updated 2022-03-05
|
||||
|
||||
This documentation reflects **v0.9**. Last updated 2022-04-23
|
||||
|
||||
.. image:: images/gravitymon.gif
|
||||
:width: 800
|
||||
:alt: User Inteface Walkthrough
|
||||
|
||||
GravityMon is used to measure gravity and temperature during fermentation of beer and report the progress. The graph below is
|
||||
an example on how the fermentation process can be tracked. This is from my last brew that was over on a few days. The graph is rendered using
|
||||
Fermentrack.
|
||||
@ -17,17 +21,19 @@ Fermentrack.
|
||||
:width: 500
|
||||
:alt: Example fermentation
|
||||
|
||||
GravityMon is a replacement firmare for the iSpindle and uses the same hardware configuration and is 100% compatible. It
|
||||
GravityMon is a replacement firmware for the iSpindle and uses the same hardware configuration and is 100% compatible. It
|
||||
implements a lot of the features that has been requested in the orginal iSpindle project but has been rejected for
|
||||
various reasons. Here is a list of :ref:`main_features`.
|
||||
|
||||
I started GravityMon because i like to create software and wanted to do some low level programming. I had done a few
|
||||
From v0.9 the firmware now supports a iSpindle build based on an ESP32 d1 mini (pin compatible with esp8266). See :ref:`hardware`.
|
||||
|
||||
I started GravityMon because I like to create software and wanted to do some low level programming. I had done a few
|
||||
projects based on esp8266 and also started to brew beer so this combination was quite natural.
|
||||
|
||||
The hardware design comes from the fantastic iSpindle project so that is not covered in this documentation. For more
|
||||
information on this topic and function please visit `iSpindel Homepage <https://www.ispindel.de>`_ .
|
||||
|
||||
My approach to this software is a little different from that the original ispindle firmware. The github repository
|
||||
My approach to this software is a little different from that the original iSpindle firmware. The github repository
|
||||
can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
|
||||
|
||||
.. note::
|
||||
@ -124,17 +130,29 @@ the following libraries and without these this would have been much more difficu
|
||||
|
||||
CSS templates for the web page.
|
||||
|
||||
* https://github.com/lorol/LITTLEFS
|
||||
|
||||
LittleFS library for ESP32 framework 1.x
|
||||
|
||||
* https://github.com/h2zero/NimBLE-Arduino
|
||||
|
||||
Bluetooth library for ESP32 framework 1.x
|
||||
|
||||
* https://github.com/spouliot/tilt-sim
|
||||
|
||||
Excellent project for simulating a tilt device.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
license
|
||||
functionallity
|
||||
intro
|
||||
installation
|
||||
releases
|
||||
functionallity
|
||||
installation
|
||||
configuration
|
||||
troubleshooting
|
||||
q_and_a
|
||||
formula
|
||||
services
|
||||
advanced
|
||||
@ -142,8 +160,8 @@ the following libraries and without these this would have been much more difficu
|
||||
data
|
||||
compiling
|
||||
contributing
|
||||
troubleshooting
|
||||
q_and_a
|
||||
hardware
|
||||
license
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@ -3,6 +3,12 @@
|
||||
Installation
|
||||
------------
|
||||
|
||||
You have these 3 options for flashing this firmware.
|
||||
|
||||
* Brewflasher via USB serial
|
||||
* Esptool via USB serial
|
||||
* iSpindel web interface
|
||||
|
||||
Brewflasher
|
||||
===========
|
||||
|
||||
@ -16,7 +22,7 @@ on both Windows and Mac. You can download the latest version from here: `Brewfla
|
||||
Binaries
|
||||
********
|
||||
|
||||
In the /bin directory you will find 2 different firmware builds;
|
||||
In the /bin directory you will find 3 different firmware builds;
|
||||
|
||||
* **firmware.bin**
|
||||
|
||||
@ -26,12 +32,16 @@ In the /bin directory you will find 2 different firmware builds;
|
||||
|
||||
This version also submits performance data to an influx database with detailed execution times.
|
||||
|
||||
* **firmware32.bin**
|
||||
|
||||
This is the standard release build for an ESP32 variant
|
||||
|
||||
In these versions all the html files are embedded in the binaries. The file system is currently only used for storing
|
||||
the configuration file.
|
||||
|
||||
If the software becomes so large the html files can be moved to the file system, but this is not enabled by
|
||||
default (see compiling for details). This approach makes installation much easier and ensure that html files
|
||||
and code is in sync.
|
||||
and code is in sync.
|
||||
|
||||
Esptool
|
||||
=======
|
||||
@ -49,6 +59,40 @@ If there are issues you can try do erase the flash first using this command;
|
||||
|
||||
``esptool.py --port COM4 erase_flash``
|
||||
|
||||
iSpindel
|
||||
========
|
||||
|
||||
If you already have the device flashed with iSpindel firmware you can go into the configuration mode where you will find
|
||||
an option for updating firmware. The option is under the maintence meny.
|
||||
|
||||
Select the esp8266 version of the firmware called firmware.bin and press upload.
|
||||
|
||||
Updating firmware
|
||||
=================
|
||||
|
||||
You can use the above options for updating the firmware as well. But if you want to do updates without connecting the USB cable these
|
||||
are your options.
|
||||
|
||||
OTA Option
|
||||
**********
|
||||
|
||||
You can use the OTA option by adding this URL to your configuration and when the device starts up in configuration mode it
|
||||
will check for a new version and if it finds a newer version it will do an update.
|
||||
|
||||
``https://mp-se.github.io/gravitymon/release/``
|
||||
|
||||
Manual update
|
||||
*************
|
||||
|
||||
When the device in is configuration mode you can manually update with a new firmware. Just open this URL in the web
|
||||
browser and select the firmware.bin file that corresponds to the version you want to flash.
|
||||
|
||||
``http://<device_name>/firmware.htm``
|
||||
|
||||
|
||||
|
||||
.. _serial_monitoring:
|
||||
|
||||
Serial Monitoring
|
||||
=================
|
||||
|
||||
@ -66,14 +110,32 @@ Configuring WIFI
|
||||
================
|
||||
|
||||
When the device is flashed it will need to have WIFI configuration in order to work. If you have used other software on
|
||||
the device its possible that wifi settings exist.
|
||||
the device its possible that wifi settings already exist.
|
||||
|
||||
If this is not configured in the device it will create an wirless access point called `GravMon`. The default password is `password`.
|
||||
|
||||
Connect to this AP and enter the SSID and password you want to use. If the web page dont open automatically you can enter the following adress
|
||||
in the browser: **http://192.168.4.1**
|
||||
|
||||
Before pressing save on the network infomration, make a note of the devicename that is shown on the screen, this will be the name that is used
|
||||
in the next step to access the configuration pages. The link would look like this: **http://gravitymon56EA34.local**
|
||||
|
||||
.. image:: images/wifi.png
|
||||
:width: 200
|
||||
:alt: Wifi page
|
||||
|
||||
|
||||
.. _setup_ip:
|
||||
|
||||
Finding the device adress
|
||||
=========================
|
||||
|
||||
Once the wifi network settings have been added then the device will reboot and connect to your network. If the blue led is flashing then it's up and running and is ready to be configured.
|
||||
|
||||
If your computer supports mDNS the adress you saw before can be used in your web browser to connect to the device. Windows does not have the best support for mDNS so if you are having issues
|
||||
with finding the network name you can try the following:
|
||||
|
||||
* Check your wireless router for the IP adress and use that to connect instead, for example; http://192.168.1.56
|
||||
* Download an IP scanner / Port Scanner on your Windows computer or mobile device and use that to find what devices are listening on port 80.
|
||||
|
||||
Once you can access the user interface then proceed to the next step.
|
||||
|
@ -7,3 +7,8 @@ My device is no going in to sleep after fully charged
|
||||
- Check the angle/tilt. If the device is reporting 90 degress then its not going into sleep. Tilt the device and check if sleep works.
|
||||
- Check in reported voltage of the battery in the web interface. If this is higher than 4.15V the device belives its beeing charged. In that case adjust the voltage factor under hardware so the voltage drops below 4.15V.
|
||||
- Check if the `always on` option is activated in the web interface.
|
||||
|
||||
My device reports a temperature of -273C or -491F
|
||||
-------------------------------------------------
|
||||
- The DS18B20 temperature sensor cannot be found and this is the default value reported in this case.
|
||||
- Check the orienation of the sensor and soldering.
|
||||
|
@ -3,6 +3,33 @@
|
||||
Releases
|
||||
########
|
||||
|
||||
* Installation instructions updated on how to find the device after wifi has been configured.
|
||||
|
||||
v0.9.0
|
||||
------
|
||||
* Added one http push target that uses HTTP GET. This can be used with ubidots or blynk api's.
|
||||
* Added function to test push targets from configuration page. It will send data and show the return code as a first step.
|
||||
* Added documetation on how to integrate with Blynk.io using http get.
|
||||
* Config page now shows the estimated runtime for the device (based on a full battery and previous average runtime)
|
||||
* Experimental release of firmware using an esp32 instead of esp8266
|
||||
* Merged index and device pages into one so that all the needed information is available on the index page.
|
||||
* Removed api for device (/api/device), it's now merged into the /api/status api.
|
||||
* Test function in format editor now uses real data and not fake.
|
||||
* Split push configuration into two sections to make it fit better on smaller devices
|
||||
* Updated WifiManager and DoubleReset libraries
|
||||
* Updated esp32 target with littlefs support
|
||||
* Updated esp32 target with BLE send support (it will simulate a tilt)
|
||||
* Mounted esp32 d1 mini mounted to a iSpindle PCB 4.0 (CherryPhilip) which worked fine.
|
||||
* Documented hardware changes on esp32
|
||||
* Default mDNS name is now shown on WIFI setup page.
|
||||
* Added option to manually update/downgrade firmware under hardware settings.
|
||||
* BUG: Corrected PIN for voltage read on ESP32
|
||||
* BUG: If using plato and not gravity formula was defined the value was set to null.
|
||||
* BUG: Temp format name was incorrect in iSpindle format causing receiver to incorrectly read temperature.
|
||||
* BUG: Temperature sensor adjusmemnt value was not handled properly when using Farenheight.
|
||||
* BUG: If the ID was to low the device id could end up with a leading space causing errors in data post. Added leading zero to ID.
|
||||
* BUG: Entering wifi setup and a timeout occured the wifi settings could be deleted.
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
|
||||
@ -13,7 +40,7 @@ v0.8.0
|
||||
* Added instructions for how to configure integration with Brewspy
|
||||
* Added instructions for how to configure integration with Thingspeak
|
||||
* Added option to do a factory reset via API.
|
||||
* Logging the runtime, how long a measurement take (last 10 are stored). This can be
|
||||
* Added logging of the runtime, how long a measurement take (last 10 are stored). This can be
|
||||
used to check how good the wifi connection is and estimate the lifetime when on battery.
|
||||
Check the device page in the UI for this information.
|
||||
* Refactored code to free up more RAM to make SSL more stable.
|
||||
|
@ -10,14 +10,55 @@ Brewfather
|
||||
|
||||
Brewfather is an all in one service that allows you to manage you recepies and brews.
|
||||
|
||||
Just enter the http adress found on brewfather, not other settings are needed. The endpoint has the following format:
|
||||
.. tip::
|
||||
|
||||
The integration named Brewfather is uses the custom stream endpoint in brewfather not the standard iSpindle
|
||||
endpoint. You can use the iSpindle endpoint as well. In that case just use the http-1 or http-2 fields.
|
||||
|
||||
**Option 1** - Custom Stream
|
||||
|
||||
This option makes use of the push endpoint called Brewfather in the UI. Just enter the http stream adress found
|
||||
on brewfather, not other settings are needed. The stream endpoint URL has the following format:
|
||||
|
||||
.. code-block::
|
||||
|
||||
http://log.brewfather.net/http://log.brewfather.net/stream?id=<yourid>
|
||||
http://log.brewfather.net/stream?id=<yourid>
|
||||
|
||||
Documentation on this can be found under `Brewfather Custom Endpoint <https://docs.brewfather.app/integrations/custom-stream>`_
|
||||
|
||||
The implementation is basically a http request with the following format template:
|
||||
|
||||
.. code-block::
|
||||
|
||||
{
|
||||
"name": "${mdns}",
|
||||
"temp": ${temp},
|
||||
"aux_temp": 0,
|
||||
"ext_temp": 0,
|
||||
"temp_unit": "${temp-unit}",
|
||||
"gravity": ${gravity},
|
||||
"gravity_unit": "${gravity-unit}",
|
||||
"pressure": 0,
|
||||
"pressure_unit": "PSI",
|
||||
"ph": 0,
|
||||
"bpm": 0,
|
||||
"comment": "",
|
||||
"beer": "",
|
||||
"battery": ${battery}
|
||||
}
|
||||
|
||||
|
||||
The URL is found under settings.
|
||||
**Option 2** - iSpindle Endpoint
|
||||
|
||||
This opion makes use of the standard http (1 or 2) endpoints in the push section. If you are using SG then the device name needs to end with [SG] or brewfather will assume
|
||||
that the data is in plato. The brewfather iSpindle endpoint has the following format:
|
||||
|
||||
.. code-block::
|
||||
|
||||
http://log.brewfather.net/ispindel?id=<yourid>
|
||||
|
||||
|
||||
Documentation on this can be found under `Brewfather iSpindle Endpoint <https://docs.brewfather.app/integrations/ispindel>`_
|
||||
|
||||
Fermentrack
|
||||
+++++++++++
|
||||
@ -149,7 +190,7 @@ format for the endpoint. Just add you API key after token.
|
||||
"token" : "[API KEY]",
|
||||
"interval": ${sleep-interval},
|
||||
"temperature": ${temp},
|
||||
"temp-units": "${temp-unit}",
|
||||
"temp_units": "${temp-unit}",
|
||||
"gravity": ${gravity},
|
||||
"angle": ${angle},
|
||||
"battery": ${battery},
|
||||
@ -195,3 +236,28 @@ you want to include. The example below sends 5 different values to the channel i
|
||||
"field4": ${battery},
|
||||
"field5": ${rssi}
|
||||
}
|
||||
|
||||
|
||||
Blynk.io
|
||||
++++++++
|
||||
|
||||
Blynk is an IoT service which can be updated via http get. It also has an mobile device that can be used to show the data.
|
||||
|
||||
In order to use this platform you need to create a device which can be used to receive the data. Each device will have a
|
||||
unique token that is used to identify it. You need to use the HTTP GET option (http-3) on the device for this to work. Enter the
|
||||
following URL in the UI. This will allow us to update several data points at once. I usually enter the token in the ``token2`` field
|
||||
so the format template does not contain any sensitive data and it's easier to switch to another device.
|
||||
|
||||
.. code-block::
|
||||
|
||||
http://blynk.cloud/external/api/batch/update
|
||||
|
||||
|
||||
In the format editor you can enter this template which will send 3 values to blynk. You can add as many as you want but make sure
|
||||
these are configured on the device with the correct validation option such as data type and range. The value should be on one line
|
||||
starting with a ``?``. This string will be added to the URL above when doing the post. You can add more values if you want.
|
||||
|
||||
.. code-block::
|
||||
|
||||
?token=${token2}&v1=${temp}&v2=${gravity}&v3=${angle}
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
"http-push2-h1": "Second",
|
||||
"http-push2-h2": "First",
|
||||
"token": "mytoken",
|
||||
"token2": "mytoken2",
|
||||
"http-push3": "http://192.168.1.10/ispindel",
|
||||
"influxdb2-push": "http://192.168.1.10:8086",
|
||||
"influxdb2-org": "hello",
|
||||
"influxdb2-bucket": "spann",
|
||||
@ -37,5 +39,7 @@
|
||||
"angle": 90.93,
|
||||
"gravity": 1.105,
|
||||
"battery": 0.04,
|
||||
"runtime-average": 2.0
|
||||
"runtime-average": 3.0,
|
||||
"ble": "pink",
|
||||
"platform": "esp32"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"app-name": "GravityMon ",
|
||||
"app-ver": "0.0.0",
|
||||
"id": "7376ef",
|
||||
"mdns": "gravmon",
|
||||
"runtime-average": 3.12
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
"id": "7376ef",
|
||||
"http-1": "%7B%22name%22%20%3A%20%22gravmon%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22gravmon%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%2Dunits%22%3A%20%22%24%7Btemp%2Dunit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22rssi%22%3A%20%24%7Brssi%7D%2C%20%22corr%2Dgravity%22%3A%20%24%7Bcorr%2Dgravity%7D%2C%20%22gravity%2Dunit%22%3A%20%22%24%7Bgravity%2Dunit%7D%22%2C%20%22run%2Dtime%22%3A%20%24%7Brun%2Dtime%7D%20%7D",
|
||||
"http-2": "%7B%22name%22%20%3A%20%22gravmon%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22gravmon%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%2Dunits%22%3A%20%22%24%7Btemp%2Dunit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22rssi%22%3A%20%24%7Brssi%7D%2C%20%22corr%2Dgravity%22%3A%20%24%7Bcorr%2Dgravity%7D%2C%20%22gravity%2Dunit%22%3A%20%22%24%7Bgravity%2Dunit%7D%22%2C%20%22run%2Dtime%22%3A%20%24%7Brun%2Dtime%7D%20%7D",
|
||||
"http-3": "%7B%22name%22%20%3A%20%22gravmon%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22gravmon%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%2Dunits%22%3A%20%22%24%7Btemp%2Dunit%7D%22%2C%20%22gravity%22%3A%20%24%7Bgravity%7D%2C%20%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%20%24%7Bbattery%7D%2C%20%22rssi%22%3A%20%24%7Brssi%7D%2C%20%22corr%2Dgravity%22%3A%20%24%7Bcorr%2Dgravity%7D%2C%20%22gravity%2Dunit%22%3A%20%22%24%7Bgravity%2Dunit%7D%22%2C%20%22run%2Dtime%22%3A%20%24%7Brun%2Dtime%7D%20%7D",
|
||||
"influxdb": "measurement%2Chost%3D%24%7Bmdns%7D%2Cdevice%3D%24%7Bid%7D%2Ctemp%2Dformat%3D%24%7Btemp%2Dunit%7D%2Cgravity%2Dformat%3D%24%7Bgravity%2Dunit%7D%20gravity%3D%24%7Bgravity%7D%2Ccorr%2Dgravity%3D%24%7Bcorr%2Dgravity%7D%2Cangle%3D%24%7Bangle%7D%2Ctemp%3D%24%7Btemp%7D%2Cbattery%3D%24%7Bbattery%7D%2Crssi%3D%24%7Brssi%7D%0A",
|
||||
"mqtt": "ispindel%2F%24%7Bmdns%7D%2Ftilt%3A%24%7Bangle%7D%7Cispindel%2F%24%7Bmdns%7D%2Ftemperature%3A%24%7Btemp%7D%7Cispindel%2F%24%7Bmdns%7D%2Ftemp%5Funits%3A%24%7Btemp%2Dunit%7D%7Cispindel%2F%24%7Bmdns%7D%2Fbattery%3A%24%7Bbattery%7D%7Cispindel%2F%24%7Bmdns%7D%2Fgravity%3A%24%7Bgravity%7D%7Cispindel%2F%24%7Bmdns%7D%2Finterval%3A%24%7Bsleep%2Dinterval%7D%7Cispindel%2F%24%7Bmdns%7D%2FRSSI%3A%24%7Brssi%7D%7C"
|
||||
}
|
5
test/push.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"success": false,
|
||||
"enabled": true,
|
||||
"code": -3
|
||||
}
|
@ -2,12 +2,17 @@
|
||||
"id": "7376ef",
|
||||
"angle": 22.4,
|
||||
"gravity": 1.044,
|
||||
"gravity-tempcorr": 1.031,
|
||||
"gravity-format": "G",
|
||||
"temp-c": 12,
|
||||
"temp-f": 32,
|
||||
"sleep-interval": 300,
|
||||
"battery": 3.81,
|
||||
"temp-format": "C",
|
||||
"sleep-mode": false,
|
||||
"rssi": -56
|
||||
"rssi": -56,
|
||||
"app-ver": "0.0.0",
|
||||
"app-build": "gitrev",
|
||||
"mdns": "gravmon",
|
||||
"platform": "esp32",
|
||||
"runtime-average": 3.12
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"index": false,
|
||||
"device": false,
|
||||
"config": false,
|
||||
"calibration": false,
|
||||
"format": false,
|
||||
"test": false,
|
||||
"about": true
|
||||
}
|