diff --git a/.github/workflows/pio-build.yaml b/.github/workflows/pio-build.yaml index fbeff83..61ca9da 100644 --- a/.github/workflows/pio-build.yaml +++ b/.github/workflows/pio-build.yaml @@ -36,7 +36,7 @@ jobs: git config --global advice.detachedHead false - 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 with: diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index bab9942..4f61a24 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v3 - name: clang format support run: | sudo apt install clang-format cppcheck diff --git a/.gitignore b/.gitignore index b9451a7..0e19ee9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .vscode/* *.map src_docs/_build/* +.env/* +*.pyc \ No newline at end of file diff --git a/README.md b/README.md index 839c4bb..cf8846a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ![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. @@ -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: -* David Conde \ No newline at end of file +* David Conde, @davidconde \ No newline at end of file diff --git a/TEST.md b/TEST.md new file mode 100644 index 0000000..f0873fb --- /dev/null +++ b/TEST.md @@ -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. diff --git a/battery-test-1.2.xlsx b/battery-test-1.2.xlsx new file mode 100644 index 0000000..88c9b2a Binary files /dev/null and b/battery-test-1.2.xlsx differ diff --git a/bin/firmware.bin b/bin/firmware.bin index 263819b..4298122 100644 Binary files a/bin/firmware.bin and b/bin/firmware.bin differ diff --git a/bin/firmware32.bin b/bin/firmware32.bin index 0bf5f74..0aaf4e9 100644 Binary files a/bin/firmware32.bin and b/bin/firmware32.bin differ diff --git a/bin/firmware32c3.bin b/bin/firmware32c3.bin index d8c0720..6fad862 100644 Binary files a/bin/firmware32c3.bin and b/bin/firmware32c3.bin differ diff --git a/bin/firmware32c3v1.bin b/bin/firmware32c3v1.bin new file mode 100644 index 0000000..d35bdf8 Binary files /dev/null and b/bin/firmware32c3v1.bin differ diff --git a/bin/firmware32lite.bin b/bin/firmware32lite.bin index 71a732f..9d9f83d 100644 Binary files a/bin/firmware32lite.bin and b/bin/firmware32lite.bin differ diff --git a/bin/firmware32s2.bin b/bin/firmware32s2.bin index a37d650..854d95a 100644 Binary files a/bin/firmware32s2.bin and b/bin/firmware32s2.bin differ diff --git a/html/config.htm b/html/config.htm index 5b3a1c6..a023157 100644 --- a/html/config.htm +++ b/html/config.htm @@ -468,7 +468,7 @@ -
+
@@ -507,9 +507,9 @@ if ( b ) { $("#ota-url").val(gravitymonUrl); - $("#ota-url").prop("disabled", true); + $('#ota-hide').hide(); } else { - $("#ota-url").prop("disabled", false); + $('#ota-hide').show(); } }); @@ -966,7 +966,7 @@ if( cfg["ota-url"] == gravitymonUrl) { $("#gravitymon-com").prop( "checked", true ); - $("#ota-url").prop("disabled", true); + $("#ota-hide").hide(); } $("#token").val(cfg["token"]); diff --git a/html/config.min.htm b/html/config.min.htm index 671e73c..9818f21 100644 --- a/html/config.min.htm +++ b/html/config.min.htm @@ -1 +1 @@ -Beer Gravity Monitor

Temperature Format



(10-100) - default 50
(50-1000) - default 500
(1 - 10) - default 3 SG
(0 - 100) - default 20C/68F

(9 - 12) - default 9 bits

(1 - 60) - default 20 s
(10 - 240) - default 120 s

(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file +Beer Gravity Monitor

Temperature Format



(10-100) - default 50
(50-1000) - default 500
(1 - 10) - default 3 SG
(0 - 100) - default 20C/68F

(9 - 12) - default 9 bits

(1 - 60) - default 20 s
(10 - 240) - default 120 s

(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(0 - 5) - default 0
(C) Copyright 2021-22 Magnus Persson
\ No newline at end of file diff --git a/platformio.ini b/platformio.ini index a0825ad..ca02814 100644 --- a/platformio.ini +++ b/platformio.ini @@ -40,8 +40,8 @@ build_flags = -DUSER_SSID=\""\"" # =\""myssid\"" -DUSER_SSID_PWD=\""\"" # =\""mypwd\"" -DCFG_APPVER="\"1.2.0\"" - -DCFG_GITREV=\""beta-2\"" - #!python script/git_rev.py + #-DCFG_GITREV=\""beta-3\"" + !python script/git_rev.py lib_deps = # Using local copy of these libraries # https://github.com/mp-se/i2cdevlib.git# @@ -103,6 +103,26 @@ lib_deps = board = ${common_env_data.board} build_type = release 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 = +<*> - +<../test/tests*.cpp> [env:gravity32-release] framework = ${common_env_data.framework} @@ -143,6 +163,39 @@ 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 = + ${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 = ${common_env_data.build_unflags} build_flags = diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file diff --git a/script/copy_firmware.py b/script/copy_firmware.py index 63a43d4..717fef8 100644 --- a/script/copy_firmware.py +++ b/script/copy_firmware.py @@ -45,6 +45,12 @@ def after_build(source, target, env): print( "Copy file : " + 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" : target = dir + "/bin/firmware32s2.bin" source = dir + "/.pio/build/" + name + "/firmware.bin" diff --git a/src/calc.cpp b/src/calc.cpp index a278f7d..c9ea030 100644 --- a/src/calc.cpp +++ b/src/calc.cpp @@ -27,9 +27,6 @@ SOFTWARE. #include #include -// -// Use values to derive a formula -// int createFormula(RawFormulaData &fd, char *formulaBuffer, int formulaBufferSize, int order) { int noAngles = 0; @@ -118,10 +115,6 @@ int createFormula(RawFormulaData &fd, char *formulaBuffer, return ERR_FORMULA_INTERNAL; } -// -// Calculates gravity according to supplied formula, compatible with -// iSpindle/Fermentrack formula -// double calculateGravity(double angle, double temp, const char *tempFormula) { const char *formula = myConfig.getGravityFormula(); @@ -163,12 +156,10 @@ double calculateGravity(double angle, double temp, const char *tempFormula) { return 0; } -// // Do a standard gravity temperature correction. This is a simple way to adjust // for differnt worth temperatures. This function uses C as temperature. // // Source: https://homebrewacademy.com/hydrometer-temperature-correction/ -// double gravityTemperatureCorrectionC(double gravitySG, double tempC, double calTempC) { #if LOG_LEVEL == 6 && !defined(CALC_DISABLE_LOGGING) diff --git a/src/main.cpp b/src/main.cpp index 3a3a558..3c5013e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -133,12 +133,6 @@ void setup() { LOG_PERF_START("main-setup"); 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 #if defined(ESP8266) Log.notice(F("Main: Started setup for %s." CR), diff --git a/src/templating.cpp b/src/templating.cpp index 90f6818..5a0b65a 100644 --- a/src/templating.cpp +++ b/src/templating.cpp @@ -80,9 +80,6 @@ const char mqttFormat[] PROGMEM = "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) { @@ -138,10 +135,9 @@ void TemplatingEngine::initialize(float angle, float gravitySG, #endif } -// -// Create the data using defined template. -// -const char* TemplatingEngine::create(TemplatingEngine::Templates idx) { +// the useDefaultTemplate param is there to support unit tests. +const char* TemplatingEngine::create(TemplatingEngine::Templates idx, + bool useDefaultTemplate) { String fname; _baseTemplate.reserve(600); @@ -169,15 +165,16 @@ const char* TemplatingEngine::create(TemplatingEngine::Templates idx) { 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); - file.readBytes(&buf[0], file.size()); - _baseTemplate = String(&buf[0]); - file.close(); - Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str()); + if (!useDefaultTemplate) { + File file = LittleFS.open(fname, "r"); + if (file) { + char buf[file.size() + 1]; + memset(&buf[0], 0, file.size() + 1); + file.readBytes(&buf[0], file.size()); + _baseTemplate = String(&buf[0]); + file.close(); + Log.notice(F("TPL : Template loaded from disk %s." CR), fname.c_str()); + } } #if LOG_LEVEL == 6 diff --git a/src/templating.hpp b/src/templating.hpp index a5da12b..f6f0bac 100644 --- a/src/templating.hpp +++ b/src/templating.hpp @@ -200,7 +200,8 @@ class TemplatingEngine { } void initialize(float angle, float gravitySG, float corrGravitySG, float tempC, float runTime); - const char *create(TemplatingEngine::Templates idx); + const char *create(TemplatingEngine::Templates idx, + bool useDefaultTemplate = false); }; #endif // SRC_TEMPLATING_HPP_ diff --git a/src/tempsensor.cpp b/src/tempsensor.cpp index 2edde3f..c1908f8 100644 --- a/src/tempsensor.cpp +++ b/src/tempsensor.cpp @@ -58,9 +58,6 @@ void TempSensor::setup() { #endif } -// -// Retrieving value from sensor, value is in Celcius -// float TempSensor::getValue(bool useGyro) { if (useGyro) { // When using the gyro temperature only the first read value will be diff --git a/src/wifi.cpp b/src/wifi.cpp index e8dd3bb..61188be 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -430,6 +430,8 @@ bool WifiConnection::checkFirmwareVersion() { } http.end(); + if (_newFirmware) Log.notice(F("WIFI: Found new version." CR)); + #if LOG_LEVEL == 6 Log.verbose(F("WIFI: OTA found new version %s." CR), _newFirmware ? "true" : "false"); diff --git a/src_docs/source/compiling.rst b/src_docs/source/compiling.rst index ef1f668..f754e32 100644 --- a/src_docs/source/compiling.rst +++ b/src_docs/source/compiling.rst @@ -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-release; Standard release * gravity32-release: Version for ESP32 mini. -* gravity32-c3-release: Version for ESP32 C3 mini. -* gravity32-s2-release: Version for ESP32 S2 mini. -* gravity32-lite-release: Version for ESP32 lite (Floaty hardware). +* gravity32c3-release: Version for ESP32 C3 mini v2.1+. +* gravity32c3v1-release: Version for ESP32 C3 mini v1.0. +* gravity32s2-release: Version for ESP32 S2 mini. +* gravity32lite-release: Version for ESP32 lite (Floaty hardware). .. 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. diff --git a/src_docs/source/hardware.rst b/src_docs/source/hardware.rst index b38a867..16a92aa 100644 --- a/src_docs/source/hardware.rst +++ b/src_docs/source/hardware.rst @@ -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. -* `Cherry Philip Hardware design, `_ -* `OpenSource Distilling, `_ +* `Cherry Philip Hardware design `_ +* `OpenSource Distilling `_ iSpindle based on esp8266 ========================= @@ -25,24 +25,20 @@ Schema for esp8266 build :width: 700 :alt: Schema esp8266 - 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. - -.. 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. +Gravitymon supports a number of ESP32 boards that offers bluetooth support. .. image:: images/esp32_hardware.jpg :width: 500 :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. -* esp32c3 mini, a newer version based on the latest risc v7 architecture, is seen as the replacement for the esp8266 with bluetooth support. +* 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. Don't buy v1.0 since that has a faulty wifi antenna. * 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:: 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 ++++++++++++ -This is model is now fully supported by gravitymon. +This is model is fully supported by gravitymon. .. image:: images/ispindel_esp32c3.jpg :width: 500 @@ -67,7 +63,7 @@ Here is an image of where I added the resistor for the voltage divider. 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 +++++++++++++ @@ -82,7 +78,7 @@ Schema for esp32 build ++++++++++++++++++++++ .. 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. The ESP8266 has a built in resistor thats not visible on the schema and this acts as a voltage divider. diff --git a/src_docs/source/index.rst b/src_docs/source/index.rst index e76969a..68f3faf 100644 --- a/src_docs/source/index.rst +++ b/src_docs/source/index.rst @@ -7,7 +7,7 @@ Welcome to GravityMon ##################### .. 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? -------------------- @@ -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 `_ .. note:: - I don't take responsibility for any errors or issues caused by the software. The software is provided as-is. I will however - try my best to fix issues that might occur. + I don't take responsibility for any errors or issues caused by the software or hardware. + 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. diff --git a/src_docs/source/installation.rst b/src_docs/source/installation.rst index 03344b6..b763cd6 100644 --- a/src_docs/source/installation.rst +++ b/src_docs/source/installation.rst @@ -45,8 +45,8 @@ In the /bin directory you will find 4 different firmware builds; * **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 - the size of the firmware we are using a custom partition setup. + 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. If you have a v1.0 board use the firmware32c3v1.bin instead. * **firmware32s2.bin** diff --git a/src_docs/source/releases.rst b/src_docs/source/releases.rst index 310313f..90f7819 100644 --- a/src_docs/source/releases.rst +++ b/src_docs/source/releases.rst @@ -3,40 +3,31 @@ Releases ######## -v1.2.0 - beta2 -============== +v1.2.0 +====== Features ++++++++ * Added function to calcualate voltage factor based on measured value. * Updated battery estimation for the various esp32 boards. * 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 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. +* 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 ++++++++++++++++ * 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 ====== * BUG: The text before the first variable was missed in the conversion of a format template. diff --git a/test/apitests.py b/test/apitests.py new file mode 100644 index 0000000..1fed557 --- /dev/null +++ b/test/apitests.py @@ -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() diff --git a/test/config.json b/test/config.json index af6a3e1..8df0a6b 100644 --- a/test/config.json +++ b/test/config.json @@ -3,7 +3,7 @@ "wifi-ssid2": "ssid 2", "mdns": "gravmon3", "id": "7376ef", - "ota-url": "http://192.168.1.100:80/firmware/gravmon/", + "ota-url": "https://www.gravitymon.com/firmware/", "temp-format": "C", "http-push": "http://192.168.1.10:9090/api/v1/ZYfjlUNeiuyu9N/telemetry", "http-push-h1": "Auth: Basic T7IF9DD9fF3RDddE=", diff --git a/test/tests.cpp b/test/tests.cpp new file mode 100644 index 0000000..441fc1c --- /dev/null +++ b/test/tests.cpp @@ -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 +#include + +#include + +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 diff --git a/test/tests_ble.cpp b/test/tests_ble.cpp new file mode 100644 index 0000000..fbfabe4 --- /dev/null +++ b/test/tests_ble.cpp @@ -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 +#include + +#include + +// No unit testing for the BLE module. + +// EOF diff --git a/test/tests_calc.cpp b/test/tests_calc.cpp new file mode 100644 index 0000000..87945ab --- /dev/null +++ b/test/tests_calc.cpp @@ -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 + +#include +#include + +// 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 diff --git a/test/tests_config.cpp b/test/tests_config.cpp new file mode 100644 index 0000000..65d65c3 --- /dev/null +++ b/test/tests_config.cpp @@ -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 + +#include + +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 diff --git a/test/tests_gyro.cpp b/test/tests_gyro.cpp new file mode 100644 index 0000000..e1a49c0 --- /dev/null +++ b/test/tests_gyro.cpp @@ -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 + +#include + +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 diff --git a/test/tests_helper.cpp b/test/tests_helper.cpp new file mode 100644 index 0000000..eaa95a0 --- /dev/null +++ b/test/tests_helper.cpp @@ -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 + +#include + +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 diff --git a/test/tests_pushtarget.cpp b/test/tests_pushtarget.cpp new file mode 100644 index 0000000..686a93a --- /dev/null +++ b/test/tests_pushtarget.cpp @@ -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 +#include + +#include + +// TODO: Build some php scripts that run on gravitymon.com for testing the push +// data. + +// EOF diff --git a/test/tests_templating.cpp b/test/tests_templating.cpp new file mode 100644 index 0000000..8168ea3 --- /dev/null +++ b/test/tests_templating.cpp @@ -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 + +#include +#include + +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 diff --git a/test/tests_tempsensor.cpp b/test/tests_tempsensor.cpp new file mode 100644 index 0000000..84d4e94 --- /dev/null +++ b/test/tests_tempsensor.cpp @@ -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 + +#include + +test(temp_readSensor) { + myTempSensor.setup(); + myTempSensor.getTempC(); + assertEqual(myTempSensor.isSensorAttached(), true); +} + +// EOF diff --git a/test/tests_webserver.cpp b/test/tests_webserver.cpp new file mode 100644 index 0000000..a44122d --- /dev/null +++ b/test/tests_webserver.cpp @@ -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 +#include + +#include + +// No unit testing for the WEB module. These tests are done using python script +// and the API's + +// EOF diff --git a/test/tests_wifi.cpp b/test/tests_wifi.cpp new file mode 100644 index 0000000..89abf5c --- /dev/null +++ b/test/tests_wifi.cpp @@ -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 +#include + +#include + +// No unit testing for the WIFI module. These tests are manual when testing the +// installation steps. + +// EOF