Compare commits

...
Author SHA1 Message Date
ESPEasy release bot 9d894d7c83 automatically updated release notes for mega-20181022 2018-10-22 04:00:15 +02:00
Gijs NoorlanderandGitHub 024ec7b727 Merge pull request #1928 from TD-er/bugfix/reduce_webserver_strings
Reduce webserver strings
2018-10-21 11:12:52 +02:00
ESPEasy release bot 1ff0141e38 automatically updated release notes for mega-20181021 2018-10-21 04:00:16 +02:00
Gijs NoorlanderandGitHub 125db8046b Merge pull request #1927 from TD-er/feature/1870_build_sysino
[#1870] Feature request build date as system info
2018-10-21 01:20:51 +02:00
TD-er 720317c829 [Webserver] Uniform row labels to reduce flash string size 2018-10-21 01:18:43 +02:00
TD-er bbe9005a32 [Webserver] Generate copyText id names
Reduce amount of flash strings to reduce build file size
2018-10-21 00:05:31 +02:00
Gijs NoorlanderandGitHub afa16e80b6 Merge pull request #1923 from TD-er/bugfix/wifi_stability
Various network stability fixes and reduce memory usage when saving settings
2018-10-20 23:09:50 +02:00
Gijs NoorlanderandGitHub 37f5c92aa7 Merge pull request #1926 from TD-er/bugfix/1891_multiple_values
[#1891] Send all values for Generic UDP controller
2018-10-20 23:08:05 +02:00
TD-er 684b89ae4c [#1870] Feature request build date as system info
See #1870
Added:
- %sysbuild_date%
- %sysbuild_time%
2018-10-20 23:05:59 +02:00
TD-er 8c4612c300 [#1891] Send all values for Generic UDP controller
See https://github.com/letscontrolit/ESPEasy/issues/1891#issuecomment-431612051

A copy of the element from the queue was used, not a reference to it.
Therefore increasing the internal counter to send the next value was only executed on the copy, not the element in the list.
2018-10-20 22:34:11 +02:00
Gijs NoorlanderandGitHub 0afab53659 Merge pull request #1925 from TD-er/revert/1924_pull_request
Revert "fix for #1920"
2018-10-20 20:05:22 +02:00
Bartlomiej ZimonandTD-er e6e98345fe Revert "fix for #1920"
This reverts commit 3f84c35014.
2018-10-20 20:03:56 +02:00
Gijs NoorlanderandGitHub 2394e4a75f Merge pull request #1924 from uzi18/patch9
fix for #1920
2018-10-20 18:51:05 +02:00
ESPEasy release bot e1c4691325 automatically updated release notes for mega-20181020 2018-10-20 04:00:16 +02:00
Bartlomiej Zimon 3f84c35014 fix for #1920
fix for out of sync, check both header bytes
proper check for abnormal state
2018-10-19 22:40:11 +00:00
TD-er bc0bafa96b [#1910] Notification settings extra strings on stack
See #1910
Added a lot more of these changes and changed the input fields of the Notification Settings to be the same as all input fields in other places.
2018-10-20 00:20:58 +02:00
TD-er 4e7e66822d [Rules] Early exit on rules processing when rules not enabled 2018-10-20 00:20:58 +02:00
TD-er 6f226e6d65 Proper look for connection.
See discussion here: https://github.com/esp8266/Arduino/issues/5257
2018-10-20 00:20:58 +02:00
TD-er a50a035238 [stack usage] Allocate temp char array on heap for parsing arguments
These char arrays were rather large, so allocate on heap instead of char[240] on the stack.
Also re-order elements in not stored structs to reduce memory usage
2018-10-20 00:20:58 +02:00
Gijs NoorlanderandGitHub d560c635f5 Merge pull request #1922 from kpalczewski/mega
add support for 64bit IR codes.
2018-10-20 00:05:31 +02:00
kpalczewski f8db0c0a05 add support for 64bit IR codes. 2018-10-19 22:34:04 +02:00
Gijs NoorlanderandGitHub 79616022df Merge pull request #1919 from giig1967g/new/statusvariables
added 4 variables to verify the system status
2018-10-19 22:15:26 +02:00
Gijs NoorlanderandGitHub 559d294607 Merge branch 'mega' into new/statusvariables 2018-10-19 21:36:45 +02:00
Gijs NoorlanderandGitHub d2588c5e01 Merge pull request #1848 from giig1967g/new/plugin_pcf
Added [plugin#pcfgpio#pinstate#xx] and [plugin#mcpgpio#pinstate#xx]
2018-10-19 21:34:18 +02:00
Gijs NoorlanderandGitHub ba45a51576 Merge pull request #1884 from giig1967g/new/uservars
added Global Variables for rules
2018-10-19 21:33:10 +02:00
Gijs NoorlanderandGitHub 8321caaeed Merge branch 'mega' into new/statusvariables 2018-10-19 21:31:18 +02:00
Gijs NoorlanderandGitHub b35ed7d809 Merge pull request #1880 from giig1967g/doubleclick
DEBOUNCE, DOUBLECLICK and LONGPRESS events for PLUGIN001
2018-10-19 21:23:22 +02:00
Plebs a4583d150e added 4 variables to verify the system status
4 new system variables:
%iswifi% (=1 if wifi is connected)
%isntp% (=1 if NTP has been initialized)
%ismqtt% (=1 if mqtt is connected)
%ismqttimp% (=1 if mqtt import is connected)

Example:
if %isntp%=1 and %systime%>%sunset% do
....
endif
2018-10-18 17:44:05 +02:00
ESPEasy release bot cadc0a885d automatically updated release notes for mega-20181017 2018-10-17 04:00:18 +02:00
Gijs NoorlanderandGitHub 061eb20ae5 Merge pull request #1914 from TD-er/bugfix/reduce_stack
[#1906] Move Notification and Controller settings to heap
2018-10-17 00:40:30 +02:00
TD-er 9a9799eee7 [Stack] Add Free Stack to System Info plugin 2018-10-17 00:04:22 +02:00
Gijs NoorlanderandGitHub f4615bb76b Added checklist to the Issue_template
Lately a lot of new issues were non-descriptive and the same basic questions were to be asked over and over again.
These are now included in this template.
2018-10-16 23:02:33 +02:00
TD-er ecc498c359 [#1906] Allocate ControllerSettings on the heap
See #1906
2018-10-16 12:24:06 +02:00
TD-er e0e10fa6c8 [#1906] Allocate NotificationSettings on the heap
See #1906
2018-10-16 11:47:46 +02:00
ESPEasy release bot 228f09f394 automatically updated release notes for mega-20181016 2018-10-16 04:00:06 +02:00
Gijs NoorlanderandGitHub 19ac19778b Merge pull request #1909 from giig1967g/bug/toggle
Fix for "togglegpio" without gpio initialization
2018-10-16 01:11:46 +02:00
Gijs NoorlanderandGitHub 5e2d517ba2 Merge pull request #1913 from TD-er/bugfix/revert_safe_strncpy
[Revert] Safe_strncpy cannot be used as generic strncpy replacement
2018-10-16 01:09:59 +02:00
TD-er 7a4b17983e [Revert] Safe_strncpy cannot be used as generic strncpy replacement
It does do more. So only use it to convert Strings to single char arrays.
2018-10-16 00:11:17 +02:00
Plebs 6eba74ccc1 Fix for togglegpio without gpio initialization 2018-10-15 17:34:35 +02:00
ESPEasy release bot 27a05b674c automatically updated release notes for mega-20181015 2018-10-15 04:00:10 +02:00
Gijs NoorlanderandGitHub 4a66bbf081 Merge pull request #1905 from TD-er/bugfix/safe_strncpy
[safe strncpy] Add warning when entered string is too large
2018-10-15 00:52:34 +02:00
TD-er 109bddb5c3 [safe strncpy] Add warning when entered string is too large
Started with this issue:
"Framed Oled Display mixing up multiple values in different lines" #1904

Now almost all calls to `strncpy` are replaced with this safe variant which does some checking and zero-ing the buffer.
2018-10-15 00:24:57 +02:00
Gijs NoorlanderandGitHub 8158a919d8 Merge pull request #1879 from giig1967g/bug/1864
fix for bug #1864
2018-10-14 10:35:19 +02:00
ESPEasy release bot 706b91168b automatically updated release notes for mega-20181014 2018-10-14 04:00:13 +02:00
Gijs NoorlanderandGitHub d047999c5e Merge pull request #1901 from TD-er/bugfix/warn_incomplete_IPsettings
[UX] Warn user for incomplete network IP settings
2018-10-14 00:59:37 +02:00
Gijs NoorlanderandGitHub 42f361aacd Merge pull request #1902 from TD-er/bugfix/1895_I2C
[#1895] Incorrect number bytes read I2C_read32_reg
2018-10-14 00:43:15 +02:00
TD-er 3dd076cf25 [#1895] Incorrect number bytes read I2C_read32_reg
See #1895
2018-10-14 00:42:03 +02:00
TD-er c0d155dd99 [UX] Warn user for incomplete network IP settings
Was a left-over remark in #1291
2018-10-14 00:22:46 +02:00
Gijs NoorlanderandGitHub c433174d71 Merge pull request #1900 from TD-er/bugfix/time_initialized_event
Time#Initialized event in rules before %syshour% is set
2018-10-13 23:37:04 +02:00
Gijs NoorlanderandGitHub b805b59baf Merge pull request #1899 from TD-er/bugfix/controller_check_reply
Fix send only first value by controller (#1891 #1892 #1835)
2018-10-13 23:07:32 +02:00
Gijs NoorlanderandGitHub 9fae0477d8 Merge pull request #1894 from letscontrolit/feature/1799_SSID_mainpage
[#1799] Add SSID to main page
2018-10-13 23:06:55 +02:00
TD-er f7ac04dc96 Time#Initialized event in rules before %syshour% is set
See [forum](http://www.letscontrolit.com/forum/viewtopic.php?f=6&t=5819&p=31376&hilit=Initialized#p31376) and [other report of same issue](https://www.letscontrolit.com/forum/viewtopic.php?t=5925)
2018-10-13 23:05:35 +02:00
TD-er d50e661efb [Controller] Fix send only first value
See #1891 #1892 #1835
Added an extra option to explicitly check the reply  on sent messages by a controller.
2018-10-13 22:21:36 +02:00
Plebs 57b5338af6 Update src/StringConverter.ino 2018-10-12 15:00:58 +02:00
Plebs bd035c8f81 optimizations 2018-10-12 12:52:22 +02:00
Plebs ac63d93e1a Update src/_P001_Switch.ino 2018-10-12 12:10:10 +02:00
Plebs 4cff0d0e02 Update src/_P001_Switch.ino 2018-10-12 10:16:54 +02:00
TD-er ca83983fd2 [#1891] Generic UPD controller - send only first value from sensor
See #1891
2018-10-12 01:04:46 +02:00
Gijs NoorlanderandGitHub d808f7e79b [#1799] Add SSID to main page
See #1799
2018-10-12 00:57:08 +02:00
Plebs e469350ad9 modified command and variable name 2018-10-11 19:23:42 +02:00
Plebs 60536b1a93 some optimizations 2018-10-11 12:23:08 +02:00
Plebs b9e45bd515 fixed length check 2018-10-11 12:16:55 +02:00
Plebs a0992b8383 fixes 2018-10-11 11:50:37 +02:00
Plebs 9315581858 some fixes 2018-10-11 11:37:15 +02:00
ESPEasy release bot 9a763303ca automatically updated release notes for mega-20181011 2018-10-11 04:00:24 +02:00
Gijs NoorlanderandGitHub 7df9379f47 Merge pull request #1886 from TD-er/bugfix/reduce_memory_usage
Bugfix/reduce memory usage & improve load ExtraTaskSettings check
2018-10-11 01:48:31 +02:00
Plebs c32fe50521 Update src/_P023_OLED.ino 2018-10-10 23:28:34 +02:00
Plebs e4569b26f7 added global variables for rules
Added a new command:
customvar,x,y
that stores in a system variable %customvarx% the value 'y'.

Values are float
2018-10-10 23:19:09 +02:00
TD-er 48998886fe [WebLog] Disable weblog when it is not being read
This will prevent log items being generated when web log level is the highest level and it will clear the web log buffer to free memory.
2018-10-10 20:23:10 +02:00
Plebs 36f97d7909 some fixes 2018-10-10 19:24:45 +02:00
TD-er 0e3b9c9e47 [Travis Doc] Remove building documentation by Travis
It seems to break the nightly builds, so remove for now.
2018-10-10 15:54:34 +02:00
ESPEasy release bot acecff4108 automatically updated release notes for mega-20181010 2018-10-10 04:00:09 +02:00
TD-er 3322b954a7 Make sure to load ExtraTaskSettings when needed
There is only 1 `ExtraTaskSettings` object, so make sure it is always in sync.
2018-10-10 01:29:38 +02:00
TD-er 4c4b844a4d [Controller] Set default timeout to 300 msec (was 100 msec)
300 msec should be enough for most online services like `cloudmqtt.com`
2018-10-10 00:19:26 +02:00
Gijs NoorlanderandGitHub cf4d28eb4f Merge pull request #1877 from giig1967g/new/pcfinvertstate
Fix for #1767
2018-10-10 00:05:11 +02:00
TD-er 555b5386ae [C009] Reduce memory usage of FHEM HTTP delay queue
The entire JSON string was stored in the queue element, but lot of it can be computed later.
2018-10-09 23:52:46 +02:00
Plebs 1903fdc7f2 fix for bug #1864
Removed '.' check
2018-10-09 18:16:15 +02:00
Plebs 918353461e Update src/_P001_Switch.ino 2018-10-09 16:31:55 +02:00
Plebs e648a20fd0 Update src/_P019_PCF8574.ino 2018-10-09 16:24:13 +02:00
Plebs 1d7ea5e129 Update src/_P001_Switch.ino 2018-10-09 14:58:21 +02:00
Plebs 6ce5240dd7 few changes 2018-10-09 09:34:50 +02:00
ESPEasy release bot 692fb248ce automatically updated release notes for mega-20181009 2018-10-09 04:00:18 +02:00
Gijs NoorlanderandGitHub 5a87397ee0 Merge pull request #1872 from TD-er/bugfix/JSON_newline
Bugfix/json newline and memory allocation optimizations.
2018-10-09 01:14:50 +02:00
TD-er 99ba09b024 [StringConverter] Some optimisations to reduce memory allocation 2018-10-09 01:11:45 +02:00
Plebs 3dea091d21 small fixes 2018-10-09 00:20:27 +02:00
Plebs ca13317cbd Update src/_P001_Switch.ino 2018-10-09 00:12:50 +02:00
TD-er f68516d5bb [LWIP1.4] Move back to LWIP1.4
The combination LWIP1.4 and core 2.4.1 were used until begin of May and those were considered stable.
2018-10-08 23:56:55 +02:00
TD-er 8102a0096d [JSON] Replace \n and \r in text fields
Those are not allowed in strings in JSON.
This leads to (web) log viewer crashing.
2018-10-08 23:44:44 +02:00
Plebs 73cec59bd5 Update src/_P001_Switch.ino 2018-10-08 22:30:02 +02:00
Gijs NoorlanderandGitHub 96ecf9bf29 Merge pull request #1871 from TD-er/bugfix/travis_check_for_docs
[Docs] Disable travis_terminate for now
2018-10-08 21:05:55 +02:00
TD-er ef1e371514 [Docs] Disable travis_terminate for now
Apparently `travis_terminate` does not exist anymore as a command to stop the script without failing.
2018-10-08 19:29:56 +02:00
Gijs NoorlanderandGitHub 45d67292a3 Merge pull request #1869 from TD-er/feature/travis_build_docs
Feature/travis build docs
2018-10-08 16:38:16 +02:00
TD-er b5662baad4 [Docs] Run Travic documentation check before compiling project 2018-10-08 16:04:12 +02:00
TD-er 12c3e5f81f [Docs] Add Travis check for documentation and stop build for docs only
Let Travis build the documentation and prevent full build when only documentation changed.
2018-10-08 15:34:02 +02:00
TD-er dececf24ee [Docs] Add favicon to documentation 2018-10-08 15:31:48 +02:00
ESPEasy release bot 3fb6dec382 automatically updated release notes for mega-20181008 2018-10-08 04:00:09 +02:00
Gijs NoorlanderandGitHub cef962c5f1 Added link to ReadTheDocs 2018-10-08 00:05:58 +02:00
Gijs NoorlanderandGitHub cca2335070 Merge pull request #1868 from TD-er/feature/update_sphinx_docs
[Docs] Added some directory structure and template
2018-10-08 00:01:07 +02:00
TD-er ffd0d26ed3 [Docs] Added some directory structure and template 2018-10-08 00:00:27 +02:00
Plebs 9b457ed77b some code optimizations 2018-10-07 23:12:23 +02:00
GrovkillenandGitHub f787a9a9a6 [Sphinx] added extension "sphinx.ext.imgconverter" 2018-10-07 19:11:49 +02:00
Gijs NoorlanderandGitHub a0ae104794 Merge pull request #1865 from TD-er/feature/sphinx_docs
Feature/sphinx docs
2018-10-07 18:26:06 +02:00
TD-er 2b78810457 [Docs] First ESPEasy documentation (Controllers) 2018-10-07 18:23:25 +02:00
TD-er 43081c3ac8 [Sphinx] Initial setup for Sphinx documentation 2018-10-07 17:09:52 +02:00
Plebs d3c86b9824 code optimization 2018-10-04 12:07:32 +02:00
Plebs 9621a91d90 added [plugin#mcpgpio#pinstate#xx] 2018-10-04 11:31:49 +02:00
Plebs 0b9c550fd7 Added [plugin#pcfgpio#pinstate#xx]
Added [plugin#pcfgpio#pinstate#xx] variable for rules
2018-10-03 22:49:11 +02:00
61 changed files with 2100 additions and 783 deletions
+23
View File
@@ -1,3 +1,25 @@
### Checklist
- [ ] The title of this issue is "to the point" and descriptive.
- [ ] This issue describes what is happening.
- [ ] This issue describes what components are affected (e.g. name of plugin/controller)
- [ ] This issue describes how to reproduce it.
- [ ] This issue describes when it was introduced (when known) and what version is now showing the problem.
### I have...
- [ ] searched the [issue tracker](https://github.com/letscontrolit/ESPEasy/issues) or the [forum](https://www.letscontrolit.com/forum/viewforum.php?f=1) for a similar issue. (include links when applicable)
- [ ] entered a system description using "Copy info to clipboard" on the sysinfo page. (when possible)
- [ ] entered the full filename of the used version (e.g. `ESP_Easy_mega-20181001_test_ESP8266_4096_VCC.bin` )
- [ ] given a list of active plugins or controllers when applicable.
- [ ] filled out all applicable fields below.
### Steps already tried...
- [ ] Tried a clean install (empty `.bin` files are included in the ZIP)
- [ ] Tested previous/other build (mention which one already tested)
- [ ] Tested on other node to make sure hardware isn't defective.
- [ ] Verified if the problem is limited to a single plugin/controller
### If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! ###
<!--- If you self compile, please state this and PLEASE try to ONLY REPORT ISSUES WITH OFFICIAL BUILDS! --->
<!--- NOTE: This is not a support forum! For questions and support go here: --->
@@ -5,6 +27,7 @@
<!--- Remove topics that are not applicable to your feature request of issue --->
<!--- Remember to have a "to the point" TITLE --->
### Summarize of the problem/feature request
<!--- Describe the problem or feature request --->
YOUR TEXT GOES HERE
+2
View File
@@ -22,3 +22,5 @@ test/output_export.cpp
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
docs/build/
+2 -1
View File
@@ -22,9 +22,10 @@ addons:
install:
- pip install -U platformio
- pip install -U platformio sphinx recommonmark sphinx_bootstrap_theme
script:
# - bash ./preflight.sh # make sure input files are OK before wasting time with prereqs
# - cppcheck --enable=warning src/*.ino -q --force -I src --include=src/ESPEasy.ino --error-exitcode=1
# - ./memanalyzer.py ~/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-objdump
- PLATFORMIO_BUILD_FLAGS="-D CONTINUOUS_INTEGRATION" platformio run
+2
View File
@@ -41,4 +41,6 @@ ESPEasy_mega-20180102_dev_ESP8285_1024.bin | ESP8285 with 1Mb flash | Stabl
Details and discussion are on the Experimental forum: https://www.letscontrolit.com/forum/viewforum.php?f=18
Automated builds of the (new) documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/)
We're also on IRC: #ESPEasy @freenode
+6 -2
View File
@@ -35,10 +35,14 @@ done
#create a source structure that is the same as the original ESPEasy project (and works with the howto on the wiki)
rm -rf dist/Source 2>/dev/null
mkdir dist/Source
#rm -rf dist/Source 2>/dev/null
rm -rf dist/Docs 2>/dev/null
mkdir -p dist/Source
#mkdir -p dist/Docs
cp -r lib dist/Source/
cp -r src dist/Source/
#cp -r docs/build/ dist/Docs/
cp platformio.ini dist/Source/
cd dist
+181
View File
@@ -1,3 +1,184 @@
-------------------------------------------------
Changes in release mega-20181022 (since mega-20181021)
-------------------------------------------------
Release date: Mon Oct 22 04:00:15 CEST 2018
TD-er (2):
[Webserver] Generate copyText id names
[Webserver] Uniform row labels to reduce flash string size
-------------------------------------------------
Changes in release mega-20181021 (since mega-20181020)
-------------------------------------------------
Release date: Sun Oct 21 04:00:16 CEST 2018
Bartlomiej Zimon (2):
fix for #1920 fix for out of sync, check both header bytes proper check for abnormal state
Revert "fix for #1920"
TD-er (6):
[stack usage] Allocate temp char array on heap for parsing arguments
Proper look for connection.
[Rules] Early exit on rules processing when rules not enabled
[#1910] Notification settings extra strings on stack
[#1891] Send all values for Generic UDP controller
[#1870] Feature request build date as system info
-------------------------------------------------
Changes in release mega-20181020 (since mega-20181017)
-------------------------------------------------
Release date: Sat Oct 20 04:00:16 CEST 2018
Plebs (22):
Added [plugin#pcfgpio#pinstate#xx]
added [plugin#mcpgpio#pinstate#xx]
code optimization
some code optimizations
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
small fixes
few changes
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
some fixes
added global variables for rules
some fixes
fixes
fixed length check
some optimizations
modified command and variable name
Update src/_P001_Switch.ino
Update src/_P001_Switch.ino
optimizations
Update src/StringConverter.ino
added 4 variables to verify the system status
kpalczewski (1):
add support for 64bit IR codes.
-------------------------------------------------
Changes in release mega-20181017 (since mega-20181016)
-------------------------------------------------
Release date: Wed Oct 17 04:00:18 CEST 2018
Gijs Noorlander (1):
Added checklist to the Issue_template
TD-er (3):
[#1906] Allocate NotificationSettings on the heap
[#1906] Allocate ControllerSettings on the heap
[Stack] Add Free Stack to System Info plugin
-------------------------------------------------
Changes in release mega-20181016 (since mega-20181015)
-------------------------------------------------
Release date: Tue Oct 16 04:00:06 CEST 2018
Plebs (1):
Fix for togglegpio without gpio initialization
TD-er (1):
[Revert] Safe_strncpy cannot be used as generic strncpy replacement
-------------------------------------------------
Changes in release mega-20181015 (since mega-20181014)
-------------------------------------------------
Release date: Mon Oct 15 04:00:10 CEST 2018
Plebs (2):
fix for bug #1864
Update src/_P023_OLED.ino
TD-er (1):
[safe strncpy] Add warning when entered string is too large
-------------------------------------------------
Changes in release mega-20181014 (since mega-20181011)
-------------------------------------------------
Release date: Sun Oct 14 04:00:13 CEST 2018
Gijs Noorlander (1):
[#1799] Add SSID to main page
TD-er (5):
[#1891] Generic UPD controller - send only first value from sensor
[Controller] Fix send only first value
Time#Initialized event in rules before %syshour% is set
[UX] Warn user for incomplete network IP settings
[#1895] Incorrect number bytes read I2C_read32_reg
-------------------------------------------------
Changes in release mega-20181011 (since mega-20181010)
-------------------------------------------------
Release date: Thu Oct 11 04:00:24 CEST 2018
TD-er (5):
[C009] Reduce memory usage of FHEM HTTP delay queue
[Controller] Set default timeout to 300 msec (was 100 msec)
Make sure to load ExtraTaskSettings when needed
[Travis Doc] Remove building documentation by Travis
[WebLog] Disable weblog when it is not being read
-------------------------------------------------
Changes in release mega-20181010 (since mega-20181009)
-------------------------------------------------
Release date: Wed Oct 10 04:00:09 CEST 2018
Plebs (1):
Update src/_P019_PCF8574.ino
-------------------------------------------------
Changes in release mega-20181009 (since mega-20181008)
-------------------------------------------------
Release date: Tue Oct 9 04:00:18 CEST 2018
TD-er (7):
[Docs] Add favicon to documentation
[Docs] Add Travis check for documentation and stop build for docs only
[Docs] Run Travic documentation check before compiling project
[Docs] Disable travis_terminate for now
[JSON] Replace \n and \r in text fields
[LWIP1.4] Move back to LWIP1.4
[StringConverter] Some optimisations to reduce memory allocation
-------------------------------------------------
Changes in release mega-20181008 (since mega-20181007)
-------------------------------------------------
Release date: Mon Oct 8 04:00:09 CEST 2018
Gijs Noorlander (1):
Added link to ReadTheDocs
Grovkillen (1):
[Sphinx] added extension "sphinx.ext.imgconverter"
TD-er (3):
[Sphinx] Initial setup for Sphinx documentation
[Docs] First ESPEasy documentation (Controllers)
[Docs] Added some directory structure and template
-------------------------------------------------
Changes in release mega-20181007 (since mega-20181006)
-------------------------------------------------
+19
View File
@@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
+3
View File
@@ -0,0 +1,3 @@
recommonmark
Sphinx
sphinx-bootstrap-theme
+66
View File
@@ -0,0 +1,66 @@
Controller
**********
A controller is a component to enable a plugin to send data elsewhere.
- Up-to 3 controllers can be active in ESPEasy.
- Per plugin up-to 3 active controllers can be selected.
- For some controllers an additional parameter can be given.
For example, Domoticz needs an 'IDX' value to identify the configured entry in
Domoticz for which new data is sent.
Controller Parameters
=====================
Generic fields
--------------
- **Protocol** - The type of controller (e.g. ThingSpeak/OpenHAB MQTT/etc.)
- **Locate Controller** - Selection between hostname/IP
- **Controller Hostname/IP** - The address to reach the selected service
- **Controller Port** - TCP/UDP Port number (0...65536)
- **Enabled** - Whether or not the controller is active.
Send queue parameters
---------------------
Controllers have a queue to keep unsent messages.
This queue is used to handle message bursts and also store messages which are recorded
before WiFi connection is made or during lost connection.
- **Minimum Send Interval** - Minimum time between two messages in msec.
- **Max Queue Depth** - Maximum length of the buffer queue to keep unsent messages.
- **Max Retries** - Maximum number of retries to send a message.
- **Full Queue Action** - How to handle when queue is full, ignore new or delete oldest message.
- **Client Timeout** - Timeout in msec for an network connection used by the controller.
- **Check Reply** - When set to false, a sent message is considered always successful.
Sample ThingSpeak configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some controllers, like ThingSpeak, need a specific configuration.
ThingSpeak only allows a message every 15 seconds for the free accounts.
- **Minimum Send Interval** - 15000 msec
- **Max Queue Depth** - 1 (only report the last value)
- **Max Retries** - 2
- **Full Queue Action** - Delete Oldest
- **Client Timeout** - 500 msec (server is online, so timeout must be a bit longer)
Controller user credentials
---------------------------
- **Controller User** - User name (optional)
- **Controller Password** - Password (optional)
MQTT related settings
---------------------
- **Controller Subscribe** - Subscribe to the given topic.
- **Controller Publish** - Publish to the given topic.
- **Controller lwl topic** - Topic to which LWT (Last Will Testament) messages should be sent.
- **LWT Connect Message** - Connection established message.
- **LWT Disconnect Message** - Connection lost message (sent to broker during connect and published by broker when connection is lost)
+2
View File
@@ -0,0 +1,2 @@
About Us
********
+2
View File
@@ -0,0 +1,2 @@
ESPEasy Function Blocks
***********************
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+35
View File
@@ -0,0 +1,35 @@
Documentation
*************
For documentation we use Sphinx and this will be read by ReadTheDocs.
We use the `Sphinx Bootstrap Theme <https://github.com/ryan-roemer/sphinx-bootstrap-theme>`_
This documentation is included in the GitHub repository.
It allows us to create documentation per version of ESPEasy.
See also the `ESPEasy wiki <https://www.letscontrolit.com/wiki/index.php/ESPEasy>`_
for more documentation which has not been moved here.
Needed Python packages::
pip install sphinx recommonmark sphinx_bootstrap_theme
PlatformIO with Atom
====================
With the ESPEasy project open in Atom, open the PIO terminal in Atom.
Install dependencies::
cd docs
pip install ... (see needed Python packages above)
Build on Windows::
cd docs
.\make.bat html
Build on Linux/Mac::
cd docs
./make html
+2
View File
@@ -0,0 +1,2 @@
Plugins
*******
+2
View File
@@ -0,0 +1,2 @@
Command Reference
*****************
+2
View File
@@ -0,0 +1,2 @@
System Variables
****************
+2
View File
@@ -0,0 +1,2 @@
Rules
*****
+286
View File
@@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from recommonmark.parser import CommonMarkParser
import sphinx_bootstrap_theme
# -- Project information -----------------------------------------------------
project = u'ESPEasy'
copyright = u'2018, ESPEasy'
author = u'Many'
# The short X.Y version
version = u''
# The full version, including alpha/beta/rc tags
release = u'2.1b1'
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.imgmath',
'sphinx.ext.imgconverter',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
source_parsers = {
'.md': CommonMarkParser,
}
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ['.rst', '.md']
#source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# -- Options for HTML output -------------------------------------------------
html_logo = 'ESPEasy/espeasy_logo.png'
html_favicon = 'ESPEasy/favicon.ico'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
#html_theme = 'sphinx_rtd_theme'
html_theme = 'bootstrap'
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
#html_theme_options = {
# 'canonical_url': '',
# 'analytics_id': '',
# 'logo_only': False,
# 'display_version': True,
# 'prev_next_buttons_location': 'bottom',
# 'style_external_links': False,
# # Toc options
# 'collapse_navigation': True,
# 'sticky_navigation': True,
# 'navigation_depth': 4,
# 'includehidden': True,
# 'titles_only': False
#}
html_theme_options = {
# Navigation bar title. (Default: ``project`` value)
'navbar_title': "ESPEasy",
# Tab name for entire site. (Default: "Site")
'navbar_site_name': "Site",
# A list of tuples containing pages or urls to link to.
# Valid tuples should be in the following forms:
# (name, page) # a link to a page
# (name, "/aa/bb", 1) # a link to an arbitrary relative url
# (name, "http://example.com", True) # arbitrary absolute url
# Note the "1" or "True" value above as the third argument to indicate
# an arbitrary url.
'navbar_links': [
("Rules", "rules"),
("Forum", "https://www.letscontrolit.com/forum/viewforum.php?f=1", True),
],
# Render the next and previous page links in navbar. (Default: true)
'navbar_sidebarrel': True,
# Render the current pages TOC in the navbar. (Default: true)
'navbar_pagenav': True,
# Tab name for the current pages TOC. (Default: "Page")
'navbar_pagenav_name': "Page",
# Global TOC depth for "site" navbar tab. (Default: 1)
# Switching to -1 shows all levels.
'globaltoc_depth': 2,
# Include hidden TOCs in Site navbar?
#
# Note: If this is "false", you cannot have mixed ``:hidden:`` and
# non-hidden ``toctree`` directives in the same page, or else the build
# will break.
#
# Values: "true" (default) or "false"
'globaltoc_includehidden': "true",
# HTML navbar class (Default: "navbar") to attach to <div> element.
# For black navbar, do "navbar navbar-inverse"
'navbar_class': "navbar navbar-inverse",
# Fix navigation bar to top of page?
# Values: "true" (default) or "false"
'navbar_fixed_top': "true",
# Location of link to source.
# Options are "nav" (default), "footer" or anything else to exclude.
'source_link_position': "none",
# Bootswatch (http://bootswatch.com/) theme.
#
# Options are nothing (default) or the name of a valid theme
# such as "cosmo" or "sandstone".
#
# The set of valid themes depend on the version of Bootstrap
# that's used (the next config option).
#
# Currently, the supported themes are:
# - Bootstrap 2: https://bootswatch.com/2
# - Bootstrap 3: https://bootswatch.com/3
'bootswatch_theme': "cerulean",
# Choose Bootstrap version.
# Values: "3" (default) or "2" (in quotes)
'bootstrap_version': "3",
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'ESPEasydoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'ESPEasy.tex', u'ESPEasy Documentation',
u'Many', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'espeasy', u'ESPEasy Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'ESPEasy', u'ESPEasy Documentation',
author, 'ESPEasy', 'ESP82xx/ESP32 firmware to connect people with any level of experience to system automation.',
'Miscellaneous'),
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
# -- Extension configuration -------------------------------------------------
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
+38
View File
@@ -0,0 +1,38 @@
.. ESPEasy documentation master file, created by
sphinx-quickstart on Sat Oct 06 22:34:34 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to ESPEasy's documentation!
===================================
.. toctree::
:maxdepth: 2
:caption: Table of Contents
ESPEasy/AboutUs.rst
ESPEasy/FunctionBlocks.rst
Controller/Controller.rst
Plugin/Plugin.rst
Rules/Rules.rst
.. raw:: latex
\appendix
.. toctree::
:caption: Appendix
Participate/Documentation.rst
Reference/Command.rst
Reference/SystemVariable.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+4 -4
View File
@@ -117,7 +117,7 @@ build_unflags =
build_flags = -D BUILD_GIT='"${sysenv.TRAVIS_TAG}"'
-D NDEBUG
-lstdc++ -lsupc++
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
-DVTABLES_IN_FLASH
@@ -133,15 +133,15 @@ monitor_speed = 115200
[normal]
platform = ${common.platform}
build_flags = -DCONT_STACKSIZE=5120
build_flags =
[testing]
platform = ${core_2_4_1.platform}
build_flags = -DCONT_STACKSIZE=5120 -DPLUGIN_BUILD_TESTING
build_flags = -DPLUGIN_BUILD_TESTING
[dev]
platform = ${core_2_4_1.platform}
build_flags = -DCONT_STACKSIZE=5120 -DPLUGIN_BUILD_DEV
build_flags = -DPLUGIN_BUILD_DEV
[ir]
lib_ignore = ESP32_ping, ESP32WebServer
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# Check for documentation only changes.
# Source: https://github.com/dev-id/Magic-Spoiler/blob/8111a06ab6682e020169991d5e2aa4fa503d787f/preflight.sh
set -e
MAIN_BRANCH="mega"
MD=".md$"
DOCS="^docs/"
CHANGED_FILES=`git diff --name-only ${MAIN_BRANCH}...${TRAVIS_COMMIT}`
ONLY_READMES=True
DOCUMENTATION_CHANGED=False
for CHANGED_FILE in $CHANGED_FILES; do
if ! [[ $CHANGED_FILE =~ $MD || $CHANGED_FILE =~ $DOCS ]]; then
ONLY_READMES=False
# break
else
DOCUMENTATION_CHANGED=True
fi
done
#if [[ $ONLY_READMES == True ]]; then
# echo "Only documentation files found, exiting."
# travis_terminate 0
# exit 1
#else
# echo "Non-documentation files found, continuing with build."
#fi
if [[ $DOCUMENTATION_CHANGED == True ]]; then
echo "Generate documentation."
cd docs
make html
fi
+1
View File
@@ -79,6 +79,7 @@ String doExecuteCommand(const char * cmd, struct EventStruct *event, const char*
break;
}
case 'l': {
COMMAND_CASE("let" , Command_Rules_Let); // Rules.h
COMMAND_CASE("load" , Command_Settings_Load); // Settings.h
COMMAND_CASE("logentry" , Command_logentry); // Diagnostic.h
COMMAND_CASE("lowmem" , Command_Lowmem); // Diagnostic.h
+1 -1
View File
@@ -33,7 +33,7 @@ String Command_HTTP_SendToHTTP(struct EventStruct *event, const char* Line)
}
String request = do_create_http_request(hostportString, F("GET"), path);
addLog(LOG_LEVEL_DEBUG, request);
send_via_http(F("Command_HTTP_SendToHTTP"), client, request);
send_via_http(F("Command_HTTP_SendToHTTP"), client, request, false);
}
}
return return_command_success();
+11
View File
@@ -34,4 +34,15 @@ String Command_Rules_Events(struct EventStruct *event, const char* Line)
return return_command_success();
}
String Command_Rules_Let(struct EventStruct *event, const char* Line)
{
char TmpStr1[INPUT_COMMAND_SIZE];
if (GetArgv(Line, TmpStr1, 3)) {
float result = 0;
Calculate(TmpStr1, &result);
customFloatVar[event->Par1-1] = result;
}
return return_command_success();
}
#endif // COMMAND_RULES_H
+5 -5
View File
@@ -132,7 +132,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
bool MQTTConnect(int controller_idx)
{
++mqtt_reconnect_count;
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(controller_idx, ControllerSettings);
if (!ControllerSettings.checkHostReachable(true))
return false;
@@ -293,9 +293,9 @@ void scheduleNextMQTTdelayQueue() {
void processMQTTdelayQueue() {
START_TIMER;
MQTT_queue_element element;
if (!MQTTDelayHandler.getNext(element)) return;
if (MQTTclient.publish(element._topic.c_str(), element._payload.c_str(), element._retained)) {
MQTT_queue_element* element(MQTTDelayHandler.getNext());
if (element == NULL) return;
if (MQTTclient.publish(element->_topic.c_str(), element->_payload.c_str(), element->_retained)) {
setIntervalTimerOverride(TIMER_MQTT, 10); // Make sure the MQTT is being processed as soon as possible.
MQTTDelayHandler.markProcessed(true);
} else {
@@ -316,7 +316,7 @@ void processMQTTdelayQueue() {
\*********************************************************************************************/
void MQTTStatus(String& status)
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
LoadControllerSettings(enabledMqttController, ControllerSettings);
+152 -94
View File
@@ -79,7 +79,7 @@
#define DEFAULT_MQTT_LWT_TOPIC "" // Default lwt topic
#define DEFAULT_MQTT_LWT_CONNECT_MESSAGE "Connected" // Default lwt message
#define DEFAULT_MQTT_LWT_DISCONNECT_MESSAGE "Connection Lost" // Default lwt message
#define DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID 0
#define DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID 0
#define DEFAULT_USE_NTP false // (true|false) Use NTP Server
#define DEFAULT_NTP_HOST "" // NTP Server Hostname
@@ -230,7 +230,7 @@
#define CONTROLLER_DELAY_QUEUE_RETRY_DFLT 10
// Timeout of the client in msec.
#define CONTROLLER_CLIENTTIMEOUT_MAX 1000
#define CONTROLLER_CLIENTTIMEOUT_DFLT 100
#define CONTROLLER_CLIENTTIMEOUT_DFLT 300
#define PLUGIN_INIT_ALL 1
@@ -292,6 +292,7 @@
#define NPLUGIN_NOT_FOUND 255
#define LOG_LEVEL_NONE 0
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_INFO 2
#define LOG_LEVEL_DEBUG 3
@@ -338,6 +339,7 @@
#define RULES_BUFFER_SIZE 64
#define NAME_FORMULA_LENGTH_MAX 40
#define RULES_IF_MAX_NESTING_LEVEL 4
#define CUSTOM_VARS_MAX 16
#define UDP_PACKETSIZE_MAX 2048
@@ -599,14 +601,17 @@ bool MQTTclient_should_reconnect = true;
bool MQTTclient_connected = false;
int mqtt_reconnect_count = 0;
//NTP status
bool statusNTPInitialized = false;
// mqtt import status
bool P037_MQTTImport_connected = false;
// udp protocol stuff (syslog, global sync, node info list, ntp time)
WiFiUDP portUDP;
class TimingStats;
#define LOADFILE_STATS 0
#define LOOP_STATS 1
/*********************************************************************************************\
* CRCStruct
\*********************************************************************************************/
@@ -674,6 +679,16 @@ struct SecurityStruct
uint8_t md5[16];
} SecuritySettings;
/*********************************************************************************************\
* Custom Variables for usage in rules and http.
* Syntax: %vX%
* usage:
* let,1,10
* if %v1%=10 do ...
\*********************************************************************************************/
float customFloatVar[CUSTOM_VARS_MAX];
/*********************************************************************************************\
* SettingsStruct
\*********************************************************************************************/
@@ -695,6 +710,10 @@ struct SettingsStruct
if (VariousBits1 > (1 << 30)) VariousBits1 = 0;
}
bool networkSettingsEmpty() {
return (IP[0] == 0 && Gateway[0] == 0 && Subnet[0] == 0 && DNS[0] == 0);
}
void clearNetworkSettings() {
for (byte i = 0; i < 4; ++i) {
IP[i] = 0;
@@ -894,13 +913,30 @@ SettingsStruct* SettingsStruct_ptr = new SettingsStruct;
SettingsStruct& Settings = *SettingsStruct_ptr;
*/
/*********************************************************************************************\
* ControllerSettingsStruct
* Analyze SettingsStruct and report inconsistencies
* Not a member function to be able to use the F-macro
\*********************************************************************************************/
bool SettingsCheck(String& error) {
error = "";
if (!Settings.networkSettingsEmpty()) {
if (Settings.IP[0] == 0 || Settings.Gateway[0] == 0 || Settings.Subnet[0] == 0 || Settings.DNS[0] == 0) {
error += F("Error: Either fill all IP settings fields or leave all empty");
}
}
return error.length() == 0;
}
/*********************************************************************************************\
* ControllerSettingsStruct definition
\*********************************************************************************************/
struct ControllerSettingsStruct
{
ControllerSettingsStruct() : UseDNS(false), Port(0),
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10), DeleteOldest(false), ClientTimeout(100) {
MinimalTimeBetweenMessages(100), MaxQueueDepth(10), MaxRetry(10),
DeleteOldest(false), ClientTimeout(100), MustCheckReply(false) {
for (byte i = 0; i < 4; ++i) {
IP[i] = 0;
}
@@ -925,6 +961,7 @@ struct ControllerSettingsStruct
unsigned int MaxRetry;
boolean DeleteOldest; // Action to perform when buffer full, delete oldest, or ignore newest.
unsigned int ClientTimeout;
boolean MustCheckReply; // When set to false, a sent message is considered always successful.
void validate() {
if (Port > 65535) Port = 0;
@@ -932,8 +969,9 @@ struct ControllerSettingsStruct
MinimalTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT;
if (MaxQueueDepth > CONTROLLER_DELAY_QUEUE_DEPTH_MAX) MaxQueueDepth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT;
if (MaxRetry > CONTROLLER_DELAY_QUEUE_RETRY_MAX) MaxRetry = CONTROLLER_DELAY_QUEUE_RETRY_MAX;
if (ClientTimeout < 10 || ClientTimeout > CONTROLLER_CLIENTTIMEOUT_MAX)
if (ClientTimeout < 10 || ClientTimeout > CONTROLLER_CLIENTTIMEOUT_MAX) {
ClientTimeout = CONTROLLER_CLIENTTIMEOUT_DFLT;
}
}
IPAddress getIP() const {
@@ -1036,6 +1074,11 @@ private:
};
typedef std::shared_ptr<ControllerSettingsStruct> ControllerSettingsStruct_ptr_type;
#define MakeControllerSettings(T) ControllerSettingsStruct_ptr_type ControllerSettingsStruct_ptr(new ControllerSettingsStruct());\
ControllerSettingsStruct& T = *ControllerSettingsStruct_ptr;
/*********************************************************************************************\
* NotificationSettingsStruct
@@ -1067,6 +1110,11 @@ struct NotificationSettingsStruct
//its safe to extend this struct, up to 4096 bytes, default values in config are 0
};
typedef std::shared_ptr<NotificationSettingsStruct> NotificationSettingsStruct_ptr_type;
#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new NotificationSettingsStruct());\
NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr;
/*********************************************************************************************\
* ExtraTaskSettingsStruct
\*********************************************************************************************/
@@ -1150,22 +1198,36 @@ struct ExtraTaskSettingsStruct
struct EventStruct
{
EventStruct() :
Data(NULL), idx(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
Source(0), TaskIndex(TASKS_MAX), ControllerIndex(0), ProtocolIndex(0), NotificationIndex(0),
BaseVarIndex(0), idx(0), sensorType(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0),
OriginTaskIndex(0), Data(NULL) {}
BaseVarIndex(0), sensorType(0), OriginTaskIndex(0) {}
EventStruct(const struct EventStruct& event):
Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
, BaseVarIndex(event.BaseVarIndex), idx(event.idx), sensorType(event.sensorType)
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
, OriginTaskIndex(event.OriginTaskIndex)
, String1(event.String1)
String1(event.String1)
, String2(event.String2)
, String3(event.String3)
, String4(event.String4)
, String5(event.String5)
, Data(event.Data) {}
, Data(event.Data)
, idx(event.idx)
, Par1(event.Par1), Par2(event.Par2), Par3(event.Par3), Par4(event.Par4), Par5(event.Par5)
, Source(event.Source), TaskIndex(event.TaskIndex), ControllerIndex(event.ControllerIndex)
, ProtocolIndex(event.ProtocolIndex), NotificationIndex(event.NotificationIndex)
, BaseVarIndex(event.BaseVarIndex), sensorType(event.sensorType)
, OriginTaskIndex(event.OriginTaskIndex)
{}
String String1;
String String2;
String String3;
String String4;
String String5;
byte *Data;
int idx;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
byte Source;
byte TaskIndex; // index position in TaskSettings array, 0-11
byte ControllerIndex; // index position in Settings.Controller, 0-3
@@ -1174,20 +1236,8 @@ struct EventStruct
//Edwin: Not needed, and wasnt used. We can determine the protocol index with getNotificationProtocolIndex(NotificationIndex)
// byte NotificationProtocolIndex; // index position in notification array, depending on which controller plugins are loaded.
byte BaseVarIndex;
int idx;
byte sensorType;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
byte OriginTaskIndex;
String String1;
String String2;
String String3;
String String4;
String String5;
byte *Data;
};
@@ -1197,18 +1247,19 @@ struct EventStruct
#define LOG_STRUCT_MESSAGE_SIZE 128
#ifdef ESP32
#define LOG_STRUCT_MESSAGE_LINES 30
#define LOG_BUFFER_EXPIRE 30000 // Time after which a buffered log item is considered expired.
#else
#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV)
#define LOG_STRUCT_MESSAGE_LINES 10
#else
#define LOG_STRUCT_MESSAGE_LINES 15
#endif
#define LOG_BUFFER_EXPIRE 5000 // Time after which a buffered log item is considered expired.
#endif
struct LogStruct {
LogStruct() : write_idx(0), read_idx(0) {
LogStruct() : write_idx(0), read_idx(0), lastReadTimeStamp(0) {
for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) {
Message[i].reserve(LOG_STRUCT_MESSAGE_SIZE);
timeStamp[i] = 0;
log_level[i] = 0;
}
@@ -1226,6 +1277,7 @@ struct LogStruct {
if (linelength > LOG_STRUCT_MESSAGE_SIZE-1)
linelength = LOG_STRUCT_MESSAGE_SIZE-1;
Message[write_idx] = "";
Message[write_idx].reserve(linelength);
for (unsigned i = 0; i < linelength; ++i) {
Message[write_idx] += *(line + i);
}
@@ -1234,6 +1286,7 @@ struct LogStruct {
// Read the next item and append it to the given string.
// Returns whether new lines are available.
bool get(String& output, const String& lineEnd) {
lastReadTimeStamp = millis();
if (!isEmpty()) {
read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES;
output += formatLine(read_idx, lineEnd);
@@ -1242,6 +1295,7 @@ struct LogStruct {
}
String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) {
lastReadTimeStamp = millis();
logLinesAvailable = false;
if (isEmpty()) {
return "";
@@ -1255,31 +1309,15 @@ struct LogStruct {
return output;
}
bool get(String& output, const String& lineEnd, int line) {
int tmpread((write_idx + 1+line) % LOG_STRUCT_MESSAGE_LINES);
if (timeStamp[tmpread] != 0) {
output += formatLine(tmpread, lineEnd);
}
return !isEmpty();
}
bool getAll(String& output, const String& lineEnd) {
int tmpread((write_idx + 1) % LOG_STRUCT_MESSAGE_LINES);
bool someAdded = false;
while (tmpread != write_idx) {
if (timeStamp[tmpread] != 0) {
output += formatLine(tmpread, lineEnd);
someAdded = true;
}
tmpread = (tmpread + 1)% LOG_STRUCT_MESSAGE_LINES;
}
return someAdded;
}
bool isEmpty() {
return (write_idx == read_idx);
}
bool logActiveRead() {
clearExpiredEntries();
return timePassedSince(lastReadTimeStamp) < LOG_BUFFER_EXPIRE;
}
private:
String formatLine(int index, const String& lineEnd) {
String output;
@@ -1303,20 +1341,38 @@ struct LogStruct {
return output;
}
void clearExpiredEntries() {
if (isEmpty()) {
return;
}
if (timePassedSince(lastReadTimeStamp) > LOG_BUFFER_EXPIRE) {
// Clear the entire log.
// If web log is the only log active, it will not be checked again until it is read.
for (read_idx = 0; read_idx < LOG_STRUCT_MESSAGE_LINES; ++read_idx) {
Message[read_idx] = String(); // Free also the reserved memory.
timeStamp[read_idx] = 0;
log_level[read_idx] = 0;
}
read_idx = 0;
write_idx = 0;
}
}
int write_idx;
int read_idx;
unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES];
unsigned long lastReadTimeStamp;
byte log_level[LOG_STRUCT_MESSAGE_LINES];
String Message[LOG_STRUCT_MESSAGE_LINES];
} Logging;
std::deque<char> serialLogBuffer;
unsigned long last_serial_log_emptied = 0;
unsigned long last_serial_log_read = 0;
byte highest_active_log_level = 0;
bool log_to_serial_disabled = false;
bool log_to_serial_disabled_temporary = false;
// Do this in a template to prevent casting to String when not needed.
#define addLog(L,S) if (loglevelActiveFor(L)) { addToLog(L,S); }
@@ -1340,16 +1396,16 @@ struct DeviceStruct
byte Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin
byte VType; // Type of value the plugin will return, used only for Domoticz
byte Ports; // Port to use when device has multiple I/O pins (N.B. not used much)
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
boolean PullUpOption; // Allow to set internal pull-up resistors.
boolean InverseLogicOption; // Allow to invert the boolean state (e.g. a switch)
boolean FormulaOption; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
boolean Custom;
boolean SendDataOption; // Allow to send data to a controller.
boolean GlobalSyncOption; // No longer used. Was used for ESPeasy values sync between nodes
boolean TimerOption; // Allow to set the "Interval" timer for the plugin.
boolean TimerOptional; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
boolean DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx
bool PullUpOption : 1; // Allow to set internal pull-up resistors.
bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch)
bool FormulaOption : 1; // Allow to enter a formula to convert values during read. (not possible with Custom enabled)
bool Custom : 1;
bool SendDataOption : 1; // Allow to send data to a controller.
bool GlobalSyncOption : 1; // No longer used. Was used for ESPeasy values sync between nodes
bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin.
bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay)
bool DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals)
};
typedef std::vector<DeviceStruct> DeviceVector;
DeviceVector Device;
@@ -1414,16 +1470,16 @@ NodesMap Nodes;
struct systemTimerStruct
{
systemTimerStruct() :
timer(0), plugin(0), TaskIndex(-1), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0) {}
timer(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0), TaskIndex(-1), plugin(0) {}
unsigned long timer;
byte plugin;
int16_t TaskIndex;
int Par1;
int Par2;
int Par3;
int Par4;
int Par5;
int16_t TaskIndex;
byte plugin;
};
std::map<unsigned long, systemTimerStruct> systemTimers;
@@ -1432,11 +1488,11 @@ std::map<unsigned long, systemTimerStruct> systemTimers;
\*********************************************************************************************/
struct pinStatesStruct
{
pinStatesStruct() : plugin(0), index(0), mode(0), value(0) {}
pinStatesStruct() : value(0), plugin(0), index(0), mode(0) {}
uint16_t value;
byte plugin;
byte index;
byte mode;
uint16_t value;
} pinStates[PINSTATE_TABLE_MAX];
@@ -1793,31 +1849,32 @@ unsigned long timediff_calls = 0;
unsigned long timediff_cpu_cycles_total = 0;
#define LOADFILE_STATS 0
#define LOOP_STATS 1
#define PLUGIN_CALL_50PS 2
#define PLUGIN_CALL_10PS 3
#define PLUGIN_CALL_10PSU 4
#define PLUGIN_CALL_1PS 5
#define SENSOR_SEND_TASK 6
#define SEND_DATA_STATS 7
#define COMPUTE_FORMULA_STATS 8
#define PROC_SYS_TIMER 9
#define SET_NEW_TIMER 10
#define TIME_DIFF_COMPUTE 11
#define MQTT_DELAY_QUEUE 12
#define C001_DELAY_QUEUE 13
#define C002_DELAY_QUEUE 14
#define C003_DELAY_QUEUE 15
#define C004_DELAY_QUEUE 16
#define C005_DELAY_QUEUE 17
#define C006_DELAY_QUEUE 18
#define C007_DELAY_QUEUE 19
#define C008_DELAY_QUEUE 20
#define C009_DELAY_QUEUE 21
#define C010_DELAY_QUEUE 22
#define C011_DELAY_QUEUE 23
#define C012_DELAY_QUEUE 24
#define C013_DELAY_QUEUE 25
#define SAVEFILE_STATS 1
#define LOOP_STATS 2
#define PLUGIN_CALL_50PS 3
#define PLUGIN_CALL_10PS 4
#define PLUGIN_CALL_10PSU 5
#define PLUGIN_CALL_1PS 6
#define SENSOR_SEND_TASK 7
#define SEND_DATA_STATS 8
#define COMPUTE_FORMULA_STATS 9
#define PROC_SYS_TIMER 10
#define SET_NEW_TIMER 11
#define TIME_DIFF_COMPUTE 12
#define MQTT_DELAY_QUEUE 13
#define C001_DELAY_QUEUE 14
#define C002_DELAY_QUEUE 15
#define C003_DELAY_QUEUE 16
#define C004_DELAY_QUEUE 17
#define C005_DELAY_QUEUE 18
#define C006_DELAY_QUEUE 19
#define C007_DELAY_QUEUE 20
#define C008_DELAY_QUEUE 21
#define C009_DELAY_QUEUE 22
#define C010_DELAY_QUEUE 23
#define C011_DELAY_QUEUE 24
#define C012_DELAY_QUEUE 25
#define C013_DELAY_QUEUE 26
@@ -1826,13 +1883,14 @@ unsigned long timediff_cpu_cycles_total = 0;
#define START_TIMER const unsigned statisticsTimerStart(micros());
#define STOP_TIMER_TASK(T,F) if (mustLogFunction(F)) pluginStats[T*32 + F].add(usecPassedSince(statisticsTimerStart));
#define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
//#define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
#define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart));
String getMiscStatsName(int stat) {
switch (stat) {
case LOADFILE_STATS: return F("Load File");
case SAVEFILE_STATS: return F("Save File");
case LOOP_STATS: return F("Loop");
case PLUGIN_CALL_50PS: return F("Plugin call 50 p/s ");
case PLUGIN_CALL_10PS: return F("Plugin call 10 p/s ");
+2 -5
View File
@@ -839,13 +839,10 @@ void SensorSendTask(byte TaskIndex)
{
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
{
String spreValue = String(preValue[varNr]);
String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
float value = UserVar[varIndex + varNr];
formula.replace(F("%pvalue%"), String(preValue[varNr]));
formula.replace(F("%value%"), String(UserVar[varIndex + varNr]));
float result = 0;
String svalue = String(value);
formula.replace(F("%pvalue%"), spreValue);
formula.replace(F("%value%"), svalue);
byte error = Calculate(formula.c_str(), &result);
if (error == 0)
UserVar[varIndex + varNr] = result;
+33 -6
View File
@@ -77,7 +77,7 @@ String BuildFixes()
Settings.UseRTOSMultitasking = false;
Settings.Pin_Reset = -1;
Settings.SyslogFacility = DEFAULT_SYSLOG_FACILITY;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID;
Settings.StructSize = sizeof(Settings);
}
@@ -150,7 +150,11 @@ String SaveSettings(void)
Settings.validate();
err=SaveToFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof(Settings));
if (err.length())
return(err);
return(err);
// Must check this after saving, or else it is not possible to fix multiple
// issues which can only corrected on different pages.
if (!SettingsCheck(err)) return err;
// }
memcpy( SecuritySettings.ProgmemMd5, CRCValues.runTimeMD5, 16);
@@ -413,6 +417,13 @@ String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
return(SaveToFile(CustomTaskSettings_Type, TaskIndex, (char*)FILE_CONFIG, memAddress, datasize));
}
String getCustomTaskSettingsError(byte varNr) {
String error = F("Error: Text too long for line ");
error += varNr + 1;
error += '\n';
return error;
}
/********************************************************************************************\
Clear custom task settings
@@ -553,20 +564,29 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
addLog(LOG_LEVEL_ERROR, log);
return log;
}
START_TIMER;
checkRAM(F("SaveToFile"));
FLASH_GUARD();
{
String log = F("SaveToFile: free stack: ");
log += getCurrentFreeStack();
addLog(LOG_LEVEL_INFO, log);
}
delay(1);
unsigned long timer = millis() + 50;
fs::File f = SPIFFS.open(fname, "r+");
if (f) {
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
byte *pointerToByteToSave = memAddress;
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(*pointerToByteToSave), fname);
pointerToByteToSave++;
if (timeOutReached(timer)) {
timer += 50;
delay(1);
}
}
f.close();
String log = F("FILE : Saved ");
@@ -579,6 +599,12 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
addLog(LOG_LEVEL_ERROR, log);
return log;
}
STOP_TIMER(SAVEFILE_STATS);
{
String log = F("SaveToFile: free stack after: ");
log += getCurrentFreeStack();
addLog(LOG_LEVEL_INFO, log);
}
//OK
return String();
@@ -635,10 +661,10 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
addLog(LOG_LEVEL_ERROR, log);
return log;
}
delay(1);
START_TIMER;
checkRAM(F("LoadFromFile"));
fs::File f = SPIFFS.open(fname, "r+");
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(offset, fs::SeekSet), fname);
@@ -646,6 +672,7 @@ String LoadFromFile(char* fname, int offset, byte* memAddress, int datasize)
f.close();
STOP_TIMER(LOADFILE_STATS);
delay(1);
return(String());
}
+1 -1
View File
@@ -121,7 +121,7 @@ int32_t I2C_read32_reg(uint8_t i2caddr, byte reg) {
Wire.beginTransmission(i2caddr);
Wire.write((uint8_t)reg);
Wire.endTransmission(END_TRANSMISSION_FLAG);
Wire.requestFrom(i2caddr, (byte)3);
Wire.requestFrom(i2caddr, (byte)4);
value = (((int32_t)Wire.read()) <<24) | (((uint32_t)Wire.read()) << 16) | (Wire.read() << 8) | Wire.read();
return value;
+124 -37
View File
@@ -445,8 +445,7 @@ void delayBackground(unsigned long delay)
void parseCommandString(struct EventStruct *event, const String& string)
{
checkRAM(F("parseCommandString"));
char TmpStr1[INPUT_COMMAND_SIZE];
TmpStr1[0] = 0;
char *TmpStr1 = new char[INPUT_COMMAND_SIZE]();
event->Par1 = 0;
event->Par2 = 0;
event->Par3 = 0;
@@ -458,6 +457,7 @@ void parseCommandString(struct EventStruct *event, const String& string)
if (GetArgv(string.c_str(), TmpStr1, 4)) { event->Par3 = CalculateParam(TmpStr1); }
if (GetArgv(string.c_str(), TmpStr1, 5)) { event->Par4 = CalculateParam(TmpStr1); }
if (GetArgv(string.c_str(), TmpStr1, 6)) { event->Par5 = CalculateParam(TmpStr1); }
delete[] TmpStr1;
}
/********************************************************************************************\
@@ -568,6 +568,7 @@ boolean GetArgv(const char *string, char *argv, unsigned int argc) {
boolean GetArgv(const char *string, char *argv, unsigned int argv_size, unsigned int argc)
{
memset(argv, 0, argv_size);
size_t string_len = strlen(string);
unsigned int string_pos = 0, argv_pos = 0, argc_pos = 0;
char c, d;
@@ -812,7 +813,7 @@ void ResetFactory(void)
Settings.MQTTRetainFlag = DEFAULT_MQTT_RETAIN;
Settings.MessageDelay = DEFAULT_MQTT_DELAY;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNANE_AS_CLIENTID;
Settings.MQTTUseUnitNameAsClientId = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID;
Settings.UseNTP = DEFAULT_USE_NTP;
strcpy_P(Settings.NTPHost, PSTR(DEFAULT_NTP_HOST));
@@ -850,7 +851,7 @@ void ResetFactory(void)
SaveSettings();
#if DEFAULT_CONTROLLER
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
strcpy_P(ControllerSettings.Subscribe, PSTR(DEFAULT_SUB));
strcpy_P(ControllerSettings.Publish, PSTR(DEFAULT_PUB));
strcpy_P(ControllerSettings.MQTTLwtTopic, PSTR(DEFAULT_MQTT_LWT_TOPIC));
@@ -1154,16 +1155,31 @@ void initLog()
/********************************************************************************************\
Logging
\*********************************************************************************************/
String getLogLevelDisplayString(byte index, int& logLevel) {
String getLogLevelDisplayString(int logLevel) {
switch (logLevel) {
case LOG_LEVEL_NONE: return F("None");
case LOG_LEVEL_ERROR: return F("Error");
case LOG_LEVEL_INFO: return F("Info");
case LOG_LEVEL_DEBUG: return F("Debug");
case LOG_LEVEL_DEBUG_MORE: return F("Debug More");
case LOG_LEVEL_DEBUG_DEV: return F("Debug dev");
default:
return "";
}
}
String getLogLevelDisplayStringFromIndex(byte index, int& logLevel) {
switch (index) {
case 0: logLevel = LOG_LEVEL_ERROR; return F("Error");
case 1: logLevel = LOG_LEVEL_INFO; return F("Info");
case 2: logLevel = LOG_LEVEL_DEBUG; return F("Debug");
case 3: logLevel = LOG_LEVEL_DEBUG_MORE; return F("Debug More");
case 4: logLevel = LOG_LEVEL_DEBUG_DEV; return F("Debug dev");
case 0: logLevel = LOG_LEVEL_ERROR; break;
case 1: logLevel = LOG_LEVEL_INFO; break;
case 2: logLevel = LOG_LEVEL_DEBUG; break;
case 3: logLevel = LOG_LEVEL_DEBUG_MORE; break;
case 4: logLevel = LOG_LEVEL_DEBUG_DEV; break;
default: logLevel = -1; return "";
}
return getLogLevelDisplayString(logLevel);
}
void addToLog(byte loglevel, const String& string)
@@ -1178,14 +1194,6 @@ void addToLog(byte logLevel, const __FlashStringHelper* flashString)
addToLog(logLevel, s.c_str());
}
bool SerialAvailableForWrite() {
if (!Settings.UseSerial) return false;
#if defined(ESP8266)
if (!Serial.availableForWrite()) return false; // UART FIFO overflow or TX disabled.
#endif
return true;
}
void disableSerialLog() {
log_to_serial_disabled = true;
setLogLevelFor(LOG_TO_SERIAL, 0);
@@ -1207,10 +1215,20 @@ void setLogLevelFor(byte destination, byte logLevel) {
void updateLogLevelCache() {
byte max_lvl = 0;
max_lvl = _max(max_lvl, Settings.SerialLogLevel);
if (!log_to_serial_disabled && serialLogActiveRead()) {
max_lvl = _max(max_lvl, Settings.SerialLogLevel);
if (Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE)
Serial.setDebugOutput(true);
} else {
Serial.setDebugOutput(false);
}
max_lvl = _max(max_lvl, Settings.SyslogLevel);
max_lvl = _max(max_lvl, Settings.WebLogLevel);
if (Logging.logActiveRead()) {
max_lvl = _max(max_lvl, Settings.WebLogLevel);
}
#ifdef FEATURE_SD
max_lvl = _max(max_lvl, Settings.SDLogLevel);
#endif
highest_active_log_level = max_lvl;
}
@@ -1218,14 +1236,42 @@ bool loglevelActiveFor(byte logLevel) {
return loglevelActive(logLevel, highest_active_log_level);
}
byte getSerialLogLevel() {
byte logLevelSettings = 0;
if (!Settings.UseSerial) return 0;
if (log_to_serial_disabled || log_to_serial_disabled_temporary) return 0;
logLevelSettings = Settings.SerialLogLevel;
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED){
logLevelSettings = 2;
}
/*
if (!serialLogActiveRead()) {
if (logLevelSettings != 0) {
updateLogLevelCache();
}
logLevelSettings = 0;
}
*/
return logLevelSettings;
}
byte getWebLogLevel() {
byte logLevelSettings = 0;
if (Logging.logActiveRead()) {
logLevelSettings = Settings.WebLogLevel;
} else {
if (Settings.WebLogLevel != 0) {
updateLogLevelCache();
}
}
return logLevelSettings;
}
boolean loglevelActiveFor(byte destination, byte logLevel) {
byte logLevelSettings = 0;
switch (destination) {
case LOG_TO_SERIAL: {
if (!SerialAvailableForWrite()) return false;
logLevelSettings = Settings.SerialLogLevel;
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED)
logLevelSettings = 2;
logLevelSettings = getSerialLogLevel();
break;
}
case LOG_TO_SYSLOG: {
@@ -1233,11 +1279,13 @@ boolean loglevelActiveFor(byte destination, byte logLevel) {
break;
}
case LOG_TO_WEBLOG: {
logLevelSettings = Settings.WebLogLevel;
logLevelSettings = getWebLogLevel();
break;
}
case LOG_TO_SDCARD: {
#ifdef FEATURE_SD
logLevelSettings = Settings.SDLogLevel;
#endif
break;
}
default:
@@ -1290,24 +1338,58 @@ void addToLog(byte logLevel, const char *line)
void process_serialLogBuffer() {
if (serialLogBuffer.size() == 0) return;
if (timePassedSince(last_serial_log_emptied) > 10000) {
last_serial_log_emptied = millis();
serialLogBuffer.clear();
return;
}
size_t snip = 128; // Some default, ESP32 doesn't have the availableForWrite function yet.
#if defined(ESP8266)
snip = Serial.availableForWrite();
#endif
if (snip > 0) last_serial_log_emptied = millis();
size_t bytes_to_write = serialLogBuffer.size();
if (snip < bytes_to_write) bytes_to_write = snip;
for (size_t i = 0; i < bytes_to_write; ++i) {
Serial.write(serialLogBuffer.front());
serialLogBuffer.pop_front();
if (snip > 0) {
last_serial_log_read = millis();
size_t bytes_to_write = serialLogBuffer.size();
if (snip < bytes_to_write) bytes_to_write = snip;
for (size_t i = 0; i < bytes_to_write; ++i) {
Serial.write(serialLogBuffer.front());
serialLogBuffer.pop_front();
}
}
}
void tempDisableSerialLog(bool setToDisabled) {
if (log_to_serial_disabled_temporary == setToDisabled) return;
// FIXME TD-er: For some reason disabling serial log will not enable it again.
// log_to_serial_disabled_temporary = setToDisabled;
// updateLogLevelCache();
}
bool serialLogActiveRead() {
if (!Settings.UseSerial) return false;
// Some default, ESP32 doesn't have the availableForWrite function yet.
// Not sure how to detect read activity on an ESP32.
size_t tx_free = 128;
#if defined(ESP8266)
tx_free = Serial.availableForWrite();
#endif
static size_t prev_tx_free = 0;
if (tx_free < prev_tx_free) {
prev_tx_free = tx_free;
tempDisableSerialLog(false);
return true;
}
// Must always set it or else it will never recover from prev_tx_free == 0
prev_tx_free = tx_free;
if (timePassedSince(last_serial_log_read) > LOG_BUFFER_EXPIRE) {
serialLogBuffer.clear();
// Just add some marker to get it going again when the serial buffer is
// read again and the serial log level was temporary set to 0 since nothing was read.
if (Settings.SerialLogLevel > 0) {
serialLogBuffer.push_back('\n');
}
tempDisableSerialLog(true);
return false;
}
return true;
}
/********************************************************************************************\
Delayed reboot, in case of issues, do not reboot with high frequency as it might not help...
\*********************************************************************************************/
@@ -2170,6 +2252,7 @@ for (byte x=0; x < RULESETS_MAX; x++){
\*********************************************************************************************/
void rulesProcessing(String& event)
{
if (!Settings.UseRules) return;
checkRAM(F("rulesProcessing"));
unsigned long timer = millis();
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
@@ -2209,6 +2292,7 @@ void rulesProcessing(String& event)
\*********************************************************************************************/
String rulesProcessingFile(const String& fileName, String& event)
{
if (!Settings.UseRules) return "";
checkRAM(F("rulesProcessingFile"));
if (Settings.SerialLogLevel == LOG_LEVEL_DEBUG_DEV){
Serial.print(F("RuleDebug Processing:"));
@@ -2504,7 +2588,7 @@ void processMatchedRule(
{
String tmpString = event.substring(equalsPos + 1);
char tmpParam[INPUT_COMMAND_SIZE];
char* tmpParam = new char[INPUT_COMMAND_SIZE];
tmpParam[0] = 0;
if (GetArgv(tmpString.c_str(),tmpParam,1)) {
@@ -2514,6 +2598,7 @@ void processMatchedRule(
if (GetArgv(tmpString.c_str(),tmpParam,2)) action.replace(F("%eventvalue2%"), tmpParam); // substitute %eventvalue2% in actions with the actual value from the event
if (GetArgv(tmpString.c_str(),tmpParam,3)) action.replace(F("%eventvalue3%"), tmpParam); // substitute %eventvalue3% in actions with the actual value from the event
if (GetArgv(tmpString.c_str(),tmpParam,4)) action.replace(F("%eventvalue4%"), tmpParam); // substitute %eventvalue4% in actions with the actual value from the event
delete[] tmpParam;
}
}
@@ -2815,6 +2900,7 @@ boolean conditionMatch(const String& check)
\*********************************************************************************************/
void rulesTimers()
{
if (!Settings.UseRules) return;
for (byte x = 0; x < RULES_TIMER_MAX; x++)
{
if (!RulesTimer[x].paused && RulesTimer[x].timestamp != 0L) // timer active?
@@ -2837,6 +2923,7 @@ void rulesTimers()
void createRuleEvents(byte TaskIndex)
{
if (!Settings.UseRules) return;
LoadTaskSettings(TaskIndex);
byte BaseVarIndex = TaskIndex * VARS_PER_TASK;
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
+76 -28
View File
@@ -131,7 +131,7 @@ String toString(float value, byte decimals)
String toString(WiFiMode_t mode)
{
String result = F("Undefinited");
String result = F("Undefined");
switch (mode)
{
case WIFI_OFF:
@@ -165,11 +165,13 @@ String doFormatUserVar(byte TaskIndex, byte rel_index, bool mustCheck, bool& isv
const byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
if (Device[DeviceIndex].ValueCount <= rel_index) {
isvalid = false;
String log = F("No sensor value for TaskIndex: ");
log += TaskIndex;
log += F(" varnumber: ");
log += rel_index;
addLog(LOG_LEVEL_ERROR, log);
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("No sensor value for TaskIndex: ");
log += TaskIndex;
log += F(" varnumber: ");
log += rel_index;
addLog(LOG_LEVEL_ERROR, log);
}
return "";
}
if (Device[DeviceIndex].VType == SENSOR_TYPE_LONG) {
@@ -178,11 +180,13 @@ String doFormatUserVar(byte TaskIndex, byte rel_index, bool mustCheck, bool& isv
float f(UserVar[BaseVarIndex + rel_index]);
if (mustCheck && !isValidFloat(f)) {
isvalid = false;
String log = F("Invalid float value for TaskIndex: ");
log += TaskIndex;
log += F(" varnumber: ");
log += rel_index;
addLog(LOG_LEVEL_DEBUG, log);
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("Invalid float value for TaskIndex: ");
log += TaskIndex;
log += F(" varnumber: ");
log += rel_index;
addLog(LOG_LEVEL_DEBUG, log);
}
f = 0;
}
return toString(f, ExtraTaskSettings.TaskDeviceValueDecimals[rel_index]);
@@ -210,13 +214,11 @@ String formatUserVar(struct EventStruct *event, byte rel_index, bool& isvalid)
/*********************************************************************************************\
Wrap a string with given pre- and postfix string.
\*********************************************************************************************/
String wrap_String(const String& string, const String& wrap) {
String result;
result.reserve(string.length() + 2* wrap.length());
result = wrap;
void wrap_String(const String& string, const String& wrap, String& result) {
result += wrap;
result += string;
result += wrap;
return result;
}
/*********************************************************************************************\
@@ -225,19 +227,25 @@ String wrap_String(const String& string, const String& wrap) {
String to_json_object_value(const String& object, const String& value) {
String result;
result.reserve(object.length() + value.length() + 6);
result = wrap_String(object, F("\""));
wrap_String(object, F("\""), result);
result += F(":");
if (value.length() == 0 || !isFloat(value)) {
if (value.indexOf('\n') == -1 && value.indexOf('"') == -1 && value.indexOf(F("Pragma")) == -1) {
result += wrap_String(value, F("\""));
} else {
if (value.length() == 0) {
// Empty string
result += F("\"\"");
} else if (!isFloat(value)) {
// Is not a numerical value, thus wrap with quotes
if (value.indexOf('\n') != -1 || value.indexOf('\r') != -1 || value.indexOf('"') != -1) {
// Must replace characters, so make a deepcopy
String tmpValue(value);
tmpValue.replace('\n', '^');
tmpValue.replace('\r', '^');
tmpValue.replace('"', '\'');
tmpValue.replace(F("Pragma"), F("Bugje!"));
result += wrap_String(tmpValue, F("\""));
wrap_String(tmpValue, F("\""), result);
} else {
wrap_String(value, F("\""), result);
}
} else {
// It is a numerical
result += value;
}
return result;
@@ -280,6 +288,26 @@ String stripQuotes(const String& text) {
return text;
}
bool safe_strncpy(char* dest, const String& source, size_t max_size) {
return safe_strncpy(dest, source.c_str(), max_size);
}
bool safe_strncpy(char* dest, const char* source, size_t max_size) {
if (max_size < 1) return false;
if (dest == NULL) return false;
if (source == NULL) return false;
bool result = true;
memset(dest, 0, max_size);
size_t str_length = strlen(source);
if (str_length >= max_size) {
str_length = max_size;
result = false;
}
strncpy(dest, source, str_length);
dest[max_size - 1] = 0;
return result;
}
/*********************************************************************************************\
Parse a string and get the xth command or parameter in lower case
\*********************************************************************************************/
@@ -379,6 +407,7 @@ void htmlEscape(String & html)
void htmlStrongEscape(String & html)
{
String escaped;
escaped.reserve(html.length());
for (unsigned i = 0; i < html.length(); ++i)
{
if ((html[i] >= 'a' && html[i] <= 'z') || (html[i] >= 'A' && html[i] <= 'Z') || (html[i] >= '0' && html[i] <= '9'))
@@ -531,6 +560,8 @@ void parseSystemVariables(String& s, boolean useURLencode)
SMART_REPL(F("%systm_hm_am%"), getTimeString_ampm(':', false))
SMART_REPL(F("%systime%"), getTimeString(':'))
SMART_REPL(F("%systime_am%"), getTimeString_ampm(':'))
SMART_REPL(F("%sysbuild_date%"), String(CRCValues.compileDate))
SMART_REPL(F("%sysbuild_time%"), String(CRCValues.compileTime))
repl(F("%sysname%"), Settings.Name, s, useURLencode);
// valueString is being used by the macro.
@@ -563,17 +594,34 @@ void parseSystemVariables(String& s, boolean useURLencode)
SMART_REPL(F("%unixtime%"), String(getUnixTime()))
SMART_REPL_T(F("%sunset"), replSunSetTimeString)
SMART_REPL_T(F("%sunrise"), replSunRiseTimeString)
if (s.indexOf(F("%is")) != -1) {
SMART_REPL(F("%ismqtt%"), String(MQTTclient_connected));
SMART_REPL(F("%iswifi%"), String(wifiStatus)); //0=disconnected, 1=connected, 2=got ip, 3=services initialized
SMART_REPL(F("%isntp%"), String(statusNTPInitialized));
#ifdef USES_P037
SMART_REPL(F("%ismqttimp%"), String(P037_MQTTImport_connected));
#endif // USES_P037
}
const int v_index = s.indexOf("%v");
if (v_index != -1 && isDigit(s[v_index+2])) {
for (byte i = 0; i < CUSTOM_VARS_MAX; ++i) {
SMART_REPL("%v"+toString(i+1,0)+'%', String(customFloatVar[i]))
}
}
}
String getReplacementString(const String& format, String& s) {
int startpos = s.indexOf(format);
int endpos = s.indexOf('%', startpos + 1);
String R = s.substring(startpos, endpos + 1);
String log = F("ReplacementString SunTime: ");
log += R;
log += F(" offset: ");
log += getSecOffset(R);
addLog(LOG_LEVEL_DEBUG, log);
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("ReplacementString SunTime: ");
log += R;
log += F(" offset: ");
log += getSecOffset(R);
addLog(LOG_LEVEL_DEBUG, log);
}
return R;
}
+10 -15
View File
@@ -147,20 +147,6 @@ void breakTime(unsigned long timeInput, struct timeStruct &tm) {
tm.Day = time + 1; // day of month
}
void setTime(unsigned long t) {
sysTime = (uint32_t)t;
applyTimeZone(t);
nextSyncTime = (uint32_t)t + syncInterval;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
if (Settings.UseRules)
{
static bool firstUpdate = true;
String event = firstUpdate ? F("Time#Initialized") : F("Time#Set");
firstUpdate = false;
rulesProcessing(event);
}
}
uint32_t getUnixTime() {
return sysTime;
}
@@ -222,13 +208,22 @@ unsigned long now() {
unsigned long t = getNtpTime();
if (t != 0) {
timeSynced = true;
setTime(t);
sysTime = (uint32_t)t;
applyTimeZone(t);
nextSyncTime = (uint32_t)t + syncInterval;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
}
uint32_t localSystime = toLocal(sysTime);
breakTime(localSystime, tm);
if (timeSynced) {
calcSunRiseAndSet();
if (Settings.UseRules)
{
String event = statusNTPInitialized ? F("Time#Set") : F("Time#Initialized");
rulesProcessing(event);
}
statusNTPInitialized = true; //@giig1967g: setting system variable %isntp%
}
return (unsigned long)localSystime;
}
+248 -219
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -33,7 +33,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C001_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -118,7 +118,7 @@ bool do_process_c001_delay_queue(int controller_number, const C001_queue_element
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt);
addLog(LOG_LEVEL_DEBUG, element.txt);
return send_via_http(controller_number, client, request);
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
}
#endif
+2 -2
View File
@@ -35,7 +35,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -145,7 +145,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
{
if (event->idx != 0)
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
/*
if (!ControllerSettings.checkHostReachable(true)) {
+3 -3
View File
@@ -32,7 +32,7 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C003_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -78,7 +78,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
client.print(" \n");
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
while (!client_available(client) && !timeOutReached(timer))
delay(1);
timer = millis() + 1000;
@@ -102,7 +102,7 @@ bool do_process_c003_delay_queue(int controller_number, const C003_queue_element
addLog(LOG_LEVEL_DEBUG, log);
client.println(SecuritySettings.ControllerPassword[element.controller_idx]);
delay(100);
while (client.available())
while (client_available(client))
client.read();
strcpy_P(log, PSTR("TELNT: Sending cmd"));
+2 -2
View File
@@ -32,7 +32,7 @@ boolean CPlugin_004(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C004_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -94,6 +94,6 @@ bool do_process_c004_delay_queue(int controller_number, const C004_queue_element
postDataStr.length());
postStr += postDataStr;
return send_via_http(controller_number, client, postStr);
return send_via_http(controller_number, client, postStr, ControllerSettings.MustCheckReply);
}
#endif
+3 -3
View File
@@ -33,7 +33,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -92,7 +92,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
if (!ControllerSettings.checkHostReachable(true)) {
success = false;
@@ -100,7 +100,7 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
}
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
+3 -3
View File
@@ -33,7 +33,7 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
MQTTDelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -90,12 +90,12 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
success = false;
break;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
+3 -2
View File
@@ -32,7 +32,7 @@ boolean CPlugin_007(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C007_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -78,6 +78,7 @@ bool do_process_c007_delay_queue(int controller_number, const C007_queue_element
Serial.println(url);
return send_via_http(controller_number, client,
create_http_get_request(controller_number, ControllerSettings, url));
create_http_get_request(controller_number, ControllerSettings, url),
ControllerSettings.MustCheckReply);
}
#endif
+4 -4
View File
@@ -34,7 +34,7 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
case CPLUGIN_INIT:
{
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C008_DelayHandler.configureControllerSettings(ControllerSettings);
break;
@@ -52,10 +52,10 @@ boolean CPlugin_008(byte function, struct EventStruct *event, String& string)
// Collect the values at the same run, to make sure all are from the same sample
byte valueCount = getValueCountFromSensorType(event->sensorType);
C008_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -97,7 +97,7 @@ bool do_process_c008_delay_queue(int controller_number, const C008_queue_element
return false;
String request = create_http_request_auth(controller_number, element.controller_idx, ControllerSettings, F("GET"), element.txt[element.valuesSent]);
return element.checkDone(send_via_http(controller_number, client, request));
return element.checkDone(send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply));
}
#endif
+57 -51
View File
@@ -57,60 +57,20 @@ boolean CPlugin_009(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
if (!WiFiConnected(100)) {
success = false;
break;
}
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
byte valueCount = getValueCountFromSensorType(event->sensorType);
C009_queue_element element(event);
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
// We now create a URI for the request
String url = F("/ESPEasy");
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
// Create json root object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root[F("module")] = String(F("ESPEasy"));
root[F("version")] = String(F("1.04"));
// Create nested objects
JsonObject& data = root.createNestedObject(String(F("data")));
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
ESP[F("name")] = Settings.Name;
ESP[F("unit")] = Settings.Unit;
ESP[F("version")] = Settings.Version;
ESP[F("build")] = Settings.Build;
ESP[F("build_notes")] = String(F(BUILD_NOTES));
ESP[F("build_git")] = String(F(BUILD_GIT));
ESP[F("node_type_id")] = NODE_TYPE_ID;
ESP[F("sleep")] = Settings.deepSleep;
// embed IP, important if there is NAT/PAT
// char ipStr[20];
// IPAddress ip = WiFi.localIP();
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
ESP[F("ip")] = WiFi.localIP().toString();
// Create nested SENSOR json object
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
byte valueCount = getValueCountFromSensorType(event->sensorType);
// char itemNames[valueCount][2];
for (byte x = 0; x < valueCount; x++)
{
// Each sensor value get an own object (0..n)
// sprintf(itemNames[x],"%d",x);
JsonObject& val = SENSOR.createNestedObject(String(x));
val[F("deviceName")] = getTaskDeviceName(event->TaskIndex);
val[F("valueName")] = ExtraTaskSettings.TaskDeviceValueNames[x];
val[F("type")] = event->sensorType;
val[F("value")] = formatUserVarNoCheck(event, x);
element.txt[x] = formatUserVarNoCheck(event, x);
element.valueNames[x] = ExtraTaskSettings.TaskDeviceValueNames[x];
}
// Create json buffer
String jsonString;
root.printTo(jsonString);
success = C009_DelayHandler.addToQueue(C009_queue_element(event->ControllerIndex, url, jsonString));
success = C009_DelayHandler.addToQueue(element);
scheduleNextDelayQueue(TIMER_C009_DELAY_QUEUE, C009_DelayHandler.getNextScheduleTime());
break;
}
@@ -126,11 +86,57 @@ bool do_process_c009_delay_queue(int controller_number, const C009_queue_element
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
// Create json root object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root[F("module")] = String(F("ESPEasy"));
root[F("version")] = String(F("1.04"));
// Create nested objects
JsonObject& data = root.createNestedObject(String(F("data")));
JsonObject& ESP = data.createNestedObject(String(F("ESP")));
ESP[F("name")] = Settings.Name;
ESP[F("unit")] = Settings.Unit;
ESP[F("version")] = Settings.Version;
ESP[F("build")] = Settings.Build;
ESP[F("build_notes")] = String(F(BUILD_NOTES));
ESP[F("build_git")] = String(F(BUILD_GIT));
ESP[F("node_type_id")] = NODE_TYPE_ID;
ESP[F("sleep")] = Settings.deepSleep;
// embed IP, important if there is NAT/PAT
// char ipStr[20];
// IPAddress ip = WiFi.localIP();
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
ESP[F("ip")] = WiFi.localIP().toString();
// Create nested SENSOR json object
JsonObject& SENSOR = data.createNestedObject(String(F("SENSOR")));
byte valueCount = getValueCountFromSensorType(element.sensorType);
// char itemNames[valueCount][2];
for (byte x = 0; x < valueCount; x++)
{
// Each sensor value get an own object (0..n)
// sprintf(itemNames[x],"%d",x);
JsonObject& val = SENSOR.createNestedObject(String(x));
val[F("deviceName")] = getTaskDeviceName(element.TaskIndex);
val[F("valueName")] = element.valueNames[x];
val[F("type")] = element.sensorType;
val[F("value")] = element.txt[x];
}
// Create json buffer
String jsonString;
root.printTo(jsonString);
// We now create a URI for the request
String url = F("/ESPEasy");
String request = create_http_request_auth(
controller_number, element.controller_idx, ControllerSettings,
F("POST"), element.url, element.json.length());
request += element.json;
F("POST"), url, jsonString.length());
request += jsonString;
return send_via_http(controller_number, client, request);
return send_via_http(controller_number, client, request, ControllerSettings.MustCheckReply);
}
#endif
+11 -6
View File
@@ -42,10 +42,10 @@ boolean CPlugin_010(byte function, struct EventStruct *event, String& string)
{
byte valueCount = getValueCountFromSensorType(event->sensorType);
C010_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -85,13 +85,18 @@ bool do_process_c010_delay_queue(int controller_number, const C010_queue_element
if (element.checkDone(true))
return true;
}
if (!try_connect_host(controller_number, portUDP, ControllerSettings))
WiFiUDP C010_portUDP;
if (!beginWiFiUDP_randomPort(C010_portUDP)) return false;
if (!try_connect_host(controller_number, C010_portUDP, ControllerSettings))
return false;
portUDP.write(
C010_portUDP.write(
(uint8_t*)element.txt[element.valuesSent].c_str(),
element.txt[element.valuesSent].length());
return element.checkDone(portUDP.endPacket() != 0);
bool reply = C010_portUDP.endPacket();
C010_portUDP.stop();
if (ControllerSettings.MustCheckReply)
return element.checkDone(reply);
return element.checkDone(true);
}
#endif
+3 -3
View File
@@ -133,7 +133,7 @@ bool do_process_c011_delay_queue(int controller_number, const C011_queue_element
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
return send_via_http(controller_number, client, element.txt);
return send_via_http(controller_number, client, element.txt, ControllerSettings.MustCheckReply);
}
@@ -147,7 +147,7 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
if (!WiFiConnected(100)) {
return false;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
C011_ConfigStruct customConfig;
@@ -158,7 +158,7 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event)
if (!try_connect_host(controller_number, client, ControllerSettings))
return false;
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String payload = create_http_request_auth(
+48 -49
View File
@@ -37,10 +37,10 @@ boolean CPlugin_012(byte function, struct EventStruct *event, String& string)
// Collect the values at the same run, to make sure all are from the same sample
byte valueCount = getValueCountFromSensorType(event->sensorType);
C012_queue_element element(event, valueCount);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
if (ExtraTaskSettings.TaskIndex != event->TaskIndex)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
for (byte x = 0; x < valueCount; x++)
@@ -73,16 +73,15 @@ bool do_process_c012_delay_queue(int controller_number, const C012_queue_element
if (element.checkDone(true))
return true;
}
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
return false;
}
return element.checkDone(Blynk_get(element.txt[element.valuesSent], element.controller_idx));
}
boolean Blynk_get(const String& command, byte controllerIndex, float *data )
{
if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) {
return false;
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(controllerIndex, ControllerSettings);
if ((SecuritySettings.ControllerPassword[controllerIndex][0] == 0)) {
@@ -104,58 +103,58 @@ boolean Blynk_get(const String& command, byte controllerIndex, float *data )
ControllerSettings.getHost().c_str());
addLog(LOG_LEVEL_DEBUG, request);
client.print(request);
boolean success = !ControllerSettings.MustCheckReply;
if (ControllerSettings.MustCheckReply || data) {
unsigned long timer = millis() + 200;
while (!client_available(client) && !timeOutReached(timer))
delay(1);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
yield();
char log[80] = {0};
boolean success = false;
char log[80] = {0};
// Read all the lines of the reply from server and log them
while (client_available(client)) {
String line;
safeReadStringUntil(client, line, '\n');
addLog(LOG_LEVEL_DEBUG_MORE, line);
// success ?
if (line.substring(0, 15) == F("HTTP/1.1 200 OK")) {
strcpy_P(log, PSTR("HTTP : Success"));
success = true;
}
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
addLog(LOG_LEVEL_DEBUG, log);
// data only
if (data && line.startsWith("["))
{
String strValue = line;
byte pos = strValue.indexOf('"',2);
strValue = strValue.substring(2, pos);
strValue.trim();
float value = strValue.toFloat();
*data = value;
success = true;
char value_char[5] = {0};
strValue.toCharArray(value_char, 5);
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
// Read all the lines of the reply from server and log them
while (client_available(client)) {
String line;
safeReadStringUntil(client, line, '\n');
addLog(LOG_LEVEL_DEBUG_MORE, line);
// success ?
if (line.substring(0, 15) == F("HTTP/1.1 200 OK")) {
strcpy_P(log, PSTR("HTTP : Success"));
success = true;
}
else if (line.substring(0, 24) == F("HTTP/1.1 400 Bad Request")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
else if (line.substring(0, 25) == F("HTTP/1.1 401 Unauthorized")) {
strcpy_P(log, PSTR("HTTP : Unauthorized"));
}
addLog(LOG_LEVEL_DEBUG, log);
// data only
if (data && line.startsWith("["))
{
String strValue = line;
byte pos = strValue.indexOf('"',2);
strValue = strValue.substring(2, pos);
strValue.trim();
float value = strValue.toFloat();
*data = value;
success = true;
char value_char[5] = {0};
strValue.toCharArray(value_char, 5);
sprintf_P(log, PSTR("Blynk get - %s => %s"),command.c_str(), value_char );
addLog(LOG_LEVEL_DEBUG, log);
}
yield();
}
yield();
}
strcpy_P(log, PSTR("HTTP : closing connection (012)"));
addLog(LOG_LEVEL_DEBUG, log);
addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection (012)"));
client.flush();
client.stop();
// important - backgroundtasks - free mem
timer = millis() + Settings.MessageDelay;
unsigned long timer = millis() + Settings.MessageDelay;
while (!timeOutReached(timer))
backgroundtasks();
+103 -78
View File
@@ -126,10 +126,10 @@ public:
return total;
}
String txt[VARS_PER_TASK];
int controller_idx;
byte TaskIndex;
int idx;
String txt[VARS_PER_TASK];
mutable byte valuesSent; // Value must be set by const function checkDone()
byte valueCount;
};
@@ -146,19 +146,31 @@ public:
\*********************************************************************************************/
class C009_queue_element {
public:
C009_queue_element() : controller_idx(0) {}
C009_queue_element(int ctrl_idx, const String& URI, const String& JSON) :
controller_idx(ctrl_idx), url(URI), json(JSON) {}
C009_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {}
C009_queue_element(const struct EventStruct* event) :
controller_idx(event->ControllerIndex),
TaskIndex(event->TaskIndex),
idx(event->idx),
sensorType(event->sensorType) {}
size_t getSize() const {
return sizeof(this) + url.length() + json.length();
size_t total = sizeof(this);
for (int i = 0; i < VARS_PER_TASK; ++i) {
total += txt[i].length();
total += valueNames[i].length();
}
return total;
}
String txt[VARS_PER_TASK];
String valueNames[VARS_PER_TASK];
int controller_idx;
String url;
String json;
byte TaskIndex;
int idx;
byte sensorType;
};
/*********************************************************************************************\
* C010_queue_element for queueing requests for 010: Generic UDP
* Using queue_element_single_value_base
@@ -189,13 +201,15 @@ struct ControllerDelayHandlerStruct {
max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT),
attempt(0),
max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT),
delete_oldest(false) {}
delete_oldest(false),
must_check_reply(false) {}
void configureControllerSettings(const ControllerSettingsStruct& settings) {
minTimeBetweenMessages = settings.MinimalTimeBetweenMessages;
max_queue_depth = settings.MaxQueueDepth;
max_retries = settings.MaxRetry;
delete_oldest = settings.DeleteOldest;
must_check_reply = settings.MustCheckReply;
// Set some sound limits when not configured
if (max_queue_depth == 0) max_queue_depth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT;
if (max_retries == 0) max_retries = CONTROLLER_DELAY_QUEUE_RETRY_DFLT;
@@ -251,15 +265,14 @@ struct ControllerDelayHandlerStruct {
// Get the next element.
// Remove front element when max_retries is reached.
bool getNext(T& element) {
if (sendQueue.empty()) return false;
T* getNext() {
if (sendQueue.empty()) return NULL;
if (attempt > max_retries) {
sendQueue.pop_front();
attempt = 0;
if (sendQueue.empty()) return false;
if (sendQueue.empty()) return NULL;
}
element = sendQueue.front();
return true;
return &sendQueue.front();
}
// Mark as processed and return time to schedule for next process.
@@ -302,6 +315,7 @@ struct ControllerDelayHandlerStruct {
byte attempt;
byte max_retries;
bool delete_oldest;
bool must_check_reply;
};
ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
@@ -321,17 +335,17 @@ ControllerDelayHandlerStruct<MQTT_queue_element> MQTTDelayHandler;
ControllerDelayHandlerStruct<C##NNN##_queue_element> C##NNN##_DelayHandler; \
bool do_process_c##NNN##_delay_queue(int controller_number, const C##NNN##_queue_element& element, ControllerSettingsStruct& ControllerSettings); \
void process_c##NNN##_delay_queue() { \
C##NNN##_queue_element element; \
if (!C##NNN##_DelayHandler.getNext(element)) return; \
ControllerSettingsStruct ControllerSettings; \
LoadControllerSettings(element.controller_idx, ControllerSettings); \
C##NNN##_queue_element* element(C##NNN##_DelayHandler.getNext()); \
if (element == NULL) return; \
MakeControllerSettings(ControllerSettings); \
LoadControllerSettings(element->controller_idx, ControllerSettings); \
C##NNN##_DelayHandler.configureControllerSettings(ControllerSettings); \
if (!WiFiConnected(100)) { \
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
return; \
} \
START_TIMER; \
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, element, ControllerSettings)); \
C##NNN##_DelayHandler.markProcessed(do_process_c##NNN##_delay_queue(M, *element, ControllerSettings)); \
STOP_TIMER(C##NNN##_DELAY_QUEUE); \
scheduleNextDelayQueue(TIMER_C##NNN##_DELAY_QUEUE, C##NNN##_DelayHandler.getNextScheduleTime()); \
}
@@ -387,26 +401,30 @@ bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned i
do {
//read character
c = input.read();
if (c >= 0) {
//found terminator, we're ok
if (c == terminator) {
return(true);
}
//found character, add to string
else{
str += char(c);
//string at max size?
if (str.length() >= maxSize) {
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
return(false);
}
}
}
// We must run the backgroundtasks every now and then.
if (timeOutReached(backgroundtasks_timer)) {
backgroundtasks_timer += 10;
backgroundtasks();
if (input.available()) {
c = input.read();
if (c >= 0) {
//found terminator, we're ok
if (c == terminator) {
return(true);
}
//found character, add to string
else{
str += char(c);
//string at max size?
if (str.length() >= maxSize) {
addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!"));
return(false);
}
}
}
// We must run the backgroundtasks every now and then.
if (timeOutReached(backgroundtasks_timer)) {
backgroundtasks_timer += 10;
backgroundtasks();
} else {
yield();
}
} else {
yield();
}
@@ -606,54 +624,61 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti
// See: https://github.com/esp8266/Arduino/pull/5113
// https://github.com/esp8266/Arduino/pull/1829
bool client_available(WiFiClient& client) {
#ifdef ESP32
yield();
#else
esp_yield(); // Could be called from events
#endif
return client.available() || client.connected();
}
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr) {
bool success = false;
bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply) {
bool success = !must_check_reply;
// This will send the request to the server
client.print(postStr);
unsigned long timer = millis() + 200;
while (!client.available()) {
if (timeOutReached(timer)) return false;
delay(1);
}
// Read all the lines of the reply from server and print them to Serial
while (client_available(client) && !success) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
if (line.length() > 80) {
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
} else {
addLog(LOG_LEVEL_DEBUG_MORE, line);
}
if (must_check_reply) {
unsigned long timer = millis() + 200;
while (!client_available(client)) {
if (timeOutReached(timer)) return false;
delay(1);
}
if (line.startsWith(F("HTTP/1.1 2")))
{
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Success! ");
log += line;
addLog(LOG_LEVEL_DEBUG, log);
// Read all the lines of the reply from server and print them to Serial
while (client_available(client) && !success) {
// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
if (line.length() > 80) {
addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80));
} else {
addLog(LOG_LEVEL_DEBUG_MORE, line);
}
}
} else if (line.startsWith(F("HTTP/1.1 4"))) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Error: ");
log += line;
addLog(LOG_LEVEL_ERROR, log);
if (line.startsWith(F("HTTP/1.1 2")))
{
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Success! ");
log += line;
addLog(LOG_LEVEL_DEBUG, log);
}
} else if (line.startsWith(F("HTTP/1.1 4"))) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("HTTP : ");
log += logIdentifier;
log += F(" Error: ");
log += line;
addLog(LOG_LEVEL_ERROR, log);
}
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
}
addLog(LOG_LEVEL_DEBUG_MORE, postStr);
yield();
}
yield();
}
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("HTTP : ");
@@ -667,8 +692,8 @@ bool send_via_http(const String& logIdentifier, WiFiClient& client, const String
return success;
}
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr) {
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr);
bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply) {
return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply);
}
+4 -4
View File
@@ -38,8 +38,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
//
// if (command == F("email"))
// {
// NotificationSettingsStruct NotificationSettings;
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// NPlugin_001_send(NotificationSettings.Domain, NotificationSettings.Receiver, NotificationSettings.Sender, NotificationSettings.Subject, NotificationSettings.Body, NotificationSettings.Server, NotificationSettings.Port);
// success = true;
// }
@@ -48,8 +48,8 @@ boolean NPlugin_001(byte function, struct EventStruct *event, String& string)
case NPLUGIN_NOTIFY:
{
NotificationSettingsStruct NotificationSettings;
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
String subject = NotificationSettings.Subject;
String body = "";
if (event->String1.length() > 0)
+4 -4
View File
@@ -35,8 +35,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
//
// if (command == F("buzzer"))
// {
// NotificationSettingsStruct NotificationSettings;
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// success = true;
// }
// break;
@@ -44,8 +44,8 @@ boolean NPlugin_002(byte function, struct EventStruct *event, String& string)
case NPLUGIN_NOTIFY:
{
NotificationSettingsStruct NotificationSettings;
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettings));
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (byte*)&NotificationSettings, sizeof(NotificationSettingsStruct));
//this reserves IRAM and uninitialized RAM
#ifndef ESP32
// Buzzer not compatible with ESP32 due to lack of tone command.
+250 -51
View File
@@ -3,6 +3,31 @@
//#################################### Plugin 001: Input Switch #########################################
//#######################################################################################################
/**************************************************\
CONFIG
TaskDevicePluginConfig settings:
0: button type (switch or dimmer)
1: dim value
2: button option (normal, push high, push low)
3: send boot state (true,false)
4: use doubleclick (true,false)
5: use longpress (true,false)
6: LP fired (true,false)
7: doubleclick counter (=0,1,2,3)
TaskDevicePluginConfigFloat settings:
0: debounce interval ms
1: doubleclick interval ms
2: longpress interval ms
3:
TaskDevicePluginConfigLong settings:
0: clickTime debounce ms
1: clickTime doubleclick ms
2: clickTime longpress ms
3:
\**************************************************/
#define PLUGIN_001
#define PLUGIN_ID_001 1
#define PLUGIN_NAME_001 "Switch input - Switch"
@@ -18,6 +43,10 @@
#define PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH 0
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW 1
#define PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH 2
#define PLUGIN_001_DOUBLECLICK_MIN_INTERVAL 1000
#define PLUGIN_001_DOUBLECLICK_MAX_INTERVAL 3000
#define PLUGIN_001_LONGPRESS_MIN_INTERVAL 1000
#define PLUGIN_001_LONGPRESS_MAX_INTERVAL 5000
boolean Plugin_001_read_switch_state(struct EventStruct *event) {
return digitalRead(Settings.TaskDevicePin1[event->TaskIndex]) == HIGH;
@@ -89,6 +118,28 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
addFormCheckBox(F("Send Boot state"),F("plugin_001_boot"),
Settings.TaskDevicePluginConfig[event->TaskIndex][3]);
addFormSubHeader(F("Advanced event management"));
addFormNumericBox(F("De-bounce (ms)"), F("plugin_001_debounce"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0]), 0, 250);
//set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
addFormCheckBox(F("Doubleclick event (3)"), F("plugin_001_dc"), Settings.TaskDevicePluginConfig[event->TaskIndex][4]);
addFormNumericBox(F("Doubleclick max. interval (ms)"), F("plugin_001_dcmaxinterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]), PLUGIN_001_DOUBLECLICK_MIN_INTERVAL, PLUGIN_001_DOUBLECLICK_MAX_INTERVAL);
//set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
addFormCheckBox(F("Longpress event (10 & 11)"), F("plugin_001_lp"), Settings.TaskDevicePluginConfig[event->TaskIndex][5]);
addFormNumericBox(F("Longpress min. interval (ms)"), F("plugin_001_lpmininterval"), round(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]), PLUGIN_001_LONGPRESS_MIN_INTERVAL, PLUGIN_001_LONGPRESS_MAX_INTERVAL);
//TO-DO: add Extra-Long Press event
//addFormCheckBox(F("Extra-Longpress event (20 & 21)"), F("plugin_001_elp"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
//addFormNumericBox(F("Extra-Longpress min. interval (ms)"), F("plugin_001_elpmininterval"), Settings.TaskDevicePluginConfigLong[event->TaskIndex][2], 500, 2000);
success = true;
break;
}
@@ -105,6 +156,18 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = isFormItemChecked(F("plugin_001_boot"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0] = getFormItemInt(F("plugin_001_debounce"));
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = isFormItemChecked(F("plugin_001_dc"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = getFormItemInt(F("plugin_001_dcmaxinterval"));
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = isFormItemChecked(F("plugin_001_lp"));
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = getFormItemInt(F("plugin_001_lpmininterval"));
//TO-DO: add Extra-Long Press event
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = isFormItemChecked(F("plugin_001_elp"));
//Settings.TaskDevicePluginConfigLong[event->TaskIndex][2] = getFormItemInt(F("plugin_001_elpmininterval"));
success = true;
break;
}
@@ -132,19 +195,48 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
}
// set initial UserVar of the switch
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
// counter = 0
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
//used to track if LP has fired
Settings.TaskDevicePluginConfig[event->TaskIndex][6]=false;
//store millis for debounce, doubleclick and long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]=millis(); //debounce timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis(); //doubleclick timer
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis(); //longpress timer
//set minimum value for doubleclick MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] < PLUGIN_001_DOUBLECLICK_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1] = PLUGIN_001_DOUBLECLICK_MIN_INTERVAL;
//set minimum value for longpress MIN max speed
if (Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] < PLUGIN_001_LONGPRESS_MIN_INTERVAL)
Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2] = PLUGIN_001_LONGPRESS_MIN_INTERVAL;
success = true;
break;
}
case PLUGIN_REQUEST:
{
String device = parseString(string, 1);
String command = parseString(string, 2);
String strPar1 = parseString(string, 3);
if (device == F("gpio") && command == F("pinstate"))
//String device = parseString(string, 1);
//String command = parseString(string, 2);
//String strPar1 = parseString(string, 3);
// returns pin value using syntax: [plugin#gpio#pinstate#xx]
if (string.length()>=13 && string.substring(0,13).equalsIgnoreCase(F("gpio,pinstate")))
{
int par1;
if (validIntFromString(strPar1, par1)) {
if (validIntFromString(parseString(string, 3), par1)) {
string = digitalRead(par1);
}
success = true;
@@ -161,7 +253,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
if (PinMonitorState[x] != state){
String eventString = F("GPIO#");
eventString += x;
eventString += F("=");
eventString += '=';
eventString += state;
rulesProcessing(eventString);
PinMonitorState[x] = state;
@@ -173,51 +265,148 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
case PLUGIN_TEN_PER_SECOND:
{
const boolean state = Plugin_001_read_switch_state(event);
/**************************************************************************\
20181009 - @giig1967g: new doubleclick logic is:
if there is a 'state' change, check debounce period.
Then if doubleclick interval exceeded, reset Plugin_001_clickCounterDC to 0
Plugin_001_clickCounterDC contains the current status for doubleclick:
0: start counting
1: 1st click
2: 2nd click
3: 3rd click = doubleclick event if inside interval (calculated as: '3rd click time' minus '1st click time')
Returned EVENT value is = 3 always for doubleclick
In rules this can be checked:
on Button#Switch=3 do //will fire if doubleclick
\**************************************************************************/
if (state != switchstate[event->TaskIndex])
{
switchstate[event->TaskIndex] = state;
const boolean currentOutputState = outputstate[event->TaskIndex];
boolean new_outputState = currentOutputState;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2]) {
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
new_outputState = state;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
new_outputState = !currentOutputState;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
new_outputState = !currentOutputState;
break;
}
//reset timer for long press
Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]=millis();
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = false;
// send if output needs to be changed
if (currentOutputState != new_outputState)
const unsigned long debounceTime = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]);
if (debounceTime >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][0])) //de-bounce check
{
outputstate[event->TaskIndex] = new_outputState;
boolean sendState = new_outputState;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
byte output_value = sendState ? 1 : 0;
event->sensorType = SENSOR_TYPE_SWITCH;
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
if (sendState) {
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
// Only set type to being dimmer when setting a value else it is "switched off".
event->sensorType = SENSOR_TYPE_DIMMER;
}
const unsigned long deltaDC = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]);
if (deltaDC >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][1]))
{
//reset timer for doubleclick
Settings.TaskDevicePluginConfig[event->TaskIndex][7]=0;
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]=millis();
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : Switch state ");
log += state ? F("1") : F("0");
log += F(" Output value ");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
Settings.TaskDevicePluginConfig[event->TaskIndex][7]++;
switchstate[event->TaskIndex] = state;
const boolean currentOutputState = outputstate[event->TaskIndex];
boolean new_outputState = currentOutputState;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
{
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
new_outputState = state;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
new_outputState = !currentOutputState;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
new_outputState = !currentOutputState;
break;
}
// send if output needs to be changed
if (currentOutputState != new_outputState)
{
byte output_value;
outputstate[event->TaskIndex] = new_outputState;
boolean sendState = new_outputState;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
if (Settings.TaskDevicePluginConfig[event->TaskIndex][7]==3 && Settings.TaskDevicePluginConfig[event->TaskIndex][4])
{
output_value = 3; //double click
} else {
output_value = sendState ? 1 : 0; //single click
}
event->sensorType = SENSOR_TYPE_SWITCH;
if (P001_getSwitchType(event) == PLUGIN_001_TYPE_DIMMER) {
if (sendState) {
output_value = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
// Only set type to being dimmer when setting a value else it is "switched off".
event->sensorType = SENSOR_TYPE_DIMMER;
}
}
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : Switch state ");
log += state ? '1' : '0';
log += F(" Output value ");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
}
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = millis();
}
}
//check if LP is enabled and if LP has not fired yet
else if (Settings.TaskDevicePluginConfig[event->TaskIndex][5] && !Settings.TaskDevicePluginConfig[event->TaskIndex][6]) {
/**************************************************************************\
20181009 - @giig1967g: new longpress logic is:
if there is no 'state' change, check if longpress interval reached
When reached send longpress event.
Returned Event value = state + 10
So if state = 0 => EVENT longpress = 10
if state = 1 => EVENT longpress = 11
So we can trigger longpress for high or low contact
In rules this can be checked:
on Button#Switch=10 do //will fire if longpress when state = 0
on Button#Switch=11 do //will fire if longpress when state = 1
\**************************************************************************/
const unsigned long deltaLP = timePassedSince(Settings.TaskDevicePluginConfigLong[event->TaskIndex][2]);
if (deltaLP >= (unsigned long)lround(Settings.TaskDevicePluginConfigFloat[event->TaskIndex][2]))
{
byte output_value;
byte needToSendEvent = false;
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = true;
switch(Settings.TaskDevicePluginConfig[event->TaskIndex][2])
{
case PLUGIN_001_BUTTON_TYPE_NORMAL_SWITCH:
needToSendEvent = true;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_LOW:
if (!state)
needToSendEvent = true;
break;
case PLUGIN_001_BUTTON_TYPE_PUSH_ACTIVE_HIGH:
if (state)
needToSendEvent = true;
break;
}
if (needToSendEvent) {
boolean sendState = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !sendState;
output_value = sendState ? 1 : 0;
output_value = output_value + 10;
UserVar[event->BaseVarIndex] = output_value;
String log = F("SW : LongPress: Switch state ");
log += state ? '1' : '0';
log += F(" Output value ");
log += output_value;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
}
}
}
success = true;
break;
}
@@ -263,13 +452,23 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
byte mode;
uint16_t currentState;
getPinState(PLUGIN_ID_001, event->Par1, &mode, &currentState);
if (mode == PIN_MODE_OUTPUT || mode == PIN_MODE_UNDEFINED) { //toggle only output pins
digitalWrite(event->Par1, !currentState);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, !currentState);
log = String(F("SW : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
if (event->Par1 >= 0 && event->Par1 <= PIN_D_MAX)
{
if (hasPinState(PLUGIN_ID_001,event->Par1)) {
getPinState(PLUGIN_ID_001, event->Par1, &mode, &currentState);
} else {
currentState = (digitalRead(event->Par1) == HIGH);
mode = PIN_MODE_OUTPUT;
}
if (mode != PIN_MODE_INPUT) { //toggle only output pins
pinMode(event->Par1, OUTPUT);
digitalWrite(event->Par1, !currentState);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, !currentState);
log = String(F("SW : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
}
}
}
+26 -2
View File
@@ -109,6 +109,24 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_REQUEST:
{
//parseString(string, 1) = device
//parseString(string, 2) = command
//parseString(string, 3) = gpio number
// returns pin value using syntax: [plugin#mcpgpio#pinstate#xx]
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("mcpgpio,pinstate")))
{
int par1;
if (validIntFromString(parseString(string, 3), par1)) {
string = Plugin_009_Read(par1);
}
success = true;
}
break;
}
case PLUGIN_WRITE:
{
String log = "";
@@ -130,8 +148,14 @@ boolean Plugin_009(byte function, struct EventStruct *event, String& string)
byte mode;
uint16_t currentState;
getPinState(PLUGIN_ID_009, event->Par1, &mode, &currentState);
if (mode == PIN_MODE_OUTPUT || mode == PIN_MODE_UNDEFINED) {
if (hasPinState(PLUGIN_ID_009,event->Par1)) {
getPinState(PLUGIN_ID_009, event->Par1, &mode, &currentState);
} else {
currentState = Plugin_009_Read(event->Par1);
mode = PIN_MODE_OUTPUT;
}
if (mode != PIN_MODE_INPUT) {
setPinState(PLUGIN_ID_009, event->Par1, PIN_MODE_OUTPUT, !currentState);
Plugin_009_Write(event->Par1, !currentState);
log = String(F("MCP : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
+17 -12
View File
@@ -21,6 +21,9 @@ int Plugin_012_mode = 1;
#define PLUGIN_NAME_012 "Display - LCD2004"
#define PLUGIN_VALUENAME1_012 "LCD"
#define P12_Nlines 4 // The number of different lines which can be displayed
#define P12_Nchars 80
boolean Plugin_012(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
@@ -81,9 +84,9 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
addFormSelector(F("Display Size"), F("plugin_012_size"), 2, options2, optionValues2, choice2);
char deviceTemplate[4][80];
char deviceTemplate[P12_Nlines][P12_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
for (byte varNr = 0; varNr < 4; varNr++)
for (byte varNr = 0; varNr < P12_Nlines; varNr++)
{
addHtml(F("<TR><TD>Line "));
addHtml(String(varNr + 1));
@@ -122,17 +125,19 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = getFormItemInt(F("plugin_12_timer"));
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_012_mode"));
char deviceTemplate[4][80];
for (byte varNr = 0; varNr < 4; varNr++)
char deviceTemplate[P12_Nlines][P12_Nchars];
String error;
for (byte varNr = 0; varNr < P12_Nlines; varNr++)
{
char argc[25];
String arg = F("Plugin_012_template");
arg += varNr + 1;
arg.toCharArray(argc, 25);
String tmpString = WebServer.arg(argc);
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr]));
String argName = F("Plugin_012_template");
argName += varNr + 1;
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P12_Nchars)) {
error += getCustomTaskSettingsError(varNr);
}
}
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
success = true;
break;
@@ -194,7 +199,7 @@ boolean Plugin_012(byte function, struct EventStruct *event, String& string)
case PLUGIN_READ:
{
char deviceTemplate[4][80];
char deviceTemplate[P12_Nlines][P12_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
for (byte x = 0; x < Plugin_012_rows; x++)
+45 -8
View File
@@ -11,7 +11,7 @@
boolean Plugin_019(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte switchstate[TASKS_MAX];
static boolean switchstate[TASKS_MAX];
switch (function)
{
@@ -23,7 +23,7 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
Device[deviceCount].Ports = 8;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].InverseLogicOption = true;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
@@ -66,10 +66,17 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
// read and store current state to prevent switching at boot time
switchstate[event->TaskIndex] = Plugin_019_Read(Settings.TaskDevicePort[event->TaskIndex]);
// if boot state must be send, inverse default state
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
// if boot state must be send, inverse default state
// this is done to force the trigger in PLUGIN_TEN_PER_SECOND
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0]) {
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
}
success = true;
break;
}
@@ -85,7 +92,13 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
log += state;
addLog(LOG_LEVEL_INFO, log);
switchstate[event->TaskIndex] = state;
UserVar[event->BaseVarIndex] = state;
if (Settings.TaskDevicePin1Inversed[event->TaskIndex]){
UserVar[event->BaseVarIndex] = !switchstate[event->TaskIndex];
} else {
UserVar[event->BaseVarIndex] = switchstate[event->TaskIndex];
}
event->sensorType = SENSOR_TYPE_SWITCH;
sendData(event);
}
@@ -105,6 +118,24 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_REQUEST:
{
//parseString(string, 1) = device
//parseString(string, 2) = command
//parseString(string, 3) = gpio number
// returns pin value using syntax: [plugin#pcfgpio#pinstate#xx]
if (string.length()>=16 && string.substring(0,16).equalsIgnoreCase(F("pcfgpio,pinstate")))
{
int par1;
if (validIntFromString(parseString(string, 3), par1)) {
string = Plugin_019_Read(par1);
}
success = true;
}
break;
}
case PLUGIN_WRITE:
{
String log = "";
@@ -134,8 +165,14 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
byte mode;
uint16_t currentState;
getPinState(PLUGIN_ID_019, event->Par1, &mode, &currentState);
if (mode == PIN_MODE_OUTPUT || mode == PIN_MODE_UNDEFINED) {
if (hasPinState(PLUGIN_ID_019,event->Par1)) {
getPinState(PLUGIN_ID_019, event->Par1, &mode, &currentState);
} else {
currentState = Plugin_019_Read(event->Par1);
mode = PIN_MODE_OUTPUT;
}
if (mode != PIN_MODE_INPUT) {
setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, !currentState);
Plugin_019_Write(event->Par1, !currentState);
log = String(F("PCF : Toggle GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(!currentState);
+20 -11
View File
@@ -15,6 +15,9 @@
#define PLUGIN_VALUENAME1_023 "OLED"
#define PLUGIN_023_MAX_DYSPALY 2
#define P23_Nlines 8 // The number of different lines which can be displayed
#define P23_Nchars 64
struct Plugin_023_OLED_SettingStruct
{
Plugin_023_OLED_SettingStruct(): address(0)
@@ -94,7 +97,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
int optionValues4[2] = { 1, 2 };
addFormSelector(F("Font Width"), F("plugin_023_font_width"), 2, options4, optionValues4, choice4);
char deviceTemplate[8][64];
char deviceTemplate[P23_Nlines][P23_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
for (byte varNr = 0; varNr < 8; varNr++)
{
@@ -117,16 +120,19 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = getFormItemInt(F("plugin_023_size"));
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = getFormItemInt(F("plugin_023_font_width"));
char deviceTemplate[8][64];
for (byte varNr = 0; varNr < 8; varNr++)
char deviceTemplate[P23_Nlines][P23_Nchars];
String error;
for (byte varNr = 0; varNr < P23_Nlines; varNr++)
{
String arg = F("Plugin_023_template");
arg += varNr + 1;
String tmpString = WebServer.arg(arg);
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr])-1);
deviceTemplate[varNr][63]=0;
String argName = F("Plugin_023_template");
argName += varNr + 1;
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P23_Nchars)) {
error += getCustomTaskSettingsError(varNr);
}
}
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
success = true;
break;
@@ -203,7 +209,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
case PLUGIN_READ:
{
char deviceTemplate[8][64];
char deviceTemplate[P23_Nlines][P23_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
int index = Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 0x3C
? 0
@@ -228,8 +234,10 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
? 0
: 1;
String arguments = String(string);
//Fixed bug #1864
int dotPos = arguments.indexOf('.');
if(dotPos > -1)
if(dotPos > -1 && arguments.substring(dotPos,dotPos+4).equalsIgnoreCase(F("oled")))
{
LoadTaskSettings(event->TaskIndex);
String name = arguments.substring(0,dotPos);
@@ -245,6 +253,7 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
}
}
int argIndex = arguments.indexOf(',');
if (argIndex)
arguments = arguments.substring(0, argIndex);
+8 -2
View File
@@ -41,7 +41,7 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_LOAD:
{
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
String options[10];
String options[11];
options[0] = F("Uptime");
options[1] = F("Free RAM");
options[2] = F("Wifi RSSI");
@@ -52,7 +52,8 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
options[7] = F("IP 3.Octet");
options[8] = F("IP 4.Octet");
options[9] = F("Web activity");
addFormSelector(F("Indicator"), F("plugin_026"), 10, options, NULL, choice);
options[10] = F("Free Stack");
addFormSelector(F("Indicator"), F("plugin_026"), 11, options, NULL, choice);
success = true;
break;
@@ -124,6 +125,11 @@ boolean Plugin_026(byte function, struct EventStruct *event, String& string)
value = (millis()-lastWeb)/1000; // respond in seconds
break;
}
case 10:
{
value = getCurrentFreeStack();
break;
}
}
UserVar[event->BaseVarIndex] = value;
String log = F("SYS : ");
+1 -1
View File
@@ -205,7 +205,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string)
// unsigned long IrSecondCode=0UL;
char ircodestr[100];
if (GetArgv(command, TmpStr1,100, 2)) IrType = TmpStr1;
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoul(TmpStr1, NULL, 16);
if (GetArgv(command, TmpStr1, 100, 3)){ IrCode = strtoull(TmpStr1, NULL, 16);
memcpy(ircodestr, TmpStr1, sizeof(TmpStr1[0])*100);
}
//if (GetArgv(command, TmpStr1, 100, 4)) IrBits = str2int(TmpStr1); //not needed any more... leave it for reverce compatibility or remove it and break existing instalations?
+8 -5
View File
@@ -180,15 +180,18 @@ boolean Plugin_036(byte function, struct EventStruct *event, String& string)
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = getFormItemInt(F("plugin_036_controler"));
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = getFormItemInt(F("plugin_036_contrast"));
String argName;
String error;
for (byte varNr = 0; varNr < P36_Nlines; varNr++)
{
argName = F("Plugin_036_template");
String argName = F("Plugin_036_template");
argName += varNr + 1;
strncpy(P036_deviceTemplate[varNr], WebServer.arg(argName).c_str(), sizeof(P036_deviceTemplate[varNr]));
if (!safe_strncpy(P036_deviceTemplate[varNr], WebServer.arg(argName), P36_Nchars)) {
error += getCustomTaskSettingsError(varNr);
}
}
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (byte*)&P036_deviceTemplate, sizeof(P036_deviceTemplate));
success = true;
+3 -1
View File
@@ -63,6 +63,8 @@ void Plugin_037_update_connect_status() {
}
if (MQTTclient_037_connected != connected) {
MQTTclient_037_connected = !MQTTclient_037_connected;
P037_MQTTImport_connected = MQTTclient_037_connected;
if (Settings.UseRules) {
String event = connected ? F("MQTTimport#Connected") : F("MQTTimport#Disconnected");
rulesProcessing(event);
@@ -392,7 +394,7 @@ boolean MQTTConnect_037()
Plugin_037_update_connect_status();
return false; // Not connected, so no use in wasting time to connect to a host.
}
ControllerSettingsStruct ControllerSettings;
MakeControllerSettings(ControllerSettings);
LoadControllerSettings(enabledMqttController, ControllerSettings);
if (ControllerSettings.UseDNS) {
MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
+55 -54
View File
@@ -2,7 +2,7 @@
//#######################################################################################################
//################################### Plugin 075: Nextion <info@sensorio.cz> ###########################
//################################### Created on the work of majklovec ###########################
//################################### Revisions by BertB and ThomasB ###########################
//################################### Revisions by BertB and ThomasB ###########################
//################################### Last Revision: Oct-03-2018 (TB) ###########################
//#######################################################################################################
//
@@ -36,18 +36,18 @@
#define PLUGIN_VALUENAME2_075 "value"
// Configuration Settings. Custom Configuration Memory must be less than 1024 Bytes (per TD'er findings).
//#define Nlines 12 // Custom Config, Number of user entered Command Statment Lines. DO NOT USE!
//#define Lenlines 64 // Custom Config, Length of user entered Command Statment Lines. DO NOT USE!
#define Nlines 10 // Custom Config, Number of user entered Command Statments.
#define Lenlines 51 // Custom Config, Length of user entered Command Statments.
//#define P75_Nlines 12 // Custom Config, Number of user entered Command Statment Lines. DO NOT USE!
//#define P75_Nchars 64 // Custom Config, Length of user entered Command Statment Lines. DO NOT USE!
#define P75_Nlines 10 // Custom Config, Number of user entered Command Statments.
#define P75_Nchars 51 // Custom Config, Length of user entered Command Statments.
// Nextion defines
#define RXBUFFSZ 64 // Serial RxD buffer (Local staging buffer and ESPeasySoftwareSerial).
#define RXBUFFSZ 64 // Serial RxD buffer (Local staging buffer and ESPeasySoftwareSerial).
#define RXBUFFWARN RXBUFFSZ-16 // Warning, Rx buffer close to being full.
#define TOUCH_BASE 500 // Base offset for 0X65 Touch Event Send Component ID.
// Serial defines
#define B9600 0
#define B9600 0
#define B38400 1
#define B57600 2
#define B115200 3
@@ -58,7 +58,7 @@
// Global vars
ESPeasySoftwareSerial *SoftSerial = NULL;
char deviceTemplate[Nlines][Lenlines];
char deviceTemplate[P75_Nlines][P75_Nchars];
int rxPin = -1;
int txPin = -1;
@@ -67,10 +67,10 @@ int txPin = -1;
// PlugIn starts here
// *****************************************************************************************************
boolean Plugin_075(byte function, struct EventStruct *event, String& string)
boolean Plugin_075(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static uint8_t BaudCode = 0; // Web GUI baud rate drop down. 9600 if 0, See array for other rates.
static uint8_t BaudCode = 0; // Web GUI baud rate drop down. 9600 if 0, See array for other rates.
static boolean HwSerial = SOFTSERIAL; // Serial mode, hardware uart or softserial.
static boolean AdvHwSerial = false; // Web GUI checkbox flag; false = softserial mode, true = hardware UART serial.
static boolean IncludeValues = false; // Web GUI checkbox flag; false = don't send idx & value data at interval.
@@ -134,7 +134,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
if (!((rxPin == 3 && txPin == 1) || (rxPin == 13 && txPin == 15))) { // Hardware Serial Compatible?
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = false; // Not HW serial compatible, Reset Check Box.
}
if (rxPin == 3 && txPin == 1) { // UART USB Port?
if(Settings.TaskDevicePluginConfig[event->TaskIndex][0]==false && // Hardware serial currently disabled.
Settings.TaskDeviceEnabled[event->TaskIndex] == true) { // Plugin is enabled.
@@ -142,10 +142,10 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
}
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == false) { // SoftSerial mode.
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == false) { // SoftSerial mode.
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = B9600; // Reset to 9600 baud.
}
if(rxPin <0 || txPin <0) { // Missing serial I/O pins!
addFormNote(F("Please configure the RX and TX sensor pins before enabling this plugin."));
addFormSubHeader(F("")); // Blank line, vertical space.
@@ -161,13 +161,13 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
options[1] = F("38400");
options[2] = F("57600");
options[3] = F("115200");
addFormSelector(F("Baud Rate"), F("plugin_075_baud"), 4, options, NULL, choice);
addFormSelector(F("Baud Rate"), F("plugin_075_baud"), 4, options, NULL, choice);
addFormNote(F("Un-check box for Soft Serial communication (low performance mode, 9600 Baud)."));
addFormNote(F("Hardware Serial is available when the GPIO pins are RX=D7 and TX=D8."));
addFormNote(F("D8 (GPIO-15) requires a Buffer Circuit (PNP transistor) or ESP boot may fail."));
addFormNote(F("Do <b>NOT</b> enable the Serial Log file on Tools->Advanced->Serial Port."));
// ** DEVELOPER DEBUG MESSAGE AREA **
// int datax = (int)(Settings.TaskDeviceEnabled[event->TaskIndex]); // Debug value.
// String Data = "Debug. Plugin Enable State: ";
@@ -176,12 +176,12 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
addFormSubHeader(F("")); // Blank line, vertical space.
addFormHeader(F("Nextion Command Statements (Optional)"));
char deviceTemplate[Nlines][Lenlines];
char deviceTemplate[P75_Nlines][P75_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
for (byte varNr = 0; varNr < Nlines; varNr++) {
addFormTextBox(String(F("Line ")) + (varNr + 1), String(F("Plugin_075_template")) + (varNr + 1), deviceTemplate[varNr], Lenlines-1);
for (byte varNr = 0; varNr < P75_Nlines; varNr++) {
addFormTextBox(String(F("Line ")) + (varNr + 1), String(F("Plugin_075_template")) + (varNr + 1), deviceTemplate[varNr], P75_Nchars-1);
}
if( Settings.TaskDeviceTimer[event->TaskIndex]==0) { // Is interval timer disabled?
if(IncludeValues) {
@@ -202,17 +202,18 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_SAVE: {
String argName;
char deviceTemplate[Nlines][Lenlines];
for (byte varNr = 0; varNr < Nlines; varNr++)
char deviceTemplate[P75_Nlines][P75_Nchars];
String error;
for (byte varNr = 0; varNr < P75_Nlines; varNr++)
{
String arg = F("Plugin_075_template");
arg += varNr + 1;
String tmpString = WebServer.arg(arg);
strncpy(deviceTemplate[varNr], tmpString.c_str(), sizeof(deviceTemplate[varNr])-1);
deviceTemplate[varNr][Lenlines-1]=0;
String argName = F("Plugin_075_template");
argName += varNr + 1;
if (!safe_strncpy(deviceTemplate[varNr], WebServer.arg(argName), P75_Nchars)) {
error += getCustomTaskSettingsError(varNr);
}
}
if (error.length() > 0) {
addHtmlError(error);
}
if(getTaskDeviceName(event->TaskIndex) == "") { // Check to see if user entered device name.
strcpy(ExtraTaskSettings.TaskDeviceName,PLUGIN_DEFAULT_NAME); // Name missing, populate default name.
@@ -244,7 +245,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
txPin = Settings.TaskDevicePin2[event->TaskIndex];
}
if (SoftSerial != NULL) {
if (SoftSerial != NULL) {
delete SoftSerial;
SoftSerial = NULL;
}
@@ -293,7 +294,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
HwSerial = SOFTSERIAL;
if (SoftSerial == NULL) {
SoftSerial = new ESPeasySoftwareSerial(rxPin, txPin, false, RXBUFFSZ);
}
}
SoftSerial->begin(9600);
SoftSerial->flush();
}
@@ -305,8 +306,8 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
case PLUGIN_READ: {
char deviceTemplate[Nlines][Lenlines];
case PLUGIN_READ: {
char deviceTemplate[P75_Nlines][P75_Nchars];
int RssiIndex;
String newString;
String tmpString;
@@ -315,7 +316,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
// Get optional LINE command statements. Special RSSIBAR bargraph keyword is supported.
for (byte x = 0; x < Nlines; x++) {
for (byte x = 0; x < P75_Nlines; x++) {
tmpString = deviceTemplate[x];
if (tmpString.length()) {
UcTmpString = deviceTemplate[x];
@@ -323,7 +324,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
RssiIndex = UcTmpString.indexOf(F("RSSIBAR")); // RSSI bargraph Keyword found, wifi value in dBm.
if(RssiIndex >= 0) {
int barVal;
newString.reserve(Lenlines+10); // Prevent re-allocation
newString.reserve(P75_Nchars+10); // Prevent re-allocation
newString = tmpString.substring(0, RssiIndex);
int nbars = WiFi.RSSI();
if (nbars < -100 || nbars >= 0)
@@ -356,9 +357,9 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
sendCommand(newString.c_str(), HwSerial);
#ifdef DEBUG_LOG
#ifdef DEBUG_LOG
String log;
log.reserve(Lenlines+50); // Prevent re-allocation
log.reserve(P75_Nchars+50); // Prevent re-allocation
log = F("NEXTION075 : Cmd Statement Line-");
log += String(x+1);
log += F(" Sent: ");
@@ -389,31 +390,31 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
#endif
success = false;
}
}
break;
}
// Nextion commands received from events (including http) get processed here. PLUGIN_WRITE
// Nextion commands received from events (including http) get processed here. PLUGIN_WRITE
// does NOT process publish commands that are sent.
case PLUGIN_WRITE: {
String tmpString = string;
int argIndex = tmpString.indexOf(',');
if (argIndex) tmpString = tmpString.substring(0, argIndex);
// Enable addLog() code below to help debug plugin write problems.
/*
String log;
log.reserve(140); // Prevent re-allocation
String log = F("Nextion arg0: ");
log += tmpString;
log += tmpString;
log += F(", TaskDeviceName: ");
log += getTaskDeviceName(event->TaskIndex);
log += F(", event->TaskIndex: ");
log += String(event->TaskIndex);
log += String(event->TaskIndex);
log += F(", cmd str: ");
log += string;
log += string;
addLog(LOG_LEVEL_INFO, log);
*/
if (tmpString.equalsIgnoreCase(getTaskDeviceName(event->TaskIndex)) == true) { // If device names match we have a command to write.
@@ -425,7 +426,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
log.reserve(110); // Prevent re-allocation
log = F("NEXTION075 : WRITE = ");
log += tmpString;
#ifdef DEBUG_LOG
#ifdef DEBUG_LOG
addLog(LOG_LEVEL_INFO, log);
#endif
SendStatus(event->Source, log); // Reply (echo) to sender. This will print message on browser.
@@ -457,7 +458,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
break;
}
case PLUGIN_TEN_PER_SECOND: {
uint16_t i;
uint8_t c;
@@ -467,7 +468,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
String Svalue;
String Nswitch;
char __buffer[RXBUFFSZ+1]; // Staging buffer.
if(rxPin < 0) {
String log = F("NEXTION075 : Missing RxD Pin, aborted serial receive");
addLog(LOG_LEVEL_INFO, log);
@@ -534,7 +535,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
#endif
}
}
}
}
else {
if (c == '|') {
__buffer[0] = c; // Store in staging buffer.
@@ -545,7 +546,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
else charCount = SoftSerial->available();
if(HwSerial == UARTSERIAL) {
i = 1;
i = 1;
while (Serial.available() > 0 && i<RXBUFFSZ) { // Copy global serial buffer to local buffer.
__buffer[i] = Serial.read();
if (__buffer[i]==0x0a || __buffer[i]==0x0d) break;
@@ -553,7 +554,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
}
else {
i = 1;
i = 1;
while (SoftSerial->available() > 0 && i<RXBUFFSZ) { // Copy global serial buffer to local buffer.
__buffer[i] = SoftSerial->read();
if (__buffer[i]==0x0a || __buffer[i]==0x0d) break;
@@ -562,7 +563,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
__buffer[i] = 0x00;
String tmpString = __buffer;
#ifdef DEBUG_LOG
@@ -632,7 +633,7 @@ boolean Plugin_075(byte function, struct EventStruct *event, String& string)
}
void sendCommand(const char *cmd, boolean SerialMode)
void sendCommand(const char *cmd, boolean SerialMode)
{
if(txPin < 0) {
String log = F("NEXTION075 : Missing TxD Pin Number, aborted sendCommand");
+1 -2
View File
@@ -1252,11 +1252,10 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
// Schedule the plugin to be read.
schedule_task_device_timer_at_init(event->TaskIndex);
}
if (Function != PLUGIN_GET_DEVICEVALUENAMES) {
if (ExtraTaskSettings.TaskIndex != event->TaskIndex) {
// LoadTaskSettings may call PLUGIN_GET_DEVICEVALUENAMES.
LoadTaskSettings(event->TaskIndex);
}
event->BaseVarIndex = event->TaskIndex * VARS_PER_TASK;
checkRAM(F("PluginCall_init"),x);
START_TIMER;