Merge pull request #122 from mp-se/dev

Merge 1.2 rc1 into master
This commit is contained in:
Magnus 2022-12-06 21:49:00 +01:00 committed by GitHub
commit a515a164fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1442 additions and 88 deletions

View File

@ -36,7 +36,7 @@ jobs:
git config --global advice.detachedHead false git config --global advice.detachedHead false
- name: Run PlatformIO - name: Run PlatformIO
run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release run: pio run -e gravity-release -e gravity32-release -e gravity32c3-release -e gravity32s2-release -e gravity32c3v1-release -e gravity32lite-release
- uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. https://github.com/marketplace/actions/add-commit
with: with:

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v3
- name: clang format support - name: clang format support
run: | run: |
sudo apt install clang-format cppcheck sudo apt install clang-format cppcheck

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
.vscode/* .vscode/*
*.map *.map
src_docs/_build/* src_docs/_build/*
.env/*
*.pyc

View File

@ -10,7 +10,7 @@
![GravityMon Logo](src_docs/source/images/gravitymon_logo_s.png) ![GravityMon Logo](src_docs/source/images/gravitymon_logo_s.png)
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. From v1.2 you can also use GravityMon for the DIY Floaty Hardware with ESP32 Lite.
Now also works with ESP32 d1 mini, ESP32 c3 mini, ESP32 S2 mini which both are pin compatible with ESP8266. Now also works with ESP32 d1 mini, ESP32 c3 mini, ESP32 S2 mini which both are pin compatible with ESP8266.
@ -28,4 +28,4 @@ If you want to support my work you can do that through these options
Thanks to the following persons for supporting me and this project: Thanks to the following persons for supporting me and this project:
* David Conde * David Conde, @davidconde

31
TEST.md Normal file
View File

@ -0,0 +1,31 @@
# Unit testing - Python Script
I have moved my test scripts into this project now. They are mainly based on python scrips and validate the features from the API's.
Create a virtual environment and install the needed dependecies
```
python -m venv .env
pip install -r requirements.txt
```
Before you runt the script you need to update the IP adress to match the device that you have on your network.
Running the ALL tests
```
cd src/test
python3 -m unittest -v apitests.py -v
```
Running the ONE test
```
cd src/test
python3 -m unittest -v apitests.API.test_status -v
```
# Unit testing - Specific build
I've added a specific build that uses the AUnit (https://github.com/bxparks/AUnit) testing framework so that we can test functions or classes on the device itself. I hope this will simplify the release and testing cycle.
1. Select the build target (gravity-unit)
2. Build/upload the code to an iSpindle device.
3. Check the output from the serial console.

BIN
battery-test-1.2.xlsx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/firmware32c3v1.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -468,7 +468,7 @@
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3" id="ota-hide">
<label for="ota-url" class="col-sm-2 col-form-label">OTA base URL</label> <label for="ota-url" class="col-sm-2 col-form-label">OTA base URL</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="url" maxlength="90" class="form-control" name="ota-url" id="ota-url" placeholder="http://www.local.com/path/" data-bs-toggle="tooltip" title="Base URL to where firmware and version.json file can be found. Needs to end with '/', example: http://www.mysite.com/firmware/"> <input type="url" maxlength="90" class="form-control" name="ota-url" id="ota-url" placeholder="http://www.local.com/path/" data-bs-toggle="tooltip" title="Base URL to where firmware and version.json file can be found. Needs to end with '/', example: http://www.mysite.com/firmware/">
@ -507,9 +507,9 @@
if ( b ) { if ( b ) {
$("#ota-url").val(gravitymonUrl); $("#ota-url").val(gravitymonUrl);
$("#ota-url").prop("disabled", true); $('#ota-hide').hide();
} else { } else {
$("#ota-url").prop("disabled", false); $('#ota-hide').show();
} }
}); });
</script> </script>
@ -966,7 +966,7 @@
if( cfg["ota-url"] == gravitymonUrl) { if( cfg["ota-url"] == gravitymonUrl) {
$("#gravitymon-com").prop( "checked", true ); $("#gravitymon-com").prop( "checked", true );
$("#ota-url").prop("disabled", true); $("#ota-hide").hide();
} }
$("#token").val(cfg["token"]); $("#token").val(cfg["token"]);

File diff suppressed because one or more lines are too long

View File

@ -40,8 +40,8 @@ build_flags =
-DUSER_SSID=\""\"" # =\""myssid\"" -DUSER_SSID=\""\"" # =\""myssid\""
-DUSER_SSID_PWD=\""\"" # =\""mypwd\"" -DUSER_SSID_PWD=\""\"" # =\""mypwd\""
-DCFG_APPVER="\"1.2.0\"" -DCFG_APPVER="\"1.2.0\""
-DCFG_GITREV=\""beta-2\"" #-DCFG_GITREV=\""beta-3\""
#!python script/git_rev.py !python script/git_rev.py
lib_deps = lib_deps =
# Using local copy of these libraries # Using local copy of these libraries
# https://github.com/mp-se/i2cdevlib.git#<document> # https://github.com/mp-se/i2cdevlib.git#<document>
@ -103,6 +103,26 @@ lib_deps =
board = ${common_env_data.board} board = ${common_env_data.board}
build_type = release build_type = release
board_build.filesystem = littlefs board_build.filesystem = littlefs
build_src_filter = +<*> -<../test/tests*.cpp>
[env:gravity-unit]
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
framework = ${common_env_data.framework}
platform = ${common_env_data.platform}
extra_scripts = ${common_env_data.extra_scripts}
build_unflags = ${common_env_data.build_unflags}
build_flags =
${common_env_data.build_flags}
-D LOG_LEVEL=4
lib_deps =
https://github.com/mp-se/incbin # https://github.com/graphitemaster/incbin
https://github.com/bxparks/AUnit#v1.6.1
${common_env_data.lib_deps}
board = ${common_env_data.board}
build_type = release
board_build.filesystem = littlefs
build_src_filter = +<*> -<main.cpp> +<../test/tests*.cpp>
[env:gravity32-release] [env:gravity32-release]
framework = ${common_env_data.framework} framework = ${common_env_data.framework}
@ -143,6 +163,39 @@ platform = ${common_env_data.platform32}
upload_speed = ${common_env_data.upload_speed} upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed} monitor_speed = ${common_env_data.monitor_speed}
extra_scripts = ${common_env_data.extra_scripts} extra_scripts = ${common_env_data.extra_scripts}
build_unflags =
${common_env_data.build_unflags}
build_flags =
-Wl,-Map,output.map
${common_env_data.build_flags}
-DLOG_LEVEL=5
-DCORE_DEBUG_LEVEL=0
-DESP32C3
-DARDUINO_ESP32C3_DEV
#-DUSE_SERIAL_PINS # Use the TX/RX pins for the serial port
lib_deps =
${common_env_data.lib_deps}
${common_env_data.lib_deps32}
lib_ignore =
board = lolin_c3_mini
build_type = release
board_build.partitions = part32.csv
board_build.filesystem = littlefs
board_build.embed_txtfiles =
html/calibration.min.htm
html/config.min.htm
html/firmware.min.htm
html/format.min.htm
html/about.min.htm
html/index.min.htm
html/test.min.htm
[env:gravity32c3v1-release]
framework = ${common_env_data.framework}
platform = ${common_env_data.platform32}
upload_speed = ${common_env_data.upload_speed}
monitor_speed = ${common_env_data.monitor_speed}
extra_scripts = ${common_env_data.extra_scripts}
build_unflags = build_unflags =
${common_env_data.build_unflags} ${common_env_data.build_unflags}
build_flags = build_flags =

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
requests

View File

@ -45,6 +45,12 @@ def after_build(source, target, env):
print( "Copy file : " + source + " -> " + target ) print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target ) shutil.copyfile( source, target )
if name == "gravity32c3v1-release" :
target = dir + "/bin/firmware32c3v1.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin"
print( "Copy file : " + source + " -> " + target )
shutil.copyfile( source, target )
if name == "gravity32s2-release" : if name == "gravity32s2-release" :
target = dir + "/bin/firmware32s2.bin" target = dir + "/bin/firmware32s2.bin"
source = dir + "/.pio/build/" + name + "/firmware.bin" source = dir + "/.pio/build/" + name + "/firmware.bin"

View File

@ -27,9 +27,6 @@ SOFTWARE.
#include <calc.hpp> #include <calc.hpp>
#include <main.hpp> #include <main.hpp>
//
// Use values to derive a formula
//
int createFormula(RawFormulaData &fd, char *formulaBuffer, int createFormula(RawFormulaData &fd, char *formulaBuffer,
int formulaBufferSize, int order) { int formulaBufferSize, int order) {
int noAngles = 0; int noAngles = 0;
@ -118,10 +115,6 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer,
return ERR_FORMULA_INTERNAL; return ERR_FORMULA_INTERNAL;
} }
//
// Calculates gravity according to supplied formula, compatible with
// iSpindle/Fermentrack formula
//
double calculateGravity(double angle, double temp, const char *tempFormula) { double calculateGravity(double angle, double temp, const char *tempFormula) {
const char *formula = myConfig.getGravityFormula(); const char *formula = myConfig.getGravityFormula();
@ -163,12 +156,10 @@ double calculateGravity(double angle, double temp, const char *tempFormula) {
return 0; return 0;
} }
//
// Do a standard gravity temperature correction. This is a simple way to adjust // Do a standard gravity temperature correction. This is a simple way to adjust
// for differnt worth temperatures. This function uses C as temperature. // for differnt worth temperatures. This function uses C as temperature.
// //
// Source: https://homebrewacademy.com/hydrometer-temperature-correction/ // Source: https://homebrewacademy.com/hydrometer-temperature-correction/
//
double gravityTemperatureCorrectionC(double gravitySG, double tempC, double gravityTemperatureCorrectionC(double gravitySG, double tempC,
double calTempC) { double calTempC) {
#if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING)

View File

@ -133,12 +133,6 @@ void setup() {
LOG_PERF_START("main-setup"); LOG_PERF_START("main-setup");
runtimeMillis = millis(); runtimeMillis = millis();
#if LOG_LEVEL == 6 && !defined(MAIN_DISABLE_LOGGING)
// Add a delay so that serial is started.
// delay(3000);
#endif
delay(2000);
// Main startup // Main startup
#if defined(ESP8266) #if defined(ESP8266)
Log.notice(F("Main: Started setup for %s." CR), Log.notice(F("Main: Started setup for %s." CR),

View File

@ -80,9 +80,6 @@ const char mqttFormat[] PROGMEM =
"ispindel/${mdns}/interval:${sleep-interval}|" "ispindel/${mdns}/interval:${sleep-interval}|"
"ispindel/${mdns}/RSSI:${rssi}|"; "ispindel/${mdns}/RSSI:${rssi}|";
//
// Initialize the variables
//
void TemplatingEngine::initialize(float angle, float gravitySG, void TemplatingEngine::initialize(float angle, float gravitySG,
float corrGravitySG, float tempC, float corrGravitySG, float tempC,
float runTime) { float runTime) {
@ -138,10 +135,9 @@ void TemplatingEngine::initialize(float angle, float gravitySG,
#endif #endif
} }
// // the useDefaultTemplate param is there to support unit tests.
// Create the data using defined template. const char* TemplatingEngine::create(TemplatingEngine::Templates idx,
// bool useDefaultTemplate) {
const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
String fname; String fname;
_baseTemplate.reserve(600); _baseTemplate.reserve(600);
@ -169,15 +165,16 @@ const char* TemplatingEngine::create(TemplatingEngine::Templates idx) {
break; break;
} }
// TODO: Add code to load templates from disk if they exist. if (!useDefaultTemplate) {
File file = LittleFS.open(fname, "r"); File file = LittleFS.open(fname, "r");
if (file) { if (file) {
char buf[file.size() + 1]; char buf[file.size() + 1];
memset(&buf[0], 0, file.size() + 1); memset(&buf[0], 0, file.size() + 1);
file.readBytes(&buf[0], file.size()); file.readBytes(&buf[0], file.size());
_baseTemplate = String(&buf[0]); _baseTemplate = String(&buf[0]);
file.close(); file.close();
Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str()); Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str());
}
} }
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6

View File

@ -200,7 +200,8 @@ class TemplatingEngine {
} }
void initialize(float angle, float gravitySG, float corrGravitySG, void initialize(float angle, float gravitySG, float corrGravitySG,
float tempC, float runTime); float tempC, float runTime);
const char *create(TemplatingEngine::Templates idx); const char *create(TemplatingEngine::Templates idx,
bool useDefaultTemplate = false);
}; };
#endif // SRC_TEMPLATING_HPP_ #endif // SRC_TEMPLATING_HPP_

View File

@ -58,9 +58,6 @@ void TempSensor::setup() {
#endif #endif
} }
//
// Retrieving value from sensor, value is in Celcius
//
float TempSensor::getValue(bool useGyro) { float TempSensor::getValue(bool useGyro) {
if (useGyro) { if (useGyro) {
// When using the gyro temperature only the first read value will be // When using the gyro temperature only the first read value will be

View File

@ -430,6 +430,8 @@ bool WifiConnection::checkFirmwareVersion() {
} }
http.end(); http.end();
if (_newFirmware) Log.notice(F("WIFI: Found new version." CR));
#if LOG_LEVEL == 6 #if LOG_LEVEL == 6
Log.verbose(F("WIFI: OTA found new version %s." CR), Log.verbose(F("WIFI: OTA found new version %s." CR),
_newFirmware ? "true" : "false"); _newFirmware ? "true" : "false");

View File

@ -30,9 +30,10 @@ In the platformio config there are 3 targets defined
* gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled. * gravity-debug; Maximum logging for trouble shooting, deep sleep is disabled.
* gravity-release; Standard release * gravity-release; Standard release
* gravity32-release: Version for ESP32 mini. * gravity32-release: Version for ESP32 mini.
* gravity32-c3-release: Version for ESP32 C3 mini. * gravity32c3-release: Version for ESP32 C3 mini v2.1+.
* gravity32-s2-release: Version for ESP32 S2 mini. * gravity32c3v1-release: Version for ESP32 C3 mini v1.0.
* gravity32-lite-release: Version for ESP32 lite (Floaty hardware). * gravity32s2-release: Version for ESP32 S2 mini.
* gravity32lite-release: Version for ESP32 lite (Floaty hardware).
.. warning:: .. warning::
The debug target can be unstable and crash the device under certain circumstanses. Excessive logging to the serial port can cause corruption and crashes. The debug target can be unstable and crash the device under certain circumstanses. Excessive logging to the serial port can cause corruption and crashes.

View File

@ -5,8 +5,8 @@ Hardware
I'm not a hardware designer so I would recommend the following resources for more in depth information on this topic. I'm not a hardware designer so I would recommend the following resources for more in depth information on this topic.
* `Cherry Philip Hardware design, <https://github.com/cherryphilip74/iSpindel-PCB>`_ * `Cherry Philip Hardware design <https://github.com/cherryphilip74/iSpindel-PCB>`_
* `OpenSource Distilling, <https://www.opensourcedistilling.com/ispindel>`_ * `OpenSource Distilling <https://www.opensourcedistilling.com/ispindel>`_
iSpindle based on esp8266 iSpindle based on esp8266
========================= =========================
@ -25,24 +25,20 @@ Schema for esp8266 build
:width: 700 :width: 700
:alt: Schema esp8266 :alt: Schema esp8266
iSpindle based on esp32 iSpindle based on esp32
======================= =======================
I've experimented with porting my software version to esp32 and this is a selection of options i have been testing. Gravitymon supports a number of ESP32 boards that offers bluetooth support.
.. warning::
Work to support esp32s2 is still ongoing, ESP32-mini and ESP32c3-mini is confirmed to be working. However I would recommend the C3 variant.
.. image:: images/esp32_hardware.jpg .. image:: images/esp32_hardware.jpg
:width: 500 :width: 500
:alt: iSpindle esp32 hardware options :alt: iSpindle esp32 hardware options
* esp32 mini, this was the first board i tried which is a smaller form factor of the first generetion esp32 with 2 cores. Slow on connecting to wifi is the main downside. * esp32d1 mini, this was the first board i tried which is a smaller form factor of the first generetion esp32 with 2 cores. Slow on connecting to wifi is the main downside.
* esp32c3 mini, a newer version based on the latest risc v7 architecture, is seen as the replacement for the esp8266 with bluetooth support. * esp32c3 mini, a newer version based on the latest risc v7 architecture, is seen as the replacement for the esp8266 with bluetooth support. Don't buy v1.0 since that has a faulty wifi antenna.
* esp32s2 mini, similar to the c3 board but without bluetooth support. * esp32s2 mini, similar to the c3 board but without bluetooth support.
It's possible to use this PCB and mount an ESP32 mini on top of that (c3 and s2 are prefered). The esp32 mini is a larger formfactor and can be hard to fit into the tube. It's possible to use this PCB and mount an ESP32 mini on top of that (c3 or s2 are prefered). The esp32 d1 mini is a larger formfactor and can be hard to fit into the tube.
.. note:: .. note::
You need to add a resistor between A0 (Analog PIN) and ground of 470k. The reason is that the esp8266 has a build in resistor for a voltage divider You need to add a resistor between A0 (Analog PIN) and ground of 470k. The reason is that the esp8266 has a build in resistor for a voltage divider
@ -52,7 +48,7 @@ It's possible to use this PCB and mount an ESP32 mini on top of that (c3 and s2
ESP32c3 mini ESP32c3 mini
++++++++++++ ++++++++++++
This is model is now fully supported by gravitymon. This is model is fully supported by gravitymon.
.. image:: images/ispindel_esp32c3.jpg .. image:: images/ispindel_esp32c3.jpg
:width: 500 :width: 500
@ -67,7 +63,7 @@ Here is an image of where I added the resistor for the voltage divider.
ESP32s2 mini ESP32s2 mini
++++++++++++ ++++++++++++
This is model is now fully supported by gravitymon. Same setup as for ESP32C3 mini. This is model is fully supported by gravitymon. Same setup as for ESP32C3 mini.
ESP32 d1 mini ESP32 d1 mini
+++++++++++++ +++++++++++++
@ -82,7 +78,7 @@ Schema for esp32 build
++++++++++++++++++++++ ++++++++++++++++++++++
.. note:: .. note::
This schema assumes that an ESP32 D1 Mini (pin compatible with ESP8266 D1 Mini is used). The ESP32 has two rows of pins but This schema assumes that an ESP32 d1 mini (pin compatible with ESP8266). The ESP32 has two rows of pins but
only the inner row is used. The main difference is the added resistor R3 so we get a voltage divider for measuring battery. only the inner row is used. The main difference is the added resistor R3 so we get a voltage divider for measuring battery.
The ESP8266 has a built in resistor thats not visible on the schema and this acts as a voltage divider. The ESP8266 has a built in resistor thats not visible on the schema and this acts as a voltage divider.

View File

@ -7,7 +7,7 @@ Welcome to GravityMon
##################### #####################
.. note:: .. note::
This documentation reflects **v1.2 - beta 2**. Last updated 2022-11-19 This documentation reflects **v1.2.0**. Last updated 2022-12-06
What is GravityMon? What is GravityMon?
-------------------- --------------------
@ -39,8 +39,8 @@ My approach to this software is a little different from that the original iSpind
The github repository can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_ The github repository can be found here; `GravityMon on Github <https://github.com/mp-se/gravitymon>`_
.. note:: .. note::
I don't take responsibility for any errors or issues caused by the software. The software is provided as-is. I will however I don't take responsibility for any errors or issues caused by the software or hardware.
try my best to fix issues that might occur. The software is provided as-is. I will however try my best to fix issues that might occur.
I have tested this software on 40+ brews with good results. I have tested this software on 40+ brews with good results.

View File

@ -45,8 +45,8 @@ In the /bin directory you will find 4 different firmware builds;
* **firmware32c3.bin** * **firmware32c3.bin**
This is the release build for an ESP32c3-mini variant. When flashing an ESP32 you also need the *partition32c3.bin* file that outlines the flash memory structure. Due to This is the release build for an ESP32c3-mini variant v2.1 or newer. When flashing an ESP32 you also need the *partition32c3.bin* file that outlines the flash memory structure. Due to
the size of the firmware we are using a custom partition setup. the size of the firmware we are using a custom partition setup. If you have a v1.0 board use the firmware32c3v1.bin instead.
* **firmware32s2.bin** * **firmware32s2.bin**

View File

@ -3,40 +3,31 @@
Releases Releases
######## ########
v1.2.0 - beta2 v1.2.0
============== ======
Features Features
++++++++ ++++++++
* Added function to calcualate voltage factor based on measured value. * Added function to calcualate voltage factor based on measured value.
* Updated battery estimation for the various esp32 boards. * Updated battery estimation for the various esp32 boards.
* Added support for the ESP32 lite board which is used in the Floaty Hydrometer variant. * Added support for the ESP32 lite board which is used in the Floaty Hydrometer variant.
Documentation
+++++++++++++
* Added section about the Floaty hardware.
v1.2.0 - beta1
==============
* Changes to 1.2.0 only affect the ESP32 hardware, the ESP8266 is the same as for v1.1.1
Features
++++++++
* Added support for the ESP32 C3 mini board * Added support for the ESP32 C3 mini board
* Added support for the ESP32 S2 mini board * Added support for the ESP32 S2 mini board
* Serial output is written to TX/RX pins instead of the USB connection for the ESP32c3. This way the serial console can be viewed when running on battery power. * Serial output is written to TX/RX pins instead of the USB connection for the ESP32c3. This way the serial console can be viewed when running on battery power.
* Merged in unit tests and api tests into this project
* Added option to download firmware updates from https://www.gravitymon.com
Documentation
+++++++++++++
* Added section about the Floaty hardware
* Fixed schema errors in hardware section and linked PCB options
* Updated hardware section with options for ESP32 boards
* Updated installation instructions.
Issues adressed Issues adressed
++++++++++++++++ ++++++++++++++++
* BUG: The first portion of a format template was lost when doing conversion. * BUG: The first portion of a format template was lost when doing conversion.
Documentation
+++++++++++++
* Updated hardware section with options for ESP32 boards
* Updated installation instructions.
v1.1.1 v1.1.1
====== ======
* BUG: The text before the first variable was missed in the conversion of a format template. * BUG: The text before the first variable was missed in the conversion of a format template.

643
test/apitests.py Normal file
View File

@ -0,0 +1,643 @@
import unittest, requests, json, time
ver = "1.2.0"
#host = "192.168.1.195"
#id = "6ac6f6"
host = "192.168.1.106"
id = "3045f4"
# python3 -m unittest -v apitests.API.test_bug_79
# python3 -m unittest -v apitests
def call_api_post( path, json ):
url = "http://" + host + path
return requests.post( url, data=json )
def call_api_get( path ):
url = "http://" + host + path
return requests.get( url )
class API(unittest.TestCase):
# Do factory reset for testing
def test_factory(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
r = call_api_get( "/api/factory?id=" + j["id"])
time.sleep(4)
# Check that all parameters exist
def test_status(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["wifi-ssid"], "@home")
self.assertNotEqual(j["angle"], 0)
self.assertNotEqual(j["gravity"], 2)
self.assertNotEqual(j["temp-c"], 0)
self.assertNotEqual(j["temp-f"], 0)
self.assertNotEqual(j["battery"], 0)
self.assertNotEqual(j["temp-format"], "")
self.assertNotEqual(j["gravity-format"], "")
self.assertNotEqual(j["sleep-mode"], True)
self.assertNotEqual(j["rssi"], 0)
self.assertNotEqual(j["app-ver"], "0.0.0")
self.assertNotEqual(j["app-build"], "test")
self.assertNotEqual(j["mdns"], "")
self.assertNotEqual(j["platform"], "")
self.assertNotEqual(j["runtime-average"], -1)
# Check that all parameters exist
def test_config_1(self):
j = { "id": id, "http-push": "https://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass", "http-push-h1": "header1", "http-push-h2": "header2", "http-push2-h1": "header1(2)", "http-push2-h2": "header2(2)" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["http-push-h1"], "header1")
self.assertEqual(j["http-push-h2"], "header2")
self.assertEqual(j["http-push2-h1"], "header1(2)")
self.assertEqual(j["http-push2-h2"], "header2(2)")
# Check that all parameters exist
def test_config_2(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "C", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
def test_config_3(self):
j = { "id": id, "http-push": "https://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_config_4(self):
j = { "id": id, "ota-url": "http://ota.url/path", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "on", "ble": "color" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["ble"], "color")
self.assertEqual(j["ota-url"], "http://ota.url/path")
self.assertEqual(j["voltage-factor"], 1.55)
self.assertEqual(j["temp-adjustment-value"], -2)
self.assertEqual(j["gyro-temp"], True)
# These are read only here, just checking them.
self.assertNotEqual(j["gyro-calibration-data"]["ax"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["ay"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["az"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gx"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gy"], 0.0001)
self.assertNotEqual(j["gyro-calibration-data"]["gz"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a1"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a2"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a3"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a4"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a5"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a6"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a7"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a8"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a9"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["a10"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g1"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g2"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g3"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g4"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g5"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g6"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g7"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g8"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g9"], 0.0001)
self.assertNotEqual(j["formula-calculation-data"]["g10"], 0.0001)
self.assertNotEqual(j["angle"], 0)
self.assertNotEqual(j["gravity"], -10)
self.assertNotEqual(j["battery"], 0)
def test_config_5(self):
j = { "id": id, "gravity-formula": "my-formula", "gravity-temp-adjustment": "on", "gravity-format": "G" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["gravity-format"], "G")
self.assertEqual(j["gravity-formula"], "my-formula")
self.assertEqual(j["gravity-temp-adjustment"], True)
def test_config_6(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "F", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["mdns"], "gravmon")
self.assertEqual(j["wifi-ssid"], "@home")
self.assertEqual(j["wifi-pass"], "") # Should not be displayed in API
self.assertEqual(j["temp-format"], "F")
self.assertEqual(j["sleep-interval"], 300)
def test_config_7(self):
j = { "id": id, "ota-url": "", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "off", "ble": "blue" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["ble"], "blue")
self.assertEqual(j["ota-url"], "")
self.assertEqual(j["voltage-factor"], 1.55)
self.assertEqual(j["temp-adjustment-value"], -2)
self.assertEqual(j["gyro-temp"], False)
def test_config_8(self):
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-format"], "P")
self.assertEqual(j["gravity-temp-adjustment"], False)
self.assertEqual(j["gravity-formula"], "")
def test_config_9(self):
j = { "id": id, "gravity-temp-adjustment": "on" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-temp-adjustment"], True)
j = { "id": id } # No checkbox tag should set it to false.
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-temp-adjustment"], False)
def test_config_A(self):
j = { "id": id, "gyro-temp": "on" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-temp"], True)
j = { "id": id } # No checkbox tag should set it to false.
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-temp"], False)
# Check formula api (sg mode)
def test_formula_sg_1(self):
# Ensure we have SG defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "G" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
def test_formula_sg_2(self):
# Fails due to wrong id
j = { "id": "wrong", "g1": 0, "g2": 1, "g3": 1.02, "g4": 1.0333, "g5": 1.00011, "g6": 1, "g7": 1, "g8": 1, "g9": 1, "g10": 1, "a1": 0, "a2": 25, "a3": 25.5, "a4": 25.55, "a5": 25.555, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertNotEqual(r.status_code, 200)
# Fails due to to few values
j = { "id": id, "g1": 0, "g2": 1, "g3": 1.02, "g4": 1.0333, "g5": 1.00011, "g6": 1, "g7": 1, "g8": 1, "g9": 1, "g10": 1, "a1": 0, "a2": 25, "a3": 0, "a4": 0, "a5": 0, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-formula"], "")
self.assertEqual(j["error"], "Not enough values to create formula, need at least 3 angles.")
def test_formula_sg_3(self):
# Check a simple formula
j = { "id": id, "g1": 1.0, "g2": 1.01, "g3": 1.02, "g4": 1.03, "g5": 1.04, "g6": 1.05, "g7": 1.06, "g8": 1.07, "g9": 1.08, "g10": 1.1, "a1": 25, "a2": 30, "a3": 35, "a4": 40, "a5": 45, "a6": 50, "a7": 55, "a8": 60, "a9": 65, "a10": 70, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gravity-formula"], "0.00000909*tilt^2+0.00124545*tilt+0.96445455")
# Check formula api (plato mode)
def test_formula_plato_1(self):
# Ensure we have Plato defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
def test_formula_plato_2(self):
# Fails due to to few values
j = { "id": id, "g1": 0, "g2": 3, "g3": 5.3, "g4": 7.44, "g5": 8.555, "g6": 9, "g7": 9.1, "g8": 9.2, "g9": 9.3, "g10": 9.4, "a1": 0, "a2": 25, "a3": 25.5, "a4": 25.55, "a5": 25.555, "a6": 35, "a7": 36, "a8": 37, "a9": 38, "a10": 39, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["g1"], 0)
self.assertEqual(j["g2"], 3)
self.assertEqual(j["g3"], 5.3)
self.assertEqual(j["g4"], 7.4)
self.assertEqual(j["g5"], 8.6)
self.assertEqual(j["g6"], 9)
self.assertEqual(j["g7"], 9.1)
self.assertEqual(j["g8"], 9.2)
self.assertEqual(j["g9"], 9.3)
self.assertEqual(j["g10"], 9.4)
self.assertEqual(j["a1"], 0)
self.assertEqual(j["a2"], 25)
self.assertEqual(j["a3"], 25.5)
self.assertEqual(j["a4"], 25.55)
self.assertEqual(j["a5"], 25.56)
self.assertEqual(j["a6"], 35)
self.assertEqual(j["a7"], 36)
self.assertEqual(j["a8"], 37)
self.assertEqual(j["a9"], 38)
self.assertEqual(j["a10"], 39)
self.assertEqual(j["gravity-format"], "P")
self.assertEqual(j["gravity-formula"], "-0.00012155*tilt^2+0.00874785*tilt+0.88003318")
self.assertEqual(j["error"], "")
def test_formula_plato_3(self):
j = { "id": id, "g1": 0, "g2": 3, "g3": 0, "g4": 0, "g5": 0, "g6": 0, "g7": 0, "g8": 0, "g9": 0, "g10": 0, "a1": 0, "a2": 25, "a3": 0, "a4": 0, "a5": 0, "a6": 0, "a7": 0, "a8": 0, "a9": 0, "a10": 0, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
# Checks that values from last call was stored
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], "Not enough values to create formula, need at least 3 angles.")
def test_formula_plato_4(self):
# Ensure we have Plato defined as gravity
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "P" }
r = call_api_post("/api/config/gravity", j)
self.assertEqual(r.status_code, 200)
# Check a simple formula
j = { "id": id, "g1": 1.0, "g2": 1.1, "g3": 1.2, "g4": 1.3, "g5": 1.4, "g6": 1.5, "g7": 1.6, "g8": 1.7, "g9": 1.8, "g10": 1.9, "a1": 25, "a2": 30, "a3": 35, "a4": 40, "a5": 45, "a6": 50, "a7": 55, "a8": 60, "a9": 65, "a10": 70, "gravity-formula": "ThisShouldChange" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
#self.assertEqual(j["gravity-formula"], "0.00000001*tilt^2+0.00007752*tilt+1.00193428") # 1.6 max deviation
self.assertEqual(j["gravity-formula"], "-0.00000352*tilt^2+0.00045454*tilt+0.99231483") # 3.0 max deviation
# Check format api
def test_pushtest_1(self):
j = { "id": id, "http-push": "http://push.me", "token": "mytoken", "token2": "mytoken2", "http-push2": "http://push.me", "http-push3": "http://push.me", "brewfather-push": "http://push.me", "influxdb2-push": "http://influx.db", "influxdb2-org": "my-org",
"influxdb2-bucket": "my-bucket", "influxdb2-auth": "my-secret", "mqtt-push": "mqtt.com", "mqtt-port": 1883, "mqtt-user": "my-user",
"mqtt-pass": "my-pass", "http-push-h1": "header1", "http-push-h2": "header2", "http-push2-h1": "header1(2)", "http-push2-h2": "header2(2)" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_pushtest_2(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-1" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_3(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-2" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_4(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-3" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_5(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=influxdb" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -1)
def test_pushtest_6(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=mqtt" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], False)
self.assertEqual(j["enabled"], True)
self.assertEqual(j["code"], -3)
# Check format api
def test_push_1(self):
r = call_api_get( "/api/factory?id=" + id)
time.sleep(4)
# Note: The endpoint test.php does not validate the payload, it only accepts the request and return 200.
j = { "id": id, "http-push": "http://www.allerum.net/test.php", "http-push2": "http://www.allerum.net/test.php", "http-push3": "http://www.allerum.net/test.php", "mqtt-push": "192.168.1.16", "mqtt-port": 1883, "mqtt-user": "", "mqtt-pass": "" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def test_push_2(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-1" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_3(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-2" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_4(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=http-3" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_5(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=mqtt" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["success"], True)
def test_push_6(self):
r = call_api_get( "/api/test/push?id=" + id + "&format=influx" )
# TODO: Figure out how to test the influx db setup. Create my own mockup ?
# Check format api
def test_format_1(self):
j = { "id": id, "http-1": "one" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_2(self):
j = { "id": id, "http-2": "two" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_3(self):
j = { "id": id, "http-3": "five" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_4(self):
j = { "id": id, "influxdb": "three" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_5(self):
j = { "id": id, "mqtt": "four" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_6(self):
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
self.assertEqual(j["http-1"], "one")
self.assertEqual(j["http-2"], "two")
self.assertEqual(j["http-3"], "five")
self.assertEqual(j["influxdb"], "three")
self.assertEqual(j["mqtt"], "four")
def test_format_7(self):
j = { "id": id, "http-1": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_8(self):
j = { "id": id, "http-2": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_9(self):
j = { "id": id, "http-3": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_A(self):
j = { "id": id, "influxdb": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_B(self):
j = { "id": id, "mqtt": "" }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
def test_format_C(self):
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
#print(j["http-1"])
#print(j["http-2"])
#print(j["http-3"])
#print(j["influxdb"])
#print(j["mqtt"])
self.assertEqual(j["http-1"], "%7B%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%5Funits%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")
self.assertEqual(j["http-2"], "%7B%22name%22%20%3A%20%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%20%22%24%7Bid%7D%22%2C%20%22token%22%20%3A%20%22%24%7Btoken%7D%22%2C%20%22interval%22%3A%20%24%7Bsleep%2Dinterval%7D%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22temp%5Funits%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")
self.assertEqual(j["http-3"], "%3Fname%3D%24%7Bmdns%7D%26id%3D%24%7Bid%7D%26token%3D%24%7Btoken2%7D%26interval%3D%24%7Bsleep%2Dinterval%7D%26temperature%3D%24%7Btemp%7D%26temp%2Dunits%3D%24%7Btemp%2Dunit%7D%26gravity%3D%24%7Bgravity%7D%26angle%3D%24%7Bangle%7D%26battery%3D%24%7Bbattery%7D%26rssi%3D%24%7Brssi%7D%26corr%2Dgravity%3D%24%7Bcorr%2Dgravity%7D%26gravity%2Dunit%3D%24%7Bgravity%2Dunit%7D%26run%2Dtime%3D%24%7Brun%2Dtime%7D")
self.assertEqual(j["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")
self.assertEqual(j["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")
# Toggle sleep mode
def toggle_sleepmode_1(self):
j = { "id": id, "sleep-mode": "on" }
r = call_api_post( "/api/status/sleepmode" )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["sleep-mode"], True)
def toggle_sleepmode_2(self):
j = { "id": id, "sleep-mode": "off" }
r = call_api_post( "/api/status/sleepmode" )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["sleep-mode"], False)
# Clear setting
def default_settings_1(self):
j = { "id": id, "mdns": "gravmon", "temp-format": "C", "sleep-interval": 300 }
r = call_api_post( "/api/config/device", j )
self.assertEqual(r.status_code, 200)
def default_settings_2(self):
j = { "id": id, "token": "", "token2": "", "http-push": "", "http-push2": "", "http-push3": "", "influxdb2-push": "", "influxdb2-org": "", "influxdb2-bucket": "",
"influxdb2-auth": "", "mqtt-push": "", "mqtt-port": 1883, "mqtt-user": "", "mqtt-pass": "" }
r = call_api_post( "/api/config/push", j )
self.assertEqual(r.status_code, 200)
def default_settings_3(self):
j = { "id": id, "ota-url": "", "voltage-factor": 1.55, "temp-adjustment-value": -2, "gyro-temp": "off" }
r = call_api_post( "/api/config/hardware", j )
self.assertEqual(r.status_code, 200)
def default_settings_4(self):
j = { "id": id, "gravity-formula": "", "gravity-temp-adjustment": "off", "gravity-format": "G" }
r = call_api_post( "/api/config/gravity", j )
self.assertEqual(r.status_code, 200)
# Check advanced
def test_advanced_config_1(self):
r = call_api_get( "/api/status" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["id"], id)
r = call_api_get( "/api/factory?id=" + j["id"])
time.sleep(4)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
#print(j)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-read-count"], 50)
self.assertEqual(j["tempsensor-resolution"], 9)
self.assertEqual(j["gyro-moving-threashold"], 500)
self.assertEqual(j["formula-max-deviation"], 3)
self.assertEqual(j["formula-calibration-temp"], 20)
self.assertEqual(j["wifi-portal-timeout"], 120)
self.assertEqual(j["wifi-connect-timeout"], 20)
self.assertEqual(j["ignore-low-angles"], False)
self.assertEqual(j["formula-calibration-temp"], 20)
self.assertEqual(j["int-http1"], 0)
self.assertEqual(j["int-http2"], 0)
self.assertEqual(j["int-http3"], 0)
self.assertEqual(j["int-influx"], 0)
self.assertEqual(j["int-mqtt"], 0)
def test_advanced_config_2(self):
j = { "id": id, "gyro-read-count": 51, "tempsensor-resolution": 10, "gyro-moving-threashold": 501, "formula-max-deviation": 1.7, "ignore-low-angles": "on",
"formula-calibration-temp": 21, "wifi-portal-timeout": 121, "wifi-connect-timeout": 21, "int-http1": 1, "int-http2": 2, "int-http3": 3, "int-influx": 4, "int-mqtt": 5 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["gyro-read-count"], 51)
self.assertEqual(j["tempsensor-resolution"], 10)
self.assertEqual(j["gyro-moving-threashold"], 501)
self.assertEqual(j["formula-max-deviation"], 1.7)
self.assertEqual(j["formula-calibration-temp"], 21)
self.assertEqual(j["wifi-portal-timeout"], 121)
self.assertEqual(j["wifi-connect-timeout"], 21)
self.assertEqual(j["ignore-low-angles"], True)
self.assertEqual(j["int-http1"], 1)
self.assertEqual(j["int-http2"], 2)
self.assertEqual(j["int-http3"], 3)
self.assertEqual(j["int-influx"], 4)
self.assertEqual(j["int-mqtt"], 5)
def test_advanced_config_3(self):
j = { "id": id, "ignore-low-angles": "on" }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["ignore-low-angles"], True)
j = { "id": id } # Skipping checkbox variable should set it to false
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/advanced" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["ignore-low-angles"], False)
def test_bug_71(self):
format = "gm%3A%7B%22name%22%3A%22%24%7Bmdns%7D%22%2C%20%22ID%22%3A%22%24%7Bid%7D%22%2C%20%22temperature%22%3A%20%24%7Btemp%7D%2C%20%22gravity%22%3A%24%7Bgravity%7D%2C%22angle%22%3A%20%24%7Bangle%7D%2C%20%22battery%22%3A%24%7Bbattery%7D%2C%20%22rssi%22%3A%20%24%7Brssi%7D%7D"
j = { "id": id, "mqtt": format }
r = call_api_post( "/api/config/format", j )
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/config/format" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["mqtt"], format)
def test_bug_79(self):
j = { "id": id, "formula-max-deviation": 1.7 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
j = { "id": id, "g1": 1.0, "g2": 1.009, "g3": 1.014, "g4": 1.027, "g5": 1.037, "g6": 1.042, "g7": 1.051, "g8": 1.060, "g9": 1.073, "g10": 1.078, "a1": 23.52, "a2": 26.47, "a3": 29.87, "a4": 33.43, "a5": 38.16, "a6": 40.6, "a7": 45.85, "a8": 50.12, "a9": 56.55, "a10": 59.078, "gravity-formula": "" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], 'Unable to find an accurate formula based on input, check error log and graph below.')
j = { "id": id, "formula-max-deviation": 4 }
r = call_api_post( "/api/config/advanced", j )
self.assertEqual(r.status_code, 200)
j = { "id": id, "g1": 1.0, "g2": 1.009, "g3": 1.014, "g4": 1.027, "g5": 1.037, "g6": 1.042, "g7": 1.051, "g8": 1.060, "g9": 1.073, "g10": 1.078, "a1": 23.52, "a2": 26.47, "a3": 29.87, "a4": 33.43, "a5": 38.16, "a6": 40.6, "a7": 45.85, "a8": 50.12, "a9": 56.55, "a10": 59.078, "gravity-formula": "" }
r = call_api_post("/api/formula", j)
self.assertEqual(r.status_code, 200)
r = call_api_get( "/api/formula" )
j = json.loads(r.text)
self.assertEqual(r.status_code, 200)
self.assertEqual(j["error"], '')
if __name__ == '__main__':
unittest.main()

View File

@ -3,7 +3,7 @@
"wifi-ssid2": "ssid 2", "wifi-ssid2": "ssid 2",
"mdns": "gravmon3", "mdns": "gravmon3",
"id": "7376ef", "id": "7376ef",
"ota-url": "http://192.168.1.100:80/firmware/gravmon/", "ota-url": "https://www.gravitymon.com/firmware/",
"temp-format": "C", "temp-format": "C",
"http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry", "http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry",
"http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=", "http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=",

82
test/tests.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
using aunit::Printer;
using aunit::TestRunner;
using aunit::Verbosity;
/*
// Boolean
assertEqual(a, b)
assertNotEqual(a, b)
assertLess(a, b)
assertMore(a, b)
assertLessOrEqual(a, b)
assertMoreOrEqual(a, b)
// String
assertStringCaseEqual(a, b)
assertStringCaseNotEqual(a, b)
assertNear(a, b, error)
assertNotNear(a, b, error)
checkTestDone(name)
checkTestNotDone(name)
checkTestPass(name)
checkTestNotPass(name)
checkTestFail(name)
checkTestNotFail(name)
checkTestSkip(name)
checkTestNotSkip(name)
checkTestExpire(name) [*]
checkTestNotExpire(name) [*]
assertTestDone(name)
assertTestNotDone(name)
assertTestPass(name)
assertTestNotPass(name)
assertTestFail(name)
assertTestNotFail(name)
assertTestSkip(name)
assertTestNotSkip(name)
assertTestExpire(name) [*]
assertTestNotExpire(name) [*]
*/
void setup() {
Serial.begin(115200);
Serial.println("Gravitymon - Unit Test Build");
delay(2000);
Printer::setPrinter(&Serial);
// TestRunner::setVerbosity(Verbosity::kAll);
}
void loop() {
TestRunner::run();
delay(10);
}
// EOF

31
test/tests_ble.cpp Normal file
View File

@ -0,0 +1,31 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the BLE module.
// EOF

67
test/tests_calc.cpp Normal file
View File

@ -0,0 +1,67 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <calc.hpp>
#include <helper.hpp>
// TODO: Add more test cases to explore formula creation error conditions when
// values are out of bounds
// TODO: Add more test cases to check order 3 + 4 formula creation as well.
test(calc_createFormula1) {
char buffer[100];
RawFormulaData fd = {
{0.0, 25.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 1.02, 1.033, 1.00011, 1.0, 1.0, 1.0, 1.0, 1.0}};
int i = createFormula(fd, &buffer[0], sizeof(buffer), 2);
assertEqual(i, ERR_FORMULA_NOTENOUGHVALUES);
}
test(calc_createFormula2) {
char buffer[100];
RawFormulaData fd = {
{25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70.0},
{1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.1}};
int i = createFormula(fd, &buffer[0], sizeof(buffer), 2);
assertEqual(i, 0);
assertEqual(&buffer[0], "0.00000909*tilt^2+0.00124545*tilt+0.96445455");
}
test(calc_calculateGravity) {
const char* formula = "0.00000909*tilt^2+0.00124545*tilt+0.96445455";
double g = calculateGravity(30, 20, formula);
float v1 = reduceFloatPrecision(g, 2);
float v2 = 1.01;
assertEqual(v1, v2);
}
test(calc_gravityTemperatureCorrectionC) {
double g = gravityTemperatureCorrectionC(1.02, 45.0, 20.0);
float v1 = reduceFloatPrecision(g, 2);
float v2 = 1.03;
assertEqual(v1, v2);
}
// EOF

73
test/tests_config.cpp Normal file
View File

@ -0,0 +1,73 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <config.hpp>
test(config_defaultValues) {
assertEqual(myConfig.getGravityFormat(), 'G');
assertEqual(myConfig.getTempFormat(), 'C');
assertEqual(myConfig.getSleepInterval(), 900);
assertEqual(myConfig.getTempSensorAdjC(), 0.0);
float f = 4.15;
assertEqual(myConfig.getVoltageConfig(), f);
}
test(config_advDefaultValues) {
assertEqual(myAdvancedConfig.getDefaultCalibrationTemp(), 20.0);
assertEqual(myAdvancedConfig.getGyroReadCount(), 50);
assertEqual(myAdvancedConfig.getGyroReadDelay(), 3150);
assertEqual(myAdvancedConfig.getGyroSensorMovingThreashold(), 500);
assertEqual(myAdvancedConfig.getMaxFormulaCreationDeviation(), 3.0);
assertEqual(myAdvancedConfig.getPushIntervalHttp1(), 0);
assertEqual(myAdvancedConfig.getPushIntervalHttp2(), 0);
assertEqual(myAdvancedConfig.getPushIntervalHttp3(), 0);
assertEqual(myAdvancedConfig.getPushIntervalMqtt(), 0);
assertEqual(myAdvancedConfig.getPushIntervalInflux(), 0);
assertEqual(myAdvancedConfig.getPushTimeout(), 10);
assertEqual(myAdvancedConfig.getTempSensorResolution(), 9);
assertEqual(myAdvancedConfig.getWifiConnectTimeout(), 20);
assertEqual(myAdvancedConfig.getWifiPortalTimeout(), 120);
assertEqual(myAdvancedConfig.isIgnoreLowAnges(), false);
}
test(config_tempFormat) {
myConfig.setTempFormat('F');
assertEqual(myConfig.getTempFormat(), 'F');
myConfig.setTempFormat('C');
assertEqual(myConfig.getTempFormat(), 'C');
myConfig.setTempFormat('X');
assertEqual(myConfig.getTempFormat(), 'C');
}
test(config_gravityFormat) {
myConfig.setGravityFormat('P');
assertEqual(myConfig.getGravityFormat(), 'P');
myConfig.setGravityFormat('G');
assertEqual(myConfig.getGravityFormat(), 'G');
myConfig.setGravityFormat('X');
assertEqual(myConfig.getGravityFormat(), 'G');
}
// EOF

44
test/tests_gyro.cpp Normal file
View File

@ -0,0 +1,44 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <gyro.hpp>
test(gyro_connectGyro) {
myGyro.setup();
assertEqual(myGyro.isConnected(), true);
}
test(gyro_readGyro) {
myGyro.setup();
assertEqual(myGyro.read(), true);
}
test(gyro_readGyroTemp) {
myGyro.setup();
assertNotEqual(myGyro.getInitialSensorTempC(), -273.0);
assertNotEqual(myGyro.getSensorTempC(), -273.0);
}
// EOF

93
test/tests_helper.cpp Normal file
View File

@ -0,0 +1,93 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <helper.hpp>
BatteryVoltage myBatteryVoltage;
test(helper_convertToPlato) {
double p = convertToPlato(1.008);
char buffer[20];
String s = convertFloatToString(p, &buffer[0], 2);
s.trim();
assertEqual(s, "2.06");
}
test(helper_convertToSG) {
double p = convertToSG(2.06);
char buffer[20];
String s = convertFloatToString(p, &buffer[0], 3);
s.trim();
assertEqual(s, "1.008");
}
test(helper_convertCtoF) {
float t = convertCtoF(20.0);
assertEqual(t, 68.0);
}
test(helper_convertFtoC) {
float t = convertFtoC(68.0);
assertEqual(t, 20.0);
}
test(helper_urlEncode) {
String s = urlencode("Hello world");
assertEqual(s, "Hello%20world");
}
test(helper_urlDecode) {
String s = urldecode("Hello%20world");
assertEqual(s, "Hello world");
}
test(helper_convertFloatToString) {
char buffer[20];
String s = convertFloatToString(20.2, &buffer[0], 2);
s.trim();
assertEqual(s, "20.20");
}
test(helper_reduceFloatPrecision1) {
float v = 20.233;
float f = reduceFloatPrecision(v, 2);
v = 20.23;
assertEqual(f, v);
}
test(helper_reduceFloatPrecision2) {
float v = 20.238;
float f = reduceFloatPrecision(v, 2);
v = 20.24;
assertEqual(f, v);
}
test(helper_readBatteryVoltage) {
myBatteryVoltage.read();
float f = myBatteryVoltage.getVoltage();
assertMoreOrEqual(f, 2.0);
}
// EOF

32
test/tests_pushtarget.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// TODO: Build some php scripts that run on gravitymon.com for testing the push
// data.
// EOF

128
test/tests_templating.cpp Normal file
View File

@ -0,0 +1,128 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <config.hpp>
#include <templating.hpp>
test(template_applyTemplate1) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP1);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "{\"name\" : \"gravitymon\", \"ID\": \"" + id +
"\", \"token\" : \"\", \"interval\": 900, \"temperature\": 21.2, "
"\"temp_units\": \"C\", \"gravity\": 1.1230, \"angle\": 45.00, "
"\"battery\": " +
batt +
", \"RSSI\": 31, \"corr-gravity\": 1.2230, \"gravity-unit\": "
"\"G\", \"run-time\": 3.0 }";
assertEqual(s, v);
}
test(template_applyTemplate2) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP2);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "{\"name\" : \"gravitymon\", \"ID\": \"" + id +
"\", \"token\" : \"\", \"interval\": 900, \"temperature\": 21.2, "
"\"temp_units\": \"C\", \"gravity\": 1.1230, \"angle\": 45.00, "
"\"battery\": " +
batt +
", \"RSSI\": 31, \"corr-gravity\": 1.2230, \"gravity-unit\": "
"\"G\", \"run-time\": 3.0 }";
assertEqual(s, v);
}
test(template_applyTemplate3) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_HTTP3);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v = "?name=gravitymon&id=" + id +
"&token=&interval=900&temperature=21.2&temp-units=C&gravity=1."
"1230&angle=45.00&battery=" +
batt + "&rssi=31&corr-gravity=1.2230&gravity-unit=G&run-time=3.0";
assertEqual(s, v);
}
test(template_applyTemplate4) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_INFLUX);
String id = myConfig.getID();
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v =
"measurement,host=gravitymon,device=" + id +
",temp-format=C,gravity-format=G "
"gravity=1.1230,corr-gravity=1.2230,angle=45.00,temp=21.2,battery=" +
batt + ",rssi=31\n";
assertEqual(s, v);
}
test(template_applyTemplate5) {
TemplatingEngine e;
char buffer[20];
myConfig.setMDNS("gravitymon");
e.initialize(45.0, 1.123, 1.223, 21.2, 2.98);
String s = e.create(TemplatingEngine::TEMPLATE_MQTT);
String batt =
convertFloatToString(myBatteryVoltage.getVoltage(), &buffer[0], 2);
batt.trim();
String v =
"ispindel/gravitymon/tilt:45.00|ispindel/gravitymon/"
"temperature:21.2|ispindel/gravitymon/temp_units:C|ispindel/gravitymon/"
"battery:" +
batt +
"|ispindel/gravitymon/gravity:1.1230|ispindel/gravitymon/"
"interval:900|ispindel/gravitymon/RSSI:31|";
assertEqual(s, v);
}
// EOF

34
test/tests_tempsensor.cpp Normal file
View File

@ -0,0 +1,34 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <tempsensor.hpp>
test(temp_readSensor) {
myTempSensor.setup();
myTempSensor.getTempC();
assertEqual(myTempSensor.isSensorAttached(), true);
}
// EOF

32
test/tests_webserver.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the WEB module. These tests are done using python script
// and the API's
// EOF

32
test/tests_wifi.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
MIT License
Copyright (c) 2022 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.
*/
#include <AUnit.h>
#include <Arduino.h>
#include <main.hpp>
// No unit testing for the WIFI module. These tests are manual when testing the
// installation steps.
// EOF