diff --git a/lib/AT24C256_512/Eeprom24C128_256.cpp b/lib/default/AT24C256_512/Eeprom24C128_256.cpp similarity index 100% rename from lib/AT24C256_512/Eeprom24C128_256.cpp rename to lib/default/AT24C256_512/Eeprom24C128_256.cpp diff --git a/lib/AT24C256_512/Eeprom24C128_256.h b/lib/default/AT24C256_512/Eeprom24C128_256.h similarity index 100% rename from lib/AT24C256_512/Eeprom24C128_256.h rename to lib/default/AT24C256_512/Eeprom24C128_256.h diff --git a/lib/AT24C256_512/Eeprom24C512.cpp b/lib/default/AT24C256_512/Eeprom24C512.cpp similarity index 100% rename from lib/AT24C256_512/Eeprom24C512.cpp rename to lib/default/AT24C256_512/Eeprom24C512.cpp diff --git a/lib/AT24C256_512/Eeprom24C512.h b/lib/default/AT24C256_512/Eeprom24C512.h similarity index 100% rename from lib/AT24C256_512/Eeprom24C512.h rename to lib/default/AT24C256_512/Eeprom24C512.h diff --git a/lib/AT24C256_512/library.properties b/lib/default/AT24C256_512/library.properties similarity index 100% rename from lib/AT24C256_512/library.properties rename to lib/default/AT24C256_512/library.properties diff --git a/lib/PubSubClient-EspEasy-2.7.12/.gitignore b/lib/default/PubSubClient-EspEasy-2.7.12/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/.gitignore rename to lib/default/PubSubClient-EspEasy-2.7.12/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.7.12/.travis.yml b/lib/default/PubSubClient-EspEasy-2.7.12/.travis.yml similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/.travis.yml rename to lib/default/PubSubClient-EspEasy-2.7.12/.travis.yml diff --git a/lib/PubSubClient-EspEasy-2.7.12/CHANGES.txt b/lib/default/PubSubClient-EspEasy-2.7.12/CHANGES.txt similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/CHANGES.txt rename to lib/default/PubSubClient-EspEasy-2.7.12/CHANGES.txt diff --git a/lib/PubSubClient-EspEasy-2.7.12/LICENSE.txt b/lib/default/PubSubClient-EspEasy-2.7.12/LICENSE.txt similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/LICENSE.txt rename to lib/default/PubSubClient-EspEasy-2.7.12/LICENSE.txt diff --git a/lib/PubSubClient-EspEasy-2.7.12/README.md b/lib/default/PubSubClient-EspEasy-2.7.12/README.md similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/README.md rename to lib/default/PubSubClient-EspEasy-2.7.12/README.md diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino b/lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino rename to lib/default/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino diff --git a/lib/PubSubClient-EspEasy-2.7.12/keywords.txt b/lib/default/PubSubClient-EspEasy-2.7.12/keywords.txt similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/keywords.txt rename to lib/default/PubSubClient-EspEasy-2.7.12/keywords.txt diff --git a/lib/PubSubClient-EspEasy-2.7.12/library.json b/lib/default/PubSubClient-EspEasy-2.7.12/library.json similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/library.json rename to lib/default/PubSubClient-EspEasy-2.7.12/library.json diff --git a/lib/PubSubClient-EspEasy-2.7.12/library.properties b/lib/default/PubSubClient-EspEasy-2.7.12/library.properties similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/library.properties rename to lib/default/PubSubClient-EspEasy-2.7.12/library.properties diff --git a/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h b/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h rename to lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/.gitignore b/lib/default/PubSubClient-EspEasy-2.7.12/tests/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/.gitignore rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/Makefile b/lib/default/PubSubClient-EspEasy-2.7.12/tests/Makefile similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/Makefile rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/Makefile diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/README.md b/lib/default/PubSubClient-EspEasy-2.7.12/tests/README.md similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/README.md rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/README.md diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp b/lib/default/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py b/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py b/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py b/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py b/lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py b/lib/default/PubSubClient-EspEasy-2.7.12/tests/testsuite.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py rename to lib/default/PubSubClient-EspEasy-2.7.12/tests/testsuite.py diff --git a/lib/TasmotaSerial-3.1.0/README.md b/lib/default/TasmotaSerial-3.1.0/README.md similarity index 100% rename from lib/TasmotaSerial-3.1.0/README.md rename to lib/default/TasmotaSerial-3.1.0/README.md diff --git a/lib/TasmotaSerial-3.1.0/examples/swsertest/swsertest.ino b/lib/default/TasmotaSerial-3.1.0/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-3.1.0/examples/swsertest/swsertest.ino rename to lib/default/TasmotaSerial-3.1.0/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-3.1.0/keywords.txt b/lib/default/TasmotaSerial-3.1.0/keywords.txt similarity index 100% rename from lib/TasmotaSerial-3.1.0/keywords.txt rename to lib/default/TasmotaSerial-3.1.0/keywords.txt diff --git a/lib/TasmotaSerial-3.1.0/library.json b/lib/default/TasmotaSerial-3.1.0/library.json similarity index 100% rename from lib/TasmotaSerial-3.1.0/library.json rename to lib/default/TasmotaSerial-3.1.0/library.json diff --git a/lib/TasmotaSerial-3.1.0/library.properties b/lib/default/TasmotaSerial-3.1.0/library.properties similarity index 100% rename from lib/TasmotaSerial-3.1.0/library.properties rename to lib/default/TasmotaSerial-3.1.0/library.properties diff --git a/lib/TasmotaSerial-3.1.0/src/TasmotaSerial.cpp b/lib/default/TasmotaSerial-3.1.0/src/TasmotaSerial.cpp similarity index 100% rename from lib/TasmotaSerial-3.1.0/src/TasmotaSerial.cpp rename to lib/default/TasmotaSerial-3.1.0/src/TasmotaSerial.cpp diff --git a/lib/TasmotaSerial-3.1.0/src/TasmotaSerial.h b/lib/default/TasmotaSerial-3.1.0/src/TasmotaSerial.h similarity index 100% rename from lib/TasmotaSerial-3.1.0/src/TasmotaSerial.h rename to lib/default/TasmotaSerial-3.1.0/src/TasmotaSerial.h diff --git a/lib/UdpListener/library.properties b/lib/default/UdpListener/library.properties similarity index 100% rename from lib/UdpListener/library.properties rename to lib/default/UdpListener/library.properties diff --git a/lib/UdpListener/src/UdpListener.h b/lib/default/UdpListener/src/UdpListener.h similarity index 100% rename from lib/UdpListener/src/UdpListener.h rename to lib/default/UdpListener/src/UdpListener.h diff --git a/lib/Unishox-1.0-shadinger/generator/generator.c b/lib/default/Unishox-1.0-shadinger/generator/generator.c similarity index 100% rename from lib/Unishox-1.0-shadinger/generator/generator.c rename to lib/default/Unishox-1.0-shadinger/generator/generator.c diff --git a/lib/Unishox-1.0-shadinger/generator/remapping.xlsx b/lib/default/Unishox-1.0-shadinger/generator/remapping.xlsx similarity index 100% rename from lib/Unishox-1.0-shadinger/generator/remapping.xlsx rename to lib/default/Unishox-1.0-shadinger/generator/remapping.xlsx diff --git a/lib/Unishox-1.0-shadinger/library.properties b/lib/default/Unishox-1.0-shadinger/library.properties similarity index 100% rename from lib/Unishox-1.0-shadinger/library.properties rename to lib/default/Unishox-1.0-shadinger/library.properties diff --git a/lib/Unishox-1.0-shadinger/python/unishox.py b/lib/default/Unishox-1.0-shadinger/python/unishox.py similarity index 100% rename from lib/Unishox-1.0-shadinger/python/unishox.py rename to lib/default/Unishox-1.0-shadinger/python/unishox.py diff --git a/lib/Unishox-1.0-shadinger/src/unishox.cpp b/lib/default/Unishox-1.0-shadinger/src/unishox.cpp similarity index 100% rename from lib/Unishox-1.0-shadinger/src/unishox.cpp rename to lib/default/Unishox-1.0-shadinger/src/unishox.cpp diff --git a/lib/Unishox-1.0-shadinger/src/unishox.h b/lib/default/Unishox-1.0-shadinger/src/unishox.h similarity index 100% rename from lib/Unishox-1.0-shadinger/src/unishox.h rename to lib/default/Unishox-1.0-shadinger/src/unishox.h diff --git a/lib/headers/DPT.h b/lib/default/headers/DPT.h similarity index 100% rename from lib/headers/DPT.h rename to lib/default/headers/DPT.h diff --git a/lib/headers/esp-knx-ip.h b/lib/default/headers/esp-knx-ip.h similarity index 100% rename from lib/headers/esp-knx-ip.h rename to lib/default/headers/esp-knx-ip.h diff --git a/lib/headers/readme.txt b/lib/default/headers/readme.txt similarity index 100% rename from lib/headers/readme.txt rename to lib/default/headers/readme.txt diff --git a/lib/jsmn-shadinger-1.0/README.md b/lib/default/jsmn-shadinger-1.0/README.md similarity index 100% rename from lib/jsmn-shadinger-1.0/README.md rename to lib/default/jsmn-shadinger-1.0/README.md diff --git a/lib/jsmn-shadinger-1.0/library.properties b/lib/default/jsmn-shadinger-1.0/library.properties similarity index 100% rename from lib/jsmn-shadinger-1.0/library.properties rename to lib/default/jsmn-shadinger-1.0/library.properties diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.cpp similarity index 100% rename from lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp rename to lib/default/jsmn-shadinger-1.0/src/JsonGenerator.cpp diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h similarity index 100% rename from lib/jsmn-shadinger-1.0/src/JsonGenerator.h rename to lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.cpp b/lib/default/jsmn-shadinger-1.0/src/JsonParser.cpp similarity index 100% rename from lib/jsmn-shadinger-1.0/src/JsonParser.cpp rename to lib/default/jsmn-shadinger-1.0/src/JsonParser.cpp diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.h b/lib/default/jsmn-shadinger-1.0/src/JsonParser.h similarity index 100% rename from lib/jsmn-shadinger-1.0/src/JsonParser.h rename to lib/default/jsmn-shadinger-1.0/src/JsonParser.h diff --git a/lib/jsmn-shadinger-1.0/src/jsmn.cpp b/lib/default/jsmn-shadinger-1.0/src/jsmn.cpp similarity index 100% rename from lib/jsmn-shadinger-1.0/src/jsmn.cpp rename to lib/default/jsmn-shadinger-1.0/src/jsmn.cpp diff --git a/lib/jsmn-shadinger-1.0/src/jsmn.h b/lib/default/jsmn-shadinger-1.0/src/jsmn.h similarity index 100% rename from lib/jsmn-shadinger-1.0/src/jsmn.h rename to lib/default/jsmn-shadinger-1.0/src/jsmn.h diff --git a/lib/jsmn-shadinger-1.0/test/test-json.cpp b/lib/default/jsmn-shadinger-1.0/test/test-json.cpp similarity index 100% rename from lib/jsmn-shadinger-1.0/test/test-json.cpp rename to lib/default/jsmn-shadinger-1.0/test/test-json.cpp diff --git a/lib_audio/ESP8266Audio/.github/workflows/pr-or-master-push.yml b/lib/lib_audio/ESP8266Audio/.github/workflows/pr-or-master-push.yml similarity index 100% rename from lib_audio/ESP8266Audio/.github/workflows/pr-or-master-push.yml rename to lib/lib_audio/ESP8266Audio/.github/workflows/pr-or-master-push.yml diff --git a/lib_audio/ESP8266Audio/LICENSE b/lib/lib_audio/ESP8266Audio/LICENSE similarity index 100% rename from lib_audio/ESP8266Audio/LICENSE rename to lib/lib_audio/ESP8266Audio/LICENSE diff --git a/lib_audio/ESP8266Audio/README.md b/lib/lib_audio/ESP8266Audio/README.md similarity index 100% rename from lib_audio/ESP8266Audio/README.md rename to lib/lib_audio/ESP8266Audio/README.md diff --git a/lib_audio/ESP8266Audio/examples/MixerSample/MixerSample.ino b/lib/lib_audio/ESP8266Audio/examples/MixerSample/MixerSample.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/MixerSample/MixerSample.ino rename to lib/lib_audio/ESP8266Audio/examples/MixerSample/MixerSample.ino diff --git a/lib_audio/ESP8266Audio/examples/MixerSample/viola.h b/lib/lib_audio/ESP8266Audio/examples/MixerSample/viola.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/MixerSample/viola.h rename to lib/lib_audio/ESP8266Audio/examples/MixerSample/viola.h diff --git a/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/PlayAACFromPROGMEM.ino b/lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/PlayAACFromPROGMEM.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/PlayAACFromPROGMEM.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/PlayAACFromPROGMEM.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/homer.aac b/lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/homer.aac similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/homer.aac rename to lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/homer.aac diff --git a/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/sampleaac.h b/lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/sampleaac.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/sampleaac.h rename to lib/lib_audio/ESP8266Audio/examples/PlayAACFromPROGMEM/sampleaac.h diff --git a/lib_audio/ESP8266Audio/examples/PlayFLAC-SD-SPDIF/PlayFLAC-SD-SPDIF.ino b/lib/lib_audio/ESP8266Audio/examples/PlayFLAC-SD-SPDIF/PlayFLAC-SD-SPDIF.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayFLAC-SD-SPDIF/PlayFLAC-SD-SPDIF.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayFLAC-SD-SPDIF/PlayFLAC-SD-SPDIF.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/PlayFLACFromPROGMEMToDAC.ino b/lib/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/PlayFLACFromPROGMEMToDAC.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/PlayFLACFromPROGMEMToDAC.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/PlayFLACFromPROGMEMToDAC.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/sample.h b/lib/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/sample.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/sample.h rename to lib/lib_audio/ESP8266Audio/examples/PlayFLACFromPROGMEMToDAC/sample.h diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/PlayMIDIFromLittleFS.ino b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/PlayMIDIFromLittleFS.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/PlayMIDIFromLittleFS.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/PlayMIDIFromLittleFS.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/1mgm.sf2 b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/1mgm.sf2 similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/1mgm.sf2 rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/1mgm.sf2 diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/furelise.mid b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/furelise.mid similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/furelise.mid rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromLittleFS/data/furelise.mid diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/PlayMIDIFromSPIFFS.ino b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/PlayMIDIFromSPIFFS.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/PlayMIDIFromSPIFFS.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/PlayMIDIFromSPIFFS.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/1mgm.sf2 b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/1mgm.sf2 similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/1mgm.sf2 rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/1mgm.sf2 diff --git a/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/furelise.mid b/lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/furelise.mid similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/furelise.mid rename to lib/lib_audio/ESP8266Audio/examples/PlayMIDIFromSPIFFS/data/furelise.mid diff --git a/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/PlayMODFromPROGMEMToDAC.ino b/lib/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/PlayMODFromPROGMEMToDAC.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/PlayMODFromPROGMEMToDAC.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/PlayMODFromPROGMEMToDAC.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/enigma.h b/lib/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/enigma.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/enigma.h rename to lib/lib_audio/ESP8266Audio/examples/PlayMODFromPROGMEMToDAC/enigma.h diff --git a/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/PlayMP3FromSPIFFS.ino b/lib/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/PlayMP3FromSPIFFS.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/PlayMP3FromSPIFFS.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/PlayMP3FromSPIFFS.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/data/pno-cs.mp3 b/lib/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/data/pno-cs.mp3 similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/data/pno-cs.mp3 rename to lib/lib_audio/ESP8266Audio/examples/PlayMP3FromSPIFFS/data/pno-cs.mp3 diff --git a/lib_audio/ESP8266Audio/examples/PlayMP3ToSPDIF/PlayMP3ToSPDIF.ino b/lib/lib_audio/ESP8266Audio/examples/PlayMP3ToSPDIF/PlayMP3ToSPDIF.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayMP3ToSPDIF/PlayMP3ToSPDIF.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayMP3ToSPDIF/PlayMP3ToSPDIF.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/PlayOpusFromSPIFFS.ino b/lib/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/PlayOpusFromSPIFFS.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/PlayOpusFromSPIFFS.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/PlayOpusFromSPIFFS.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/data/gs-16b-2c-44100hz.opus b/lib/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/data/gs-16b-2c-44100hz.opus similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/data/gs-16b-2c-44100hz.opus rename to lib/lib_audio/ESP8266Audio/examples/PlayOpusFromSPIFFS/data/gs-16b-2c-44100hz.opus diff --git a/lib_audio/ESP8266Audio/examples/PlayRTTTLToI2SDAC/PlayRTTTLToI2SDAC.ino b/lib/lib_audio/ESP8266Audio/examples/PlayRTTTLToI2SDAC/PlayRTTTLToI2SDAC.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayRTTTLToI2SDAC/PlayRTTTLToI2SDAC.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayRTTTLToI2SDAC/PlayRTTTLToI2SDAC.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/PlayWAVFromPROGMEM.ino b/lib/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/PlayWAVFromPROGMEM.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/PlayWAVFromPROGMEM.ino rename to lib/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/PlayWAVFromPROGMEM.ino diff --git a/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/viola.h b/lib/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/viola.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/viola.h rename to lib/lib_audio/ESP8266Audio/examples/PlayWAVFromPROGMEM/viola.h diff --git a/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP/StreamMP3FromHTTP.ino b/lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP/StreamMP3FromHTTP.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP/StreamMP3FromHTTP.ino rename to lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP/StreamMP3FromHTTP.ino diff --git a/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/Schema_Spiram.png b/lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/Schema_Spiram.png similarity index 100% rename from lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/Schema_Spiram.png rename to lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/Schema_Spiram.png diff --git a/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino b/lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino rename to lib/lib_audio/ESP8266Audio/examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino diff --git a/lib_audio/ESP8266Audio/examples/TalkingClockI2S/TalkingClockI2S.ino b/lib/lib_audio/ESP8266Audio/examples/TalkingClockI2S/TalkingClockI2S.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/TalkingClockI2S/TalkingClockI2S.ino rename to lib/lib_audio/ESP8266Audio/examples/TalkingClockI2S/TalkingClockI2S.ino diff --git a/lib_audio/ESP8266Audio/examples/WebRadio/WebRadio.ino b/lib/lib_audio/ESP8266Audio/examples/WebRadio/WebRadio.ino similarity index 100% rename from lib_audio/ESP8266Audio/examples/WebRadio/WebRadio.ino rename to lib/lib_audio/ESP8266Audio/examples/WebRadio/WebRadio.ino diff --git a/lib_audio/ESP8266Audio/examples/WebRadio/web.cpp b/lib/lib_audio/ESP8266Audio/examples/WebRadio/web.cpp similarity index 100% rename from lib_audio/ESP8266Audio/examples/WebRadio/web.cpp rename to lib/lib_audio/ESP8266Audio/examples/WebRadio/web.cpp diff --git a/lib_audio/ESP8266Audio/examples/WebRadio/web.h b/lib/lib_audio/ESP8266Audio/examples/WebRadio/web.h similarity index 100% rename from lib_audio/ESP8266Audio/examples/WebRadio/web.h rename to lib/lib_audio/ESP8266Audio/examples/WebRadio/web.h diff --git a/lib_audio/ESP8266Audio/keywords.txt b/lib/lib_audio/ESP8266Audio/keywords.txt similarity index 100% rename from lib_audio/ESP8266Audio/keywords.txt rename to lib/lib_audio/ESP8266Audio/keywords.txt diff --git a/lib_audio/ESP8266Audio/library.json b/lib/lib_audio/ESP8266Audio/library.json similarity index 100% rename from lib_audio/ESP8266Audio/library.json rename to lib/lib_audio/ESP8266Audio/library.json diff --git a/lib_audio/ESP8266Audio/library.properties b/lib/lib_audio/ESP8266Audio/library.properties similarity index 100% rename from lib_audio/ESP8266Audio/library.properties rename to lib/lib_audio/ESP8266Audio/library.properties diff --git a/lib_audio/ESP8266Audio/src/AudioFileSource.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSource.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSource.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSource.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceBuffer.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceFATFS.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceFATFS.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceFATFS.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceFATFS.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceFS.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceFS.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceFS.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceFS.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceFS.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceFS.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceFS.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceFS.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceHTTPStream.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceICYStream.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceID3.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceID3.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceID3.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceID3.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceID3.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceID3.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceID3.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceID3.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceLittleFS.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceLittleFS.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceLittleFS.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceLittleFS.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourcePROGMEM.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSD.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSD.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSD.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSD.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSD.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSD.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSD.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSD.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSPIFFS.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIFFS.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSPIFFS.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIFFS.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.h b/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileSourceSTDIO.h diff --git a/lib_audio/ESP8266Audio/src/AudioFileStream.cpp b/lib/lib_audio/ESP8266Audio/src/AudioFileStream.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileStream.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioFileStream.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioFileStream.h b/lib/lib_audio/ESP8266Audio/src/AudioFileStream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioFileStream.h rename to lib/lib_audio/ESP8266Audio/src/AudioFileStream.h diff --git a/lib_audio/ESP8266Audio/src/AudioGenerator.h b/lib/lib_audio/ESP8266Audio/src/AudioGenerator.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGenerator.h rename to lib/lib_audio/ESP8266Audio/src/AudioGenerator.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorAAC.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorAAC.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorAAC.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorFLAC.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMOD.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMOD.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMOD.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMP3.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMP3.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorMP3a.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorOpus.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorOpus.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorOpus.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorRTTTL.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorTalkie.h diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.cpp b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorWAV.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.h b/lib/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioGeneratorWAV.h rename to lib/lib_audio/ESP8266Audio/src/AudioGeneratorWAV.h diff --git a/lib_audio/ESP8266Audio/src/AudioLogger.cpp b/lib/lib_audio/ESP8266Audio/src/AudioLogger.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioLogger.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioLogger.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioLogger.h b/lib/lib_audio/ESP8266Audio/src/AudioLogger.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioLogger.h rename to lib/lib_audio/ESP8266Audio/src/AudioLogger.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutput.h b/lib/lib_audio/ESP8266Audio/src/AudioOutput.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutput.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutput.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputBuffer.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputBuffer.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputBuffer.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputBuffer.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputBuffer.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputBuffer.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputBuffer.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputBuffer.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputFilterDecimate.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputI2S.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputI2S.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputI2SNoDAC.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputMixer.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputMixer.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputMixer.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputMixer.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputMixer.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputMixer.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputMixer.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputMixer.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputNull.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputNull.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputNull.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputNull.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSPDIF.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSPDIF.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSPDIF.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSPIFFSWAV.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSTDIO.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSTDIO.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSTDIO.h diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.cpp rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.cpp diff --git a/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.h rename to lib/lib_audio/ESP8266Audio/src/AudioOutputSerialWAV.h diff --git a/lib_audio/ESP8266Audio/src/AudioStatus.h b/lib/lib_audio/ESP8266Audio/src/AudioStatus.h similarity index 100% rename from lib_audio/ESP8266Audio/src/AudioStatus.h rename to lib/lib_audio/ESP8266Audio/src/AudioStatus.h diff --git a/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.cpp b/lib/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.cpp similarity index 100% rename from lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.cpp rename to lib/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.cpp diff --git a/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.h b/lib/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.h similarity index 100% rename from lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.h rename to lib/lib_audio/ESP8266Audio/src/driver/SinglePinI2SDriver.h diff --git a/lib_audio/ESP8266Audio/src/libflac/AUTHORS b/lib/lib_audio/ESP8266Audio/src/libflac/AUTHORS similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/AUTHORS rename to lib/lib_audio/ESP8266Audio/src/libflac/AUTHORS diff --git a/lib_audio/ESP8266Audio/src/libflac/COPYING.FDL b/lib/lib_audio/ESP8266Audio/src/libflac/COPYING.FDL similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/COPYING.FDL rename to lib/lib_audio/ESP8266Audio/src/libflac/COPYING.FDL diff --git a/lib_audio/ESP8266Audio/src/libflac/COPYING.GPL b/lib/lib_audio/ESP8266Audio/src/libflac/COPYING.GPL similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/COPYING.GPL rename to lib/lib_audio/ESP8266Audio/src/libflac/COPYING.GPL diff --git a/lib_audio/ESP8266Audio/src/libflac/COPYING.LGPL b/lib/lib_audio/ESP8266Audio/src/libflac/COPYING.LGPL similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/COPYING.LGPL rename to lib/lib_audio/ESP8266Audio/src/libflac/COPYING.LGPL diff --git a/lib_audio/ESP8266Audio/src/libflac/COPYING.Xiph b/lib/lib_audio/ESP8266Audio/src/libflac/COPYING.Xiph similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/COPYING.Xiph rename to lib/lib_audio/ESP8266Audio/src/libflac/COPYING.Xiph diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/assert.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/assert.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/assert.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/assert.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/callback.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/callback.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/callback.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/callback.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/export.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/export.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/export.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/export.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/format.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/format.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/format.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/format.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/metadata.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/metadata.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/metadata.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/metadata.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/ordinals.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/ordinals.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/ordinals.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/ordinals.h diff --git a/lib_audio/ESP8266Audio/src/libflac/FLAC/stream_decoder.h b/lib/lib_audio/ESP8266Audio/src/libflac/FLAC/stream_decoder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/FLAC/stream_decoder.h rename to lib/lib_audio/ESP8266Audio/src/libflac/FLAC/stream_decoder.h diff --git a/lib_audio/ESP8266Audio/src/libflac/README b/lib/lib_audio/ESP8266Audio/src/libflac/README similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/README rename to lib/lib_audio/ESP8266Audio/src/libflac/README diff --git a/lib_audio/ESP8266Audio/src/libflac/README.ESP8266 b/lib/lib_audio/ESP8266Audio/src/libflac/README.ESP8266 similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/README.ESP8266 rename to lib/lib_audio/ESP8266Audio/src/libflac/README.ESP8266 diff --git a/lib_audio/ESP8266Audio/src/libflac/bitmath.c b/lib/lib_audio/ESP8266Audio/src/libflac/bitmath.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/bitmath.c rename to lib/lib_audio/ESP8266Audio/src/libflac/bitmath.c diff --git a/lib_audio/ESP8266Audio/src/libflac/bitreader.c b/lib/lib_audio/ESP8266Audio/src/libflac/bitreader.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/bitreader.c rename to lib/lib_audio/ESP8266Audio/src/libflac/bitreader.c diff --git a/lib_audio/ESP8266Audio/src/libflac/config.h b/lib/lib_audio/ESP8266Audio/src/libflac/config.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/config.h rename to lib/lib_audio/ESP8266Audio/src/libflac/config.h diff --git a/lib_audio/ESP8266Audio/src/libflac/cpu.c b/lib/lib_audio/ESP8266Audio/src/libflac/cpu.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/cpu.c rename to lib/lib_audio/ESP8266Audio/src/libflac/cpu.c diff --git a/lib_audio/ESP8266Audio/src/libflac/crc.c b/lib/lib_audio/ESP8266Audio/src/libflac/crc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/crc.c rename to lib/lib_audio/ESP8266Audio/src/libflac/crc.c diff --git a/lib_audio/ESP8266Audio/src/libflac/fixed.c b/lib/lib_audio/ESP8266Audio/src/libflac/fixed.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/fixed.c rename to lib/lib_audio/ESP8266Audio/src/libflac/fixed.c diff --git a/lib_audio/ESP8266Audio/src/libflac/float.c b/lib/lib_audio/ESP8266Audio/src/libflac/float.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/float.c rename to lib/lib_audio/ESP8266Audio/src/libflac/float.c diff --git a/lib_audio/ESP8266Audio/src/libflac/format.c b/lib/lib_audio/ESP8266Audio/src/libflac/format.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/format.c rename to lib/lib_audio/ESP8266Audio/src/libflac/format.c diff --git a/lib_audio/ESP8266Audio/src/libflac/lpc.c b/lib/lib_audio/ESP8266Audio/src/libflac/lpc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/lpc.c rename to lib/lib_audio/ESP8266Audio/src/libflac/lpc.c diff --git a/lib_audio/ESP8266Audio/src/libflac/md5.c b/lib/lib_audio/ESP8266Audio/src/libflac/md5.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/md5.c rename to lib/lib_audio/ESP8266Audio/src/libflac/md5.c diff --git a/lib_audio/ESP8266Audio/src/libflac/memory.c b/lib/lib_audio/ESP8266Audio/src/libflac/memory.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/memory.c rename to lib/lib_audio/ESP8266Audio/src/libflac/memory.c diff --git a/lib_audio/ESP8266Audio/src/libflac/private/bitmath.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/bitmath.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/bitmath.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/bitmath.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/bitreader.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/bitreader.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/bitreader.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/bitreader.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/cpu.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/cpu.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/cpu.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/cpu.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/crc.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/crc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/crc.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/crc.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/fixed.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/fixed.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/fixed.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/fixed.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/float.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/float.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/float.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/float.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/format.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/format.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/format.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/format.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/lpc.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/lpc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/lpc.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/lpc.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/macros.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/macros.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/macros.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/macros.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/md5.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/md5.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/md5.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/md5.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/memory.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/memory.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/memory.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/memory.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/metadata.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/metadata.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/metadata.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/metadata.h diff --git a/lib_audio/ESP8266Audio/src/libflac/private/window.h b/lib/lib_audio/ESP8266Audio/src/libflac/private/window.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/private/window.h rename to lib/lib_audio/ESP8266Audio/src/libflac/private/window.h diff --git a/lib_audio/ESP8266Audio/src/libflac/protected/all.h b/lib/lib_audio/ESP8266Audio/src/libflac/protected/all.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/protected/all.h rename to lib/lib_audio/ESP8266Audio/src/libflac/protected/all.h diff --git a/lib_audio/ESP8266Audio/src/libflac/protected/stream_decoder.h b/lib/lib_audio/ESP8266Audio/src/libflac/protected/stream_decoder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/protected/stream_decoder.h rename to lib/lib_audio/ESP8266Audio/src/libflac/protected/stream_decoder.h diff --git a/lib_audio/ESP8266Audio/src/libflac/protected/stream_encoder.h b/lib/lib_audio/ESP8266Audio/src/libflac/protected/stream_encoder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/protected/stream_encoder.h rename to lib/lib_audio/ESP8266Audio/src/libflac/protected/stream_encoder.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/alloc.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/alloc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/alloc.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/alloc.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/compat.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/compat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/compat.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/compat.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/endswap.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/endswap.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/endswap.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/endswap.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/getopt.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/getopt.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/getopt.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/getopt.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/macros.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/macros.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/macros.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/macros.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/private.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/private.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/private.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/private.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/safe_str.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/safe_str.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/safe_str.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/safe_str.h diff --git a/lib_audio/ESP8266Audio/src/libflac/share/utf8.h b/lib/lib_audio/ESP8266Audio/src/libflac/share/utf8.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/share/utf8.h rename to lib/lib_audio/ESP8266Audio/src/libflac/share/utf8.h diff --git a/lib_audio/ESP8266Audio/src/libflac/stream_decoder.c b/lib/lib_audio/ESP8266Audio/src/libflac/stream_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/stream_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libflac/stream_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libflac/window.c b/lib/lib_audio/ESP8266Audio/src/libflac/window.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libflac/window.c rename to lib/lib_audio/ESP8266Audio/src/libflac/window.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/aaccommon.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/aaccommon.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/aaccommon.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/aaccommon.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/aacdec.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/aactabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/aactabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/aactabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/aactabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/assembly.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/assembly.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/assembly.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/assembly.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/bitstream.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/buffers.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/buffers.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/buffers.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/buffers.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/coder.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/coder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/coder.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/coder.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/dct4.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/dct4.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/dct4.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/dct4.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/decelmnt.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/decelmnt.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/decelmnt.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/decelmnt.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/dequant.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/dequant.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/dequant.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/dequant.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/fft.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/fft.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/fft.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/fft.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/filefmt.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/filefmt.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/filefmt.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/filefmt.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/huffman.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/huffman.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/huffman.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/huffman.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/hufftabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/hufftabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/hufftabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/hufftabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/imdct.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/imdct.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/imdct.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/imdct.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/noiseless.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/noiseless.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/noiseless.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/noiseless.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/pns.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/pns.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/pns.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/pns.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/readme.txt b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/readme.txt similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/readme.txt rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/readme.txt diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbr.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbr.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbr.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfft.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfft.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrfft.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfft.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfreq.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfreq.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrfreq.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrfreq.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfadj.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfadj.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfadj.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfadj.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfgen.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfgen.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfgen.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhfgen.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhuff.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhuff.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrhuff.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrhuff.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrimdct.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrimdct.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrimdct.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrimdct.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrmath.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrmath.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrmath.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrmath.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrqmf.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrqmf.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrqmf.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrqmf.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrside.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrside.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrside.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrside.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/sbrtabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrtabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/sbrtabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/sbrtabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/statname.h b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/statname.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/statname.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/statname.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/stproc.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/stproc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/stproc.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/stproc.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/tns.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/tns.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/tns.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/tns.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-aac/trigtabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-aac/trigtabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-aac/trigtabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-aac/trigtabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/LICENSE.txt b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/LICENSE.txt similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/LICENSE.txt rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/LICENSE.txt diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/RCSL.txt b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/RCSL.txt similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/RCSL.txt rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/RCSL.txt diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/RPSL.txt b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/RPSL.txt similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/RPSL.txt rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/RPSL.txt diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/assembly.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/assembly.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/assembly.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/assembly.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/bitstream.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/bitstream.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/bitstream.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/bitstream.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/buffers.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/buffers.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/buffers.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/buffers.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/coder.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/coder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/coder.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/coder.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/dct32.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dct32.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/dct32.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dct32.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/dequant.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dequant.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/dequant.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dequant.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/dqchan.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dqchan.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/dqchan.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/dqchan.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/huffman.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/huffman.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/huffman.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/huffman.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/hufftabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/hufftabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/hufftabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/hufftabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/imdct.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/imdct.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/imdct.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/imdct.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3common.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3common.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/mp3common.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3common.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3dec.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3tabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3tabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/mp3tabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mp3tabs.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/mpadecobjfixpt.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mpadecobjfixpt.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/mpadecobjfixpt.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/mpadecobjfixpt.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/player.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/player.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/player.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/player.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/polyphase.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/polyphase.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/polyphase.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/polyphase.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/scalfact.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/scalfact.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/scalfact.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/scalfact.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/statname.h b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/statname.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/statname.h rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/statname.h diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/stproc.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/stproc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/stproc.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/stproc.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/subband.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/subband.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/subband.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/subband.c diff --git a/lib_audio/ESP8266Audio/src/libhelix-mp3/trigtabs.c b/lib/lib_audio/ESP8266Audio/src/libhelix-mp3/trigtabs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libhelix-mp3/trigtabs.c rename to lib/lib_audio/ESP8266Audio/src/libhelix-mp3/trigtabs.c diff --git a/lib_audio/ESP8266Audio/src/libmad/CHANGES b/lib/lib_audio/ESP8266Audio/src/libmad/CHANGES similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/CHANGES rename to lib/lib_audio/ESP8266Audio/src/libmad/CHANGES diff --git a/lib_audio/ESP8266Audio/src/libmad/COPYING b/lib/lib_audio/ESP8266Audio/src/libmad/COPYING similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/COPYING rename to lib/lib_audio/ESP8266Audio/src/libmad/COPYING diff --git a/lib_audio/ESP8266Audio/src/libmad/COPYRIGHT b/lib/lib_audio/ESP8266Audio/src/libmad/COPYRIGHT similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/COPYRIGHT rename to lib/lib_audio/ESP8266Audio/src/libmad/COPYRIGHT diff --git a/lib_audio/ESP8266Audio/src/libmad/CREDITS b/lib/lib_audio/ESP8266Audio/src/libmad/CREDITS similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/CREDITS rename to lib/lib_audio/ESP8266Audio/src/libmad/CREDITS diff --git a/lib_audio/ESP8266Audio/src/libmad/D.dat.h b/lib/lib_audio/ESP8266Audio/src/libmad/D.dat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/D.dat.h rename to lib/lib_audio/ESP8266Audio/src/libmad/D.dat.h diff --git a/lib_audio/ESP8266Audio/src/libmad/LICENSE b/lib/lib_audio/ESP8266Audio/src/libmad/LICENSE similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/LICENSE rename to lib/lib_audio/ESP8266Audio/src/libmad/LICENSE diff --git a/lib_audio/ESP8266Audio/src/libmad/README b/lib/lib_audio/ESP8266Audio/src/libmad/README similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/README rename to lib/lib_audio/ESP8266Audio/src/libmad/README diff --git a/lib_audio/ESP8266Audio/src/libmad/README.ESP8266 b/lib/lib_audio/ESP8266Audio/src/libmad/README.ESP8266 similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/README.ESP8266 rename to lib/lib_audio/ESP8266Audio/src/libmad/README.ESP8266 diff --git a/lib_audio/ESP8266Audio/src/libmad/TODO b/lib/lib_audio/ESP8266Audio/src/libmad/TODO similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/TODO rename to lib/lib_audio/ESP8266Audio/src/libmad/TODO diff --git a/lib_audio/ESP8266Audio/src/libmad/VERSION b/lib/lib_audio/ESP8266Audio/src/libmad/VERSION similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/VERSION rename to lib/lib_audio/ESP8266Audio/src/libmad/VERSION diff --git a/lib_audio/ESP8266Audio/src/libmad/bit.c b/lib/lib_audio/ESP8266Audio/src/libmad/bit.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/bit.c rename to lib/lib_audio/ESP8266Audio/src/libmad/bit.c diff --git a/lib_audio/ESP8266Audio/src/libmad/bit.h b/lib/lib_audio/ESP8266Audio/src/libmad/bit.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/bit.h rename to lib/lib_audio/ESP8266Audio/src/libmad/bit.h diff --git a/lib_audio/ESP8266Audio/src/libmad/config.h b/lib/lib_audio/ESP8266Audio/src/libmad/config.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/config.h rename to lib/lib_audio/ESP8266Audio/src/libmad/config.h diff --git a/lib_audio/ESP8266Audio/src/libmad/decoder.c b/lib/lib_audio/ESP8266Audio/src/libmad/decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/decoder.c rename to lib/lib_audio/ESP8266Audio/src/libmad/decoder.c diff --git a/lib_audio/ESP8266Audio/src/libmad/decoder.h b/lib/lib_audio/ESP8266Audio/src/libmad/decoder.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/decoder.h rename to lib/lib_audio/ESP8266Audio/src/libmad/decoder.h diff --git a/lib_audio/ESP8266Audio/src/libmad/fixed.c b/lib/lib_audio/ESP8266Audio/src/libmad/fixed.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/fixed.c rename to lib/lib_audio/ESP8266Audio/src/libmad/fixed.c diff --git a/lib_audio/ESP8266Audio/src/libmad/fixed.h b/lib/lib_audio/ESP8266Audio/src/libmad/fixed.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/fixed.h rename to lib/lib_audio/ESP8266Audio/src/libmad/fixed.h diff --git a/lib_audio/ESP8266Audio/src/libmad/frame.c b/lib/lib_audio/ESP8266Audio/src/libmad/frame.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/frame.c rename to lib/lib_audio/ESP8266Audio/src/libmad/frame.c diff --git a/lib_audio/ESP8266Audio/src/libmad/frame.h b/lib/lib_audio/ESP8266Audio/src/libmad/frame.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/frame.h rename to lib/lib_audio/ESP8266Audio/src/libmad/frame.h diff --git a/lib_audio/ESP8266Audio/src/libmad/global.h b/lib/lib_audio/ESP8266Audio/src/libmad/global.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/global.h rename to lib/lib_audio/ESP8266Audio/src/libmad/global.h diff --git a/lib_audio/ESP8266Audio/src/libmad/huffman.c b/lib/lib_audio/ESP8266Audio/src/libmad/huffman.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/huffman.c rename to lib/lib_audio/ESP8266Audio/src/libmad/huffman.c diff --git a/lib_audio/ESP8266Audio/src/libmad/huffman.h b/lib/lib_audio/ESP8266Audio/src/libmad/huffman.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/huffman.h rename to lib/lib_audio/ESP8266Audio/src/libmad/huffman.h diff --git a/lib_audio/ESP8266Audio/src/libmad/imdct_s.dat.h b/lib/lib_audio/ESP8266Audio/src/libmad/imdct_s.dat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/imdct_s.dat.h rename to lib/lib_audio/ESP8266Audio/src/libmad/imdct_s.dat.h diff --git a/lib_audio/ESP8266Audio/src/libmad/layer3.c b/lib/lib_audio/ESP8266Audio/src/libmad/layer3.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/layer3.c rename to lib/lib_audio/ESP8266Audio/src/libmad/layer3.c diff --git a/lib_audio/ESP8266Audio/src/libmad/layer3.h b/lib/lib_audio/ESP8266Audio/src/libmad/layer3.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/layer3.h rename to lib/lib_audio/ESP8266Audio/src/libmad/layer3.h diff --git a/lib_audio/ESP8266Audio/src/libmad/mad.h b/lib/lib_audio/ESP8266Audio/src/libmad/mad.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/mad.h rename to lib/lib_audio/ESP8266Audio/src/libmad/mad.h diff --git a/lib_audio/ESP8266Audio/src/libmad/mad.h.sed b/lib/lib_audio/ESP8266Audio/src/libmad/mad.h.sed similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/mad.h.sed rename to lib/lib_audio/ESP8266Audio/src/libmad/mad.h.sed diff --git a/lib_audio/ESP8266Audio/src/libmad/qc_table.dat.h b/lib/lib_audio/ESP8266Audio/src/libmad/qc_table.dat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/qc_table.dat.h rename to lib/lib_audio/ESP8266Audio/src/libmad/qc_table.dat.h diff --git a/lib_audio/ESP8266Audio/src/libmad/rq_table.dat.h b/lib/lib_audio/ESP8266Audio/src/libmad/rq_table.dat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/rq_table.dat.h rename to lib/lib_audio/ESP8266Audio/src/libmad/rq_table.dat.h diff --git a/lib_audio/ESP8266Audio/src/libmad/sf_table.dat.h b/lib/lib_audio/ESP8266Audio/src/libmad/sf_table.dat.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/sf_table.dat.h rename to lib/lib_audio/ESP8266Audio/src/libmad/sf_table.dat.h diff --git a/lib_audio/ESP8266Audio/src/libmad/stream.c b/lib/lib_audio/ESP8266Audio/src/libmad/stream.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/stream.c rename to lib/lib_audio/ESP8266Audio/src/libmad/stream.c diff --git a/lib_audio/ESP8266Audio/src/libmad/stream.h b/lib/lib_audio/ESP8266Audio/src/libmad/stream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/stream.h rename to lib/lib_audio/ESP8266Audio/src/libmad/stream.h diff --git a/lib_audio/ESP8266Audio/src/libmad/synth.c b/lib/lib_audio/ESP8266Audio/src/libmad/synth.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/synth.c rename to lib/lib_audio/ESP8266Audio/src/libmad/synth.c diff --git a/lib_audio/ESP8266Audio/src/libmad/synth.h b/lib/lib_audio/ESP8266Audio/src/libmad/synth.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/synth.h rename to lib/lib_audio/ESP8266Audio/src/libmad/synth.h diff --git a/lib_audio/ESP8266Audio/src/libmad/timer.c b/lib/lib_audio/ESP8266Audio/src/libmad/timer.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/timer.c rename to lib/lib_audio/ESP8266Audio/src/libmad/timer.c diff --git a/lib_audio/ESP8266Audio/src/libmad/timer.h b/lib/lib_audio/ESP8266Audio/src/libmad/timer.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/timer.h rename to lib/lib_audio/ESP8266Audio/src/libmad/timer.h diff --git a/lib_audio/ESP8266Audio/src/libmad/version.c b/lib/lib_audio/ESP8266Audio/src/libmad/version.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/version.c rename to lib/lib_audio/ESP8266Audio/src/libmad/version.c diff --git a/lib_audio/ESP8266Audio/src/libmad/version.h b/lib/lib_audio/ESP8266Audio/src/libmad/version.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libmad/version.h rename to lib/lib_audio/ESP8266Audio/src/libmad/version.h diff --git a/lib_audio/ESP8266Audio/src/libogg/AUTHORS b/lib/lib_audio/ESP8266Audio/src/libogg/AUTHORS similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/AUTHORS rename to lib/lib_audio/ESP8266Audio/src/libogg/AUTHORS diff --git a/lib_audio/ESP8266Audio/src/libogg/CHANGES b/lib/lib_audio/ESP8266Audio/src/libogg/CHANGES similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/CHANGES rename to lib/lib_audio/ESP8266Audio/src/libogg/CHANGES diff --git a/lib_audio/ESP8266Audio/src/libogg/COPYING b/lib/lib_audio/ESP8266Audio/src/libogg/COPYING similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/COPYING rename to lib/lib_audio/ESP8266Audio/src/libogg/COPYING diff --git a/lib_audio/ESP8266Audio/src/libogg/README.esp8266.md b/lib/lib_audio/ESP8266Audio/src/libogg/README.esp8266.md similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/README.esp8266.md rename to lib/lib_audio/ESP8266Audio/src/libogg/README.esp8266.md diff --git a/lib_audio/ESP8266Audio/src/libogg/README.md b/lib/lib_audio/ESP8266Audio/src/libogg/README.md similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/README.md rename to lib/lib_audio/ESP8266Audio/src/libogg/README.md diff --git a/lib_audio/ESP8266Audio/src/libogg/bitwise.c b/lib/lib_audio/ESP8266Audio/src/libogg/bitwise.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/bitwise.c rename to lib/lib_audio/ESP8266Audio/src/libogg/bitwise.c diff --git a/lib_audio/ESP8266Audio/src/libogg/config.h b/lib/lib_audio/ESP8266Audio/src/libogg/config.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/config.h rename to lib/lib_audio/ESP8266Audio/src/libogg/config.h diff --git a/lib_audio/ESP8266Audio/src/libogg/crctable.h b/lib/lib_audio/ESP8266Audio/src/libogg/crctable.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/crctable.h rename to lib/lib_audio/ESP8266Audio/src/libogg/crctable.h diff --git a/lib_audio/ESP8266Audio/src/libogg/framing.c b/lib/lib_audio/ESP8266Audio/src/libogg/framing.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/framing.c rename to lib/lib_audio/ESP8266Audio/src/libogg/framing.c diff --git a/lib_audio/ESP8266Audio/src/libogg/ogg.pc b/lib/lib_audio/ESP8266Audio/src/libogg/ogg.pc similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/ogg.pc rename to lib/lib_audio/ESP8266Audio/src/libogg/ogg.pc diff --git a/lib_audio/ESP8266Audio/src/libogg/ogg/config_types.h b/lib/lib_audio/ESP8266Audio/src/libogg/ogg/config_types.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/ogg/config_types.h rename to lib/lib_audio/ESP8266Audio/src/libogg/ogg/config_types.h diff --git a/lib_audio/ESP8266Audio/src/libogg/ogg/ogg.h b/lib/lib_audio/ESP8266Audio/src/libogg/ogg/ogg.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/ogg/ogg.h rename to lib/lib_audio/ESP8266Audio/src/libogg/ogg/ogg.h diff --git a/lib_audio/ESP8266Audio/src/libogg/ogg/os_types.h b/lib/lib_audio/ESP8266Audio/src/libogg/ogg/os_types.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libogg/ogg/os_types.h rename to lib/lib_audio/ESP8266Audio/src/libogg/ogg/os_types.h diff --git a/lib_audio/ESP8266Audio/src/libopus/AUTHORS b/lib/lib_audio/ESP8266Audio/src/libopus/AUTHORS similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/AUTHORS rename to lib/lib_audio/ESP8266Audio/src/libopus/AUTHORS diff --git a/lib_audio/ESP8266Audio/src/libopus/COPYING b/lib/lib_audio/ESP8266Audio/src/libopus/COPYING similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/COPYING rename to lib/lib_audio/ESP8266Audio/src/libopus/COPYING diff --git a/lib_audio/ESP8266Audio/src/libopus/ChangeLog b/lib/lib_audio/ESP8266Audio/src/libopus/ChangeLog similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/ChangeLog rename to lib/lib_audio/ESP8266Audio/src/libopus/ChangeLog diff --git a/lib_audio/ESP8266Audio/src/libopus/INSTALL b/lib/lib_audio/ESP8266Audio/src/libopus/INSTALL similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/INSTALL rename to lib/lib_audio/ESP8266Audio/src/libopus/INSTALL diff --git a/lib_audio/ESP8266Audio/src/libopus/NEWS b/lib/lib_audio/ESP8266Audio/src/libopus/NEWS similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/NEWS rename to lib/lib_audio/ESP8266Audio/src/libopus/NEWS diff --git a/lib_audio/ESP8266Audio/src/libopus/README b/lib/lib_audio/ESP8266Audio/src/libopus/README similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/README rename to lib/lib_audio/ESP8266Audio/src/libopus/README diff --git a/lib_audio/ESP8266Audio/src/libopus/analysis.h b/lib/lib_audio/ESP8266Audio/src/libopus/analysis.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/analysis.h rename to lib/lib_audio/ESP8266Audio/src/libopus/analysis.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/_kiss_fft_guts.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/_kiss_fft_guts.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/_kiss_fft_guts.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/_kiss_fft_guts.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/arch.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/arch.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/arch.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/arch.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/bands.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/bands.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/bands.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/bands.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/bands.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/bands.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/bands.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/bands.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt_encoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_encoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt_encoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_encoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/celt_lpc.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/cpu_support.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/cpu_support.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/cpu_support.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/cpu_support.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/cwrs.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/cwrs.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/cwrs.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/ecintrin.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/ecintrin.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/ecintrin.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/ecintrin.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entcode.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entcode.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entcode.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entcode.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entcode.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entcode.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entcode.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entcode.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entdec.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entdec.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entdec.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entdec.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entdec.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entdec.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entdec.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entdec.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entenc.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entenc.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entenc.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entenc.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/entenc.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/entenc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/entenc.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/entenc.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/fixed_debug.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/fixed_debug.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/fixed_debug.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/fixed_debug.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/fixed_generic.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/fixed_generic.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/fixed_generic.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/fixed_generic.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/float_cast.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/float_cast.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/float_cast.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/float_cast.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/kiss_fft.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/laplace.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/laplace.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/laplace.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/laplace.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/laplace.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/laplace.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/laplace.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/laplace.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/mathops.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/mathops.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/mathops.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/mathops.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/mathops.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/mathops.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/mathops.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/mathops.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/mdct.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/mdct.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/mdct.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/mdct.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/mdct.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/mdct.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/mdct.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/mdct.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/mfrngcod.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/mfrngcod.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/mfrngcod.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/mfrngcod.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/modes.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/modes.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/modes.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/modes.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/modes.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/modes.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/modes.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/modes.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/os_support.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/os_support.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/os_support.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/os_support.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/pitch.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/pitch.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/pitch.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/pitch.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/pitch.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/pitch.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/pitch.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/pitch.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/quant_bands.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/rate.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/rate.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/rate.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/rate.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/rate.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/rate.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/rate.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/rate.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/stack_alloc.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/stack_alloc.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/stack_alloc.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/stack_alloc.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_fixed.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_fixed.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/static_modes_fixed.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_fixed.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_float.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_float.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/static_modes_float.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/static_modes_float.h diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/vq.c b/lib/lib_audio/ESP8266Audio/src/libopus/celt/vq.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/vq.c rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/vq.c diff --git a/lib_audio/ESP8266Audio/src/libopus/celt/vq.h b/lib/lib_audio/ESP8266Audio/src/libopus/celt/vq.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/celt/vq.h rename to lib/lib_audio/ESP8266Audio/src/libopus/celt/vq.h diff --git a/lib_audio/ESP8266Audio/src/libopus/config.h b/lib/lib_audio/ESP8266Audio/src/libopus/config.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/config.h rename to lib/lib_audio/ESP8266Audio/src/libopus/config.h diff --git a/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.c b/lib/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/mapping_matrix.c rename to lib/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.c diff --git a/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.h b/lib/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/mapping_matrix.h rename to lib/lib_audio/ESP8266Audio/src/libopus/mapping_matrix.h diff --git a/lib_audio/ESP8266Audio/src/libopus/mlp.h b/lib/lib_audio/ESP8266Audio/src/libopus/mlp.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/mlp.h rename to lib/lib_audio/ESP8266Audio/src/libopus/mlp.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus.pc b/lib/lib_audio/ESP8266Audio/src/libopus/opus.pc similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus.pc rename to lib/lib_audio/ESP8266Audio/src/libopus/opus.pc diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_custom.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_custom.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_custom.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_custom.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_defines.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_defines.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_defines.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_defines.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_encoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_encoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_encoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_encoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_multistream.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_multistream.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_multistream.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_multistream.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_multistream_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_multistream_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_multistream_encoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream_encoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_multistream_encoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_multistream_encoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_private.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_private.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_private.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_private.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_projection.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_projection.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_projection.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_projection.h diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_projection_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_projection_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_projection_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_projection_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_projection_encoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/opus_projection_encoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_projection_encoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_projection_encoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/opus_types.h b/lib/lib_audio/ESP8266Audio/src/libopus/opus_types.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/opus_types.h rename to lib/lib_audio/ESP8266Audio/src/libopus/opus_types.h diff --git a/lib_audio/ESP8266Audio/src/libopus/repacketizer.c b/lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/repacketizer.c rename to lib/lib_audio/ESP8266Audio/src/libopus/repacketizer.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/A2NLSF.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/A2NLSF.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/A2NLSF.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/A2NLSF.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/API.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/API.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/API.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/API.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/CNG.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/CNG.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/CNG.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/CNG.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/HP_variable_cutoff.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/HP_variable_cutoff.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/HP_variable_cutoff.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/HP_variable_cutoff.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/Inlines.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/Inlines.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/Inlines.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/Inlines.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/LPC_analysis_filter.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_analysis_filter.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/LPC_analysis_filter.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_analysis_filter.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/LPC_fit.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_fit.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/LPC_fit.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_fit.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/LPC_inv_pred_gain.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_inv_pred_gain.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/LPC_inv_pred_gain.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/LPC_inv_pred_gain.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/LP_variable_cutoff.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/LP_variable_cutoff.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/LP_variable_cutoff.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/LP_variable_cutoff.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/MacroCount.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/MacroCount.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/MacroCount.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/MacroCount.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/MacroDebug.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/MacroDebug.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/MacroDebug.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/MacroDebug.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF2A.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ_weights_laroia.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ_weights_laroia.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ_weights_laroia.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_VQ_weights_laroia.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_decode.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_decode.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_decode.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_decode.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_del_dec_quant.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_del_dec_quant.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_del_dec_quant.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_del_dec_quant.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_encode.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_encode.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_encode.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_encode.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_stabilize.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_stabilize.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_stabilize.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_stabilize.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_unpack.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_unpack.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NLSF_unpack.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NLSF_unpack.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NSQ.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NSQ.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/NSQ_del_dec.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ_del_dec.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/NSQ_del_dec.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/NSQ_del_dec.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/PLC.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/PLC.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/PLC.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/PLC.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/PLC.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/PLC.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/PLC.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/PLC.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/SigProc_FIX.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/SigProc_FIX.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/SigProc_FIX.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/SigProc_FIX.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/VAD.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/VAD.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/VAD.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/VAD.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/VQ_WMat_EC.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/VQ_WMat_EC.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/VQ_WMat_EC.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/VQ_WMat_EC.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/ana_filt_bank_1.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/ana_filt_bank_1.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/ana_filt_bank_1.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/ana_filt_bank_1.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/biquad_alt.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/biquad_alt.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/biquad_alt.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/biquad_alt.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/bwexpander.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander_32.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander_32.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/bwexpander_32.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/bwexpander_32.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/check_control_input.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/check_control_input.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/check_control_input.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/check_control_input.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/code_signs.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/code_signs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/code_signs.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/code_signs.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/control.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/control.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/control.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/control.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/control_SNR.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/control_SNR.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/control_SNR.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/control_SNR.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/control_audio_bandwidth.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/control_audio_bandwidth.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/control_audio_bandwidth.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/control_audio_bandwidth.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/control_codec.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/control_codec.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/control_codec.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/control_codec.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/debug.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/debug.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/debug.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/debug.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/debug.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/debug.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/debug.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/debug.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/dec_API.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/dec_API.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/dec_API.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/dec_API.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_core.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_core.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_core.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_core.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_frame.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_frame.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_frame.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_frame.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_indices.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_indices.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_indices.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_indices.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_parameters.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_parameters.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_parameters.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_parameters.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_pitch.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_pitch.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_pitch.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_pitch.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decode_pulses.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_pulses.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decode_pulses.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decode_pulses.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/decoder_set_fs.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/decoder_set_fs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/decoder_set_fs.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/decoder_set_fs.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/define.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/define.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/define.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/define.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/enc_API.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/enc_API.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/enc_API.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/enc_API.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/encode_indices.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/encode_indices.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/encode_indices.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/encode_indices.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/encode_pulses.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/encode_pulses.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/encode_pulses.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/encode_pulses.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/errors.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/errors.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/errors.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/errors.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_analysis_filter_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/LTP_scale_ctrl_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/apply_sine_window_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/autocorr_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/burg_modified_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/corrMatrix_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/encode_frame_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LPC_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_LTP_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pitch_lags_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/find_pred_coefs_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/k2a_Q16_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/main_FIX.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/main_FIX.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/main_FIX.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/main_FIX.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/noise_shape_analysis_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/pitch_analysis_core_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/process_gains_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/regularize_correlations_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy16_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/residual_energy_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur64_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/schur_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/structs_FIX.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/structs_FIX.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/structs_FIX.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/structs_FIX.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/vector_ops_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.lo b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.lo similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.lo rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.lo diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.o b/lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.o similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.o rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/fixed/warped_autocorrelation_FIX.o diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/gain_quant.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/gain_quant.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/gain_quant.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/gain_quant.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/init_decoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/init_decoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/init_decoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/init_decoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/init_encoder.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/init_encoder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/init_encoder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/init_encoder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/inner_prod_aligned.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/inner_prod_aligned.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/inner_prod_aligned.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/inner_prod_aligned.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/interpolate.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/interpolate.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/interpolate.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/interpolate.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/lin2log.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/lin2log.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/lin2log.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/lin2log.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/log2lin.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/log2lin.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/log2lin.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/log2lin.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/macros.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/macros.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/macros.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/macros.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/main.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/main.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/main.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/main.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_defines.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_defines.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_defines.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_defines.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_tables.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_tables.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_tables.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/pitch_est_tables.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/process_NLSFs.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/process_NLSFs.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/process_NLSFs.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/process_NLSFs.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/quant_LTP_gains.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/quant_LTP_gains.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/quant_LTP_gains.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/quant_LTP_gains.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_down2_3.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_private.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_AR2.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_AR2.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_AR2.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_AR2.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_IIR_FIR.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_IIR_FIR.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_IIR_FIR.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_IIR_FIR.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_down_FIR.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_down_FIR.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_down_FIR.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_down_FIR.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_up2_HQ.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_up2_HQ.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_up2_HQ.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_private_up2_HQ.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_rom.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/resampler_structs.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_structs.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/resampler_structs.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/resampler_structs.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/shell_coder.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/shell_coder.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/shell_coder.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/shell_coder.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/sigm_Q15.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/sigm_Q15.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/sigm_Q15.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/sigm_Q15.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/sort.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/sort.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/sort.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/sort.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_LR_to_MS.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_LR_to_MS.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_LR_to_MS.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_LR_to_MS.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_MS_to_LR.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_MS_to_LR.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_MS_to_LR.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_MS_to_LR.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_decode_pred.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_decode_pred.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_decode_pred.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_decode_pred.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_encode_pred.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_encode_pred.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_encode_pred.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_encode_pred.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_find_predictor.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_find_predictor.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_find_predictor.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_find_predictor.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/stereo_quant_pred.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_quant_pred.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/stereo_quant_pred.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/stereo_quant_pred.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/structs.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/structs.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/structs.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/structs.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/sum_sqr_shift.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/sum_sqr_shift.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/sum_sqr_shift.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/sum_sqr_shift.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/table_LSF_cos.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/table_LSF_cos.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/table_LSF_cos.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/table_LSF_cos.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_LTP.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_LTP.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_LTP.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_LTP.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_NB_MB.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_NB_MB.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_NB_MB.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_NB_MB.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_WB.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_WB.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_WB.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_NLSF_CB_WB.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_gain.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_gain.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_gain.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_gain.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_other.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_other.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_other.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_other.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_pitch_lag.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_pitch_lag.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_pitch_lag.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_pitch_lag.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tables_pulses_per_block.c b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_pulses_per_block.c similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tables_pulses_per_block.c rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tables_pulses_per_block.c diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/tuning_parameters.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/tuning_parameters.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/tuning_parameters.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/tuning_parameters.h diff --git a/lib_audio/ESP8266Audio/src/libopus/silk/typedef.h b/lib/lib_audio/ESP8266Audio/src/libopus/silk/typedef.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/silk/typedef.h rename to lib/lib_audio/ESP8266Audio/src/libopus/silk/typedef.h diff --git a/lib_audio/ESP8266Audio/src/libopus/tansig_table.h b/lib/lib_audio/ESP8266Audio/src/libopus/tansig_table.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libopus/tansig_table.h rename to lib/lib_audio/ESP8266Audio/src/libopus/tansig_table.h diff --git a/lib_audio/ESP8266Audio/src/libtinysoundfont/LICENSE b/lib/lib_audio/ESP8266Audio/src/libtinysoundfont/LICENSE similarity index 100% rename from lib_audio/ESP8266Audio/src/libtinysoundfont/LICENSE rename to lib/lib_audio/ESP8266Audio/src/libtinysoundfont/LICENSE diff --git a/lib_audio/ESP8266Audio/src/libtinysoundfont/README.ESP8266 b/lib/lib_audio/ESP8266Audio/src/libtinysoundfont/README.ESP8266 similarity index 100% rename from lib_audio/ESP8266Audio/src/libtinysoundfont/README.ESP8266 rename to lib/lib_audio/ESP8266Audio/src/libtinysoundfont/README.ESP8266 diff --git a/lib_audio/ESP8266Audio/src/libtinysoundfont/README.md b/lib/lib_audio/ESP8266Audio/src/libtinysoundfont/README.md similarity index 100% rename from lib_audio/ESP8266Audio/src/libtinysoundfont/README.md rename to lib/lib_audio/ESP8266Audio/src/libtinysoundfont/README.md diff --git a/lib_audio/ESP8266Audio/src/libtinysoundfont/tsf.h b/lib/lib_audio/ESP8266Audio/src/libtinysoundfont/tsf.h similarity index 100% rename from lib_audio/ESP8266Audio/src/libtinysoundfont/tsf.h rename to lib/lib_audio/ESP8266Audio/src/libtinysoundfont/tsf.h diff --git a/lib_audio/ESP8266Audio/src/opusfile/AUTHORS b/lib/lib_audio/ESP8266Audio/src/opusfile/AUTHORS similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/AUTHORS rename to lib/lib_audio/ESP8266Audio/src/opusfile/AUTHORS diff --git a/lib_audio/ESP8266Audio/src/opusfile/COPYING b/lib/lib_audio/ESP8266Audio/src/opusfile/COPYING similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/COPYING rename to lib/lib_audio/ESP8266Audio/src/opusfile/COPYING diff --git a/lib_audio/ESP8266Audio/src/opusfile/README.esp8266.md b/lib/lib_audio/ESP8266Audio/src/opusfile/README.esp8266.md similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/README.esp8266.md rename to lib/lib_audio/ESP8266Audio/src/opusfile/README.esp8266.md diff --git a/lib_audio/ESP8266Audio/src/opusfile/README.md b/lib/lib_audio/ESP8266Audio/src/opusfile/README.md similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/README.md rename to lib/lib_audio/ESP8266Audio/src/opusfile/README.md diff --git a/lib_audio/ESP8266Audio/src/opusfile/config.h b/lib/lib_audio/ESP8266Audio/src/opusfile/config.h similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/config.h rename to lib/lib_audio/ESP8266Audio/src/opusfile/config.h diff --git a/lib_audio/ESP8266Audio/src/opusfile/info.c b/lib/lib_audio/ESP8266Audio/src/opusfile/info.c similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/info.c rename to lib/lib_audio/ESP8266Audio/src/opusfile/info.c diff --git a/lib_audio/ESP8266Audio/src/opusfile/internal.c b/lib/lib_audio/ESP8266Audio/src/opusfile/internal.c similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/internal.c rename to lib/lib_audio/ESP8266Audio/src/opusfile/internal.c diff --git a/lib_audio/ESP8266Audio/src/opusfile/internal.h b/lib/lib_audio/ESP8266Audio/src/opusfile/internal.h similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/internal.h rename to lib/lib_audio/ESP8266Audio/src/opusfile/internal.h diff --git a/lib_audio/ESP8266Audio/src/opusfile/opusfile.c b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/opusfile.c rename to lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.c diff --git a/lib_audio/ESP8266Audio/src/opusfile/opusfile.h b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/opusfile.h rename to lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.h diff --git a/lib_audio/ESP8266Audio/src/opusfile/opusfile.pc b/lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.pc similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/opusfile.pc rename to lib/lib_audio/ESP8266Audio/src/opusfile/opusfile.pc diff --git a/lib_audio/ESP8266Audio/src/opusfile/stream.c b/lib/lib_audio/ESP8266Audio/src/opusfile/stream.c similarity index 100% rename from lib_audio/ESP8266Audio/src/opusfile/stream.c rename to lib/lib_audio/ESP8266Audio/src/opusfile/stream.c diff --git a/lib_audio/ESP8266Audio/src/spiram-fast.h b/lib/lib_audio/ESP8266Audio/src/spiram-fast.h similarity index 100% rename from lib_audio/ESP8266Audio/src/spiram-fast.h rename to lib/lib_audio/ESP8266Audio/src/spiram-fast.h diff --git a/lib_audio/ESP8266Audio/tests/common.sh b/lib/lib_audio/ESP8266Audio/tests/common.sh similarity index 100% rename from lib_audio/ESP8266Audio/tests/common.sh rename to lib/lib_audio/ESP8266Audio/tests/common.sh diff --git a/lib_audio/ESP8266Audio/tests/host/Arduino.h b/lib/lib_audio/ESP8266Audio/tests/host/Arduino.h similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/Arduino.h rename to lib/lib_audio/ESP8266Audio/tests/host/Arduino.h diff --git a/lib_audio/ESP8266Audio/tests/host/Makefile b/lib/lib_audio/ESP8266Audio/tests/host/Makefile similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/Makefile rename to lib/lib_audio/ESP8266Audio/tests/host/Makefile diff --git a/lib_audio/ESP8266Audio/tests/host/Serial.cpp b/lib/lib_audio/ESP8266Audio/tests/host/Serial.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/Serial.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/Serial.cpp diff --git a/lib_audio/ESP8266Audio/tests/host/aac.cpp b/lib/lib_audio/ESP8266Audio/tests/host/aac.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/aac.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/aac.cpp diff --git a/lib_audio/ESP8266Audio/tests/host/midi.cpp b/lib/lib_audio/ESP8266Audio/tests/host/midi.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/midi.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/midi.cpp diff --git a/lib_audio/ESP8266Audio/tests/host/mp3.cpp b/lib/lib_audio/ESP8266Audio/tests/host/mp3.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/mp3.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/mp3.cpp diff --git a/lib_audio/ESP8266Audio/tests/host/opus.cpp b/lib/lib_audio/ESP8266Audio/tests/host/opus.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/opus.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/opus.cpp diff --git a/lib_audio/ESP8266Audio/tests/host/pgmspace.h b/lib/lib_audio/ESP8266Audio/tests/host/pgmspace.h similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/pgmspace.h rename to lib/lib_audio/ESP8266Audio/tests/host/pgmspace.h diff --git a/lib_audio/ESP8266Audio/tests/host/test_8u_16.wav b/lib/lib_audio/ESP8266Audio/tests/host/test_8u_16.wav similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/test_8u_16.wav rename to lib/lib_audio/ESP8266Audio/tests/host/test_8u_16.wav diff --git a/lib_audio/ESP8266Audio/tests/host/wav.cpp b/lib/lib_audio/ESP8266Audio/tests/host/wav.cpp similarity index 100% rename from lib_audio/ESP8266Audio/tests/host/wav.cpp rename to lib/lib_audio/ESP8266Audio/tests/host/wav.cpp diff --git a/lib_audio/ESP8266SAM/README.md b/lib/lib_audio/ESP8266SAM/README.md similarity index 100% rename from lib_audio/ESP8266SAM/README.md rename to lib/lib_audio/ESP8266SAM/README.md diff --git a/lib_audio/ESP8266SAM/examples/Speak/Speak.ino b/lib/lib_audio/ESP8266SAM/examples/Speak/Speak.ino similarity index 100% rename from lib_audio/ESP8266SAM/examples/Speak/Speak.ino rename to lib/lib_audio/ESP8266SAM/examples/Speak/Speak.ino diff --git a/lib_audio/ESP8266SAM/library.json b/lib/lib_audio/ESP8266SAM/library.json similarity index 100% rename from lib_audio/ESP8266SAM/library.json rename to lib/lib_audio/ESP8266SAM/library.json diff --git a/lib_audio/ESP8266SAM/library.properties b/lib/lib_audio/ESP8266SAM/library.properties similarity index 100% rename from lib_audio/ESP8266SAM/library.properties rename to lib/lib_audio/ESP8266SAM/library.properties diff --git a/lib_audio/ESP8266SAM/src/ESP8266SAM.cpp b/lib/lib_audio/ESP8266SAM/src/ESP8266SAM.cpp similarity index 100% rename from lib_audio/ESP8266SAM/src/ESP8266SAM.cpp rename to lib/lib_audio/ESP8266SAM/src/ESP8266SAM.cpp diff --git a/lib_audio/ESP8266SAM/src/ESP8266SAM.h b/lib/lib_audio/ESP8266SAM/src/ESP8266SAM.h similarity index 100% rename from lib_audio/ESP8266SAM/src/ESP8266SAM.h rename to lib/lib_audio/ESP8266SAM/src/ESP8266SAM.h diff --git a/lib_audio/ESP8266SAM/src/ReciterTabs.h b/lib/lib_audio/ESP8266SAM/src/ReciterTabs.h similarity index 100% rename from lib_audio/ESP8266SAM/src/ReciterTabs.h rename to lib/lib_audio/ESP8266SAM/src/ReciterTabs.h diff --git a/lib_audio/ESP8266SAM/src/RenderTabs.h b/lib/lib_audio/ESP8266SAM/src/RenderTabs.h similarity index 100% rename from lib_audio/ESP8266SAM/src/RenderTabs.h rename to lib/lib_audio/ESP8266SAM/src/RenderTabs.h diff --git a/lib_audio/ESP8266SAM/src/SamData.h b/lib/lib_audio/ESP8266SAM/src/SamData.h similarity index 100% rename from lib_audio/ESP8266SAM/src/SamData.h rename to lib/lib_audio/ESP8266SAM/src/SamData.h diff --git a/lib_audio/ESP8266SAM/src/SamTabs.h b/lib/lib_audio/ESP8266SAM/src/SamTabs.h similarity index 100% rename from lib_audio/ESP8266SAM/src/SamTabs.h rename to lib/lib_audio/ESP8266SAM/src/SamTabs.h diff --git a/lib_audio/ESP8266SAM/src/reciter.c b/lib/lib_audio/ESP8266SAM/src/reciter.c similarity index 100% rename from lib_audio/ESP8266SAM/src/reciter.c rename to lib/lib_audio/ESP8266SAM/src/reciter.c diff --git a/lib_audio/ESP8266SAM/src/reciter.h b/lib/lib_audio/ESP8266SAM/src/reciter.h similarity index 100% rename from lib_audio/ESP8266SAM/src/reciter.h rename to lib/lib_audio/ESP8266SAM/src/reciter.h diff --git a/lib_audio/ESP8266SAM/src/render.c b/lib/lib_audio/ESP8266SAM/src/render.c similarity index 100% rename from lib_audio/ESP8266SAM/src/render.c rename to lib/lib_audio/ESP8266SAM/src/render.c diff --git a/lib_audio/ESP8266SAM/src/render.h b/lib/lib_audio/ESP8266SAM/src/render.h similarity index 100% rename from lib_audio/ESP8266SAM/src/render.h rename to lib/lib_audio/ESP8266SAM/src/render.h diff --git a/lib_audio/ESP8266SAM/src/sam.c b/lib/lib_audio/ESP8266SAM/src/sam.c similarity index 100% rename from lib_audio/ESP8266SAM/src/sam.c rename to lib/lib_audio/ESP8266SAM/src/sam.c diff --git a/lib_audio/ESP8266SAM/src/sam.h b/lib/lib_audio/ESP8266SAM/src/sam.h similarity index 100% rename from lib_audio/ESP8266SAM/src/sam.h rename to lib/lib_audio/ESP8266SAM/src/sam.h diff --git a/lib_audio/ESP8266SAM/src/samdebug.c b/lib/lib_audio/ESP8266SAM/src/samdebug.c similarity index 100% rename from lib_audio/ESP8266SAM/src/samdebug.c rename to lib/lib_audio/ESP8266SAM/src/samdebug.c diff --git a/lib_audio/ESP8266SAM/src/samdebug.h b/lib/lib_audio/ESP8266SAM/src/samdebug.h similarity index 100% rename from lib_audio/ESP8266SAM/src/samdebug.h rename to lib/lib_audio/ESP8266SAM/src/samdebug.h diff --git a/lib_basic/C2Programmer-1.0.0/README.md b/lib/lib_basic/C2Programmer-1.0.0/README.md similarity index 100% rename from lib_basic/C2Programmer-1.0.0/README.md rename to lib/lib_basic/C2Programmer-1.0.0/README.md diff --git a/lib_basic/C2Programmer-1.0.0/keywords.txt b/lib/lib_basic/C2Programmer-1.0.0/keywords.txt similarity index 100% rename from lib_basic/C2Programmer-1.0.0/keywords.txt rename to lib/lib_basic/C2Programmer-1.0.0/keywords.txt diff --git a/lib_basic/C2Programmer-1.0.0/library.json b/lib/lib_basic/C2Programmer-1.0.0/library.json similarity index 100% rename from lib_basic/C2Programmer-1.0.0/library.json rename to lib/lib_basic/C2Programmer-1.0.0/library.json diff --git a/lib_basic/C2Programmer-1.0.0/library.properties b/lib/lib_basic/C2Programmer-1.0.0/library.properties similarity index 100% rename from lib_basic/C2Programmer-1.0.0/library.properties rename to lib/lib_basic/C2Programmer-1.0.0/library.properties diff --git a/lib_basic/C2Programmer-1.0.0/src/c2.cpp b/lib/lib_basic/C2Programmer-1.0.0/src/c2.cpp similarity index 100% rename from lib_basic/C2Programmer-1.0.0/src/c2.cpp rename to lib/lib_basic/C2Programmer-1.0.0/src/c2.cpp diff --git a/lib_basic/C2Programmer-1.0.0/src/c2.h b/lib/lib_basic/C2Programmer-1.0.0/src/c2.h similarity index 100% rename from lib_basic/C2Programmer-1.0.0/src/c2.h rename to lib/lib_basic/C2Programmer-1.0.0/src/c2.h diff --git a/lib_basic/C2Programmer-1.0.0/src/ihx.cpp b/lib/lib_basic/C2Programmer-1.0.0/src/ihx.cpp similarity index 100% rename from lib_basic/C2Programmer-1.0.0/src/ihx.cpp rename to lib/lib_basic/C2Programmer-1.0.0/src/ihx.cpp diff --git a/lib_basic/C2Programmer-1.0.0/src/ihx.h b/lib/lib_basic/C2Programmer-1.0.0/src/ihx.h similarity index 100% rename from lib_basic/C2Programmer-1.0.0/src/ihx.h rename to lib/lib_basic/C2Programmer-1.0.0/src/ihx.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/.github/CONTRIBUTING.md b/lib/lib_basic/IRremoteESP8266-2.7.11/.github/CONTRIBUTING.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.github/CONTRIBUTING.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/.github/CONTRIBUTING.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/.github/Contributors.md b/lib/lib_basic/IRremoteESP8266-2.7.11/.github/Contributors.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.github/Contributors.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/.github/Contributors.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/.github/issue_template.md b/lib/lib_basic/IRremoteESP8266-2.7.11/.github/issue_template.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.github/issue_template.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/.github/issue_template.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/.gitignore b/lib/lib_basic/IRremoteESP8266-2.7.11/.gitignore similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.gitignore rename to lib/lib_basic/IRremoteESP8266-2.7.11/.gitignore diff --git a/lib_basic/IRremoteESP8266-2.7.11/.gitmodules b/lib/lib_basic/IRremoteESP8266-2.7.11/.gitmodules similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.gitmodules rename to lib/lib_basic/IRremoteESP8266-2.7.11/.gitmodules diff --git a/lib_basic/IRremoteESP8266-2.7.11/.style.yapf b/lib/lib_basic/IRremoteESP8266-2.7.11/.style.yapf similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.style.yapf rename to lib/lib_basic/IRremoteESP8266-2.7.11/.style.yapf diff --git a/lib_basic/IRremoteESP8266-2.7.11/.travis.yml b/lib/lib_basic/IRremoteESP8266-2.7.11/.travis.yml similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/.travis.yml rename to lib/lib_basic/IRremoteESP8266-2.7.11/.travis.yml diff --git a/lib_basic/IRremoteESP8266-2.7.11/CPPLINT.cfg b/lib/lib_basic/IRremoteESP8266-2.7.11/CPPLINT.cfg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/CPPLINT.cfg rename to lib/lib_basic/IRremoteESP8266-2.7.11/CPPLINT.cfg diff --git a/lib_basic/IRremoteESP8266-2.7.11/Doxyfile b/lib/lib_basic/IRremoteESP8266-2.7.11/Doxyfile similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/Doxyfile rename to lib/lib_basic/IRremoteESP8266-2.7.11/Doxyfile diff --git a/lib_basic/IRremoteESP8266-2.7.11/LICENSE.txt b/lib/lib_basic/IRremoteESP8266-2.7.11/LICENSE.txt similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/LICENSE.txt rename to lib/lib_basic/IRremoteESP8266-2.7.11/LICENSE.txt diff --git a/lib_basic/IRremoteESP8266-2.7.11/README.md b/lib/lib_basic/IRremoteESP8266-2.7.11/README.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/README.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/README.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/README_fr.md b/lib/lib_basic/IRremoteESP8266-2.7.11/README_fr.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/README_fr.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/README_fr.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/ReleaseNotes.md b/lib/lib_basic/IRremoteESP8266-2.7.11/ReleaseNotes.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/ReleaseNotes.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/ReleaseNotes.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/SupportedProtocols.md b/lib/lib_basic/IRremoteESP8266-2.7.11/SupportedProtocols.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/SupportedProtocols.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/SupportedProtocols.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/README.md b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/README.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/README.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/README.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/README_fr.md b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/README_fr.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/README_fr.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/README_fr.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/_config.yml b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/_config.yml similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/_config.yml rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/_config.yml diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRac_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRrecv_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRremoteESP8266_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRsend_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtext_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRtimer_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/IRutils_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/README_8md.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/README_8md.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/README_8md.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/README_8md.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/annotated.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/annotated.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/annotated.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/annotated.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bc_s.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bc_s.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bc_s.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bc_s.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bdwn.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bdwn.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bdwn.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/bdwn.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAirwellAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRAmcorAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRArgoAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCarrierAc64__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoolixAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRCoronaAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin128__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin152__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin160__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin176__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin216__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin2__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikin64__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDaikinESP__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRDelonghiAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRElectraAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRFujitsuAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGoodweatherAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRGreeAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierACYRW02__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHaierAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc1__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc344__inherit__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc3__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc424__inherit__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRHitachiAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRKelvinatorAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRLgAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMideaAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi112__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishi136__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy152Ac__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRMitsubishiHeavy88Ac__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRNeoclimaAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRPanasonicAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSamsungAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSanyoAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRSharpAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTcl112Ac__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTechnibelAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTecoAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRToshibaAC__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTranscoldAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRTrotecESP__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVestelAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRVoltas__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRWhirlpoolAc__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRac__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRrecv__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRsend.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classIRtimer.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classTimerMs.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classdecode__results.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classes.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classes.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classes.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/classes.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/closed.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/closed.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/closed.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/closed.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-CH_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/de-DE_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/defaults_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/deprecated.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/deprecated.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/deprecated.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/deprecated.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_49e56c817e5e54854c35e136979f97ca.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_49e56c817e5e54854c35e136979f97ca.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_49e56c817e5e54854c35e136979f97ca.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_49e56c817e5e54854c35e136979f97ca.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_68267d1309a1af8e8297ef4c3efbcdba.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_84fe998d1eb06414cc389ad334e77e63.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_84fe998d1eb06414cc389ad334e77e63.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_84fe998d1eb06414cc389ad334e77e63.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dir_84fe998d1eb06414cc389ad334e77e63.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doc.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doc.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doc.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doc.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.css b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.css similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.css rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.css diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen__index_8md.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen__index_8md.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen__index_8md.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/doxygen__index_8md.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dynsections.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dynsections.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dynsections.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/dynsections.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-AU_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-IE_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-UK_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/en-US_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/es-ES_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/files.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/files.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/files.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/files.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderclosed.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderclosed.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderclosed.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderclosed.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderopen.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderopen.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderopen.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/folderopen.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/fr-FR_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_g.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_g.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_g.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_g.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_i.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_i.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_i.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_i.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_k.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_k.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_k.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_k.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_l.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_l.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_l.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_l.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_m.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_m.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_m.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_m.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_n.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_n.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_n.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_n.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_o.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_o.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_o.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_o.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_p.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_p.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_p.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_p.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_r.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_r.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_r.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_r.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_s.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_s.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_s.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_s.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_u.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_u.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_u.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_u.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_v.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_v.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_v.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_v.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_w.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_w.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_w.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_w.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_~.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_~.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_~.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_func_~.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_g.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_g.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_g.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_g.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_i.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_i.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_i.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_i.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_k.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_k.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_k.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_k.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_l.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_l.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_l.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_l.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_m.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_m.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_m.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_m.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_n.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_n.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_n.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_n.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_o.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_o.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_o.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_o.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_p.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_p.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_p.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_p.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_q.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_q.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_q.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_q.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_r.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_r.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_r.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_r.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_rela.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_rela.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_rela.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_rela.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_s.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_s.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_s.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_s.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_u.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_u.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_u.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_u.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_v.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_v.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_v.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_v.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_i.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_i.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_i.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_i.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_l.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_l.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_l.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_l.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_m.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_m.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_m.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_m.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_n.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_n.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_n.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_n.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_o.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_o.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_o.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_o.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_p.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_p.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_p.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_p.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_q.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_q.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_q.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_q.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_r.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_r.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_r.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_r.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_s.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_s.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_s.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_s.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_u.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_u.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_u.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_u.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_v.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_v.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_v.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_v.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_w.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_w.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_w.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_w.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_x.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_x.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_x.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_x.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_z.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_z.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_z.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_vars_z.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_w.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_w.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_w.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_w.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_x.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_x.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_x.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_x.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_z.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_z.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_z.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_z.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_~.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_~.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_~.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/functions_~.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_enum.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_enum.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_enum.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_enum.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_eval.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_eval.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_eval.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_eval.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_func.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_func.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_func.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_func.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_g.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_g.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_g.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_g.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_i.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_i.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_i.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_i.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_j.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_j.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_j.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_j.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_k.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_k.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_k.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_k.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_l.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_l.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_l.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_l.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_m.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_m.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_m.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_m.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_n.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_n.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_n.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_n.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_p.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_p.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_p.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_p.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_r.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_r.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_r.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_r.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_s.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_s.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_s.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_s.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_type.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_type.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_type.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_type.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_u.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_u.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_u.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_u.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_v.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_v.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_v.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_v.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_i.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_i.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_i.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_i.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_k.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_k.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_k.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_vars_k.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_w.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_w.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_w.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_w.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_x.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_x.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_x.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_x.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_y.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_y.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_y.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_y.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_z.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_z.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_z.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/globals_z.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/graph_legend.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/hierarchy.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/hierarchy.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/hierarchy.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/hierarchy.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/i18n_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/index.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/index.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/index.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/index.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_0.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_1.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_10.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_11.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_12.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_13.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_14.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_15.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_16.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_17.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_18.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_19.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_2.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_20.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_21.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_22.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_23.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_24.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_25.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_26.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_27.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_28.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_29.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_3.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_30.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_31.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_32.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_33.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_34.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_35.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_36.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_37.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_38.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_39.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_4.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_40.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_41.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_42.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_43.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_44.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_45.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_46.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_47.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_48.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_49.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_5.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_50.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_51.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_52.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_53.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_54.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_55.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_56.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_57.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_58.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_59.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_6.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_60.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_61.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_62.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_63.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_64.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_65.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_66.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_67.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_68.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_7.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_8.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherit_graph_9.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherits.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherits.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherits.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/inherits.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Airwell_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Aiwa_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Aiwa_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Aiwa_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Aiwa_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Amcor_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Argo_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Carrier_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Coolix_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Corona_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Daikin_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Delonghi_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Denon_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Denon_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Denon_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Denon_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Dish_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Dish_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Dish_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Dish_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Doshisha_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Doshisha_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Doshisha_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Doshisha_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Electra_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Epson_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Epson_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Epson_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Epson_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Fujitsu_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GICable_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GICable_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GICable_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GICable_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GlobalCache_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GlobalCache_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GlobalCache_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__GlobalCache_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Goodweather_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Gree_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Haier_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Hitachi_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Inax_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Inax_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Inax_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Inax_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__JVC_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__JVC_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__JVC_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__JVC_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Kelvinator_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__LG_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lasertag_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lasertag_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lasertag_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lasertag_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lego_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lego_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lego_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lego_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lutron_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lutron_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lutron_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Lutron_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MWM_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MWM_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MWM_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MWM_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Magiquest_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Metz_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Metz_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Metz_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Metz_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Midea_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__MitsubishiHeavy_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Mitsubishi_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Multibrackets_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Multibrackets_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Multibrackets_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Multibrackets_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__NEC_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Neoclima_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Nikai_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Nikai_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Nikai_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Nikai_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Panasonic_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pioneer_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pioneer_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pioneer_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pioneer_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pronto_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pronto_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pronto_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Pronto_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RC5__RC6_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RC5__RC6_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RC5__RC6_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RC5__RC6_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RCMM_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RCMM_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RCMM_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__RCMM_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Samsung_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sanyo_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sharp_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sherwood_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sherwood_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sherwood_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sherwood_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sony_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sony_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sony_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Sony_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Symphony_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Symphony_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Symphony_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Symphony_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Tcl_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Technibel_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Teco_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Toshiba_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Transcold_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Trotec_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Vestel_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Voltas_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whirlpool_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whynter_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whynter_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whynter_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Whynter_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Zepeal_8cpp.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Zepeal_8cpp.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Zepeal_8cpp.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/ir__Zepeal_8cpp.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/it-IT_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/jquery.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/jquery.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/jquery.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/jquery.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/md_src_locale_README.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/md_src_locale_README.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/md_src_locale_README.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/md_src_locale_README.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menu.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menu.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menu.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menu.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menudata.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menudata.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menudata.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/menudata.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceIRAcUtils.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceIRAcUtils.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceIRAcUtils.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceIRAcUtils.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceirutils.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceirutils.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceirutils.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaceirutils.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_enum.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_enum.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_enum.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_enum.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_func.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_func.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_func.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacemembers_func.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaces.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaces.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaces.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespaces.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacestdAc.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacestdAc.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacestdAc.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/namespacestdAc.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_f.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_f.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_f.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_f.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_g.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_g.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_g.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_g.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_h.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_h.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_h.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/nav_h.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/open.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/open.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/open.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/open.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/pages.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/pages.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/pages.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/pages.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_10.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_11.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_12.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_13.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_14.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_15.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_16.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_17.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_18.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_19.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1a.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_1b.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_a.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_b.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_c.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_d.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_e.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/all_f.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/classes_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/close.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/close.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/close.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/close.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enums_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_10.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_11.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_12.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_13.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_14.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_15.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_a.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_b.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_c.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_d.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_e.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/enumvalues_f.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/files_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_10.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_11.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_12.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_13.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_14.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_15.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_16.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_17.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_a.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_b.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_c.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_d.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_e.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/functions_f.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/mag_sel.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/mag_sel.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/mag_sel.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/mag_sel.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/namespaces_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/nomatches.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/nomatches.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/nomatches.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/nomatches.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/pages_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/related_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.css b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.css similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.css rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.css diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_l.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_l.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_l.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_l.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_m.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_m.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_m.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_m.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_r.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_r.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_r.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/search_r.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/searchdata.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/searchdata.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/searchdata.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/searchdata.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/typedefs_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_0.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_1.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_10.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_11.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_12.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_13.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_14.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_15.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_16.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_17.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_2.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_3.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_4.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_5.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_6.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_7.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_8.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_9.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_a.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_b.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_c.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_d.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_e.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.js b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/search/variables_f.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/splitbar.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/splitbar.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/splitbar.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/splitbar.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structCoronaSection.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structirparams__t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structmatch__result__t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/structstdAc_1_1state__t.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_off.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_off.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_off.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_off.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_on.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_on.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_on.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/sync_on.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_a.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_a.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_a.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_a.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_b.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_b.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_b.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_b.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_h.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_h.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_h.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_h.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_s.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_s.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_s.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tab_s.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tabs.css b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tabs.css similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tabs.css rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/tabs.css diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/todo.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/todo.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/todo.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/todo.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAirwellProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionAmcorProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionArgoProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCarrierProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.map b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.map similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.map rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.map diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.md5 b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.md5 similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.md5 rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.md5 diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.png b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionCoronaProtocol__coll__graph.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionDelonghiProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionGreeProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionHaierYRW02Protocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionMideaProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionVoltasProtocol.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest-members.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest-members.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest-members.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest-members.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/unionmagiquest.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h_source.html b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h_source.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h_source.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen/html/zh-CN_8h_source.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen_index.md b/lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen_index.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/docs/doxygen_index.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/docs/doxygen_index.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/BlynkIrRemote.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/BlynkIrRemote.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/BlynkIrRemote.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/BlynkIrRemote.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/BlynkIrRemote/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/CommonAcControl.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/CommonAcControl.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/CommonAcControl.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/CommonAcControl.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/CommonAcControl/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/ControlSamsungAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/ControlSamsungAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/ControlSamsungAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/ControlSamsungAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/DumbIRRepeater.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/DumbIRRepeater.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/DumbIRRepeater.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/DumbIRRepeater/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/IRGCSendDemo.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCSendDemo/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/IRGCTCPServer.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRGCTCPServer/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.h b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/IRMQTTServer.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRMQTTServer/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/IRServer.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/IRServer.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/IRServer.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/IRServer.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRServer/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/IRrecvDemo.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/IRrecvDemo.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/IRrecvDemo.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDemo/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/IRrecvDump.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/IRrecvDump.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/IRrecvDump.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/IRrecvDump.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDump/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/IRrecvDumpV2.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV2/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/BaseOTA.h b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/BaseOTA.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/BaseOTA.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/BaseOTA.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/IRrecvDumpV3.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/IRrecvDumpV3.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/IRrecvDumpV3.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/IRrecvDumpV3.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRrecvDumpV3/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/IRsendDemo.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/IRsendDemo.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/IRsendDemo.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/IRsendDemo.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendDemo/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/IRsendProntoDemo.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/IRsendProntoDemo/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/JVCPanasonicSendDemo/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/LGACSend.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/LGACSend.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/LGACSend.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/LGACSend/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/SmartIRRepeater.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/SmartIRRepeater.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/SmartIRRepeater.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/SmartIRRepeater/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/TurnOnArgoAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnArgoAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnDaikinAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnFujitsuAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/TurnOnGreeAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/TurnOnGreeAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/TurnOnGreeAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/TurnOnGreeAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnGreeAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnKelvinatorAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnMitsubishiHeavyAc/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnPanasonicAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnToshibaAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/TurnOnTrotecAC/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/README.md b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/README.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/README.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/README.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.h b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.ino b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.ino similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.ino rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/Web-AC-control.ino diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/favicon.ico b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/favicon.ico similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/favicon.ico rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/favicon.ico diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_off.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_off.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_off.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_off.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_on.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_on.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_on.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_1_on.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_off.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_off.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_off.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_off.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_on.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_on.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_on.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_2_on.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_off.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_off.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_off.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_off.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_on.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_on.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_on.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_3_on.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_off.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_off.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_off.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_off.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_on.svg b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_on.svg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_on.svg rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/level_4_on.svg diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.html b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.html similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.html rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.html diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.js b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.js similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.js rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/data/ui.js diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/printscreen.png b/lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/printscreen.png similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/printscreen.png rename to lib/lib_basic/IRremoteESP8266-2.7.11/examples/Web-AC-control/printscreen.png diff --git a/lib_basic/IRremoteESP8266-2.7.11/keywords.txt b/lib/lib_basic/IRremoteESP8266-2.7.11/keywords.txt similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/keywords.txt rename to lib/lib_basic/IRremoteESP8266-2.7.11/keywords.txt diff --git a/lib_basic/IRremoteESP8266-2.7.11/library.json b/lib/lib_basic/IRremoteESP8266-2.7.11/library.json similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/library.json rename to lib/lib_basic/IRremoteESP8266-2.7.11/library.json diff --git a/lib_basic/IRremoteESP8266-2.7.11/library.properties b/lib/lib_basic/IRremoteESP8266-2.7.11/library.properties similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/library.properties rename to lib/lib_basic/IRremoteESP8266-2.7.11/library.properties diff --git a/lib_basic/IRremoteESP8266-2.7.11/platformio.ini b/lib/lib_basic/IRremoteESP8266-2.7.11/platformio.ini similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/platformio.ini rename to lib/lib_basic/IRremoteESP8266-2.7.11/platformio.ini diff --git a/lib_basic/IRremoteESP8266-2.7.11/pylintrc b/lib/lib_basic/IRremoteESP8266-2.7.11/pylintrc similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/pylintrc rename to lib/lib_basic/IRremoteESP8266-2.7.11/pylintrc diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/CPPLINT.cfg b/lib/lib_basic/IRremoteESP8266-2.7.11/src/CPPLINT.cfg similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/CPPLINT.cfg rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/CPPLINT.cfg diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRac.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRac.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRac.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRac.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRac.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRac.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRac.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRac.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRrecv.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRremoteESP8266.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRremoteESP8266.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRremoteESP8266.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRremoteESP8266.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRsend.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRsend.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRsend.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRtext.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRtext.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtext.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRtimer.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRutils.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/IRutils.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/IRutils.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/i18n.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/i18n.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/i18n.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/i18n.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Airwell.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Aiwa.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Aiwa.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Aiwa.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Aiwa.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Amcor.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Argo.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Carrier.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Coolix.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Corona.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Daikin.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Delonghi.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Denon.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Denon.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Denon.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Denon.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Dish.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Dish.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Dish.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Dish.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Doshisha.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Doshisha.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Doshisha.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Doshisha.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Electra.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Epson.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Epson.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Epson.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Epson.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Fujitsu.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_GICable.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_GICable.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_GICable.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_GICable.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_GlobalCache.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_GlobalCache.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_GlobalCache.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_GlobalCache.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Goodweather.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Gree.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Haier.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Hitachi.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Inax.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Inax.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Inax.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Inax.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_JVC.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_JVC.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_JVC.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_JVC.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Kelvinator.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_LG.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lasertag.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lasertag.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Lasertag.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lasertag.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lego.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lego.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Lego.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lego.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lutron.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lutron.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Lutron.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Lutron.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_MWM.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MWM.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_MWM.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MWM.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Magiquest.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Metz.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Metz.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Metz.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Metz.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Midea.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Mitsubishi.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_MitsubishiHeavy.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Multibrackets.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Multibrackets.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Multibrackets.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Multibrackets.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_NEC.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Neoclima.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Nikai.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Nikai.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Nikai.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Nikai.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Panasonic.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pioneer.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pioneer.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Pioneer.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pioneer.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pronto.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pronto.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Pronto.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Pronto.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_RC5_RC6.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_RC5_RC6.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_RC5_RC6.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_RC5_RC6.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_RCMM.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_RCMM.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_RCMM.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_RCMM.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Samsung.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sanyo.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sharp.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sherwood.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sherwood.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sherwood.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sherwood.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sony.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sony.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Sony.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Sony.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Symphony.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Symphony.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Symphony.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Symphony.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Tcl.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Technibel.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Teco.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Toshiba.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Transcold.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Trotec.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Vestel.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Voltas.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whirlpool.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whynter.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whynter.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Whynter.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Whynter.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/ir_Zepeal.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Zepeal.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/ir_Zepeal.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/ir_Zepeal.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/README.md b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/README.md similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/README.md rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/README.md diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-CH.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-CH.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/de-CH.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-CH.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-DE.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-DE.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/de-DE.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/de-DE.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/defaults.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/defaults.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/defaults.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/defaults.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-AU.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-AU.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/en-AU.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-AU.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-IE.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-IE.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/en-IE.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-IE.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-UK.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-UK.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/en-UK.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-UK.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-US.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-US.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/en-US.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/en-US.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/es-ES.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/es-ES.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/es-ES.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/es-ES.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/fr-FR.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/fr-FR.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/fr-FR.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/fr-FR.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/it-IT.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/it-IT.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/it-IT.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/it-IT.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/src/locale/zh-CN.h b/lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/zh-CN.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/src/locale/zh-CN.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/src/locale/zh-CN.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRac_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRac_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRac_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRac_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.h b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRrecv_test.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.h b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.h similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.h rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRsend_test.h diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/IRutils_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/IRutils_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/IRutils_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/IRutils_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/Makefile b/lib/lib_basic/IRremoteESP8266-2.7.11/test/Makefile similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/Makefile rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/Makefile diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Airwell_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Airwell_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Airwell_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Airwell_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Aiwa_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Aiwa_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Aiwa_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Aiwa_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Amcor_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Amcor_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Amcor_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Amcor_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Argo_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Argo_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Argo_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Argo_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Carrier_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Carrier_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Carrier_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Carrier_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Coolix_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Coolix_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Coolix_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Coolix_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Corona_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Corona_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Corona_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Corona_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Daikin_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Daikin_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Daikin_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Daikin_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Delonghi_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Delonghi_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Delonghi_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Delonghi_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Denon_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Denon_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Denon_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Denon_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Dish_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Dish_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Dish_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Dish_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Doshisha_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Doshisha_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Doshisha_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Doshisha_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Electra_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Electra_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Electra_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Electra_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Epson_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Epson_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Epson_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Epson_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Fujitsu_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Fujitsu_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Fujitsu_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Fujitsu_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_GICable_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_GICable_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_GICable_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_GICable_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_GlobalCache_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_GlobalCache_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_GlobalCache_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_GlobalCache_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Goodweather_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Goodweather_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Goodweather_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Goodweather_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Gree_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Gree_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Gree_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Gree_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Haier_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Haier_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Haier_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Haier_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Hitachi_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Hitachi_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Hitachi_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Hitachi_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Inax_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Inax_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Inax_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Inax_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_JVC_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_JVC_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_JVC_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_JVC_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Kelvinator_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Kelvinator_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Kelvinator_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Kelvinator_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_LG_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_LG_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_LG_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_LG_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lasertag_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lasertag_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Lasertag_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lasertag_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lego_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lego_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Lego_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lego_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lutron_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lutron_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Lutron_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Lutron_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_MWM_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_MWM_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_MWM_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_MWM_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Magiquest_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Magiquest_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Magiquest_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Magiquest_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Metz_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Metz_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Metz_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Metz_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Midea_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Midea_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Midea_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Midea_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_MitsubishiHeavy_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_MitsubishiHeavy_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_MitsubishiHeavy_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_MitsubishiHeavy_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Mitsubishi_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Mitsubishi_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Mitsubishi_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Mitsubishi_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Multibrackets_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Multibrackets_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Multibrackets_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Multibrackets_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_NEC_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_NEC_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_NEC_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_NEC_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Neoclima_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Neoclima_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Neoclima_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Neoclima_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Nikai_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Nikai_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Nikai_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Nikai_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Panasonic_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Panasonic_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Panasonic_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Panasonic_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pioneer_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pioneer_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Pioneer_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pioneer_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pronto_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pronto_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Pronto_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Pronto_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_RC5_RC6_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_RC5_RC6_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_RC5_RC6_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_RC5_RC6_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_RCMM_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_RCMM_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_RCMM_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_RCMM_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Samsung_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Samsung_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Samsung_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Samsung_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sanyo_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sanyo_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Sanyo_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sanyo_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sharp_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sharp_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Sharp_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sharp_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sherwood_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sherwood_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Sherwood_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sherwood_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sony_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sony_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Sony_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Sony_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Symphony_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Symphony_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Symphony_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Symphony_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Tcl_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Tcl_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Tcl_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Tcl_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Technibel_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Technibel_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Technibel_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Technibel_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Teco_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Teco_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Teco_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Teco_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Toshiba_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Toshiba_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Toshiba_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Toshiba_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Transcold_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Transcold_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Transcold_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Transcold_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Trotec_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Trotec_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Trotec_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Trotec_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Vestel_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Vestel_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Vestel_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Vestel_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Voltas_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Voltas_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Voltas_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Voltas_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whirlpool_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whirlpool_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Whirlpool_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whirlpool_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whynter_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whynter_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Whynter_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Whynter_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/test/ir_Zepeal_test.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Zepeal_test.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/test/ir_Zepeal_test.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/test/ir_Zepeal_test.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/Makefile b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/Makefile similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/Makefile rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/Makefile diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/RawToGlobalCache.sh b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/RawToGlobalCache.sh similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/RawToGlobalCache.sh rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/RawToGlobalCache.sh diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data.py b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data.py similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data.py rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data.py diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data_test.py b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data_test.py similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data_test.py rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/auto_analyse_raw_data_test.py diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/gc_decode.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/gc_decode.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/gc_decode.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/gc_decode.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/generate_irtext_h.sh b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/generate_irtext_h.sh similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/generate_irtext_h.sh rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/generate_irtext_h.sh diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/mkkeywords b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/mkkeywords similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/mkkeywords rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/mkkeywords diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/mode2_decode.cpp b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/mode2_decode.cpp similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/mode2_decode.cpp rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/mode2_decode.cpp diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code.py b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code.py similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code.py rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code.py diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code_test.py b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code_test.py similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code_test.py rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/raw_to_pronto_code_test.py diff --git a/lib_basic/IRremoteESP8266-2.7.11/tools/scrape_supported_devices.py b/lib/lib_basic/IRremoteESP8266-2.7.11/tools/scrape_supported_devices.py similarity index 100% rename from lib_basic/IRremoteESP8266-2.7.11/tools/scrape_supported_devices.py rename to lib/lib_basic/IRremoteESP8266-2.7.11/tools/scrape_supported_devices.py diff --git a/lib_basic/NeoPixelBus-2.6.0/.gitattributes b/lib/lib_basic/NeoPixelBus-2.6.0/.gitattributes similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.gitattributes rename to lib/lib_basic/NeoPixelBus-2.6.0/.gitattributes diff --git a/lib_basic/NeoPixelBus-2.6.0/.github/FUNDING.yml b/lib/lib_basic/NeoPixelBus-2.6.0/.github/FUNDING.yml similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.github/FUNDING.yml rename to lib/lib_basic/NeoPixelBus-2.6.0/.github/FUNDING.yml diff --git a/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/all-others.md b/lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/all-others.md similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/all-others.md rename to lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/all-others.md diff --git a/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/bug_report.md b/lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/bug_report.md rename to lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/feature_request.md b/lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/feature_request.md similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/feature_request.md rename to lib/lib_basic/NeoPixelBus-2.6.0/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/lib_basic/NeoPixelBus-2.6.0/.gitignore b/lib/lib_basic/NeoPixelBus-2.6.0/.gitignore similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/.gitignore rename to lib/lib_basic/NeoPixelBus-2.6.0/.gitignore diff --git a/lib_basic/NeoPixelBus-2.6.0/COPYING b/lib/lib_basic/NeoPixelBus-2.6.0/COPYING similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/COPYING rename to lib/lib_basic/NeoPixelBus-2.6.0/COPYING diff --git a/lib_basic/NeoPixelBus-2.6.0/ReadMe.md b/lib/lib_basic/NeoPixelBus-2.6.0/ReadMe.md similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/ReadMe.md rename to lib/lib_basic/NeoPixelBus-2.6.0/ReadMe.md diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/DotStarTest/DotStarTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/DotStarTest/DotStarTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/DotStarTest/DotStarTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/DotStarTest/DotStarTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelBrightness/NeoPixelBrightness.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelBrightness/NeoPixelBrightness.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelBrightness/NeoPixelBrightness.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelBrightness/NeoPixelBrightness.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelGamma/NeoPixelGamma.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelGamma/NeoPixelGamma.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelGamma/NeoPixelGamma.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelGamma/NeoPixelGamma.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelTest/NeoPixelTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelTest/NeoPixelTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelTest/NeoPixelTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/NeoPixelTest/NeoPixelTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelCylon/NeoPixelCylon.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelCylon/NeoPixelCylon.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelCylon/NeoPixelCylon.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelCylon/NeoPixelCylon.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/Strings.bmp b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/Strings.bmp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/Strings.bmp rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/Strings.bmp diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/StringsW.bmp b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/StringsW.bmp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/StringsW.bmp rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBitmap/StringsW.bmp diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino b/lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino rename to lib/lib_basic/NeoPixelBus-2.6.0/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/circular.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/circular.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/circular.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/circular.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/cubic.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/cubic.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/cubic.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/cubic.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/different.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/different.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/different.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/different.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/exponential.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/exponential.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/exponential.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/exponential.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/gamma.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/gamma.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/gamma.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/gamma.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/pronounced.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/pronounced.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/pronounced.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/pronounced.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/quadratic.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/quadratic.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/quadratic.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/quadratic.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/quintic.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/quintic.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/quintic.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/quintic.png diff --git a/lib_basic/NeoPixelBus-2.6.0/extras/curves/sinusoidal.png b/lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/sinusoidal.png similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/extras/curves/sinusoidal.png rename to lib/lib_basic/NeoPixelBus-2.6.0/extras/curves/sinusoidal.png diff --git a/lib_basic/NeoPixelBus-2.6.0/keywords.txt b/lib/lib_basic/NeoPixelBus-2.6.0/keywords.txt similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/keywords.txt rename to lib/lib_basic/NeoPixelBus-2.6.0/keywords.txt diff --git a/lib_basic/NeoPixelBus-2.6.0/library.json b/lib/lib_basic/NeoPixelBus-2.6.0/library.json similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/library.json rename to lib/lib_basic/NeoPixelBus-2.6.0/library.json diff --git a/lib_basic/NeoPixelBus-2.6.0/library.properties b/lib/lib_basic/NeoPixelBus-2.6.0/library.properties similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/library.properties rename to lib/lib_basic/NeoPixelBus-2.6.0/library.properties diff --git a/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelAnimator.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelAnimator.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/NeoPixelAnimator.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelAnimator.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBrightnessBus.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBrightnessBus.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBrightnessBus.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBrightnessBus.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBus.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBus.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBus.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelBus.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelSegmentBus.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelSegmentBus.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/NeoPixelSegmentBus.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/NeoPixelSegmentBus.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarColorFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarColorFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarColorFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarColorFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarGenericMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarGenericMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarGenericMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/DotStarGenericMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.c b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.c similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.c rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.c diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Esp32_i2s.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HsbColor.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HslColor.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColor.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNameStrings.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNames.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNames.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNames.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorNames.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorShortNames.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorShortNames.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorShortNames.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/HtmlColorShortNames.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Layouts.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Layouts.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Layouts.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Layouts.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806ColorFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806ColorFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806ColorFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806ColorFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806GenericMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806GenericMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806GenericMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Lpd8806GenericMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoArmMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoArmMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoArmMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoArmMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoAvrMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoAvrMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoAvrMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoAvrMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBitmapFile.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBitmapFile.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBitmapFile.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBitmapFile.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBuffer.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBuffer.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBuffer.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBuffer.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferContext.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferContext.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferContext.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferContext.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferMethods.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferMethods.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferMethods.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoBufferMethods.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoColorFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoColorFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoColorFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoColorFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoDib.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoDib.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoDib.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoDib.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEase.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEase.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEase.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEase.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32I2sMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32I2sMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32I2sMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32I2sMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp32RmtMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266DmaMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266DmaMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266DmaMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266DmaMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEsp8266UartMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEspBitBangMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEspBitBangMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEspBitBangMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoEspBitBangMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoGamma.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoHueBlend.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoHueBlend.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoHueBlend.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoHueBlend.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoMosaic.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoMosaic.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoMosaic.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoMosaic.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoNrf52xMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoNrf52xMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoNrf52xMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoNrf52xMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAnimator.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAnimator.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAnimator.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAnimator.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAvr.c b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAvr.c similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAvr.c rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoPixelAvr.c diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoRingTopology.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoRingTopology.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoRingTopology.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoRingTopology.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSegmentFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSegmentFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSegmentFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSegmentFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSettings.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSettings.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSettings.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSettings.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSpriteSheet.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSpriteSheet.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSpriteSheet.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoSpriteSheet.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTiles.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTiles.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTiles.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTiles.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTm1814ColorFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTm1814ColorFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTm1814ColorFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTm1814ColorFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTopology.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTopology.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTopology.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/NeoTopology.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813ColorFeatures.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813ColorFeatures.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/P9813ColorFeatures.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813ColorFeatures.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813GenericMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813GenericMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/P9813GenericMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/P9813GenericMethod.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbColor.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/RgbwColor.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.cpp b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.cpp similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.cpp rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.cpp diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/SegmentDigit.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImple.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImple.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImple.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImple.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImpleAvr.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImpleAvr.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImpleAvr.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireBitBangImpleAvr.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireSpiImple.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireSpiImple.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireSpiImple.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/TwoWireSpiImple.h diff --git a/lib_basic/NeoPixelBus-2.6.0/src/internal/Ws2801GenericMethod.h b/lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Ws2801GenericMethod.h similarity index 100% rename from lib_basic/NeoPixelBus-2.6.0/src/internal/Ws2801GenericMethod.h rename to lib/lib_basic/NeoPixelBus-2.6.0/src/internal/Ws2801GenericMethod.h diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.cpp diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.h b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.h similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.h rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/OneWire.h diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/README.md b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/README.md similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/README.md rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/README.md diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS18x20_Temperature/DS18x20_Temperature.pde diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS2408_Switch/DS2408_Switch.pde diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/examples/DS250x_PROM/DS250x_PROM.pde diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/keywords.txt b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/keywords.txt similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/keywords.txt rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/keywords.txt diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.json b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.json similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/library.json rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.json diff --git a/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties similarity index 100% rename from lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties rename to lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties diff --git a/lib_basic/TasmotaModbus-1.2.0/README.md b/lib/lib_basic/TasmotaModbus-1.2.0/README.md similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/README.md rename to lib/lib_basic/TasmotaModbus-1.2.0/README.md diff --git a/lib_basic/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino b/lib/lib_basic/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino rename to lib/lib_basic/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino diff --git a/lib_basic/TasmotaModbus-1.2.0/keywords.txt b/lib/lib_basic/TasmotaModbus-1.2.0/keywords.txt similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/keywords.txt rename to lib/lib_basic/TasmotaModbus-1.2.0/keywords.txt diff --git a/lib_basic/TasmotaModbus-1.2.0/library.json b/lib/lib_basic/TasmotaModbus-1.2.0/library.json similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/library.json rename to lib/lib_basic/TasmotaModbus-1.2.0/library.json diff --git a/lib_basic/TasmotaModbus-1.2.0/library.properties b/lib/lib_basic/TasmotaModbus-1.2.0/library.properties similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/library.properties rename to lib/lib_basic/TasmotaModbus-1.2.0/library.properties diff --git a/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp rename to lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp diff --git a/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h similarity index 100% rename from lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h rename to lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties diff --git a/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt b/lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt similarity index 100% rename from lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt rename to lib/lib_display/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/ISSUE_TEMPLATE.md b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/ISSUE_TEMPLATE.md rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/ISSUE_TEMPLATE.md diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/PULL_REQUEST_TEMPLATE.md b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/PULL_REQUEST_TEMPLATE.md rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.gitignore b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.gitignore similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.gitignore rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.gitignore diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.travis.yml b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.travis.yml similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.travis.yml rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/.travis.yml diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.cpp b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.cpp similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.cpp rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.cpp diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.h b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.h similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.h rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/Adafruit_ILI9341.h diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/README.md b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/README.md similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/README.md rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/README.md diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/.esp8266.test.skip b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/.esp8266.test.skip similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/.esp8266.test.skip rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/.esp8266.test.skip diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/breakouttouchpaint.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/breakouttouchpaint.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/breakouttouchpaint.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/breakouttouchpaint/breakouttouchpaint.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/.mega2560.test.skip b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/.mega2560.test.skip similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/.mega2560.test.skip rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/.mega2560.test.skip diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/fulltest_featherwing.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/fulltest_featherwing.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/fulltest_featherwing.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/fulltest_featherwing/fulltest_featherwing.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest/graphicstest.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest/graphicstest.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest/graphicstest.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest/graphicstest.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/.mega2560.test.skip b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/.mega2560.test.skip similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/.mega2560.test.skip rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/.mega2560.test.skip diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/graphicstest_featherwing.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/graphicstest_featherwing.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/graphicstest_featherwing.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/graphicstest_featherwing/graphicstest_featherwing.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton/onoffbutton.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton/onoffbutton.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton/onoffbutton.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton/onoffbutton.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/.esp8266.test.skip b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/.esp8266.test.skip similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/.esp8266.test.skip rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/.esp8266.test.skip diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/onoffbutton_breakout.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/onoffbutton_breakout.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/onoffbutton_breakout.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/onoffbutton_breakout/onoffbutton_breakout.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/dragon.h b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/dragon.h similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/dragon.h rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/dragon.h diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/pictureEmbed.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/pictureEmbed.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/pictureEmbed.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/pictureEmbed/pictureEmbed.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/spitftbitmap/spitftbitmap.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/spitftbitmap/spitftbitmap.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/spitftbitmap/spitftbitmap.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/spitftbitmap/spitftbitmap.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint/touchpaint.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint/touchpaint.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint/touchpaint.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint/touchpaint.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/.mega2560.test.skip b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/.mega2560.test.skip similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/.mega2560.test.skip rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/.mega2560.test.skip diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/touchpaint_featherwing.ino b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/touchpaint_featherwing.ino similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/touchpaint_featherwing.ino rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/examples/touchpaint_featherwing/touchpaint_featherwing.ino diff --git a/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/library.properties b/lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/library.properties similarity index 100% rename from lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/library.properties rename to lib/lib_display/Adafruit_ILI9341-1.2.0-Tasmota-1.0/library.properties diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.cpp b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.cpp similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.cpp rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.cpp diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.h b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.h similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.h rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/Adafruit_LEDBackpack.h diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/README.md b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/README.md similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/README.md rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/README.md diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/README.txt b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/README.txt similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/README.txt rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/README.txt diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/HT16K33/HT16K33.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/HT16K33/HT16K33.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/HT16K33/HT16K33.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/HT16K33/HT16K33.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bargraph24/bargraph24.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bargraph24/bargraph24.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/bargraph24/bargraph24.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bargraph24/bargraph24.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bicolor8x8/bicolor8x8.pde b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bicolor8x8/bicolor8x8.pde similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/bicolor8x8/bicolor8x8.pde rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/bicolor8x8/bicolor8x8.pde diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_ds1307/clock_sevenseg_ds1307.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_ds1307/clock_sevenseg_ds1307.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_ds1307/clock_sevenseg_ds1307.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_ds1307/clock_sevenseg_ds1307.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_gps/clock_sevenseg_gps.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_gps/clock_sevenseg_gps.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_gps/clock_sevenseg_gps.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/clock_sevenseg_gps/clock_sevenseg_gps.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix16x8/matrix16x8.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix16x8/matrix16x8.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix16x8/matrix16x8.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix16x8/matrix16x8.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix8x8/matrix8x8.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix8x8/matrix8x8.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix8x8/matrix8x8.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/matrix8x8/matrix8x8.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/minimatrix16x8/minimatrix16x8.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/minimatrix16x8/minimatrix16x8.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/minimatrix16x8/minimatrix16x8.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/minimatrix16x8/minimatrix16x8.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum/quadalphanum.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum/quadalphanum.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum/quadalphanum.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum/quadalphanum.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum_mini/quadalphanum_mini.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum_mini/quadalphanum_mini.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum_mini/quadalphanum_mini.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/quadalphanum_mini/quadalphanum_mini.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/roboface/roboface.pde b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/roboface/roboface.pde similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/roboface/roboface.pde rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/roboface/roboface.pde diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/sevenseg/sevenseg.ino b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/sevenseg/sevenseg.ino similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/sevenseg/sevenseg.ino rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/sevenseg/sevenseg.ino diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavface.pde b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavface.pde similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavface.pde rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavface.pde diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/beware_i.wav b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/beware_i.wav similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/beware_i.wav rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/beware_i.wav diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/ihunger.wav b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/ihunger.wav similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/ihunger.wav rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/ihunger.wav diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/run_cowd.wav b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/run_cowd.wav similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/run_cowd.wav rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/examples/wavface/wavs/run_cowd.wav diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/library.properties b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/library.properties similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/library.properties rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/library.properties diff --git a/lib_display/Adafruit_LED_Backpack-1.1.6/license.txt b/lib/lib_display/Adafruit_LED_Backpack-1.1.6/license.txt similarity index 100% rename from lib_display/Adafruit_LED_Backpack-1.1.6/license.txt rename to lib/lib_display/Adafruit_LED_Backpack-1.1.6/license.txt diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp b/lib/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h b/lib/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/LICENSE.txt b/lib/lib_display/Adafruit_SH1106-gemu-1.0/LICENSE.txt similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/LICENSE.txt rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/LICENSE.txt diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/README.md b/lib/lib_display/Adafruit_SH1106-gemu-1.0/README.md similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/README.md rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/README.md diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino b/lib/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_spi/sh1106_128x64_spi.ino b/lib/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_spi/sh1106_128x64_spi.ino similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_spi/sh1106_128x64_spi.ino rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_spi/sh1106_128x64_spi.ino diff --git a/lib_display/Adafruit_SH1106-gemu-1.0/library.properties b/lib/lib_display/Adafruit_SH1106-gemu-1.0/library.properties similarity index 100% rename from lib_display/Adafruit_SH1106-gemu-1.0/library.properties rename to lib/lib_display/Adafruit_SH1106-gemu-1.0/library.properties diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt diff --git a/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h b/lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h similarity index 100% rename from lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h rename to lib/lib_display/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/README.md b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/README.md similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/README.md rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/README.md diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.h b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.h similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.h rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/SSD1351.h diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.c b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.c similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.c rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.c diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.rgb b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.rgb similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.rgb rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/Tiger.rgb diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/keywords.txt b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/keywords.txt similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/keywords.txt rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/keywords.txt diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/library.properties b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/library.properties similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/library.properties rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/library.properties diff --git a/lib_display/Adafruit_SSD1351-gemu-1.0/spi_register.h b/lib/lib_display/Adafruit_SSD1351-gemu-1.0/spi_register.h similarity index 100% rename from lib_display/Adafruit_SSD1351-gemu-1.0/spi_register.h rename to lib/lib_display/Adafruit_SSD1351-gemu-1.0/spi_register.h diff --git a/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp rename to lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp diff --git a/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h rename to lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h diff --git a/lib_display/Arduino_ST7789-gemu-1.0/README.txt b/lib/lib_display/Arduino_ST7789-gemu-1.0/README.txt similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/README.txt rename to lib/lib_display/Arduino_ST7789-gemu-1.0/README.txt diff --git a/lib_display/Arduino_ST7789-gemu-1.0/examples/graphicstest/graphicstest.ino b/lib/lib_display/Arduino_ST7789-gemu-1.0/examples/graphicstest/graphicstest.ino similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/examples/graphicstest/graphicstest.ino rename to lib/lib_display/Arduino_ST7789-gemu-1.0/examples/graphicstest/graphicstest.ino diff --git a/lib_display/Arduino_ST7789-gemu-1.0/keywords.txt b/lib/lib_display/Arduino_ST7789-gemu-1.0/keywords.txt similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/keywords.txt rename to lib/lib_display/Arduino_ST7789-gemu-1.0/keywords.txt diff --git a/lib_display/Arduino_ST7789-gemu-1.0/library.properties b/lib/lib_display/Arduino_ST7789-gemu-1.0/library.properties similarity index 100% rename from lib_display/Arduino_ST7789-gemu-1.0/library.properties rename to lib/lib_display/Arduino_ST7789-gemu-1.0/library.properties diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/README.md b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/README.md similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/README.md rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/README.md diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/keywords.txt b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/keywords.txt similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/keywords.txt rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/keywords.txt diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/library.properties b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/library.properties similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/library.properties rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/library.properties diff --git a/lib_display/JaretBurkett_ILI9488-gemu-1.0/spi_register.h b/lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/spi_register.h similarity index 100% rename from lib_display/JaretBurkett_ILI9488-gemu-1.0/spi_register.h rename to lib/lib_display/JaretBurkett_ILI9488-gemu-1.0/spi_register.h diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.cpp b/lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.cpp similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.cpp rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.cpp diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.h b/lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.h similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.h rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.h diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.o b/lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.o similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.o rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/LiquidCrystal_I2C.o diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/README.md b/lib/lib_display/LiquidCrystal_I2C-1.1.3/README.md similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/README.md rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/README.md diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/examples/CustomChars/CustomChars.pde b/lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/CustomChars/CustomChars.pde similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/examples/CustomChars/CustomChars.pde rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/CustomChars/CustomChars.pde diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/examples/HelloWorld/HelloWorld.pde b/lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/HelloWorld/HelloWorld.pde similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/examples/HelloWorld/HelloWorld.pde rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/HelloWorld/HelloWorld.pde diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/examples/SerialDisplay/SerialDisplay.pde b/lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/SerialDisplay/SerialDisplay.pde similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/examples/SerialDisplay/SerialDisplay.pde rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/examples/SerialDisplay/SerialDisplay.pde diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/keywords.txt b/lib/lib_display/LiquidCrystal_I2C-1.1.3/keywords.txt similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/keywords.txt rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/keywords.txt diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/library.json b/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/library.json rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json diff --git a/lib_display/LiquidCrystal_I2C-1.1.3/library.properties b/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.properties similarity index 100% rename from lib_display/LiquidCrystal_I2C-1.1.3/library.properties rename to lib/lib_display/LiquidCrystal_I2C-1.1.3/library.properties diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.cpp b/lib/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.cpp similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/RA8876.cpp rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.cpp diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.h b/lib/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.h similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/RA8876.h rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/RA8876.h diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/README.md b/lib/lib_display/Xlatb_RA8876-gemu-1.0/README.md similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/README.md rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/README.md diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/keywords.txt b/lib/lib_display/Xlatb_RA8876-gemu-1.0/keywords.txt similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/keywords.txt rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/keywords.txt diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/library.properties b/lib/lib_display/Xlatb_RA8876-gemu-1.0/library.properties similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/library.properties rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/library.properties diff --git a/lib_display/Xlatb_RA8876-gemu-1.0/spi_register.h b/lib/lib_display/Xlatb_RA8876-gemu-1.0/spi_register.h similarity index 100% rename from lib_display/Xlatb_RA8876-gemu-1.0/spi_register.h rename to lib/lib_display/Xlatb_RA8876-gemu-1.0/spi_register.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Makefile b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Makefile similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Makefile rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/Makefile diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/README.md b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/README.md similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/README.md rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/README.md diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp diff --git a/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h similarity index 100% rename from lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h rename to lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h diff --git a/lib_div/A4988_Stepper/README.adoc b/lib/lib_div/A4988_Stepper/README.adoc similarity index 100% rename from lib_div/A4988_Stepper/README.adoc rename to lib/lib_div/A4988_Stepper/README.adoc diff --git a/lib_div/A4988_Stepper/keywords.txt b/lib/lib_div/A4988_Stepper/keywords.txt similarity index 100% rename from lib_div/A4988_Stepper/keywords.txt rename to lib/lib_div/A4988_Stepper/keywords.txt diff --git a/lib_div/A4988_Stepper/library.properties b/lib/lib_div/A4988_Stepper/library.properties similarity index 100% rename from lib_div/A4988_Stepper/library.properties rename to lib/lib_div/A4988_Stepper/library.properties diff --git a/lib_div/A4988_Stepper/src/A4988_Stepper.cpp b/lib/lib_div/A4988_Stepper/src/A4988_Stepper.cpp similarity index 100% rename from lib_div/A4988_Stepper/src/A4988_Stepper.cpp rename to lib/lib_div/A4988_Stepper/src/A4988_Stepper.cpp diff --git a/lib_div/A4988_Stepper/src/A4988_Stepper.h b/lib/lib_div/A4988_Stepper/src/A4988_Stepper.h similarity index 100% rename from lib_div/A4988_Stepper/src/A4988_Stepper.h rename to lib/lib_div/A4988_Stepper/src/A4988_Stepper.h diff --git a/lib_div/ArduinoNTPd/NTPPacket.cpp b/lib/lib_div/ArduinoNTPd/NTPPacket.cpp similarity index 100% rename from lib_div/ArduinoNTPd/NTPPacket.cpp rename to lib/lib_div/ArduinoNTPd/NTPPacket.cpp diff --git a/lib_div/ArduinoNTPd/NTPPacket.h b/lib/lib_div/ArduinoNTPd/NTPPacket.h similarity index 100% rename from lib_div/ArduinoNTPd/NTPPacket.h rename to lib/lib_div/ArduinoNTPd/NTPPacket.h diff --git a/lib_div/ArduinoNTPd/NTPServer.cpp b/lib/lib_div/ArduinoNTPd/NTPServer.cpp similarity index 100% rename from lib_div/ArduinoNTPd/NTPServer.cpp rename to lib/lib_div/ArduinoNTPd/NTPServer.cpp diff --git a/lib_div/ArduinoNTPd/NTPServer.h b/lib/lib_div/ArduinoNTPd/NTPServer.h similarity index 100% rename from lib_div/ArduinoNTPd/NTPServer.h rename to lib/lib_div/ArduinoNTPd/NTPServer.h diff --git a/lib_div/ArduinoNTPd/library.properties b/lib/lib_div/ArduinoNTPd/library.properties similarity index 100% rename from lib_div/ArduinoNTPd/library.properties rename to lib/lib_div/ArduinoNTPd/library.properties diff --git a/lib_div/LibTeleinfo/README.md b/lib/lib_div/LibTeleinfo/README.md similarity index 100% rename from lib_div/LibTeleinfo/README.md rename to lib/lib_div/LibTeleinfo/README.md diff --git a/lib_div/LibTeleinfo/library.json b/lib/lib_div/LibTeleinfo/library.json similarity index 100% rename from lib_div/LibTeleinfo/library.json rename to lib/lib_div/LibTeleinfo/library.json diff --git a/lib_div/LibTeleinfo/library.properties b/lib/lib_div/LibTeleinfo/library.properties similarity index 100% rename from lib_div/LibTeleinfo/library.properties rename to lib/lib_div/LibTeleinfo/library.properties diff --git a/lib_div/LibTeleinfo/src/LibTeleinfo.cpp b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp similarity index 100% rename from lib_div/LibTeleinfo/src/LibTeleinfo.cpp rename to lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp diff --git a/lib_div/LibTeleinfo/src/LibTeleinfo.h b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.h similarity index 100% rename from lib_div/LibTeleinfo/src/LibTeleinfo.h rename to lib/lib_div/LibTeleinfo/src/LibTeleinfo.h diff --git a/lib_div/NewPing-1.9.1/README.md b/lib/lib_div/NewPing-1.9.1/README.md similarity index 100% rename from lib_div/NewPing-1.9.1/README.md rename to lib/lib_div/NewPing-1.9.1/README.md diff --git a/lib_div/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde b/lib/lib_div/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde rename to lib/lib_div/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde diff --git a/lib_div/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde b/lib/lib_div/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde rename to lib/lib_div/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde diff --git a/lib_div/NewPing-1.9.1/examples/NewPingEventTimer/NewPingEventTimer.pde b/lib/lib_div/NewPing-1.9.1/examples/NewPingEventTimer/NewPingEventTimer.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/NewPingEventTimer/NewPingEventTimer.pde rename to lib/lib_div/NewPing-1.9.1/examples/NewPingEventTimer/NewPingEventTimer.pde diff --git a/lib_div/NewPing-1.9.1/examples/NewPingExample/NewPingExample.pde b/lib/lib_div/NewPing-1.9.1/examples/NewPingExample/NewPingExample.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/NewPingExample/NewPingExample.pde rename to lib/lib_div/NewPing-1.9.1/examples/NewPingExample/NewPingExample.pde diff --git a/lib_div/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde b/lib/lib_div/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde rename to lib/lib_div/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde diff --git a/lib_div/NewPing-1.9.1/examples/TimerExample/TimerExample.pde b/lib/lib_div/NewPing-1.9.1/examples/TimerExample/TimerExample.pde similarity index 100% rename from lib_div/NewPing-1.9.1/examples/TimerExample/TimerExample.pde rename to lib/lib_div/NewPing-1.9.1/examples/TimerExample/TimerExample.pde diff --git a/lib_div/NewPing-1.9.1/keywords.txt b/lib/lib_div/NewPing-1.9.1/keywords.txt similarity index 100% rename from lib_div/NewPing-1.9.1/keywords.txt rename to lib/lib_div/NewPing-1.9.1/keywords.txt diff --git a/lib_div/NewPing-1.9.1/library.properties b/lib/lib_div/NewPing-1.9.1/library.properties similarity index 100% rename from lib_div/NewPing-1.9.1/library.properties rename to lib/lib_div/NewPing-1.9.1/library.properties diff --git a/lib_div/NewPing-1.9.1/src/NewPing.cpp b/lib/lib_div/NewPing-1.9.1/src/NewPing.cpp similarity index 100% rename from lib_div/NewPing-1.9.1/src/NewPing.cpp rename to lib/lib_div/NewPing-1.9.1/src/NewPing.cpp diff --git a/lib_div/NewPing-1.9.1/src/NewPing.h b/lib/lib_div/NewPing-1.9.1/src/NewPing.h similarity index 100% rename from lib_div/NewPing-1.9.1/src/NewPing.h rename to lib/lib_div/NewPing-1.9.1/src/NewPing.h diff --git a/lib_div/OpenTherm-0.9.0/LICENSE b/lib/lib_div/OpenTherm-0.9.0/LICENSE similarity index 100% rename from lib_div/OpenTherm-0.9.0/LICENSE rename to lib/lib_div/OpenTherm-0.9.0/LICENSE diff --git a/lib_div/OpenTherm-0.9.0/README.md b/lib/lib_div/OpenTherm-0.9.0/README.md similarity index 100% rename from lib_div/OpenTherm-0.9.0/README.md rename to lib/lib_div/OpenTherm-0.9.0/README.md diff --git a/lib_div/OpenTherm-0.9.0/keywords.txt b/lib/lib_div/OpenTherm-0.9.0/keywords.txt similarity index 100% rename from lib_div/OpenTherm-0.9.0/keywords.txt rename to lib/lib_div/OpenTherm-0.9.0/keywords.txt diff --git a/lib_div/OpenTherm-0.9.0/library.properties b/lib/lib_div/OpenTherm-0.9.0/library.properties similarity index 100% rename from lib_div/OpenTherm-0.9.0/library.properties rename to lib/lib_div/OpenTherm-0.9.0/library.properties diff --git a/lib_div/OpenTherm-0.9.0/src/OpenTherm.cpp b/lib/lib_div/OpenTherm-0.9.0/src/OpenTherm.cpp similarity index 100% rename from lib_div/OpenTherm-0.9.0/src/OpenTherm.cpp rename to lib/lib_div/OpenTherm-0.9.0/src/OpenTherm.cpp diff --git a/lib_div/OpenTherm-0.9.0/src/OpenTherm.h b/lib/lib_div/OpenTherm-0.9.0/src/OpenTherm.h similarity index 100% rename from lib_div/OpenTherm-0.9.0/src/OpenTherm.h rename to lib/lib_div/OpenTherm-0.9.0/src/OpenTherm.h diff --git a/lib_div/esp-knx-ip-0.5.2/LICENSE b/lib/lib_div/esp-knx-ip-0.5.2/LICENSE similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/LICENSE rename to lib/lib_div/esp-knx-ip-0.5.2/LICENSE diff --git a/lib_div/esp-knx-ip-0.5.2/README.md b/lib/lib_div/esp-knx-ip-0.5.2/README.md similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/README.md rename to lib/lib_div/esp-knx-ip-0.5.2/README.md diff --git a/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino b/lib/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino rename to lib/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino diff --git a/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino b/lib/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino rename to lib/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino diff --git a/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino b/lib/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino rename to lib/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino diff --git a/lib_div/esp-knx-ip-0.5.2/keywords.txt b/lib/lib_div/esp-knx-ip-0.5.2/keywords.txt similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/keywords.txt rename to lib/lib_div/esp-knx-ip-0.5.2/keywords.txt diff --git a/lib_div/esp-knx-ip-0.5.2/library.json b/lib/lib_div/esp-knx-ip-0.5.2/library.json similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/library.json rename to lib/lib_div/esp-knx-ip-0.5.2/library.json diff --git a/lib_div/esp-knx-ip-0.5.2/library.properties.off b/lib/lib_div/esp-knx-ip-0.5.2/library.properties.off similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/library.properties.off rename to lib/lib_div/esp-knx-ip-0.5.2/library.properties.off diff --git a/lib_div/esp-knx-ip-0.5.2/src/DPT.h b/lib/lib_div/esp-knx-ip-0.5.2/src/DPT.h similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/DPT.h rename to lib/lib_div/esp-knx-ip-0.5.2/src/DPT.h diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h similarity index 100% rename from lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h rename to lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h diff --git a/lib_i2c/Adafruit_BusIO/.travis.yml b/lib/lib_i2c/Adafruit_BusIO/.travis.yml similarity index 100% rename from lib_i2c/Adafruit_BusIO/.travis.yml rename to lib/lib_i2c/Adafruit_BusIO/.travis.yml diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.cpp b/lib/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.cpp similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.cpp rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.cpp diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.h b/lib/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.h similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.h rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_BusIO_Register.h diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.cpp b/lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.cpp similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.cpp rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.cpp diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.h b/lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.h similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.h rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CDevice.h diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_I2CRegister.h b/lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CRegister.h similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_I2CRegister.h rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_I2CRegister.h diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.cpp b/lib/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.cpp similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.cpp rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.cpp diff --git a/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.h b/lib/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.h similarity index 100% rename from lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.h rename to lib/lib_i2c/Adafruit_BusIO/Adafruit_SPIDevice.h diff --git a/lib_i2c/Adafruit_BusIO/LICENSE b/lib/lib_i2c/Adafruit_BusIO/LICENSE similarity index 100% rename from lib_i2c/Adafruit_BusIO/LICENSE rename to lib/lib_i2c/Adafruit_BusIO/LICENSE diff --git a/lib_i2c/Adafruit_BusIO/README.md b/lib/lib_i2c/Adafruit_BusIO/README.md similarity index 100% rename from lib_i2c/Adafruit_BusIO/README.md rename to lib/lib_i2c/Adafruit_BusIO/README.md diff --git a/lib_i2c/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino b/lib/lib_i2c/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/i2c_address_detect/i2c_address_detect.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino b/lib/lib_i2c/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/i2c_readwrite/i2c_readwrite.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino b/lib/lib_i2c/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/i2c_registers/i2c_registers.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino b/lib/lib_i2c/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/i2corspi_register/i2corspi_register.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino b/lib/lib_i2c/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/spi_modetest/spi_modetest.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino b/lib/lib_i2c/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/spi_readwrite/spi_readwrite.ino diff --git a/lib_i2c/Adafruit_BusIO/examples/spi_registers/spi_registers.ino b/lib/lib_i2c/Adafruit_BusIO/examples/spi_registers/spi_registers.ino similarity index 100% rename from lib_i2c/Adafruit_BusIO/examples/spi_registers/spi_registers.ino rename to lib/lib_i2c/Adafruit_BusIO/examples/spi_registers/spi_registers.ino diff --git a/lib_i2c/Adafruit_BusIO/library.properties b/lib/lib_i2c/Adafruit_BusIO/library.properties similarity index 100% rename from lib_i2c/Adafruit_BusIO/library.properties rename to lib/lib_i2c/Adafruit_BusIO/library.properties diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/.travis.yml b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/.travis.yml similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/.travis.yml rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/.travis.yml diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.cpp b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.cpp similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.cpp rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.cpp diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.h b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.h similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.h rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/Adafruit_CCS811.h diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/LICENSE b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/LICENSE similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/LICENSE rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/LICENSE diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/README.md b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/README.md similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/README.md rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/README.md diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_OLED_Demo/CCS811_OLED_Demo.ino b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_OLED_Demo/CCS811_OLED_Demo.ino similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_OLED_Demo/CCS811_OLED_Demo.ino rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_OLED_Demo/CCS811_OLED_Demo.ino diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_test/CCS811_test.ino b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_test/CCS811_test.ino similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_test/CCS811_test.ino rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/examples/CCS811_test/CCS811_test.ino diff --git a/lib_i2c/Adafruit_CCS811-1.0.0.14/library.properties b/lib/lib_i2c/Adafruit_CCS811-1.0.0.14/library.properties similarity index 100% rename from lib_i2c/Adafruit_CCS811-1.0.0.14/library.properties rename to lib/lib_i2c/Adafruit_CCS811-1.0.0.14/library.properties diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.h b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.h similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.h rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.h diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.md b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.md similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.md rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.md diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.txt b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.txt similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.txt rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/README.txt diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino diff --git a/lib_i2c/Adafruit_MAX31865-1.1.0-custom/library.properties b/lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/library.properties similarity index 100% rename from lib_i2c/Adafruit_MAX31865-1.1.0-custom/library.properties rename to lib/lib_i2c/Adafruit_MAX31865-1.1.0-custom/library.properties diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/ISSUE_TEMPLATE.md diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/.github/workflows/githubci.yml diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/.gitignore b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/.gitignore similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/.gitignore rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/.gitignore diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.cpp diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/Adafruit_MCP9808.h diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/README.md b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/README.md similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/README.md rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/README.md diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/assets/board.jpg b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/assets/board.jpg similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/assets/board.jpg rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/assets/board.jpg diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/code-of-conduct.md b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/code-of-conduct.md similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/code-of-conduct.md rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/code-of-conduct.md diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/examples/mcp9808test/mcp9808test.ino b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/examples/mcp9808test/mcp9808test.ino similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/examples/mcp9808test/mcp9808test.ino rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/examples/mcp9808test/mcp9808test.ino diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/library.properties b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/library.properties similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/library.properties rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/library.properties diff --git a/lib_i2c/Adafruit_MCP9808_Tasmota/license.txt b/lib/lib_i2c/Adafruit_MCP9808_Tasmota/license.txt similarity index 100% rename from lib_i2c/Adafruit_MCP9808_Tasmota/license.txt rename to lib/lib_i2c/Adafruit_MCP9808_Tasmota/license.txt diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp b/lib/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h b/lib/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/README.md b/lib/lib_i2c/Adafruit_SGP30-1.2.0/README.md similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/README.md rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/README.md diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino b/lib/lib_i2c/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/library.properties b/lib/lib_i2c/Adafruit_SGP30-1.2.0/library.properties similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/library.properties rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/library.properties diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/license.txt b/lib/lib_i2c/Adafruit_SGP30-1.2.0/license.txt similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/license.txt rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/license.txt diff --git a/lib_i2c/Adafruit_SGP30-1.2.0/travis.yml b/lib/lib_i2c/Adafruit_SGP30-1.2.0/travis.yml similarity index 100% rename from lib_i2c/Adafruit_SGP30-1.2.0/travis.yml rename to lib/lib_i2c/Adafruit_SGP30-1.2.0/travis.yml diff --git a/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp b/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp similarity index 100% rename from lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp rename to lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp diff --git a/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.h b/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.h similarity index 100% rename from lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.h rename to lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.h diff --git a/lib_i2c/Adafruit_TSL2591_Library/library.properties b/lib/lib_i2c/Adafruit_TSL2591_Library/library.properties similarity index 100% rename from lib_i2c/Adafruit_TSL2591_Library/library.properties rename to lib/lib_i2c/Adafruit_TSL2591_Library/library.properties diff --git a/lib_i2c/Adafruit_VEML7700/.github/ISSUE_TEMPLATE.md b/lib/lib_i2c/Adafruit_VEML7700/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib_i2c/Adafruit_VEML7700/.github/ISSUE_TEMPLATE.md rename to lib/lib_i2c/Adafruit_VEML7700/.github/ISSUE_TEMPLATE.md diff --git a/lib_i2c/Adafruit_VEML7700/.github/PULL_REQUEST_TEMPLATE.md b/lib/lib_i2c/Adafruit_VEML7700/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib_i2c/Adafruit_VEML7700/.github/PULL_REQUEST_TEMPLATE.md rename to lib/lib_i2c/Adafruit_VEML7700/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib_i2c/Adafruit_VEML7700/.gitignore b/lib/lib_i2c/Adafruit_VEML7700/.gitignore similarity index 100% rename from lib_i2c/Adafruit_VEML7700/.gitignore rename to lib/lib_i2c/Adafruit_VEML7700/.gitignore diff --git a/lib_i2c/Adafruit_VEML7700/.travis.yml b/lib/lib_i2c/Adafruit_VEML7700/.travis.yml similarity index 100% rename from lib_i2c/Adafruit_VEML7700/.travis.yml rename to lib/lib_i2c/Adafruit_VEML7700/.travis.yml diff --git a/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.cpp b/lib/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.cpp similarity index 100% rename from lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.cpp rename to lib/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.cpp diff --git a/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.h b/lib/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.h similarity index 100% rename from lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.h rename to lib/lib_i2c/Adafruit_VEML7700/Adafruit_VEML7700.h diff --git a/lib_i2c/Adafruit_VEML7700/README.md b/lib/lib_i2c/Adafruit_VEML7700/README.md similarity index 100% rename from lib_i2c/Adafruit_VEML7700/README.md rename to lib/lib_i2c/Adafruit_VEML7700/README.md diff --git a/lib_i2c/Adafruit_VEML7700/examples/veml7700_test/veml7700_test.ino b/lib/lib_i2c/Adafruit_VEML7700/examples/veml7700_test/veml7700_test.ino similarity index 100% rename from lib_i2c/Adafruit_VEML7700/examples/veml7700_test/veml7700_test.ino rename to lib/lib_i2c/Adafruit_VEML7700/examples/veml7700_test/veml7700_test.ino diff --git a/lib_i2c/Adafruit_VEML7700/library.properties b/lib/lib_i2c/Adafruit_VEML7700/library.properties similarity index 100% rename from lib_i2c/Adafruit_VEML7700/library.properties rename to lib/lib_i2c/Adafruit_VEML7700/library.properties diff --git a/lib_i2c/Adafruit_VEML7700/license.txt b/lib/lib_i2c/Adafruit_VEML7700/license.txt similarity index 100% rename from lib_i2c/Adafruit_VEML7700/license.txt rename to lib/lib_i2c/Adafruit_VEML7700/license.txt diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/LICENSE b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/LICENSE similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/LICENSE rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/LICENSE diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/README.md b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/README.md similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/README.md rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/README.md diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.c b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.c similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.c rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.c diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.h b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.h similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.h rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/Self test/bme680_selftest.h diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.c b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.c similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/bme680.c rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.c diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.h b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.h similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/bme680.h rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680.h diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/bme680_defs.h b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680_defs.h similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/bme680_defs.h rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/bme680_defs.h diff --git a/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties similarity index 100% rename from lib_i2c/BME680_driver-bme680_v3.5.9/library.properties rename to lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties diff --git a/lib_i2c/FT5206_Library/.gitignore b/lib/lib_i2c/FT5206_Library/.gitignore similarity index 100% rename from lib_i2c/FT5206_Library/.gitignore rename to lib/lib_i2c/FT5206_Library/.gitignore diff --git a/lib_i2c/FT5206_Library/LICENSE b/lib/lib_i2c/FT5206_Library/LICENSE similarity index 100% rename from lib_i2c/FT5206_Library/LICENSE rename to lib/lib_i2c/FT5206_Library/LICENSE diff --git a/lib_i2c/FT5206_Library/README.md b/lib/lib_i2c/FT5206_Library/README.md similarity index 100% rename from lib_i2c/FT5206_Library/README.md rename to lib/lib_i2c/FT5206_Library/README.md diff --git a/lib_i2c/FT5206_Library/keywords.txt b/lib/lib_i2c/FT5206_Library/keywords.txt similarity index 100% rename from lib_i2c/FT5206_Library/keywords.txt rename to lib/lib_i2c/FT5206_Library/keywords.txt diff --git a/lib_i2c/FT5206_Library/library.properties b/lib/lib_i2c/FT5206_Library/library.properties similarity index 100% rename from lib_i2c/FT5206_Library/library.properties rename to lib/lib_i2c/FT5206_Library/library.properties diff --git a/lib_i2c/FT5206_Library/src/FT5206.cpp b/lib/lib_i2c/FT5206_Library/src/FT5206.cpp similarity index 100% rename from lib_i2c/FT5206_Library/src/FT5206.cpp rename to lib/lib_i2c/FT5206_Library/src/FT5206.cpp diff --git a/lib_i2c/FT5206_Library/src/FT5206.h b/lib/lib_i2c/FT5206_Library/src/FT5206.h similarity index 100% rename from lib_i2c/FT5206_Library/src/FT5206.h rename to lib/lib_i2c/FT5206_Library/src/FT5206.h diff --git a/lib_i2c/FrogmoreScd30/FrogmoreScd30.cpp b/lib/lib_i2c/FrogmoreScd30/FrogmoreScd30.cpp similarity index 100% rename from lib_i2c/FrogmoreScd30/FrogmoreScd30.cpp rename to lib/lib_i2c/FrogmoreScd30/FrogmoreScd30.cpp diff --git a/lib_i2c/FrogmoreScd30/FrogmoreScd30.h b/lib/lib_i2c/FrogmoreScd30/FrogmoreScd30.h similarity index 100% rename from lib_i2c/FrogmoreScd30/FrogmoreScd30.h rename to lib/lib_i2c/FrogmoreScd30/FrogmoreScd30.h diff --git a/lib_i2c/FrogmoreScd30/library.properties b/lib/lib_i2c/FrogmoreScd30/library.properties similarity index 100% rename from lib_i2c/FrogmoreScd30/library.properties rename to lib/lib_i2c/FrogmoreScd30/library.properties diff --git a/lib_i2c/HPMA115S0/LICENSE.md b/lib/lib_i2c/HPMA115S0/LICENSE.md similarity index 100% rename from lib_i2c/HPMA115S0/LICENSE.md rename to lib/lib_i2c/HPMA115S0/LICENSE.md diff --git a/lib_i2c/HPMA115S0/README.md b/lib/lib_i2c/HPMA115S0/README.md similarity index 100% rename from lib_i2c/HPMA115S0/README.md rename to lib/lib_i2c/HPMA115S0/README.md diff --git a/lib_i2c/HPMA115S0/example/example.ino b/lib/lib_i2c/HPMA115S0/example/example.ino similarity index 100% rename from lib_i2c/HPMA115S0/example/example.ino rename to lib/lib_i2c/HPMA115S0/example/example.ino diff --git a/lib_i2c/HPMA115S0/library.properties b/lib/lib_i2c/HPMA115S0/library.properties similarity index 100% rename from lib_i2c/HPMA115S0/library.properties rename to lib/lib_i2c/HPMA115S0/library.properties diff --git a/lib_i2c/HPMA115S0/src/hpma115S0.cpp b/lib/lib_i2c/HPMA115S0/src/hpma115S0.cpp similarity index 100% rename from lib_i2c/HPMA115S0/src/hpma115S0.cpp rename to lib/lib_i2c/HPMA115S0/src/hpma115S0.cpp diff --git a/lib_i2c/HPMA115S0/src/hpma115S0.h b/lib/lib_i2c/HPMA115S0/src/hpma115S0.h similarity index 100% rename from lib_i2c/HPMA115S0/src/hpma115S0.h rename to lib/lib_i2c/HPMA115S0/src/hpma115S0.h diff --git a/lib_i2c/I2Cdevlib-Core/.library.json b/lib/lib_i2c/I2Cdevlib-Core/.library.json similarity index 100% rename from lib_i2c/I2Cdevlib-Core/.library.json rename to lib/lib_i2c/I2Cdevlib-Core/.library.json diff --git a/lib_i2c/I2Cdevlib-Core/I2Cdev.cpp b/lib/lib_i2c/I2Cdevlib-Core/I2Cdev.cpp similarity index 100% rename from lib_i2c/I2Cdevlib-Core/I2Cdev.cpp rename to lib/lib_i2c/I2Cdevlib-Core/I2Cdev.cpp diff --git a/lib_i2c/I2Cdevlib-Core/I2Cdev.h b/lib/lib_i2c/I2Cdevlib-Core/I2Cdev.h similarity index 100% rename from lib_i2c/I2Cdevlib-Core/I2Cdev.h rename to lib/lib_i2c/I2Cdevlib-Core/I2Cdev.h diff --git a/lib_i2c/I2Cdevlib-Core/keywords.txt b/lib/lib_i2c/I2Cdevlib-Core/keywords.txt similarity index 100% rename from lib_i2c/I2Cdevlib-Core/keywords.txt rename to lib/lib_i2c/I2Cdevlib-Core/keywords.txt diff --git a/lib_i2c/I2Cdevlib-Core/library.json b/lib/lib_i2c/I2Cdevlib-Core/library.json similarity index 100% rename from lib_i2c/I2Cdevlib-Core/library.json rename to lib/lib_i2c/I2Cdevlib-Core/library.json diff --git a/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino b/lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino rename to lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino diff --git a/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde b/lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde rename to lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_DMP6/Processing/MPUTeapot/MPUTeapot.pde diff --git a/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino b/lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino rename to lib/lib_i2c/I2Cdevlib-MPU6050/Examples/MPU6050_raw/MPU6050_raw.ino diff --git a/lib_i2c/I2Cdevlib-MPU6050/MPU6050.cpp b/lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050.cpp similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/MPU6050.cpp rename to lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050.cpp diff --git a/lib_i2c/I2Cdevlib-MPU6050/MPU6050.h b/lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050.h similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/MPU6050.h rename to lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050.h diff --git a/lib_i2c/I2Cdevlib-MPU6050/MPU6050_6Axis_MotionApps20.h b/lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050_6Axis_MotionApps20.h similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/MPU6050_6Axis_MotionApps20.h rename to lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050_6Axis_MotionApps20.h diff --git a/lib_i2c/I2Cdevlib-MPU6050/MPU6050_9Axis_MotionApps41.h b/lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050_9Axis_MotionApps41.h similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/MPU6050_9Axis_MotionApps41.h rename to lib/lib_i2c/I2Cdevlib-MPU6050/MPU6050_9Axis_MotionApps41.h diff --git a/lib_i2c/I2Cdevlib-MPU6050/helper_3dmath.h b/lib/lib_i2c/I2Cdevlib-MPU6050/helper_3dmath.h similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/helper_3dmath.h rename to lib/lib_i2c/I2Cdevlib-MPU6050/helper_3dmath.h diff --git a/lib_i2c/I2Cdevlib-MPU6050/library.properties b/lib/lib_i2c/I2Cdevlib-MPU6050/library.properties similarity index 100% rename from lib_i2c/I2Cdevlib-MPU6050/library.properties rename to lib/lib_i2c/I2Cdevlib-MPU6050/library.properties diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/.gitignore b/lib/lib_i2c/Joba_Tsl2561-2.0.10/.gitignore similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/.gitignore rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/.gitignore diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/.hgignore b/lib/lib_i2c/Joba_Tsl2561-2.0.10/.hgignore similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/.hgignore rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/.hgignore diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/.travis.yml b/lib/lib_i2c/Joba_Tsl2561-2.0.10/.travis.yml similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/.travis.yml rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/.travis.yml diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/COPYING b/lib/lib_i2c/Joba_Tsl2561-2.0.10/COPYING similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/COPYING rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/COPYING diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/COPYING.LESSER b/lib/lib_i2c/Joba_Tsl2561-2.0.10/COPYING.LESSER similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/COPYING.LESSER rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/COPYING.LESSER diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/README b/lib/lib_i2c/Joba_Tsl2561-2.0.10/README similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/README rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/README diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.ini b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.ini similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.ini rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.ini diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.sh b/lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.sh similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.sh rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/examples/platformio.sh diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/lib/readme.txt b/lib/lib_i2c/Joba_Tsl2561-2.0.10/lib/readme.txt similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/lib/readme.txt rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/lib/readme.txt diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/library.json b/lib/lib_i2c/Joba_Tsl2561-2.0.10/library.json similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/library.json rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/library.json diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/library.properties b/lib/lib_i2c/Joba_Tsl2561-2.0.10/library.properties similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/library.properties rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/library.properties diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/platformio.ini b/lib/lib_i2c/Joba_Tsl2561-2.0.10/platformio.ini similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/platformio.ini rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/platformio.ini diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp b/lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.h b/lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.h similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.h rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561.h diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp b/lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp diff --git a/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h b/lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h similarity index 100% rename from lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h rename to lib/lib_i2c/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h diff --git a/lib_i2c/LOLIN_HP303B/README.md b/lib/lib_i2c/LOLIN_HP303B/README.md similarity index 100% rename from lib_i2c/LOLIN_HP303B/README.md rename to lib/lib_i2c/LOLIN_HP303B/README.md diff --git a/lib_i2c/LOLIN_HP303B/examples/i2c_background/i2c_background.ino b/lib/lib_i2c/LOLIN_HP303B/examples/i2c_background/i2c_background.ino similarity index 100% rename from lib_i2c/LOLIN_HP303B/examples/i2c_background/i2c_background.ino rename to lib/lib_i2c/LOLIN_HP303B/examples/i2c_background/i2c_background.ino diff --git a/lib_i2c/LOLIN_HP303B/examples/i2c_command/i2c_command.ino b/lib/lib_i2c/LOLIN_HP303B/examples/i2c_command/i2c_command.ino similarity index 100% rename from lib_i2c/LOLIN_HP303B/examples/i2c_command/i2c_command.ino rename to lib/lib_i2c/LOLIN_HP303B/examples/i2c_command/i2c_command.ino diff --git a/lib_i2c/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino b/lib/lib_i2c/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino similarity index 100% rename from lib_i2c/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino rename to lib/lib_i2c/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino diff --git a/lib_i2c/LOLIN_HP303B/examples/library.properties b/lib/lib_i2c/LOLIN_HP303B/examples/library.properties similarity index 100% rename from lib_i2c/LOLIN_HP303B/examples/library.properties rename to lib/lib_i2c/LOLIN_HP303B/examples/library.properties diff --git a/lib_i2c/LOLIN_HP303B/keywords.txt b/lib/lib_i2c/LOLIN_HP303B/keywords.txt similarity index 100% rename from lib_i2c/LOLIN_HP303B/keywords.txt rename to lib/lib_i2c/LOLIN_HP303B/keywords.txt diff --git a/lib_i2c/LOLIN_HP303B/library.properties b/lib/lib_i2c/LOLIN_HP303B/library.properties similarity index 100% rename from lib_i2c/LOLIN_HP303B/library.properties rename to lib/lib_i2c/LOLIN_HP303B/library.properties diff --git a/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.cpp similarity index 100% rename from lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.cpp rename to lib/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.cpp diff --git a/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.h similarity index 100% rename from lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.h rename to lib/lib_i2c/LOLIN_HP303B/src/LOLIN_HP303B.h diff --git a/lib_i2c/LOLIN_HP303B/src/util/hp303b_consts.h b/lib/lib_i2c/LOLIN_HP303B/src/util/hp303b_consts.h similarity index 100% rename from lib_i2c/LOLIN_HP303B/src/util/hp303b_consts.h rename to lib/lib_i2c/LOLIN_HP303B/src/util/hp303b_consts.h diff --git a/lib_i2c/LinkedList-1.2.3/LICENSE.txt b/lib/lib_i2c/LinkedList-1.2.3/LICENSE.txt similarity index 100% rename from lib_i2c/LinkedList-1.2.3/LICENSE.txt rename to lib/lib_i2c/LinkedList-1.2.3/LICENSE.txt diff --git a/lib_i2c/LinkedList-1.2.3/LinkedList.h b/lib/lib_i2c/LinkedList-1.2.3/LinkedList.h similarity index 100% rename from lib_i2c/LinkedList-1.2.3/LinkedList.h rename to lib/lib_i2c/LinkedList-1.2.3/LinkedList.h diff --git a/lib_i2c/LinkedList-1.2.3/README.md b/lib/lib_i2c/LinkedList-1.2.3/README.md similarity index 100% rename from lib_i2c/LinkedList-1.2.3/README.md rename to lib/lib_i2c/LinkedList-1.2.3/README.md diff --git a/lib_i2c/LinkedList-1.2.3/examples/ClassList/ClassList.pde b/lib/lib_i2c/LinkedList-1.2.3/examples/ClassList/ClassList.pde similarity index 100% rename from lib_i2c/LinkedList-1.2.3/examples/ClassList/ClassList.pde rename to lib/lib_i2c/LinkedList-1.2.3/examples/ClassList/ClassList.pde diff --git a/lib_i2c/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde b/lib/lib_i2c/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde similarity index 100% rename from lib_i2c/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde rename to lib/lib_i2c/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde diff --git a/lib_i2c/LinkedList-1.2.3/keywords.txt b/lib/lib_i2c/LinkedList-1.2.3/keywords.txt similarity index 100% rename from lib_i2c/LinkedList-1.2.3/keywords.txt rename to lib/lib_i2c/LinkedList-1.2.3/keywords.txt diff --git a/lib_i2c/LinkedList-1.2.3/library.json b/lib/lib_i2c/LinkedList-1.2.3/library.json similarity index 100% rename from lib_i2c/LinkedList-1.2.3/library.json rename to lib/lib_i2c/LinkedList-1.2.3/library.json diff --git a/lib_i2c/LinkedList-1.2.3/library.properties b/lib/lib_i2c/LinkedList-1.2.3/library.properties similarity index 100% rename from lib_i2c/LinkedList-1.2.3/library.properties rename to lib/lib_i2c/LinkedList-1.2.3/library.properties diff --git a/lib_i2c/Mutichannel_Gas_Sensor/License.txt b/lib/lib_i2c/Mutichannel_Gas_Sensor/License.txt similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/License.txt rename to lib/lib_i2c/Mutichannel_Gas_Sensor/License.txt diff --git a/lib_i2c/Mutichannel_Gas_Sensor/README.md b/lib/lib_i2c/Mutichannel_Gas_Sensor/README.md similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/README.md rename to lib/lib_i2c/Mutichannel_Gas_Sensor/README.md diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/GetVersion/GetVersion.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/GetVersion/GetVersion.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/GetVersion/GetVersion.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/GetVersion/GetVersion.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/I2C_Address/I2C_Address.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/I2C_Address/I2C_Address.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/I2C_Address/I2C_Address.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/I2C_Address/I2C_Address.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/RawData/RawData.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/RawData/RawData.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/RawData/RawData.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/RawData/RawData.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Grove/ReadSensorValue_Grove.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Grove/ReadSensorValue_Grove.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Grove/ReadSensorValue_Grove.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Grove/ReadSensorValue_Grove.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Xadow/ReadSensorValue_Xadow.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Xadow/ReadSensorValue_Xadow.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Xadow/ReadSensorValue_Xadow.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/ReadSensorValue_Xadow/ReadSensorValue_Xadow.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/UpdateFrimware.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/UpdateFrimware.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/UpdateFrimware.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/UpdateFrimware.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/bootloader_atmega168.h b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/bootloader_atmega168.h similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/bootloader_atmega168.h rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/bootloader_atmega168.h diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/gpl.txt b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/gpl.txt similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/gpl.txt rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/UpdateFrimware/gpl.txt diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/calibration/calibration.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/calibration/calibration.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/calibration/calibration.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/calibration/calibration.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/factory_setting/factory_setting.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/factory_setting/factory_setting.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/factory_setting/factory_setting.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/factory_setting/factory_setting.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/examples/new_firmware/new_firmware.ino b/lib/lib_i2c/Mutichannel_Gas_Sensor/examples/new_firmware/new_firmware.ino similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/examples/new_firmware/new_firmware.ino rename to lib/lib_i2c/Mutichannel_Gas_Sensor/examples/new_firmware/new_firmware.ino diff --git a/lib_i2c/Mutichannel_Gas_Sensor/library.json b/lib/lib_i2c/Mutichannel_Gas_Sensor/library.json similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/library.json rename to lib/lib_i2c/Mutichannel_Gas_Sensor/library.json diff --git a/lib_i2c/Mutichannel_Gas_Sensor/library.properties b/lib/lib_i2c/Mutichannel_Gas_Sensor/library.properties similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/library.properties rename to lib/lib_i2c/Mutichannel_Gas_Sensor/library.properties diff --git a/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.cpp b/lib/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.cpp similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.cpp rename to lib/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.cpp diff --git a/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.h b/lib/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.h similarity index 100% rename from lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.h rename to lib/lib_i2c/Mutichannel_Gas_Sensor/src/MutichannelGasSensor.h diff --git a/lib_i2c/mlx90640-library/MLX90640_API.cpp b/lib/lib_i2c/mlx90640-library/MLX90640_API.cpp similarity index 100% rename from lib_i2c/mlx90640-library/MLX90640_API.cpp rename to lib/lib_i2c/mlx90640-library/MLX90640_API.cpp diff --git a/lib_i2c/mlx90640-library/MLX90640_API.h b/lib/lib_i2c/mlx90640-library/MLX90640_API.h similarity index 100% rename from lib_i2c/mlx90640-library/MLX90640_API.h rename to lib/lib_i2c/mlx90640-library/MLX90640_API.h diff --git a/lib_i2c/mlx90640-library/library.properties b/lib/lib_i2c/mlx90640-library/library.properties similarity index 100% rename from lib_i2c/mlx90640-library/library.properties rename to lib/lib_i2c/mlx90640-library/library.properties diff --git a/lib_i2c/vl53l0x-arduino-1.02/.travis.yml b/lib/lib_i2c/vl53l0x-arduino-1.02/.travis.yml similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/.travis.yml rename to lib/lib_i2c/vl53l0x-arduino-1.02/.travis.yml diff --git a/lib_i2c/vl53l0x-arduino-1.02/LICENSE.txt b/lib/lib_i2c/vl53l0x-arduino-1.02/LICENSE.txt similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/LICENSE.txt rename to lib/lib_i2c/vl53l0x-arduino-1.02/LICENSE.txt diff --git a/lib_i2c/vl53l0x-arduino-1.02/README.md b/lib/lib_i2c/vl53l0x-arduino-1.02/README.md similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/README.md rename to lib/lib_i2c/vl53l0x-arduino-1.02/README.md diff --git a/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.cpp b/lib/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.cpp similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/VL53L0X.cpp rename to lib/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.cpp diff --git a/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.h b/lib/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.h similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/VL53L0X.h rename to lib/lib_i2c/vl53l0x-arduino-1.02/VL53L0X.h diff --git a/lib_i2c/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino b/lib/lib_i2c/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino rename to lib/lib_i2c/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino diff --git a/lib_i2c/vl53l0x-arduino-1.02/examples/Single/Single.ino b/lib/lib_i2c/vl53l0x-arduino-1.02/examples/Single/Single.ino similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/examples/Single/Single.ino rename to lib/lib_i2c/vl53l0x-arduino-1.02/examples/Single/Single.ino diff --git a/lib_i2c/vl53l0x-arduino-1.02/keywords.txt b/lib/lib_i2c/vl53l0x-arduino-1.02/keywords.txt similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/keywords.txt rename to lib/lib_i2c/vl53l0x-arduino-1.02/keywords.txt diff --git a/lib_i2c/vl53l0x-arduino-1.02/library.properties b/lib/lib_i2c/vl53l0x-arduino-1.02/library.properties similarity index 100% rename from lib_i2c/vl53l0x-arduino-1.02/library.properties rename to lib/lib_i2c/vl53l0x-arduino-1.02/library.properties diff --git a/lib_i2c/vl53l1x-arduino-1.01/LICENSE.txt b/lib/lib_i2c/vl53l1x-arduino-1.01/LICENSE.txt similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/LICENSE.txt rename to lib/lib_i2c/vl53l1x-arduino-1.01/LICENSE.txt diff --git a/lib_i2c/vl53l1x-arduino-1.01/README.md b/lib/lib_i2c/vl53l1x-arduino-1.01/README.md similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/README.md rename to lib/lib_i2c/vl53l1x-arduino-1.01/README.md diff --git a/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.cpp b/lib/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.cpp similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/VL53L1X.cpp rename to lib/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.cpp diff --git a/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.h b/lib/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.h similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/VL53L1X.h rename to lib/lib_i2c/vl53l1x-arduino-1.01/VL53L1X.h diff --git a/lib_i2c/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino b/lib/lib_i2c/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino rename to lib/lib_i2c/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino diff --git a/lib_i2c/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino b/lib/lib_i2c/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino rename to lib/lib_i2c/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino diff --git a/lib_i2c/vl53l1x-arduino-1.01/keywords.txt b/lib/lib_i2c/vl53l1x-arduino-1.01/keywords.txt similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/keywords.txt rename to lib/lib_i2c/vl53l1x-arduino-1.01/keywords.txt diff --git a/lib_i2c/vl53l1x-arduino-1.01/library.properties b/lib/lib_i2c/vl53l1x-arduino-1.01/library.properties similarity index 100% rename from lib_i2c/vl53l1x-arduino-1.01/library.properties rename to lib/lib_i2c/vl53l1x-arduino-1.01/library.properties diff --git a/lib_rf/KeeloqLib/README.md b/lib/lib_rf/KeeloqLib/README.md similarity index 100% rename from lib_rf/KeeloqLib/README.md rename to lib/lib_rf/KeeloqLib/README.md diff --git a/lib_rf/KeeloqLib/keywords.txt b/lib/lib_rf/KeeloqLib/keywords.txt similarity index 100% rename from lib_rf/KeeloqLib/keywords.txt rename to lib/lib_rf/KeeloqLib/keywords.txt diff --git a/lib_rf/KeeloqLib/library.properties b/lib/lib_rf/KeeloqLib/library.properties similarity index 100% rename from lib_rf/KeeloqLib/library.properties rename to lib/lib_rf/KeeloqLib/library.properties diff --git a/lib_rf/KeeloqLib/src/KeeloqLib.cpp b/lib/lib_rf/KeeloqLib/src/KeeloqLib.cpp similarity index 100% rename from lib_rf/KeeloqLib/src/KeeloqLib.cpp rename to lib/lib_rf/KeeloqLib/src/KeeloqLib.cpp diff --git a/lib_rf/KeeloqLib/src/KeeloqLib.h b/lib/lib_rf/KeeloqLib/src/KeeloqLib.h similarity index 100% rename from lib_rf/KeeloqLib/src/KeeloqLib.h rename to lib/lib_rf/KeeloqLib/src/KeeloqLib.h diff --git a/lib_rf/KeeloqLib/tests/KeeloqLibTest/KeeloqLibTest.ino b/lib/lib_rf/KeeloqLib/tests/KeeloqLibTest/KeeloqLibTest.ino similarity index 100% rename from lib_rf/KeeloqLib/tests/KeeloqLibTest/KeeloqLibTest.ino rename to lib/lib_rf/KeeloqLib/tests/KeeloqLibTest/KeeloqLibTest.ino diff --git a/lib_rf/RF24/.gitignore b/lib/lib_rf/RF24/.gitignore similarity index 100% rename from lib_rf/RF24/.gitignore rename to lib/lib_rf/RF24/.gitignore diff --git a/lib_rf/RF24/CONTRIBUTING.md b/lib/lib_rf/RF24/CONTRIBUTING.md similarity index 100% rename from lib_rf/RF24/CONTRIBUTING.md rename to lib/lib_rf/RF24/CONTRIBUTING.md diff --git a/lib_rf/RF24/Doxyfile b/lib/lib_rf/RF24/Doxyfile similarity index 100% rename from lib_rf/RF24/Doxyfile rename to lib/lib_rf/RF24/Doxyfile diff --git a/lib_rf/RF24/LICENSE b/lib/lib_rf/RF24/LICENSE similarity index 100% rename from lib_rf/RF24/LICENSE rename to lib/lib_rf/RF24/LICENSE diff --git a/lib_rf/RF24/Makefile b/lib/lib_rf/RF24/Makefile similarity index 100% rename from lib_rf/RF24/Makefile rename to lib/lib_rf/RF24/Makefile diff --git a/lib_rf/RF24/README.md b/lib/lib_rf/RF24/README.md similarity index 100% rename from lib_rf/RF24/README.md rename to lib/lib_rf/RF24/README.md diff --git a/lib_rf/RF24/RF24.cpp b/lib/lib_rf/RF24/RF24.cpp similarity index 100% rename from lib_rf/RF24/RF24.cpp rename to lib/lib_rf/RF24/RF24.cpp diff --git a/lib_rf/RF24/RF24.h b/lib/lib_rf/RF24/RF24.h similarity index 100% rename from lib_rf/RF24/RF24.h rename to lib/lib_rf/RF24/RF24.h diff --git a/lib_rf/RF24/RF24_config.h b/lib/lib_rf/RF24/RF24_config.h similarity index 100% rename from lib_rf/RF24/RF24_config.h rename to lib/lib_rf/RF24/RF24_config.h diff --git a/lib_rf/RF24/configure b/lib/lib_rf/RF24/configure similarity index 100% rename from lib_rf/RF24/configure rename to lib/lib_rf/RF24/configure diff --git a/lib_rf/RF24/doxygen-custom.css b/lib/lib_rf/RF24/doxygen-custom.css similarity index 100% rename from lib_rf/RF24/doxygen-custom.css rename to lib/lib_rf/RF24/doxygen-custom.css diff --git a/lib_rf/RF24/examples/GettingStarted/GettingStarted.ino b/lib/lib_rf/RF24/examples/GettingStarted/GettingStarted.ino similarity index 100% rename from lib_rf/RF24/examples/GettingStarted/GettingStarted.ino rename to lib/lib_rf/RF24/examples/GettingStarted/GettingStarted.ino diff --git a/lib_rf/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/lib/lib_rf/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino similarity index 100% rename from lib_rf/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino rename to lib/lib_rf/RF24/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino diff --git a/lib_rf/RF24/examples/GettingStarted_HandlingData/GettingStarted_HandlingData.ino b/lib/lib_rf/RF24/examples/GettingStarted_HandlingData/GettingStarted_HandlingData.ino similarity index 100% rename from lib_rf/RF24/examples/GettingStarted_HandlingData/GettingStarted_HandlingData.ino rename to lib/lib_rf/RF24/examples/GettingStarted_HandlingData/GettingStarted_HandlingData.ino diff --git a/lib_rf/RF24/examples/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino b/lib/lib_rf/RF24/examples/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino similarity index 100% rename from lib_rf/RF24/examples/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino rename to lib/lib_rf/RF24/examples/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino diff --git a/lib_rf/RF24/examples/Transfer/Transfer.ino b/lib/lib_rf/RF24/examples/Transfer/Transfer.ino similarity index 100% rename from lib_rf/RF24/examples/Transfer/Transfer.ino rename to lib/lib_rf/RF24/examples/Transfer/Transfer.ino diff --git a/lib_rf/RF24/examples/TransferTimeouts/TransferTimeouts.ino b/lib/lib_rf/RF24/examples/TransferTimeouts/TransferTimeouts.ino similarity index 100% rename from lib_rf/RF24/examples/TransferTimeouts/TransferTimeouts.ino rename to lib/lib_rf/RF24/examples/TransferTimeouts/TransferTimeouts.ino diff --git a/lib_rf/RF24/examples/Usage/led_remote/Jamfile b/lib/lib_rf/RF24/examples/Usage/led_remote/Jamfile similarity index 100% rename from lib_rf/RF24/examples/Usage/led_remote/Jamfile rename to lib/lib_rf/RF24/examples/Usage/led_remote/Jamfile diff --git a/lib_rf/RF24/examples/Usage/led_remote/led_remote.pde b/lib/lib_rf/RF24/examples/Usage/led_remote/led_remote.pde similarity index 100% rename from lib_rf/RF24/examples/Usage/led_remote/led_remote.pde rename to lib/lib_rf/RF24/examples/Usage/led_remote/led_remote.pde diff --git a/lib_rf/RF24/examples/Usage/nordic_fob/Jamfile b/lib/lib_rf/RF24/examples/Usage/nordic_fob/Jamfile similarity index 100% rename from lib_rf/RF24/examples/Usage/nordic_fob/Jamfile rename to lib/lib_rf/RF24/examples/Usage/nordic_fob/Jamfile diff --git a/lib_rf/RF24/examples/Usage/nordic_fob/nordic_fob.pde b/lib/lib_rf/RF24/examples/Usage/nordic_fob/nordic_fob.pde similarity index 100% rename from lib_rf/RF24/examples/Usage/nordic_fob/nordic_fob.pde rename to lib/lib_rf/RF24/examples/Usage/nordic_fob/nordic_fob.pde diff --git a/lib_rf/RF24/examples/Usage/pingpair_maple/Jamfile b/lib/lib_rf/RF24/examples/Usage/pingpair_maple/Jamfile similarity index 100% rename from lib_rf/RF24/examples/Usage/pingpair_maple/Jamfile rename to lib/lib_rf/RF24/examples/Usage/pingpair_maple/Jamfile diff --git a/lib_rf/RF24/examples/Usage/pingpair_maple/main.cpp b/lib/lib_rf/RF24/examples/Usage/pingpair_maple/main.cpp similarity index 100% rename from lib_rf/RF24/examples/Usage/pingpair_maple/main.cpp rename to lib/lib_rf/RF24/examples/Usage/pingpair_maple/main.cpp diff --git a/lib_rf/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde b/lib/lib_rf/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde similarity index 100% rename from lib_rf/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde rename to lib/lib_rf/RF24/examples/Usage/pingpair_maple/pingpair_maple.pde diff --git a/lib_rf/RF24/examples/Usage/readme.md b/lib/lib_rf/RF24/examples/Usage/readme.md similarity index 100% rename from lib_rf/RF24/examples/Usage/readme.md rename to lib/lib_rf/RF24/examples/Usage/readme.md diff --git a/lib_rf/RF24/examples/pingpair_ack/pingpair_ack.ino b/lib/lib_rf/RF24/examples/pingpair_ack/pingpair_ack.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_ack/pingpair_ack.ino rename to lib/lib_rf/RF24/examples/pingpair_ack/pingpair_ack.ino diff --git a/lib_rf/RF24/examples/pingpair_dyn/Jamfile b/lib/lib_rf/RF24/examples/pingpair_dyn/Jamfile similarity index 100% rename from lib_rf/RF24/examples/pingpair_dyn/Jamfile rename to lib/lib_rf/RF24/examples/pingpair_dyn/Jamfile diff --git a/lib_rf/RF24/examples/pingpair_dyn/pingpair_dyn.ino b/lib/lib_rf/RF24/examples/pingpair_dyn/pingpair_dyn.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_dyn/pingpair_dyn.ino rename to lib/lib_rf/RF24/examples/pingpair_dyn/pingpair_dyn.ino diff --git a/lib_rf/RF24/examples/pingpair_irq/pingpair_irq.ino b/lib/lib_rf/RF24/examples/pingpair_irq/pingpair_irq.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_irq/pingpair_irq.ino rename to lib/lib_rf/RF24/examples/pingpair_irq/pingpair_irq.ino diff --git a/lib_rf/RF24/examples/pingpair_irq_simple/pingpair_irq_simple.ino b/lib/lib_rf/RF24/examples/pingpair_irq_simple/pingpair_irq_simple.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_irq_simple/pingpair_irq_simple.ino rename to lib/lib_rf/RF24/examples/pingpair_irq_simple/pingpair_irq_simple.ino diff --git a/lib_rf/RF24/examples/pingpair_multi_dyn/Jamfile b/lib/lib_rf/RF24/examples/pingpair_multi_dyn/Jamfile similarity index 100% rename from lib_rf/RF24/examples/pingpair_multi_dyn/Jamfile rename to lib/lib_rf/RF24/examples/pingpair_multi_dyn/Jamfile diff --git a/lib_rf/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.ino b/lib/lib_rf/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.ino rename to lib/lib_rf/RF24/examples/pingpair_multi_dyn/pingpair_multi_dyn.ino diff --git a/lib_rf/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino b/lib/lib_rf/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino similarity index 100% rename from lib_rf/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino rename to lib/lib_rf/RF24/examples/pingpair_sleepy/pingpair_sleepy.ino diff --git a/lib_rf/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino b/lib/lib_rf/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino similarity index 100% rename from lib_rf/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino rename to lib/lib_rf/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino diff --git a/lib_rf/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino b/lib/lib_rf/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino similarity index 100% rename from lib_rf/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino rename to lib/lib_rf/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino diff --git a/lib_rf/RF24/examples/scanner/Jamfile b/lib/lib_rf/RF24/examples/scanner/Jamfile similarity index 100% rename from lib_rf/RF24/examples/scanner/Jamfile rename to lib/lib_rf/RF24/examples/scanner/Jamfile diff --git a/lib_rf/RF24/examples/scanner/scanner.ino b/lib/lib_rf/RF24/examples/scanner/scanner.ino similarity index 100% rename from lib_rf/RF24/examples/scanner/scanner.ino rename to lib/lib_rf/RF24/examples/scanner/scanner.ino diff --git a/lib_rf/RF24/examples/starping/Jamfile b/lib/lib_rf/RF24/examples/starping/Jamfile similarity index 100% rename from lib_rf/RF24/examples/starping/Jamfile rename to lib/lib_rf/RF24/examples/starping/Jamfile diff --git a/lib_rf/RF24/examples/starping/starping.pde b/lib/lib_rf/RF24/examples/starping/starping.pde similarity index 100% rename from lib_rf/RF24/examples/starping/starping.pde rename to lib/lib_rf/RF24/examples/starping/starping.pde diff --git a/lib_rf/RF24/examples_linux/Makefile.examples b/lib/lib_rf/RF24/examples_linux/Makefile.examples similarity index 100% rename from lib_rf/RF24/examples_linux/Makefile.examples rename to lib/lib_rf/RF24/examples_linux/Makefile.examples diff --git a/lib_rf/RF24/examples_linux/extra/Makefile b/lib/lib_rf/RF24/examples_linux/extra/Makefile similarity index 100% rename from lib_rf/RF24/examples_linux/extra/Makefile rename to lib/lib_rf/RF24/examples_linux/extra/Makefile diff --git a/lib_rf/RF24/examples_linux/extra/rpi-hub.cpp b/lib/lib_rf/RF24/examples_linux/extra/rpi-hub.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/extra/rpi-hub.cpp rename to lib/lib_rf/RF24/examples_linux/extra/rpi-hub.cpp diff --git a/lib_rf/RF24/examples_linux/extra/scanner.cpp b/lib/lib_rf/RF24/examples_linux/extra/scanner.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/extra/scanner.cpp rename to lib/lib_rf/RF24/examples_linux/extra/scanner.cpp diff --git a/lib_rf/RF24/examples_linux/gettingstarted.cpp b/lib/lib_rf/RF24/examples_linux/gettingstarted.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/gettingstarted.cpp rename to lib/lib_rf/RF24/examples_linux/gettingstarted.cpp diff --git a/lib_rf/RF24/examples_linux/gettingstarted_call_response.cpp b/lib/lib_rf/RF24/examples_linux/gettingstarted_call_response.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/gettingstarted_call_response.cpp rename to lib/lib_rf/RF24/examples_linux/gettingstarted_call_response.cpp diff --git a/lib_rf/RF24/examples_linux/interrupts/Makefile b/lib/lib_rf/RF24/examples_linux/interrupts/Makefile similarity index 100% rename from lib_rf/RF24/examples_linux/interrupts/Makefile rename to lib/lib_rf/RF24/examples_linux/interrupts/Makefile diff --git a/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp b/lib/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp rename to lib/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp diff --git a/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp b/lib/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp rename to lib/lib_rf/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp diff --git a/lib_rf/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp b/lib/lib_rf/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp rename to lib/lib_rf/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp diff --git a/lib_rf/RF24/examples_linux/interrupts/transfer_interrupt.cpp b/lib/lib_rf/RF24/examples_linux/interrupts/transfer_interrupt.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/interrupts/transfer_interrupt.cpp rename to lib/lib_rf/RF24/examples_linux/interrupts/transfer_interrupt.cpp diff --git a/lib_rf/RF24/examples_linux/pingpair_dyn.cpp b/lib/lib_rf/RF24/examples_linux/pingpair_dyn.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/pingpair_dyn.cpp rename to lib/lib_rf/RF24/examples_linux/pingpair_dyn.cpp diff --git a/lib_rf/RF24/examples_linux/pingpair_dyn.py b/lib/lib_rf/RF24/examples_linux/pingpair_dyn.py similarity index 100% rename from lib_rf/RF24/examples_linux/pingpair_dyn.py rename to lib/lib_rf/RF24/examples_linux/pingpair_dyn.py diff --git a/lib_rf/RF24/examples_linux/readme.md b/lib/lib_rf/RF24/examples_linux/readme.md similarity index 100% rename from lib_rf/RF24/examples_linux/readme.md rename to lib/lib_rf/RF24/examples_linux/readme.md diff --git a/lib_rf/RF24/examples_linux/transfer.cpp b/lib/lib_rf/RF24/examples_linux/transfer.cpp similarity index 100% rename from lib_rf/RF24/examples_linux/transfer.cpp rename to lib/lib_rf/RF24/examples_linux/transfer.cpp diff --git a/lib_rf/RF24/keywords.txt b/lib/lib_rf/RF24/keywords.txt similarity index 100% rename from lib_rf/RF24/keywords.txt rename to lib/lib_rf/RF24/keywords.txt diff --git a/lib_rf/RF24/library.json b/lib/lib_rf/RF24/library.json similarity index 100% rename from lib_rf/RF24/library.json rename to lib/lib_rf/RF24/library.json diff --git a/lib_rf/RF24/library.properties b/lib/lib_rf/RF24/library.properties similarity index 100% rename from lib_rf/RF24/library.properties rename to lib/lib_rf/RF24/library.properties diff --git a/lib_rf/RF24/nRF24L01.h b/lib/lib_rf/RF24/nRF24L01.h similarity index 100% rename from lib_rf/RF24/nRF24L01.h rename to lib/lib_rf/RF24/nRF24L01.h diff --git a/lib_rf/RF24/printf.h b/lib/lib_rf/RF24/printf.h similarity index 100% rename from lib_rf/RF24/printf.h rename to lib/lib_rf/RF24/printf.h diff --git a/lib_rf/RF24/pyRF24/crossunixccompiler.py b/lib/lib_rf/RF24/pyRF24/crossunixccompiler.py similarity index 100% rename from lib_rf/RF24/pyRF24/crossunixccompiler.py rename to lib/lib_rf/RF24/pyRF24/crossunixccompiler.py diff --git a/lib_rf/RF24/pyRF24/pyRF24.cpp b/lib/lib_rf/RF24/pyRF24/pyRF24.cpp similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24.cpp rename to lib/lib_rf/RF24/pyRF24/pyRF24.cpp diff --git a/lib_rf/RF24/pyRF24/pyRF24/crossunixccompiler.py b/lib/lib_rf/RF24/pyRF24/pyRF24/crossunixccompiler.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24/crossunixccompiler.py rename to lib/lib_rf/RF24/pyRF24/pyRF24/crossunixccompiler.py diff --git a/lib_rf/RF24/pyRF24/pyRF24/pyRF24.cpp b/lib/lib_rf/RF24/pyRF24/pyRF24/pyRF24.cpp similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24/pyRF24.cpp rename to lib/lib_rf/RF24/pyRF24/pyRF24/pyRF24.cpp diff --git a/lib_rf/RF24/pyRF24/pyRF24/readme.md b/lib/lib_rf/RF24/pyRF24/pyRF24/readme.md similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24/readme.md rename to lib/lib_rf/RF24/pyRF24/pyRF24/readme.md diff --git a/lib_rf/RF24/pyRF24/pyRF24/setup.py b/lib/lib_rf/RF24/pyRF24/pyRF24/setup.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24/setup.py rename to lib/lib_rf/RF24/pyRF24/pyRF24/setup.py diff --git a/lib_rf/RF24/pyRF24/pyRF24Mesh/example_master.py b/lib/lib_rf/RF24/pyRF24/pyRF24Mesh/example_master.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Mesh/example_master.py rename to lib/lib_rf/RF24/pyRF24/pyRF24Mesh/example_master.py diff --git a/lib_rf/RF24/pyRF24/pyRF24Mesh/pyRF24Mesh.cpp b/lib/lib_rf/RF24/pyRF24/pyRF24Mesh/pyRF24Mesh.cpp similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Mesh/pyRF24Mesh.cpp rename to lib/lib_rf/RF24/pyRF24/pyRF24Mesh/pyRF24Mesh.cpp diff --git a/lib_rf/RF24/pyRF24/pyRF24Mesh/setup.py b/lib/lib_rf/RF24/pyRF24/pyRF24Mesh/setup.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Mesh/setup.py rename to lib/lib_rf/RF24/pyRF24/pyRF24Mesh/setup.py diff --git a/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_rx.py b/lib/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_rx.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_rx.py rename to lib/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_rx.py diff --git a/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_tx.py b/lib/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_tx.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_tx.py rename to lib/lib_rf/RF24/pyRF24/pyRF24Network/examples/helloworld_tx.py diff --git a/lib_rf/RF24/pyRF24/pyRF24Network/pyRF24Network.cpp b/lib/lib_rf/RF24/pyRF24/pyRF24Network/pyRF24Network.cpp similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Network/pyRF24Network.cpp rename to lib/lib_rf/RF24/pyRF24/pyRF24Network/pyRF24Network.cpp diff --git a/lib_rf/RF24/pyRF24/pyRF24Network/setup.py b/lib/lib_rf/RF24/pyRF24/pyRF24Network/setup.py similarity index 100% rename from lib_rf/RF24/pyRF24/pyRF24Network/setup.py rename to lib/lib_rf/RF24/pyRF24/pyRF24Network/setup.py diff --git a/lib_rf/RF24/pyRF24/readme.md b/lib/lib_rf/RF24/pyRF24/readme.md similarity index 100% rename from lib_rf/RF24/pyRF24/readme.md rename to lib/lib_rf/RF24/pyRF24/readme.md diff --git a/lib_rf/RF24/pyRF24/setup.py b/lib/lib_rf/RF24/pyRF24/setup.py similarity index 100% rename from lib_rf/RF24/pyRF24/setup.py rename to lib/lib_rf/RF24/pyRF24/setup.py diff --git a/lib_rf/RF24/tests/README b/lib/lib_rf/RF24/tests/README similarity index 100% rename from lib_rf/RF24/tests/README rename to lib/lib_rf/RF24/tests/README diff --git a/lib_rf/RF24/tests/native/Jamfile b/lib/lib_rf/RF24/tests/native/Jamfile similarity index 100% rename from lib_rf/RF24/tests/native/Jamfile rename to lib/lib_rf/RF24/tests/native/Jamfile diff --git a/lib_rf/RF24/tests/native/pingpair_irq.pde b/lib/lib_rf/RF24/tests/native/pingpair_irq.pde similarity index 100% rename from lib_rf/RF24/tests/native/pingpair_irq.pde rename to lib/lib_rf/RF24/tests/native/pingpair_irq.pde diff --git a/lib_rf/RF24/tests/native/printf.h b/lib/lib_rf/RF24/tests/native/printf.h similarity index 100% rename from lib_rf/RF24/tests/native/printf.h rename to lib/lib_rf/RF24/tests/native/printf.h diff --git a/lib_rf/RF24/tests/pingpair_blocking/Jamfile b/lib/lib_rf/RF24/tests/pingpair_blocking/Jamfile similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/Jamfile rename to lib/lib_rf/RF24/tests/pingpair_blocking/Jamfile diff --git a/lib_rf/RF24/tests/pingpair_blocking/pingpair_blocking.pde b/lib/lib_rf/RF24/tests/pingpair_blocking/pingpair_blocking.pde similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/pingpair_blocking.pde rename to lib/lib_rf/RF24/tests/pingpair_blocking/pingpair_blocking.pde diff --git a/lib_rf/RF24/tests/pingpair_blocking/printf.h b/lib/lib_rf/RF24/tests/pingpair_blocking/printf.h similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/printf.h rename to lib/lib_rf/RF24/tests/pingpair_blocking/printf.h diff --git a/lib_rf/RF24/tests/pingpair_blocking/runtest.py b/lib/lib_rf/RF24/tests/pingpair_blocking/runtest.py similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/runtest.py rename to lib/lib_rf/RF24/tests/pingpair_blocking/runtest.py diff --git a/lib_rf/RF24/tests/pingpair_blocking/runtests.sh b/lib/lib_rf/RF24/tests/pingpair_blocking/runtests.sh similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/runtests.sh rename to lib/lib_rf/RF24/tests/pingpair_blocking/runtests.sh diff --git a/lib_rf/RF24/tests/pingpair_blocking/test.ex b/lib/lib_rf/RF24/tests/pingpair_blocking/test.ex similarity index 100% rename from lib_rf/RF24/tests/pingpair_blocking/test.ex rename to lib/lib_rf/RF24/tests/pingpair_blocking/test.ex diff --git a/lib_rf/RF24/tests/pingpair_test/Jamfile b/lib/lib_rf/RF24/tests/pingpair_test/Jamfile similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/Jamfile rename to lib/lib_rf/RF24/tests/pingpair_test/Jamfile diff --git a/lib_rf/RF24/tests/pingpair_test/pingpair_test.pde b/lib/lib_rf/RF24/tests/pingpair_test/pingpair_test.pde similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/pingpair_test.pde rename to lib/lib_rf/RF24/tests/pingpair_test/pingpair_test.pde diff --git a/lib_rf/RF24/tests/pingpair_test/printf.h b/lib/lib_rf/RF24/tests/pingpair_test/printf.h similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/printf.h rename to lib/lib_rf/RF24/tests/pingpair_test/printf.h diff --git a/lib_rf/RF24/tests/pingpair_test/runtest.py b/lib/lib_rf/RF24/tests/pingpair_test/runtest.py similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/runtest.py rename to lib/lib_rf/RF24/tests/pingpair_test/runtest.py diff --git a/lib_rf/RF24/tests/pingpair_test/runtests.sh b/lib/lib_rf/RF24/tests/pingpair_test/runtests.sh similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/runtests.sh rename to lib/lib_rf/RF24/tests/pingpair_test/runtests.sh diff --git a/lib_rf/RF24/tests/pingpair_test/test.ex b/lib/lib_rf/RF24/tests/pingpair_test/test.ex similarity index 100% rename from lib_rf/RF24/tests/pingpair_test/test.ex rename to lib/lib_rf/RF24/tests/pingpair_test/test.ex diff --git a/lib_rf/RF24/utility/ATTiny/RF24_arch_config.h b/lib/lib_rf/RF24/utility/ATTiny/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/ATTiny/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/ATTiny/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/ATTiny/spi.h b/lib/lib_rf/RF24/utility/ATTiny/spi.h similarity index 100% rename from lib_rf/RF24/utility/ATTiny/spi.h rename to lib/lib_rf/RF24/utility/ATTiny/spi.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/README.md b/lib/lib_rf/RF24/utility/ATXMegaD3/README.md similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/README.md rename to lib/lib_rf/RF24/utility/ATXMegaD3/README.md diff --git a/lib_rf/RF24/utility/ATXMegaD3/RF24_arch_config.h b/lib/lib_rf/RF24/utility/ATXMegaD3/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/compatibility.c b/lib/lib_rf/RF24/utility/ATXMegaD3/compatibility.c similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/compatibility.c rename to lib/lib_rf/RF24/utility/ATXMegaD3/compatibility.c diff --git a/lib_rf/RF24/utility/ATXMegaD3/compatibility.h b/lib/lib_rf/RF24/utility/ATXMegaD3/compatibility.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/compatibility.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/compatibility.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/gpio.cpp b/lib/lib_rf/RF24/utility/ATXMegaD3/gpio.cpp similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/gpio.cpp rename to lib/lib_rf/RF24/utility/ATXMegaD3/gpio.cpp diff --git a/lib_rf/RF24/utility/ATXMegaD3/gpio.h b/lib/lib_rf/RF24/utility/ATXMegaD3/gpio.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/gpio.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/gpio.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.c b/lib/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.c similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/gpio_helper.c rename to lib/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.c diff --git a/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.h b/lib/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/gpio_helper.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/gpio_helper.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/includes.h b/lib/lib_rf/RF24/utility/ATXMegaD3/includes.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/includes.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/includes.h diff --git a/lib_rf/RF24/utility/ATXMegaD3/spi.cpp b/lib/lib_rf/RF24/utility/ATXMegaD3/spi.cpp similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/spi.cpp rename to lib/lib_rf/RF24/utility/ATXMegaD3/spi.cpp diff --git a/lib_rf/RF24/utility/ATXMegaD3/spi.h b/lib/lib_rf/RF24/utility/ATXMegaD3/spi.h similarity index 100% rename from lib_rf/RF24/utility/ATXMegaD3/spi.h rename to lib/lib_rf/RF24/utility/ATXMegaD3/spi.h diff --git a/lib_rf/RF24/utility/Due/RF24_arch_config.h b/lib/lib_rf/RF24/utility/Due/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/Due/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/Due/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/LittleWire/RF24_arch_config.h b/lib/lib_rf/RF24/utility/LittleWire/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/LittleWire/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/LittleWire/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/LittleWire/includes.h b/lib/lib_rf/RF24/utility/LittleWire/includes.h similarity index 100% rename from lib_rf/RF24/utility/LittleWire/includes.h rename to lib/lib_rf/RF24/utility/LittleWire/includes.h diff --git a/lib_rf/RF24/utility/MRAA/RF24_arch_config.h b/lib/lib_rf/RF24/utility/MRAA/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/MRAA/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/MRAA/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/MRAA/compatibility.c b/lib/lib_rf/RF24/utility/MRAA/compatibility.c similarity index 100% rename from lib_rf/RF24/utility/MRAA/compatibility.c rename to lib/lib_rf/RF24/utility/MRAA/compatibility.c diff --git a/lib_rf/RF24/utility/MRAA/compatibility.h b/lib/lib_rf/RF24/utility/MRAA/compatibility.h similarity index 100% rename from lib_rf/RF24/utility/MRAA/compatibility.h rename to lib/lib_rf/RF24/utility/MRAA/compatibility.h diff --git a/lib_rf/RF24/utility/MRAA/gpio.cpp b/lib/lib_rf/RF24/utility/MRAA/gpio.cpp similarity index 100% rename from lib_rf/RF24/utility/MRAA/gpio.cpp rename to lib/lib_rf/RF24/utility/MRAA/gpio.cpp diff --git a/lib_rf/RF24/utility/MRAA/gpio.h b/lib/lib_rf/RF24/utility/MRAA/gpio.h similarity index 100% rename from lib_rf/RF24/utility/MRAA/gpio.h rename to lib/lib_rf/RF24/utility/MRAA/gpio.h diff --git a/lib_rf/RF24/utility/MRAA/includes.h b/lib/lib_rf/RF24/utility/MRAA/includes.h similarity index 100% rename from lib_rf/RF24/utility/MRAA/includes.h rename to lib/lib_rf/RF24/utility/MRAA/includes.h diff --git a/lib_rf/RF24/utility/MRAA/spi.cpp b/lib/lib_rf/RF24/utility/MRAA/spi.cpp similarity index 100% rename from lib_rf/RF24/utility/MRAA/spi.cpp rename to lib/lib_rf/RF24/utility/MRAA/spi.cpp diff --git a/lib_rf/RF24/utility/MRAA/spi.h b/lib/lib_rf/RF24/utility/MRAA/spi.h similarity index 100% rename from lib_rf/RF24/utility/MRAA/spi.h rename to lib/lib_rf/RF24/utility/MRAA/spi.h diff --git a/lib_rf/RF24/utility/RPi/RF24_arch_config.h b/lib/lib_rf/RF24/utility/RPi/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/RPi/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/RPi/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/RPi/bcm2835.c b/lib/lib_rf/RF24/utility/RPi/bcm2835.c similarity index 100% rename from lib_rf/RF24/utility/RPi/bcm2835.c rename to lib/lib_rf/RF24/utility/RPi/bcm2835.c diff --git a/lib_rf/RF24/utility/RPi/bcm2835.h b/lib/lib_rf/RF24/utility/RPi/bcm2835.h similarity index 100% rename from lib_rf/RF24/utility/RPi/bcm2835.h rename to lib/lib_rf/RF24/utility/RPi/bcm2835.h diff --git a/lib_rf/RF24/utility/RPi/includes.h b/lib/lib_rf/RF24/utility/RPi/includes.h similarity index 100% rename from lib_rf/RF24/utility/RPi/includes.h rename to lib/lib_rf/RF24/utility/RPi/includes.h diff --git a/lib_rf/RF24/utility/RPi/interrupt.c b/lib/lib_rf/RF24/utility/RPi/interrupt.c similarity index 100% rename from lib_rf/RF24/utility/RPi/interrupt.c rename to lib/lib_rf/RF24/utility/RPi/interrupt.c diff --git a/lib_rf/RF24/utility/RPi/interrupt.h b/lib/lib_rf/RF24/utility/RPi/interrupt.h similarity index 100% rename from lib_rf/RF24/utility/RPi/interrupt.h rename to lib/lib_rf/RF24/utility/RPi/interrupt.h diff --git a/lib_rf/RF24/utility/RPi/spi.cpp b/lib/lib_rf/RF24/utility/RPi/spi.cpp similarity index 100% rename from lib_rf/RF24/utility/RPi/spi.cpp rename to lib/lib_rf/RF24/utility/RPi/spi.cpp diff --git a/lib_rf/RF24/utility/RPi/spi.h b/lib/lib_rf/RF24/utility/RPi/spi.h similarity index 100% rename from lib_rf/RF24/utility/RPi/spi.h rename to lib/lib_rf/RF24/utility/RPi/spi.h diff --git a/lib_rf/RF24/utility/SPIDEV/RF24_arch_config.h b/lib/lib_rf/RF24/utility/SPIDEV/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/SPIDEV/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/SPIDEV/compatibility.c b/lib/lib_rf/RF24/utility/SPIDEV/compatibility.c similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/compatibility.c rename to lib/lib_rf/RF24/utility/SPIDEV/compatibility.c diff --git a/lib_rf/RF24/utility/SPIDEV/compatibility.h b/lib/lib_rf/RF24/utility/SPIDEV/compatibility.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/compatibility.h rename to lib/lib_rf/RF24/utility/SPIDEV/compatibility.h diff --git a/lib_rf/RF24/utility/SPIDEV/gpio.cpp b/lib/lib_rf/RF24/utility/SPIDEV/gpio.cpp similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/gpio.cpp rename to lib/lib_rf/RF24/utility/SPIDEV/gpio.cpp diff --git a/lib_rf/RF24/utility/SPIDEV/gpio.h b/lib/lib_rf/RF24/utility/SPIDEV/gpio.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/gpio.h rename to lib/lib_rf/RF24/utility/SPIDEV/gpio.h diff --git a/lib_rf/RF24/utility/SPIDEV/includes.h b/lib/lib_rf/RF24/utility/SPIDEV/includes.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/includes.h rename to lib/lib_rf/RF24/utility/SPIDEV/includes.h diff --git a/lib_rf/RF24/utility/SPIDEV/interrupt.c b/lib/lib_rf/RF24/utility/SPIDEV/interrupt.c similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/interrupt.c rename to lib/lib_rf/RF24/utility/SPIDEV/interrupt.c diff --git a/lib_rf/RF24/utility/SPIDEV/interrupt.h b/lib/lib_rf/RF24/utility/SPIDEV/interrupt.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/interrupt.h rename to lib/lib_rf/RF24/utility/SPIDEV/interrupt.h diff --git a/lib_rf/RF24/utility/SPIDEV/spi.cpp b/lib/lib_rf/RF24/utility/SPIDEV/spi.cpp similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/spi.cpp rename to lib/lib_rf/RF24/utility/SPIDEV/spi.cpp diff --git a/lib_rf/RF24/utility/SPIDEV/spi.h b/lib/lib_rf/RF24/utility/SPIDEV/spi.h similarity index 100% rename from lib_rf/RF24/utility/SPIDEV/spi.h rename to lib/lib_rf/RF24/utility/SPIDEV/spi.h diff --git a/lib_rf/RF24/utility/Teensy/RF24_arch_config.h b/lib/lib_rf/RF24/utility/Teensy/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/Teensy/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/Teensy/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/Template/RF24_arch_config.h b/lib/lib_rf/RF24/utility/Template/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/Template/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/Template/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/Template/compatibility.h b/lib/lib_rf/RF24/utility/Template/compatibility.h similarity index 100% rename from lib_rf/RF24/utility/Template/compatibility.h rename to lib/lib_rf/RF24/utility/Template/compatibility.h diff --git a/lib_rf/RF24/utility/Template/gpio.h b/lib/lib_rf/RF24/utility/Template/gpio.h similarity index 100% rename from lib_rf/RF24/utility/Template/gpio.h rename to lib/lib_rf/RF24/utility/Template/gpio.h diff --git a/lib_rf/RF24/utility/Template/includes.h b/lib/lib_rf/RF24/utility/Template/includes.h similarity index 100% rename from lib_rf/RF24/utility/Template/includes.h rename to lib/lib_rf/RF24/utility/Template/includes.h diff --git a/lib_rf/RF24/utility/Template/spi.h b/lib/lib_rf/RF24/utility/Template/spi.h similarity index 100% rename from lib_rf/RF24/utility/Template/spi.h rename to lib/lib_rf/RF24/utility/Template/spi.h diff --git a/lib_rf/RF24/utility/wiringPi/RF24_arch_config.h b/lib/lib_rf/RF24/utility/wiringPi/RF24_arch_config.h similarity index 100% rename from lib_rf/RF24/utility/wiringPi/RF24_arch_config.h rename to lib/lib_rf/RF24/utility/wiringPi/RF24_arch_config.h diff --git a/lib_rf/RF24/utility/wiringPi/includes.h b/lib/lib_rf/RF24/utility/wiringPi/includes.h similarity index 100% rename from lib_rf/RF24/utility/wiringPi/includes.h rename to lib/lib_rf/RF24/utility/wiringPi/includes.h diff --git a/lib_rf/RF24/utility/wiringPi/spi.cpp b/lib/lib_rf/RF24/utility/wiringPi/spi.cpp similarity index 100% rename from lib_rf/RF24/utility/wiringPi/spi.cpp rename to lib/lib_rf/RF24/utility/wiringPi/spi.cpp diff --git a/lib_rf/RF24/utility/wiringPi/spi.h b/lib/lib_rf/RF24/utility/wiringPi/spi.h similarity index 100% rename from lib_rf/RF24/utility/wiringPi/spi.h rename to lib/lib_rf/RF24/utility/wiringPi/spi.h diff --git a/lib_rf/RF24/wikidoc.xslt b/lib/lib_rf/RF24/wikidoc.xslt similarity index 100% rename from lib_rf/RF24/wikidoc.xslt rename to lib/lib_rf/RF24/wikidoc.xslt diff --git a/lib_rf/cc1101/README.md b/lib/lib_rf/cc1101/README.md similarity index 100% rename from lib_rf/cc1101/README.md rename to lib/lib_rf/cc1101/README.md diff --git a/lib_rf/cc1101/cc1101.cpp b/lib/lib_rf/cc1101/cc1101.cpp similarity index 100% rename from lib_rf/cc1101/cc1101.cpp rename to lib/lib_rf/cc1101/cc1101.cpp diff --git a/lib_rf/cc1101/cc1101.h b/lib/lib_rf/cc1101/cc1101.h similarity index 100% rename from lib_rf/cc1101/cc1101.h rename to lib/lib_rf/cc1101/cc1101.h diff --git a/lib_rf/cc1101/cc1101.h.txt b/lib/lib_rf/cc1101/cc1101.h.txt similarity index 100% rename from lib_rf/cc1101/cc1101.h.txt rename to lib/lib_rf/cc1101/cc1101.h.txt diff --git a/lib_rf/cc1101/ccpacket.h b/lib/lib_rf/cc1101/ccpacket.h similarity index 100% rename from lib_rf/cc1101/ccpacket.h rename to lib/lib_rf/cc1101/ccpacket.h diff --git a/lib_rf/cc1101/library.properties b/lib/lib_rf/cc1101/library.properties similarity index 100% rename from lib_rf/cc1101/library.properties rename to lib/lib_rf/cc1101/library.properties diff --git a/lib_rf/rc-switch/.gitignore b/lib/lib_rf/rc-switch/.gitignore similarity index 100% rename from lib_rf/rc-switch/.gitignore rename to lib/lib_rf/rc-switch/.gitignore diff --git a/lib_rf/rc-switch/README.md b/lib/lib_rf/rc-switch/README.md similarity index 100% rename from lib_rf/rc-switch/README.md rename to lib/lib_rf/rc-switch/README.md diff --git a/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino b/lib/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino similarity index 100% rename from lib_rf/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino rename to lib/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino diff --git a/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/output.ino b/lib/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/output.ino similarity index 100% rename from lib_rf/rc-switch/examples/ReceiveDemo_Advanced/output.ino rename to lib/lib_rf/rc-switch/examples/ReceiveDemo_Advanced/output.ino diff --git a/lib_rf/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino b/lib/lib_rf/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino similarity index 100% rename from lib_rf/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino rename to lib/lib_rf/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino diff --git a/lib_rf/rc-switch/examples/SendDemo/SendDemo.ino b/lib/lib_rf/rc-switch/examples/SendDemo/SendDemo.ino similarity index 100% rename from lib_rf/rc-switch/examples/SendDemo/SendDemo.ino rename to lib/lib_rf/rc-switch/examples/SendDemo/SendDemo.ino diff --git a/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino b/lib/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino similarity index 100% rename from lib_rf/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino rename to lib/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino diff --git a/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino b/lib/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino similarity index 100% rename from lib_rf/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino rename to lib/lib_rf/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino diff --git a/lib_rf/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino b/lib/lib_rf/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino similarity index 100% rename from lib_rf/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino rename to lib/lib_rf/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino diff --git a/lib_rf/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino b/lib/lib_rf/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino similarity index 100% rename from lib_rf/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino rename to lib/lib_rf/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino diff --git a/lib_rf/rc-switch/examples/TypeD_REV/TypeD_REV.ino b/lib/lib_rf/rc-switch/examples/TypeD_REV/TypeD_REV.ino similarity index 100% rename from lib_rf/rc-switch/examples/TypeD_REV/TypeD_REV.ino rename to lib/lib_rf/rc-switch/examples/TypeD_REV/TypeD_REV.ino diff --git a/lib_rf/rc-switch/examples/Webserver/Webserver.ino b/lib/lib_rf/rc-switch/examples/Webserver/Webserver.ino similarity index 100% rename from lib_rf/rc-switch/examples/Webserver/Webserver.ino rename to lib/lib_rf/rc-switch/examples/Webserver/Webserver.ino diff --git a/lib_rf/rc-switch/keywords.txt b/lib/lib_rf/rc-switch/keywords.txt similarity index 100% rename from lib_rf/rc-switch/keywords.txt rename to lib/lib_rf/rc-switch/keywords.txt diff --git a/lib_rf/rc-switch/library.json b/lib/lib_rf/rc-switch/library.json similarity index 100% rename from lib_rf/rc-switch/library.json rename to lib/lib_rf/rc-switch/library.json diff --git a/lib_rf/rc-switch/library.properties b/lib/lib_rf/rc-switch/library.properties similarity index 100% rename from lib_rf/rc-switch/library.properties rename to lib/lib_rf/rc-switch/library.properties diff --git a/lib_rf/rc-switch/platformio.ini b/lib/lib_rf/rc-switch/platformio.ini similarity index 100% rename from lib_rf/rc-switch/platformio.ini rename to lib/lib_rf/rc-switch/platformio.ini diff --git a/lib_rf/rc-switch/src/RCSwitch.cpp b/lib/lib_rf/rc-switch/src/RCSwitch.cpp similarity index 100% rename from lib_rf/rc-switch/src/RCSwitch.cpp rename to lib/lib_rf/rc-switch/src/RCSwitch.cpp diff --git a/lib_rf/rc-switch/src/RCSwitch.h b/lib/lib_rf/rc-switch/src/RCSwitch.h similarity index 100% rename from lib_rf/rc-switch/src/RCSwitch.h rename to lib/lib_rf/rc-switch/src/RCSwitch.h diff --git a/lib_ssl/base64-1.1.1/LICENSE b/lib/lib_ssl/base64-1.1.1/LICENSE similarity index 100% rename from lib_ssl/base64-1.1.1/LICENSE rename to lib/lib_ssl/base64-1.1.1/LICENSE diff --git a/lib_ssl/base64-1.1.1/Makefile b/lib/lib_ssl/base64-1.1.1/Makefile similarity index 100% rename from lib_ssl/base64-1.1.1/Makefile rename to lib/lib_ssl/base64-1.1.1/Makefile diff --git a/lib_ssl/base64-1.1.1/README.md b/lib/lib_ssl/base64-1.1.1/README.md similarity index 100% rename from lib_ssl/base64-1.1.1/README.md rename to lib/lib_ssl/base64-1.1.1/README.md diff --git a/lib_ssl/base64-1.1.1/catch.cpp b/lib/lib_ssl/base64-1.1.1/catch.cpp similarity index 100% rename from lib_ssl/base64-1.1.1/catch.cpp rename to lib/lib_ssl/base64-1.1.1/catch.cpp diff --git a/lib_ssl/base64-1.1.1/catch.hpp b/lib/lib_ssl/base64-1.1.1/catch.hpp similarity index 100% rename from lib_ssl/base64-1.1.1/catch.hpp rename to lib/lib_ssl/base64-1.1.1/catch.hpp diff --git a/lib_ssl/base64-1.1.1/library.properties b/lib/lib_ssl/base64-1.1.1/library.properties similarity index 100% rename from lib_ssl/base64-1.1.1/library.properties rename to lib/lib_ssl/base64-1.1.1/library.properties diff --git a/lib_ssl/base64-1.1.1/src/base64.hpp b/lib/lib_ssl/base64-1.1.1/src/base64.hpp similarity index 100% rename from lib_ssl/base64-1.1.1/src/base64.hpp rename to lib/lib_ssl/base64-1.1.1/src/base64.hpp diff --git a/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt b/lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt similarity index 100% rename from lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt rename to lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt diff --git a/lib_ssl/bearssl-esp8266/conf/esp8266.mk b/lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk similarity index 100% rename from lib_ssl/bearssl-esp8266/conf/esp8266.mk rename to lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk diff --git a/lib_ssl/bearssl-esp8266/library.properties b/lib/lib_ssl/bearssl-esp8266/library.properties similarity index 100% rename from lib_ssl/bearssl-esp8266/library.properties rename to lib/lib_ssl/bearssl-esp8266/library.properties diff --git a/lib_ssl/bearssl-esp8266/src/aead/ccm.c b/lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/aead/ccm.c rename to lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c diff --git a/lib_ssl/bearssl-esp8266/src/aead/eax.c b/lib/lib_ssl/bearssl-esp8266/src/aead/eax.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/aead/eax.c rename to lib/lib_ssl/bearssl-esp8266/src/aead/eax.c diff --git a/lib_ssl/bearssl-esp8266/src/aead/gcm.c b/lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/aead/gcm.c rename to lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/ccopy.c b/lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/ccopy.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec16be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec16be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec16le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec16le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec32be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec32be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec32le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec32le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec64be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec64be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/dec64le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/dec64le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc16be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc16be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc16le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc16le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc32be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc32be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc32le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc32le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc64be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc64be.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/enc64le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/enc64le.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/pemdec.c b/lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/pemdec.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c diff --git a/lib_ssl/bearssl-esp8266/src/codec/pemenc.c b/lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/codec/pemenc.c rename to lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_i15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_i15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_c25519_i15.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_i15.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_default.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_default.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c diff --git a/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c rename to lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/dig_oid.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/dig_size.c b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/dig_size.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/md5.c b/lib/lib_ssl/bearssl-esp8266/src/hash/md5.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/md5.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/md5.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/md5sha1.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/mgf1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/mgf1.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/multihash.c b/lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/multihash.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/sha1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/sha1.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/sha2big.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/sha2big.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c diff --git a/lib_ssl/bearssl-esp8266/src/hash/sha2small.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/hash/sha2small.c rename to lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_add.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_add.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_decmod.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_decode.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_decode.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_decred.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_decred.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_encode.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_encode.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_fmont.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_iszero.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_modpow.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_montmul.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_muladd.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_reduce.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_rshift.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_sub.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_sub.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c diff --git a/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/int/i15_tmont.c rename to lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c diff --git a/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c b/lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/kdf/hkdf.c rename to lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c diff --git a/lib_ssl/bearssl-esp8266/src/kdf/shake.c b/lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/kdf/shake.c rename to lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c diff --git a/lib_ssl/bearssl-esp8266/src/mac/hmac.c b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/mac/hmac.c rename to lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c diff --git a/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c rename to lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c diff --git a/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h b/lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h rename to lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h diff --git a/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c b/lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c rename to lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c diff --git a/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c b/lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c rename to lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c diff --git a/lib_ssl/bearssl-esp8266/src/rand/sysrng.c b/lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rand/sysrng.c rename to lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c diff --git a/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c rename to lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c diff --git a/lib_ssl/bearssl-esp8266/src/settings.c b/lib/lib_ssl/bearssl-esp8266/src/settings.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/settings.c rename to lib/lib_ssl/bearssl-esp8266/src/settings.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/prf.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/prf.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c diff --git a/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c rename to lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_support.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c diff --git a/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c rename to lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_block.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h diff --git a/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h rename to lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h diff --git a/lib_ssl/bearssl-esp8266/src/t_config.h b/lib/lib_ssl/bearssl-esp8266/src/t_config.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_config.h rename to lib/lib_ssl/bearssl-esp8266/src/t_config.h diff --git a/lib_ssl/bearssl-esp8266/src/t_inner.h b/lib/lib_ssl/bearssl-esp8266/src/t_inner.h similarity index 100% rename from lib_ssl/bearssl-esp8266/src/t_inner.h rename to lib/lib_ssl/bearssl-esp8266/src/t_inner.h diff --git a/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c b/lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/asn1enc.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c diff --git a/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c similarity index 100% rename from lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c rename to lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c diff --git a/libesp32/ESP32-Ethernet/README.md b/lib/libesp32/ESP32-Ethernet/README.md similarity index 100% rename from libesp32/ESP32-Ethernet/README.md rename to lib/libesp32/ESP32-Ethernet/README.md diff --git a/libesp32/ESP32-Ethernet/examples/EthernetLAN_IP101/EthernetLAN_IP101.ino b/lib/libesp32/ESP32-Ethernet/examples/EthernetLAN_IP101/EthernetLAN_IP101.ino similarity index 100% rename from libesp32/ESP32-Ethernet/examples/EthernetLAN_IP101/EthernetLAN_IP101.ino rename to lib/libesp32/ESP32-Ethernet/examples/EthernetLAN_IP101/EthernetLAN_IP101.ino diff --git a/libesp32/ESP32-Ethernet/library.properties b/lib/libesp32/ESP32-Ethernet/library.properties similarity index 100% rename from libesp32/ESP32-Ethernet/library.properties rename to lib/libesp32/ESP32-Ethernet/library.properties diff --git a/libesp32/ESP32-Ethernet/src/ETH.cpp b/lib/libesp32/ESP32-Ethernet/src/ETH.cpp similarity index 100% rename from libesp32/ESP32-Ethernet/src/ETH.cpp rename to lib/libesp32/ESP32-Ethernet/src/ETH.cpp diff --git a/libesp32/ESP32-Ethernet/src/ETH.h b/lib/libesp32/ESP32-Ethernet/src/ETH.h similarity index 100% rename from libesp32/ESP32-Ethernet/src/ETH.h rename to lib/libesp32/ESP32-Ethernet/src/ETH.h diff --git a/libesp32/ESP32-Mail-Client/LICENSE b/lib/libesp32/ESP32-Mail-Client/LICENSE similarity index 100% rename from libesp32/ESP32-Mail-Client/LICENSE rename to lib/libesp32/ESP32-Mail-Client/LICENSE diff --git a/libesp32/ESP32-Mail-Client/README.md b/lib/libesp32/ESP32-Mail-Client/README.md similarity index 100% rename from libesp32/ESP32-Mail-Client/README.md rename to lib/libesp32/ESP32-Mail-Client/README.md diff --git a/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino b/lib/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino similarity index 100% rename from libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino rename to lib/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino b/lib/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino similarity index 100% rename from libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino rename to lib/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/image.h b/lib/libesp32/ESP32-Mail-Client/examples/Send_email/image.h similarity index 100% rename from libesp32/ESP32-Mail-Client/examples/Send_email/image.h rename to lib/libesp32/ESP32-Mail-Client/examples/Send_email/image.h diff --git a/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino b/lib/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino similarity index 100% rename from libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino rename to lib/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino diff --git a/libesp32/ESP32-Mail-Client/examples/Time/Time.ino b/lib/libesp32/ESP32-Mail-Client/examples/Time/Time.ino similarity index 100% rename from libesp32/ESP32-Mail-Client/examples/Time/Time.ino rename to lib/libesp32/ESP32-Mail-Client/examples/Time/Time.ino diff --git a/libesp32/ESP32-Mail-Client/keywords.txt b/lib/libesp32/ESP32-Mail-Client/keywords.txt similarity index 100% rename from libesp32/ESP32-Mail-Client/keywords.txt rename to lib/libesp32/ESP32-Mail-Client/keywords.txt diff --git a/libesp32/ESP32-Mail-Client/library.properties b/lib/libesp32/ESP32-Mail-Client/library.properties similarity index 100% rename from libesp32/ESP32-Mail-Client/library.properties rename to lib/libesp32/ESP32-Mail-Client/library.properties diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg b/lib/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg similarity index 100% rename from libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg rename to lib/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png b/lib/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png similarity index 100% rename from libesp32/ESP32-Mail-Client/media/images/esp32-mail.png rename to lib/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp b/lib/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp rename to lib/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h b/lib/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h rename to lib/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp b/lib/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp rename to lib/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h b/lib/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h rename to lib/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp b/lib/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp rename to lib/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h b/lib/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h rename to lib/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.cpp b/lib/libesp32/ESP32-Mail-Client/src/RFC2047.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/RFC2047.cpp rename to lib/libesp32/ESP32-Mail-Client/src/RFC2047.cpp diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.h b/lib/libesp32/ESP32-Mail-Client/src/RFC2047.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/RFC2047.h rename to lib/libesp32/ESP32-Mail-Client/src/RFC2047.h diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp b/lib/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp rename to lib/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h b/lib/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h rename to lib/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp b/lib/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ssl_client32.cpp rename to lib/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.h b/lib/libesp32/ESP32-Mail-Client/src/ssl_client32.h similarity index 100% rename from libesp32/ESP32-Mail-Client/src/ssl_client32.h rename to lib/libesp32/ESP32-Mail-Client/src/ssl_client32.h diff --git a/libesp32/ESP32-to-ESP8266-compat/README.adoc b/lib/libesp32/ESP32-to-ESP8266-compat/README.adoc similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/README.adoc rename to lib/libesp32/ESP32-to-ESP8266-compat/README.adoc diff --git a/libesp32/ESP32-to-ESP8266-compat/keywords.txt b/lib/libesp32/ESP32-to-ESP8266-compat/keywords.txt similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/keywords.txt rename to lib/libesp32/ESP32-to-ESP8266-compat/keywords.txt diff --git a/libesp32/ESP32-to-ESP8266-compat/library.properties b/lib/libesp32/ESP32-to-ESP8266-compat/library.properties similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/library.properties rename to lib/libesp32/ESP32-to-ESP8266-compat/library.properties diff --git a/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/AddrList.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/AddrList.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266HTTPClient.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WebServer.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266httpUpdate.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266mDNS.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/c_types.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/c_types.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/c_types.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/c_types.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/eboot_command.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp rename to lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/ets_sys.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/gpio.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/gpio.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/gpio.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/gpio.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/os_type.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/os_type.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/os_type.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/os_type.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/osapi.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/osapi.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/osapi.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/osapi.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/sntp.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/sntp.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/sntp.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/sntp.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/spi_flash.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/twi.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/twi.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/twi.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/twi.h diff --git a/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h similarity index 100% rename from libesp32/ESP32-to-ESP8266-compat/src/user_interface.h rename to lib/libesp32/ESP32-to-ESP8266-compat/src/user_interface.h diff --git a/libesp32/ESP32README.md b/lib/libesp32/ESP32README.md similarity index 100% rename from libesp32/ESP32README.md rename to lib/libesp32/ESP32README.md diff --git a/libesp32/NimBLE-Arduino/CHANGELOG.md b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md similarity index 100% rename from libesp32/NimBLE-Arduino/CHANGELOG.md rename to lib/libesp32/NimBLE-Arduino/CHANGELOG.md diff --git a/libesp32/NimBLE-Arduino/LICENSE b/lib/libesp32/NimBLE-Arduino/LICENSE similarity index 100% rename from libesp32/NimBLE-Arduino/LICENSE rename to lib/libesp32/NimBLE-Arduino/LICENSE diff --git a/libesp32/NimBLE-Arduino/README.md b/lib/libesp32/NimBLE-Arduino/README.md similarity index 100% rename from libesp32/NimBLE-Arduino/README.md rename to lib/libesp32/NimBLE-Arduino/README.md diff --git a/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md b/lib/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md similarity index 100% rename from libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md rename to lib/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md diff --git a/libesp32/NimBLE-Arduino/docs/Migration_guide.md b/lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md similarity index 100% rename from libesp32/NimBLE-Arduino/docs/Migration_guide.md rename to lib/libesp32/NimBLE-Arduino/docs/Migration_guide.md diff --git a/libesp32/NimBLE-Arduino/docs/New_user_guide.md b/lib/libesp32/NimBLE-Arduino/docs/New_user_guide.md similarity index 100% rename from libesp32/NimBLE-Arduino/docs/New_user_guide.md rename to lib/libesp32/NimBLE-Arduino/docs/New_user_guide.md diff --git a/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino rename to lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino diff --git a/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md rename to lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino rename to lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md b/lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md rename to lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino b/lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino rename to lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino diff --git a/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md b/lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md similarity index 100% rename from libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md rename to lib/libesp32/NimBLE-Arduino/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino rename to lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino rename to lib/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_client/BLE_client.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino similarity index 100% rename from libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino rename to lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_write/BLE_write.ino diff --git a/libesp32/NimBLE-Arduino/library.properties b/lib/libesp32/NimBLE-Arduino/library.properties similarity index 100% rename from libesp32/NimBLE-Arduino/library.properties rename to lib/libesp32/NimBLE-Arduino/library.properties diff --git a/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md b/lib/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md similarity index 100% rename from libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md rename to lib/libesp32/NimBLE-Arduino/src/CODING_STANDARDS.md diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/FreeRTOS.cpp rename to lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.h b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/FreeRTOS.h rename to lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h diff --git a/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h b/lib/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h rename to lib/libesp32/NimBLE-Arduino/src/HIDKeyboardTypes.h diff --git a/libesp32/NimBLE-Arduino/src/HIDTypes.h b/lib/libesp32/NimBLE-Arduino/src/HIDTypes.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/HIDTypes.h rename to lib/libesp32/NimBLE-Arduino/src/HIDTypes.h diff --git a/libesp32/NimBLE-Arduino/src/NOTICE b/lib/libesp32/NimBLE-Arduino/src/NOTICE similarity index 100% rename from libesp32/NimBLE-Arduino/src/NOTICE rename to lib/libesp32/NimBLE-Arduino/src/NOTICE diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLE2904.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2904.h b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLE2904.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLE2904.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAddress.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAddress.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAddress.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEBeacon.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEBeacon.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEClient.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEClient.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEDevice.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLELog.h b/lib/libesp32/NimBLE-Arduino/src/NimBLELog.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLELog.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLELog.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLERemoteService.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEScan.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEScan.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.h b/lib/libesp32/NimBLE-Arduino/src/NimBLESecurity.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLESecurity.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLESecurity.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEServer.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEServer.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEService.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEService.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEService.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEService.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEService.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEUUID.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.h diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp rename to lib/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEUtils.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/NimBLEUtils.h rename to lib/libesp32/NimBLE-Arduino/src/NimBLEUtils.h diff --git a/libesp32/NimBLE-Arduino/src/README.md b/lib/libesp32/NimBLE-Arduino/src/README.md similarity index 100% rename from libesp32/NimBLE-Arduino/src/README.md rename to lib/libesp32/NimBLE-Arduino/src/README.md diff --git a/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md b/lib/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md similarity index 100% rename from libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md rename to lib/libesp32/NimBLE-Arduino/src/RELEASE_NOTES.md diff --git a/libesp32/NimBLE-Arduino/src/console/console.h b/lib/libesp32/NimBLE-Arduino/src/console/console.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/console/console.h rename to lib/libesp32/NimBLE-Arduino/src/console/console.h diff --git a/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c rename to lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c diff --git a/libesp32/NimBLE-Arduino/src/esp_compiler.h b/lib/libesp32/NimBLE-Arduino/src/esp_compiler.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/esp_compiler.h rename to lib/libesp32/NimBLE-Arduino/src/esp_compiler.h diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h rename to lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/esp_nimble_hci.h rename to lib/libesp32/NimBLE-Arduino/src/esp_nimble_hci.h diff --git a/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/esp_nimble_mem.h rename to lib/libesp32/NimBLE-Arduino/src/esp_nimble_mem.h diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/AUTHORS diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/LICENSE diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/README rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/README diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/VERSION diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/documentation/tinycrypt.rst diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_decrypt.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/aes_encrypt.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cbc_mode.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ccm_mode.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/cmac_mode.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_mode.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ctr_prng.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dh.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_dsa.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/ecc_platform_specific.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/hmac_prng.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/sha256.c diff --git a/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c b/lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c rename to lib/libesp32/NimBLE-Arduino/src/ext/tinycrypt/src/utils.c diff --git a/libesp32/NimBLE-Arduino/src/hal/hal_timer.h b/lib/libesp32/NimBLE-Arduino/src/hal/hal_timer.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/hal/hal_timer.h rename to lib/libesp32/NimBLE-Arduino/src/hal/hal_timer.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_att.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_att.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_att.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_att.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_eddystone.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_eddystone.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_gap.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_gap.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_gap.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_gap.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_gatt.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_gatt.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_gatt.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_gatt.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_adv.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_hci.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_id.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_id.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_log.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_log.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_mbuf.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_pvcy.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_hs_stop.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_ibeacon.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_l2cap.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_l2cap.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_monitor.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_monitor.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_monitor.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_monitor.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_sm.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_sm.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_sm.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_sm.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_store.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_store.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_store.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_store.h diff --git a/libesp32/NimBLE-Arduino/src/host/ble_uuid.h b/lib/libesp32/NimBLE-Arduino/src/host/ble_uuid.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/ble_uuid.h rename to lib/libesp32/NimBLE-Arduino/src/host/ble_uuid.h diff --git a/libesp32/NimBLE-Arduino/src/host/util/util.h b/lib/libesp32/NimBLE-Arduino/src/host/util/util.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/host/util/util.h rename to lib/libesp32/NimBLE-Arduino/src/host/util/util.h diff --git a/libesp32/NimBLE-Arduino/src/log/log.h b/lib/libesp32/NimBLE-Arduino/src/log/log.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/log/log.h rename to lib/libesp32/NimBLE-Arduino/src/log/log.h diff --git a/libesp32/NimBLE-Arduino/src/mem/mem.h b/lib/libesp32/NimBLE-Arduino/src/mem/mem.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mem/mem.h rename to lib/libesp32/NimBLE-Arduino/src/mem/mem.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/access.h b/lib/libesp32/NimBLE-Arduino/src/mesh/access.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/access.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/access.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h b/lib/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/cfg_cli.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h b/lib/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/cfg_srv.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/glue.h b/lib/libesp32/NimBLE-Arduino/src/mesh/glue.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/glue.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/glue.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/health_cli.h b/lib/libesp32/NimBLE-Arduino/src/mesh/health_cli.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/health_cli.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/health_cli.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/health_srv.h b/lib/libesp32/NimBLE-Arduino/src/mesh/health_srv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/health_srv.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/health_srv.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/main.h b/lib/libesp32/NimBLE-Arduino/src/mesh/main.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/main.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/main.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/mesh.h b/lib/libesp32/NimBLE-Arduino/src/mesh/mesh.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/mesh.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/mesh.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/model_cli.h b/lib/libesp32/NimBLE-Arduino/src/mesh/model_cli.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/model_cli.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/model_cli.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/model_srv.h b/lib/libesp32/NimBLE-Arduino/src/mesh/model_srv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/model_srv.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/model_srv.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/porting.h b/lib/libesp32/NimBLE-Arduino/src/mesh/porting.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/porting.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/porting.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/proxy.h b/lib/libesp32/NimBLE-Arduino/src/mesh/proxy.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/proxy.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/proxy.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/slist.h b/lib/libesp32/NimBLE-Arduino/src/mesh/slist.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/slist.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/slist.h diff --git a/libesp32/NimBLE-Arduino/src/mesh/testing.h b/lib/libesp32/NimBLE-Arduino/src/mesh/testing.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/mesh/testing.h rename to lib/libesp32/NimBLE-Arduino/src/mesh/testing.h diff --git a/libesp32/NimBLE-Arduino/src/modlog/modlog.h b/lib/libesp32/NimBLE-Arduino/src/modlog/modlog.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/modlog/modlog.h rename to lib/libesp32/NimBLE-Arduino/src/modlog/modlog.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/ble.h b/lib/libesp32/NimBLE-Arduino/src/nimble/ble.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/ble.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/ble.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h b/lib/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/ble_hci_trans.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/hci_common.h b/lib/libesp32/NimBLE-Arduino/src/nimble/hci_common.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/hci_common.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/hci_common.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/access.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/adv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/atomic.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/beacon.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_att_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_gatt_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_hs_conn_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_coc_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/ble_l2cap_sig_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_cli.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/cfg_srv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/crypto.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/foundation.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/friend.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/glue.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_cli.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/health_srv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/light_model.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/lpn.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/mesh_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_cli.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/model_srv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/net.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/prov.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/proxy.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/settings.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/shell.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_cmd_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_cmd_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_cmd_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_cmd_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_att_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gap_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gap_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gap_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gap_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gatt_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gatt_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gatt_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_gatt_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_adv_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_adv_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_adv_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_adv_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_atomic_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_atomic_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_atomic_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_atomic_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_conn_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_conn_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_conn_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_conn_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_dbg_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_dbg_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_dbg_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_dbg_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_flow_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_flow_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_flow_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_flow_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_hci_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_hci_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_hci_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_hci_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_id_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_id_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_id_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_id_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_mbuf_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_mbuf_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_mbuf_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_mbuf_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_periodic_sync_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_periodic_sync_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_periodic_sync_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_periodic_sync_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_pvcy_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_pvcy_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_pvcy_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_pvcy_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_startup_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_startup_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_startup_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_startup_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_coc_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_coc_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_coc_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_coc_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_sig_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_sig_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_sig_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_l2cap_sig_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_monitor_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_monitor_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_monitor_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_monitor_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_sm_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_sm_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_sm_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_sm_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_uuid_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_uuid_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_uuid_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_uuid_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/testing.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/transport.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/ans/src/ble_svc_ans.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ans/src/ble_svc_ans.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/ans/src/ble_svc_ans.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ans/src/ble_svc_ans.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/bas/src/ble_svc_bas.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/bas/src/ble_svc_bas.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/bas/src/ble_svc_bas.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/bas/src/ble_svc_bas.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/gap/src/ble_svc_gap.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/gap/src/ble_svc_gap.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/gap/src/ble_svc_gap.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/gap/src/ble_svc_gap.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/gatt/src/ble_svc_gatt.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/gatt/src/ble_svc_gatt.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/gatt/src/ble_svc_gatt.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/gatt/src/ble_svc_gatt.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/ias/src/ble_svc_ias.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ias/src/ble_svc_ias.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/ias/src/ble_svc_ias.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ias/src/ble_svc_ias.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/ipss/src/ble_svc_ipss.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ipss/src/ble_svc_ipss.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/ipss/src/ble_svc_ipss.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/ipss/src/ble_svc_ipss.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/lls/src/ble_svc_lls.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/lls/src/ble_svc_lls.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/lls/src/ble_svc_lls.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/lls/src/ble_svc_lls.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_hs_hci_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_hs_hci_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_hs_hci_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_hs_hci_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_svc_tps.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_svc_tps.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_svc_tps.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/services/tps/src/ble_svc_tps.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_clt.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_clt.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_clt.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_clt.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_cmd_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_svr.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_svr.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_svr.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_att_svr.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatt_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatt_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatt_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatt_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gattc.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gattc.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gattc.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gattc.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts_lcl.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts_lcl.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts_lcl.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gatts_lcl.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_adv_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_atomic_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_cfg.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_cfg.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_cfg.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_cfg.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_dbg_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_flow_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_cmd.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_cmd.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_cmd.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_cmd.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_evt.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_evt.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_evt.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_evt.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_util.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_util.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_util.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_hci_util.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_id_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_log.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_log.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_log.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_log.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mbuf_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_misc.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_misc.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_misc.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_misc.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mqueue.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mqueue.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mqueue.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_mqueue.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_periodic_sync_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_pvcy_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_shutdown.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_shutdown.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_shutdown.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_shutdown.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_startup_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_stop.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_stop.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_stop.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_stop.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_ibeacon.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_ibeacon.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_ibeacon.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_ibeacon.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_coc_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_cmd.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_cmd.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_cmd.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_cmd.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_l2cap_sig_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_monitor_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_alg.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_alg.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_alg.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_alg.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_cmd.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_cmd.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_cmd.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_cmd.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_lgcy.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_lgcy.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_lgcy.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_lgcy.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_sc.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_sc.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_sc.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_sm_sc.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store_util.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store_util.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store_util.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_store_util.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_uuid_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config_priv.h b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config_priv.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_config_priv.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/store/ram/src/ble_store_ram.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/ram/src/ble_store_ram.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/store/ram/src/ble_store_ram.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/store/ram/src/ble_store_ram.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/util/src/addr.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/util/src/addr.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/host/util/src/addr.c rename to lib/libesp32/NimBLE-Arduino/src/nimble/host/util/src/addr.c diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_npl.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_npl.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_npl.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_npl.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_npl_os.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_npl_os.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_npl_os.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_npl_os.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_opt.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_opt.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_opt.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_opt.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_opt_auto.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_opt_auto.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_opt_auto.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_opt_auto.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_port.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_port.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_port.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_port.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/nimble_port_freertos.h b/lib/libesp32/NimBLE-Arduino/src/nimble/nimble_port_freertos.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/nimble_port_freertos.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/nimble_port_freertos.h diff --git a/libesp32/NimBLE-Arduino/src/nimble/npl_freertos.h b/lib/libesp32/NimBLE-Arduino/src/nimble/npl_freertos.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimble/npl_freertos.h rename to lib/libesp32/NimBLE-Arduino/src/nimble/npl_freertos.h diff --git a/libesp32/NimBLE-Arduino/src/nimconfig.h b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/nimconfig.h rename to lib/libesp32/NimBLE-Arduino/src/nimconfig.h diff --git a/libesp32/NimBLE-Arduino/src/os/endian.h b/lib/libesp32/NimBLE-Arduino/src/os/endian.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/endian.h rename to lib/libesp32/NimBLE-Arduino/src/os/endian.h diff --git a/libesp32/NimBLE-Arduino/src/os/os.h b/lib/libesp32/NimBLE-Arduino/src/os/os.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os.h rename to lib/libesp32/NimBLE-Arduino/src/os/os.h diff --git a/libesp32/NimBLE-Arduino/src/os/os_cputime.h b/lib/libesp32/NimBLE-Arduino/src/os/os_cputime.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os_cputime.h rename to lib/libesp32/NimBLE-Arduino/src/os/os_cputime.h diff --git a/libesp32/NimBLE-Arduino/src/os/os_error.h b/lib/libesp32/NimBLE-Arduino/src/os/os_error.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os_error.h rename to lib/libesp32/NimBLE-Arduino/src/os/os_error.h diff --git a/libesp32/NimBLE-Arduino/src/os/os_mbuf.h b/lib/libesp32/NimBLE-Arduino/src/os/os_mbuf.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os_mbuf.h rename to lib/libesp32/NimBLE-Arduino/src/os/os_mbuf.h diff --git a/libesp32/NimBLE-Arduino/src/os/os_mempool.h b/lib/libesp32/NimBLE-Arduino/src/os/os_mempool.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os_mempool.h rename to lib/libesp32/NimBLE-Arduino/src/os/os_mempool.h diff --git a/libesp32/NimBLE-Arduino/src/os/os_trace_api.h b/lib/libesp32/NimBLE-Arduino/src/os/os_trace_api.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/os_trace_api.h rename to lib/libesp32/NimBLE-Arduino/src/os/os_trace_api.h diff --git a/libesp32/NimBLE-Arduino/src/os/queue.h b/lib/libesp32/NimBLE-Arduino/src/os/queue.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/os/queue.h rename to lib/libesp32/NimBLE-Arduino/src/os/queue.h diff --git a/libesp32/NimBLE-Arduino/src/port/src/esp_nimble_mem.c b/lib/libesp32/NimBLE-Arduino/src/port/src/esp_nimble_mem.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/port/src/esp_nimble_mem.c rename to lib/libesp32/NimBLE-Arduino/src/port/src/esp_nimble_mem.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/include/nimble/nimble_port.h b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/include/nimble/nimble_port.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/include/nimble/nimble_port.h rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/include/nimble/nimble_port.h diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/endian.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/endian.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/endian.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/endian.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/hal_timer.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/hal_timer.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/hal_timer.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/hal_timer.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/mem.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/mem.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/mem.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/mem.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/nimble_port.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/nimble_port.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/nimble_port.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/nimble_port.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime_pwr2.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime_pwr2.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime_pwr2.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_cputime_pwr2.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mbuf.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mbuf.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mbuf.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mbuf.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_msys_init.c b/lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_msys_init.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/nimble/src/os_msys_init.c rename to lib/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_msys_init.c diff --git a/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/nimble_port_freertos.c b/lib/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/nimble_port_freertos.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/nimble_port_freertos.c rename to lib/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/nimble_port_freertos.c diff --git a/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/npl_os_freertos.c b/lib/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/npl_os_freertos.c similarity index 100% rename from libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/npl_os_freertos.c rename to lib/libesp32/NimBLE-Arduino/src/porting/npl/freertos/src/npl_os_freertos.c diff --git a/libesp32/NimBLE-Arduino/src/services/ans/ble_svc_ans.h b/lib/libesp32/NimBLE-Arduino/src/services/ans/ble_svc_ans.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/ans/ble_svc_ans.h rename to lib/libesp32/NimBLE-Arduino/src/services/ans/ble_svc_ans.h diff --git a/libesp32/NimBLE-Arduino/src/services/bas/ble_svc_bas.h b/lib/libesp32/NimBLE-Arduino/src/services/bas/ble_svc_bas.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/bas/ble_svc_bas.h rename to lib/libesp32/NimBLE-Arduino/src/services/bas/ble_svc_bas.h diff --git a/libesp32/NimBLE-Arduino/src/services/gap/ble_svc_gap.h b/lib/libesp32/NimBLE-Arduino/src/services/gap/ble_svc_gap.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/gap/ble_svc_gap.h rename to lib/libesp32/NimBLE-Arduino/src/services/gap/ble_svc_gap.h diff --git a/libesp32/NimBLE-Arduino/src/services/gatt/ble_svc_gatt.h b/lib/libesp32/NimBLE-Arduino/src/services/gatt/ble_svc_gatt.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/gatt/ble_svc_gatt.h rename to lib/libesp32/NimBLE-Arduino/src/services/gatt/ble_svc_gatt.h diff --git a/libesp32/NimBLE-Arduino/src/services/ias/ble_svc_ias.h b/lib/libesp32/NimBLE-Arduino/src/services/ias/ble_svc_ias.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/ias/ble_svc_ias.h rename to lib/libesp32/NimBLE-Arduino/src/services/ias/ble_svc_ias.h diff --git a/libesp32/NimBLE-Arduino/src/services/ipss/ble_svc_ipss.h b/lib/libesp32/NimBLE-Arduino/src/services/ipss/ble_svc_ipss.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/ipss/ble_svc_ipss.h rename to lib/libesp32/NimBLE-Arduino/src/services/ipss/ble_svc_ipss.h diff --git a/libesp32/NimBLE-Arduino/src/services/lls/ble_svc_lls.h b/lib/libesp32/NimBLE-Arduino/src/services/lls/ble_svc_lls.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/lls/ble_svc_lls.h rename to lib/libesp32/NimBLE-Arduino/src/services/lls/ble_svc_lls.h diff --git a/libesp32/NimBLE-Arduino/src/services/tps/ble_svc_tps.h b/lib/libesp32/NimBLE-Arduino/src/services/tps/ble_svc_tps.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/services/tps/ble_svc_tps.h rename to lib/libesp32/NimBLE-Arduino/src/services/tps/ble_svc_tps.h diff --git a/libesp32/NimBLE-Arduino/src/src/ble_hs_hci_priv.h b/lib/libesp32/NimBLE-Arduino/src/src/ble_hs_hci_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/src/ble_hs_hci_priv.h rename to lib/libesp32/NimBLE-Arduino/src/src/ble_hs_hci_priv.h diff --git a/libesp32/NimBLE-Arduino/src/src/ble_sm_priv.h b/lib/libesp32/NimBLE-Arduino/src/src/ble_sm_priv.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/src/ble_sm_priv.h rename to lib/libesp32/NimBLE-Arduino/src/src/ble_sm_priv.h diff --git a/libesp32/NimBLE-Arduino/src/stats/stats.h b/lib/libesp32/NimBLE-Arduino/src/stats/stats.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/stats/stats.h rename to lib/libesp32/NimBLE-Arduino/src/stats/stats.h diff --git a/libesp32/NimBLE-Arduino/src/store/config/ble_store_config.h b/lib/libesp32/NimBLE-Arduino/src/store/config/ble_store_config.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/store/config/ble_store_config.h rename to lib/libesp32/NimBLE-Arduino/src/store/config/ble_store_config.h diff --git a/libesp32/NimBLE-Arduino/src/store/ram/ble_store_ram.h b/lib/libesp32/NimBLE-Arduino/src/store/ram/ble_store_ram.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/store/ram/ble_store_ram.h rename to lib/libesp32/NimBLE-Arduino/src/store/ram/ble_store_ram.h diff --git a/libesp32/NimBLE-Arduino/src/syscfg/syscfg.h b/lib/libesp32/NimBLE-Arduino/src/syscfg/syscfg.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/syscfg/syscfg.h rename to lib/libesp32/NimBLE-Arduino/src/syscfg/syscfg.h diff --git a/libesp32/NimBLE-Arduino/src/sysinit/sysinit.h b/lib/libesp32/NimBLE-Arduino/src/sysinit/sysinit.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/sysinit/sysinit.h rename to lib/libesp32/NimBLE-Arduino/src/sysinit/sysinit.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/AUTHORS b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/AUTHORS similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/AUTHORS rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/AUTHORS diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/LICENSE b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/LICENSE similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/LICENSE rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/LICENSE diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/README b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/README similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/README rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/README diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/VERSION b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/VERSION similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/VERSION rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/VERSION diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/aes.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/aes.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/aes.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/aes.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/cbc_mode.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/cbc_mode.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/cbc_mode.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/cbc_mode.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ccm_mode.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ccm_mode.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ccm_mode.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ccm_mode.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/cmac_mode.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/cmac_mode.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/cmac_mode.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/cmac_mode.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/constants.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/constants.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/constants.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/constants.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_mode.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_mode.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ctr_mode.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_mode.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_prng.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_prng.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ctr_prng.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ctr_prng.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/documentation/tinycrypt.rst b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/documentation/tinycrypt.rst similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/documentation/tinycrypt.rst rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/documentation/tinycrypt.rst diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ecc.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ecc.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dh.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dh.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dh.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dh.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dsa.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dsa.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dsa.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_dsa.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_platform_specific.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_platform_specific.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/ecc_platform_specific.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/ecc_platform_specific.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/hmac.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/hmac.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/hmac.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/hmac.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/hmac_prng.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/hmac_prng.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/hmac_prng.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/hmac_prng.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/sha256.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/sha256.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/sha256.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/sha256.h diff --git a/libesp32/NimBLE-Arduino/src/tinycrypt/utils.h b/lib/libesp32/NimBLE-Arduino/src/tinycrypt/utils.h similarity index 100% rename from libesp32/NimBLE-Arduino/src/tinycrypt/utils.h rename to lib/libesp32/NimBLE-Arduino/src/tinycrypt/utils.h diff --git a/libesp32/TTGO_TWatch_Library/LICENSE b/lib/libesp32/TTGO_TWatch_Library/LICENSE similarity index 100% rename from libesp32/TTGO_TWatch_Library/LICENSE rename to lib/libesp32/TTGO_TWatch_Library/LICENSE diff --git a/libesp32/TTGO_TWatch_Library/README.MD b/lib/libesp32/TTGO_TWatch_Library/README.MD similarity index 100% rename from libesp32/TTGO_TWatch_Library/README.MD rename to lib/libesp32/TTGO_TWatch_Library/README.MD diff --git a/libesp32/TTGO_TWatch_Library/library.json b/lib/libesp32/TTGO_TWatch_Library/library.json similarity index 100% rename from libesp32/TTGO_TWatch_Library/library.json rename to lib/libesp32/TTGO_TWatch_Library/library.json diff --git a/libesp32/TTGO_TWatch_Library/library.properties b/lib/libesp32/TTGO_TWatch_Library/library.properties similarity index 100% rename from libesp32/TTGO_TWatch_Library/library.properties rename to lib/libesp32/TTGO_TWatch_Library/library.properties diff --git a/libesp32/TTGO_TWatch_Library/src/axp20x.cpp b/lib/libesp32/TTGO_TWatch_Library/src/axp20x.cpp similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/axp20x.cpp rename to lib/libesp32/TTGO_TWatch_Library/src/axp20x.cpp diff --git a/libesp32/TTGO_TWatch_Library/src/axp20x.h b/lib/libesp32/TTGO_TWatch_Library/src/axp20x.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/axp20x.h rename to lib/libesp32/TTGO_TWatch_Library/src/axp20x.h diff --git a/libesp32/TTGO_TWatch_Library/src/bma.cpp b/lib/libesp32/TTGO_TWatch_Library/src/bma.cpp similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma.cpp rename to lib/libesp32/TTGO_TWatch_Library/src/bma.cpp diff --git a/libesp32/TTGO_TWatch_Library/src/bma.h b/lib/libesp32/TTGO_TWatch_Library/src/bma.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma.h rename to lib/libesp32/TTGO_TWatch_Library/src/bma.h diff --git a/libesp32/TTGO_TWatch_Library/src/bma4.c b/lib/libesp32/TTGO_TWatch_Library/src/bma4.c similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma4.c rename to lib/libesp32/TTGO_TWatch_Library/src/bma4.c diff --git a/libesp32/TTGO_TWatch_Library/src/bma4.h b/lib/libesp32/TTGO_TWatch_Library/src/bma4.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma4.h rename to lib/libesp32/TTGO_TWatch_Library/src/bma4.h diff --git a/libesp32/TTGO_TWatch_Library/src/bma423.c b/lib/libesp32/TTGO_TWatch_Library/src/bma423.c similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma423.c rename to lib/libesp32/TTGO_TWatch_Library/src/bma423.c diff --git a/libesp32/TTGO_TWatch_Library/src/bma423.h b/lib/libesp32/TTGO_TWatch_Library/src/bma423.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma423.h rename to lib/libesp32/TTGO_TWatch_Library/src/bma423.h diff --git a/libesp32/TTGO_TWatch_Library/src/bma4_defs.h b/lib/libesp32/TTGO_TWatch_Library/src/bma4_defs.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/bma4_defs.h rename to lib/libesp32/TTGO_TWatch_Library/src/bma4_defs.h diff --git a/libesp32/TTGO_TWatch_Library/src/i2c_bus.cpp b/lib/libesp32/TTGO_TWatch_Library/src/i2c_bus.cpp similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/i2c_bus.cpp rename to lib/libesp32/TTGO_TWatch_Library/src/i2c_bus.cpp diff --git a/libesp32/TTGO_TWatch_Library/src/i2c_bus.h b/lib/libesp32/TTGO_TWatch_Library/src/i2c_bus.h similarity index 100% rename from libesp32/TTGO_TWatch_Library/src/i2c_bus.h rename to lib/libesp32/TTGO_TWatch_Library/src/i2c_bus.h diff --git a/platformio.ini b/platformio.ini index a30214f4c..728bcafaf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -54,7 +54,7 @@ default_envs = [platformio] description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware src_dir = tasmota -lib_dir = lib +lib_dir = lib/default build_cache_dir = .cache extra_configs = platformio_tasmota32.ini platformio_tasmota_env.ini diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 299452daf..058ad1788 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -82,19 +82,19 @@ shared_libdeps_dir = lib ; *** If you dont know what it is all about, do not change lib_extra_dirs = ; *** Only disabled for Tasmota minimal and Tasmota light. For all other variants needed! - lib/lib_basic + lib/lib_basic ; **** I2C devices. Most sensors. Disable only if you dont have ANY I2C device enabled - lib/lib_i2c + lib/lib_i2c ; *** Displays. Disable if you dont have any Display activated -; lib/lib_display + lib/lib_display ; *** Bear SSL and base64. Disable if you dont have SSL or TLS activated -; lib/lib_ssl + lib/lib_ssl ; *** Audio needs a lot of time to compile. Mostly not used functions. Recommended to disable -; lib/lib_audio + lib/lib_audio ; *** RF 433 stuff (not RF Bridge). Recommended to disable -; lib/lib_rf + lib/lib_rf ; *** Mostly not used functions. Recommended to disable -; lib/lib_div + lib/lib_div [core] ; Activate only (one set) if you want to override the standard core defined in platformio.ini !!! diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 310124a30..6cc05dfe7 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -85,19 +85,19 @@ shared_libdeps_dir = lib ; *** If you dont know what it is all about, do not change lib_extra_dirs = ; *** ESP32 lib. ALWAYS needed for ESP32 !!! - libesp32 + lib/libesp32 ; *** Only disabled for Tasmota minimal and Tasmota light. For all other variants needed! - lib_basic + lib/lib_basic ; **** I2C devices. Most sensors. Disable only if you dont have ANY I2C device enabled - lib_i2c + lib/lib_i2c ; *** Displays. Disable if you dont have any Display activated - lib_display + lib/lib_display ; *** Bear SSL and base64. Disable if you dont have SSL or TLS activated - lib_ssl + lib/lib_ssl ; *** Audio needs a lot of time to compile. Mostly not used functions. Recommended to disable - lib_audio + lib/lib_audio ; *** RF 433 stuff (not RF Bridge). Recommended to disable - lib_rf + lib/lib_rf ; *** Mostly not used functions. Recommended to disable - lib_div + lib/lib_div diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index e88e37e8a..9817fdb0f 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -43,19 +43,19 @@ lib_extra_dirs = [env:tasmota-knx] build_flags = ${common.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_extra_dirs = lib_basic, lib_div +lib_extra_dirs = lib/lib_basic, lib/lib_div [env:tasmota-sensors] build_flags = ${common.build_flags} -DFIRMWARE_SENSORS -lib_extra_dirs = lib_basic, lib_i2c, lib_rf, lib_div +lib_extra_dirs = lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div [env:tasmota-display] build_flags = ${common.build_flags} -DFIRMWARE_DISPLAYS -lib_extra_dirs = lib_basic, lib_display +lib_extra_dirs = lib/lib_basic, lib/lib_display [env:tasmota-ir] build_flags = ${common.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR -lib_extra_dirs = lib_basic +lib_extra_dirs = lib/lib_basic [env:tasmota-ircustom] build_flags = ${common.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR_CUSTOM @@ -63,7 +63,7 @@ build_flags = ${common.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_I [env:tasmota-zbbridge] build_flags = ${common.build_flags} -DFIRMWARE_ZBBRIDGE board_build.f_cpu = 160000000L -lib_extra_dirs = lib_ssl +lib_extra_dirs = lib/lib_ssl [env:tasmota-BG] build_flags = ${common.build_flags} -DMY_LANGUAGE=bg_BG diff --git a/platformio_tasmota_env.ini.new b/platformio_tasmota_env.ini.new deleted file mode 100644 index 6cee57be9..000000000 --- a/platformio_tasmota_env.ini.new +++ /dev/null @@ -1,144 +0,0 @@ -[env] -platform = ${common.platform} -platform_packages = ${common.platform_packages} -framework = ${common.framework} -board = ${common.board} -board_build.ldscript = ${common.board_build.ldscript} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.f_flash = ${common.board_build.f_flash} -board_build.f_cpu = ${common.board_build.f_cpu} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} -monitor_speed = ${common.monitor_speed} -upload_port = ${common.upload_port} -upload_resetmethod = ${common.upload_resetmethod} -upload_speed = ${common.upload_speed} -extra_scripts = ${common.extra_scripts} -lib_extra_dirs = ${common.shared_libdeps_dir} - -[env:tasmota] -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-minimal] -build_flags = ${common.build_flags} -DFIRMWARE_MINIMAL -lib_ignore = ${lib_minimal.lib_ignore} - -[env:tasmota-lite] -build_flags = ${common.build_flags} -DFIRMWARE_LITE -lib_ignore = ${lib_lite.lib_ignore} - -[env:tasmota-knx] -build_flags = ${common.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_ignore = ${lib_knx.lib_ignore} - -[env:tasmota-sensors] -build_flags = ${common.build_flags} -DFIRMWARE_SENSORS -lib_ignore = ${lib_sensors.lib_ignore} - -[env:tasmota-display] -build_flags = ${common.build_flags} -DFIRMWARE_DISPLAYS -lib_ignore = ${lib_display.lib_ignore} - -[env:tasmota-ir] -build_flags = ${common.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR -lib_ignore = ${lib_ir.lib_ignore} - -[env:tasmota-ircustom] -build_flags = ${common.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR_CUSTOM -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-zbbridge] -build_flags = ${common.build_flags} -DFIRMWARE_ZBBRIDGE -lib_ignore = ${lib_zbbridge.lib_ignore} - -[env:tasmota-BG] -build_flags = ${common.build_flags} -DMY_LANGUAGE=bg_BG -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-BR] -build_flags = ${common.build_flags} -DMY_LANGUAGE=pt_BR -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-CN] -build_flags = ${common.build_flags} -DMY_LANGUAGE=zh_CN -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-CZ] -build_flags = ${common.build_flags} -DMY_LANGUAGE=cs_CZ -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-DE] -build_flags = ${common.build_flags} -DMY_LANGUAGE=de_DE -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-ES] -build_flags = ${common.build_flags} -DMY_LANGUAGE=es_ES -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-FR] -build_flags = ${common.build_flags} -DMY_LANGUAGE=fr_FR -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-GR] -build_flags = ${common.build_flags} -DMY_LANGUAGE=el_GR -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-HE] -build_flags = ${common.build_flags} -DMY_LANGUAGE=he_HE -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-HU] -build_flags = ${common.build_flags} -DMY_LANGUAGE=hu_HU -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-IT] -build_flags = ${common.build_flags} -DMY_LANGUAGE=it_IT -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-KO] -build_flags = ${common.build_flags} -DMY_LANGUAGE=ko_KO -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-NL] -build_flags = ${common.build_flags} -DMY_LANGUAGE=nl_NL -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-PL] -build_flags = ${common.build_flags} -DMY_LANGUAGE=pl_PL -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-PT] -build_flags = ${common.build_flags} -DMY_LANGUAGE=pt_PT -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-RO] -build_flags = ${common.build_flags} -DMY_LANGUAGE=ro_RO -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-RU] -build_flags = ${common.build_flags} -DMY_LANGUAGE=ru_RU -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-SE] -build_flags = ${common.build_flags} -DMY_LANGUAGE=sv_SE -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-SK] -build_flags = ${common.build_flags} -DMY_LANGUAGE=sk_SK -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-TR] -build_flags = ${common.build_flags} -DMY_LANGUAGE=tr_TR -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-TW] -build_flags = ${common.build_flags} -DMY_LANGUAGE=zh_TW -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-UK] -build_flags = ${common.build_flags} -DMY_LANGUAGE=uk_UA -lib_ignore = ${lib_Tasmota.lib_ignore} - -[env:tasmota-VN] -build_flags = ${common.build_flags} -DMY_LANGUAGE=vi_VN -lib_ignore = ${lib_Tasmota.lib_ignore} diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index cb42e1e72..58eb34a4a 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -32,37 +32,37 @@ extends = env:tasmota32 board = esp32cam board_build.f_cpu = 240000000L build_flags = ${common32.build_flags} -DFIRMWARE_WEBCAM -lib_extra_dirs = libesp32, lib_basic +lib_extra_dirs = lib/libesp32, lib/lib_basic [env:tasmota32-minimal] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL -lib_extra_dirs = libesp32 +lib_extra_dirs = lib/libesp32 [env:tasmota32-lite] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_LITE -lib_extra_dirs = libesp32 +lib_extra_dirs = lib/libesp32 [env:tasmota32-knx] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_extra_dirs = libesp32, lib_basic, lib_div +lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_div [env:tasmota32-sensors] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_SENSORS -lib_extra_dirs = libesp32, lib_basic, lib_i2c, lib_rf, lib_div +lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div [env:tasmota32-display] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_DISPLAYS -lib_extra_dirs = libesp32, lib_basic, lib_display +lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_display [env:tasmota32-ir] extends = env:tasmota32 build_flags = ${common32.build_flags} ${irremoteesp_full.build_flags} -DFIRMWARE_IR -lib_extra_dirs = libesp32, lib_basic +lib_extra_dirs = lib/libesp32, lib/lib_basic [env:tasmota32-ircustom] extends = env:tasmota32 diff --git a/tasmota/tasmota.ino.cpp b/tasmota/tasmota.ino.cpp new file mode 100644 index 000000000..b3dc46cc3 --- /dev/null +++ b/tasmota/tasmota.ino.cpp @@ -0,0 +1,115375 @@ +# 1 "/tmp/tmpjvshbmyn" +#include +# 1 "/workspace/Tasmota/tasmota/tasmota.ino" +# 37 "/workspace/Tasmota/tasmota/tasmota.ino" +#include +#include "tasmota_compat.h" +#include "tasmota_version.h" +#include "tasmota.h" +#include "my_user_config.h" +#ifdef USE_TLS + #include +#endif +#include "tasmota_globals.h" +#include "i18n.h" +#include "tasmota_template.h" + + +#include +#include +#include +#include +#include +#ifdef USE_ARDUINO_OTA + #include + #ifndef USE_DISCOVERY + #define USE_DISCOVERY + #endif +#endif +#ifdef USE_DISCOVERY + #include +#endif + + #include + +#ifdef USE_SPI + #include +#endif + + +#include "settings.h" + + + + + +WiFiUDP PortUdp; + +unsigned long feature_drv1; +unsigned long feature_drv2; +unsigned long feature_sns1; +unsigned long feature_sns2; +unsigned long feature5; +unsigned long feature6; +unsigned long feature7; +unsigned long serial_polling_window = 0; +unsigned long state_second = 0; +unsigned long state_50msecond = 0; +unsigned long state_100msecond = 0; +unsigned long state_250msecond = 0; +unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; +unsigned long blink_timer = 0; +unsigned long backlog_delay = 0; +power_t power = 0; +power_t last_power = 0; +power_t blink_power; +power_t blink_mask = 0; +power_t blink_powersave; +power_t latching_power = 0; +power_t rel_inverted = 0; +int serial_in_byte_counter = 0; +int ota_state_flag = 0; +int ota_result = 0; +int restart_flag = 0; +int wifi_state_flag = WIFI_RESTART; +int blinks = 201; +uint32_t uptime = 0; +uint32_t loop_load_avg = 0; +uint32_t global_update = 0; +uint32_t web_log_index = 1; +uint32_t baudrate = APP_BAUDRATE; +float global_temperature_celsius = NAN; +float global_humidity = 0.0f; +float global_pressure_hpa = 0.0f; +uint16_t tele_period = 9999; +uint16_t blink_counter = 0; +uint16_t seriallog_timer = 0; +uint16_t syslog_timer = 0; +uint16_t gpio_pin[MAX_GPIO_PIN] = { 0 }; +int16_t save_data_counter; +RulesBitfield rules_flag; +uint8_t mqtt_cmnd_blocked = 0; +uint8_t mqtt_cmnd_blocked_reset = 0; +uint8_t state_250mS = 0; +uint8_t latching_relay_pulse = 0; +uint8_t ssleep; +uint8_t blinkspeed = 1; +uint8_t active_device = 1; +uint8_t leds_present = 0; +uint8_t led_inverted = 0; +uint8_t led_power = 0; +uint8_t ledlnk_inverted = 0; +uint8_t pwm_inverted = 0; +uint8_t energy_flg = 0; +uint8_t light_flg = 0; +uint8_t light_type = 0; +uint8_t serial_in_byte; +uint8_t ota_retry_counter = OTA_ATTEMPTS; +uint8_t devices_present = 0; +uint8_t masterlog_level = 0; +uint8_t seriallog_level; +uint8_t syslog_level; +uint8_t my_module_type; +uint8_t last_source = 0; +uint8_t shutters_present = 0; +uint8_t prepped_loglevel = 0; + +bool serial_local = false; +bool serial_buffer_overrun = false; +bool fallback_topic_flag = false; +bool backlog_mutex = false; +bool interlock_mutex = false; +bool stop_flash_rotate = false; +bool blinkstate = false; + +bool pwm_present = false; +bool i2c_flg = false; +bool spi_flg = false; +bool soft_spi_flg = false; +bool ntp_force_sync = false; +bool is_8285 = false; +bool skip_light_fade; +bool restart_halt = false; +myio my_module; +StateBitfield global_state; +char my_version[33]; +char my_image[33]; +char my_hostname[33]; +char mqtt_client[TOPSZ]; +char mqtt_topic[TOPSZ]; +char serial_in_buffer[INPUT_BUFFER_SIZE]; +char mqtt_data[MESSZ]; +char log_data[LOGSZ]; +char web_log[WEB_LOG_SIZE] = {'\0'}; +#ifdef SUPPORT_IF_STATEMENT + #include + LinkedList backlog; + #define BACKLOG_EMPTY (backlog.size() == 0) +#else + uint8_t backlog_index = 0; + uint8_t backlog_pointer = 0; + String backlog[MAX_BACKLOG]; + #define BACKLOG_EMPTY (backlog_pointer == backlog_index) +#endif +void setup(void); +void BacklogLoop(void); +void SleepDelay(uint32_t mseconds); +void loop(void); +uint16_t SendMail(char *buffer); +void xsend_message_txt(char *msg); +void attach_File(char *path); +void attach_Array(char *aname); +uint16_t SendMail(char *buffer); +void attach_Array(char *aname); +void send_message_txt(char *txt); +uint32_t GetRtcSettingsCrc(void); +void RtcSettingsSave(void); +void RtcSettingsLoad(void); +bool RtcSettingsValid(void); +uint32_t GetRtcRebootCrc(void); +void RtcRebootSave(void); +void RtcRebootReset(void); +void RtcRebootLoad(void); +bool RtcRebootValid(void); +void SetFlashModeDout(void); +bool VersionCompatible(void); +void SettingsBufferFree(void); +bool SettingsBufferAlloc(void); +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size); +uint16_t GetSettingsCrc(void); +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size); +uint32_t GetSettingsCrc32(void); +void SettingsSaveAll(void); +void UpdateQuickPowerCycle(bool update); +uint32_t GetSettingsTextLen(void); +bool SettingsUpdateFinished(void); +bool SettingsUpdateText(uint32_t index, const char* replace_me); +char* SettingsText(uint32_t index); +void UpdateBackwardCompatibility(void); +uint32_t GetSettingsAddress(void); +void SettingsSave(uint8_t rotate); +void SettingsLoad(void); +void EspErase(uint32_t start_sector, uint32_t end_sector); +void SettingsErase(uint8_t type); +void SettingsSdkErase(void); +void SettingsDefault(void); +void SettingsDefaultSet1(void); +void SettingsDefaultSet2(void); +void SettingsResetStd(void); +void SettingsResetDst(void); +void SettingsDefaultWebColor(void); +void SettingsEnableAllI2cDrivers(void); +void SettingsDelta(void); +void OsWatchTicker(void); +void OsWatchInit(void); +void OsWatchLoop(void); +bool OsWatchBlockedLoop(void); +uint32_t ResetReason(void); +String GetResetReason(void); +String GetBinary8(uint8_t value, size_t count); +size_t strchrspn(const char *str1, int character); +uint32_t ChrCount(const char *str, const char *delim); +char* subStr(char* dest, char* str, const char *delim, int index); +float CharToFloat(const char *str); +int TextToInt(char *str); +char* ulltoa(unsigned long long value, char *str, int radix); +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween); +char* Uint64toHex(uint64_t value, char *str, uint16_t bits); +char* dtostrfd(double number, unsigned char prec, char *s); +char* Unescape(char* buffer, uint32_t* size); +char* RemoveSpace(char* p); +char* RemoveControlCharacter(char* p); +char* ReplaceCommaWithDot(char* p); +char* LowerCase(char* dest, const char* source); +char* UpperCase(char* dest, const char* source); +char* UpperCase_P(char* dest, const char* source); +char* Trim(char* p); +char* NoAlNumToUnderscore(char* dest, const char* source); +char IndexSeparator(void); +void SetShortcutDefault(void); +uint8_t Shortcut(void); +bool ValidIpAddress(const char* str); +bool ParseIp(uint32_t* addr, const char* str); +uint32_t ParseParameters(uint32_t count, uint32_t *params); +bool NewerVersion(char* version_str); +char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option); +char* GetPowerDevice(char* dest, uint32_t idx, size_t size); +void GetEspHardwareType(void); +String GetDeviceHardware(void); +float ConvertTemp(float c); +float ConvertTempToCelsius(float c); +char TempUnit(void); +float ConvertHumidity(float h); +float CalcTempHumToDew(float t, float h); +float ConvertPressure(float p); +String PressureUnit(void); +float ConvertSpeed(float s); +String SpeedUnit(void); +void ResetGlobalValues(void); +uint32_t SqrtInt(uint32_t num); +uint32_t RoundSqrtInt(uint32_t num); +char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack); +int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack); +int GetStateNumber(char *state_text); +String GetSerialConfig(void); +void SetSerialBegin(void); +void SetSerialConfig(uint32_t serial_config); +void SetSerialBaudrate(uint32_t ubaudrate); +void SetSerial(uint32_t ubaudrate, uint32_t serial_config); +void ClaimSerial(void); +void SerialSendRaw(char *codes); +void SerialSendDecimal(char *values); +uint32_t GetHash(const char *buffer, size_t size); +void ShowSource(uint32_t source); +void WebHexCode(uint32_t i, const char* code); +uint32_t WebColor(uint32_t i); +char* ResponseGetTime(uint32_t format, char* time_str); +int Response_P(const char* format, ...); +int ResponseTime_P(const char* format, ...); +int ResponseAppend_P(const char* format, ...); +int ResponseAppendTimeFormat(uint32_t format); +int ResponseAppendTime(void); +int ResponseAppendTHD(float f_temperature, float f_humidity); +int ResponseJsonEnd(void); +int ResponseJsonEndEnd(void); +uint16_t GpioConvert(uint8_t gpio); +uint16_t Adc0Convert(uint8_t adc0); +void TemplateConvert(uint8_t template8[], uint16_t template16[]); +void ConvertGpios(void); +uint32_t ICACHE_RAM_ATTR Pin(uint32_t gpio, uint32_t index); +bool PinUsed(uint32_t gpio, uint32_t index); +uint32_t GetPin(uint32_t lpin); +void SetPin(uint32_t lpin, uint32_t gpio); +void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state); +uint8_t ModuleNr(void); +bool ValidTemplateModule(uint32_t index); +bool ValidModule(uint32_t index); +bool ValidTemplate(const char *search); +String AnyModuleName(uint32_t index); +String ModuleName(void); +void GetInternalTemplate(void* ptr, uint32_t module, uint32_t option); +void ModuleGpios(myio *gp); +gpio_flag ModuleFlag(void); +void ModuleDefault(uint32_t module); +void SetModuleType(void); +bool FlashPin(uint32_t pin); +uint32_t ValidPin(uint32_t pin, uint32_t gpio); +bool ValidGPIO(uint32_t pin, uint32_t gpio); +bool GetUsedInModule(uint32_t val, uint16_t *arr); +bool JsonTemplate(char* dataBuf); +void TemplateJson(void); +inline int32_t TimeDifference(uint32_t prev, uint32_t next); +int32_t TimePassedSince(uint32_t timestamp); +bool TimeReached(uint32_t timer); +void SetNextTimeInterval(unsigned long& timer, const unsigned long step); +int32_t TimePassedSinceUsec(uint32_t timestamp); +bool TimeReachedUsec(uint32_t timer); +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size); +bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg); +uint8_t I2cRead8(uint8_t addr, uint8_t reg); +uint16_t I2cRead16(uint8_t addr, uint8_t reg); +int16_t I2cReadS16(uint8_t addr, uint8_t reg); +uint16_t I2cRead16LE(uint8_t addr, uint8_t reg); +int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg); +int32_t I2cRead24(uint8_t addr, uint8_t reg); +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size); +bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val); +bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val); +int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); +int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); +void I2cScan(char *devs, unsigned int devs_len); +void I2cSetActiveFound(uint32_t addr, const char *types); +bool I2cActive(uint32_t addr); +bool I2cSetDevice(uint32_t addr); +void SetSeriallog(uint32_t loglevel); +void SetSyslog(uint32_t loglevel); +void GetLog(uint32_t idx, char** entry_pp, size_t* len_p); +void Syslog(void); +void AddLog(uint32_t loglevel); +void AddLog_P(uint32_t loglevel, const char *formatP); +void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2); +void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...); +void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...); +void AddLog_Debug(PGM_P formatP, ...); +void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count); +void AddLogSerial(uint32_t loglevel); +void AddLogMissed(const char *sensor, uint32_t misses); +void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size); +String Decompress(const char * compressed, size_t uncompressed_size); +uint32_t HwRandom(void); +void ButtonPullupFlag(uint8 button_bit); +void ButtonInvertFlag(uint8 button_bit); +void ButtonTouchFlag(uint8 button_bit); +void ButtonInit(void); +uint8_t ButtonSerial(uint8_t serial_in_byte); +void ButtonHandler(void); +void MqttButtonTopic(uint8_t button_id, uint8_t action, uint8_t hold); +void ButtonLoop(void); +void ResponseCmndNumber(int value); +void ResponseCmndFloat(float value, uint32_t decimals); +void ResponseCmndIdxNumber(int value); +void ResponseCmndChar_P(const char* value); +void ResponseCmndChar(const char* value); +void ResponseCmndStateText(uint32_t value); +void ResponseCmndDone(void); +void ResponseCmndIdxChar(const char* value); +void ResponseCmndAll(uint32_t text_index, uint32_t count); +void ExecuteCommand(const char *cmnd, uint32_t source); +void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len); +void CmndBacklog(void); +void CmndDelay(void); +void CmndPower(void); +void CmndStatus(void); +void CmndState(void); +void CmndTempOffset(void); +void CmndHumOffset(void); +void CmndGlobalTemp(void); +void CmndGlobalHum(void); +void CmndSleep(void); +void CmndUpgrade(void); +void CmndOtaUrl(void); +void CmndSeriallog(void); +void CmndRestart(void); +void CmndPowerOnState(void); +void CmndPulsetime(void); +void CmndBlinktime(void); +void CmndBlinkcount(void); +void CmndSavedata(void); +void CmndSetoption(void); +void CmndTemperatureResolution(void); +void CmndHumidityResolution(void); +void CmndPressureResolution(void); +void CmndPowerResolution(void); +void CmndVoltageResolution(void); +void CmndFrequencyResolution(void); +void CmndCurrentResolution(void); +void CmndEnergyResolution(void); +void CmndWeightResolution(void); +void CmndSpeedUnit(void); +void CmndModule(void); +void CmndModules(void); +void CmndGpio(void); +void ShowGpios(const uint16_t *NiceList, uint32_t size, uint32_t offset, uint32_t &lines); +void CmndGpios(void); +void CmndTemplate(void); +void CmndPwm(void); +void CmndPwmfrequency(void); +void CmndPwmrange(void); +void CmndButtonDebounce(void); +void CmndSwitchDebounce(void); +void CmndBaudrate(void); +void CmndSerialConfig(void); +void CmndSerialSend(void); +void CmndSerialDelimiter(void); +void CmndSyslog(void); +void CmndLoghost(void); +void CmndLogport(void); +void CmndIpAddress(void); +void CmndNtpServer(void); +void CmndAp(void); +void CmndSsid(void); +void CmndPassword(void); +void CmndHostname(void); +void CmndWifiConfig(void); +void CmndWifi(void); +void CmndDevicename(void); +void CmndFriendlyname(void); +void CmndSwitchMode(void); +void CmndInterlock(void); +void CmndTeleperiod(void); +void CmndReset(void); +void CmndTime(void); +void CmndTimezone(void); +void CmndTimeStdDst(uint32_t ts); +void CmndTimeStd(void); +void CmndTimeDst(void); +void CmndAltitude(void); +void CmndLedPower(void); +void CmndLedState(void); +void CmndLedMask(void); +void CmndLedPwmOff(void); +void CmndLedPwmOn(void); +void CmndLedPwmMode(void); +void CmndWifiPower(void); +void CmndI2cScan(void); +void CmndI2cDriver(void); +void CmndDevGroupName(void); +void CmndDevGroupSend(void); +void CmndDevGroupShare(void); +void CmndDevGroupStatus(void); +void CmndSensor(void); +void CmndDriver(void); +void CmndCpuFrequency(void); +void CmndTouchCal(void); +void CmndTouchThres(void); +void CmndTouchNum(void); +void resetPins(); +void HwWdtDisable(void); +void HwWdtEnable(void); +void WdtDisable(void); +void WdtEnable(void); +void CmndCrash(void); +void CmndWDT(void); +void CmndBlockedLoop(void); +void CrashDumpClear(void); +bool CrashFlag(void); +void CrashDump(void); +bool DeviceGroupItemShared(bool incoming, uint8_t item); +void DeviceGroupsInit(void); +void DeviceGroupsStart(); +void DeviceGroupsStop(); +void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct device_group_member * device_group_member, uint8_t * message, int message_length, bool received); +bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...); +void ProcessDeviceGroupMessage(uint8_t * message, int message_length); +void DeviceGroupStatus(uint8_t device_group_index); +void DeviceGroupsLoop(void); +void eeprom_writeBytes(uint32_t addr, uint32_t len, uint8_t *buff); +void eeprom_readBytes(uint32_t addr, uint32_t len, uint8_t *buff); +uint32_t eeprom_init(uint32_t size); +uint32_t eeprom_init(uint32_t size); +void eeprom_writeBytes(uint32_t addr, uint32_t len, uint8_t *buff); +void eeprom_readBytes(uint32_t addr, uint32_t len, uint8_t *buff); +uint32_t eeprom_init(uint32_t size); +void eeprom_writeBytes(uint32_t adr, uint32_t len, uint8_t *buf); +void eeprom_readBytes(uint32_t adr, uint32_t len, uint8_t *buf); +uint32_t ESP_ResetInfoReason(void); +String ESP_getResetReason(void); +uint32_t ESP_getChipId(void); +uint32_t ESP_getSketchSize(void); +uint32_t ESP_getFreeHeap(void); +uint32_t ESP_getMaxAllocHeap(void); +void ESP_Restart(void); +void NvmLoad(const char *sNvsName, const char *sName, void *pSettings, unsigned nSettingsLen); +void NvmSave(const char *sNvsName, const char *sName, const void *pSettings, unsigned nSettingsLen); +void NvmErase(const char *sNvsName); +void SettingsErase(uint8_t type); +void SettingsRead(void *data, size_t size); +void SettingsWrite(const void *pSettings, unsigned nSettingsLen); +void QPCRead(void *pSettings, unsigned nSettingsLen); +void QPCWrite(const void *pSettings, unsigned nSettingsLen); +void ZigbeeErase(void); +void ZigbeeRead(void *pSettings, unsigned nSettingsLen); +void ZigbeeWrite(const void *pSettings, unsigned nSettingsLen); +void SntpInit(); +uint32_t SntpGetCurrentTimestamp(void); +void CrashDump(void); +bool CrashFlag(void); +void CrashDumpClear(void); +void CmndCrash(void); +void CmndWDT(void); +void CmndBlockedLoop(void); +void DisableBrownout(void); +String ESP32GetResetReason(uint32_t cpu_no); +String ESP_getResetReason(void); +uint32_t ESP_ResetInfoReason(void); +uint32_t ESP_getChipId(void); +uint32_t ESP_getSketchSize(void); +uint32_t ESP_getFreeHeap(void); +uint32_t ESP_getMaxAllocHeap(void); +void ESP_Restart(void); +static bool spiflash_is_ready(void); +static void spi_write_enable(void); +bool EsptoolEraseSector(uint32_t sector); +void EsptoolErase(uint32_t start_sector, uint32_t end_sector); +void GetFeatures(void); +float fmodf(float x, float y); +double FastPrecisePow(double a, double b); +float FastPrecisePowf(const float x, const float y); +double TaylorLog(double x); +inline float sinf(float x); +inline float cosf(float x); +inline float tanf(float x); +inline float atanf(float x); +inline float asinf(float x); +inline float acosf(float x); +inline float sqrtf(float x); +inline float powf(float x, float y); +float cos_52s(float x); +float cos_52(float x); +float sin_52(float x); +float tan_56s(float x); +float tan_56(float x); +float atan_66s(float x); +float atan_66(float x); +float asinf1(float x); +float acosf1(float x); +float sqrt1(const float x); +uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, + uint16_t ito_min, uint16_t ito_max); +float ModulusRangef(float f, float a, float b); +float Polynomialf(const float *factors, uint32_t degree, float x); +static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len); +static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data); +void createBitmapFileHeader(uint32_t height, uint32_t width, uint8_t *fileHeader); +void createBitmapInfoHeader(uint32_t height, uint32_t width, uint8_t *infoHeader ); +void StartMdns(void); +void MqttDiscoverServer(void); +void MdnsAddServiceHttp(void); +void MdnsUpdate(void); +char* NetworkHostname(void); +IPAddress NetworkAddress(void); +String NetworkMacAddress(void); +bool RotaryButtonPressed(uint32_t button_index); +void ICACHE_RAM_ATTR RotaryIsrArgMiDesk(void *arg); +void ICACHE_RAM_ATTR RotaryIsrArg(void *arg); +void RotaryInit(void); +void RotaryHandler(void); +uint32_t UtcTime(void); +uint32_t LocalTime(void); +uint32_t Midnight(void); +bool MidnightNow(void); +bool IsDst(void); +String GetBuildDateAndTime(void); +String GetMinuteTime(uint32_t minutes); +String GetTimeZone(void); +String GetDuration(uint32_t time); +String GetDT(uint32_t time); +String GetDateAndTime(uint8_t time_type); +uint32_t UpTime(void); +uint32_t MinutesUptime(void); +String GetUptime(void); +uint32_t MinutesPastMidnight(void); +uint32_t RtcMillis(void); +void BreakTime(uint32_t time_input, TIME_T &tm); +uint32_t MakeTime(TIME_T &tm); +uint32_t RuleToTime(TimeRule r, int yr); +void RtcSecond(void); +void RtcSetTime(uint32_t epoch); +void RtcInit(void); +bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2); +String GetStatistics(void); +String GetStatistics(void); +void SwitchPullupFlag(uint16 switch_bit); +void SwitchSetVirtual(uint32_t index, uint8_t state); +uint8_t SwitchGetVirtual(uint32_t index); +uint8_t SwitchLastState(uint32_t index); +bool SwitchState(uint32_t index); +void SwitchProbe(void); +void SwitchInit(void); +void SwitchHandler(uint8_t mode); +void SwitchLoop(void); +char* Format(char* output, const char* input, int size); +char* GetOtaUrl(char *otaurl, size_t otaurl_size); +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic); +char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic); +char* GetFallbackTopic_P(char *stopic, const char* subtopic); +char* GetStateText(uint32_t state); +void SetLatchingRelay(power_t lpower, uint32_t state); +void SetDevicePower(power_t rpower, uint32_t source); +void RestorePower(bool publish_power, uint32_t source); +void SetAllPower(uint32_t state, uint32_t source); +void SetPowerOnState(void); +void UpdateLedPowerAll(); +void SetLedPowerIdx(uint32_t led, uint32_t state); +void SetLedPower(uint32_t state); +void SetLedPowerAll(uint32_t state); +void SetLedLink(uint32_t state); +void SetPulseTimer(uint32_t index, uint32_t time); +uint32_t GetPulseTimer(uint32_t index); +bool SendKey(uint32_t key, uint32_t device, uint32_t state); +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source); +void StopAllPowerBlink(void); +void MqttShowPWMState(void); +void MqttShowState(void); +void MqttPublishTeleState(void); +void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperature, float f_humidity); +bool MqttShowSensor(void); +void MqttPublishSensor(void); +void PerformEverySecond(void); +void Every100mSeconds(void); +void Every250mSeconds(void); +void ArduinoOTAInit(void); +void ArduinoOtaLoop(void); +void SerialInput(void); +void ResetPwm(void); +void GpioInit(void); +bool UdpDisconnect(void); +bool UdpConnect(void); +void PollUdp(void); +int WifiGetRssiAsQuality(int rssi); +bool WifiConfigCounter(void); +void WifiConfig(uint8_t type); +void WifiSetMode(WiFiMode_t wifi_mode); +void WiFiSetSleepMode(void); +void WifiBegin(uint8_t flag, uint8_t channel); +void WifiBeginAfterScan(void); +uint16_t WifiLinkCount(void); +String WifiDowntime(void); +void WifiSetState(uint8_t state); +bool WifiCheckIPv6(void); +String WifiGetIPv6(void); +bool WifiCheckIPAddrStatus(void); +void WifiCheckIp(void); +void WifiCheck(uint8_t param); +int WifiState(void); +String WifiGetOutputPower(void); +void WifiSetOutputPower(void); +void WifiConnect(void); +void EspRestart(void); +void stationKeepAliveNow(void); +void wifiKeepAlive(void); +static void WebGetArg(const char* arg, char* out, size_t max); +static bool WifiIsInManagerMode(); +void ShowWebSource(uint32_t source); +void ExecuteWebCommand(char* svalue, uint32_t source); +void StartWebserver(int type, IPAddress ipweb); +void StopWebserver(void); +void WifiManagerBegin(bool reset_only); +void PollDnsWebserver(void); +bool WebAuthenticate(void); +void HttpHeaderCors(void); +void WSHeaderSend(void); +void WSSend(int code, int ctype, const String& content); +void WSContentBegin(int code, int ctype); +void _WSContentSend(const String& content); +void WSContentFlush(void); +void _WSContentSendBuffer(void); +void WSContentSend_P(const char* formatP, ...); +void WSContentSend_PD(const char* formatP, ...); +void WSContentStart_P(const char* title, bool auth); +void WSContentStart_P(const char* title); +void WSContentSendStyle_P(const char* formatP, ...); +void WSContentSendStyle(void); +void WSContentButton(uint32_t title_index); +void WSContentSpaceButton(uint32_t title_index); +void WSContentSend_THD(const char *types, float f_temperature, float f_humidity); +void WSContentEnd(void); +void WSContentStop(void); +void WebRestart(uint32_t type); +void HandleWifiLogin(void); +void WebSliderColdWarm(void); +void HandleRoot(void); +bool HandleRootStatusRefresh(void); +int32_t IsShutterWebButton(uint32_t idx); +void HandleConfiguration(void); +void WSContentSendNiceLists(uint32_t option); +void WSContentSendAdcNiceList(uint32_t option); +void HandleTemplateConfiguration(void); +uint16_t WebGetGpioArg(uint32_t i); +void TemplateSaveSettings(void); +void HandleModuleConfiguration(void); +void ModuleSaveSettings(void); +String HtmlEscape(const String unescaped); +void HandleWifiConfiguration(void); +void WifiSaveSettings(void); +void HandleLoggingConfiguration(void); +void LoggingSaveSettings(void); +void HandleOtherConfiguration(void); +void OtherSaveSettings(void); +void HandleBackupConfiguration(void); +void HandleResetConfiguration(void); +void HandleRestoreConfiguration(void); +void HandleInformation(void); +void HandleUpgradeFirmware(void); +void HandleUpgradeFirmwareStart(void); +void HandleUploadDone(void); +void HandleUploadLoop(void); +void HandlePreflightRequest(void); +void HandleHttpCommand(void); +void HandleConsole(void); +void HandleConsoleRefresh(void); +void HandleNotFound(void); +bool CaptivePortal(void); +String UrlEncode(const String& text); +int WebSend(char *buffer); +bool JsonWebColor(const char* dataBuf); +void CmndEmulation(void); +void CmndSendmail(void); +void CmndWebServer(void); +void CmndWebPassword(void); +void CmndWeblog(void); +void CmndWebRefresh(void); +void CmndWebSend(void); +void CmndWebColor(void); +void CmndWebSensor(void); +void CmndWebButton(void); +void CmndCors(void); +bool Xdrv01(uint8_t function); +bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value); +void MakeValidMqtt(uint32_t option, char* str); +void MqttInit(void); +bool MqttIsConnected(void); +void MqttDisconnect(void); +void MqttSubscribeLib(const char *topic); +void MqttUnsubscribeLib(const char *topic); +bool MqttPublishLib(const char* topic, bool retained); +void MqttDumpData(char* topic, char* data, uint32_t data_len); +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len); +void MqttRetryCounter(uint8_t value); +void MqttSubscribe(const char *topic); +void MqttUnsubscribe(const char *topic); +void MqttPublishLogging(const char *mxtime); +void MqttPublish(const char* topic, bool retained); +void MqttPublish(const char* topic); +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained); +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic); +void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic, bool retained); +void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic); +void MqttPublishTeleSensor(void); +void MqttPublishPowerState(uint32_t device); +void MqttPublishAllPowerState(void); +void MqttPublishPowerBlinkState(uint32_t device); +uint16_t MqttConnectCount(void); +void MqttDisconnected(int state); +void MqttConnected(void); +void MqttReconnect(void); +void MqttCheck(void); +bool KeyTopicActive(uint32_t key); +void CmndMqttFingerprint(void); +void CmndMqttUser(void); +void CmndMqttPassword(void); +void CmndMqttlog(void); +void CmndMqttHost(void); +void CmndMqttPort(void); +void CmndMqttRetry(void); +void CmndStateText(void); +void CmndMqttClient(void); +void CmndFullTopic(void); +void CmndPrefix(void); +void CmndPublish(void); +void CmndGroupTopic(void); +void CmndTopic(void); +void CmndButtonTopic(void); +void CmndSwitchTopic(void); +void CmndButtonRetain(void); +void CmndSwitchRetain(void); +void CmndPowerRetain(void); +void CmndSensorRetain(void); +inline void TlsEraseBuffer(uint8_t *buffer); +void loadTlsDir(void); +void CmndTlsKey(void); +uint32_t bswap32(uint32_t x); +void CmndTlsDump(void); +void HandleMqttConfiguration(void); +void MqttSaveSettings(void); +bool Xdrv02(uint8_t function); +bool EnergyTariff1Active(); +void EnergyUpdateToday(void); +void EnergyUpdateTotal(float value, bool kwh); +void Energy200ms(void); +void EnergySaveState(void); +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag); +void EnergyMarginCheck(void); +void EnergyMqttShow(void); +void EnergyEverySecond(void); +void EnergyCommandCalResponse(uint32_t nvalue); +void CmndEnergyReset(void); +void CmndTariff(void); +void CmndPowerCal(void); +void CmndVoltageCal(void); +void CmndCurrentCal(void); +void CmndPowerSet(void); +void CmndVoltageSet(void); +void CmndCurrentSet(void); +void CmndFrequencySet(void); +void CmndModuleAddress(void); +void CmndPowerDelta(void); +void CmndPowerLow(void); +void CmndPowerHigh(void); +void CmndVoltageLow(void); +void CmndVoltageHigh(void); +void CmndCurrentLow(void); +void CmndCurrentHigh(void); +void CmndMaxPower(void); +void CmndMaxPowerHold(void); +void CmndMaxPowerWindow(void); +void CmndSafePower(void); +void CmndSafePowerHold(void); +void CmndSafePowerWindow(void); +void CmndMaxEnergy(void); +void CmndMaxEnergyStart(void); +void EnergySnsInit(void); +void EnergyShow(bool json); +bool Xdrv03(uint8_t function); +bool Xsns03(uint8_t function); +power_t LightPower(void); +uint8_t LightDevice(void); +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c); +void mat3x3(const float *mat33, const float *vec3, float *res3); +uint16_t change8to10(uint8_t v); +uint8_t change10to8(uint16_t v); +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr); +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr); +uint16_t ledGamma10_10(uint16_t v); +uint16_t ledGamma10(uint8_t v); +uint8_t ledGamma(uint8_t v); +void LightPwmOffset(uint32_t offset); +bool LightModuleInit(void); +void LightCalcPWMRange(void); +void LightInit(void); +void LightUpdateColorMapping(void); +uint8_t LightGetDimmer(uint8_t dimmer); +void LightSetDimmer(uint8_t dimmer); +void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri); +void LightGetXY(float *X, float *Y); +void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); +uint8_t LightGetBri(uint8_t device); +void LightSetBri(uint8_t device, uint8_t bri); +void LightColorOffset(int32_t offset); +bool LightColorTempOffset(int32_t offset); +void LightSetColorTemp(uint16_t ct); +uint16_t LightGetColorTemp(void); +void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value); +void LightPowerOn(void); +void ResponseLightState(uint8_t append); +void LightSetPaletteEntry(void); +void LightCycleColor(int8_t direction); +void LightSetPower(void); +void LightAnimate(void); +bool isChannelGammaCorrected(uint32_t channel); +bool isChannelCT(uint32_t channel); +uint16_t fadeGamma(uint32_t channel, uint16_t v); +uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg); +bool LightApplyFade(void); +void LightApplyPower(uint8_t new_color[LST_MAX], power_t power); +void LightSetOutputs(const uint16_t *cur_col_10); +void calcGammaMultiChannels(uint16_t cur_col_10[5]); +void calcGammaBulbs(uint16_t cur_col_10[5]); +void LightSendDeviceGroupStatus(bool status); +void LightHandleDevGroupItem(void); +bool LightColorEntry(char *buffer, uint32_t buffer_length); +void CmndSupportColor(void); +void CmndColor(void); +void CmndWhite(void); +void CmndChannel(void); +void CmndHsbColor(void); +void CmndScheme(void); +void CmndWakeup(void); +void CmndColorTemperature(void); +void LightDimmerOffset(uint32_t index, int32_t offset); +void CmndDimmer(void); +void CmndDimmerRange(void); +void CmndLedTable(void); +void CmndRgbwwTable(void); +void CmndFade(void); +void CmndSpeed(void); +void CmndWakeupDuration(void); +void CmndPalette(void); +void CmndSequenceOffset(void); +void CmndUndocA(void); +bool Xdrv04(uint8_t function); +void IrSendInit(void); +void IrReceiveUpdateThreshold(void); +void IrReceiveInit(void); +void IrReceiveCheck(void); +uint32_t IrRemoteCmndIrSendJson(void); +void CmndIrSend(void); +void IrRemoteCmndResponse(uint32_t error); +bool Xdrv05(uint8_t function); +void IrSendInit(void); +uint8_t reverseBitsInByte(uint8_t b); +uint64_t reverseBitsInBytes64(uint64_t b); +void IrReceiveUpdateThreshold(void); +void IrReceiveInit(void); +String sendIRJsonState(const struct decode_results &results); +void IrReceiveCheck(void); +String listSupportedProtocols(bool hvac); +bool strToBool(class JsonParserToken token, bool def); +uint32_t IrRemoteCmndIrHvacJson(void); +void CmndIrHvac(void); +uint32_t IrRemoteCmndIrSendJson(void); +uint32_t IrRemoteSendGC(char ** pp, uint32_t count, uint32_t repeat); +uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat); +uint32_t IrRemoteParseRawCompact(char * str, uint16_t * arr, size_t arr_len); +uint32_t IrRemoteSendRawStandard(char ** pp, uint32_t count, uint32_t repeat); +uint16_t parsqeFreq(char * str); +uint32_t IrRemoteCmndIrSendRaw(void); +void CmndIrSend(void); +void IrRemoteCmndResponse(uint32_t error); +bool Xdrv05(uint8_t function); +ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size); +ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size); +ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len); +ssize_t rf_decode_and_write(uint8_t *record, size_t size); +ssize_t rf_search_and_write(uint8_t *buf, size_t size); +uint8_t rf_erase_flash(void); +uint8_t SnfBrUpdateInit(void); +void SonoffBridgeReceivedRaw(void); +void SonoffBridgeLearnFailed(void); +void SonoffBridgeReceived(void); +bool SonoffBridgeSerialInput(void); +void SonoffBridgeSendCommand(uint8_t code); +void SonoffBridgeSendAck(void); +void SonoffBridgeSendCode(uint32_t code); +void SonoffBridgeSend(uint8_t idx, uint8_t key); +void SonoffBridgeLearn(uint8_t key); +void CmndRfBridge(void); +void CmndRfKey(void); +void CmndRfRaw(void); +bool Xdrv06(uint8_t function); +int DomoticzBatteryQuality(void); +int DomoticzRssiQuality(void); +void MqttPublishDomoticzFanState(void); +void DomoticzUpdateFanState(void); +void MqttPublishDomoticzPowerState(uint8_t device); +void DomoticzUpdatePowerState(uint8_t device); +void DomoticzMqttUpdate(void); +void DomoticzMqttSubscribe(void); +bool DomoticzMqttData(void); +void DomoticzSendSwitch(uint32_t type, uint32_t index, uint32_t state); +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg); +void DomoticzSendData(uint32_t sensor_idx, uint32_t idx, char *data); +void DomoticzSensor(uint8_t idx, char *data); +uint8_t DomoticzHumidityState(float h); +void DomoticzSensor(uint8_t idx, uint32_t value); +void DomoticzTempHumPressureSensor(float temp, float hum, float baro); +void DomoticzSensorPowerEnergy(int power, char *energy); +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power); +void CmndDomoticzIdx(void); +void CmndDomoticzKeyIdx(void); +void CmndDomoticzSwitchIdx(void); +void CmndDomoticzSensorIdx(void); +void CmndDomoticzUpdateTimer(void); +void CmndDomoticzSend(void); +void HandleDomoticzConfiguration(void); +void DomoticzSaveSettings(void); +bool Xdrv07(uint8_t function); +void SerialBridgeInput(void); +void SerialBridgeInit(void); +void CmndSSerialSend(void); +void CmndSBaudrate(void); +bool Xdrv08(uint8_t function); +uint32_t JulianDate(const struct TIME_T &now); +float InPi(float x); +float TimeFormula(float *DK, uint32_t Tdays); +void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down); +void ApplyTimerOffsets(Timer *duskdawn); +String GetSun(uint32_t dawn); +uint16_t SunMinutes(uint32_t dawn); +void TimerSetRandomWindow(uint32_t index); +void TimerSetRandomWindows(void); +void TimerEverySecond(void); +void PrepShowTimer(uint32_t index); +void CmndTimer(void); +void CmndTimers(void); +void CmndLongitude(void); +void CmndLatitude(void); +void HandleTimerConfiguration(void); +void TimerSaveSettings(void); +bool Xdrv09(uint8_t function); +inline bool IsRuleUncompressed(uint32_t idx); +inline bool IsRuleEmpty(uint32_t idx); +size_t GetRuleLen(uint32_t idx); +size_t GetRuleLenStorage(uint32_t idx); +void GetRule_decompress(String &rule, const char *rule_head); +String GetRule(uint32_t idx); +int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len); +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all_rules); +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr); +void RulesVarReplace(String &commands, const String &sfind, const String &replace); +bool RuleSetProcess(uint8_t rule_set, String &event_saved); +bool RulesProcessEvent(char *json_event); +bool RulesProcess(void); +void RulesInit(void); +void RulesEvery50ms(void); +void RulesEvery100ms(void); +void RulesEverySecond(void); +void RulesSaveBeforeRestart(void); +void RulesSetPower(void); +void RulesTeleperiod(void); +bool RulesMqttData(void); +void CmndSubscribe(void); +void CmndUnsubscribe(void); +bool findNextNumber(char * &pNumber, float &value); +bool findNextVariableValue(char * &pVarname, float &value); +bool findNextObjectValue(char * &pointer, float &value); +bool findNextOperator(char * &pointer, int8_t &op); +float calculateTwoValues(float v1, float v2, uint8_t op); +float evaluateExpression(const char * expression, unsigned int len); +void CmndIf(void); +bool evaluateComparisonExpression(const char *expression, int len); +bool findNextLogicOperator(char * &pointer, int8_t &op); +bool findNextLogicObjectValue(char * &pointer, bool &value); +bool evaluateLogicalExpression(const char * expression, int len); +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type); +void ExecuteCommandBlock(const char * commands, int len); +void ProcessIfStatement(const char* statements); +void RulesPreprocessCommand(char *pCommands); +void CmndRule(void); +void CmndRuleTimer(void); +void CmndEvent(void); +void CmndVariable(void); +void CmndMemory(void); +void CmndCalcResolution(void); +void CmndAddition(void); +void CmndSubtract(void); +void CmndMultiply(void); +void CmndScale(void); +float map_double(float x, float in_min, float in_max, float out_min, float out_max); +bool Xdrv10(uint8_t function); +void Script_ticker1_end(void); +void Script_ticker2_end(void); +void Script_ticker3_end(void); +void Script_ticker4_end(void); +void SaveFile(const char *name, const uint8_t *buf, uint32_t len); +void LoadFile(const char *name, uint8_t *buf, uint32_t len); +void f2char(float num, uint32_t dprec, uint32_t lzeros, char *nbuff); +void ScriptEverySecond(void); +void RulesTeleperiod(void); +int16_t Init_Scripter(void); +uint32_t get_fsinfo(uint32_t sel); +void form1000(uint32_t number, char *dp, char sc); +void Restart_globvars(void); +void Script_Stop_UDP(void); +void Script_Init_UDP(); +void Script_PollUdp(void); +void script_udp_sendvar(char *vname,float *fp,char *sp); +void ws2812_set_array(float *array ,uint32_t len, uint32_t offset); +float median_array(float *array, uint16_t len); +float Get_MFVal(uint8_t index, int16_t bind); +void Set_MFVal(uint8_t index, uint16_t bind, float val); +float Get_MFilter(uint8_t index); +void Set_MFilter(uint8_t index, float invar); +float DoMedian5(uint8_t index, float in); +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value); +void ICACHE_RAM_ATTR MP_Timer(void); +uint32_t MeasurePulseTime(int32_t in); +uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind); +uint16_t GetStack(void); +uint16_t GetStack(void); +void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize); +void toLog(const char *str); +void toLogN(const char *cp, uint8_t len); +void toLogEOL(const char *s1,const char *str); +void toSLog(const char *str); +void esp32_beep(int32_t freq ,uint32_t len); +int16_t Run_Scripter(const char *type, int8_t tlen, char *js); +int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo); +void ScripterEvery100ms(void); +void Scripter_save_pvars(void); +void script_upload_start(void); +void ScriptExecuteUploadSuccess(void); +void ListDir(char *path, uint8_t depth); +void Script_FileUploadConfiguration(void); +void ScriptFileUploadSuccess(void); +void script_upload(void); +uint8_t DownloadFile(char *file); +void HandleScriptTextareaConfiguration(void); +void HandleScriptConfiguration(void); +void SaveScript(void); +void ScriptSaveSettings(void); +void SaveScriptEnd(void); +void Script_HueStatus(String *response, uint16_t hue_devs); +void Script_Check_Hue(String *response); +void Script_Handle_Hue(String *path); +bool Script_SubCmd(void); +void execute_script(char *script); +bool ScriptCommand(void); +uint16_t xFAT_DATE(uint16_t year, uint8_t month, uint8_t day); +uint16_t xFAT_TIME(uint8_t hour, uint8_t minute, uint8_t second); +void dateTime(uint16_t* date, uint16_t* time); +bool ScriptMqttData(void); +String ScriptSubscribe(const char *data, int data_len); +String ScriptUnsubscribe(const char * data, int data_len); +void ScriptGetSDCard(void); +void SendFile(char *fname); +void ScriptFullWebpage(void); +void Script_Check_HTML_Setvars(void); +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen); +void ScriptWebShow(char mc); +void ScriptJsonAppend(void); +bool RulesProcessEvent(char *json_event); +void script_task1(void *arg); +void script_task2(void *arg); +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio); +void script_task1(void *arg); +void script_task2(void *arg); +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio); +uint32_t call2https(const char *host, const char *path); +void cpy2lf(char *dst, uint32_t dstlen, char *src); +bool Xdrv10(uint8_t function); +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ); +void KNX_DEL_GA( uint8_t GAnum ); +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ); +void KNX_DEL_CB( uint8_t CBnum ); +bool KNX_CONFIG_NOT_MATCH(void); +void KNXStart(void); +void KNX_INIT(void); +void KNX_CB_Action(message_t const &msg, void *arg); +void KnxUpdatePowerState(uint8_t device, power_t state); +void KnxSendButtonPower(void); +void KnxSensor(uint8_t sensor_type, float value); +void HandleKNXConfiguration(void); +void KNX_Save_Settings(void); +void CmndKnxTxCmnd(void); +void CmndKnxTxVal(void); +void CmndKnxTxScene(void); +void CmndKnxEnabled(void); +void CmndKnxEnhanced(void); +void CmndKnxPa(void); +void CmndKnxGa(void); +void CmndKnxCb(void); +bool Xdrv11(uint8_t function); +void HassDiscoveryRelays(struct HASS &Hass); +void NewHAssDiscovery(void); +void TryResponseAppend_P(const char *format, ...); +void HAssAnnounceRelayLight(void); +void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold, uint8_t single, uint8_t trg_start, uint8_t trg_end); +void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint8_t toggle, uint8_t pir); +void HAssAnnounceSwitches(void); +void HAssAnnounceButtons(void); +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, bool nested, const char* SubKey); +void HAssAnnounceSensors(void); +void HAssAnnounceShutters(void); +void HAssAnnounceDeviceInfoAndStatusSensor(void); +void HAssPublishStatus(void); +void HAssDiscovery(void); +void HAssDiscover(void); +void HAssAnyKey(void); +bool HAssMqttLWT(void); +void HassLwtSubscribe(bool hasslwt); +bool Xdrv12(uint8_t function); +void DisplayInit(uint8_t mode); +void DisplayClear(void); +void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); +void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); +void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); +void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); +void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawFrame(void); +void DisplaySetSize(uint8_t size); +void DisplaySetFont(uint8_t font); +void DisplaySetRotation(uint8_t rotation); +void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void DisplayOnOff(uint8_t on); +uint8_t fatoiv(char *cp,float *res); +uint8_t atoiv(char *cp, int16_t *res); +uint8_t atoiV(char *cp, uint16_t *res); +void alignright(char *string); +uint32_t decode_te(char *line); +void DisplayText(void); +void DisplayClearScreenBuffer(void); +void DisplayFreeScreenBuffer(void); +void DisplayAllocScreenBuffer(void); +void DisplayReAllocScreenBuffer(void); +void DisplayFillScreen(uint32_t line); +void DisplayClearLogBuffer(void); +void DisplayFreeLogBuffer(void); +void DisplayAllocLogBuffer(void); +void DisplayReAllocLogBuffer(void); +void DisplayLogBufferAdd(char* txt); +char* DisplayLogBuffer(char temp_code); +void DisplayLogBufferInit(void); +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value); +void DisplayAnalyzeJson(char *topic, char *json); +void DisplayMqttSubscribe(void); +bool DisplayMqttData(void); +void DisplayLocalSensor(void); +void DisplayInitDriver(void); +void DisplaySetPower(void); +void CmndDisplay(void); +void CmndDisplayModel(void); +void CmndDisplayWidth(void); +void CmndDisplayHeight(void); +void CmndDisplayMode(void); +void CmndDisplayDimmer(void); +void CmndDisplaySize(void); +void CmndDisplayFont(void); +void CmndDisplayRotate(void); +void CmndDisplayText(void); +void CmndDisplayAddress(void); +void CmndDisplayRefresh(void); +void CmndDisplayColumns(void); +void CmndDisplayRows(void); +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp); +void DrawAClock(uint16_t rad); +void ClrGraph(uint16_t num); +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol); +void DisplayCheckGraph(); +void Save_graph(uint8_t num, char *path); +void Restore_graph(uint8_t num, char *path); +void RedrawGraph(uint8_t num, uint8_t flags); +void AddGraph(uint8_t num,uint8_t val); +void AddValue(uint8_t num,float fval); +bool Touch_Init(TwoWire &i2c); +uint32_t Touch_Status(uint32_t sel); +void Touch_MQTT(uint8_t index, const char *cp); +void Touch_RDW_BUTT(uint32_t count, uint32_t pwr); +bool Xdrv13(uint8_t function); +uint16_t MP3_Checksum(uint8_t *array); +void MP3PlayerInit(void); +void MP3_CMD(uint8_t mp3cmd,uint16_t val); +bool MP3PlayerCmd(void); +bool Xdrv14(uint8_t function); +void PCA9685_Detect(void); +void PCA9685_Reset(void); +void PCA9685_SetPWMfreq(double freq); +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off); +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted); +bool PCA9685_Command(void); +void PCA9685_OutputTelemetry(bool telemetry); +bool Xdrv15(uint8_t function); +bool IsModuleTuya(void); +bool AsModuleTuyaMS(void); +bool IsTuyaFanCtrl(void); +bool TuyaModeSet(void); +uint8_t TuyaFanSpeeds(void); +uint8_t TuyaFanState(void); +void CmndTuyaSend(void); +void CmndTuyaMcu(void); +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId); +void UpdateDevices(); +inline bool TuyaFuncIdValid(uint8_t fnId); +uint8_t TuyaGetFuncId(uint8_t dpid); +uint8_t TuyaGetDpId(uint8_t fnId); +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value); +void TuyaSendBool(uint8_t id, bool value); +void TuyaSendValue(uint8_t id, uint32_t value); +void TuyaSendEnum(uint8_t id, uint32_t value); +void TuyaSendString(uint8_t id, char data[]); +bool TuyaSetPower(void); +bool TuyaSetChannels(void); +void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx); +void TuyaRequestState(uint8_t state_type); +void TuyaResetWifi(void); +void TuyaProcessStatePacket(void); +void TuyaLowPowerModePacketProcess(void); +void TuyaHandleProductInfoPacket(void); +void TuyaSendLowPowerSuccessIfNeeded(void); +void TuyaNormalPowerModePacketProcess(void); +bool TuyaModuleSelected(void); +void TuyaInit(void); +void TuyaSerialInput(void); +bool TuyaButtonPressed(void); +uint8_t TuyaGetTuyaWifiState(void); +void TuyaSetWifiLed(void); +void TuyaSetTime(void); +bool Xnrg32(uint8_t function); +bool Xdrv16(uint8_t function); +void RfReceiveCheck(void); +void RfInit(void); +void CmndRfSend(void); +bool Xdrv17(uint8_t function); +bool ArmtronixSetChannels(void); +void LightSerial2Duty(uint8_t duty1, uint8_t duty2); +void ArmtronixRequestState(void); +bool ArmtronixModuleSelected(void); +void ArmtronixInit(void); +void ArmtronixSerialInput(void); +void ArmtronixSetWifiLed(void); +bool Xdrv18(uint8_t function); +void PS16DZSerialSend(const char *tx_buffer); +void PS16DZSerialSendOk(void); +void PS16DZSerialSendUpdateCommand(void); +void PS16DZSerialInput(void); +bool PS16DZSerialSendUpdateCommandIfRequired(void); +void PS16DZInit(void); +bool PS16DZModuleSelected(void); +bool Xdrv19(uint8_t function); +String HueBridgeId(void); +String HueSerialnumber(void); +String HueUuid(void); +void HueRespondToMSearch(void); +String GetHueDeviceId(uint16_t id); +String GetHueUserId(void); +void HandleUpnpSetupHue(void); +void HueNotImplemented(String *path); +void HueConfigResponse(String *response); +void HueConfig(String *path); +uint8_t getLocalLightSubtype(uint8_t device); +void HueLightStatus1(uint8_t device, String *response); +bool HueActive(uint8_t device); +void HueLightStatus2(uint8_t device, String *response); +inline uint32_t findEchoGeneration(void); +void HueGlobalConfig(String *path); +void HueAuthentication(String *path); +void CheckHue(String * response, bool &appending); +void HueLightsCommand(uint8_t device, uint32_t device_id, String &response); +void HueLights(String *path); +void HueGroups(String *path); +void HandleHueApi(String *path); +bool Xdrv20(uint8_t function); +String WemoSerialnumber(void); +String WemoUuid(void); +void WemoRespondToMSearch(int echo_type); +void LogUpnpWithClient(const char *msg); +void HandleUpnpEvent(void); +void HandleUpnpService(void); +void HandleUpnpMetaService(void); +void HandleUpnpSetupWemo(void); +bool Xdrv21(uint8_t function); +bool IsModuleIfan(void); +uint8_t MaxFanspeed(void); +uint8_t GetFanspeed(void); +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence); +void SonoffIfanReceived(void); +bool SonoffIfanSerialInput(void); +void CmndFanspeed(void); +bool SonoffIfanInit(void); +void SonoffIfanUpdate(void); +bool Xdrv22(uint8_t function); +String getZDPStatusMessage(uint8_t status); +String getEmberStatus(uint8_t status); +String getZigbeeStatusMessage(uint8_t status); +int strcmp_PP(const char *p1, const char *p2); +uint16_t Z_GetLastDevice(void); +uint16_t Z_GetLastGroup(void); +uint16_t Z_GetLastCluster(void); +uint8_t Z_GetLastEndpoint(void); +void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response); +void HueLightStatus2Zigbee(uint16_t shortaddr, String *response); +void ZigbeeHueStatus(String * response, uint16_t shortaddr); +void ZigbeeCheckHue(String * response, bool &appending); +void ZigbeeHueGroups(String * lights); +void ZigbeeHuePower(uint16_t shortaddr, bool power); +void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer); +void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct); +void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y); +void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat); +void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response); +class SBuffer hibernateDevice(const struct Z_Device &device); +class SBuffer hibernateDevices(void); +void hydrateDevices(const SBuffer &buf); +void loadZigbeeDevices(void); +void saveZigbeeDevices(void); +void eraseZigbeeDevices(void); +uint8_t Z_getDatatypeLen(uint8_t t); +bool Z_isDiscreteDataType(uint8_t t); +uint16_t CxToCluster(uint8_t cx); +uint8_t ClusterToCx(uint16_t cluster); +int8_t CmToMultiplier(uint8_t cm); +const __FlashStringHelper* zigbeeFindAttributeById(uint16_t cluster, uint16_t attr_id, + uint8_t *attr_type, int8_t *multiplier); +uint8_t toPercentageCR2032(uint32_t voltage); +int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype); +void Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +bool Z_parseAttributeKey(class Z_attribute & attr); +void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +inline bool isXYZ(char c); +inline int8_t hexValue(char c); +void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz); +void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload); +bool convertTuyaSpecificCluster(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &buf); +const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd); +inline char hexDigit(uint32_t h); +String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z); +uint32_t ZigbeeAliasOrNumber(const char *state_text); +void ZNP_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h); +void EZ_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h, int8_t zb_txradio_dbm); +uint8_t ZigbeeGetInstructionSize(uint8_t instr); +void ZigbeeGotoLabel(uint8_t label); +void ZigbeeStateMachine_Run(void); +int32_t ZigbeeProcessInput(class SBuffer &buf); +uint8_t ZNP_RSSI2Lqi(int8_t rssi); +int32_t EZ_RSTACK(uint8_t reset_code); +int32_t EZ_ERROR(uint8_t error_code); +int32_t EZ_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf); +int32_t EZ_GetEUI64(int32_t res, class SBuffer &buf); +int32_t EZ_GetNodeId(int32_t res, class SBuffer &buf); +int32_t EZ_NetworkParameters(int32_t res, class SBuffer &buf); +int32_t EZ_CheckKeyNWK(int32_t res, class SBuffer &buf); +int32_t EZ_RouteError(int32_t res, const class SBuffer &buf); +int32_t EZ_PermitJoinRsp(int32_t res, const class SBuffer &buf); +void Z_PermitJoinDisable(void); +int32_t EZ_MessageSent(int32_t res, const class SBuffer &buf); +int32_t Z_EZSPGetEUI64(int32_t res, class SBuffer &buf); +int32_t Z_EZSPGetNodeId(int32_t res, class SBuffer &buf); +int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf); +int32_t ZNP_ReceiveDeviceInfo(int32_t res, class SBuffer &buf); +int32_t ZNP_CheckNVWrite(int32_t res, class SBuffer &buf); +int32_t ZNP_Reboot(int32_t res, class SBuffer &buf); +int32_t ZNP_ReceiveCheckVersion(int32_t res, class SBuffer &buf); +int32_t EZ_ReceiveCheckVersion(int32_t res, class SBuffer &buf); +int32_t EZ_Set_ResetConfig(uint8_t value); +int32_t EZ_GotoIfResetConfig(uint8_t value); +int32_t Z_SwitchDeviceType(int32_t res, class SBuffer &buf); +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match); +int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf); +int32_t ZNP_ReceiveNodeDesc(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf); +int32_t Z_ClusterToCxBinding(uint16_t cluster); +void Z_AutoBindDefer(uint16_t shortaddr, uint8_t endpoint, const SBuffer &buf, + size_t in_index, size_t in_len, size_t out_index, size_t out_len); +int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf); +int32_t ZNP_DataConfirm(int32_t res, const class SBuffer &buf); +int32_t ZNP_ReceiveStateChange(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf); +int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf); +int32_t Z_BindRsp(int32_t res, const class SBuffer &buf); +int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf); +int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf); +int32_t EZ_ParentAnnceRsp(int32_t res, const class SBuffer &buf, bool rsp); +void Z_SendIEEEAddrReq(uint16_t shortaddr); +void Z_SendActiveEpReq(uint16_t shortaddr); +void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +void Z_SendDeviceInfoRequest(uint16_t shortaddr); +void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +void Z_AutoBind(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf); +void Z_IncomingMessage(class ZCLFrame &zcl_received); +void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload, size_t payload_len); +int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf); +int32_t EZ_Reset_Device(uint8_t value); +int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf); +void Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +int32_t ZNP_Reset_Device(uint8_t value); +int32_t ZNP_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf); +int32_t ZNP_Recv_Default(int32_t res, const class SBuffer &buf); +int32_t Z_Load_Devices(uint8_t value); +void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms); +int32_t Z_Query_Bulbs(uint8_t value); +int32_t Z_State_Ready(uint8_t value); +bool Z_LedStatusSet(bool onoff); +void ZigbeeInputLoop(void); +void ZigbeeInitSerial(void); +void ZigbeeZNPFlush(void); +void ZigbeeZNPSend(const uint8_t *msg, size_t len); +void CmndZbZNPSendOrReceive(bool send); +void CmndZbZNPReceive(void); +void CmndZbZNPSend(void); +void ZigbeeEZSPSend_Out(uint8_t out_byte); +void ZigbeeEZSPSendRaw(const uint8_t *msg, size_t len, bool send_cancel); +void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len); +void ZigbeeEZSPSendDATA_frm(bool send_cancel, uint8_t to_frm, uint8_t from_ack); +void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len); +int32_t ZigbeeProcessInputEZSP(class SBuffer &buf); +void EZSP_HandleAck(uint8_t new_ack); +int32_t ZigbeeProcessInputRaw(class SBuffer &buf); +void CmndZbEZSPSendOrReceive(bool send); +void CmndZbEZSPReceive(void); +void CmndZbEZSPSend(void); +void ZigbeeOutputLoop(void); +uint32_t ZigbeeUploadFlashStart(void); +uint32_t ZigbeeUploadAvailable(void); +char ZigbeeUploadFlashRead(void); +void XModemOutputByte(uint8_t out_char); +char XModemWaitACK(void); +bool XModemSendPacket(uint32_t packet_no); +void ZigbeeUploadSetSoftwareBootloader(); +void ZigbeeUploadSetBootloader(uint8_t state); +bool ZigbeeUploadBootloaderPrompt(void); +bool ZigbeeUploadXmodem(void); +bool ZigbeeUploadOtaReady(void); +bool ZigbeeUploadFinish(void); +uint8_t ZigbeeUploadInit(void); +bool ZigbeeUploadWriteBuffer(uint8_t *buf, size_t size); +void ZigbeeUploadDone(void); +void HandleZigbeeXfer(void); +void ZigbeeInit(void); +void CmndZbReset(void); +void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, + uint16_t cluster, uint8_t cmd, const char *param); +void ZbApplyMultiplier(double &val_d, int8_t multiplier); +bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr, uint8_t transid); +bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok); +void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZigbeeZCLSendMessage & packet); +void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf); +void ZbSendRead(JsonParserToken val_attr, ZigbeeZCLSendMessage & packet); +void CmndZbSend(void); +void ZbBindUnbind(bool unbind); +void CmndZbBind(void); +void CmndZbUnbind(void); +void CmndZbBindState(void); +void CmndZbProbe(void); +void CmndZbProbeOrPing(boolean probe); +void CmndZbPing(void); +void CmndZbName(void); +void CmndZbModelId(void); +void CmndZbLight(void); +void CmndZbForget(void); +void CmndZbSave(void); +void CmndZbRestore(void); +void CmndZbPermitJoin(void); +void CmndZbEZSPListen(void); +void ZigbeeGlowPermitJoinLight(void); +void CmndZbStatus(void); +bool parseDeviceInnerData(class Z_Device & device, JsonParserObject root); +void CmndZbData(void); +void CmndZbConfig(void); +void ZigbeeShow(bool json); +bool Xdrv23(uint8_t function); +void BuzzerSet(uint8_t state); +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode); +void BuzzerSetStateToLed(uint32_t state); +void BuzzerBeep(uint32_t count); +void BuzzerEnabledBeep(uint32_t count, uint32_t duration); +bool BuzzerPinState(void); +void BuzzerInit(void); +void BuzzerEvery100mSec(void); +void CmndBuzzer(void); +bool Xdrv24(uint8_t function); +void A4988Init(void); +void CmndDoMove(void); +void CmndDoRotate(void); +void CmndDoTurn(void); +void CmndSetMIS(void); +void CmndSetSPR(void); +void CmndSetRPM(void); +bool Xdrv25(uint8_t function); +void AriluxRfInterrupt(void); +void AriluxRfHandler(void); +void AriluxRfInit(void); +void AriluxRfDisable(void); +bool Xdrv26(uint8_t function); +void ShutterLogPos(uint32_t i); +void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source); +void ShutterUpdateVelocity(uint8_t i); +void ShutterRtc50mS(void); +int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index); +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index); +void ShutterInit(void); +void ShutterReportPosition(bool always, uint32_t index); +void ShutterLimitRealAndTargetPositions(uint32_t i); +void ShutterCalculateAccelerator(uint8_t i); +void ShutterDecellerateForStop(uint8_t i); +void ShutterPowerOff(uint8_t i); +void ShutterUpdatePosition(void); +bool ShutterState(uint32_t device); +void ShutterAllowPreStartProcedure(uint8_t i); +void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos); +int32_t ShutterCalculatePosition(uint32_t i); +void ShutterRelayChanged(void); +bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index); +void ShutterButtonHandler(void); +void ShutterSetPosition(uint32_t device, uint32_t position); +void ShutterToggle(bool dir); +void CmndShutterOpen(void); +void CmndShutterStopOpen(void); +void CmndShutterClose(void); +void CmndShutterStopClose(void); +void CmndShutterToggle(void); +void CmndShutterToggleDir(void); +void CmndShutterStopToggle(void); +void CmndShutterStopToggleDir(void); +void CmndShutterStop(void); +void CmndShutterIncDec(void); +void CmndShutterPosition(void); +void CmndShutterStopPosition(void); +void CmndShutterOpenTime(void); +void CmndShutterCloseTime(void); +void CmndShutterMotorDelay(void); +void CmndShutterMode(void); +void CmndShutterRelay(void); +void CmndShutterButton(void); +void CmndShutterSetHalfway(void); +void CmndShutterFrequency(void); +void CmndShutterSetClose(void); +void CmndShutterSetOpen(void); +void CmndShutterPwmRange(void); +void CmndShutterCalibration(void); +void ShutterOptionsSetHelper(uint16_t option); +void CmndShutterInvert(void); +void CmndShutterLock(void); +void CmndShutterEnableEndStopTime(void); +void CmndShutterInvertWebButtons(void); +bool Xdrv27(uint8_t function); +void Pcf8574SwitchRelay(void); +void Pcf8574Init(void); +void HandlePcf8574(void); +void Pcf8574SaveSettings(void); +bool Xdrv28(uint8_t function); +bool DeepSleepEnabled(void); +void DeepSleepReInit(void); +void DeepSleepPrepare(void); +void DeepSleepStart(void); +void DeepSleepEverySecond(void); +void CmndDeepsleepTime(void); +bool Xdrv29(uint8_t function); +uint8_t crc8(const uint8_t *p, uint8_t len); +void ExsSendCmd(uint8_t cmd, uint8_t value); +void ExsSetPower(uint8_t device, uint8_t power); +void ExsSetBri(uint8_t device, uint8_t bri); +void ExsSyncState(uint8_t device); +bool ExsSyncState(); +void ExsDebugState(); +void ExsPacketProcess(void); +bool ExsModuleSelected(void); +bool ExsSetChannels(void); +bool ExsSetPower(void); +void EsxMcuStart(void); +void ExsInit(void); +void ExsSerialInput(void); +void CmndExsDimm(void); +void CmndExsDimmTbl(void); +void CmndExsDimmVal(void); +void CmndExsDimms(void); +void CmndExsChLock(void); +void CmndExsState(void); +bool Xdrv30(uint8_t function); +uint32_t TasmotaClient_FlashStart(void); +uint8_t TasmotaClient_UpdateInit(void); +void TasmotaClient_Reset(void); +uint8_t TasmotaClient_waitForSerialData(int dataCount, int timeout); +uint8_t TasmotaClient_sendBytes(uint8_t* bytes, int count); +uint8_t TasmotaClient_execCmd(uint8_t cmd); +uint8_t TasmotaClient_execParam(uint8_t cmd, uint8_t* params, int count); +uint8_t TasmotaClient_exitProgMode(void); +uint8_t TasmotaClient_SetupFlash(void); +uint8_t TasmotaClient_loadAddress(uint8_t adrHi, uint8_t adrLo); +void TasmotaClient_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data); +void TasmotaClient_Flash(void); +void TasmotaClient_SetFlagFlashing(bool value); +bool TasmotaClient_GetFlagFlashing(void); +void TasmotaClient_WriteBuffer(uint8_t *buf, size_t size); +void TasmotaClient_Init(void); +bool TasmotaClient_Available(void); +void TasmotaClient_Show(void); +void TasmotaClient_sendCmnd(uint8_t cmnd, uint8_t param); +void CmndClientReset(void); +void CmndClientSend(void); +void TasmotaClient_ProcessIn(void); +bool Xdrv31(uint8_t function); +void HotPlugInit(void); +void HotPlugEverySecond(void); +void CmndHotPlugTime(void); +bool Xdrv32(uint8_t function); +bool NRF24initRadio(); +bool NRF24Detect(void); +bool Xdrv33(uint8_t function); +void WMotorV1Detect(void); +void WMotorV1Reset(void); +void WMotorV1SetFrequency(uint32_t freq); +void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val); +bool WMotorV1Command(void); +bool Xdrv34(uint8_t function); +void PWMModulePreInit(void); +void PWMDimmerSetBrightnessLeds(int32_t bri); +void PWMDimmerSetPoweredOffLed(void); +void PWMDimmerSetPower(void); +void PWMDimmerHandleDevGroupItem(void); +void PWMDimmerHandleButton(uint32_t button_index, bool pressed); +void CmndBriPreset(void); +void CmndPWMDimmerPWMs(void); +bool Xdrv35(uint8_t function); +void CmdSet(void); +void GenerateDeviceCryptKey(); +void CmdSendButton(void); +void SendBit(byte bitToSend); +void CmndSendRaw(void); +void enterrx(); +void entertx(); +void SendSyncPreamble(int l); +void CreateKeeloqPacket(); +void KeeloqInit(); +bool Xdrv36(uint8_t function); +void SonoffD1Received(void); +bool SonoffD1SerialInput(void); +void SonoffD1Send(); +bool SonoffD1SendPower(void); +bool SonoffD1SendDimmer(void); +bool SonoffD1ModuleSelected(void); +bool Xdrv37(uint8_t function); +void PingResponsePoll(void); +void CmndPing(void); +bool Xdrv38(uint8_t function); +void ThermostatInit(uint8_t ctr_output); +bool ThermostatMinuteCounter(uint8_t ctr_output); +inline bool ThermostatSwitchIdValid(uint8_t switchId); +inline bool ThermostatRelayIdValid(uint8_t relayId); +uint8_t ThermostatInputStatus(uint8_t input_switch); +uint8_t ThermostatOutputStatus(uint8_t output_switch); +int16_t ThermostatCelsiusToFahrenheit(const int32_t deg, uint8_t conv_type); +int16_t ThermostatFahrenheitToCelsius(const int32_t deg, uint8_t conv_type); +void ThermostatSignalPreProcessingSlow(uint8_t ctr_output); +void ThermostatSignalPostProcessingSlow(uint8_t ctr_output); +void ThermostatSignalProcessingFast(uint8_t ctr_output); +void ThermostatCtrState(uint8_t ctr_output); +void ThermostatHybridCtrPhase(uint8_t ctr_output); +bool ThermostatStateAutoToManual(uint8_t ctr_output); +bool ThermostatStateManualToAuto(uint8_t ctr_output); +void ThermostatEmergencyShutdown(uint8_t ctr_output); +void ThermostatState(uint8_t ctr_output); +void ThermostatOutputRelay(uint8_t ctr_output, uint32_t command); +void ThermostatCalculatePI(uint8_t ctr_output); +void ThermostatWorkAutomaticPI(uint8_t ctr_output); +void ThermostatWorkAutomaticRampUp(uint8_t ctr_output); +void ThermostatPeakDetectorInit(uint8_t ctr_output); +void ThermostatPeakDetector(uint8_t ctr_output); +void ThermostatAutotuneParamCalc(uint8_t ctr_output); +void ThermostatWorkAutomaticPIAutotune(uint8_t ctr_output); +void ThermostatCtrWork(uint8_t ctr_output); +void ThermostatWork(uint8_t ctr_output); +void ThermostatDiagnostics(uint8_t ctr_output); +void ThermostatController(uint8_t ctr_output); +bool ThermostatTimerArm(uint8_t ctr_output, int16_t tempVal); +void ThermostatTimerDisarm(uint8_t ctr_output); +void ThermostatVirtualSwitch(uint8_t ctr_output); +void ThermostatVirtualSwitchCtrState(uint8_t ctr_output); +void ThermostatDebug(uint8_t ctr_output); +void ThermostatGetLocalSensor(uint8_t ctr_output); +void CmndThermostatModeSet(void); +void CmndClimateModeSet(void); +void CmndTempFrostProtectSet(void); +void CmndControllerModeSet(void); +void CmndInputSwitchSet(void); +void CmndInputSwitchUse(void); +void CmndSensorInputSet(void); +void CmndOutputRelaySet(void); +void CmndTimeAllowRampupSet(void); +void CmndTempFormatSet(void); +void CmndTempMeasuredSet(void); +void CmndTempTargetSet(void); +void CmndTempMeasuredGrdRead(void); +void CmndStateEmergencySet(void); +void CmndTimeManualToAutoSet(void); +void CmndPropBandSet(void); +void CmndTimeResetSet(void); +void CmndTimePiProportRead(void); +void CmndTimePiIntegrRead(void); +void CmndTimePiCycleSet(void); +void CmndTempAntiWindupResetSet(void); +void CmndTempHystSet(void); +void CmndPerfLevelAutotune(void); +void CmndTimeMaxActionSet(void); +void CmndTimeMinActionSet(void); +void CmndTimeSensLostSet(void); +void CmndTimeMinTurnoffActionSet(void); +void CmndTempRupDeltInSet(void); +void CmndTempRupDeltOutSet(void); +void CmndTimeRampupMaxSet(void); +void CmndTimeRampupCycleSet(void); +void CmndTempRampupPiAccErrSet(void); +void CmndDiagnosticModeSet(void); +void CmndCtrDutyCycleRead(void); +void CmndEnableOutputSet(void); +bool Xdrv39(uint8_t function); +bool TelegramInit(void); +String TelegramConnectToTelegram(String command); +void TelegramGetUpdates(uint32_t offset); +bool TelegramSendMessage(uint32_t chat_id, String text); +String TelegramExecuteCommand(const char *svalue); +void TelegramLoop(void); +void CmndTmState(void); +void CmndTmPoll(void); +void CmndTmToken(void); +void CmndTmChatId(void); +void CmndTmSend(void); +bool Xdrv40(uint8_t function); +void TCPLoop(void); +void TCPInit(void); +void CmndTCPStart(void); +void CmndTCPBaudrate(void); +bool Xdrv41(uint8_t function); +void I2S_Init(void); +void mp3_task(void *arg); +void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str); +void StatusCallback(void *cbData, int code, const char *string); +void Webradio(const char *url); +void mp3_task2(void *arg); +void StopPlaying(); +void Cmd_WebRadio(void); +void I2S_WR_Show(void); +void Play_mp3(const char *path); +void mp3_delete(void); +void Say(char *text); +void Cmd_Play(void); +void Cmd_Gain(void); +void Cmd_Say(void); +void Cmd_Time(void); +bool Xdrv42(uint8_t function); +void MLX90640UpdateGUI(void); +void MLX90640HandleWebGuiResponse(void); +void MLX90640HandleWebGui(void); +bool MLX90640Cmd(void); +void MLX90640init(); +void MLX90640every100msec(); +void MLX90640Show(uint8_t json); +bool Xdrv43(uint8_t function); +static uint8_t +miel_hvac_write(struct miel_hvac_softc *sc, const uint8_t *bytes, size_t len); +static void +miel_hvac_send(struct miel_hvac_softc *sc, uint8_t type, + const void *data, size_t len); +static void +miel_hvac_request(struct miel_hvac_softc *sc, uint8_t type); +static void +miel_hvac_init_update(struct miel_hvac_msg_update *update); +static bool +miel_hvac_set_power(struct miel_hvac_softc *sc); +static void +miel_hvac_respond_unsupported(void); +static void +miel_hvac_cmnd_setfanspeed(void); +static void +miel_hvac_cmnd_setmode(void); +static void +miel_hvac_cmnd_sethamode(void); +static void +miel_hvac_cmnd_settemp(void); +static void +miel_hvac_cmnd_setvane(void); +static void +miel_hvac_cmnd_setwidevane(void); +static void +miel_hvac_cmnd_remotetemp(void); +static void +miel_hvac_cmnd_request(void); +static void +miel_hvac_log_bytes(struct miel_hvac_softc *sc, const char *name, + const void *buf, size_t len); +static void +miel_hvac_input_connected(struct miel_hvac_softc *sc, + const void *buf, size_t len); +static void +miel_hvac_publish_settings(struct miel_hvac_softc *sc); +static void +miel_hvac_input_settings(struct miel_hvac_softc *sc, + const struct miel_hvac_data *d); +static void +miel_hvac_data_response(struct miel_hvac_softc *sc, + const struct miel_hvac_data *d); +static void +miel_hvac_input_sensor(struct miel_hvac_softc *sc, struct miel_hvac_data *dst, + const struct miel_hvac_data *src); +static void +miel_hvac_input_data(struct miel_hvac_softc *sc, + const void *buf, size_t len); +static void +miel_hvac_input_updated(struct miel_hvac_softc *sc, + const void *buf, size_t len); +static void +miel_hvac_pre_init(void); +static void +miel_hvac_loop(struct miel_hvac_softc *sc); +static void +miel_hvac_sensor(struct miel_hvac_softc *sc); +static void +miel_hvac_connect(struct miel_hvac_softc *sc); +static void +miel_hvac_tick(struct miel_hvac_softc *sc); +bool Xdrv44(uint8_t function); +bool WcPinUsed(void); +uint32_t WcSetup(int32_t fsiz); +int32_t WcSetOptions(uint32_t sel, int32_t value); +uint32_t WcGetWidth(void); +uint32_t WcGetHeight(void); +uint32_t WcSetMotionDetect(int32_t value); +void WcDetectMotion(void); +void fd_init(void); +uint32_t WcSetFaceDetect(int32_t value); +uint32_t WcGetPicstore(int32_t num, uint8_t **buff); +uint32_t WcGetFrame(int32_t bnum); +void HandleImage(void); +void HandleImageBasic(void); +void HandleWebcamMjpeg(void); +void HandleWebcamMjpegTask(void); +void HandleWebcamRoot(void); +uint32_t WcSetStreamserver(uint32_t flag); +void WcStreamControl(); +void WcLoop(void); +void WcPicSetup(void); +void WcShowStream(void); +void WcInit(void); +void CmndWebcam(void); +void CmndWebcamStream(void); +void CmndWebcamResolution(void); +void CmndWebcamMirror(void); +void CmndWebcamFlip(void); +void CmndWebcamSaturation(void); +void CmndWebcamBrightness(void); +void CmndWebcamContrast(void); +void CmndWebcamInit(void); +bool Xdrv81(uint8_t function); +void EthernetEvent(WiFiEvent_t event); +void EthernetInit(void); +IPAddress EthernetLocalIP(void); +char* EthernetHostname(void); +String EthernetMacAddress(void); +void CmndEthernet(void); +void CmndEthAddress(void); +void CmndEthType(void); +void CmndEthClockMode(void); +bool Xdrv82(uint8_t function); +void TTGO_Init(void); +void initPower(void); +static uint8_t axpWriteBytes(uint8_t devAddress, uint8_t regAddress, uint8_t *data, uint8_t len); +static uint8_t axpReadBytes(uint8_t devAddress, uint8_t regAddress, uint8_t *data, uint8_t len); +void TTGO_GetADC(void); +void TTGO_WebShow(uint32_t json); +void TTGO_audio_power(bool power); +void TTGO_LightSleep(void); +void TTGO_Sleep(int32_t stime); +void TTGO_loop(uint32_t flg); +bool TTGO_doubleclick(void); +bool TTGO_button(void); +bool Xdrv83(uint8_t function); +void ExceptionTest(uint8_t type); +void CpuLoadLoop(void); +void DebugFreeMem(void); +void DebugFreeMem(void); +void DebugRtcDump(char* parms); +void DebugCfgDump(char* parms); +void DebugCfgPeek(char* parms); +void DebugCfgPoke(char* parms); +void SetFlashMode(uint8_t mode); +void CmndHelp(void); +void CmndRtcDump(void); +void CmndCfgDump(void); +void CmndCfgPeek(void); +void CmndCfgPoke(void); +void CmndCfgXor(void); +void CmndException(void); +void CmndCpuCheck(void); +void CmndSerBufSize(void); +void CmndFreemem(void); +void CmndSetSensor(void); +void CmndFlashMode(void); +uint32_t DebugSwap32(uint32_t x); +void CmndFlashDump(void); +void CmndI2cWrite(void); +void CmndI2cRead(void); +void CmndI2cStretch(void); +void CmndI2cClock(void); +bool Xdrv99(uint8_t function); +void XsnsDriverState(void); +bool XdrvRulesProcess(void); +void ShowFreeMem(const char *where); +bool XdrvCallDriver(uint32_t driver, uint8_t Function); +bool XdrvCall(uint8_t Function); +void LcdInitMode(void); +void LcdInit(uint8_t mode); +void LcdInitDriver(void); +void LcdDrawStringAt(void); +void LcdDisplayOnOff(); +void LcdCenter(uint8_t row, char* txt); +bool LcdPrintLog(void); +void LcdTime(void); +void LcdRefresh(void); +bool Xdsp01(uint8_t function); +void SSD1306InitDriver(void); +void Ssd1306PrintLog(void); +void Ssd1306Time(void); +void Ssd1306Refresh(void); +bool Xdsp02(byte function); +void MatrixWrite(void); +void MatrixClear(void); +void MatrixFixed(char* txt); +void MatrixCenter(char* txt); +void MatrixScrollLeft(char* txt, int loop); +void MatrixScrollUp(char* txt, int loop); +void MatrixInitMode(void); +void MatrixInit(uint8_t mode); +void MatrixInitDriver(void); +void MatrixOnOff(void); +void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void MatrixPrintLog(uint8_t direction); +void MatrixRefresh(void); +bool Xdsp03(uint8_t function); +bool Ili9341Header(void); +void Ili9341InitMode(void); +void Ili9341Init(uint8_t mode); +void Ili9341InitDriver(void); +void Ili9341Clear(void); +void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void Ili9341DisplayOnOff(); +void Ili9341PrintLog(void); +void Ili9341Refresh(void); +bool Xdsp04(uint8_t function); +void EpdInitDriver29(); +void EpdPrintLog29(void); +void EpdRefresh29(void); +bool Xdsp05(uint8_t function); +void EpdInitDriver42(); +void EpdRefresh42(); +bool Xdsp06(uint8_t function); +void SH1106InitDriver(); +void SH1106PrintLog(void); +void SH1106Time(void); +void SH1106Refresh(void); +bool Xdsp07(uint8_t function); +void ILI9488_InitDriver(); +void ILI9488_RotConvert(int16_t *x, int16_t *y); +void ILI9488_CheckTouch(void); +bool Xdsp08(uint8_t function); +void SSD1351_InitDriver(); +void SSD1351PrintLog(void); +void SSD1351Time(void); +void SSD1351Refresh(void); +bool Xdsp09(uint8_t function); +void RA8876_InitDriver(); +void RA8876_RotConvert(int16_t *x, int16_t *y); +void RA8876_CheckTouch(void); +bool Xdsp10(uint8_t function); +void SevensegWrite(void); +void SevensegClear(void); +void SevensegInitMode(void); +void SevensegInit(uint8_t mode); +void SevensegInitDriver(void); +void SevensegOnOff(void); +void SevensegDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void SevensegTime(boolean time_24); +void SevensegRefresh(void); +bool Xdsp11(uint8_t function); +void ST7789_InitDriver(); +void ST7789_RotConvert(int16_t *x, int16_t *y); +void ST7789_CheckTouch(); +bool Xdsp12(uint8_t function); +uint8_t XdspPresent(void); +bool XdspCall(uint8_t Function); +void Ws2812StripShow(void); +int mod(int a, int b); +void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset); +void Ws2812UpdateHand(int position, uint32_t index); +void Ws2812Clock(void); +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i); +void Ws2812Gradient(uint32_t schemenr); +void Ws2812Bars(uint32_t schemenr); +void Ws2812Clear(void); +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white); +char* Ws2812GetColor(uint32_t led, char* scolor); +void Ws2812ForceSuspend (void); +void Ws2812ForceUpdate (void); +bool Ws2812SetChannels(void); +void Ws2812ShowScheme(void); +void Ws2812ModuleSelected(void); +void CmndLed(void); +void CmndPixels(void); +void CmndRotation(void); +void CmndWidth(void); +bool Xlgt01(uint8_t function); +void LightDiPulse(uint8_t times); +void LightDckiPulse(uint8_t times); +void LightMy92x1Write(uint8_t data); +void LightMy92x1Init(void); +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c); +bool My92x1SetChannels(void); +void My92x1ModuleSelected(void); +bool Xlgt02(uint8_t function); +void SM16716_SendBit(uint8_t v); +void SM16716_SendByte(uint8_t v); +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b); +void SM16716_Init(void); +bool Sm16716SetChannels(void); +void Sm16716ModuleSelected(void); +bool Xlgt03(uint8_t function); +void Sm2135SetLow(uint8_t pin); +void Sm2135SetHigh(uint8_t pin); +bool Sm2135Init(void); +bool Sm2135Write(uint8_t value); +bool Sm2135Start(uint8_t addr); +void Sm2135Stop(void); +bool Sm2135SetChannels(void); +void Sm2135ModuleSelected(void); +bool Xlgt04(uint8_t function); +void SnfL1Send(const char *buffer); +void SnfL1SerialSendOk(void); +bool SnfL1SerialInput(void); +bool SnfL1SetChannels(void); +void SnfL1ModuleSelected(void); +bool Xlgt05(uint8_t function); +bool ElectriqMoodLSetChannels(void); +void ElectriqMoodLModuleSelected(void); +bool Xlgt06(uint8_t function); +bool XlgtCall(uint8_t function); +void HlwCfInterrupt(void); +void HlwCf1Interrupt(void); +void HlwEvery200ms(void); +void HlwEverySecond(void); +void HlwSnsInit(void); +void HlwDrvInit(void); +bool HlwCommand(void); +bool Xnrg01(uint8_t function); +void CseReceived(void); +void CseSerialInput(void); +void CseEverySecond(void); +void CseSnsInit(void); +void CseDrvInit(void); +bool CseCommand(void); +bool Xnrg02(uint8_t function); +uint8_t PzemCrc(uint8_t *data); +void PzemSend(uint8_t cmd); +bool PzemReceiveReady(void); +bool PzemRecieve(uint8_t resp, float *data); +void PzemEvery250ms(void); +void PzemSnsInit(void); +void PzemDrvInit(void); +bool PzemCommand(void); +bool Xnrg03(uint8_t function); +uint8_t McpChecksum(uint8_t *data); +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size); +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size); +void McpSend(uint8_t *data); +void McpGetAddress(void); +void McpAddressReceive(void); +void McpGetCalibration(void); +void McpParseCalibration(void); +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift); +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers); +void McpSetSystemConfiguration(uint16 interval); +void McpGetFrequency(void); +void McpParseFrequency(void); +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency); +void McpGetData(void); +void McpParseData(void); +void McpSerialInput(void); +void McpEverySecond(void); +void McpSnsInit(void); +void McpDrvInit(void); +bool McpCommand(void); +bool Xnrg04(uint8_t function); +void PzemAcEverySecond(void); +void PzemAcSnsInit(void); +void PzemAcDrvInit(void); +bool PzemAcCommand(void); +bool Xnrg05(uint8_t function); +void PzemDcEverySecond(void); +void PzemDcSnsInit(void); +void PzemDcDrvInit(void); +bool PzemDcCommand(void); +bool Xnrg06(uint8_t function); +int Ade7953RegSize(uint16_t reg); +void Ade7953Write(uint16_t reg, uint32_t val); +int32_t Ade7953Read(uint16_t reg); +void Ade7953Init(void); +void Ade7953GetData(void); +void Ade7953EnergyEverySecond(void); +void Ade7953DrvInit(void); +bool Ade7953Command(void); +bool Xnrg07(uint8_t function); +void SDM120Every250ms(void); +void Sdm120SnsInit(void); +void Sdm120DrvInit(void); +void Sdm220Reset(void); +void Sdm220Show(bool json); +bool Xnrg08(uint8_t function); +void Dds2382EverySecond(void); +void Dds2382SnsInit(void); +void Dds2382DrvInit(void); +bool Xnrg09(uint8_t function); +void SDM630Every250ms(void); +void Sdm630SnsInit(void); +void Sdm630DrvInit(void); +bool Xnrg10(uint8_t function); +void DDSU666Every250ms(void); +void Ddsu666SnsInit(void); +void Ddsu666DrvInit(void); +bool Xnrg11(uint8_t function); +bool solaxX1_RS485ReceiveReady(void); +void solaxX1_RS485Send(uint16_t msgLen); +uint8_t solaxX1_RS485Receive(uint8_t *value); +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen); +void solaxX1_SendInverterAddress(void); +void solaxX1_QueryLiveData(void); +uint8_t solaxX1_ParseErrorCode(uint32_t code); +void solaxX1250MSecond(void); +void solaxX1SnsInit(void); +void solaxX1DrvInit(void); +void solaxX1Show(bool json); +bool Xnrg12(uint8_t function); +void FifLEEvery250ms(void); +void FifLESnsInit(void); +void FifLEDrvInit(void); +void FifLEReset(void); +void FifLEShow(bool json); +bool Xnrg13(uint8_t function); +void Bl0940Received(void); +void Bl0940SerialInput(void); +void Bl0940EverySecond(void); +void Bl0940SnsInit(void); +void Bl0940DrvInit(void); +bool Bl0940Command(void); +void Bl0940Show(bool json); +bool Xnrg14(uint8_t function); +void ADPSCallback(uint8_t phase); +void DataCallback(struct _ValueList * me, uint8_t flags); +void ResponseAppendTInfo(char sep); +void NewFrameCallback(struct _ValueList * me); +void TInfoDrvInit(void); +void TInfoInit(void); +void TInfoEvery250ms(void); +void TInfoShow(bool json); +bool Xnrg15(uint8_t function); +void IEM3000Every250ms(void); +void Iem3000SnsInit(void); +void Iem3000DrvInit(void); +bool Xnrg16(uint8_t function); +void WE517Every250ms(void); +void We517SnsInit(void); +void We517DrvInit(void); +bool Xnrg17(uint8_t function); +bool XnrgCall(uint8_t function); +void ICACHE_RAM_ATTR CounterIsrArg(void *arg); +void CounterInterruptDisable(bool state); +bool CounterPinState(void); +void CounterInit(void); +void CounterEverySecond(void); +void CounterSaveState(void); +void CounterShow(bool json); +void SyncACDimmer(void); +void CmndCounter(void); +void CmndCounterType(void); +void CmndCounterDebounce(void); +void CmndCounterDebounceLow(void); +void CmndCounterDebounceHigh(void); +bool Xsns01(uint8_t function); +bool adcAttachPin(uint8_t pin); +void AdcSaveSettings(uint32_t idx); +void AdcGetSettings(uint32_t idx); +void AdcInitParams(uint8_t idx); +void AdcAttach(uint8_t pin, uint8_t type); +void AdcInit(void); +uint16_t AdcRead(uint32_t pin, uint32_t factor); +void AdcEvery250ms(void); +uint8_t AdcGetButton(uint32_t pin); +uint16_t AdcGetLux(uint32_t idx); +uint16_t AdcGetRange(uint32_t idx); +void AdcGetCurrentPower(uint8_t idx, uint8_t factor); +void AdcEverySecond(void); +void AdcShowContinuation(bool *jsonflg); +void AdcShow(bool json); +void CmndAdcParam(void); +bool Xsns02(uint8_t function); +void SonoffScSend(const char *data); +void SonoffScInit(void); +void SonoffScSerialInput(char *rcvstat); +void SonoffScShow(bool json); +bool Xsns04(uint8_t function); +uint8_t OneWireReset(void); +void OneWireWriteBit(uint8_t v); +uint8_t OneWire1ReadBit(void); +uint8_t OneWire2ReadBit(void); +void OneWireWrite(uint8_t v); +uint8_t OneWireRead(void); +void OneWireSelect(const uint8_t rom[8]); +void OneWireResetSearch(void); +uint8_t OneWireSearch(uint8_t *newAddr); +bool OneWireCrc8(uint8_t *addr); +void Ds18x20Init(void); +void Ds18x20Convert(void); +bool Ds18x20Read(uint8_t sensor); +void Ds18x20Name(uint8_t sensor); +void Ds18x20EverySecond(void); +void Ds18x20Show(bool json); +bool Xsns05(uint8_t function); +void Ds18x20Init(void); +void Ds18x20Search(void); +void Ds18x20Convert(void); +bool Ds18x20Read(uint8_t sensor, float &t); +void Ds18x20Name(uint8_t sensor); +void Ds18x20EverySecond(void); +void Ds18x20Show(bool json); +bool Xsns05(uint8_t function); +bool DhtWaitState(uint32_t sensor, uint32_t level); +bool DhtRead(uint32_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +bool ShtReset(void); +bool ShtSendCommand(const uint8_t cmd); +bool ShtAwaitResult(void); +int ShtReadData(void); +bool ShtRead(void); +void ShtDetect(void); +void ShtEverySecond(void); +void ShtShow(bool json); +bool Xsns07(uint8_t function); +uint8_t HtuCheckCrc8(uint16_t data); +uint8_t HtuReadDeviceId(void); +void HtuSetResolution(uint8_t resolution); +void HtuReset(void); +void HtuHeater(uint8_t heater); +void HtuInit(void); +bool HtuRead(void); +void HtuDetect(void); +void HtuEverySecond(void); +void HtuShow(bool json); +bool Xsns08(uint8_t function); +bool Bmp180Calibration(uint8_t bmp_idx); +void Bmp180Read(uint8_t bmp_idx); +bool Bmx280Calibrate(uint8_t bmp_idx); +void Bme280Read(uint8_t bmp_idx); +static void BmeDelayMs(uint32_t ms); +bool Bme680Init(uint8_t bmp_idx); +void Bme680Read(uint8_t bmp_idx); +void BmpDetect(void); +void BmpRead(void); +void BmpShow(bool json); +void BMP_EnterSleep(void); +bool Xsns09(uint8_t function); +uint8_t Bh1750Resolution(uint32_t sensor_index); +bool Bh1750SetResolution(uint32_t sensor_index); +bool Bh1750SetMTreg(uint32_t sensor_index); +bool Bh1750Read(uint32_t sensor_index); +void Bh1750Detect(void); +void Bh1750EverySecond(void); +void CmndBh1750Resolution(void); +void CmndBh1750MTime(void); +void Bh1750Show(bool json); +bool Xsns10(uint8_t function); +void Veml6070Detect(void); +void Veml6070UvTableInit(void); +void Veml6070EverySecond(void); +void Veml6070ModeCmd(bool mode_cmd); +uint16_t Veml6070ReadUv(void); +double Veml6070UvRiskLevel(uint16_t uv_level); +double Veml6070UvPower(double uvrisk); +void Veml6070Show(bool json); +bool Xsns11(uint8_t function); +void Ads1115StartComparator(uint8_t channel, uint16_t mode); +int16_t Ads1115GetConversion(uint8_t channel); +void Ads1115Detect(void); +void Ads1115Label(char* label, uint32_t maxsize, uint8_t address); +void AdsEvery250ms(void); +void Ads1115Show(bool json); +bool Xsns12(uint8_t function); +bool Ina219SetCalibration(uint8_t mode, uint16_t addr); +float Ina219GetShuntVoltage_mV(uint16_t addr); +float Ina219GetBusVoltage_V(uint16_t addr); +bool Ina219Read(void); +bool Ina219CommandSensor(void); +void Ina219Detect(void); +void Ina219EverySecond(void); +void Ina219Show(bool json); +bool Xsns13(uint8_t function); +bool Sht3xRead(float &t, float &h, uint8_t sht3x_address); +void Sht3xDetect(void); +void Sht3xShow(bool json); +bool Xsns14(uint8_t function); +uint8_t MhzCalculateChecksum(uint8_t *array); +size_t MhzSendCmd(uint8_t command_id); +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s); +void MhzEverySecond(void); +bool MhzCommandSensor(void); +void MhzInit(void); +void MhzShow(bool json); +bool Xsns15(uint8_t function); +bool Tsl2561Read(void); +void Tsl2561Detect(void); +void Tsl2561EverySecond(void); +void Tsl2561Show(bool json); +bool Xsns16(uint8_t function); +void Senseair250ms(void); +void SenseairInit(void); +void SenseairShow(bool json); +bool Xsns17(uint8_t function); +size_t PmsSendCmd(uint8_t command_id); +bool PmsReadData(void); +bool PmsCommandSensor(void); +void PmsSecond(void); +void PmsInit(void); +void PmsShow(bool json); +bool Xsns18(uint8_t function); +void MGSInit(void); +void MGSPrepare(void); +char* measure_gas(int gas_type, char* buffer); +void MGSShow(bool json); +bool Xsns19(uint8_t function); +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer); +void NovaSdsSetWorkPeriod(void); +bool NovaSdsReadData(void); +void NovaSdsSecond(void); +bool NovaSdsCommandSensor(void); +void NovaSdsInit(void); +void NovaSdsShow(bool json); +bool Xsns20(uint8_t function); +void sgp30_Init(void); +float sgp30_AbsoluteHumidity(float temperature, float humidity); +void Sgp30Update(void); +void Sgp30Show(bool json); +bool Xsns21(uint8_t function); +uint8_t Sr04TModeDetect(void); +uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third); +uint16_t Sr04TMode3Distance(); +uint16_t Sr04TMode2Distance(void); +void Sr04TReading(void); +void Sr04Show(bool json); +bool Xsns22(uint8_t function); +uint8_t Si1145ReadByte(uint8_t reg); +uint16_t Si1145ReadHalfWord(uint8_t reg); +bool Si1145WriteByte(uint8_t reg, uint16_t val); +uint8_t Si1145WriteParamData(uint8_t p, uint8_t v); +bool Si1145Present(void); +void Si1145Reset(void); +void Si1145DeInit(void); +bool Si1145Begin(void); +uint16_t Si1145ReadUV(void); +uint16_t Si1145ReadVisible(void); +uint16_t Si1145ReadIR(void); +bool Si1145Read(void); +void Si1145Detect(void); +void Si1145Update(void); +void Si1145Show(bool json); +bool Xsns24(uint8_t function); +void LM75ADDetect(void); +float LM75ADGetTemp(void); +void LM75ADShow(bool json); +bool Xsns26(uint8_t function); +void calculateColorTemperature(void); +uint8_t getProxIntLowThresh(void); +inline void setProxIntLowThresh(uint8_t threshold); +uint8_t getProxIntHighThresh(void); +inline void setProxIntHighThresh(uint8_t threshold); +uint8_t getLEDDrive(void); +void setLEDDrive(uint8_t drive); +uint8_t getProximityGain(void); +void setProximityGain(uint8_t drive); +uint8_t getAmbientLightGain(); +void setAmbientLightGain(uint8_t drive); +uint8_t getLEDBoost(void); +void setLEDBoost(uint8_t boost); +uint8_t getProxGainCompEnable(void); +void setProxGainCompEnable(uint8_t enable); +uint8_t getProxPhotoMask(void); +void setProxPhotoMask(uint8_t mask); +uint8_t getGestureEnterThresh(void); +inline void setGestureEnterThresh(uint8_t threshold); +uint8_t getGestureExitThresh(void); +inline void setGestureExitThresh(uint8_t threshold); +uint8_t getGestureGain(void); +void setGestureGain(uint8_t gain); +uint8_t getGestureLEDDrive(void); +void setGestureLEDDrive(uint8_t drive); +uint8_t getGestureWaitTime(void); +void setGestureWaitTime(uint8_t time); +void getLightIntLowThreshold(uint16_t &threshold); +void setLightIntLowThreshold(uint16_t threshold); +void getLightIntHighThreshold(uint16_t &threshold); +void setLightIntHighThreshold(uint16_t threshold); +void getProximityIntLowThreshold(uint8_t &threshold); +void setProximityIntLowThreshold(uint8_t threshold); +void getProximityIntHighThreshold(uint8_t &threshold); +void setProximityIntHighThreshold(uint8_t threshold); +uint8_t getAmbientLightIntEnable(void); +void setAmbientLightIntEnable(uint8_t enable); +uint8_t getProximityIntEnable(void); +void setProximityIntEnable(uint8_t enable); +uint8_t getGestureIntEnable(void); +void setGestureIntEnable(uint8_t enable); +void clearAmbientLightInt(void); +void clearProximityInt(void); +uint8_t getGestureMode(void); +void setGestureMode(uint8_t mode); +bool APDS9960_init(void); +inline uint8_t getMode(void); +void setMode(uint8_t mode, uint8_t enable); +void enableLightSensor(void); +void disableLightSensor(void); +void enableProximitySensor(void); +void disableProximitySensor(void); +void enableGestureSensor(void); +void disableGestureSensor(void); +bool isGestureAvailable(void); +int16_t readGesture(void); +inline void enablePower(void); +inline void disablePower(void); +inline void readAllColorAndProximityData(void); +void APDS9960_adjustATime(void); +void resetGestureParameters(void); +bool processGestureData(void); +bool decodeGesture(void); +void handleGesture(void); +void APDS9960_loop(void); +void APDS9960_detect(void); +void APDS9960_show(bool json); +bool APDS9960CommandSensor(void); +bool Xsns27(uint8_t function); +void Tm16XXSend(uint8_t data); +void Tm16XXSendCommand(uint8_t cmd); +void TM16XXSendData(uint8_t address, uint8_t data); +uint8_t Tm16XXReceive(void); +void Tm16XXClearDisplay(void); +void Tm1638SetLED(uint8_t color, uint8_t pos); +void Tm1638SetLEDs(word leds); +uint8_t Tm1638GetButtons(void); +void TmInit(void); +void TmLoop(void); +bool Xsns28(uint8_t function); +void MCP230xx_CheckForIntCounter(void); +void MCP230xx_CheckForIntRetainer(void); +const char* IntModeTxt(uint8_t intmo); +uint8_t MCP230xx_readGPIO(uint8_t port); +void MCP230xx_ApplySettings(void); +void MCP230xx_Detect(void); +void MCP230xx_CheckForInterrupt(void); +void MCP230xx_Show(bool json); +void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate); +void MCP230xx_Reset(uint8_t pinmode); +bool MCP230xx_Command(void); +void MCP230xx_UpdateWebData(void); +void MCP230xx_Interrupt_Counter_Report(void); +void MCP230xx_Interrupt_Retain_Report(void); +void MCP230xx_SwitchRelay(); +bool Xsns29(uint8_t function); +void Mpr121Init(struct mpr121 *pS, bool initial); +void Mpr121Show(struct mpr121 *pS, uint8_t function); +bool Xsns30(uint8_t function); +void CCS811Detect(void); +void CCS811Update(void); +void CCS811Show(bool json); +bool Xsns31(uint8_t function); +void MPU_6050PerformReading(void); +void MPU_6050Detect(void); +void MPU_6050Show(bool json); +bool Xsns32(uint8_t function); +void DS3231Detect(void); +uint8_t bcd2dec(uint8_t n); +uint8_t dec2bcd(uint8_t n); +uint32_t ReadFromDS3231(void); +void SetDS3231Time (uint32_t epoch_time); +void DS3231EverySecond(void); +bool Xsns33(uint8_t function); +bool HxIsReady(uint16_t timeout); +long HxRead(void); +void HxResetPart(void); +void HxReset(void); +void HxCalibrationStateTextJson(uint8_t msg_id); +void SetWeightDelta(); +bool HxCommand(void); +long HxWeight(void); +void HxInit(void); +void HxEvery100mSecond(void); +void HxSaveBeforeRestart(void); +void HxShow(bool json); +void HandleHxAction(void); +void HxSaveSettings(void); +void HxLogUpdates(void); +bool Xsns34(uint8_t function); +void ICACHE_RAM_ATTR TX2xStartRead(void); +bool Tx2xAvailable(void); +float atan2f(float a, float b); +void Tx2xCheckSampleCount(void); +void Tx2xResetStat(void); +void Tx2xResetStatData(void); +void Tx2xRead(void); +void Tx2xInit(void); +int32_t Tx2xNormalize(int32_t value); +void Tx2xShow(bool json); +bool Xsns35(uint8_t function); +void MGC3130_handleSensorData(); +void MGC3130_sendMessage(uint8_t data[], uint8_t length); +void MGC3130_handleGesture(); +bool MGC3130_handleTouch(); +void MGC3130_handleAirWheel(); +void MGC3130_handleSystemStatus(); +bool MGC3130_receiveMessage(); +bool MGC3130_readData(); +void MGC3130_nextMode(); +void MGC3130_loop(); +void MGC3130_detect(void); +void MGC3130_show(bool json); +bool MGC3130CommandSensor(); +bool Xsns36(uint8_t function); +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal); +void RfSnsInitTheoV2(void); +void RfSnsAnalyzeTheov2(void); +void RfSnsTheoV2Show(bool json); +void RfSnsInitAlectoV2(void); +void RfSnsAnalyzeAlectov2(); +void RfSnsAlectoResetRain(void); +uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len); +void RfSnsAlectoV2Show(bool json); +void RfSnsInit(void); +void RfSnsAnalyzeRawSignal(void); +void RfSnsEverySecond(void); +void RfSnsShow(bool json); +bool Xsns37(uint8_t function); +void AzEverySecond(void); +void AzInit(void); +void AzShow(bool json); +bool Xsns38(uint8_t function); +void MAX31855_Init(void); +int32_t MAX31855_ShiftIn(uint8_t Length); +float MAX31855_GetProbeTemperature(int32_t RawData); +float MAX31855_GetReferenceTemperature(int32_t RawData); +void MAX31855_GetResult(void); +void MAX31855_Show(bool Json); +bool Xsns39(uint8_t function); +void PN532_Init(void); +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout); +int8_t PN532_readAckFrame(void); +uint32_t PN532_getFirmwareVersion(void); +void PN532_wakeup(void); +bool PN532_setPassiveActivationRetries(uint8_t maxRetries); +bool PN532_SAMConfig(void); +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data); +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data); +void PN532_ScanForTag(void); +bool PN532_Command(void); +bool Xsns40(uint8_t function); +bool Max4409Read_lum(void); +void Max4409Detect(void); +void Max4409EverySecond(void); +void Max4409Show(bool json); +bool Xsns41(uint8_t function); +void Scd30Detect(void); +void Scd30Update(void); +int Scd30GetCommand(int command_code, uint16_t *pvalue); +int Scd30SetCommand(int command_code, uint16_t value); +bool Scd30CommandSensor(); +void Scd30Show(bool json); +bool Xsns42(byte function); +int hreReadBit(); +char hreReadChar(int &parity_errors); +void hreInit(void); +void hreEvery50ms(void); +void hreShow(boolean json); +bool Xsns43(byte function); +uint8_t sps30_calc_CRC(uint8_t *data); +void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen); +void sps30_cmd(uint16_t cmd); +void SPS30_Detect(void); +void SPS30_Every_Second(); +void SPS30_Show(bool json); +bool SPS30_cmd(void); +bool Xsns44(byte function); +void Vl53l0Detect(void); +void Vl53l0Every_250MSecond(void); +void Vl53l0Every_Second(void); +void Vl53l0Show(boolean json); +bool Xsns45(byte function); +void MLX90614_Init(void); +void MLX90614_Every_Second(void); +void MLX90614_Show(uint8_t json); +bool Xsns46(byte function); +void MAX31865_Init(void); +void MAX31865_GetResult(void); +void MAX31865_Show(bool Json); +bool Xsns47(uint8_t function); +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg); +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr); +void ChirpReset(uint8_t addr); +void ChirpResetAll(void); +void ChirpClockSet(); +void ChirpSleep(uint8_t addr); +void ChirpSelect(uint8_t sensor); +uint8_t ChirpReadVersion(uint8_t addr); +bool ChirpSet(uint8_t addr); +bool ChirpScan(); +void ChirpDetect(void); +void ChirpServiceAllSensors(uint8_t job); +void ChirpEvery100MSecond(void); +void ChirpShow(bool json); +bool ChirpCmd(void); +bool Xsns48(uint8_t function); +void PAJ7620SelectBank(uint8_t bank); +void PAJ7620DecodeGesture(void); +void PAJ7620ReadGesture(void); +void PAJ7620Detect(void); +void PAJ7620Init(void); +void PAJ7620Loop(void); +void PAJ7620Show(bool json); +bool PAJ7620CommandSensor(void); +bool Xsns50(uint8_t function); +uint8_t RDM6300HexNibble(char chr); +void RDM6300HexStringToArray(uint8_t array[], uint8_t len, char buffer[]); +void RDM6300Init(); +void RDM6300ScanForTag(); +void RDM6300Show(void); +bool Xsns51(byte function); +void IBEACON_Init(); +void hm17_every_second(void); +void hm17_sbclr(void); +void hm17_sendcmd(uint8_t cmd); +uint32_t ibeacon_add(struct IBEACON *ib); +void hm17_decode(void); +void IBEACON_loop(); +void IBEACON_Show(void); +bool xsns52_cmd(void); +bool ibeacon_cmd(void); +void ib_sendbeep(void); +void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor); +bool Xsns52(byte function); +double sml_median_array(double *array,uint8_t len); +double sml_median(struct SML_MEDIAN_FILTER* mf, double in); +void ADS1115_init(void); +bool Serial_available(); +uint8_t Serial_read(); +uint8_t Serial_peek(); +void Dump2log(void); +double sml_getvalue(unsigned char *cp,uint8_t index); +uint8_t hexnibble(char chr); +double CharToDouble(const char *str); +void ebus_esc(uint8_t *ebus_buffer, unsigned char len); +uint8_t ebus_crc8(uint8_t data, uint8_t crc_init); +uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ); +void sml_empty_receiver(uint32_t meters); +void sml_shift_in(uint32_t meters,uint32_t shard); +void SML_Poll(void); +void SML_Decode(uint8_t index); +void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex); +void SML_Show(boolean json); +void ICACHE_RAM_ATTR SML_CounterUpd(uint8_t index); +void ICACHE_RAM_ATTR SML_CounterUpd1(void); +void ICACHE_RAM_ATTR SML_CounterUpd2(void); +void ICACHE_RAM_ATTR SML_CounterUpd3(void); +void ICACHE_RAM_ATTR SML_CounterUpd4(void); +uint32_t SML_getlinelen(char *lp); +uint32_t SML_getscriptsize(char *lp); +uint32_t SML_getscriptsize(char *lp); +bool Gpio_used(uint8_t gpiopin); +void SML_Init(void); +uint32_t SML_SetBaud(uint32_t meter, uint32_t br); +uint32_t SML_Status(uint32_t meter); +uint32_t SML_Write(uint32_t meter,char *hstr); +uint32_t SML_Read(int32_t meter,char *str, uint32_t slen); +float SML_GetVal(uint32_t index); +void SetDBGLed(uint8_t srcpin, uint8_t ledpin); +void SML_Counter_Poll(void); +void SML_Check_Send(void); +uint8_t sml_hexnibble(char chr); +void SML_Send_Seq(uint32_t meter,char *seq); +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num); +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len); +uint8_t CalcEvenParity(uint8_t data); +bool XSNS_53_cmd(void); +void InjektCounterValue(uint8_t meter,uint32_t counter); +void SML_CounterSaveState(void); +bool Xsns53(byte function); +static uint32_t _expand_r_shunt(uint16_t compact_r_shunt); +void Ina226SetCalibration(uint8_t Ina226Index); +bool Ina226TestPresence(uint8_t device); +void Ina226ResetActive(void); +void Ina226Init(); +float Ina226ReadBus_v(uint8_t device); +float Ina226ReadShunt_i(uint8_t device); +float Ina226ReadPower_w(uint8_t device); +void Ina226Read(uint8_t device); +void Ina226EverySecond(); +bool Ina226CommandSensor(); +void Ina226Show(bool json); +bool Xsns54(byte callback_id); +bool Hih6Read(void); +void Hih6Detect(void); +void Hih6EverySecond(void); +void Hih6Show(bool json); +bool Xsns55(uint8_t function); +void HpmaSecond(void); +void HpmaInit(void); +void HpmaShow(bool json); +bool Xsns56(uint8_t function); +void Tsl2591Init(void); +bool Tsl2591Read(void); +void Tsl2591EverySecond(void); +void Tsl2591Show(bool json); +bool Xsns57(uint8_t function); +bool Dht12Read(void); +void Dht12Detect(void); +void Dht12EverySecond(void); +void Dht12Show(bool json); +bool Xsns58(uint8_t function); +uint32_t DS1624_Idx2Addr(uint32_t idx); +void DS1624_Restart(uint8_t config, uint32_t idx); +void DS1624_HotPlugUp(uint32_t idx); +void DS1624_HotPlugDown(int idx); +bool DS1624GetTemp(float *value, int idx); +void DS1624HotPlugScan(void); +void DS1624EverySecond(void); +void DS1624Show(bool json); +bool Xsns59(uint8_t function); +void UBXcalcChecksum(char* CK, size_t msgSize); +bool UBXcompareMsgHeader(const char* msgHeader); +void UBXinitCFG(void); +void UBXsendCFGLine(uint8_t _line); +void UBXTriggerTele(void); +void UBXDetect(void); +uint32_t UBXprocessGPS(); +void UBXsendHeader(void); +void UBXsendRecord(uint8_t *buf); +void UBXsendFooter(void); +void UBXsendFile(void); +void UBXSetRate(uint16_t interval); +void UBXSelectMode(uint16_t mode); +bool UBXHandlePOSLLH(); +void UBXHandleSTATUS(); +void UBXHandleTIME(); +void UBXHandleOther(void); +void UBXLoop50msec(void); +void UBXLoop(void); +void UBXShow(bool json); +bool UBXCmd(void); +bool Xsns60(uint8_t function); +void MINRFinit(void); +bool MINRFinitBLE(uint8_t _mode); +void MINRFhopChannel(); +bool MINRFreceivePacket(void); +void MINRFswapbuf(uint8_t *buf, uint8_t len); +void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr); +void MINRFhandleScan(void); +void MINRFstartBeacon(uint16_t entry); +void MINRFbeaconCounter(void); +int MINRFdecryptPacket(char *_buf); +int MINRFdecryptMJYD2SPacket(char *_buf, uint8_t _light, char* _output); +void MINRFreverseMAC(uint8_t _MAC[]); +void MINRFAddKey(char* payload); +void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]); +void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]); +void MINRFcomputefirstUsedPacketMode(void); +void MINRFrecalcBuffer(uint8_t *_buf, uint32_t offset); +void MINRFchangePacketModeTo(uint8_t _mode); +void MINRFpurgeFakeSensors(void); +void MINRFconfirmSensors(void); +void MINRFtriggerTele(void); +void MINRFhandleMiBeaconPacket(void); +void MINRFhandleCGD1Packet(void); +void MINRFhandleNlightPacket(void); +void MINRFhandleMJYD2SPacket(void); +void MINRFhandleLightPacket(void); +void MINRFaddLight(uint8_t _MAC[], uint8_t _type); +void MINRFhandleATCPacket(void); +void MINRF_EVERY_50_MSECOND(); +bool NRFCmd(void); +void MINRFShow(bool json); +bool Xsns61(uint8_t function); +void MI32stripColon(char* _string); +void MI32HexStringToBytes(char* _string, uint8_t* _byteArray); +void MI32_ReverseMAC(uint8_t _mac[]); +void MI32AddKey(char* payload); +int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type); +void MI32nullifyEndOfMQTT_DATA(); +void MI32triggerTele(void); +void MI32StatusInfo(); +void MI32Init(void); +void MI32StartTask(uint32_t task); +bool MI32ConnectActiveSensor(); +void MI32StartScanTask(); +void MI32ScanTask(void *pvParameters); +void MI32StartSensorTask(); +void MI32SensorTask(void *pvParameters); +bool MI32connectLYWSD03forNotification(); +void MI32StartTimeTask(); +void MI32TimeTask(void *pvParameters); +void MI32StartUnitTask(); +void MI32UnitTask(void *pvParameters); +void MI32StartBatteryTask(); +void MI32BatteryTask(void *pvParameters); +void MI32batteryRead(uint32_t _type); +void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize); +void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI); +void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI); +void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI); +void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID); +void MI32HandleGenericBeacon(uint8_t* payload, size_t payloadLength, int RSSI, uint8_t* addr); +void MI32addBeacon(uint8_t index, char* data); +void MI32showScanResults(); +void MI32readHT_LY(char *_buf); +bool MI32readBat(char *_buf); +void MI32Every50mSecond(); +void MI32EverySecond(bool restart); +bool MI32Cmd(void); +void MI32Show(bool json); +bool Xsns62(uint8_t function); +void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay); +void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot); +void HM10_ReverseMAC(uint8_t _mac[]); +void HM10_Reset(void); +void HM10_Discovery_Scan(void); +void HM10_Read_LYWSD03(void); +void HM10_Read_LYWSD02(void); +void HM10_Time_LYWSD02(void); +void HM10_Read_Flora(void); +void HM10_Read_CGD1(void); +void HM10_Read_MJ_HT_V1(void); +void HM10SerialInit(void); +void HM10parseMiBeacon(char * _buf, uint32_t _slot); +void HM10parseATC(char * _buf, uint32_t _slot); +char* HM10ParseResponse(char *buf, uint16_t bufsize); +void HM10readHT_LY(char *_buf); +void HM10readHT_CGD1(char *_buf); +void HM10readHT_MJ_HT_V1(char *_buf); +void HM10readTLMF(char *_buf); +bool HM10readBat(char *_buf); +bool HM10SerialHandleFeedback(); +void HM10_TaskEvery100ms(); +void HM10StatusInfo(); +void HM10EverySecond(bool restart); +bool HM10Cmd(void); +void HM10triggerTele(void); +void HM10Show(bool json); +bool Xsns62(uint8_t function); +bool AHT1XWrite(uint8_t aht1x_idx); +bool AHT1XRead(uint8_t aht1x_idx); +void AHT1XPoll(void); +unsigned char AHT1XReadStatus(uint8_t aht1x_address); +void AHT1XReset(uint8_t aht1x_address); +bool AHT1XInit(uint8_t aht1x_address); +void AHT1XDetect(void); +void AHT1XShow(bool json); +bool Xsns63(uint8_t function); +void HRXLInit(void); +void HRXLEverySecond(void); +void HRXLShow(bool json); +bool Xsns64(uint8_t function); +uint16_t HdcReadDeviceId(void); +uint16_t HdcReadManufacturerId(void); +void HdcConfig(uint16_t config); +void HdcReset(void); +int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg); +int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len); +void HdcInit(void); +bool HdcTriggerRead(void); +bool HdcRead(void); +void HdcDetect(void); +void HdcEverySecond(void); +void HdcShow(bool json); +bool Xsns65(uint8_t function); +void IAQ_Init(void); +void IAQ_Read(void); +void IAQ_Show(uint8_t json); +bool Xsns66(byte function); +void ICACHE_RAM_ATTR AS3935Isr(void); +uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift); +void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data); +void ICACHE_RAM_ATTR AS3935CountFreq(void); +bool AS3935AutoTuneCaps(uint8_t irqpin); +bool AS3935CalRCOResult(void); +bool AS3935CalibrateRCO(void); +void AS3935Reset(void); +void AS3935PwrDown(void); +void AS3935PwrUp(void); +uint8_t AS3935GetPwrStat(void); +uint8_t AS3935GetIRQ(void); +uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance); +uint8_t AS3935GetDistance(void); +int16_t AS3935CalcDistance(void); +uint32_t AS3935GetIntensity(void); +uint8_t AS3935GetTuneCaps(void); +void AS3935SetTuneCaps(uint8_t tune); +uint8_t AS3935GetDisturber(void); +void AS3935SetDisturber(uint8_t stat); +uint8_t AS3935GetMinLights(void); +void AS3935SetMinLights(uint8_t stat); +uint8_t AS3935TransMinLights(uint8_t min_lights); +uint8_t AS3935TranslMinLightsInt(uint8_t min_lights); +uint8_t AS3935GetNoiseFloor(void); +void AS3935SetNoiseFloor(uint8_t noise); +uint8_t AS3935GetGain(void); +void AS3935SetGain(uint8_t room); +uint8_t AS3935GetGainInt(void); +void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage); +uint8_t AS3935GetSpikeRejection(void); +void AS3935SetSpikeRejection(uint8_t rej); +uint8_t AS3935GetWdth(void); +void AS3935SetWdth(uint8_t wdth); +bool AS3935AutoTune(void); +bool AS3935LowerNoiseFloor(void); +bool AS3935RaiseNoiseFloor(void); +bool AS3935SetDefault(void); +void AS3935InitSettings(void); +bool AS3935Setup(void); +bool AS3935init(void); +void AS3935Detect(void); +void AS3935EverySecond(void); +bool AS3935Cmd(void); +void AH3935Show(bool json); +bool Xsns67(uint8_t function); +void ICACHE_RAM_ATTR WindMeterUpdateSpeed(void); +void WindMeterInit(void); +void WindMeterEverySecond(void); +bool WindMeterShouldTriggerTele(); +void WindMeterResetStatData(void); +void WindMeterCheckSampleCount(void); +void WindMeterShow(bool json); +void WindMeterTriggerTele(void); +bool Xsns68Cmnd(void); +bool Xsns68(uint8_t function); +void sns_opentherm_init_boiler_status(); +void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt(); +void sns_opentherm_processResponseCallback(unsigned long response, int st); +bool sns_opentherm_Init(); +void sns_opentherm_stat(bool json); +void sns_ot_start_handshake(); +void sns_ot_process_handshake(unsigned long response, int st); +void sns_opentherm_CheckSettings(void); +uint8_t sns_opentherm_parse_flag(char *flag); +uint8_t sns_opentherm_read_flags(char *data, uint32_t len); +void sns_opentherm_cmd(void); +void sns_opentherm_boiler_setpoint_cmd(void); +void sns_opentherm_hot_water_setpoint_cmd(void); +void sns_opentherm_save_settings_cmd(void); +void sns_opentherm_flags_cmd(void); +void sns_opentherm_set_central_heating_cmd(void); +bool Xsns69(uint8_t function); +unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status); +void sns_opentherm_parse_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_slave_flags(struct OpenThermCommandT *self); +unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status); +void sns_opentherm_parse_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_boiler_temperature(struct OpenThermCommandT *self); +unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status); +void sns_opentherm_parse_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_boiler_dhw_temperature(struct OpenThermCommandT *self); +unsigned long sns_opentherm_get_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *); +void sns_opentherm_parse_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_flags(struct OpenThermCommandT *self); +unsigned long sns_opentherm_get_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *); +void sns_opentherm_parse_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_oem_diag(struct OpenThermCommandT *self); +unsigned long sns_opentherm_get_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *); +void sns_opentherm_parse_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_tele_generic_float(struct OpenThermCommandT *self); +void sns_opentherm_parse_dhw_setpoint(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_parse_flame_modulation(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_parse_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +unsigned long sns_opentherm_get_next_request(struct OT_BOILER_STATUS_T *boilerStatus); +void sns_opentherm_check_retry_request(); +void sns_opentherm_process_success_response(struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response); +void sns_opentherm_dump_telemetry(); +uint16_t VEML6075read16 (uint8_t reg); +void VEML6075write16 (uint8_t reg, uint16_t val); +float VEML6075calcUVA (void); +float VEML6075calcUVB (void); +float VEML6075calcUVI (void); +void VEML6075SetHD(uint8_t val); +uint8_t VEML6075ReadHD(void); +void VEML6075SetUvIt(uint8_t val); +uint8_t VEML6075GetUvIt(void); +void VEML6075Pwr(uint8_t val); +uint8_t VEML6075GetPwr(void); +void VEML6075ReadData(void); +bool VEML6075init(void); +void VEML6075Detect(void); +void VEML6075EverySecond(void); +bool VEML6075Cmd(void); +void VEML6075Show(bool json); +bool Xsns70(uint8_t function); +void VEML7700Detect(void); +uint16_t VEML7700TranslateItMs (uint8_t ittime); +uint8_t VEML7700TranslateItInt (uint16_t ittimems); +void VEML7700EverySecond(void); +void VEML7700Show(bool json); +bool VEML7700Cmd(void); +bool Xsns71(uint8_t function); +float MCP9808Read(uint8_t addr); +void MCP9808Detect(void); +void MCP9808EverySecond(void); +void MCP9808Show(bool json); +bool Xsns72(uint8_t function); +bool HP303B_Read(uint32_t hp303b_idx); +void HP303B_Detect(void); +void HP303B_EverySecond(void); +void HP303B_Show(bool json); +bool Xsns73(uint8_t function); +void LMT01_Init(void); +void ICACHE_RAM_ATTR LMT01_countPulse(void); +void LMT01_GetTemperature(void); +int LMT01_getPulses(void); +void LMT01_Show(bool Json); +bool Xsns74(uint8_t function); +void HandleMetrics(void); +bool Xsns75(uint8_t function); +void DYPInit(void); +void DYPEverySecond(void); +void DYPShow(bool json); +bool Xsns76(uint8_t function); +void Vl53l1Detect(void); +void Vl53l1Every_250MSecond(void); +void Vl53l1Every_Second(void); +void Vl53l1Show(bool json); +bool Xsns77(uint8_t function); +bool Xsns78(uint8_t function); +bool XsnsEnabled(uint32_t sns_index); +void XsnsSensorState(void); +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index); +bool XsnsCall(uint8_t Function); +bool I2cEnabled(uint32_t i2c_index); +void I2cDriverState(void); +#line 191 "/workspace/Tasmota/tasmota/tasmota.ino" +void setup(void) { +#ifdef ESP32 +#ifdef DISABLE_ESP32_BROWNOUT + DisableBrownout(); +#endif +#endif + + global_state.data = 0xF; + + RtcRebootLoad(); + if (!RtcRebootValid()) { + RtcReboot.fast_reboot_count = 0; + } +#ifdef FIRMWARE_MINIMAL + RtcReboot.fast_reboot_count = 0; +#else + RtcReboot.fast_reboot_count++; +#endif + RtcRebootSave(); + + Serial.begin(baudrate); + + seriallog_level = LOG_LEVEL_INFO; + + snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); + if (VERSION & 0xff) { + snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff); + } + + snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); + + SettingsLoad(); + SettingsDelta(); + + OsWatchInit(); + + GetFeatures(); + + if (1 == RtcReboot.fast_reboot_count) { + UpdateQuickPowerCycle(true); + XdrvCall(FUNC_SETTINGS_OVERRIDE); + } + + + seriallog_level = Settings.seriallog_level; + seriallog_timer = SERIALLOG_TIMER; + syslog_level = Settings.syslog_level; + stop_flash_rotate = Settings.flag.stop_flash_rotate; + save_data_counter = Settings.save_data; + ssleep = Settings.sleep; +#ifndef USE_EMULATION + Settings.flag2.emulation = 0; +#else +#ifndef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif +#ifndef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif +#endif + + if (Settings.param[P_BOOT_LOOP_OFFSET]) { + + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { + Settings.flag3.user_esp8285_enable = 0; + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (bitRead(Settings.rule_stop, i)) { + bitWrite(Settings.rule_enabled, i, 0); + } + } + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +2) { + Settings.rule_enabled = 0; + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { + Settings.module = Settings.fallback_module; + + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("FRC: " D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); + } + } + + Format(mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(mqtt_client)); + Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP_getChipId() & 0x1FFF); + } else { + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); + } + + GetEspHardwareType(); + GpioInit(); + + + + WifiConnect(); + + SetPowerOnState(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE), PROJECT, SettingsText(SET_DEVICENAME), my_version, my_image); +#ifdef FIRMWARE_MINIMAL + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); +#endif + + memcpy_P(log_data, VERSION_MARKER, 1); + + RtcInit(); + +#ifdef USE_ARDUINO_OTA + ArduinoOTAInit(); +#endif + + XdrvCall(FUNC_INIT); + XsnsCall(FUNC_INIT); +#ifdef USE_SCRIPT + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">BS",3,0); +#endif + + rules_flag.system_init = 1; +} + +void BacklogLoop(void) { + if (TimeReached(backlog_delay)) { + if (!BACKLOG_EMPTY && !backlog_mutex) { + backlog_mutex = true; + bool nodelay = false; + bool nodelay_detected = false; + String cmd; + do { +#ifdef SUPPORT_IF_STATEMENT + cmd = backlog.shift(); +#else + cmd = backlog[backlog_pointer]; + backlog[backlog_pointer] = (const char*) nullptr; + backlog_pointer++; + if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } +#endif + nodelay_detected = !strncasecmp_P(cmd.c_str(), PSTR(D_CMND_NODELAY), strlen(D_CMND_NODELAY)); + if (nodelay_detected) { nodelay = true; } + } while (!BACKLOG_EMPTY && nodelay_detected); + if (!nodelay_detected) { ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG); } + if (nodelay) { backlog_delay = 0; } + backlog_mutex = false; + } + } +} + +void SleepDelay(uint32_t mseconds) { + if (mseconds) { + for (uint32_t wait = 0; wait < mseconds; wait++) { + delay(1); + if (Serial.available()) { break; } + } + } else { + delay(0); + } +} + +void loop(void) { + uint32_t my_sleep = millis(); + + XdrvCall(FUNC_LOOP); + XsnsCall(FUNC_LOOP); + + OsWatchLoop(); + + ButtonLoop(); + SwitchLoop(); +#ifdef USE_DEVICE_GROUPS + DeviceGroupsLoop(); +#endif + BacklogLoop(); + + if (TimeReached(state_50msecond)) { + SetNextTimeInterval(state_50msecond, 50); +#ifdef ROTARY_V1 + RotaryHandler(); +#endif + XdrvCall(FUNC_EVERY_50_MSECOND); + XsnsCall(FUNC_EVERY_50_MSECOND); + } + if (TimeReached(state_100msecond)) { + SetNextTimeInterval(state_100msecond, 100); + Every100mSeconds(); + XdrvCall(FUNC_EVERY_100_MSECOND); + XsnsCall(FUNC_EVERY_100_MSECOND); + } + if (TimeReached(state_250msecond)) { + SetNextTimeInterval(state_250msecond, 250); + Every250mSeconds(); + XdrvCall(FUNC_EVERY_250_MSECOND); + XsnsCall(FUNC_EVERY_250_MSECOND); + } + if (TimeReached(state_second)) { + SetNextTimeInterval(state_second, 1000); + PerformEverySecond(); + XdrvCall(FUNC_EVERY_SECOND); + XsnsCall(FUNC_EVERY_SECOND); + } + + if (!serial_local) { SerialInput(); } + +#ifdef USE_ARDUINO_OTA + ArduinoOtaLoop(); +#endif + + uint32_t my_activity = millis() - my_sleep; + + if (Settings.flag3.sleep_normal) { + + SleepDelay(ssleep); + } else { + if (my_activity < (uint32_t)ssleep) { + SleepDelay((uint32_t)ssleep - my_activity); + } else { + if (global_state.network_down) { + SleepDelay(my_activity /2); + } + } + } + + if (!my_activity) { my_activity++; } + uint32_t loop_delay = ssleep; + if (!loop_delay) { loop_delay++; } + uint32_t loops_per_second = 1000 / loop_delay; + uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; + loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); +} +# 1 "/workspace/Tasmota/tasmota/sendemail.ino" +#ifdef USE_SENDMAIL + +#ifndef USE_ESP32MAIL + +#include "sendemail.h" +# 27 "/workspace/Tasmota/tasmota/sendemail.ino" +#ifndef SEND_MAIL_MINRAM +#define SEND_MAIL_MINRAM 12*1024 +#endif + +void script_send_email_body(void(*func)(char *)); + +#define xPSTR(a) a + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + char auth=0; + uint16_t status=1; + SendEmail *mail=0; + uint16_t blen; + char *endcmd; + + + + uint16_t mem=ESP.getFreeHeap(); + if (memsend(from,to,subject,cmd); + delete mail; + if (result==true) status=0; + } + +exit: + if (oparams) free(oparams); + return status; +} + + + +#ifdef ESP8266 +WiFiClient *g_client; +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { +} +#else +WiFiClient *g_client; +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new WiFiClientSecure()) { +} +#endif + +String SendEmail::readClient() { + delay(0); + String r = client->readStringUntil('\n'); + + r.trim(); + while (client->available()) { + delay(0); + r += client->readString(); + } + return r; +} + +bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { +bool status=false; +String buffer; + + if (!host.length()) { + return status; + } + + client->setTimeout(timeout); + +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); +#endif + + if (!client->connect(host.c_str(), port)) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed")); +#endif + goto exit; + } + + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("220"))) { + goto exit; + } + + buffer = F("EHLO "); + buffer += client->localIP().toString(); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + if (user.length()>0 && passwd.length()>0 ) { + + buffer = F("AUTH LOGIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) + { + goto exit; + } + base64 b; + buffer = b.encode(user); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) { + goto exit; + } + buffer = b.encode(passwd); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("235"))) { + goto exit; + } + } + + + buffer = F("MAIL FROM:"); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + buffer = F("RCPT TO:"); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + + buffer = F("DATA"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("354"))) { + goto exit; + } + + buffer = F("MIME-Version: 1.0\r\n"); + client->print(buffer); + buffer = F("Content-Type: Multipart/mixed; boundary=frontier\r\n"); + client->print(buffer); + + buffer = F("From: "); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("To: "); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("Subject: "); + buffer += subject; + buffer += F("\r\n"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + +#ifdef USE_SCRIPT + if (*msg=='*' && *(msg+1)==0) { + g_client=client; + script_send_email_body(xsend_message_txt); + } else { + client->println(msg); + } +#else + client->println(msg); +#endif + client->println('.'); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + buffer = F("QUIT"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + status=true; +exit: + + return status; +} + +void xsend_message_txt(char *msg) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),msg); +#endif +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*msg=='@') { + msg++; + attach_File(msg); + } else if (*msg=='&') { + msg++; + attach_Array(msg); + } else { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n\r\n")); + g_client->println(msg); + g_client->print(F("\r\n--frontier\r\n")); + } +#else + if (*msg=='&') { + msg++; + attach_Array(msg); + } else { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n\r\n")); + g_client->println(msg); + g_client->print(F("\r\n--frontier\r\n")); + } +#endif +} + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#include +extern FS *fsp; + +void attach_File(char *path) { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n")); + char buff[64]; + char *cp = path; + while (*cp=='/') cp++; + File file = fsp->open(path, "r"); + if (file) { + sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n"), cp); + g_client->write(buff); + uint16_t flen = file.size(); + uint8_t fbuff[64]; + uint16_t blen = sizeof(fbuff); + while (flen>0) { + file.read(fbuff, blen); + flen -= blen; + g_client->write(fbuff, blen); + if (flenprint(F("\r\n\r\nfile not found!\r\n")); + } + g_client->print(F("\r\n--frontier\r\n")); +} + +#endif + +float *get_array_by_name(char *name, uint16_t *alen); +void flt2char(float num, char *nbuff); + +void attach_Array(char *aname) { + float *array = 0; + uint16_t alen; + array = get_array_by_name(aname, &alen); + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n")); + if (array && alen) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("array found %d"),alen); +#endif + char buff[64]; + sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); + g_client->write(buff); + + strcpy(buff, GetDateAndTime(DT_LOCAL).c_str()); + strcat(buff,"\t"); + g_client->write(buff); + + float *fp=array; + for (uint32_t cnt = 0; cntwrite(nbuff, strlen(nbuff)); + } + } else { + g_client->print(F("\r\n\r\narray not found!\r\n")); + } + g_client->print(F("\r\n--frontier\r\n")); +} + +#else +# 507 "/workspace/Tasmota/tasmota/sendemail.ino" +#include "ESP32_MailClient.h" +#include "SD.h" + + + + +#ifndef SEND_MAIL32_MINRAM +#define SEND_MAIL32_MINRAM 30*1024 +#endif + +void script_send_email_body(void(*func)(char *)); + +#define xPSTR(a) a + +SMTPData smtpData; +#define MAX_ATTCHMENTS 8 +char *attachments[MAX_ATTCHMENTS]; +uint8_t num_attachments; + + + + + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + uint16_t status=1; + uint16_t blen; + char *endcmd; + + + uint32_t mem=ESP.getFreeHeap(); + + if (mempicmax) cnt=1; + len=WcGetPicstore(cnt-1,&buff); + if (len) { + char str[12]; + sprintf(str,"img_%1d.jpg",cnt+1); + smtpData.addAttachData(str, "image/jpg",buff,len); + } +#endif + } else { + smtpData.addMessage(txt); + } +} +# 828 "/workspace/Tasmota/tasmota/sendemail.ino" +#endif + + +#endif +# 1 "/workspace/Tasmota/tasmota/settings.ino" +# 24 "/workspace/Tasmota/tasmota/settings.ino" +const uint16_t RTC_MEM_VALID = 0xA55A; + +uint32_t rtc_settings_crc = 0; + +uint32_t GetRtcSettingsCrc(void) +{ + uint32_t crc = 0; + uint8_t *bytes = (uint8_t*)&RtcSettings; + + for (uint32_t i = 0; i < sizeof(RtcSettings); i++) { + crc += bytes[i]*(i+1); + } + return crc; +} + +void RtcSettingsSave(void) +{ + if (GetRtcSettingsCrc() != rtc_settings_crc) { + RtcSettings.valid = RTC_MEM_VALID; +#ifdef ESP8266 + ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); +#else + RtcDataSettings = RtcSettings; +#endif + rtc_settings_crc = GetRtcSettingsCrc(); + } +} + +void RtcSettingsLoad(void) +{ +#ifdef ESP8266 + ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); +#else + RtcSettings = RtcDataSettings; +#endif + if (RtcSettings.valid != RTC_MEM_VALID) { + memset(&RtcSettings, 0, sizeof(RtcSettings)); + RtcSettings.valid = RTC_MEM_VALID; + RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; + RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; + RtcSettings.energy_usage = Settings.energy_usage; + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; + } + RtcSettings.power = Settings.power; + RtcSettingsSave(); + } + rtc_settings_crc = GetRtcSettingsCrc(); +} + +bool RtcSettingsValid(void) +{ + return (RTC_MEM_VALID == RtcSettings.valid); +} + + + +uint32_t rtc_reboot_crc = 0; + +uint32_t GetRtcRebootCrc(void) +{ + uint32_t crc = 0; + uint8_t *bytes = (uint8_t*)&RtcReboot; + + for (uint32_t i = 0; i < sizeof(RtcReboot); i++) { + crc += bytes[i]*(i+1); + } + return crc; +} + +void RtcRebootSave(void) +{ + if (GetRtcRebootCrc() != rtc_reboot_crc) { + RtcReboot.valid = RTC_MEM_VALID; +#ifdef ESP8266 + ESP.rtcUserMemoryWrite(100 - sizeof(RtcReboot), (uint32_t*)&RtcReboot, sizeof(RtcReboot)); +#else + RtcDataReboot = RtcReboot; +#endif + rtc_reboot_crc = GetRtcRebootCrc(); + } +} + +void RtcRebootReset(void) +{ + RtcReboot.fast_reboot_count = 0; + RtcRebootSave(); +} + +void RtcRebootLoad(void) +{ +#ifdef ESP8266 + ESP.rtcUserMemoryRead(100 - sizeof(RtcReboot), (uint32_t*)&RtcReboot, sizeof(RtcReboot)); +#else + RtcReboot = RtcDataReboot; +#endif + if (RtcReboot.valid != RTC_MEM_VALID) { + memset(&RtcReboot, 0, sizeof(RtcReboot)); + RtcReboot.valid = RTC_MEM_VALID; + + RtcRebootSave(); + } + rtc_reboot_crc = GetRtcRebootCrc(); +} + +bool RtcRebootValid(void) +{ + return (RTC_MEM_VALID == RtcReboot.valid); +} +# 155 "/workspace/Tasmota/tasmota/settings.ino" +extern "C" { +#include "spi_flash.h" +} +#include "eboot_command.h" + +#ifdef ESP8266 + +#if AUTOFLASHSIZE + +#include "flash_hal.h" + + +const uint32_t SPIFFS_END = (FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#else + +extern "C" uint32_t _FS_end; + +const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#endif + + +const uint32_t SETTINGS_LOCATION = SPIFFS_END; + +#endif + + +const uint8_t CFG_ROTATES = 8; + +uint32_t settings_location = SETTINGS_LOCATION; +uint32_t settings_crc32 = 0; +uint8_t *settings_buffer = nullptr; + + + + + +void SetFlashModeDout(void) +{ +#ifdef ESP8266 + uint8_t *_buffer; + uint32_t address; + + eboot_command ebcmd; + eboot_command_read(&ebcmd); + address = ebcmd.args[0]; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != 3) { + _buffer[2] = 3; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + } + delete[] _buffer; +#endif +} + +bool VersionCompatible(void) +{ +#ifdef ESP8266 + + if (Settings.flag3.compatibility_check) { + return true; + } + + eboot_command ebcmd; + eboot_command_read(&ebcmd); + uint32_t start_address = ebcmd.args[0]; + uint32_t end_address = start_address + (ebcmd.args[2] & 0xFFFFF000) + FLASH_SECTOR_SIZE; + uint32_t* buffer = new uint32_t[FLASH_SECTOR_SIZE / 4]; + + uint32_t version[3] = { 0 }; + bool found = false; + for (uint32_t address = start_address; address < end_address; address = address + FLASH_SECTOR_SIZE) { + ESP.flashRead(address, (uint32_t*)buffer, FLASH_SECTOR_SIZE); + if ((address == start_address) && (0x1F == (buffer[0] & 0xFF))) { + version[1] = 0xFFFFFFFF; + found = true; + } else { + for (uint32_t i = 0; i < (FLASH_SECTOR_SIZE / 4); i++) { + version[0] = version[1]; + version[1] = version[2]; + version[2] = buffer[i]; + if ((MARKER_START == version[0]) && (MARKER_END == version[2])) { + found = true; + break; + } + } + } + if (found) { break; } + } + delete[] buffer; + + if (!found) { version[1] = 0; } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("OTA: Version 0x%08X, Compatible 0x%08X"), version[1], VERSION_COMPATIBLE); + + if (version[1] < VERSION_COMPATIBLE) { + uint32_t eboot_magic = 0; + ESP.rtcUserMemoryWrite(0, (uint32_t*)&eboot_magic, sizeof(eboot_magic)); + return false; + } + +#endif + + return true; +} + +void SettingsBufferFree(void) +{ + if (settings_buffer != nullptr) { + free(settings_buffer); + settings_buffer = nullptr; + } +} + +bool SettingsBufferAlloc(void) +{ + SettingsBufferFree(); + if (!(settings_buffer = (uint8_t *)malloc(sizeof(Settings)))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_UPLOAD_ERR_2)); + return false; + } + return true; +} + +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) +{ + uint16_t crc = 0; + + for (uint32_t i = 0; i < size; i++) { + if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } + } + return crc; +} + +uint16_t GetSettingsCrc(void) +{ + + uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(Settings); + return GetCfgCrc16((uint8_t*)&Settings, size); +} + +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) +{ + + uint32_t crc = 0; + + while (size--) { + crc ^= *bytes++; + for (uint32_t j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + } + } + return ~crc; +} + +uint32_t GetSettingsCrc32(void) +{ + return GetCfgCrc32((uint8_t*)&Settings, sizeof(Settings) -4); +} + +void SettingsSaveAll(void) +{ + if (Settings.flag.save_state) { + Settings.power = power; + } else { + Settings.power = 0; + } + XsnsCall(FUNC_SAVE_BEFORE_RESTART); + XdrvCall(FUNC_SAVE_BEFORE_RESTART); + SettingsSave(0); +} + + + + + +void UpdateQuickPowerCycle(bool update) { +#ifndef FIRMWARE_MINIMAL + if (Settings.flag3.fast_power_cycle_disable) { return; } + + const uint32_t QPC_COUNT = 7; + const uint32_t QPC_SIGNATURE = 0xFFA55AFF; + +#ifdef ESP8266 + const uint32_t qpc_sector = SETTINGS_LOCATION - CFG_ROTATES; + const uint32_t qpc_location = qpc_sector * SPI_FLASH_SEC_SIZE; + + uint32_t qpc_buffer[QPC_COUNT +1]; + ESP.flashRead(qpc_location, (uint32*)&qpc_buffer, sizeof(qpc_buffer)); + if (update && (QPC_SIGNATURE == qpc_buffer[0])) { + uint32_t counter = 1; + while ((0 == qpc_buffer[counter]) && (counter <= QPC_COUNT)) { counter++; } + if (QPC_COUNT == counter) { + SettingsErase(3); + EspRestart(); + } else { + qpc_buffer[0] = 0; + ESP.flashWrite(qpc_location + (counter * 4), (uint32*)&qpc_buffer, 4); + AddLog_P2(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter); + } + } + else if ((qpc_buffer[0] != QPC_SIGNATURE) || (0 == qpc_buffer[1])) { + qpc_buffer[0] = QPC_SIGNATURE; + + if (ESP.flashEraseSector(qpc_sector)) { + ESP.flashWrite(qpc_location, (uint32*)&qpc_buffer, 4); + AddLog_P2(LOG_LEVEL_INFO, PSTR("QPC: Reset")); + } + } +#else + uint32_t pc_register; + QPCRead(&pc_register, sizeof(pc_register)); + if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AF0)) { + uint32_t counter = pc_register & 0xF; + if (0xF == counter) { counter = 0; } + counter++; + if (QPC_COUNT == counter) { + SettingsErase(3); + EspRestart(); + } else { + pc_register = 0xFFA55AF0 | counter; + QPCWrite(&pc_register, sizeof(pc_register)); + AddLog_P2(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter); + } + } + else if (pc_register != QPC_SIGNATURE) { + pc_register = QPC_SIGNATURE; + QPCWrite(&pc_register, sizeof(pc_register)); + AddLog_P2(LOG_LEVEL_INFO, PSTR("QPC: Reset")); + } +#endif + +#endif +} + + + + + +uint32_t GetSettingsTextLen(void) { + char* position = Settings.text_pool; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + } + return position - Settings.text_pool; +} + +bool settings_text_mutex = false; +uint32_t settings_text_busy_count = 0; + +bool SettingsUpdateFinished(void) { + uint32_t wait_loop = 10; + while (settings_text_mutex && wait_loop) { + yield(); + delayMicroseconds(1); + wait_loop--; + } + return (wait_loop > 0); +} + +bool SettingsUpdateText(uint32_t index, const char* replace_me) { + if (index >= SET_MAX) { + return false; + } + + + uint32_t replace_len = strlen_P(replace_me); + char replace[replace_len +1]; + memcpy_P(replace, replace_me, sizeof(replace)); + uint32_t index_save = index; + + uint32_t start_pos = 0; + uint32_t end_pos = 0; + char* position = Settings.text_pool; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + if (1 == index) { + start_pos = position - Settings.text_pool; + } + else if (0 == index) { + end_pos = position - Settings.text_pool -1; + } + index--; + } + uint32_t char_len = position - Settings.text_pool; + + uint32_t current_len = end_pos - start_pos; + int diff = replace_len - current_len; + + + + + int too_long = (char_len + diff) - settings_text_size; + if (too_long > 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long); + return false; + } + + if (settings_text_mutex && !SettingsUpdateFinished()) { + settings_text_busy_count++; + } else { + settings_text_mutex = true; + + if (diff != 0) { + + memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos); + } + + memmove_P(Settings.text_pool + start_pos, replace, replace_len); + + memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff); + + settings_text_mutex = false; + } + +#ifdef DEBUG_FUNC_SETTINGSUPDATETEXT + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d, Id %02d = \"%s\""), GetSettingsTextLen(), settings_text_size, settings_text_busy_count, index_save, replace); +#else + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count); +#endif + + return true; +} + +char* SettingsText(uint32_t index) +{ + char* position = Settings.text_pool; + + if (index >= SET_MAX) { + position += settings_text_size -1; + } else { + SettingsUpdateFinished(); + for (;index > 0; index--) { + while (*position++ != '\0') { } + } + } + return position; +} + + + + + +void UpdateBackwardCompatibility(void) +{ + + strlcpy(Settings.user_template_name, SettingsText(SET_TEMPLATE_NAME), sizeof(Settings.user_template_name)); +} + +uint32_t GetSettingsAddress(void) +{ + return settings_location * SPI_FLASH_SEC_SIZE; +} + +void SettingsSave(uint8_t rotate) +{ +# 525 "/workspace/Tasmota/tasmota/settings.ino" +#ifndef FIRMWARE_MINIMAL + UpdateBackwardCompatibility(); + if ((GetSettingsCrc32() != settings_crc32) || rotate) { + if (1 == rotate) { + stop_flash_rotate = 1; + } + if (2 == rotate) { + settings_location = SETTINGS_LOCATION +1; + } + if (stop_flash_rotate) { + settings_location = SETTINGS_LOCATION; + } else { + settings_location--; + if (settings_location <= (SETTINGS_LOCATION - CFG_ROTATES)) { + settings_location = SETTINGS_LOCATION; + } + } + + Settings.save_flag++; + if (UtcTime() > START_VALID_TIME) { + Settings.cfg_timestamp = UtcTime(); + } else { + Settings.cfg_timestamp++; + } + Settings.cfg_size = sizeof(Settings); + Settings.cfg_crc = GetSettingsCrc(); + Settings.cfg_crc32 = GetSettingsCrc32(); + +#ifdef ESP8266 + if (ESP.flashEraseSector(settings_location)) { + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); + } + + if (!stop_flash_rotate && rotate) { + for (uint32_t i = 1; i < CFG_ROTATES; i++) { + ESP.flashEraseSector(settings_location -i); + delay(1); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(Settings)); +#else + SettingsWrite(&Settings, sizeof(Settings)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "Saved, " D_COUNT " %d, " D_BYTES " %d"), Settings.save_flag, sizeof(Settings)); +#endif + + settings_crc32 = Settings.cfg_crc32; + } +#endif + RtcSettingsSave(); +} + +void SettingsLoad(void) { +#ifdef ESP8266 + + + settings_location = 0; + uint32_t save_flag = 0; + uint32_t flash_location = SETTINGS_LOCATION; + for (uint32_t i = 0; i < CFG_ROTATES; i++) { + ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); + if ((Settings.cfg_crc32 != 0xFFFFFFFF) && (Settings.cfg_crc32 != 0x00000000) && (Settings.cfg_crc32 == GetSettingsCrc32())) { + if (Settings.save_flag > save_flag) { + save_flag = Settings.save_flag; + settings_location = flash_location; + if (Settings.flag.stop_flash_rotate && (0 == i)) { + break; + } + } + } + flash_location--; + delay(1); + } + if (settings_location > 0) { + ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings)); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); + } +#else + SettingsRead(&Settings, sizeof(Settings)); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded, " D_COUNT " %lu"), Settings.save_flag); +#endif + +#ifndef FIRMWARE_MINIMAL + if ((0 == settings_location) || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { + SettingsDefault(); + } + settings_crc32 = GetSettingsCrc32(); +#endif + + RtcSettingsLoad(); +} + +void EspErase(uint32_t start_sector, uint32_t end_sector) +{ + bool serial_output = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); + for (uint32_t sector = start_sector; sector < end_sector; sector++) { + + bool result = ESP.flashEraseSector(sector); + + + + if (serial_output) { + Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR); + delay(10); + } else { + yield(); + } + OsWatchLoop(); + } +} + +#ifdef ESP8266 +void SettingsErase(uint8_t type) +{ +# 654 "/workspace/Tasmota/tasmota/settings.ino" +#ifndef FIRMWARE_MINIMAL + uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; + uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; + if (1 == type) { + + _sectorStart = (ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE) - 4; + } + else if (2 == type) { + _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; + _sectorEnd = SETTINGS_LOCATION +1; + } + else if (3 == type) { + _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; + _sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; + } + else if (4 == type) { + + _sectorStart = SETTINGS_LOCATION +1; + _sectorEnd = _sectorStart +1; + } + + + + + + + else { + return; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " from 0x%08X to 0x%08X"), _sectorStart * SPI_FLASH_SEC_SIZE, (_sectorEnd * SPI_FLASH_SEC_SIZE) -1); + + + EsptoolErase(_sectorStart, _sectorEnd); +#endif +} +#endif + +void SettingsSdkErase(void) +{ + WiFi.disconnect(false); + SettingsErase(1); + delay(1000); +} + + + +void SettingsDefault(void) +{ + AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS)); + SettingsDefaultSet1(); + SettingsDefaultSet2(); + SettingsSave(2); +} + +void SettingsDefaultSet1(void) +{ + memset(&Settings, 0x00, sizeof(Settings)); + + Settings.cfg_holder = (uint16_t)CFG_HOLDER; + Settings.cfg_size = sizeof(Settings); + + Settings.version = VERSION; + + +} + + +const uint8_t default_fingerprint1[] PROGMEM = { MQTT_FINGERPRINT1 }; +const uint8_t default_fingerprint2[] PROGMEM = { MQTT_FINGERPRINT2 }; + +void SettingsDefaultSet2(void) +{ + memset((char*)&Settings +16, 0x00, sizeof(Settings) -16); + + + SysBitfield flag = { 0 }; + SysBitfield2 flag2 = { 0 }; + SysBitfield3 flag3 = { 0 }; + SysBitfield4 flag4 = { 0 }; + SysBitfield5 flag5 = { 0 }; + +#ifdef ESP8266 + Settings.gpio16_converted = 0xF5A0; + +#endif +#ifdef ESP32 + Settings.config_version = 1; +#endif + + flag.stop_flash_rotate |= APP_FLASH_CYCLE; + flag.global_state |= APP_ENABLE_LEDLINK; + flag3.sleep_normal |= APP_NORMAL_SLEEP; + flag3.no_power_feedback |= APP_NO_RELAY_SCAN; + flag3.fast_power_cycle_disable |= APP_DISABLE_POWERCYCLE; + flag3.bootcount_update |= DEEPSLEEP_BOOTCOUNT; + flag3.compatibility_check |= OTA_COMPATIBILITY; + Settings.save_data = SAVE_DATA; + Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; + Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; + Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; + Settings.sleep = APP_SLEEP; + if (Settings.sleep < 50) { + Settings.sleep = 50; + } + + + flag.interlock |= APP_INTERLOCK_MODE; + Settings.interlock[0] = APP_INTERLOCK_GROUP_1; + Settings.interlock[1] = APP_INTERLOCK_GROUP_2; + Settings.interlock[2] = APP_INTERLOCK_GROUP_3; + Settings.interlock[3] = APP_INTERLOCK_GROUP_4; + Settings.module = MODULE; + Settings.fallback_module = FALLBACK_MODULE; + ModuleDefault(WEMOS); + + SettingsUpdateText(SET_FRIENDLYNAME1, PSTR(FRIENDLY_NAME)); + SettingsUpdateText(SET_FRIENDLYNAME2, PSTR(FRIENDLY_NAME"2")); + SettingsUpdateText(SET_FRIENDLYNAME3, PSTR(FRIENDLY_NAME"3")); + SettingsUpdateText(SET_FRIENDLYNAME4, PSTR(FRIENDLY_NAME"4")); + SettingsUpdateText(SET_DEVICENAME, SettingsText(SET_FRIENDLYNAME1)); + SettingsUpdateText(SET_OTAURL, PSTR(OTA_URL)); + + + flag.save_state |= SAVE_STATE; + Settings.power = APP_POWER; + Settings.poweronstate = APP_POWERON_STATE; + Settings.blinktime = APP_BLINKTIME; + Settings.blinkcount = APP_BLINKCOUNT; + Settings.ledstate = APP_LEDSTATE; + Settings.ledmask = APP_LEDMASK; + + Settings.ledpwm_on = 255; + + Settings.pulse_timer[0] = APP_PULSETIME; + + + + Settings.serial_config = TS_SERIAL_8N1; + Settings.baudrate = APP_BAUDRATE / 300; + Settings.sbaudrate = SOFT_BAUDRATE / 300; + Settings.serial_delimiter = 0xff; + Settings.seriallog_level = SERIAL_LOG_LEVEL; + + + flag4.network_ethernet |= 1; +#ifdef ESP32 + Settings.eth_type = ETH_TYPE; + Settings.eth_clk_mode = ETH_CLKMODE; + Settings.eth_address = ETH_ADDR; +#endif + + + flag4.network_wifi |= 1; + flag3.use_wifi_scan |= WIFI_SCAN_AT_RESTART; + flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY; + Settings.wifi_output_power = 170; + Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL; + ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); + ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); + ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); + ParseIp(&Settings.ip_address[3], WIFI_DNS); + Settings.sta_config = WIFI_CONFIG_TOOL; + + SettingsUpdateText(SET_STASSID1, PSTR(STA_SSID1)); + SettingsUpdateText(SET_STASSID2, PSTR(STA_SSID2)); + SettingsUpdateText(SET_STAPWD1, PSTR(STA_PASS1)); + SettingsUpdateText(SET_STAPWD2, PSTR(STA_PASS2)); + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + + + SettingsUpdateText(SET_SYSLOG_HOST, PSTR(SYS_LOG_HOST)); + Settings.syslog_port = SYS_LOG_PORT; + Settings.syslog_level = SYS_LOG_LEVEL; + + + flag2.emulation |= EMULATION; + flag4.alexa_gen_1 |= EMULATION_HUE_1ST_GEN; + flag3.gui_hostname_ip |= GUI_SHOW_HOSTNAME; + flag3.mdns_enabled |= MDNS_ENABLED; + Settings.webserver = WEB_SERVER; + Settings.weblog_level = WEB_LOG_LEVEL; + SettingsUpdateText(SET_WEBPWD, PSTR(WEB_PASSWORD)); + SettingsUpdateText(SET_CORS, PSTR(CORS_DOMAIN)); + + + flag.button_restrict |= KEY_DISABLE_MULTIPRESS; + flag.button_swap |= KEY_SWAP_DOUBLE_PRESS; + flag.button_single |= KEY_ONLY_SINGLE_PRESS; + Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; + + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } + + + flag.mqtt_enabled |= MQTT_USE; + flag.mqtt_response |= MQTT_RESULT_COMMAND; + flag.mqtt_offline |= MQTT_LWT_MESSAGE; + flag.mqtt_power_retain |= MQTT_POWER_RETAIN; + flag.mqtt_button_retain |= MQTT_BUTTON_RETAIN; + flag.mqtt_switch_retain |= MQTT_SWITCH_RETAIN; + flag.mqtt_sensor_retain |= MQTT_SENSOR_RETAIN; + + flag.device_index_enable |= MQTT_POWER_FORMAT; + flag3.time_append_timezone |= MQTT_APPEND_TIMEZONE; + flag3.button_switch_force_local |= MQTT_BUTTON_SWITCH_FORCE_LOCAL; + flag3.no_hold_retain |= MQTT_NO_HOLD_RETAIN; + flag3.use_underscore |= MQTT_INDEX_SEPARATOR; + flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT; + SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); + Settings.mqtt_port = MQTT_PORT; + SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); + SettingsUpdateText(SET_MQTT_USER, MQTT_USER); + SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); + SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); + SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); + Settings.mqtt_retry = MQTT_RETRY_SECS; + SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); + SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); + SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); + SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); + SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); + memcpy_P(Settings.mqtt_fingerprint[0], default_fingerprint1, sizeof(default_fingerprint1)); + memcpy_P(Settings.mqtt_fingerprint[1], default_fingerprint2, sizeof(default_fingerprint2)); + Settings.tele_period = TELE_PERIOD; + Settings.mqttlog_level = MQTT_LOG_LEVEL; + + + flag.no_power_on_check |= ENERGY_VOLTAGE_ALWAYS; + flag2.current_resolution |= 3; + + + flag2.energy_resolution |= ENERGY_RESOLUTION; + flag3.dds2382_model |= ENERGY_DDS2382_MODE; + flag3.hardware_energy_total |= ENERGY_HARDWARE_TOTALS; + Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; + + + + Settings.energy_power_calibration = HLW_PREF_PULSE; + Settings.energy_voltage_calibration = HLW_UREF_PULSE; + Settings.energy_current_calibration = HLW_IREF_PULSE; +# 911 "/workspace/Tasmota/tasmota/settings.ino" + Settings.energy_max_power_limit_hold = MAX_POWER_HOLD; + Settings.energy_max_power_limit_window = MAX_POWER_WINDOW; + + Settings.energy_max_power_safe_limit_hold = SAFE_POWER_HOLD; + Settings.energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; + + + + RtcSettings.energy_kWhtotal = 0; + + memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); + Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; + + + flag.ir_receive_decimal |= IR_DATA_RADIX; + flag3.receive_raw |= IR_ADD_RAW_DATA; + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + + + flag.rf_receive_decimal |= RF_DATA_RADIX; + + memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); + + + Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; +# 946 "/workspace/Tasmota/tasmota/settings.ino" + flag.temperature_conversion |= TEMP_CONVERSION; + flag.pressure_conversion |= PRESSURE_CONVERSION; + flag2.pressure_resolution |= PRESSURE_RESOLUTION; + flag2.humidity_resolution |= HUMIDITY_RESOLUTION; + flag2.temperature_resolution |= TEMP_RESOLUTION; + flag3.ds18x20_internal_pullup |= DS18X20_PULL_UP; + flag3.counter_reset_on_tele |= COUNTER_RESET; + + + + + + + flag2.calc_resolution |= CALC_RESOLUTION; + + + flag3.timers_enable |= TIMERS_ENABLED; + + + flag.hass_light |= HASS_AS_LIGHT; + flag.hass_discovery |= HOME_ASSISTANT_DISCOVERY_ENABLE; + flag3.hass_tele_on_power |= TELE_ON_POWER; + + + flag.knx_enabled |= KNX_ENABLED; + flag.knx_enable_enhancement |= KNX_ENHANCED; + + + flag.pwm_control |= LIGHT_MODE; + flag.ws_clock_reverse |= LIGHT_CLOCK_DIRECTION; + flag.light_signal |= LIGHT_PAIRS_CO2; + flag.not_power_linked |= LIGHT_POWER_CONTROL; + flag.decimal_text |= LIGHT_COLOR_RADIX; + flag3.pwm_multi_channels |= LIGHT_CHANNEL_MODE; + flag3.slider_dimmer_stay_on |= LIGHT_SLIDER_POWER; + flag4.alexa_ct_range |= LIGHT_ALEXA_CT_RANGE; + flag4.pwm_ct_mode |= LIGHT_PWM_CT_MODE; + flag4.white_blend_mode |= LIGHT_WHITE_BLEND_MODE; + flag4.virtual_ct |= LIGHT_VIRTUAL_CT; + flag4.virtual_ct_cw |= LIGHT_VIRTUAL_CT_CW; + + Settings.pwm_frequency = PWM_FREQ; + Settings.pwm_range = PWM_RANGE; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + Settings.light_color[i] = DEFAULT_LIGHT_COMPONENT; + + } + Settings.light_correction = 1; + Settings.light_dimmer = DEFAULT_LIGHT_DIMMER; + + Settings.light_speed = 1; + + Settings.light_width = 1; + + Settings.light_pixels = WS2812_LEDS; + + Settings.ws_width[WS_SECOND] = 1; + Settings.ws_color[WS_SECOND][WS_RED] = 255; + + Settings.ws_color[WS_SECOND][WS_BLUE] = 255; + Settings.ws_width[WS_MINUTE] = 3; + + Settings.ws_color[WS_MINUTE][WS_GREEN] = 255; + + Settings.ws_width[WS_HOUR] = 5; + Settings.ws_color[WS_HOUR][WS_RED] = 255; + + + + Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; + Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; + + + + Settings.display_mode = 1; + Settings.display_refresh = 2; + Settings.display_rows = 2; + Settings.display_cols[0] = 16; + Settings.display_cols[1] = 8; + Settings.display_dimmer = 1; + Settings.display_size = 1; + Settings.display_font = 1; + + Settings.display_address[0] = MTX_ADDRESS1; + Settings.display_address[1] = MTX_ADDRESS2; + Settings.display_address[2] = MTX_ADDRESS3; + Settings.display_address[3] = MTX_ADDRESS4; + Settings.display_address[4] = MTX_ADDRESS5; + Settings.display_address[5] = MTX_ADDRESS6; + Settings.display_address[6] = MTX_ADDRESS7; + Settings.display_address[7] = MTX_ADDRESS8; + + + if (((APP_TIMEZONE > -14) && (APP_TIMEZONE < 15)) || (99 == APP_TIMEZONE)) { + Settings.timezone = APP_TIMEZONE; + Settings.timezone_minutes = 0; + } else { + Settings.timezone = APP_TIMEZONE / 60; + Settings.timezone_minutes = abs(APP_TIMEZONE % 60); + } + SettingsUpdateText(SET_NTPSERVER1, PSTR(NTP_SERVER1)); + SettingsUpdateText(SET_NTPSERVER2, PSTR(NTP_SERVER2)); + SettingsUpdateText(SET_NTPSERVER3, PSTR(NTP_SERVER3)); + for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) { + SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); + } + Settings.latitude = (int)((double)LATITUDE * 1000000); + Settings.longitude = (int)((double)LONGITUDE * 1000000); + SettingsResetStd(); + SettingsResetDst(); + + Settings.button_debounce = KEY_DEBOUNCE_TIME; + Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; + + for (uint32_t j = 0; j < 5; j++) { + Settings.rgbwwTable[j] = 255; + } + + Settings.novasds_startingoffset = STARTING_OFFSET; + + SettingsDefaultWebColor(); + + memset(&Settings.monitors, 0xFF, 20); + SettingsEnableAllI2cDrivers(); + + + flag3.tuya_apply_o20 |= TUYA_SETOPTION_20; + flag3.tuya_serial_mqtt_publish |= MQTT_TUYA_RECEIVED; + + flag3.buzzer_enable |= BUZZER_ENABLE; + flag3.shutter_mode |= SHUTTER_SUPPORT; + flag3.pcf8574_ports_inverted |= PCF8574_INVERT_PORTS; + flag4.zigbee_use_names |= ZIGBEE_FRIENDLY_NAMES; + flag4.remove_zbreceived |= ZIGBEE_RMV_ZBRECEIVED; + flag4.zb_index_ep |= ZIGBEE_INDEX_EP; + flag4.mqtt_tls |= MQTT_TLS_ENABLED; + flag4.mqtt_no_retain |= MQTT_NO_RETAIN; + +#ifdef USER_TEMPLATE + JsonTemplate((char *)USER_TEMPLATE); +#endif + + Settings.flag = flag; + Settings.flag2 = flag2; + Settings.flag3 = flag3; + Settings.flag4 = flag4; +} + + + +void SettingsResetStd(void) +{ + Settings.tflag[0].hemis = TIME_STD_HEMISPHERE; + Settings.tflag[0].week = TIME_STD_WEEK; + Settings.tflag[0].dow = TIME_STD_DAY; + Settings.tflag[0].month = TIME_STD_MONTH; + Settings.tflag[0].hour = TIME_STD_HOUR; + Settings.toffset[0] = TIME_STD_OFFSET; +} + +void SettingsResetDst(void) +{ + Settings.tflag[1].hemis = TIME_DST_HEMISPHERE; + Settings.tflag[1].week = TIME_DST_WEEK; + Settings.tflag[1].dow = TIME_DST_DAY; + Settings.tflag[1].month = TIME_DST_MONTH; + Settings.tflag[1].hour = TIME_DST_HOUR; + Settings.toffset[1] = TIME_DST_OFFSET; +} + +void SettingsDefaultWebColor(void) +{ + char scolor[10]; + for (uint32_t i = 0; i < COL_LAST; i++) { + WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); + } +} + +void SettingsEnableAllI2cDrivers(void) +{ + Settings.i2c_drivers[0] = 0xFFFFFFFF; + Settings.i2c_drivers[1] = 0xFFFFFFFF; + Settings.i2c_drivers[2] = 0xFFFFFFFF; +} + + + +void SettingsDelta(void) +{ + if (Settings.version != VERSION) { + +#ifdef ESP8266 + if (Settings.version < 0x07000002) { + Settings.web_color2[0][0] = Settings.web_color[0][0]; + Settings.web_color2[0][1] = Settings.web_color[0][1]; + Settings.web_color2[0][2] = Settings.web_color[0][2]; + } + if (Settings.version < 0x07000003) { + SettingsEnableAllI2cDrivers(); + } + if (Settings.version < 0x07000004) { + Settings.ex_wifi_output_power = 170; + } + if (Settings.version < 0x07010202) { + Settings.ex_serial_config = TS_SERIAL_8N1; + } + if (Settings.version < 0x07010204) { + if (Settings.flag3.mqtt_buttons == 1) { + strlcpy(Settings.ex_cors_domain, CORS_ENABLED_ALL, sizeof(Settings.ex_cors_domain)); + } else { + Settings.ex_cors_domain[0] = 0; + } + } + if (Settings.version < 0x07010205) { + Settings.seriallog_level = Settings.ex_seriallog_level; + Settings.sta_config = Settings.ex_sta_config; + Settings.sta_active = Settings.ex_sta_active; + memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); + } + if (Settings.version < 0x07010206) { + Settings.flag4 = Settings.ex_flag4; + Settings.mqtt_port = Settings.ex_mqtt_port; + memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); + } + if (Settings.version < 0x08000000) { + char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); + char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); + char temp22[strlen(Settings.ex_mqtt_prefix[1]) +1]; strncpy(temp22, Settings.ex_mqtt_prefix[1], sizeof(temp22)); + char temp23[strlen(Settings.ex_mqtt_prefix[2]) +1]; strncpy(temp23, Settings.ex_mqtt_prefix[2], sizeof(temp23)); + char temp31[strlen(Settings.ex_sta_ssid[0]) +1]; strncpy(temp31, Settings.ex_sta_ssid[0], sizeof(temp31)); + char temp32[strlen(Settings.ex_sta_ssid[1]) +1]; strncpy(temp32, Settings.ex_sta_ssid[1], sizeof(temp32)); + char temp41[strlen(Settings.ex_sta_pwd[0]) +1]; strncpy(temp41, Settings.ex_sta_pwd[0], sizeof(temp41)); + char temp42[strlen(Settings.ex_sta_pwd[1]) +1]; strncpy(temp42, Settings.ex_sta_pwd[1], sizeof(temp42)); + char temp5[strlen(Settings.ex_hostname) +1]; strncpy(temp5, Settings.ex_hostname, sizeof(temp5)); + char temp6[strlen(Settings.ex_syslog_host) +1]; strncpy(temp6, Settings.ex_syslog_host, sizeof(temp6)); + char temp7[strlen(Settings.ex_mqtt_host) +1]; strncpy(temp7, Settings.ex_mqtt_host, sizeof(temp7)); + char temp8[strlen(Settings.ex_mqtt_client) +1]; strncpy(temp8, Settings.ex_mqtt_client, sizeof(temp8)); + char temp9[strlen(Settings.ex_mqtt_user) +1]; strncpy(temp9, Settings.ex_mqtt_user, sizeof(temp9)); + char temp10[strlen(Settings.ex_mqtt_pwd) +1]; strncpy(temp10, Settings.ex_mqtt_pwd, sizeof(temp10)); + char temp11[strlen(Settings.ex_mqtt_topic) +1]; strncpy(temp11, Settings.ex_mqtt_topic, sizeof(temp11)); + char temp12[strlen(Settings.ex_button_topic) +1]; strncpy(temp12, Settings.ex_button_topic, sizeof(temp12)); + char temp13[strlen(Settings.ex_mqtt_grptopic) +1]; strncpy(temp13, Settings.ex_mqtt_grptopic, sizeof(temp13)); + + memset(Settings.text_pool, 0x00, settings_text_size); + SettingsUpdateText(SET_OTAURL, temp); + SettingsUpdateText(SET_MQTTPREFIX1, temp21); + SettingsUpdateText(SET_MQTTPREFIX2, temp22); + SettingsUpdateText(SET_MQTTPREFIX3, temp23); + SettingsUpdateText(SET_STASSID1, temp31); + SettingsUpdateText(SET_STASSID2, temp32); + SettingsUpdateText(SET_STAPWD1, temp41); + SettingsUpdateText(SET_STAPWD2, temp42); + SettingsUpdateText(SET_HOSTNAME, temp5); + SettingsUpdateText(SET_SYSLOG_HOST, temp6); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + if (!strlen(Settings.ex_mqtt_user)) { + SettingsUpdateText(SET_MQTT_HOST, temp7); + SettingsUpdateText(SET_MQTT_USER, temp9); + } else { + char aws_mqtt_host[66]; + snprintf_P(aws_mqtt_host, sizeof(aws_mqtt_host), PSTR("%s%s"), temp9, temp7); + SettingsUpdateText(SET_MQTT_HOST, aws_mqtt_host); + SettingsUpdateText(SET_MQTT_USER, ""); + } +#else + SettingsUpdateText(SET_MQTT_HOST, temp7); + SettingsUpdateText(SET_MQTT_USER, temp9); +#endif + SettingsUpdateText(SET_MQTT_CLIENT, temp8); + SettingsUpdateText(SET_MQTT_PWD, temp10); + SettingsUpdateText(SET_MQTT_TOPIC, temp11); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, temp12); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, temp13); + + SettingsUpdateText(SET_WEBPWD, Settings.ex_web_password); + SettingsUpdateText(SET_CORS, Settings.ex_cors_domain); + SettingsUpdateText(SET_MQTT_FULLTOPIC, Settings.ex_mqtt_fulltopic); + + SettingsUpdateText(SET_STATE_TXT1, Settings.ex_state_text[0]); + SettingsUpdateText(SET_STATE_TXT2, Settings.ex_state_text[1]); + SettingsUpdateText(SET_STATE_TXT3, Settings.ex_state_text[2]); + SettingsUpdateText(SET_STATE_TXT4, Settings.ex_state_text[3]); + SettingsUpdateText(SET_NTPSERVER1, Settings.ex_ntp_server[0]); + SettingsUpdateText(SET_NTPSERVER2, Settings.ex_ntp_server[1]); + SettingsUpdateText(SET_NTPSERVER3, Settings.ex_ntp_server[2]); + SettingsUpdateText(SET_MEM1, Settings.script_pram[0]); + SettingsUpdateText(SET_MEM2, Settings.script_pram[1]); + SettingsUpdateText(SET_MEM3, Settings.script_pram[2]); + SettingsUpdateText(SET_MEM4, Settings.script_pram[3]); + SettingsUpdateText(SET_MEM5, Settings.script_pram[4]); + + + + + } + if (Settings.version < 0x08020003) { + SettingsUpdateText(SET_TEMPLATE_NAME, Settings.user_template_name); + Settings.zb_channel = 0; + } +#endif + + if (Settings.version < 0x08020004) { + Settings.flag3.mqtt_buttons = 0; +#ifdef ESP8266 + Settings.config_version = 0; +#endif +#ifdef ESP32 + Settings.config_version = 1; +#endif + } + if (Settings.version < 0x08020006) { +#ifdef ESP32 + Settings.module = WEMOS; + ModuleDefault(WEMOS); +#endif + + if (Settings.rules[0][0] == 0) { Settings.rules[0][1] = 0; } + if (Settings.rules[1][0] == 0) { Settings.rules[1][1] = 0; } + if (Settings.rules[2][0] == 0) { Settings.rules[2][1] = 0; } + } + if (Settings.version < 0x08030002) { + SettingsUpdateText(SET_DEVICENAME, SettingsText(SET_FRIENDLYNAME1)); + Settings.ledpwm_off = 0; + Settings.ledpwm_on = 255; + Settings.ledpwm_mask = 0; + } + if (Settings.version < 0x08030104) { + Settings.flag4.network_wifi = 1; + Settings.flag4.network_ethernet = 1; + } +#ifdef ESP32 + if (Settings.version < 0x08030105) { + Settings.eth_type = ETH_TYPE; + Settings.eth_clk_mode = ETH_CLKMODE; + Settings.eth_address = ETH_ADDR; + } +#endif + if (Settings.version < 0x08030106) { + Settings.fallback_module = FALLBACK_MODULE; + } + if (Settings.version < 0x08040003) { + Settings.energy_power_delta[0] = Settings.hass_new_discovery; + Settings.energy_power_delta[1] = 0; + Settings.energy_power_delta[2] = 0; + } +#ifdef ESP8266 + if (Settings.version < 0x09000002) { + char parameters[32]; + snprintf_P(parameters, sizeof(parameters), PSTR("%d,%d,%d,%d,%d"), + Settings.ex_adc_param_type, Settings.ex_adc_param1, Settings.ex_adc_param2, Settings.ex_adc_param3, Settings.ex_adc_param4); + SettingsUpdateText(SET_ADC_PARAM1, parameters); + } +#endif + + Settings.version = VERSION; + SettingsSave(1); + } + +} +# 1 "/workspace/Tasmota/tasmota/support.ino" +# 20 "/workspace/Tasmota/tasmota/support.ino" +IPAddress syslog_host_addr; +uint32_t syslog_host_hash = 0; + +extern "C" { +extern struct rst_info resetInfo; +} + + + + + +#include + +Ticker tickerOSWatch; + +const uint32_t OSWATCH_RESET_TIME = 120; + +static unsigned long oswatch_last_loop_time; +uint8_t oswatch_blocked_loop = 0; + +#ifndef USE_WS2812_DMA + +#endif + +#ifdef USE_KNX +bool knx_started = false; +#endif + +void OsWatchTicker(void) +{ + uint32_t t = millis(); + uint32_t last_run = t - oswatch_last_loop_time; + +#ifdef DEBUG_THEO + int32_t rssi = WiFi.RSSI(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP_getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run); +#endif + if (last_run >= (OSWATCH_RESET_TIME * 1000)) { + + RtcSettings.oswatch_blocked_loop = 1; + RtcSettingsSave(); + + + + + + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); + } +} + +void OsWatchInit(void) +{ + oswatch_blocked_loop = RtcSettings.oswatch_blocked_loop; + RtcSettings.oswatch_blocked_loop = 0; + oswatch_last_loop_time = millis(); + tickerOSWatch.attach_ms(((OSWATCH_RESET_TIME / 3) * 1000), OsWatchTicker); +} + +void OsWatchLoop(void) +{ + oswatch_last_loop_time = millis(); + +} + +bool OsWatchBlockedLoop(void) +{ + return oswatch_blocked_loop; +} + +uint32_t ResetReason(void) +{ +# 102 "/workspace/Tasmota/tasmota/support.ino" + return ESP_ResetInfoReason(); +} + +String GetResetReason(void) +{ + if (oswatch_blocked_loop) { + char buff[32]; + strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff)); + return String(buff); + } else { + return ESP_getResetReason(); + } +} +# 132 "/workspace/Tasmota/tasmota/support.ino" +String GetBinary8(uint8_t value, size_t count) { + if (count > 8) { count = 8; } + value <<= (8 - count); + String result; + result.reserve(count + 1); + for (uint32_t i = 0; i < count; i++) { + result += (value &0x80) ? '1' : '0'; + value <<= 1; + } + return result; +} + + +size_t strchrspn(const char *str1, int character) +{ + size_t ret = 0; + char *start = (char*)str1; + char *end = strchr(str1, character); + if (end) ret = end - start; + return ret; +} + +uint32_t ChrCount(const char *str, const char *delim) { + uint32_t count = 0; + char* read = (char*)str; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (ch == *delim) { count++; } + } + return count; +} + + +char* subStr(char* dest, char* str, const char *delim, int index) +{ + char *act; + char *sub = nullptr; + char *ptr; + int i; + + + strncpy(dest, str, strlen(str)+1); + for (i = 1, act = dest; i <= index; i++, act = nullptr) { + sub = strtok_r(act, delim, &ptr); + if (sub == nullptr) break; + } + sub = Trim(sub); + return sub; +} + +float CharToFloat(const char *str) +{ + + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + if (*pt == '\0') { return 0.0; } + + while ((*pt != '\0') && isblank(*pt)) { pt++; } + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt == '+') { pt++; } + + float left = 0; + if (*pt != '.') { + left = atoi(pt); + while (isdigit(*pt)) { pt++; } + } + + float right = 0; + if (*pt == '.') { + pt++; + uint32_t max_decimals = 0; + while ((max_decimals < 8) && isdigit(pt[max_decimals])) { max_decimals++; } + pt[max_decimals] = '\0'; + right = atoi(pt); + while (isdigit(*pt)) { + pt++; + right /= 10.0f; + } + } + + float result = left + right; + if (sign < 0) { + return -result; + } + return result; +} + +int TextToInt(char *str) +{ + char *p; + uint8_t radix = 10; + if ('#' == str[0]) { + radix = 16; + str++; + } + return strtol(str, &p, radix); +} + +char* ulltoa(unsigned long long value, char *str, int radix) +{ + char digits[64]; + char *dst = str; + int i = 0; + + + + do { + int n = value % radix; + digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; + value /= radix; + } while (value != 0); + + while (i > 0) { *dst++ = digits[--i]; } + + *dst = 0; + return str; +} + + + +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) +{ + + + + static const char * hex = "0123456789ABCDEF"; + int between = (inbetween) ? 3 : 2; + const unsigned char * pin = in; + char * pout = out; + for (; pin < in+insz; pout += between, pin++) { + pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF]; + pout[1] = hex[ pgm_read_byte(pin) & 0xF]; + if (inbetween) { pout[2] = inbetween; } + if (pout + 3 - out > outsz) { break; } + } + pout[(inbetween && insz) ? -1 : 0] = 0; + return out; +} + +char* Uint64toHex(uint64_t value, char *str, uint16_t bits) +{ + ulltoa(value, str, 16); + + int fill = 8; + if ((bits > 3) && (bits < 65)) { + fill = bits / 4; + if (bits % 4) { fill++; } + } + int len = strlen(str); + fill -= len; + if (fill > 0) { + memmove(str + fill, str, len +1); + memset(str, '0', fill); + } + return str; +} + +char* dtostrfd(double number, unsigned char prec, char *s) +{ + if ((isnan(number)) || (isinf(number))) { + strcpy(s, "null"); + return s; + } else { + return dtostrf(number, 1, prec, s); + } +} + +char* Unescape(char* buffer, uint32_t* size) +{ + uint8_t* read = (uint8_t*)buffer; + uint8_t* write = (uint8_t*)buffer; + int32_t start_size = *size; + int32_t end_size = *size; + uint8_t che = 0; + + + + while (start_size > 0) { + uint8_t ch = *read++; + start_size--; + if (ch != '\\') { + *write++ = ch; + } else { + if (start_size > 0) { + uint8_t chi = *read++; + start_size--; + end_size--; + switch (chi) { + case '\\': che = '\\'; break; + case 'a': che = '\a'; break; + case 'b': che = '\b'; break; + case 'e': che = '\e'; break; + case 'f': che = '\f'; break; + case 'n': che = '\n'; break; + case 'r': che = '\r'; break; + case 's': che = ' '; break; + case 't': che = '\t'; break; + case 'v': che = '\v'; break; + case 'x': { + uint8_t* start = read; + che = (uint8_t)strtol((const char*)read, (char**)&read, 16); + start_size -= (uint16_t)(read - start); + end_size -= (uint16_t)(read - start); + break; + } + case '"': che = '\"'; break; + + default : { + che = chi; + *write++ = ch; + end_size++; + } + } + *write++ = che; + } + } + } + *size = end_size; + *write++ = 0; + + + return buffer; +} + +char* RemoveSpace(char* p) { + + char* write = p; + char* read = p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (!isspace(ch)) { + *write++ = ch; + } + } + return p; +} + +char* RemoveControlCharacter(char* p) { + + char* write = p; + char* read = p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (!iscntrl(ch)) { + *write++ = ch; + } + } + *write++ = '\0'; + return p; +} + +char* ReplaceCommaWithDot(char* p) { + + char* write = (char*)p; + char* read = (char*)p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (ch == ',') { + ch = '.'; + } + *write++ = ch; + } + return p; +} + +char* LowerCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = tolower(ch); + } + return dest; +} + +char* UpperCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = toupper(ch); + } + return dest; +} + +char* UpperCase_P(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = pgm_read_byte(read++); + *write++ = toupper(ch); + } + return dest; +} + +char* Trim(char* p) +{ + if (*p != '\0') { + while ((*p != '\0') && isblank(*p)) { p++; } + char* q = p + strlen(p) -1; + while ((q >= p) && isblank(*q)) { q--; } + q++; + *q = '\0'; + } + return p; +} +# 479 "/workspace/Tasmota/tasmota/support.ino" +char* NoAlNumToUnderscore(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = (isalnum(ch) || ('\0' == ch)) ? ch : '_'; + } + return dest; +} + +char IndexSeparator(void) +{ + + + + + + + if (Settings.flag3.use_underscore) { + return '_'; + } else { + return '-'; + } +} + +void SetShortcutDefault(void) +{ + if ('\0' != XdrvMailbox.data[0]) { + XdrvMailbox.data[0] = '0' + SC_DEFAULT; + XdrvMailbox.data[1] = '\0'; + } +} + +uint8_t Shortcut(void) +{ + uint8_t result = 10; + + if ('\0' == XdrvMailbox.data[1]) { + if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) { + result = SC_CLEAR; + } else { + result = atoi(XdrvMailbox.data); + if (0 == result) { + result = 10; + } + } + } + return result; +} + +bool ValidIpAddress(const char* str) +{ + const char* p = str; + + while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } + return (*p == '\0'); +} + +bool ParseIp(uint32_t* addr, const char* str) +{ + uint8_t *part = (uint8_t*)addr; + uint8_t i; + + *addr = 0; + for (i = 0; i < 4; i++) { + part[i] = strtoul(str, nullptr, 10); + str = strchr(str, '.'); + if (str == nullptr || *str == '\0') { + break; + } + str++; + } + return (3 == i); +} + +uint32_t ParseParameters(uint32_t count, uint32_t *params) +{ + char *p; + uint32_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) { + params[i] = strtoul(str, nullptr, 0); + } + return i; +} + + +bool NewerVersion(char* version_str) +{ + uint32_t version = 0; + uint32_t i = 0; + char *str_ptr; + + char version_dup[strlen(version_str) +1]; + strncpy(version_dup, version_str, sizeof(version_dup)); + + for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { + int field = atoi(str); + + if ((field < 0) || (field > 255)) { + return false; + } + + version = (version << 8) + field; + + if ((2 == i) && isalpha(str[strlen(str)-1])) { + field = str[strlen(str)-1] & 0x1f; + version = (version << 8) + field; + i++; + } + } + + + if ((i < 2) || (i > sizeof(VERSION))) { + return false; + } + + + while (i < sizeof(VERSION)) { + version <<= 8; + i++; + } + + return (version > VERSION); +} + +char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) +{ + strncpy_P(dest, S_RSLT_POWER, size); + if ((devices_present + option) > 1) { + char sidx[8]; + snprintf_P(sidx, sizeof(sidx), PSTR("%d"), idx); + strncat(dest, sidx, size - strlen(dest) -1); + } + return dest; +} + +char* GetPowerDevice(char* dest, uint32_t idx, size_t size) +{ + return GetPowerDevice(dest, idx, size, 0); +} + +void GetEspHardwareType(void) +{ +#ifdef ESP8266 + + uint32_t efuse1 = *(uint32_t*)(0x3FF00050); + uint32_t efuse2 = *(uint32_t*)(0x3FF00054); + + + + is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) ); + if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { + is_8285 = false; + } +#else + is_8285 = false; +#endif +} + +String GetDeviceHardware(void) +{ + char buff[10]; +#ifdef ESP8266 + if (is_8285) { + strcpy_P(buff, PSTR("ESP8285")); + } else { + strcpy_P(buff, PSTR("ESP8266EX")); + } +#else + strcpy_P(buff, PSTR("ESP32")); +#endif + return String(buff); +} + +float ConvertTemp(float c) +{ + float result = c; + + global_update = uptime; + global_temperature_celsius = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = c * 1.8 + 32; + } + result = result + (0.1 * Settings.temp_comp); + return result; +} + +float ConvertTempToCelsius(float c) +{ + float result = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = (c - 32) / 1.8; + } + result = result + (0.1 * Settings.temp_comp); + return result; +} + +char TempUnit(void) +{ + + return (Settings.flag.temperature_conversion) ? D_UNIT_FAHRENHEIT[0] : D_UNIT_CELSIUS[0]; +} + +float ConvertHumidity(float h) +{ + float result = h; + + global_update = uptime; + global_humidity = h; + + result = result + (0.1 * Settings.hum_comp); + + return result; +} + +float CalcTempHumToDew(float t, float h) +{ + if (isnan(h) || isnan(t)) { return NAN; } + + if (Settings.flag.temperature_conversion) { + t = (t - 32) / 1.8; + } + + float gamma = TaylorLog(h / 100) + 17.62 * t / (243.5 + t); + float result = (243.5 * gamma / (17.62 - gamma)); + + if (Settings.flag.temperature_conversion) { + result = result * 1.8 + 32; + } + return result; +} + +float ConvertPressure(float p) +{ + float result = p; + + global_update = uptime; + global_pressure_hpa = p; + + if (!isnan(p) && Settings.flag.pressure_conversion) { + result = p * 0.75006375541921; + } + return result; +} + +String PressureUnit(void) +{ + return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); +} + +float ConvertSpeed(float s) +{ + + return s * kSpeedConversionFactor[Settings.flag2.speed_conversion]; +} + +String SpeedUnit(void) +{ + char speed[8]; + return String(GetTextIndexed(speed, sizeof(speed), Settings.flag2.speed_conversion, kSpeedUnit)); +} + +void ResetGlobalValues(void) +{ + if ((uptime - global_update) > GLOBAL_VALUES_VALID) { + global_update = 0; + global_temperature_celsius = NAN; + global_humidity = 0.0f; + global_pressure_hpa = 0.0f; + } +} + +uint32_t SqrtInt(uint32_t num) +{ + if (num <= 1) { + return num; + } + + uint32_t x = num / 2; + uint32_t y; + do { + y = (x + num / x) / 2; + if (y >= x) { + return x; + } + x = y; + } while (true); +} + +uint32_t RoundSqrtInt(uint32_t num) +{ + uint32_t s = SqrtInt(4 * num); + if (s & 1) { + s++; + } + return s / 2; +} + +char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack) +{ + + + char* write = destination; + const char* read = haystack; + + index++; + while (index--) { + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + if (0 == ch) { + if (index) { + write = destination; + } + break; + } + } + *write = '\0'; + return destination; +} + +int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) +{ + + + int result = -1; + const char* read = haystack; + char* write = destination; + + while (true) { + result++; + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + *write = '\0'; + if (!strcasecmp(needle, destination)) { + break; + } + if (0 == ch) { + result = -1; + break; + } + } + return result; +} + +bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) +{ + GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); + int prefix_length = strlen(XdrvMailbox.command); + if (prefix_length) { + char prefix[prefix_length +1]; + snprintf_P(prefix, sizeof(prefix), XdrvMailbox.topic); + if (strcasecmp(prefix, XdrvMailbox.command)) { + return false; + } + } + int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack); + if (command_code > 0) { + XdrvMailbox.command_code = command_code -1; + MyCommand[XdrvMailbox.command_code](); + return true; + } + return false; +} + +const char kOptions[] PROGMEM = "OFF|" D_OFF "|FALSE|" D_FALSE "|STOP|" D_STOP "|" D_CELSIUS "|" + "ON|" D_ON "|TRUE|" D_TRUE "|START|" D_START "|" D_FAHRENHEIT "|" D_USER "|" + "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" + "BLINK|" D_BLINK "|" + "BLINKOFF|" D_BLINKOFF "|" + "ALL" ; + +const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2, + 3,3, + 4,4, + 255 }; + +int GetStateNumber(char *state_text) +{ + char command[CMDSZ]; + int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); + if (state_number >= 0) { + state_number = pgm_read_byte(sNumbers + state_number); + } + return state_number; +} + +String GetSerialConfig(void) { + + + + + + const char kParity[] = "NEOI"; + + char config[4]; + config[0] = '5' + (Settings.serial_config & 0x3); + config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; + config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); + config[3] = '\0'; + return String(config); +} + +void SetSerialBegin(void) { + baudrate = Settings.baudrate * 300; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), baudrate); + Serial.flush(); + Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config)); +} + +void SetSerialConfig(uint32_t serial_config) { + if (serial_config > TS_SERIAL_8O2) { + serial_config = TS_SERIAL_8N1; + } + if (serial_config != Settings.serial_config) { + Settings.serial_config = serial_config; + SetSerialBegin(); + } +} + +void SetSerialBaudrate(uint32_t ubaudrate) { + baudrate = ubaudrate; + Settings.baudrate = baudrate / 300; + if (Serial.baudRate() != baudrate) { + SetSerialBegin(); + } +} + +void SetSerial(uint32_t ubaudrate, uint32_t serial_config) { + Settings.flag.mqtt_serial = 0; + Settings.serial_config = serial_config; + baudrate = ubaudrate; + Settings.baudrate = baudrate / 300; + SetSeriallog(LOG_LEVEL_NONE); + SetSerialBegin(); +} + +void ClaimSerial(void) { + serial_local = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); + SetSeriallog(LOG_LEVEL_NONE); + baudrate = Serial.baudRate(); + Settings.baudrate = baudrate / 300; +} + +void SerialSendRaw(char *codes) +{ + char *p; + char stemp[3]; + uint8_t code; + + int size = strlen(codes); + + while (size > 1) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + Serial.write(code); + size -= 2; + codes += 2; + } +} + + +void SerialSendDecimal(char *values) +{ + char *p; + uint8_t code; + for (char* str = strtok_r(values, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { + code = (uint8_t)atoi(str); + Serial.write(code); + } +} + +uint32_t GetHash(const char *buffer, size_t size) +{ + uint32_t hash = 0; + for (uint32_t i = 0; i <= size; i++) { + hash += (uint8_t)*buffer++ * (i +1); + } + return hash; +} + +void ShowSource(uint32_t source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); + } +} + +void WebHexCode(uint32_t i, const char* code) +{ + char scolor[10]; + + strlcpy(scolor, code, sizeof(scolor)); + char* p = scolor; + if ('#' == p[0]) { p++; } + + if (3 == strlen(p)) { + p[6] = p[3]; + p[5] = p[2]; + p[4] = p[2]; + p[3] = p[1]; + p[2] = p[1]; + p[1] = p[0]; + } + + uint32_t color = strtol(p, nullptr, 16); + + + + + + + uint32_t j = sizeof(Settings.web_color) / 3; +# 1027 "/workspace/Tasmota/tasmota/support.ino" + if (i >= j) { + + i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); + } + Settings.web_color[i][0] = (color >> 16) & 0xFF; + Settings.web_color[i][1] = (color >> 8) & 0xFF; + Settings.web_color[i][2] = color & 0xFF; +} + +uint32_t WebColor(uint32_t i) +{ + uint32_t j = sizeof(Settings.web_color) / 3; + + + + + if (i >= j) { + + i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); + } + uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; + + return tcolor; +} + + + + + +const uint16_t TIMESZ = 100; + +char* ResponseGetTime(uint32_t format, char* time_str) +{ + switch (format) { + case 1: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); + break; + case 2: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime()); + break; + case 3: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL_MILLIS).c_str()); + break; + default: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + } + return time_str; +} + +int Response_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); + va_end(args); + return len; +} + +int ResponseTime_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + + ResponseGetTime(Settings.flag2.time_format, mqtt_data); + + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + +int ResponseAppend_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + +int ResponseAppendTimeFormat(uint32_t format) +{ + char time_str[TIMESZ]; + return ResponseAppend_P(ResponseGetTime(format, time_str)); +} + +int ResponseAppendTime(void) +{ + return ResponseAppendTimeFormat(Settings.flag2.time_format); +} + +int ResponseAppendTHD(float f_temperature, float f_humidity) +{ + char temperature[FLOATSZ]; + dtostrfd(f_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[FLOATSZ]; + dtostrfd(f_humidity, Settings.flag2.humidity_resolution, humidity); + char dewpoint[FLOATSZ]; + dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, dewpoint); + + return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), temperature, humidity, dewpoint); +} + +int ResponseJsonEnd(void) +{ + return ResponseAppend_P(PSTR("}")); +} + +int ResponseJsonEndEnd(void) +{ + return ResponseAppend_P(PSTR("}}")); +} + + + + + +#ifdef ESP8266 +uint16_t GpioConvert(uint8_t gpio) { + if (gpio > ARRAY_SIZE(kGpioConvert)) { + return AGPIO(GPIO_USER); + } + return pgm_read_word(kGpioConvert + gpio); +} + +uint16_t Adc0Convert(uint8_t adc0) { + if (adc0 > 7) { + return AGPIO(GPIO_USER); + } + else if (0 == adc0) { + return GPIO_NONE; + } + return AGPIO(GPIO_ADC_INPUT + adc0 -1); +} + +void TemplateConvert(uint8_t template8[], uint16_t template16[]) { + for (uint32_t i = 0; i < (sizeof(mytmplt) / 2) -2; i++) { + template16[i] = GpioConvert(template8[i]); + } + template16[(sizeof(mytmplt) / 2) -2] = Adc0Convert(template8[sizeof(mytmplt8285) -1]); + + + + +} + +void ConvertGpios(void) { + if (Settings.gpio16_converted != 0xF5A0) { + + TemplateConvert((uint8_t*)&Settings.ex_user_template8, (uint16_t*)&Settings.user_template); + + for (uint32_t i = 0; i < sizeof(Settings.ex_my_gp8.io); i++) { + Settings.my_gp.io[i] = GpioConvert(Settings.ex_my_gp8.io[i]); + } + Settings.my_gp.io[(sizeof(myio) / 2) -1] = Adc0Convert(Settings.ex_my_adc0); + Settings.gpio16_converted = 0xF5A0; + + + + + } +} +# 1231 "/workspace/Tasmota/tasmota/support.ino" +#endif + +uint32_t ICACHE_RAM_ATTR Pin(uint32_t gpio, uint32_t index = 0); +uint32_t ICACHE_RAM_ATTR Pin(uint32_t gpio, uint32_t index) { + uint16_t real_gpio = gpio << 5; + uint16_t mask = 0xFFE0; + if (index < GPIO_ANY) { + real_gpio += index; + mask = 0xFFFF; + } + for (uint32_t i = 0; i < ARRAY_SIZE(gpio_pin); i++) { + if ((gpio_pin[i] & mask) == real_gpio) { + return i; + } + } + return 99; +} + +bool PinUsed(uint32_t gpio, uint32_t index = 0); +bool PinUsed(uint32_t gpio, uint32_t index) { + return (Pin(gpio, index) < 99); +} + +uint32_t GetPin(uint32_t lpin) { + if (lpin < ARRAY_SIZE(gpio_pin)) { + return gpio_pin[lpin]; + } else { + return GPIO_NONE; + } +} + +void SetPin(uint32_t lpin, uint32_t gpio) { + gpio_pin[lpin] = gpio; +} + +void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) +{ + if (PinUsed(gpio_pin, index)) { + digitalWrite(Pin(gpio_pin, index), state &1); + } +} + +uint8_t ModuleNr(void) +{ + + + return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; +} + +bool ValidTemplateModule(uint32_t index) +{ + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + if (index == pgm_read_byte(kModuleNiceList + i)) { + return true; + } + } + return false; +} + +bool ValidModule(uint32_t index) +{ + if (index == USER_MODULE) { return true; } + return ValidTemplateModule(index); +} + +bool ValidTemplate(const char *search) { + char template_name[strlen(SettingsText(SET_TEMPLATE_NAME)) +1]; + char search_name[strlen(search) +1]; + + LowerCase(template_name, SettingsText(SET_TEMPLATE_NAME)); + LowerCase(search_name, search); + + return (strstr(template_name, search_name) != nullptr); +} + +String AnyModuleName(uint32_t index) +{ + if (USER_MODULE == index) { + return String(SettingsText(SET_TEMPLATE_NAME)); + } else { + char name[TOPSZ]; + return String(GetTextIndexed(name, sizeof(name), index, kModuleNames)); + } +} + +String ModuleName(void) +{ + return AnyModuleName(Settings.module); +} + +#ifdef ESP8266 +void GetInternalTemplate(void* ptr, uint32_t module, uint32_t option) { + uint8_t module_template = pgm_read_byte(kModuleTemplateList + module); + + + + + uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE }; + if (module_template < TMP_WEMOS) { + memcpy_P(&template8, &kModules8266[module_template], 6); + memcpy_P(&template8[8], &kModules8266[module_template].gp.io[6], 6); + } else { + memcpy_P(&template8, &kModules8285[module_template - TMP_WEMOS], sizeof(template8)); + } + + + + + uint16_t template16[(sizeof(mytmplt) / 2)] = { GPIO_NONE }; + TemplateConvert(template8, template16); + + uint32_t index = 0; + uint32_t size = sizeof(mycfgio); + switch (option) { + case 2: { + index = (sizeof(mytmplt) / 2) -1; + size = 2; + break; + } + case 3: { + size = sizeof(mytmplt); + break; + } + } + memcpy(ptr, &template16[index], size); + + + +} +#endif + +void ModuleGpios(myio *gp) +{ + uint16_t *dest = (uint16_t *)gp; + uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; + + memset(dest, GPIO_NONE, sizeof(myio)); + if (USER_MODULE == Settings.module) { + memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); + } else { +#ifdef ESP8266 + GetInternalTemplate(&src, Settings.module, 1); +#else + memcpy_P(&src, &kModules.gp, sizeof(mycfgio)); +#endif + } + + + + + uint32_t j = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + dest[j] = src[i]; + j++; + } + + + +} + +gpio_flag ModuleFlag(void) +{ + gpio_flag flag; + + if (USER_MODULE == Settings.module) { + flag = Settings.user_template.flag; + } else { +#ifdef ESP8266 + GetInternalTemplate(&flag, Settings.module, 2); +#else + memcpy_P(&flag, &kModules.flag, sizeof(gpio_flag)); +#endif + } + + return flag; +} + +void ModuleDefault(uint32_t module) +{ + if (USER_MODULE == module) { module = WEMOS; } + Settings.user_template_base = module; + char name[TOPSZ]; + SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames)); +#ifdef ESP8266 + GetInternalTemplate(&Settings.user_template, module, 3); +#else + memcpy_P(&Settings.user_template, &kModules, sizeof(mytmplt)); +#endif +} + +void SetModuleType(void) +{ + my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; +} + +bool FlashPin(uint32_t pin) +{ + return (((pin > 5) && (pin < 9)) || (11 == pin)); +} + +uint32_t ValidPin(uint32_t pin, uint32_t gpio) +{ + if (FlashPin(pin)) { + return GPIO_NONE; + } + + + if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { + if ((9 == pin) || (10 == pin)) { + return GPIO_NONE; + } + } + + return gpio; +} + +bool ValidGPIO(uint32_t pin, uint32_t gpio) +{ +#ifdef ESP8266 +#ifdef USE_ADC_VCC + if (ADC0_PIN == pin) { return false; } +#endif +#endif + return (GPIO_USER == ValidPin(pin, BGPIO(gpio))); +} + +bool GetUsedInModule(uint32_t val, uint16_t *arr) +{ + int offset = 0; + + if (!val) { return false; } + + if ((val >= GPIO_KEY1) && (val < GPIO_KEY1 + MAX_KEYS)) { + offset = (GPIO_KEY1_NP - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_NP) && (val < GPIO_KEY1_NP + MAX_KEYS)) { + offset = -(GPIO_KEY1_NP - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_INV) && (val < GPIO_KEY1_INV + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_INV_NP) && (val < GPIO_KEY1_INV_NP + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV_NP - GPIO_KEY1); + } + + if ((val >= GPIO_SWT1) && (val < GPIO_SWT1 + MAX_SWITCHES)) { + offset = (GPIO_SWT1_NP - GPIO_SWT1); + } + if ((val >= GPIO_SWT1_NP) && (val < GPIO_SWT1_NP + MAX_SWITCHES)) { + offset = -(GPIO_SWT1_NP - GPIO_SWT1); + } + + if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { + offset = (GPIO_REL1_INV - GPIO_REL1); + } + if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { + offset = -(GPIO_REL1_INV - GPIO_REL1); + } + + if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { + offset = (GPIO_LED1_INV - GPIO_LED1); + } + if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { + offset = -(GPIO_LED1_INV - GPIO_LED1); + } + + if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { + offset = (GPIO_PWM1_INV - GPIO_PWM1); + } + if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { + offset = -(GPIO_PWM1_INV - GPIO_PWM1); + } + + if ((val >= GPIO_CNTR1) && (val < GPIO_CNTR1 + MAX_COUNTERS)) { + offset = (GPIO_CNTR1_NP - GPIO_CNTR1); + } + if ((val >= GPIO_CNTR1_NP) && (val < GPIO_CNTR1_NP + MAX_COUNTERS)) { + offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); + } + + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { + if (arr[i] == val) { return true; } + if (arr[i] == val + offset) { return true; } + } + return false; +} + +bool JsonTemplate(char* dataBuf) +{ + + + + + + if (strlen(dataBuf) < 9) { return false; } + + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); + if (!root) { return false; } + + + JsonParserToken val = root[PSTR(D_JSON_NAME)]; + if (val) { + SettingsUpdateText(SET_TEMPLATE_NAME, val.getStr()); + } + JsonParserArray arr = root[PSTR(D_JSON_GPIO)]; + if (arr) { +#ifdef ESP8266 + bool old_template = false; + uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE }; + if (13 == arr.size()) { + uint32_t gpio = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(template8) -1; i++) { + gpio = arr[i].getUInt(); + if (gpio > 255) { + break; + } + template8[i] = gpio; + } + old_template = (gpio < 256); + } + if (old_template) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: Converting template ...")); + + val = root[PSTR(D_JSON_FLAG)]; + if (val) { + template8[ARRAY_SIZE(template8) -1] = val.getUInt() & 0x0F; + } + TemplateConvert(template8, Settings.user_template.gp.io); + Settings.user_template.flag.data = 0; + } else { +#endif + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + JsonParserToken val = arr[i]; + if (!val) { break; } + uint16_t gpio = val.getUInt(); + if (gpio == (AGPIO(GPIO_NONE) +1)) { + gpio = AGPIO(GPIO_USER); + } + Settings.user_template.gp.io[i] = gpio; + } + val = root[PSTR(D_JSON_FLAG)]; + if (val) { + Settings.user_template.flag.data = val.getUInt(); + } + } +#ifdef ESP8266 + } +#endif + val = root[PSTR(D_JSON_BASE)]; + if (val) { + uint32_t base = val.getUInt(); + if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } + Settings.user_template_base = base -1; + } + + + + + return true; +} + +void TemplateJson(void) +{ + + + + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME)); + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + uint16_t gpio = Settings.user_template.gp.io[i]; + if (gpio == AGPIO(GPIO_USER)) { + gpio = AGPIO(GPIO_NONE) +1; + } + ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", gpio); + } + ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); +} + + + + + +inline int32_t TimeDifference(uint32_t prev, uint32_t next) +{ + return ((int32_t) (next - prev)); +} + +int32_t TimePassedSince(uint32_t timestamp) +{ + + + return TimeDifference(timestamp, millis()); +} + +bool TimeReached(uint32_t timer) +{ + + const long passed = TimePassedSince(timer); + return (passed >= 0); +} + +void SetNextTimeInterval(unsigned long& timer, const unsigned long step) +{ + timer += step; + const long passed = TimePassedSince(timer); + if (passed < 0) { return; } + if (static_cast(passed) > step) { + + timer = millis() + step; + return; + } + + timer = millis() + (step - passed); +} + +int32_t TimePassedSinceUsec(uint32_t timestamp) +{ + return TimeDifference(timestamp, micros()); +} + +bool TimeReachedUsec(uint32_t timer) +{ + + const long passed = TimePassedSinceUsec(timer); + return (passed >= 0); +} + + + + + +#ifdef USE_I2C +const uint8_t I2C_RETRY_COUNTER = 3; + +uint32_t i2c_active[4] = { 0 }; +uint32_t i2c_buffer = 0; + +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) +{ + uint8_t retry = I2C_RETRY_COUNTER; + bool status = false; + + i2c_buffer = 0; + while (!status && retry) { + Wire.beginTransmission(addr); + Wire.write(reg); + if (0 == Wire.endTransmission(false)) { + Wire.requestFrom((int)addr, (int)size); + if (Wire.available() == size) { + for (uint32_t i = 0; i < size; i++) { + i2c_buffer = i2c_buffer << 8 | Wire.read(); + } + status = true; + } + } + retry--; + } + if (!retry) Wire.endTransmission(); + return status; +} + +bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 1); + *data = (uint8_t)i2c_buffer; + return status; +} + +bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 2); + *data = (uint16_t)i2c_buffer; + return status; +} + +bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 2); + *data = (int16_t)i2c_buffer; + return status; +} + +bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg) +{ + uint16_t ldata; + bool status = I2cValidRead16(&ldata, addr, reg); + *data = (ldata >> 8) | (ldata << 8); + return status; +} + +bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg) +{ + uint16_t ldata; + bool status = I2cValidRead16LE(&ldata, addr, reg); + *data = (int16_t)ldata; + return status; +} + +bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 3); + *data = i2c_buffer; + return status; +} + +uint8_t I2cRead8(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 1); + return (uint8_t)i2c_buffer; +} + +uint16_t I2cRead16(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + return (uint16_t)i2c_buffer; +} + +int16_t I2cReadS16(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + return (int16_t)i2c_buffer; +} + +uint16_t I2cRead16LE(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + uint16_t temp = (uint16_t)i2c_buffer; + return (temp >> 8) | (temp << 8); +} + +int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg) +{ + return (int16_t)I2cRead16LE(addr, reg); +} + +int32_t I2cRead24(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 3); + return i2c_buffer; +} + +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) +{ + uint8_t x = I2C_RETRY_COUNTER; + + do { + Wire.beginTransmission((uint8_t)addr); + Wire.write(reg); + uint8_t bytes = size; + while (bytes--) { + Wire.write((val >> (8 * bytes)) & 0xFF); + } + x--; + } while (Wire.endTransmission(true) != 0 && x != 0); + return (x); +} + +bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val) +{ + return I2cWrite(addr, reg, val, 1); +} + +bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val) +{ + return I2cWrite(addr, reg, val, 2); +} + +int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) +{ + Wire.beginTransmission((uint8_t)addr); + Wire.write((uint8_t)reg); + Wire.endTransmission(); + if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) { + return 1; + } + while (len--) { + *reg_data = (uint8_t)Wire.read(); + reg_data++; + } + return 0; +} + +int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) +{ + Wire.beginTransmission((uint8_t)addr); + Wire.write((uint8_t)reg); + while (len--) { + Wire.write(*reg_data); + reg_data++; + } + Wire.endTransmission(); + return 0; +} + +void I2cScan(char *devs, unsigned int devs_len) +{ + + + + + + + + uint8_t error = 0; + uint8_t address = 0; + uint8_t any = 0; + + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); + for (address = 1; address <= 127; address++) { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (0 == error) { + any = 1; + snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address); + } + else if (error != 2) { + any = 2; + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address); + break; + } + } + if (any) { + strncat(devs, "\"}", devs_len - strlen(devs) -1); + } + else { + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}")); + } +} + +void I2cResetActive(uint32_t addr, uint32_t count = 1) +{ + addr &= 0x7F; + count &= 0x7F; + while (count-- && (addr < 128)) { + i2c_active[addr / 32] &= ~(1 << (addr % 32)); + addr++; + } + +} + +void I2cSetActive(uint32_t addr, uint32_t count = 1) +{ + addr &= 0x7F; + count &= 0x7F; + while (count-- && (addr < 128)) { + i2c_active[addr / 32] |= (1 << (addr % 32)); + addr++; + } + +} + +void I2cSetActiveFound(uint32_t addr, const char *types) +{ + I2cSetActive(addr); + AddLog_P2(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); +} + +bool I2cActive(uint32_t addr) +{ + addr &= 0x7F; + if (i2c_active[addr / 32] & (1 << (addr % 32))) { + return true; + } + return false; +} + +bool I2cSetDevice(uint32_t addr) +{ + addr &= 0x7F; + if (I2cActive(addr)) { + return false; + } + Wire.beginTransmission((uint8_t)addr); + return (0 == Wire.endTransmission()); +} +#endif +# 1919 "/workspace/Tasmota/tasmota/support.ino" +void SetSeriallog(uint32_t loglevel) +{ + Settings.seriallog_level = loglevel; + seriallog_level = loglevel; + seriallog_timer = 0; +} + +void SetSyslog(uint32_t loglevel) +{ + Settings.syslog_level = loglevel; + syslog_level = loglevel; + syslog_timer = 0; +} + +#ifdef USE_WEBSERVER +void GetLog(uint32_t idx, char** entry_pp, size_t* len_p) +{ + char* entry_p = nullptr; + size_t len = 0; + + if (idx) { + char* it = web_log; + do { + uint32_t cur_idx = *it; + it++; + size_t tmp = strchrspn(it, '\1'); + tmp++; + if (cur_idx == idx) { + len = tmp; + entry_p = it; + break; + } + it += tmp; + } while (it < web_log + WEB_LOG_SIZE && *it != '\0'); + } + *entry_pp = entry_p; + *len_p = len; +} +#endif + +void Syslog(void) +{ + + + uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); + if (syslog_host_hash != current_hash) { + syslog_host_hash = current_hash; + WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); + } + if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { + char syslog_preamble[64]; + snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s ESP-"), NetworkHostname()); + memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); + log_data[sizeof(log_data) -1] = '\0'; + memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); + PortUdp_write(log_data, strlen(log_data)); + PortUdp.endPacket(); + delay(1); + } else { + syslog_level = 0; + syslog_timer = SYSLOG_TIMER; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); + } +} + +void AddLog(uint32_t loglevel) +{ + char mxtime[10]; + snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); + + if ((loglevel <= seriallog_level) && + (masterlog_level <= seriallog_level)) { + Serial.printf("%s%s\r\n", mxtime, log_data); + } +#ifdef USE_WEBSERVER + if (Settings.webserver && + (loglevel <= Settings.weblog_level) && + (masterlog_level <= Settings.weblog_level)) { + + + web_log_index &= 0xFF; + if (!web_log_index) web_log_index++; + while (web_log_index == web_log[0] || + strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) + { + char* it = web_log; + it++; + it += strchrspn(it, '\1'); + it++; + memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); + } + snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); + web_log_index &= 0xFF; + if (!web_log_index) web_log_index++; + } +#endif + if (Settings.flag.mqtt_enabled && + !global_state.mqtt_down && + (loglevel <= Settings.mqttlog_level) && + (masterlog_level <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); } + + if (!global_state.network_down && + (loglevel <= syslog_level) && + (masterlog_level <= syslog_level)) { Syslog(); } + + prepped_loglevel = 0; +} + +void AddLog_P(uint32_t loglevel, const char *formatP) +{ + snprintf_P(log_data, sizeof(log_data), formatP); + AddLog(loglevel); +} + +void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2) +{ + char message[sizeof(log_data)]; + + snprintf_P(log_data, sizeof(log_data), formatP); + snprintf_P(message, sizeof(message), formatP2); + strncat(log_data, message, sizeof(log_data) - strlen(log_data) -1); + AddLog(loglevel); +} + +void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + prepped_loglevel = loglevel; +} + +void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(loglevel); +} + +void AddLog_Debug(PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_DEBUG); +} + +void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count) +{ +# 2087 "/workspace/Tasmota/tasmota/support.ino" + char hex_char[(count * 3) + 2]; + AddLog_P2(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' ')); +} + +void AddLogSerial(uint32_t loglevel) +{ + AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); +} + +void AddLogMissed(const char *sensor, uint32_t misses) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); +} + +void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) { + snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); + for (uint32_t i = 0; i < count; i++) { + if (1 == size) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer)); + } else { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X%02X"), log_data, *(buffer +1), *(buffer)); + } + buffer += size; + } + AddLog(loglevel); +} + + + + + +#ifdef USE_UNISHOX_COMPRESSION + +#include + +Unishox compressor; + +String Decompress(const char * compressed, size_t uncompressed_size) { + String content(""); + + uncompressed_size += 2; + + + + + + content.reserve(uncompressed_size); + char * buffer = content.begin(); + + int32_t len = compressor.unishox_decompress(compressed, strlen_P(compressed), buffer, uncompressed_size); + if (len > 0) { + buffer[len] = 0; + content = buffer; + } + return content; +} + +#endif + + + + + + +uint32_t HwRandom(void) { +#if ESP8266 + + #define _RAND_ADDR 0x3FF20E44UL +#else + #define _RAND_ADDR 0x3FF75144UL +#endif + static uint32_t last_ccount = 0; + uint32_t ccount; + uint32_t result = 0; + do { + ccount = ESP.getCycleCount(); + result ^= *(volatile uint32_t *)_RAND_ADDR; + } while (ccount - last_ccount < 64); + last_ccount = ccount; + return result ^ *(volatile uint32_t *)_RAND_ADDR; +#undef _RAND_ADDR +} +# 1 "/workspace/Tasmota/tasmota/support_button.ino" +# 20 "/workspace/Tasmota/tasmota/support_button.ino" +#define BUTTON_V2 +#ifdef BUTTON_V2 + + + + +#define MAX_RELAY_BUTTON1 5 +#ifdef ESP32 +#define TOUCH_PIN_THRESHOLD 12 +#define TOUCH_HIT_THRESHOLD 3 +#endif + +const char kMultiPress[] PROGMEM = + "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|"; + +struct BUTTON { + unsigned long debounce = 0; + uint16_t hold_timer[MAX_KEYS] = { 0 }; + uint16_t dual_code = 0; + + uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; + uint8_t window_timer[MAX_KEYS] = { 0 }; + uint8_t press_counter[MAX_KEYS] = { 0 }; + + uint8_t dual_receive_count = 0; + uint8_t no_pullup_mask = 0; + uint8_t inverted_mask = 0; +#ifdef ESP32 + uint8_t touch_mask = 0; + uint8_t touch_hits[MAX_KEYS] = { 0 }; +#endif + uint8_t present = 0; +} Button; + +#ifdef ESP32 +struct TOUCH_BUTTON { + uint8_t pin_threshold = TOUCH_PIN_THRESHOLD; + uint8_t hit_threshold = TOUCH_HIT_THRESHOLD; + uint8_t calibration = 0; +} TOUCH_BUTTON; +#endif + + + +void ButtonPullupFlag(uint8 button_bit) +{ + bitSet(Button.no_pullup_mask, button_bit); +} + +void ButtonInvertFlag(uint8 button_bit) +{ + bitSet(Button.inverted_mask, button_bit); +} +#ifdef ESP32 +void ButtonTouchFlag(uint8 button_bit) +{ + bitSet(Button.touch_mask, button_bit); +} +#endif +void ButtonInit(void) +{ + Button.present = 0; +#ifdef ESP8266 + if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + Button.present++; + } +#endif + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if (PinUsed(GPIO_KEY1, i)) { + Button.present++; +#ifdef ESP8266 + pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); +#else + pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : INPUT_PULLUP); +#endif + } +#ifdef USE_ADC + else if (PinUsed(GPIO_ADC_BUTTON, i) || PinUsed(GPIO_ADC_BUTTON_INV, i)) { + Button.present++; + } +#endif + } +} + +uint8_t ButtonSerial(uint8_t serial_in_byte) +{ + if (Button.dual_receive_count) { + Button.dual_receive_count--; + if (Button.dual_receive_count) { + Button.dual_code = (Button.dual_code << 8) | serial_in_byte; + serial_in_byte = 0; + } else { + if (serial_in_byte != 0xA1) { + Button.dual_code = 0; + } + } + } + if (0xA0 == serial_in_byte) { + serial_in_byte = 0; + Button.dual_code = 0; + Button.dual_receive_count = 3; + } + + return serial_in_byte; +} +# 136 "/workspace/Tasmota/tasmota/support_button.ino" +void ButtonHandler(void) +{ + if (uptime < 4) { return; } + + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; + uint16_t loops_per_second = 1000 / Settings.button_debounce; + char scmnd[20]; + + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { + uint8_t button = NOT_PRESSED; + uint8_t button_present = 0; + +#ifdef ESP8266 + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { + button_present = 1; + if (Button.dual_code) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); + button = PRESSED; + if (0xF500 == Button.dual_code) { + Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; + hold_time_extent = 1; + } + Button.dual_code = 0; + } + } else +#endif + if (PinUsed(GPIO_KEY1, button_index)) { + button_present = 1; +#ifdef ESP32 + if (bitRead(Button.touch_mask, button_index)) { + uint32_t _value = touchRead(Pin(GPIO_KEY1, button_index)); + button = NOT_PRESSED; + if (_value != 0) { + if (_value < TOUCH_BUTTON.pin_threshold) { + if (++Button.touch_hits[button_index] > TOUCH_BUTTON.hit_threshold) { + if (!bitRead(TOUCH_BUTTON.calibration, button_index+1)) { + button = PRESSED; + } + } + } else { + Button.touch_hits[button_index] = 0; + } + } else { + Button.touch_hits[button_index] = 0; + } + if (bitRead(TOUCH_BUTTON.calibration, button_index+1)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index+1, _value, Button.touch_hits[button_index]); + } + } else +#endif + { + button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); + } + } +#ifdef USE_ADC + else if (PinUsed(GPIO_ADC_BUTTON, button_index)) { + button_present = 1; + button = AdcGetButton(Pin(GPIO_ADC_BUTTON, button_index)); + } + else if (PinUsed(GPIO_ADC_BUTTON_INV, button_index)) { + button_present = 1; + button = AdcGetButton(Pin(GPIO_ADC_BUTTON_INV, button_index)); + } +#endif + if (button_present) { + XdrvMailbox.index = button_index; + XdrvMailbox.payload = button; + if (XdrvCall(FUNC_BUTTON_PRESSED)) { + + } +#ifdef ESP8266 + else if (SONOFF_4CHPRO == my_module_type) { + if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } + + bool button_pressed = false; + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); + Button.hold_timer[button_index] = loops_per_second; + button_pressed = true; + } + if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); + if (!Button.hold_timer[button_index]) { button_pressed = true; } + } + if (button_pressed) { + if (!Settings.flag3.mqtt_buttons) { + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); + } + } else { + MqttButtonTopic(button_index +1, 1, 0); + } + } + } +#endif + else { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + + if (Settings.flag.button_single) { + if (!Settings.flag3.mqtt_buttons) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); + } + } else { + MqttButtonTopic(button_index +1, 1, 0); + } + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); + Button.window_timer[button_index] = loops_per_second / 2; + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (Settings.flag.button_single) { + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } else { + if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { + Button.press_counter[button_index] = 0; + if (Settings.flag3.mqtt_buttons) { + MqttButtonTopic(button_index +1, 3, 1); + } else { + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); + } + } else { + if (!Settings.flag.button_restrict) { + if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10)) { + Button.press_counter[button_index] = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + } + } + + if (!Settings.flag.button_single) { + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < 7)) { + + bool single_press = false; + if (Button.press_counter[button_index] < 3) { +#ifdef ESP8266 + if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + single_press = true; + } else +#endif + { + single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); + if ((1 == Button.present) && (2 == devices_present)) { + if (Settings.flag.button_swap) { + Button.press_counter[button_index] = (single_press) ? 1 : 2; + } + } + } + } +#ifdef ROTARY_V1 + if (!RotaryButtonPressed(button_index)) { +#endif + if (!Settings.flag3.mqtt_buttons && single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { + + } else { + if (Button.press_counter[button_index] < 6) { + if (WifiState() > WIFI_RESTART) { + restart_flag = 1; + } + if (!Settings.flag3.mqtt_buttons) { + if (Button.press_counter[button_index] == 1) { + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); + } else { + SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); + if (0 == button_index) { + bool valid_relay = PinUsed(GPIO_REL1, Button.press_counter[button_index]-1); +#ifdef ESP8266 + if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + valid_relay = (Button.press_counter[button_index] <= devices_present); + } +#endif + if ((Button.press_counter[button_index] > 1) && valid_relay && (Button.press_counter[button_index] <= MAX_RELAY_BUTTON1)) { + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); + + } + } + } + } + + } else { + if (!Settings.flag.button_restrict) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + if (Settings.flag3.mqtt_buttons) { + if (Button.press_counter[button_index] >= 1 && Button.press_counter[button_index] <= 5) { + MqttButtonTopic(button_index +1, Button.press_counter[button_index], 0); + } + } + } +#ifdef ROTARY_V1 + } +#endif + Button.press_counter[button_index] = 0; + } + } + } + + } + } + Button.last_state[button_index] = button; + } +} + +void MqttButtonTopic(uint8_t button_id, uint8_t action, uint8_t hold) +{ + char scommand[CMDSZ]; + char stopic[TOPSZ]; + char mqttstate[7]; + + SendKey(KEY_BUTTON, button_id, (hold) ? 3 : action +9); + + if (!Settings.flag.hass_discovery) { + GetTextIndexed(mqttstate, sizeof(mqttstate), action, kMultiPress); + snprintf_P(scommand, sizeof(scommand), PSTR("BUTTON%d"), button_id); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(S_JSON_COMMAND_SVALUE, "ACTION", (hold) ? SettingsText(SET_STATE_TXT4) : mqttstate); + MqttPublish(stopic); + } +} + +void ButtonLoop(void) +{ + if (Button.present) { + if (TimeReached(Button.debounce)) { + SetNextTimeInterval(Button.debounce, Settings.button_debounce); + ButtonHandler(); + } + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_command.ino" +# 20 "/workspace/Tasmota/tasmota/support_command.ino" +const char kTasmotaCommands[] PROGMEM = "|" + D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" + D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" + D_CMND_SO "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" + D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" + D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" + D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" + D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" + D_CMND_DEVICENAME "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" + D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|" + D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|" D_CMND_WIFI "|" +#ifdef USE_I2C + D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" +#endif +#ifdef USE_DEVICE_GROUPS + D_CMND_DEVGROUP_NAME "|" +#ifdef USE_DEVICE_GROUPS_SEND + D_CMND_DEVGROUP_SEND "|" +#endif + D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" +#endif + D_CMND_SENSOR "|" D_CMND_DRIVER +#ifdef ESP32 + "|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM "|" D_CMND_CPU_FREQUENCY +#endif + ; + +void (* const TasmotaCommand[])(void) PROGMEM = { + &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, + &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, + &CmndSetoption, &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, + &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, + &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, + &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, + &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, + &CmndDevicename, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, + &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode, + &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndWifi, +#ifdef USE_I2C + &CmndI2cScan, CmndI2cDriver, +#endif +#ifdef USE_DEVICE_GROUPS + &CmndDevGroupName, +#ifdef USE_DEVICE_GROUPS_SEND + &CmndDevGroupSend, +#endif + &CmndDevGroupShare, &CmndDevGroupStatus, +#endif + &CmndSensor, &CmndDriver +#ifdef ESP32 + ,&CmndTouchCal, &CmndTouchThres, &CmndTouchNum, &CmndCpuFrequency +#endif + }; + +const char kWifiConfig[] PROGMEM = + D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; + + + +void ResponseCmndNumber(int value) +{ + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndFloat(float value, uint32_t decimals) +{ + char stemp1[TOPSZ]; + dtostrfd(value, decimals, stemp1); + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); +} + +void ResponseCmndIdxNumber(int value) +{ + Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value); +} + +void ResponseCmndChar_P(const char* value) +{ + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndChar(const char* value) +{ + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, EscapeJSONString(value).c_str()); +} + +void ResponseCmndStateText(uint32_t value) +{ + ResponseCmndChar(GetStateText(value)); +} + +void ResponseCmndDone(void) +{ + ResponseCmndChar(D_JSON_DONE); +} + +void ResponseCmndIdxChar(const char* value) +{ + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, EscapeJSONString(value).c_str()); +} + +void ResponseCmndAll(uint32_t text_index, uint32_t count) +{ + uint32_t real_index = text_index; + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < count; i++) { + if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, EscapeJSONString(SettingsText(real_index +i)).c_str()); + } + ResponseJsonEnd(); +} + + + +void ExecuteCommand(const char *cmnd, uint32_t source) +{ + + + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("ExecuteCommand")); +#endif + ShowSource(source); + + const char *pos = cmnd; + while (*pos && isspace(*pos)) { + pos++; + } + + const char *start = pos; + + while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) { + if ('/' == *pos) { + start = pos + 1; + } + pos++; + } + if ('\0' == *start || pos <= start) { + return; + } + + uint32_t size = pos - start; + char stopic[size + 2]; + stopic[0] = '/'; + memcpy(stopic+1, start, size); + stopic[size+1] = '\0'; + + char svalue[strlen(pos) +1]; + strlcpy(svalue, pos, sizeof(svalue)); + CommandHandler(stopic, svalue, strlen(svalue)); +} +# 179 "/workspace/Tasmota/tasmota/support_command.ino" +void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("CommandHandler")); +#endif + + while (*dataBuf && isspace(*dataBuf)) { + dataBuf++; + data_len--; + } + + bool grpflg = false; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + char *group_topic = SettingsText(real_index +i); + if (*group_topic && strstr(topicBuf, group_topic) != nullptr) { + grpflg = true; + break; + } + } + + char stemp1[TOPSZ]; + GetFallbackTopic_P(stemp1, ""); + fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); + + char *type = strrchr(topicBuf, '/'); + + uint32_t index = 1; + bool user_index = false; + if (type != nullptr) { + type++; + uint32_t i; + int nLen; + char *s = type; + for (nLen = 0; *s; s++, nLen++) { + *s=toupper(*s); + } + i = nLen; + if (i > 0) { + while (isdigit(type[i-1])) { + i--; + } + } + if (i < nLen) { + index = atoi(type + i); + user_index = true; + } + type[i] = '\0'; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf); + + if (type != nullptr) { + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); + + if (Settings.ledstate &0x02) { blinks++; } + + if (!strcmp(dataBuf,"?")) { data_len = 0; } + + char *p; + int32_t payload = strtol(dataBuf, &p, 0); + if (p == dataBuf) { payload = -99; } + int temp_payload = GetStateNumber(dataBuf); + if (temp_payload > -1) { payload = temp_payload; } + + DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload); + + + backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY]; + + char command[CMDSZ] = { 0 }; + XdrvMailbox.command = command; + XdrvMailbox.index = index; + XdrvMailbox.data_len = data_len; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.usridx = user_index; + XdrvMailbox.topic = type; + XdrvMailbox.data = dataBuf; + +#ifdef USE_SCRIPT_SUB_COMMAND + + if (!Script_SubCmd()) { + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; + } + } + } + } +#else + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; + } + } + } +#endif + + } + + if (type == nullptr) { + blinks = 201; + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_COMMAND)); + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); + type = (char*)stemp1; + } + + if (mqtt_data[0] != '\0') { +# 303 "/workspace/Tasmota/tasmota/support_command.ino" + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, type); + } + fallback_topic_flag = false; +} + + + +void CmndBacklog(void) +{ + if (XdrvMailbox.data_len) { + +#ifdef SUPPORT_IF_STATEMENT + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) +#else + uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; + bl_pointer--; + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog_index != bl_pointer)) +#endif + { + while(true) { + blcommand = Trim(blcommand); + if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { + blcommand += strlen(D_CMND_BACKLOG); + } else { + break; + } + } + if (*blcommand != '\0') { +#ifdef SUPPORT_IF_STATEMENT + if (backlog.size() < MAX_BACKLOG) { + backlog.add(blcommand); + } +#else + backlog[backlog_index] = blcommand; + backlog_index++; + if (backlog_index >= MAX_BACKLOG) backlog_index = 0; +#endif + } + blcommand = strtok(nullptr, ";"); + } + + mqtt_data[0] = '\0'; + backlog_delay = 0; + } else { + bool blflag = BACKLOG_EMPTY; +#ifdef SUPPORT_IF_STATEMENT + backlog.clear(); +#else + backlog_pointer = backlog_index; +#endif + ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + } +} + +void CmndDelay(void) +{ + if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) { + backlog_delay = millis() + (100 * XdrvMailbox.payload); + } + uint32_t bl_delay = 0; + long bl_delta = TimePassedSince(backlog_delay); + if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } + ResponseCmndNumber(bl_delay); +} + +void CmndPower(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + + ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } + else if (0 == XdrvMailbox.index) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + SetAllPower(XdrvMailbox.payload, SRC_IGNORE); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + mqtt_data[0] = '\0'; + } +} + +void CmndStatus(void) +{ + uint32_t payload = XdrvMailbox.payload; + + if (payload > MAX_STATUS) { return; } + if (!Settings.flag.mqtt_enabled && (6 == payload)) { return; } + if (!energy_flg && (9 == payload)) { return; } + if (!CrashFlag() && (12 == payload)) { return; } + if (!Settings.flag3.shutter_mode && (13 == payload)) { return; } + + char stemp[200]; + char stemp2[TOPSZ]; + + if (0 == payload) { + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + stemp[0] = '\0'; + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); + } + stemp2[0] = '\0'; + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); + } + Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_DEVICENAME "\":\"%s\",\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" + D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" + D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" + D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + ModuleNr(), EscapeJSONString(SettingsText(SET_DEVICENAME)).c_str(), stemp, mqtt_topic, + SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate, + Settings.ledmask, Settings.save_data, + Settings.flag.save_state, + SettingsText(SET_MQTT_SWITCH_TOPIC), + stemp2, + Settings.flag.mqtt_button_retain, + Settings.flag.mqtt_switch_retain, + Settings.flag.mqtt_sensor_retain, + Settings.flag.mqtt_power_retain); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS)); + } + + if ((0 == payload) || (1 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" + D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" + D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d" +#ifdef ESP8266 + ",\"" D_JSON_SAVEADDRESS "\":\"%X\"" +#endif + "}}"), + baudrate, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), + GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, + Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag +#ifdef ESP8266 + , GetSettingsAddress() +#endif + ); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "1")); + } + + if ((0 == payload) || (2 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"" +#ifdef ESP8266 + ",\"" D_JSON_BOOTVERSION "\":%d" +#endif + ",\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + "\"CpuFrequency\":%d,\"Hardware\":\"%s\"" + "%s}}"), + my_version, my_image, GetBuildDateAndTime().c_str() +#ifdef ESP8266 + , ESP.getBootVersion() +#endif + , ESP.getSdkVersion(), + ESP.getCpuFreqMHz(), GetDeviceHardware().c_str(), + GetStatistics().c_str()); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "2")); + } + + if ((0 == payload) || (3 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" + D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" + D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\",\"%08X\"]}}"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, + SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, EscapeJSONString(SettingsText(SET_STASSID1)).c_str(), EscapeJSONString(SettingsText(SET_STASSID2)).c_str(), Settings.tele_period, + Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), + Settings.flag3.data, Settings.flag4.data, Settings.flag5.data); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "3")); + } + + if ((0 == payload) || (4 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" +#ifdef ESP32 + D_JSON_PSRMAXMEMORY "\":%d,\"" D_JSON_PSRFREEMEMORY "\":%d,\"" +#endif + D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d" +#ifdef ESP8266 + ",\"" D_JSON_FLASHCHIPID "\":\"%06X\"" +#endif + ",\"FlashFrequency\":%d,\"" D_JSON_FLASHMODE "\":%d,\"" + D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), + ESP_getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP_getFreeHeap()/1024, +#ifdef ESP32 + ESP.getPsramSize()/1024, ESP.getFreePsram()/1024, +#endif + ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024 +#ifdef ESP8266 + , ESP.getFlashChipId() +#endif + , ESP.getFlashChipSpeed()/1000000, ESP.getFlashChipMode(), + LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6, feature7); + XsnsDriverState(); + ResponseAppend_P(PSTR(",\"Sensors\":")); + XsnsSensorState(); + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "4")); + } + + if ((0 == payload) || (5 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" + D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" + D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"), + NetworkHostname(), NetworkAddress().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), + IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), NetworkMacAddress().c_str(), + Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str()); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "5")); + } + + if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" + D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, EscapeJSONString(SettingsText(SET_MQTT_CLIENT)).c_str(), + mqtt_client, EscapeJSONString(SettingsText(SET_MQTT_USER)).c_str(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "6")); + } + + if ((0 == payload) || (7 == payload)) { + if (99 == Settings.timezone) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(), + GetDateAndTime(DT_STD).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); +#else + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(), + GetDateAndTime(DT_STD).c_str(), stemp); +#endif + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "7")); + } + +#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) + if (energy_flg) { + if ((0 == payload) || (9 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":[%d,%d,%d],\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" + D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), + Settings.energy_power_delta[0], Settings.energy_power_delta[1], Settings.energy_power_delta[2], Settings.energy_min_power, Settings.energy_max_power, + Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "9")); + } + } +#endif + + if ((0 == payload) || (8 == payload) || (10 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); + MqttShowSensor(); + ResponseJsonEnd(); + if (8 == payload) { + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "8")); + } else { + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "10")); + } + } + + if ((0 == payload) || (11 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); + MqttShowState(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "11")); + } + + if (CrashFlag()) { + if ((0 == payload) || (12 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); + CrashDump(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "12")); + } + } + +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + if ((0 == payload) || (13 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS13_SHUTTER "\":")); + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (0 == Settings.shutter_startrelay[i]) { break; } + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("{\"" D_STATUS13_SHUTTER "%d\":{\"Relay1\":%d,\"Relay2\":%d,\"Open\":%d,\"Close\":%d," + "\"50perc\":%d,\"Delay\":%d,\"Opt\":\"%s\"," + "\"Calib\":[%d,%d,%d,%d,%d]," + "\"Mode\":\"%d\"}}"), + i, Settings.shutter_startrelay[i], Settings.shutter_startrelay[i] +1, Settings.shutter_opentime[i], Settings.shutter_closetime[i], + Settings.shutter_set50percent[i], Settings.shutter_motordelay[i], GetBinary8(Settings.shutter_options[i], 4).c_str(), + Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], + Settings.shutter_mode); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "13")); + } + } +#endif + +#ifdef USE_SCRIPT_STATUS + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); +#endif + + if (payload) { + XdrvRulesProcess(); + } + + mqtt_data[0] = '\0'; +} + +void CmndState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); + } +#ifdef USE_HOME_ASSISTANT + if (Settings.flag.hass_discovery) { + HAssPublishStatus(); + } +#endif +} + +void CmndTempOffset(void) +{ + if (XdrvMailbox.data_len > 0) { + int value = (int)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > -127) && (value < 127)) { + Settings.temp_comp = value; + } + } + ResponseCmndFloat((float)(Settings.temp_comp) / 10, 1); +} + +void CmndHumOffset(void) +{ + if (XdrvMailbox.data_len > 0) { + int value = (int)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > -101) && (value < 101)) { + Settings.hum_comp = value; + } + } + ResponseCmndFloat((float)(Settings.hum_comp) / 10, 1); +} + +void CmndGlobalTemp(void) +{ + if (XdrvMailbox.data_len > 0) { + float temperature = CharToFloat(XdrvMailbox.data); + if (!isnan(temperature) && Settings.flag.temperature_conversion) { + temperature = (temperature - 32) / 1.8; + } + if ((temperature >= -50.0f) && (temperature <= 100.0f)) { + ConvertTemp(temperature); + global_update = 1; + } + } + ResponseCmndFloat(global_temperature_celsius, 1); +} + +void CmndGlobalHum(void) +{ + if (XdrvMailbox.data_len > 0) { + float humidity = CharToFloat(XdrvMailbox.data); + if ((humidity >= 0.0) && (humidity <= 100.0)) { + ConvertHumidity(humidity); + global_update = 1; + } + } + ResponseCmndFloat(global_humidity, 1); +} + +void CmndSleep(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { + Settings.sleep = XdrvMailbox.payload; + ssleep = XdrvMailbox.payload; + WiFiSetSleepMode(); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, ssleep); + +} + +void CmndUpgrade(void) +{ + + + + + if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) { + ota_state_flag = 3; + char stemp1[TOPSZ]; + Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + } else { + Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version); + } +} + +void CmndOtaUrl(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? PSTR(OTA_URL) : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_OTAURL)); +} + +void CmndSeriallog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.flag.mqtt_serial = 0; + SetSeriallog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level); +} + +void CmndRestart(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 2; + ResponseCmndChar(D_JSON_RESTARTING); + break; + case 2: + restart_flag = 2; + restart_halt = true; + ResponseCmndChar(D_JSON_HALTING); + break; + case -1: + CmndCrash(); + break; + case -2: + CmndWDT(); + break; + case -3: + CmndBlockedLoop(); + break; + case 99: + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); + EspRestart(); + break; + default: + ResponseCmndChar_P(PSTR(D_JSON_ONE_TO_RESTART)); + } +} + +void CmndPowerOnState(void) +{ +#ifdef ESP8266 + if (my_module_type != MOTOR) +#endif + { + + + + + + + + if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) { + Settings.poweronstate = XdrvMailbox.payload; + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + for (uint32_t i = 1; i <= devices_present; i++) { + ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); + } + } + } + ResponseCmndNumber(Settings.poweronstate); + } +} + +void CmndPulsetime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) { + uint32_t items = 1; + if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { + items = MAX_PULSETIMERS; + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; + SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < items; i++) { + uint32_t index = (1 == items) ? XdrvMailbox.index : i +1; + ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"), + (i) ? ',' : '{', + XdrvMailbox.command, index, + Settings.pulse_timer[index -1], GetPulseTimer(index -1)); + } + ResponseJsonEnd(); + } +} + +void CmndBlinktime(void) +{ + if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) { + Settings.blinktime = XdrvMailbox.payload; + if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); } + } + ResponseCmndNumber(Settings.blinktime); +} + +void CmndBlinkcount(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.blinkcount = XdrvMailbox.payload; + if (blink_counter) { blink_counter = Settings.blinkcount *2; } + } + ResponseCmndNumber(Settings.blinkcount); +} + +void CmndSavedata(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) { + Settings.save_data = XdrvMailbox.payload; + save_data_counter = Settings.save_data; + } + SettingsSaveAll(); + char stemp1[TOPSZ]; + if (Settings.save_data > 1) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); + } + ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); +} + +void CmndSetoption(void) +{ + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR(D_CMND_SETOPTION)); + + if (XdrvMailbox.index < 146) { + uint32_t ptype; + uint32_t pindex; + if (XdrvMailbox.index <= 31) { + ptype = 2; + pindex = XdrvMailbox.index; + } + else if (XdrvMailbox.index <= 49) { + ptype = 1; + pindex = XdrvMailbox.index -32; + } + else if (XdrvMailbox.index <= 81) { + ptype = 3; + pindex = XdrvMailbox.index -50; + } + else if (XdrvMailbox.index <= 113) { + ptype = 4; + pindex = XdrvMailbox.index -82; + } + else { + ptype = 5; + pindex = XdrvMailbox.index -114; + } + + if (XdrvMailbox.payload >= 0) { + if (1 == ptype) { + uint32_t param_low = 0; + uint32_t param_high = 255; + switch (pindex) { + case P_HOLD_TIME: + case P_MAX_POWER_RETRY: + param_low = 1; + param_high = 250; + break; + } + if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { + Settings.param[pindex] = XdrvMailbox.payload; +#ifdef USE_LIGHT + if (P_RGB_REMAP == pindex) { + LightUpdateColorMapping(); + restart_flag = 2; + } +#endif +#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL) + if (P_IR_UNKNOW_THRESHOLD == pindex) { + IrReceiveUpdateThreshold(); + } +#endif + } else { + ptype = 99; + } + } else { + if (XdrvMailbox.payload <= 1) { + if (2 == ptype) { + switch (pindex) { + case 5: + case 6: + case 7: + case 9: + case 14: + case 22: + case 23: + case 25: + case 27: + ptype = 99; + break; + case 3: + case 15: + restart_flag = 2; + default: + bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload); + } + if (12 == pindex) { + stop_flash_rotate = XdrvMailbox.payload; + SettingsSave(2); + } + #ifdef USE_HOME_ASSISTANT + if ((19 == pindex) || (30 == pindex)) { + HAssDiscover(); + } + #endif + } + else if (3 == ptype) { + bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); + switch (pindex) { + case 5: + if (0 == XdrvMailbox.payload) { + restart_flag = 2; + } + break; + case 10: + WiFiSetSleepMode(); + break; + case 18: + case 25: + restart_flag = 2; + break; + } + } + else if (4 == ptype) { + bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); + switch (pindex) { + case 3: + case 6: + case 15: + case 20: + case 21: + case 22: + case 24: + case 25: + restart_flag = 2; + break; + } + } + else if (5 == ptype) { + bitWrite(Settings.flag5.data, pindex, XdrvMailbox.payload); + } + } else { + ptype = 99; + } + } + } + + if (ptype < 99) { + if (1 == ptype) { + ResponseCmndIdxNumber(Settings.param[pindex]); + } else { + uint32_t flag = Settings.flag.data; + if (3 == ptype) { + flag = Settings.flag3.data; + } + else if (4 == ptype) { + flag = Settings.flag4.data; + } + else if (5 == ptype) { + flag = Settings.flag5.data; + } + ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex))); + } + } + } +} + +void CmndTemperatureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.temperature_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.temperature_resolution); +} + +void CmndHumidityResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.humidity_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.humidity_resolution); +} + +void CmndPressureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.pressure_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.pressure_resolution); +} + +void CmndPowerResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.wattage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.wattage_resolution); +} + +void CmndVoltageResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.voltage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.voltage_resolution); +} + +void CmndFrequencyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.frequency_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.frequency_resolution); +} + +void CmndCurrentResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.current_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.current_resolution); +} + +void CmndEnergyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + Settings.flag2.energy_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.energy_resolution); +} + +void CmndWeightResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.weight_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.weight_resolution); +} + +void CmndSpeedUnit(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) { + Settings.flag2.speed_conversion = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.speed_conversion); +} + +void CmndModule(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) { + bool present = false; + if (0 == XdrvMailbox.payload) { + XdrvMailbox.payload = USER_MODULE; + present = true; + } else { + XdrvMailbox.payload--; + present = ValidTemplateModule(XdrvMailbox.payload); + } + if (present) { + if (XdrvMailbox.index == 2) { + Settings.fallback_module = XdrvMailbox.payload; + } else { + Settings.last_module = Settings.module; + Settings.module = XdrvMailbox.payload; + SetModuleType(); + if (Settings.last_module != XdrvMailbox.payload) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + restart_flag = 2; + } + } + } + uint8_t module_real = Settings.module; + uint8_t module_number = ModuleNr(); + if (XdrvMailbox.index == 2) { + module_real = Settings.fallback_module; + module_number = (USER_MODULE == Settings.fallback_module) ? 0 : Settings.fallback_module +1; + strcat(XdrvMailbox.command, "2"); + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, module_number, AnyModuleName(module_real).c_str()); +} + +void CmndModules(void) +{ + uint32_t midx = USER_MODULE; + uint32_t lines = 1; + bool jsflg = false; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + uint32_t j = i ? midx +1 : 0; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + jsflg = false; + lines++; + } + } + mqtt_data[0] = '\0'; +} + +void CmndGpio(void) +{ + if (XdrvMailbox.index < ARRAY_SIZE(Settings.my_gp.io)) { + myio cmodule; + ModuleGpios(&cmodule); + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < AGPIO(GPIO_SENSOR_END))) { + bool present = false; + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { + uint32_t midx = pgm_read_word(kGpioNiceList + i); + if ((XdrvMailbox.payload >= (midx & 0xFFE0)) && (XdrvMailbox.payload < midx)) { + present = true; + break; + } + } + if (present) { + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload; + restart_flag = 2; + } + } + Response_P(PSTR("{")); + bool jsflg = false; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if (ValidGPIO(i, cmodule.io[i]) || ((255 == XdrvMailbox.payload) && !FlashPin(i))) { + if (jsflg) { ResponseAppend_P(PSTR(",")); } + jsflg = true; + uint32_t sensor_type = Settings.my_gp.io[i]; + if (!ValidGPIO(i, cmodule.io[i])) { + sensor_type = cmodule.io[i]; + if (AGPIO(GPIO_USER) == sensor_type) { + sensor_type = GPIO_NONE; + } + } + char sindex[4] = { 0 }; + uint32_t sensor_name_idx = BGPIO(sensor_type); + uint32_t nice_list_search = sensor_type & 0xFFE0; + for (uint32_t j = 0; j < ARRAY_SIZE(kGpioNiceList); j++) { + uint32_t nls_idx = pgm_read_word(kGpioNiceList + j); + if (((nls_idx & 0xFFE0) == nice_list_search) && ((nls_idx & 0x001F) > 0)) { + snprintf_P(sindex, sizeof(sindex), PSTR("%d"), (sensor_type & 0x001F) +1); + break; + } + } + const char *sensor_names = kSensorNames; + if (sensor_name_idx > GPIO_FIX_START) { + sensor_name_idx = sensor_name_idx - GPIO_FIX_START -1; + sensor_names = kSensorNamesFixed; + } + char stemp1[TOPSZ]; + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex); + } + } + if (jsflg) { + ResponseJsonEnd(); + } else { + ResponseCmndChar(D_JSON_NOT_SUPPORTED); + } + } +} + +void ShowGpios(const uint16_t *NiceList, uint32_t size, uint32_t offset, uint32_t &lines) { + myio cmodule; + ModuleGpios(&cmodule); + bool jsflg = false; + for (uint32_t i = offset; i < size; i++) { + uint32_t ridx = pgm_read_word(NiceList + i) & 0xFFE0; + uint32_t midx = BGPIO(ridx); + if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + char stemp1[TOPSZ]; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), ridx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == size -1)) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + jsflg = false; + lines++; + } + } +} + +void CmndGpios(void) +{ + + + + + + + uint32_t lines = 1; + ShowGpios(kGpioNiceList, ARRAY_SIZE(kGpioNiceList), 0, lines); +#ifdef ESP8266 +#ifndef USE_ADC_VCC + ShowGpios(kAdcNiceList, ARRAY_SIZE(kAdcNiceList), 1, lines); +#endif +#endif + mqtt_data[0] = '\0'; +} + +void CmndTemplate(void) +{ + + + bool error = false; + + if (strstr(XdrvMailbox.data, "{") == nullptr) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) { + XdrvMailbox.payload--; + if (ValidTemplateModule(XdrvMailbox.payload)) { + ModuleDefault(XdrvMailbox.payload); + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } + } + else if (0 == XdrvMailbox.payload) { + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + } + else if (255 == XdrvMailbox.payload) { + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + SettingsUpdateText(SET_TEMPLATE_NAME, "Merged"); + uint32_t j = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + if (my_module.io[j] > GPIO_NONE) { + Settings.user_template.gp.io[i] = my_module.io[j]; + } + j++; + } + } + } + else { + if (JsonTemplate(XdrvMailbox.data)) { + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } else { + ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); + error = true; + } + } + if (!error) { TemplateJson(); } +} + +void CmndPwm(void) +{ + if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) { + Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; + analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); + } + Response_P(PSTR("{")); + MqttShowPWMState(); + ResponseJsonEnd(); + } +} + +void CmndPwmfrequency(void) +{ + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { + Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; + analogWriteFreq(Settings.pwm_frequency); + } + ResponseCmndNumber(Settings.pwm_frequency); +} + +void CmndPwmrange(void) { + + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { + uint32_t pwm_range = XdrvMailbox.payload; + uint32_t pwm_resolution = 0; + while (pwm_range) { + pwm_resolution++; + pwm_range >>= 1; + } + pwm_range = (1 << pwm_resolution) - 1; + uint32_t old_pwm_range = Settings.pwm_range; + Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : pwm_range; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (Settings.pwm_value[i] > Settings.pwm_range) { + Settings.pwm_value[i] = Settings.pwm_range; + } + } + if (Settings.pwm_range != old_pwm_range) { + analogWriteRange(Settings.pwm_range); + } + } + ResponseCmndNumber(Settings.pwm_range); +} + +void CmndButtonDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { + Settings.button_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.button_debounce); +} + +void CmndSwitchDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1010)) { + Settings.switch_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.switch_debounce); +} + +void CmndBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; + baudrate = (XdrvMailbox.payload & 0xFFFF) * 300; + SetSerialBaudrate(baudrate); + } + ResponseCmndNumber(baudrate); +} + +void CmndSerialConfig(void) +{ + + + + + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len < 3) { + if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { + SetSerialConfig(XdrvMailbox.payload); + } + } + else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { + uint8_t serial_config = XdrvMailbox.payload -5; + + bool valid = true; + char parity = (XdrvMailbox.data[1] & 0xdf); + if ('E' == parity) { + serial_config += 0x08; + } + else if ('O' == parity) { + serial_config += 0x10; + } + else if ('N' != parity) { + valid = false; + } + + if ('2' == XdrvMailbox.data[2]) { + serial_config += 0x04; + } + else if ('1' != XdrvMailbox.data[2]) { + valid = false; + } + + if (valid) { + SetSerialConfig(serial_config); + } + } + } + ResponseCmndChar(GetSerialConfig().c_str()); +} + +void CmndSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + SetSeriallog(LOG_LEVEL_NONE); + Settings.flag.mqtt_serial = 1; + Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + Serial.printf("%s\n", XdrvMailbox.data); + } + else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) { + for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) { + Serial.write(XdrvMailbox.data[i]); + } + } + else if (3 == XdrvMailbox.index) { + uint32_t dat_len = XdrvMailbox.data_len; + Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); + } + else if (5 == XdrvMailbox.index) { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + } + else if (6 == XdrvMailbox.index) { + SerialSendDecimal(XdrvMailbox.data); + } + ResponseCmndDone(); + } + } +} + +void CmndSerialDelimiter(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) { + if (XdrvMailbox.payload > 0) { + Settings.serial_delimiter = XdrvMailbox.payload; + } else { + uint32_t dat_len = XdrvMailbox.data_len; + Unescape(XdrvMailbox.data, &dat_len); + Settings.serial_delimiter = XdrvMailbox.data[0]; + } + } + ResponseCmndNumber(Settings.serial_delimiter); +} + +void CmndSyslog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + SetSyslog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level); +} + +void CmndLoghost(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_SYSLOG_HOST)); +} + +void CmndLogport(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.syslog_port); +} + +void CmndIpAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + uint32_t address; + if (ParseIp(&address, XdrvMailbox.data)) { + Settings.ip_address[XdrvMailbox.index -1] = address; + + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); + Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:""); + } +} + +void CmndNtpServer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_NTP_SERVERS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_NTPSERVER1, MAX_NTP_SERVERS); + } else { + uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(ntp_server, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? PSTR(NTP_SERVER1) : (2 == XdrvMailbox.index) ? PSTR(NTP_SERVER2) : PSTR(NTP_SERVER3) : XdrvMailbox.data); + SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); + + ntp_force_sync = true; + } + ResponseCmndIdxChar(SettingsText(ntp_server)); + } + } +} + +void CmndAp(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: + Settings.sta_active ^= 1; + break; + case 1: + case 2: + Settings.sta_active = XdrvMailbox.payload -1; + } + Settings.wifi_channel = 0; + restart_flag = 2; + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, EscapeJSONString(SettingsText(SET_STASSID1 + Settings.sta_active)).c_str()); +} + +void CmndSsid(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_STASSID1, MAX_SSIDS); + } else { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1)); + } + } +} + +void CmndPassword(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) { + SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1)); + } else { + Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); + } + } +} + +void CmndHostname(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + } + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_HOSTNAME)); +} + +void CmndWifiConfig(void) +{ + if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) { + if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) { + XdrvMailbox.payload = WIFI_MANAGER; + } + Settings.sta_config = XdrvMailbox.payload; + wifi_state_flag = Settings.sta_config; + if (WifiState() > WIFI_RESTART) { + restart_flag = 2; + } + } + char stemp1[TOPSZ]; + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig)); +} + +void CmndWifi(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag4.network_wifi = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndStateText(Settings.flag4.network_wifi); +} + +void CmndDevicename(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_DEVICENAME, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? SettingsText(SET_FRIENDLYNAME1) : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_DEVICENAME)); +} + +void CmndFriendlyname(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { + if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { + ResponseCmndAll(SET_FRIENDLYNAME1, MAX_FRIENDLYNAMES); + } else { + if (XdrvMailbox.data_len > 0) { + char stemp1[TOPSZ]; + if (1 == XdrvMailbox.index) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); + } else { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); + } + SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data); + } + ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1)); + } + } +} + +void CmndSwitchMode(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) { + Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]); + } +} + +void CmndInterlock(void) +{ + + uint32_t max_relays = devices_present; + if (light_type) { max_relays--; } + if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } + if (max_relays > 1) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } + char *group; + char *q; + uint32_t group_index = 0; + power_t relay_mask = 0; + for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { + char *str; + char *p; + for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { + int pbit = atoi(str); + if ((pbit > 0) && (pbit <= max_relays)) { + pbit--; + if (!bitRead(relay_mask, pbit)) { + bitSet(relay_mask, pbit); + bitSet(Settings.interlock[group_index], pbit); + } + } + } + group_index++; + } + for (uint32_t i = 0; i < group_index; i++) { + uint32_t minimal_bits = 0; + for (uint32_t j = 0; j < max_relays; j++) { + if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } + } + if (minimal_bits < 2) { Settings.interlock[i] = 0; } + } + } else { + Settings.flag.interlock = XdrvMailbox.payload &1; + if (Settings.flag.interlock) { + SetDevicePower(power, SRC_IGNORE); + } + } +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + ShutterInit(); + } +#endif + } + Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + uint32_t anygroup = 0; + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i]) { + anygroup++; + ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); + uint32_t anybit = 0; + power_t mask = 1; + for (uint32_t j = 0; j < max_relays; j++) { + if (Settings.interlock[i] & mask) { + anybit++; + ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); + } + mask <<= 1; + } + } + } + if (!anygroup) { + for (uint32_t j = 1; j <= max_relays; j++) { + ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); + } + } + ResponseAppend_P(PSTR("\"}")); + } else { + + Settings.flag.interlock = APP_INTERLOCK_MODE; + ResponseCmndStateText(Settings.flag.interlock); + } +} + +void CmndTeleperiod(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload; + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; + + } + tele_period = Settings.tele_period; + ResponseCmndNumber(Settings.tele_period); +} + +void CmndReset(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 211; + ResponseCmndChar(PSTR(D_JSON_RESET_AND_RESTARTING)); + break; + case 2 ... 6: + restart_flag = 210 + XdrvMailbox.payload; + Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); + break; + case 99: + Settings.bootcount = 0; + Settings.bootcount_reset_time = 0; + ResponseCmndDone(); + break; + default: + ResponseCmndChar(PSTR(D_JSON_ONE_TO_RESET)); + } +} + +void CmndTime(void) +{ + + + + + + + + uint32_t format = Settings.flag2.time_format; + if (XdrvMailbox.data_len > 0) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 5)) { + Settings.flag2.time_format = XdrvMailbox.payload -1; + format = Settings.flag2.time_format; + } else { + format = 1; + RtcSetTime(XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + ResponseAppendTimeFormat(format); + ResponseJsonEnd(); +} + +void CmndTimezone(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) { + Settings.timezone = XdrvMailbox.payload; + Settings.timezone_minutes = 0; + if (XdrvMailbox.payload < 15) { + char *p = strtok (XdrvMailbox.data, ":"); + if (p) { + p = strtok (nullptr, ":"); + if (p) { + Settings.timezone_minutes = strtol(p, nullptr, 10); + if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } + } + } + } else { + Settings.timezone = 99; + } + ntp_force_sync = true; + } + if (99 == Settings.timezone) { + ResponseCmndNumber(Settings.timezone); + } else { + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); + ResponseCmndChar(stemp1); + } +} + +void CmndTimeStdDst(uint32_t ts) +{ + + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + uint32_t tpos = 0; + int value = 0; + char *p = XdrvMailbox.data; + char *q = p; + while (p && (tpos < 7)) { + if (p > q) { + if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } + if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } + if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } + if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } + if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } + if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } + } + p = Trim(p); + if (tpos && (*p == ',')) { p++; } + p = Trim(p); + q = p; + value = strtol(p, &p, 10); + tpos++; + } + ntp_force_sync = true; + } else { + if (0 == XdrvMailbox.payload) { + if (0 == ts) { + SettingsResetStd(); + } else { + SettingsResetDst(); + } + } + ntp_force_sync = true; + } + } + Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), + XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); +} + +void CmndTimeStd(void) +{ + CmndTimeStdDst(0); +} + +void CmndTimeDst(void) +{ + CmndTimeStdDst(1); +} + +void CmndAltitude(void) +{ + if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) { + Settings.altitude = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.altitude); +} + +void CmndLedPower(void) { + + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { + if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.ledstate &= 8; + uint32_t mask = 1 << (XdrvMailbox.index -1); + switch (XdrvMailbox.payload) { + case 0: + led_power &= (0xFF ^ mask); + Settings.ledstate = 0; + break; + case 1: + led_power |= mask; + Settings.ledstate = 8; + break; + case 2: + led_power ^= mask; + Settings.ledstate ^= 8; + break; + } + blinks = 0; + if (!PinUsed(GPIO_LEDLNK)) { + SetLedPower(Settings.ledstate &8); + } else { + SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); + } + } + bool state = bitRead(led_power, XdrvMailbox.index -1); + if (!PinUsed(GPIO_LEDLNK)) { + state = bitRead(Settings.ledstate, 3); + } + ResponseCmndIdxChar(GetStateText(state)); + } +} + +void CmndLedState(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) { + Settings.ledstate = XdrvMailbox.payload; + if (!Settings.ledstate) { + SetLedPowerAll(0); + SetLedLink(0); + } + } + ResponseCmndNumber(Settings.ledstate); +} + +void CmndLedMask(void) { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_PWM_DIMMER + PWMDimmerSetBrightnessLeds(0); +#endif + Settings.ledmask = XdrvMailbox.payload; +#ifdef USE_PWM_DIMMER + PWMDimmerSetBrightnessLeds(-1); +#endif + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); + ResponseCmndChar(stemp1); +} + +void CmndLedPwmOff(void) { + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload < 0) { + Settings.ledpwm_off = 0; + } + else if (XdrvMailbox.payload > 255) { + Settings.ledpwm_off = 255; + } else { + Settings.ledpwm_off = XdrvMailbox.payload; + } + UpdateLedPowerAll(); + } + ResponseCmndNumber(Settings.ledpwm_off); +} + +void CmndLedPwmOn(void) { + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload < 0) { + Settings.ledpwm_on = 0; + } + else if (XdrvMailbox.payload > 255) { + Settings.ledpwm_on = 255; + } else { + Settings.ledpwm_on = XdrvMailbox.payload; + } + UpdateLedPowerAll(); + } + ResponseCmndNumber(Settings.ledpwm_on); +} + +void CmndLedPwmMode(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { + if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + uint32_t mask = 1 << (XdrvMailbox.index -1); + switch (XdrvMailbox.payload) { + case 0: + Settings.ledpwm_mask &= (0xFF ^ mask); + break; + case 1: + Settings.ledpwm_mask |= mask; + break; + case 2: + Settings.ledpwm_mask ^= mask; + break; + } + UpdateLedPowerAll(); + } + bool state = bitRead(Settings.ledpwm_mask, XdrvMailbox.index -1); + ResponseCmndIdxChar(GetStateText(state)); + } +} + +void CmndWifiPower(void) +{ + if (XdrvMailbox.data_len > 0) { + Settings.wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if (Settings.wifi_output_power > 205) { + Settings.wifi_output_power = 205; + } + WifiSetOutputPower(); + } + ResponseCmndChar(WifiGetOutputPower().c_str()); +} + +#ifdef USE_I2C +void CmndI2cScan(void) +{ + if (i2c_flg) { + I2cScan(mqtt_data, sizeof(mqtt_data)); + } +} + +void CmndI2cDriver(void) +{ + if (XdrvMailbox.index < MAX_I2C_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.i2c_drivers[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + restart_flag = 2; + } + } + Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":")); + I2cDriverState(); + ResponseJsonEnd(); +} +#endif + +#ifdef USE_DEVICE_GROUPS +void CmndDevGroupName(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) { + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len > TOPSZ) + XdrvMailbox.data[TOPSZ - 1] = 0; + else if (1 == XdrvMailbox.data_len && ('"' == XdrvMailbox.data[0] || '0' == XdrvMailbox.data[0])) + XdrvMailbox.data[0] = 0; + SettingsUpdateText(SET_DEV_GROUP_NAME1 + XdrvMailbox.index - 1, XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndAll(SET_DEV_GROUP_NAME1, MAX_DEV_GROUP_NAMES); + } +} + +#ifdef USE_DEVICE_GROUPS_SEND +void CmndDevGroupSend(void) +{ + uint8_t device_group_index = (XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0); + if (device_group_index < device_group_count) { + if (!_SendDeviceGroupMessage(device_group_index, (DevGroupMessageType)(DGR_MSGTYPE_UPDATE_COMMAND + DGR_MSGTYPFLAG_WITH_LOCAL))) { + ResponseCmndChar(XdrvMailbox.data); + } + } +} +#endif + +void CmndDevGroupShare(void) +{ + uint32_t parm[2] = { Settings.device_group_share_in, Settings.device_group_share_out }; + ParseParameters(2, parm); + Settings.device_group_share_in = parm[0]; + Settings.device_group_share_out = parm[1]; + Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":\"%X\",\"Out\":\"%X\"}}"), Settings.device_group_share_in, Settings.device_group_share_out); +} + +void CmndDevGroupStatus(void) +{ + DeviceGroupStatus((XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0)); +} +#endif + +void CmndSensor(void) +{ + XsnsCall(FUNC_COMMAND_SENSOR); +} + +void CmndDriver(void) +{ + XdrvCall(FUNC_COMMAND_DRIVER); +} + +#ifdef ESP32 + +void CmndCpuFrequency(void) { + if ((80 == XdrvMailbox.payload) || (160 == XdrvMailbox.payload) || (240 == XdrvMailbox.payload)) { + setCpuFrequencyMhz(XdrvMailbox.payload); + } + ResponseCmndNumber(getCpuFrequencyMhz()); +} + +void CmndTouchCal(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload < MAX_KEYS + 1) TOUCH_BUTTON.calibration = bitSet(TOUCH_BUTTON.calibration, XdrvMailbox.payload); + if (XdrvMailbox.payload == 0) TOUCH_BUTTON.calibration = 0; + if (XdrvMailbox.payload == 255) TOUCH_BUTTON.calibration = 255; + } + Response_P(PSTR("{\"" D_CMND_TOUCH_CAL "\": %u"), TOUCH_BUTTON.calibration); + ResponseJsonEnd(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,")); +} + +void CmndTouchThres(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload<256){ + TOUCH_BUTTON.pin_threshold = XdrvMailbox.payload; + } + } + Response_P(PSTR("{\"" D_CMND_TOUCH_THRES "\": %u"), TOUCH_BUTTON.pin_threshold); + ResponseJsonEnd(); +} + +void CmndTouchNum(void) +{ + if (XdrvMailbox.payload >= 0) { + if (XdrvMailbox.payload<32){ + TOUCH_BUTTON.hit_threshold = XdrvMailbox.payload; + } + } + Response_P(PSTR("{\"" D_CMND_TOUCH_NUM "\": %u"), TOUCH_BUTTON.hit_threshold); + ResponseJsonEnd(); + +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_cores.ino" +# 26 "/workspace/Tasmota/tasmota/support_cores.ino" +void resetPins() +{ +# 37 "/workspace/Tasmota/tasmota/support_cores.ino" +} + + + + + +#ifdef ESP8266 + +void HwWdtDisable(void) { + *((volatile uint32_t*) 0x60000900) &= ~(1); +} + +void HwWdtEnable(void) { + *((volatile uint32_t*) 0x60000900) |= 1; +} + +void WdtDisable(void) { + ESP.wdtDisable(); + HwWdtDisable(); +} + +void WdtEnable(void) { + HwWdtEnable(); + ESP.wdtEnable(0); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_crash_recorder.ino" +# 20 "/workspace/Tasmota/tasmota/support_crash_recorder.ino" +#ifdef ESP8266 + +const uint32_t crash_magic = 0x53415400; +const uint32_t crash_rtc_offset = 32; +const uint32_t crash_dump_max_len = 31; + + + + + + +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) +{ + uint32_t addr_written = 0; + uint32_t value; + + for (uint32_t i = stack; i < stack_end; i += 4) { + value = *((uint32_t*) i); + if ((value >= 0x40000000) && (value < 0x40300000)) { + ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value)); + addr_written++; + if (addr_written >= crash_dump_max_len) { break; } + } + } + value = crash_magic + addr_written; + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); +} + + +void CmndCrash(void) +{ + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); +} + + +void CmndWDT(void) +{ + volatile uint32_t dummy = 0; + while (1) { + dummy++; + } +} + + +void CmndBlockedLoop(void) +{ + while (1) { + delay(1000); + } +} + + +void CrashDumpClear(void) +{ + uint32_t value = 0; + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); +} + + + + + +bool CrashFlag(void) +{ + return ((ResetReason() == REASON_EXCEPTION_RST) || (ResetReason() == REASON_SOFT_WDT_RST) || oswatch_blocked_loop); +} + +void CrashDump(void) +{ + ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""), + resetInfo.exccause, + GetResetReason().c_str(), + resetInfo.epc1, + resetInfo.epc2, + resetInfo.epc3, + resetInfo.excvaddr, + resetInfo.depc); + + uint32_t value; + ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); + if (crash_magic == (value & 0xFFFFFF00)) { + ResponseAppend_P(PSTR(",\"CallChain\":[")); + uint32_t count = value & 0x3F; + for (uint32_t i = 0; i < count; i++) { + ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value)); + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_device_groups.ino" +# 24 "/workspace/Tasmota/tasmota/support_device_groups.ino" +#ifdef USE_DEVICE_GROUPS + + +#define DGR_MEMBER_TIMEOUT 45000 +#define DGR_ANNOUNCEMENT_INTERVAL 60000 +#define DEVICE_GROUP_MESSAGE "TASMOTA_DGR" + +const char kDeviceGroupMessage[] PROGMEM = DEVICE_GROUP_MESSAGE; + +struct device_group_member { + struct device_group_member * flink; + IPAddress ip_address; + uint16_t received_sequence; + uint16_t acked_sequence; + uint32_t unicast_count; +}; + +struct device_group { + uint32_t next_announcement_time; + uint32_t next_ack_check_time; + uint32_t member_timeout_time; + uint16_t outgoing_sequence; + uint16_t last_full_status_sequence; + uint16_t message_length; + uint16_t ack_check_interval; + uint8_t message_header_length; + uint8_t initial_status_requests_remaining; + char group_name[TOPSZ]; + uint8_t message[128]; + struct device_group_member * device_group_members; +#ifdef USE_DEVICE_GROUPS_SEND + uint8_t values_8bit[DGR_ITEM_LAST_8BIT]; + uint16_t values_16bit[DGR_ITEM_LAST_16BIT - DGR_ITEM_MAX_8BIT - 1]; + uint32_t values_32bit[DGR_ITEM_LAST_32BIT - DGR_ITEM_MAX_16BIT - 1]; +#endif +}; + +WiFiUDP device_groups_udp; +struct device_group * device_groups; +uint32_t next_check_time; +bool device_groups_initialized = false; +bool device_groups_up = false; +bool building_status_message = false; +bool ignore_dgr_sends = false; + +char * IPAddressToString(const IPAddress& ip_address) +{ + static char buffer[16]; + sprintf_P(buffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); + return buffer; +} + +uint8_t * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false) +{ + uint8_t * message_ptr = &device_group->message[device_group->message_header_length]; + if (!hold_sequence && !++device_group->outgoing_sequence) device_group->outgoing_sequence = 1; + *message_ptr++ = device_group->outgoing_sequence & 0xff; + *message_ptr++ = device_group->outgoing_sequence >> 8; + *message_ptr++ = flags & 0xff; + *message_ptr++ = flags >> 8; + return message_ptr; +} + + +bool DeviceGroupItemShared(bool incoming, uint8_t item) +{ + uint32_t mask; + if (item == DGR_ITEM_LIGHT_BRI || item == DGR_ITEM_BRI_POWER_ON) + mask = DGR_SHARE_LIGHT_BRI; + else if (item == DGR_ITEM_POWER) + mask = DGR_SHARE_POWER; + else if (item == DGR_ITEM_LIGHT_SCHEME) + mask = DGR_SHARE_LIGHT_SCHEME; + else if (item == DGR_ITEM_LIGHT_FIXED_COLOR || DGR_ITEM_LIGHT_CHANNELS) + mask = DGR_SHARE_LIGHT_COLOR; + else if (item == DGR_ITEM_LIGHT_FADE || item == DGR_ITEM_LIGHT_SPEED) + mask = DGR_SHARE_LIGHT_FADE; + else if (item == DGR_ITEM_BRI_PRESET_LOW || item == DGR_ITEM_BRI_PRESET_HIGH) + mask = DGR_SHARE_DIMMER_SETTINGS; + else if (item == DGR_ITEM_EVENT) + mask = DGR_SHARE_EVENT; + else + return true; + return mask & (incoming ? Settings.device_group_share_in : Settings.device_group_share_out); +} + +void DeviceGroupsInit(void) +{ + + if (!device_group_count) { + + + + if (Settings.flag4.multiple_device_groups) { + for (uint32_t relay_index = 0; relay_index < MAX_RELAYS; relay_index++) { + if (PinUsed(GPIO_REL1, relay_index)) device_group_count = relay_index + 1; + } + if (device_group_count > MAX_DEV_GROUP_NAMES) device_group_count = MAX_DEV_GROUP_NAMES; + } + + + else { + device_group_count = 1; + } + } + + + + for (; device_group_count < MAX_DEV_GROUP_NAMES; device_group_count++) { + if (!*SettingsText(SET_DEV_GROUP_NAME1 + device_group_count)) break; + } + + + device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group)); + if (!device_groups) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating %u-element array"), device_group_count); + return; + } + + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + strcpy(device_group->group_name, SettingsText(SET_DEV_GROUP_NAME1 + device_group_index)); + + + + if (!device_group->group_name[0]) { + strcpy(device_group->group_name, SettingsText(SET_MQTT_GRP_TOPIC)); + if (device_group_index) { + snprintf_P(device_group->group_name, sizeof(device_group->group_name), PSTR("%s%u"), device_group->group_name, device_group_index + 1); + } + } + device_group->message_header_length = sprintf_P((char *)device_group->message, PSTR("%s%s"), kDeviceGroupMessage, device_group->group_name) + 1; + device_group->last_full_status_sequence = -1; + } + + + if (!Settings.device_group_share_in && !Settings.device_group_share_out) { + Settings.device_group_share_in = Settings.device_group_share_out = 0xffffffff; + } + + device_groups_initialized = true; +} + +void DeviceGroupsStart() +{ + if (Settings.flag4.device_groups_enabled && !device_groups_up && !restart_flag) { + + + if (!device_groups_initialized) { + DeviceGroupsInit(); + if (!device_groups_initialized) return; + } + + + if (!device_groups_udp.beginMulticast(WiFi.localIP(), IPAddress(DEVICE_GROUPS_ADDRESS), DEVICE_GROUPS_PORT)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error subscribing")); + return; + } + device_groups_up = true; + + + + + next_check_time = millis() + 2000; + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + device_group->next_announcement_time = -1; + device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message; + device_group->initial_status_requests_remaining = 10; + device_group->next_ack_check_time = next_check_time; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering members")); + } +} + +void DeviceGroupsStop() +{ + device_groups_udp.flush(); + device_groups_up = false; +} + +void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct device_group_member * device_group_member, uint8_t * message, int message_length, bool received) +{ + char log_buffer[512]; + bool item_processed = false; + uint16_t message_sequence; + uint16_t flags; + int device_group_index = device_group - device_groups; + int log_length; + int log_remaining; + char * log_ptr; + + + uint8_t * message_end_ptr = message + message_length; + uint8_t * message_ptr = message + strlen((char *)message) + 1; + + + if (message_ptr + 4 > message_end_ptr) goto badmsg; + message_sequence = *message_ptr++; + message_sequence |= *message_ptr++ << 8; + flags = *message_ptr++; + flags |= *message_ptr++ << 8; + + + log_length = sprintf(log_buffer, PSTR("DGR: %s %s message %s %s: seq=%u, flags=%u"), (received ? PSTR("Received") : PSTR("Sending")), device_group->group_name, (received ? PSTR("from") : PSTR("to")), (device_group_member ? IPAddressToString(device_group_member->ip_address) : received ? PSTR("local") : PSTR("network")), message_sequence, flags); + log_ptr = log_buffer + log_length; + log_remaining = sizeof(log_buffer) - log_length; + + + if (flags == DGR_FLAG_ANNOUNCEMENT) goto write_log; + + + + if (flags == DGR_FLAG_ACK) { + if (received && device_group_member && (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536)) { + device_group_member->acked_sequence = message_sequence; + } + goto write_log; + } + + + if (device_group_member) { + if (received) { + if (!(flags & DGR_FLAG_MORE_TO_COME)) { + *(message_ptr - 2) = DGR_FLAG_ACK; + *(message_ptr - 1) = 0; + SendReceiveDeviceGroupMessage(device_group, device_group_member, message, message_ptr - message, false); + } + } + + + else { + log_length = snprintf(log_ptr, log_remaining, PSTR(", last ack=%u"), device_group_member->acked_sequence); + log_ptr += log_length; + log_remaining -= log_length; + goto write_log; + } + } + + + if ((flags & DGR_FLAG_STATUS_REQUEST)) goto write_log; + + + if (received) { + + + if (device_group_member) { + if (message_sequence <= device_group_member->received_sequence) { + if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) { + log_length = snprintf(log_ptr, log_remaining, PSTR(" (old)")); + log_ptr += log_length; + log_remaining -= log_length; + goto write_log; + } + } + device_group_member->received_sequence = message_sequence; + } +# 294 "/workspace/Tasmota/tasmota/support_device_groups.ino" + XdrvMailbox.command = nullptr; + XdrvMailbox.index = flags | message_sequence << 16; + if (device_group_index == 0 && first_device_group_is_local) XdrvMailbox.index |= DGR_FLAG_LOCAL; + XdrvMailbox.topic = (char *)&device_group_index; + if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true; + + + + ignore_dgr_sends = true; + } + + uint8_t item; + int32_t value; + for (;;) { + if (message_ptr >= message_end_ptr) goto badmsg; + item = *message_ptr++; + if (!item) break; + +#ifdef DEVICE_GROUPS_DEBUG + switch (item) { + case DGR_ITEM_FLAGS: + case DGR_ITEM_LIGHT_FADE: + case DGR_ITEM_LIGHT_SPEED: + case DGR_ITEM_LIGHT_BRI: + case DGR_ITEM_LIGHT_SCHEME: + case DGR_ITEM_LIGHT_FIXED_COLOR: + case DGR_ITEM_BRI_PRESET_LOW: + case DGR_ITEM_BRI_PRESET_HIGH: + case DGR_ITEM_BRI_POWER_ON: + case DGR_ITEM_POWER: + case DGR_ITEM_EVENT: + case DGR_ITEM_LIGHT_CHANNELS: + break; + default: + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: *** Invalid item=%u"), item); + } +#endif + + log_length = snprintf(log_ptr, log_remaining, PSTR(", %u="), item); + log_ptr += log_length; + log_remaining -= log_length; + log_length = 0; + if (item <= DGR_ITEM_LAST_32BIT) { + value = *message_ptr++; + if (item > DGR_ITEM_MAX_8BIT) { + value |= *message_ptr++ << 8; + if (item > DGR_ITEM_MAX_16BIT) { + value |= *message_ptr++ << 16; + value |= *message_ptr++ << 24; +#ifdef USE_DEVICE_GROUPS_SEND + device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value); +#endif + } +#ifdef USE_DEVICE_GROUPS_SEND + else { + device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value; + } +#endif + } +#ifdef USE_DEVICE_GROUPS_SEND + else { + device_group->values_8bit[item] = value; + } +#endif + log_length = snprintf(log_ptr, log_remaining, PSTR("%u"), value); + } + else { + value = *message_ptr++; + if (received) XdrvMailbox.data = (char *)message_ptr; + if (message_ptr + value >= message_end_ptr) goto badmsg; + if (item <= DGR_ITEM_MAX_STRING) { + log_length = snprintf(log_ptr, log_remaining, PSTR("'%s'"), message_ptr); + } + else { + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + log_length = snprintf(log_ptr, log_remaining, PSTR("%u,%u,%u,%u,%u,%u"), *message_ptr, *(message_ptr + 1), *(message_ptr + 2), *(message_ptr + 3), *(message_ptr + 4), *(message_ptr + 5)); + break; + } + } + message_ptr += value; + } + log_ptr += log_length; + log_remaining -= log_length; + + if (received && DeviceGroupItemShared(true, item)) { + item_processed = true; + XdrvMailbox.command_code = item; + XdrvMailbox.payload = value; + XdrvMailbox.data_len = value; + *log_ptr++ = '*'; + log_remaining--; + switch (item) { + case DGR_ITEM_POWER: + if (Settings.flag4.multiple_device_groups) { + if (device_group_index < devices_present) { + bool on = (value & 1); + if (on != (power & (1 << device_group_index))) ExecuteCommandPower(device_group_index + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE); + } + } + else if (XdrvMailbox.index & DGR_FLAG_LOCAL) { + uint8_t mask_devices = value >> 24; + if (mask_devices > devices_present) mask_devices = devices_present; + for (uint32_t i = 0; i < mask_devices; i++) { + uint32_t mask = 1 << i; + bool on = (value & mask); + if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE); + } + } + break; +#ifdef USE_RULES + case DGR_ITEM_EVENT: + CmndEvent(); + break; +#endif + case DGR_ITEM_COMMAND: + ExecuteCommand(XdrvMailbox.data, SRC_REMOTE); + break; + } + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + } + } + + if (received) { + if (item_processed) { + XdrvMailbox.command_code = DGR_ITEM_EOL; + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + } + } + +write_log: + *log_ptr++ = 0; + AddLog_P(LOG_LEVEL_DEBUG_MORE, log_buffer); + + + + if (received) { + if ((flags & DGR_FLAG_STATUS_REQUEST)) { + if ((flags & DGR_FLAG_RESET) || device_group_member->acked_sequence != device_group->last_full_status_sequence) { + _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); + } + } + } + + + else { + int attempt; + IPAddress ip_address = (device_group_member ? device_group_member->ip_address : IPAddress(DEVICE_GROUPS_ADDRESS)); + for (attempt = 1; attempt <= 5; attempt++) { + if (device_groups_udp.beginPacket(ip_address, DEVICE_GROUPS_PORT)) { + device_groups_udp.write(message, message_length); + if (device_groups_udp.endPacket()) break; + } + delay(10); + } + if (attempt > 5) AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error sending message")); + } + goto cleanup; + +badmsg: + AddLog_P(LOG_LEVEL_ERROR, PSTR("%s ** incorrect length"), log_buffer); + +cleanup: + if (received) { + skip_light_fade = false; + ignore_dgr_sends = false; + } +} + +bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...) +{ + + if (!device_groups_up) return 1; + + + if (device_group_index >= device_group_count) return 0; + + + bool with_local = ((message_type & DGR_MSGTYPFLAG_WITH_LOCAL) != 0); + message_type = (DevGroupMessageType)(message_type & 0x7F); + + + if (ignore_dgr_sends && message_type != DGR_MSGTYPE_UPDATE_COMMAND) return 0; + + + struct device_group * device_group = &device_groups[device_group_index]; + + + if (device_group->initial_status_requests_remaining) return 1; + + +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Building %s %spacket"), device_group->group_name, (message_type == DGR_MSGTYP_FULL_STATUS ? PSTR("full status ") : PSTR(""))); +#endif + uint16_t original_sequence = device_group->outgoing_sequence; + uint16_t flags = 0; + if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) + flags = DGR_FLAG_MORE_TO_COME; + else if (message_type == DGR_MSGTYP_UPDATE_DIRECT) + flags = DGR_FLAG_DIRECT; + uint8_t * message_ptr = BeginDeviceGroupMessage(device_group, flags, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE); + + + + + if (message_type == DGR_MSGTYP_FULL_STATUS) { + device_group->last_full_status_sequence = device_group->outgoing_sequence; + device_group->message_length = 0; + + + + building_status_message = true; + + + SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power); + XdrvMailbox.index = 0; + if (device_group_index == 0 && first_device_group_is_local) XdrvMailbox.index = DGR_FLAG_LOCAL; + XdrvMailbox.command_code = DGR_ITEM_STATUS; + XdrvMailbox.topic = (char *)&device_group_index; + XdrvCall(FUNC_DEVICE_GROUP_ITEM); + building_status_message = false; + + + *(message_ptr - 2) |= DGR_FLAG_FULL_STATUS; + + + if (!device_group->message_length) { + *message_ptr++ = 0; + device_group->message_length = message_ptr - device_group->message; + } + } + + else { +#ifdef USE_DEVICE_GROUPS_SEND + uint8_t out_buffer[128]; + bool escaped; + char chr; + char oper; + uint32_t old_value; + uint8_t * out_ptr = out_buffer; +#endif + struct item { + uint8_t item; + uint32_t value; + void * value_ptr; + } item_array[32]; + bool shared; + uint8_t item; + uint32_t value; + uint8_t * value_ptr; + uint8_t * first_item_ptr = message_ptr; + struct item * item_ptr; + va_list ap; + + + item_ptr = item_array; +#ifdef USE_DEVICE_GROUPS_SEND + if (message_type == DGR_MSGTYPE_UPDATE_COMMAND) { + value_ptr = (uint8_t *)XdrvMailbox.data; + while ((item = strtoul((char *)value_ptr, (char **)&value_ptr, 0))) { + item_ptr->item = item; + if (*value_ptr != '=') return 1; + value_ptr++; + if (item <= DGR_ITEM_MAX_32BIT) { + oper = 0; + if (*value_ptr == '@') { + oper = value_ptr[1]; + value_ptr += 2; + } + value = (isdigit(*value_ptr) ? strtoul((char *)value_ptr, (char **)&value_ptr, 0) : 1); + if (oper) { + old_value = (item <= DGR_ITEM_MAX_8BIT ? device_group->values_8bit[item] : (item <= DGR_ITEM_MAX_16BIT ? device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] : device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1])); + value = (oper == '+' ? old_value + value : (oper == '-' ? old_value - value : (oper == '^' ? old_value ^ (value ? value : 0xffffffff) : old_value))); + } + item_ptr->value = value; + } + else { + item_ptr->value_ptr = out_ptr; + if (item <= DGR_ITEM_MAX_STRING) { + escaped = false; + while ((chr = *value_ptr++)) { + if (chr == ' ' && !escaped) break; + if (!(escaped = (chr == '\\' && !escaped))) *out_ptr++ = chr; + } + *out_ptr = 0; + } + else { + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + for (int i = 0; i < 6; i++) { + *out_ptr++ = strtoul((char *)value_ptr, (char **)&value_ptr, 0); + if (*value_ptr == ',') value_ptr++; + } + break; + } + } + } + item_ptr++; + } + } + else { +#endif + va_start(ap, message_type); + while ((item = va_arg(ap, int))) { + item_ptr->item = item; + if (item <= DGR_ITEM_MAX_32BIT) + item_ptr->value = va_arg(ap, int); + else if (item <= DGR_ITEM_MAX_STRING) + item_ptr->value_ptr = va_arg(ap, char *); + else { + item_ptr->value_ptr = va_arg(ap, uint8_t *); + } + item_ptr++; + } + va_end(ap); +#ifdef USE_DEVICE_GROUPS_SEND + } +#endif + item_ptr->item = 0; + + + + + + if (device_group->message_length) { + int kept_item_count = 0; + + + + uint8_t * previous_message_ptr = message_ptr; + while (item = *previous_message_ptr++) { + + + if (item <= DGR_ITEM_MAX_32BIT) { + value = 1; + if (item > DGR_ITEM_MAX_8BIT) { + value = 2; + if (item > DGR_ITEM_MAX_16BIT) { + value = 4; + } + } + } + else { + value = *previous_message_ptr + 1; + } + + + for (item_ptr = item_array; item_ptr->item; item_ptr++) { + if (item_ptr->item == item) break; + } + + + if (!item_ptr->item) { + kept_item_count++; + *message_ptr++ = item; + memmove(message_ptr, previous_message_ptr, value); + message_ptr += value; + } + + + previous_message_ptr += value; + } +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: %u items carried over"), kept_item_count); +#endif + } + + + for (item_ptr = item_array; (item = item_ptr->item); item_ptr++) { + + + shared = true; + if (!device_group_index && message_type != DGR_MSGTYPE_UPDATE_COMMAND) shared = DeviceGroupItemShared(false, item); + if (shared) { + *message_ptr++ = item; + + + if (item <= DGR_ITEM_MAX_32BIT) { + value = item_ptr->value; + *message_ptr++ = value & 0xff; + if (item > DGR_ITEM_MAX_8BIT) { + value >>= 8; + *message_ptr++ = value & 0xff; + if (item > DGR_ITEM_MAX_16BIT) { + value >>= 8; + *message_ptr++ = value & 0xff; + value >>= 8; + + if (item == DGR_ITEM_POWER && !value) value = (device_group_index == 0 && first_device_group_is_local ? devices_present : 1); + *message_ptr++ = value; + } + } + } + + + else { + if (item <= DGR_ITEM_MAX_STRING) { + value = strlen((const char *)item_ptr->value_ptr) + 1; + } + else { + switch (item) { + case DGR_ITEM_LIGHT_CHANNELS: + value = 6; + break; + } + } + + + *message_ptr++ = value; + memcpy(message_ptr, item_ptr->value_ptr, value); + message_ptr += value; + } + } + } + + + if (message_ptr != first_item_ptr) { + *message_ptr++ = 0; + device_group->message_length = message_ptr - device_group->message; + } + + + if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return 0; + } + + + if (!device_group->message_length) { + device_group->outgoing_sequence = original_sequence; + return 0; + } + + + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, false); + +#ifdef USE_DEVICE_GROUPS_SEND + + if (with_local) { + struct XDRVMAILBOX save_XdrvMailbox = XdrvMailbox; + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, true); + XdrvMailbox = save_XdrvMailbox; + } +#endif + + uint32_t now = millis(); + if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { + device_group->message_length = 0; + device_group->next_ack_check_time = 0; + } + else { + device_group->ack_check_interval = 200; + device_group->next_ack_check_time = now + device_group->ack_check_interval; + if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; + device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT; + } + + device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL; + if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; + return 0; +} + +void ProcessDeviceGroupMessage(uint8_t * message, int message_length) +{ + + uint8_t device_group_index = 0; + struct device_group * device_group = device_groups; + char * message_group_name = (char *)message + sizeof(DEVICE_GROUP_MESSAGE) - 1; + for (;;) { + if (!strcmp(message_group_name, device_group->group_name)) break; + if (++device_group_index >= device_group_count) return; + device_group++; + } + + + struct device_group_member * device_group_member; + IPAddress remote_ip = device_groups_udp.remoteIP(); + struct device_group_member * * flink = &device_group->device_group_members; + for (;;) { + device_group_member = *flink; + if (!device_group_member) { + device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); + if (device_group_member == nullptr) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating member block")); + return; + } + device_group_member->ip_address = remote_ip; + *flink = device_group_member; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s added"), IPAddressToString(remote_ip)); + break; + } + else if (device_group_member->ip_address == remote_ip) { + break; + } + flink = &device_group_member->flink; + } + + SendReceiveDeviceGroupMessage(device_group, device_group_member, message, message_length, true); +} + +void DeviceGroupStatus(uint8_t device_group_index) +{ + if (Settings.flag4.device_groups_enabled && device_group_index < device_group_count) { + char buffer[1024]; + int member_count = 0; + struct device_group * device_group = &device_groups[device_group_index]; + buffer[0] = buffer[1] = 0; + for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member; device_group_member = device_group_member->flink) { + snprintf_P(buffer, sizeof(buffer), PSTR("%s,{\"IPAddress\":\"%s\",\"ResendCount\":%u,\"LastRcvdSeq\":%u,\"LastAckedSeq\":%u}"), buffer, IPAddressToString(device_group_member->ip_address), device_group_member->unicast_count, device_group_member->received_sequence, device_group_member->acked_sequence); + member_count++; + } + Response_P(PSTR("{\"" D_CMND_DEVGROUPSTATUS "\":{\"Index\":%u,\"GroupName\":\"%s\",\"MessageSeq\":%u,\"MemberCount\":%d,\"Members\":[%s]}}"), device_group_index, device_group->group_name, device_group->outgoing_sequence, member_count, &buffer[1]); + } +} + +void DeviceGroupsLoop(void) +{ + if (!device_groups_up || restart_flag) return; + + while (device_groups_udp.parsePacket()) { + uint8_t packet_buffer[512]; + int length = device_groups_udp.read(packet_buffer, sizeof(packet_buffer) - 1); + if (length > 0) { + packet_buffer[length] = 0; + if (!strncmp_P((char *)packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) { + ProcessDeviceGroupMessage(packet_buffer, length); + } + } + } + + uint32_t now = millis(); + + + if ((long)(now - next_check_time) >= 0) { +#ifdef DEVICE_GROUPS_DEBUG +AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next_check_time, now); +#endif + next_check_time = now + DGR_ANNOUNCEMENT_INTERVAL * 2; + + struct device_group * device_group = device_groups; + for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++, device_group++) { + + + if (device_group->next_ack_check_time) { + + + if ((long)(now - device_group->next_ack_check_time) >= 0) { + + + if (device_group->initial_status_requests_remaining) { + if (--device_group->initial_status_requests_remaining) { +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name); +#endif + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, false); + device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; + next_check_time = device_group->next_ack_check_time = now + 200; + continue; + } + + + + else { + _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); + } + } + + + else { +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Checking for ack's")); +#endif + bool acked = true; + struct device_group_member ** flink = &device_group->device_group_members; + struct device_group_member * device_group_member; + while ((device_group_member = *flink)) { + + + if (device_group_member->acked_sequence != device_group->outgoing_sequence) { + + + + if ((long)(now - device_group->member_timeout_time) >= 0) { + *flink = device_group_member->flink; + free(device_group_member); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s removed"), IPAddressToString(device_group_member->ip_address)); + continue; + } + + + SendReceiveDeviceGroupMessage(device_group, device_group_member, device_group->message, device_group->message_length, false); + device_group_member->unicast_count++; + acked = false; + } + flink = &device_group_member->flink; + } + + + + if (acked) { + device_group->next_ack_check_time = 0; + device_group->message_length = 0; + } + + + + + else { + device_group->ack_check_interval *= 2; + if (device_group->ack_check_interval > 5000) device_group->ack_check_interval = 5000; + device_group->next_ack_check_time = now + device_group->ack_check_interval; + } + } + } + + if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; + } + + + + + +#ifdef DEVICE_GROUPS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now); +#endif + if ((long)(now - device_group->next_announcement_time) >= 0) { + SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, false); + device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000); + } + if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; + } + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_eeprom.ino" +# 26 "/workspace/Tasmota/tasmota/support_eeprom.ino" +#ifdef USE_EEPROM + +#ifdef USE_24C256 + +#include +#define EEPROM_ADDRESS 0x50 +static Eeprom24C128_256 m_eeprom(EEPROM_ADDRESS); + +void eeprom_writeBytes(uint32_t addr, uint32_t len, uint8_t *buff) { + m_eeprom.writeBytes(addr,len,(uint8_t*)buff); +} +void eeprom_readBytes(uint32_t addr, uint32_t len, uint8_t *buff) { + m_eeprom.readBytes(addr,len,(uint8_t*)buff); +} + +uint32_t eeprom_init(uint32_t size) { + if (i2c_flg) { + if (I2cActive(EEPROM_ADDRESS) || I2cSetDevice(EEPROM_ADDRESS)) { + + I2cSetActiveFound(EEPROM_ADDRESS, "24C256"); + return 1; + } + } + return 0; +} + +#else + +#ifdef ESP32 + + +uint32_t eeprom_init(uint32_t size) { + return EEPROM.begin(size); +} +void eeprom_writeBytes(uint32_t addr, uint32_t len, uint8_t *buff) { + EEPROM.writeBytes(addr, buff, len); + EEPROM.commit(); +} +void eeprom_readBytes(uint32_t addr, uint32_t len, uint8_t *buff) { + EEPROM.readBytes(addr, buff, len); +} + +#else + +uint32_t eeprom_init(uint32_t size) { + EEPROM.begin(size); + return 1; +} +void eeprom_writeBytes(uint32_t adr, uint32_t len, uint8_t *buf) { + for (uint32_t cnt=0; cnt 100) { free_block_size -= 100; } + return free_block_size; +} + +void ESP_Restart(void) { + + ESP.reset(); +} + +#endif + + + + + +#ifdef ESP32 + + + +#include +#include + +void NvmLoad(const char *sNvsName, const char *sName, void *pSettings, unsigned nSettingsLen) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READONLY, &handle); + size_t size = nSettingsLen; + nvs_get_blob(handle, sName, pSettings, &size); + nvs_close(handle); + interrupts(); +} + +void NvmSave(const char *sNvsName, const char *sName, const void *pSettings, unsigned nSettingsLen) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READWRITE, &handle); + nvs_set_blob(handle, sName, pSettings, nSettingsLen); + nvs_commit(handle); + nvs_close(handle); + interrupts(); +} + +void NvmErase(const char *sNvsName) { + nvs_handle handle; + noInterrupts(); + nvs_open(sNvsName, NVS_READWRITE, &handle); + nvs_erase_all(handle); + nvs_commit(handle); + nvs_close(handle); + interrupts(); +} + +void SettingsErase(uint8_t type) { + if (1 == type) { + } else if (2 == type) { + } else if (3 == type) { + } + + NvmErase("main"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " t=%d"), type); +} + +void SettingsRead(void *data, size_t size) { + NvmLoad("main", "Settings", data, size); +} + +void SettingsWrite(const void *pSettings, unsigned nSettingsLen) { + NvmSave("main", "Settings", pSettings, nSettingsLen); +} + +void QPCRead(void *pSettings, unsigned nSettingsLen) { + NvmLoad("qpc", "pcreg", pSettings, nSettingsLen); +} + +void QPCWrite(const void *pSettings, unsigned nSettingsLen) { + NvmSave("qpc", "pcreg", pSettings, nSettingsLen); +} + +void ZigbeeErase(void) { + NvmErase("zb"); +} + +void ZigbeeRead(void *pSettings, unsigned nSettingsLen) { + NvmLoad("zb", "zigbee", pSettings, nSettingsLen); +} + +void ZigbeeWrite(const void *pSettings, unsigned nSettingsLen) { + NvmSave("zb", "zigbee", pSettings, nSettingsLen); +} + + + + +static bool bNetIsTimeSync = false; + +void SntpInit() { + bNetIsTimeSync = true; +} + +uint32_t SntpGetCurrentTimestamp(void) { + time_t now = 0; + if (bNetIsTimeSync || ntp_force_sync) + { + + + configTime(0, 0, SettingsText(SET_NTPSERVER1), SettingsText(SET_NTPSERVER2), SettingsText(SET_NTPSERVER3)); + bNetIsTimeSync = false; + ntp_force_sync = false; + } + time(&now); + return now; +} + + + + + +void CrashDump(void) { +} + +bool CrashFlag(void) { + return false; +} + +void CrashDumpClear(void) { +} + +void CmndCrash(void) { + + + + +} + + +void CmndWDT(void) { + + + + + + +} + +void CmndBlockedLoop(void) { + + + + + +} + + + + + +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + +void DisableBrownout(void) { + + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); +} + + + + + +String ESP32GetResetReason(uint32_t cpu_no) { + + switch (rtc_get_reset_reason( (RESET_REASON) cpu_no)) { + case POWERON_RESET : return F("Vbat power on reset"); + case SW_RESET : return F("Software reset digital core"); + case OWDT_RESET : return F("Legacy watch dog reset digital core"); + case DEEPSLEEP_RESET : return F("Deep Sleep reset digital core"); + case SDIO_RESET : return F("Reset by SLC module, reset digital core"); + case TG0WDT_SYS_RESET : return F("Timer Group0 Watch dog reset digital core"); + case TG1WDT_SYS_RESET : return F("Timer Group1 Watch dog reset digital core"); + case RTCWDT_SYS_RESET : return F("RTC Watch dog Reset digital core"); + case INTRUSION_RESET : return F("Instrusion tested to reset CPU"); + case TGWDT_CPU_RESET : return F("Time Group reset CPU"); + case SW_CPU_RESET : return F("Software reset CPU"); + case RTCWDT_CPU_RESET : return F("RTC Watch dog Reset CPU"); + case EXT_CPU_RESET : return F("or APP CPU, reseted by PRO CPU"); + case RTCWDT_BROWN_OUT_RESET : return F("Reset when the vdd voltage is not stable"); + case RTCWDT_RTC_RESET : return F("RTC Watch dog reset digital core and rtc module"); + default : return F("NO_MEAN"); + } +} + +String ESP_getResetReason(void) { + return ESP32GetResetReason(0); +} + +uint32_t ESP_ResetInfoReason(void) { + RESET_REASON reason = rtc_get_reset_reason(0); + if (POWERON_RESET == reason) { return REASON_DEFAULT_RST; } + if (SW_CPU_RESET == reason) { return REASON_SOFT_RESTART; } + if (DEEPSLEEP_RESET == reason) { return REASON_DEEP_SLEEP_AWAKE; } + if (SW_RESET == reason) { return REASON_EXT_SYS_RST; } +} + +uint32_t ESP_getChipId(void) { + uint32_t id = 0; + for (uint32_t i = 0; i < 17; i = i +8) { + id |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + return id; +} + +uint32_t ESP_getSketchSize(void) { + static uint32_t sketchsize = 0; + + if (!sketchsize) { + sketchsize = ESP.getSketchSize(); + } + return sketchsize; +} + +uint32_t ESP_getFreeHeap(void) { + + return ESP.getMaxAllocHeap(); +} + +uint32_t ESP_getMaxAllocHeap(void) { + + uint32_t free_block_size = ESP.getMaxAllocHeap(); + if (free_block_size > 100) { free_block_size -= 100; } + return free_block_size; +} + +void ESP_Restart(void) { + ESP.restart(); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_esptool.ino" +# 20 "/workspace/Tasmota/tasmota/support_esptool.ino" +#ifdef ESP8266 +#define USE_ESPTOOL +#endif + +#ifdef USE_ESPTOOL + + + + + + + +#define READ_REG(REG) (*((volatile uint32_t *)(REG))) +#define WRITE_REG(REG,VAL) *((volatile uint32_t *)(REG)) = (VAL) +#define REG_SET_MASK(reg,mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) + +#define SPI_BASE_REG 0x60000200 + +#define SPI_CMD_REG (SPI_BASE_REG + 0x00) +#define SPI_FLASH_RDSR (1<<27) +#define SPI_FLASH_SE (1<<24) +#define SPI_FLASH_BE (1<<23) +#define SPI_FLASH_WREN (1<<30) + +#define SPI_ADDR_REG (SPI_BASE_REG + 0x04) +#define SPI_CTRL_REG (SPI_BASE_REG + 0x08) +#define SPI_RD_STATUS_REG (SPI_BASE_REG + 0x10) +#define SPI_W0_REG (SPI_BASE_REG + 0x40) +#define SPI_EXT2_REG (SPI_BASE_REG + 0xF8) + +#define SPI_ST 0x7 + + +#define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / SPI_FLASH_SEC_SIZE) + + +static const uint32_t STATUS_WIP_BIT = (1 << 0); + + +inline static void spi_wait_ready(void) +{ + while((READ_REG(SPI_EXT2_REG) & SPI_ST)) { } +} + + + +static bool spiflash_is_ready(void) +{ + spi_wait_ready(); + WRITE_REG(SPI_RD_STATUS_REG, 0); + WRITE_REG(SPI_CMD_REG, SPI_FLASH_RDSR); + while(READ_REG(SPI_CMD_REG) != 0) { } + uint32_t status_value = READ_REG(SPI_RD_STATUS_REG); + return (status_value & STATUS_WIP_BIT) == 0; +} + +static void spi_write_enable(void) +{ + while(!spiflash_is_ready()) { } + WRITE_REG(SPI_CMD_REG, SPI_FLASH_WREN); + while(READ_REG(SPI_CMD_REG) != 0) { } +} + +bool EsptoolEraseSector(uint32_t sector) +{ + spi_write_enable(); + spi_wait_ready(); + + WRITE_REG(SPI_ADDR_REG, (sector * SPI_FLASH_SEC_SIZE) & 0xffffff); + WRITE_REG(SPI_CMD_REG, SPI_FLASH_SE); + while(READ_REG(SPI_CMD_REG) != 0) { } + while(!spiflash_is_ready()) { } + + return true; +} + +void EsptoolErase(uint32_t start_sector, uint32_t end_sector) +{ + int next_erase_sector = start_sector; + int remaining_erase_sector = end_sector - start_sector; + + while (remaining_erase_sector > 0) { + spi_write_enable(); + + uint32_t command = SPI_FLASH_SE; + uint32_t sectors_to_erase = 1; + if (remaining_erase_sector >= SECTORS_PER_BLOCK && + next_erase_sector % SECTORS_PER_BLOCK == 0) { + command = SPI_FLASH_BE; + sectors_to_erase = SECTORS_PER_BLOCK; + } + uint32_t addr = next_erase_sector * SPI_FLASH_SEC_SIZE; + + spi_wait_ready(); + WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); + WRITE_REG(SPI_CMD_REG, command); + while(READ_REG(SPI_CMD_REG) != 0) { } + remaining_erase_sector -= sectors_to_erase; + next_erase_sector += sectors_to_erase; + + while (!spiflash_is_ready()) { } + yield(); + OsWatchLoop(); + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_features.ino" +# 24 "/workspace/Tasmota/tasmota/support_features.ino" +void GetFeatures(void) +{ + feature_drv1 = 0x00000000; + +#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) + feature_drv1 |= 0x00000001; +#endif +#ifdef USE_LIGHT + feature_drv1 |= 0x00000002; +#endif +#ifdef USE_I2C + feature_drv1 |= 0x00000004; +#endif +#ifdef USE_SPI + feature_drv1 |= 0x00000008; +#endif +#ifdef USE_DISCOVERY + feature_drv1 |= 0x00000010; +#endif +#ifdef USE_ARDUINO_OTA + feature_drv1 |= 0x00000020; +#endif +#ifdef USE_MQTT_TLS + feature_drv1 |= 0x00000040; +#endif +#ifdef USE_WEBSERVER + feature_drv1 |= 0x00000080; +#endif +#if defined(USE_WEBSERVER) && defined(WEBSERVER_ADVERTISE) + feature_drv1 |= 0x00000100; +#endif +#if defined(USE_WEBSERVER) && defined(USE_EMULATION_HUE) + feature_drv1 |= 0x00000200; +#endif + + feature_drv1 |= 0x00000400; + + + + + + + +#if defined(USE_DISCOVERY) && defined(MQTT_HOST_DISCOVERY) + feature_drv1 |= 0x00002000; +#endif +#if defined(USE_LIGHT) && defined(USE_ARILUX_RF) + feature_drv1 |= 0x00004000; +#endif +#if defined(USE_LIGHT) && defined(USE_WS2812) + feature_drv1 |= 0x00008000; +#endif +#if defined(USE_LIGHT) && defined(USE_WS2812) && defined(USE_WS2812_DMA) + feature_drv1 |= 0x00010000; +#endif +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + feature_drv1 |= 0x00020000; +#endif +#ifdef USE_IR_HVAC + feature_drv1 |= 0x00040000; +#endif +#if defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE) + feature_drv1 |= 0x00080000; +#endif +#ifdef USE_DOMOTICZ + feature_drv1 |= 0x00100000; +#endif +#ifdef USE_DISPLAY + feature_drv1 |= 0x00200000; +#endif +#ifdef USE_HOME_ASSISTANT + feature_drv1 |= 0x00400000; +#endif +#ifdef USE_SERIAL_BRIDGE + feature_drv1 |= 0x00800000; +#endif +#ifdef USE_TIMERS + feature_drv1 |= 0x01000000; +#endif +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + feature_drv1 |= 0x02000000; +#endif +#if defined(USE_TIMERS) && defined(USE_TIMERS_WEB) + feature_drv1 |= 0x04000000; +#endif +#ifdef USE_RULES + feature_drv1 |= 0x08000000; +#endif +#ifdef USE_KNX + feature_drv1 |= 0x10000000; +#endif +#ifdef USE_WPS + feature_drv1 |= 0x20000000; +#endif +#ifdef USE_SMARTCONFIG + feature_drv1 |= 0x40000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_POWER_LIMIT) + feature_drv1 |= 0x80000000; +#endif + + + + feature_drv2 = 0x00000000; + +#ifdef USE_CONFIG_OVERRIDE + feature_drv2 |= 0x00000001; +#endif +#ifdef FIRMWARE_MINIMAL + feature_drv2 |= 0x00000002; +#endif +#ifdef FIRMWARE_SENSORS + feature_drv2 |= 0x00000004; +#endif +#ifdef FIRMWARE_CLASSIC + feature_drv2 |= 0x00000008; +#endif +#ifdef FIRMWARE_KNX_NO_EMULATION + feature_drv2 |= 0x00000010; +#endif +#if defined(USE_DISPLAY) && defined(USE_DISPLAY_MODES1TO5) + feature_drv2 |= 0x00000020; +#endif +#if defined(USE_DISPLAY) && defined(USE_DISPLAY_GRAPH) + feature_drv2 |= 0x00000040; +#endif +#if defined(USE_I2C) && defined(USE_DISPLAY) && defined(USE_DISPLAY_LCD) + feature_drv2 |= 0x00000080; +#endif +#if defined(USE_I2C) && defined(USE_DISPLAY) && defined(USE_DISPLAY_SSD1306) + feature_drv2 |= 0x00000100; +#endif +#if defined(USE_I2C) && defined(USE_DISPLAY) && defined(USE_DISPLAY_MATRIX) + feature_drv2 |= 0x00000200; +#endif +#if defined(USE_SPI) && defined(USE_DISPLAY) && defined(USE_DISPLAY_ILI9341) + feature_drv2 |= 0x00000400; +#endif +#if defined(USE_SPI) && defined(USE_DISPLAY) && defined(USE_DISPLAY_EPAPER_29) + feature_drv2 |= 0x00000800; +#endif +#if defined(USE_I2C) && defined(USE_DISPLAY) && defined(USE_DISPLAY_SH1106) + feature_drv2 |= 0x00001000; +#endif +#ifdef USE_MP3_PLAYER + feature_drv2 |= 0x00002000; +#endif +#if defined(USE_I2C) && defined(USE_PCA9685) + feature_drv2 |= 0x00004000; +#endif +#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) + feature_drv2 |= 0x00008000; +#endif +#ifdef USE_RC_SWITCH + feature_drv2 |= 0x00010000; +#endif +#if defined(USE_LIGHT) && defined(USE_ARMTRONIX_DIMMERS) + feature_drv2 |= 0x00020000; +#endif +#if defined(USE_LIGHT) && defined(USE_SM16716) + feature_drv2 |= 0x00040000; +#endif +#ifdef USE_SCRIPT + feature_drv2 |= 0x00080000; +#endif +#if defined(USE_WEBSERVER) && defined(USE_EMULATION_WEMO) + feature_drv2 |= 0x00100000; +#endif +#ifdef USE_SONOFF_IFAN + feature_drv2 |= 0x00200000; +#endif +#ifdef USE_ZIGBEE + feature_drv2 |= 0x00400000; +#endif +#ifdef NO_EXTRA_4K_HEAP + feature_drv2 |= 0x00800000; +#endif +#ifdef VTABLES_IN_IRAM + feature_drv2 |= 0x01000000; +#endif +#ifdef VTABLES_IN_DRAM + feature_drv2 |= 0x02000000; +#endif +#ifdef VTABLES_IN_FLASH + feature_drv2 |= 0x04000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + feature_drv2 |= 0x08000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY + feature_drv2 |= 0x10000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + feature_drv2 |= 0x20000000; +#endif +#ifdef DEBUG_THEO + feature_drv2 |= 0x40000000; +#endif +#ifdef USE_DEBUG_DRIVER + feature_drv2 |= 0x80000000; +#endif + + + + feature_sns1 = 0x00000000; + +#ifdef USE_COUNTER + feature_sns1 |= 0x00000001; +#endif +#if defined(USE_ADC_VCC) || defined(USE_ADC) + feature_sns1 |= 0x00000002; +#endif +#ifdef USE_ENERGY_SENSOR + feature_sns1 |= 0x00000004; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_PZEM004T) + feature_sns1 |= 0x00000008; +#endif +#ifdef USE_DS18B20 + feature_sns1 |= 0x00000010; +#endif +#ifdef USE_DS18x20_LEGACY + feature_sns1 |= 0x00000020; +#endif +#ifdef USE_DS18x20 + feature_sns1 |= 0x00000040; +#endif +#ifdef USE_DHT + feature_sns1 |= 0x00000080; +#endif +#if defined(USE_I2C) && defined(USE_SHT) + feature_sns1 |= 0x00000100; +#endif +#if defined(USE_I2C) && defined(USE_HTU) + feature_sns1 |= 0x00000200; +#endif +#if defined(USE_I2C) && defined(USE_BMP) + feature_sns1 |= 0x00000400; +#endif +#if defined(USE_I2C) && defined(USE_BMP) && defined(USE_BME680) + feature_sns1 |= 0x00000800; +#endif +#if defined(USE_I2C) && defined(USE_BH1750) + feature_sns1 |= 0x00001000; +#endif +#if defined(USE_I2C) && defined(USE_VEML6070) + feature_sns1 |= 0x00002000; +#endif +#if defined(USE_I2C) && defined(USE_ADS1115_I2CDEV) + feature_sns1 |= 0x00004000; +#endif +#if defined(USE_I2C) && defined(USE_ADS1115) + feature_sns1 |= 0x00008000; +#endif +#if defined(USE_I2C) && defined(USE_INA219) + feature_sns1 |= 0x00010000; +#endif +#if defined(USE_I2C) && defined(USE_SHT3X) + feature_sns1 |= 0x00020000; +#endif +#ifdef USE_MHZ19 + feature_sns1 |= 0x00040000; +#endif +#if defined(USE_I2C) && defined(USE_TSL2561) + feature_sns1 |= 0x00080000; +#endif +#ifdef USE_SENSEAIR + feature_sns1 |= 0x00100000; +#endif +#ifdef USE_PMS5003 + feature_sns1 |= 0x00200000; +#endif +#if defined(USE_I2C) && defined(USE_MGS) + feature_sns1 |= 0x00400000; +#endif +#ifdef USE_NOVA_SDS + feature_sns1 |= 0x00800000; +#endif +#if defined(USE_I2C) && defined(USE_SGP30) + feature_sns1 |= 0x01000000; +#endif +#ifdef USE_SR04 + feature_sns1 |= 0x02000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_SDM120) + feature_sns1 |= 0x04000000; +#endif +#if defined(USE_I2C) && defined(USE_SI1145) + feature_sns1 |= 0x08000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_SDM630) + feature_sns1 |= 0x10000000; +#endif +#if defined(USE_I2C) && defined(USE_LM75AD) + feature_sns1 |= 0x20000000; +#endif +#if defined(USE_I2C) && defined(USE_APDS9960) + feature_sns1 |= 0x40000000; +#endif +#ifdef USE_TM1638 + feature_sns1 |= 0x80000000; +#endif + + + feature_sns2 = 0x00000000; + +#if defined(USE_I2C) && defined(USE_MCP230xx) + feature_sns2 |= 0x00000001; +#endif +#if defined(USE_I2C) && defined(USE_MPR121) + feature_sns2 |= 0x00000002; +#endif +#if defined(USE_I2C) && defined(USE_CCS811) + feature_sns2 |= 0x00000004; +#endif +#if defined(USE_I2C) && defined(USE_MPU6050) + feature_sns2 |= 0x00000008; +#endif +#if defined(USE_I2C) && defined(USE_MCP230xx) && defined(USE_MCP230xx_OUTPUT) + feature_sns2 |= 0x00000010; +#endif +#if defined(USE_I2C) && defined(USE_MCP230xx) && defined(USE_MCP230xx_DISPLAYOUTPUT) + feature_sns2 |= 0x00000020; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_HLW8012) + feature_sns2 |= 0x00000040; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_CSE7766) + feature_sns2 |= 0x00000080; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_MCP39F501) + feature_sns2 |= 0x00000100; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_PZEM_AC) + feature_sns2 |= 0x00000200; +#endif +#if defined(USE_I2C) && defined(USE_DS3231) + feature_sns2 |= 0x00000400; +#endif +#ifdef USE_HX711 + feature_sns2 |= 0x00000800; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_PZEM_DC) + feature_sns2 |= 0x00001000; +#endif +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) + feature_sns2 |= 0x00002000; +#endif +#if defined(USE_I2C) && defined(USE_MGC3130) + feature_sns2 |= 0x00004000; +#endif +#ifdef USE_RF_SENSOR + feature_sns2 |= 0x00008000; +#endif +#if defined(USE_RF_SENSOR) && defined(USE_THEO_V2) + feature_sns2 |= 0x00010000; +#endif +#if defined(USE_RF_SENSOR) && defined(USE_ALECTO_V2) + feature_sns2 |= 0x00020000; +#endif +#ifdef USE_AZ7798 + feature_sns2 |= 0x00040000; +#endif +#ifdef USE_MAX31855 + feature_sns2 |= 0x00080000; +#endif +#ifdef USE_PN532_HSU + feature_sns2 |= 0x00100000; +#endif +#if defined(USE_I2C) && defined(USE_MAX44009) + feature_sns2 |= 0x00200000; +#endif +#if defined(USE_I2C) && defined(USE_SCD30) + feature_sns2 |= 0x00400000; +#endif +#ifdef USE_HRE + feature_sns2 |= 0x00800000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_ADE7953) + feature_sns2 |= 0x01000000; +#endif +#if defined(USE_I2C) && defined(USE_SPS30) + feature_sns2 |= 0x02000000; +#endif +#if defined(USE_I2C) && defined(USE_VL53L0X) + feature_sns2 |= 0x04000000; +#endif +#if defined(USE_I2C) && defined(USE_MLX90614) + feature_sns2 |= 0x08000000; +#endif +#ifdef USE_MAX31865 + feature_sns2 |= 0x10000000; +#endif +#if defined(USE_I2C) && defined(USE_CHIRP) + feature_sns2 |= 0x20000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_SOLAX_X1) + feature_sns2 |= 0x40000000; +#endif +#if defined(USE_I2C) && defined(USE_PAJ7620) + feature_sns2 |= 0x80000000; +#endif + + + + feature5 = 0x00000000; + +#ifdef USE_BUZZER + feature5 |= 0x00000001; +#endif +#ifdef USE_RDM6300 + feature5 |= 0x00000002; +#endif +#ifdef USE_IBEACON + feature5 |= 0x00000004; +#endif +#ifdef USE_SML_M + feature5 |= 0x00000008; +#endif +#if defined(USE_I2C) && defined(USE_INA226) + feature5 |= 0x00000010; +#endif +#ifdef USE_A4988_STEPPER + feature5 |= 0x00000020; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_DDS2382) + feature5 |= 0x00000040; +#endif +#if defined(USE_LIGHT) && defined(USE_SM2135) + feature5 |= 0x00000080; +#endif +#ifdef USE_SHUTTER + feature5 |= 0x00000100; +#endif +#if defined(USE_I2C) && defined(USE_PCF8574) + feature5 |= 0x00000200; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_DDSU666) + feature5 |= 0x00000400; +#endif +#ifdef USE_DEEPSLEEP + feature5 |= 0x00000800; +#endif +#ifdef USE_SONOFF_SC + feature5 |= 0x00001000; +#endif +#ifdef USE_SONOFF_RF + feature5 |= 0x00002000; +#endif +#if defined(USE_LIGHT) && defined(USE_SONOFF_L1) + feature5 |= 0x00004000; +#endif +#if defined(USE_LIGHT) && defined(USE_EXS_DIMMER) + feature5 |= 0x00008000; +#endif +#ifdef USE_TASMOTA_CLIENT + feature5 |= 0x00010000; +#endif +#if defined(USE_I2C) && defined(USE_HIH6) + feature5 |= 0x00020000; +#endif +#ifdef USE_HPMA + feature5 |= 0x00040000; +#endif +#if defined(USE_I2C) && defined(USE_TSL2591) + feature5 |= 0x00080000; +#endif +#if defined(USE_I2C) && defined(USE_DHT12) + feature5 |= 0x00100000; +#endif +#if defined(USE_I2C) && defined(USE_DS1624) + feature5 |= 0x00200000; +#endif +#ifdef USE_GPS + feature5 |= 0x00400000; +#endif +#if defined(USE_I2C) && defined(USE_HOTPLUG) + feature5 |= 0x00800000; +#endif +#ifdef USE_NRF24 + feature5 |= 0x01000000; +#endif +#ifdef USE_MIBLE + feature5 |= 0x02000000; +#endif +#ifdef USE_HM10 + feature5 |= 0x04000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_LE01MR) + feature5 |= 0x08000000; +#endif +#if defined(USE_I2C) && defined(USE_AHT1x) + feature5 |= 0x10000000; +#endif +#if defined(USE_I2C) && defined(USE_WEMOS_MOTOR_V1) + feature5 |= 0x20000000; +#endif +#ifdef USE_DEVICE_GROUPS + feature5 |= 0x40000000; +#endif +#ifdef USE_PWM_DIMMER + feature5 |= 0x80000000; +#endif + + + + feature6 = 0x00000000; + +#ifdef USE_KEELOQ + feature6 |= 0x00000001; +#endif +#ifdef USE_HRXL + feature6 |= 0x00000002; +#endif +#ifdef USE_SONOFF_D1 + feature6 |= 0x00000004; +#endif +#if defined(USE_I2C) && defined(USE_HDC1080) + feature6 |= 0x00000008; +#endif +#if defined(USE_I2C) && defined(USE_IAQ) + feature6 |= 0x00000010; +#endif +#if defined(USE_DISPLAY) && defined(USE_DISPLAY_SEVENSEG) + feature6 |= 0x00000020; +#endif +#if defined(USE_I2C) && defined(USE_AS3935) + feature6 |= 0x00000040; +#endif +#ifdef USE_PING + feature6 |= 0x00000080; +#endif +#ifdef USE_WINDMETER + feature6 |= 0x00000100; +#endif +#ifdef USE_OPENTHERM + feature6 |= 0x00000200; +#endif +#ifdef USE_THERMOSTAT + feature6 |= 0x00000400; +#endif +#if defined(USE_I2C) && defined(USE_VEML6075) + feature6 |= 0x00000800; +#endif +#if defined(USE_I2C) && defined(USE_VEML7700) + feature6 |= 0x00001000; +#endif +#if defined(USE_I2C) && defined(USE_MCP9808) + feature6 |= 0x00002000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_BL0940) + feature6 |= 0x00004000; +#endif +#ifdef USE_TELEGRAM + feature6 |= 0x00008000; +#endif +#if defined(USE_I2C) && defined(USE_HP303B) + feature6 |= 0x00010000; +#endif +#ifdef USE_TCP_BRIDGE + feature6 |= 0x00020000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_TELEINFO) + feature6 |= 0x00040000; +#endif +#ifdef USE_LMT01 + feature6 |= 0x00080000; +#endif +#ifdef USE_PROMETHEUS + feature6 |= 0x00100000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_IEM3000) + feature6 |= 0x00200000; +#endif +#ifdef USE_DYP + feature6 |= 0x00400000; +#endif +#ifdef USE_I2S_AUDIO + feature6 |= 0x00800000; +#endif +#ifdef USE_MLX90640 + feature6 |= 0x01000000; +#endif +#if defined(USE_I2C) && defined(USE_VL53L1X) + feature6 |= 0x02000000; +#endif +#ifdef USE_MIEL_HVAC + feature6 |= 0x04000000; +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_WE517) + feature6 |= 0x08000000; +#endif +#if defined(USE_I2C) && defined(USE_EZOPH) + feature6 |= 0x10000000; +#endif +#if defined(ESP32) && defined(USE_TTGO_WATCH) + feature6 |= 0x20000000; +#endif +#if defined(ESP32) && defined(USE_ETHERNET) + feature6 |= 0x40000000; +#endif +#if defined(ESP32) && defined(USE_WEBCAM) + feature6 |= 0x80000000; +#endif + + + + feature7 = 0x00000000; + +#if defined(USE_I2C) && defined(USE_EZOORP) + feature7 |= 0x00000001; +#endif +#if defined(USE_I2C) && defined(USE_EZORTD) + feature7 |= 0x00000002; +#endif +#if defined(USE_I2C) && defined(USE_EZOHUM) + feature7 |= 0x00000004; +#endif +#if defined(USE_I2C) && defined(USE_EZOEC) + feature7 |= 0x00000008; +#endif +#if defined(USE_I2C) && defined(USE_EZOCO2) + feature7 |= 0x00000010; +#endif +# 683 "/workspace/Tasmota/tasmota/support_features.ino" +} +# 1 "/workspace/Tasmota/tasmota/support_flash_log.ino" +# 39 "/workspace/Tasmota/tasmota/support_flash_log.ino" +#ifdef USE_FLOG +#ifdef ESP8266 + +class FLOG + +#define MAGIC_WORD_FL 0x464c + +{ + +struct header_t{ + uint16_t magic_word; + uint16_t padding; + uint32_t physical_start_sector:10; + uint32_t number:10; + uint32_t buf_pointer:12; + }; + +private: +void _readSector(uint8_t one_sector); +void _eraseSector(uint8_t one_sector); +void _writeSector(uint8_t one_sector); +void _clearBuffer(void); +void _searchSaves(void); +void _findFirstErasedSector(void); +void _showBuffer(void); +void _initBuffer(void); +void _saveBufferToSector(void); +header_t _saved_header; + +public: + uint32_t size; + uint32_t start; + uint32_t end; + uint16_t num_sectors; + + uint16_t first_erased_sector; + uint16_t current_sector; + + uint16_t bytes_left; + uint16_t sectors_left; + + uint8_t mode = 0; + bool found_saved_data = false; + bool ready = false; + bool running_download = false; + bool recording = false; + + union sector_t{ + uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; + uint8_t byte_buffer[FLASH_SECTOR_SIZE]; + header_t header; + } sector; + + void init(void); + void addToBuffer(uint8_t src[], uint32_t size); + void startRecording(bool append); + void stopRecording(void); + + typedef void (*CallbackNoArgs) (); + typedef void (*CallbackWithArgs) (uint8_t *_record); + + void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); +}; + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _FS_start; + + + + +void FLOG::init(void) +{ +DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); +size = ESP.getSketchSize(); + +start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); +end = (uint32_t)&_FS_start - 0x40200000; +num_sectors = (end - start)/FLASH_SECTOR_SIZE; +DEBUG_SENSOR_LOG(PSTR("FLOG: size: 0x%lx, start: 0x%lx, end: 0x%lx, num_sectors(dec): %lu"), size, start, end, num_sectors ); +_findFirstErasedSector(); +if(first_erased_sector == 0xffff){ + _eraseSector(0); + first_erased_sector = 0; +} +_searchSaves(); +_initBuffer(); +ready = true; +} +# 139 "/workspace/Tasmota/tasmota/support_flash_log.ino" +void FLOG::_readSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: read sector number: %u" ), one_sector); + ESP.flashRead(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); +} + + + + + +void FLOG::_eraseSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); + ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); +} + + + + + +void FLOG::_writeSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to sector number: %u" ), one_sector); + ESP.flashWrite(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); +} + + + + +void FLOG::_clearBuffer(){ + for (uint32_t i = sizeof(sector.header)/4; i<(sizeof(sector.dword_buffer)/4); i++){ + sector.dword_buffer[i] = 0; + } + sector.header.buf_pointer = sizeof(sector.header); + +} + + + + +void FLOG::_saveBufferToSector(){ + DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); + _writeSector(current_sector); + if(current_sector == num_sectors){ + current_sector = 0; + } + else{ + current_sector++; + } + _eraseSector(current_sector); + _clearBuffer(); + sector.header.number++; + DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); +} + + + + + +void FLOG::_findFirstErasedSector(){ + for (uint32_t i = 0; i3){ + break; + } + } +} + + + + + + + +void FLOG::addToBuffer(uint8_t src[], uint32_t size){ + if(mode == 0){ + if(sector.header.number == num_sectors && !ready){ + return; + } + } + if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ + + + + memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); + sector.header.buf_pointer+=size; + + } + else{ + DEBUG_SENSOR_LOG(PSTR("FLOG: save buffer to flash sector: %u"), current_sector); + DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); + _saveBufferToSector(); + sectors_left++; + + if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ + memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); + sector.header.buf_pointer+=size; + } + } +} + + + + + + +void FLOG::startRecording(bool append){ + if(recording){ + DEBUG_SENSOR_LOG(PSTR("FLOG: already recording")); + return; + } + recording = true; + DEBUG_SENSOR_LOG(PSTR("FLOG: start recording")); + _initBuffer(); + if(!found_saved_data) { + append = false; + } + if(append){ + sector.header.number = _saved_header.number+1; + sector.header.physical_start_sector = _saved_header.physical_start_sector; + } + else{ + sector.header.physical_start_sector = (uint16_t)first_erased_sector; + found_saved_data = false; + sectors_left = 0; + } + } + + + + + +void FLOG::stopRecording(void){ + _saveBufferToSector(); + _findFirstErasedSector(); + _searchSaves(); + _initBuffer(); + recording = false; + found_saved_data = true; + } +# 378 "/workspace/Tasmota/tasmota/support_flash_log.ino" + void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){ + + _readSector(sector.header.physical_start_sector); + uint32_t next_sector = sector.header.physical_start_sector; + bytes_left = sector.header.buf_pointer - sizeof(sector.header); + DEBUG_SENSOR_LOG(PSTR("FLOG: create file for download, will process %u bytes"), bytes_left); + running_download = true; + + sendHeader(); + + while(sectors_left){ + DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); + + uint32_t k = sizeof(sector.header); + while(bytes_left){ + + uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; + + sendRecord(_record_start); + if(k%128 == 0){ + + OsWatchLoop(); + delay(ssleep); + } + k+=size; + if(bytes_left>7){ + bytes_left-=size; + } + else{ + bytes_left = 0; + DEBUG_SENSOR_LOG(PSTR("FLOG: Flog->bytes_left not dividable by 8 ??????")); + } + } + next_sector++; + if(next_sector>num_sectors){ + next_sector = 0; + } + sectors_left--; + _readSector(next_sector); + bytes_left = sector.header.buf_pointer - sizeof(sector.header); + OsWatchLoop(); + delay(ssleep); + } + running_download = false; + + sendFooter(); + + _searchSaves(); + _initBuffer(); + } + + #endif + #endif +# 1 "/workspace/Tasmota/tasmota/support_float.ino" +# 20 "/workspace/Tasmota/tasmota/support_float.ino" +float fmodf(float x, float y) +{ + + union {float f; uint32_t i;} ux = {x}, uy = {y}; + int ex = ux.i>>23 & 0xff; + int ey = uy.i>>23 & 0xff; + uint32_t sx = ux.i & 0x80000000; + uint32_t i; + uint32_t uxi = ux.i; + + if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) + return (x*y)/(x*y); + if (uxi<<1 <= uy.i<<1) { + if (uxi<<1 == uy.i<<1) + return 0*x; + return x; + } + + + if (!ex) { + for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); + uxi <<= -ex + 1; + } else { + uxi &= -1U >> 9; + uxi |= 1U << 23; + } + if (!ey) { + for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); + uy.i <<= -ey + 1; + } else { + uy.i &= -1U >> 9; + uy.i |= 1U << 23; + } + + + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + for (; uxi>>23 == 0; uxi <<= 1, ex--); + + + if (ex > 0) { + uxi -= 1U << 23; + uxi |= (uint32_t)ex << 23; + } else { + uxi >>= -ex + 1; + } + uxi |= sx; + ux.i = uxi; + return ux.f; +} + +double FastPrecisePow(double a, double b) +{ + + + int e = abs((int)b); + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + + + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + return r * u.d; +} + +float FastPrecisePowf(const float x, const float y) +{ + + return (float)FastPrecisePow(x, y); +} + +double TaylorLog(double x) +{ + + + if (x <= 0.0) { return NAN; } + if (x == 1.0) { return 0; } + double z = (x + 1) / (x - 1); + double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); + double totalValue = 0; + double powe = 1; + for (uint32_t count = 0; count < 10; count++) { + z *= step; + double y = (1 / powe) * z; + totalValue = totalValue + y; + powe = powe + 2; + } + totalValue *= 2; +# 141 "/workspace/Tasmota/tasmota/support_float.ino" + return totalValue; +} +# 151 "/workspace/Tasmota/tasmota/support_float.ino" +inline float sinf(float x) { return sin_52(x); } +inline float cosf(float x) { return cos_52(x); } +inline float tanf(float x) { return tan_56(x); } +inline float atanf(float x) { return atan_66(x); } +inline float asinf(float x) { return asinf1(x); } +inline float acosf(float x) { return acosf1(x); } +inline float sqrtf(float x) { return sqrt1(x); } +inline float powf(float x, float y) { return FastPrecisePow(x, y); } + + +double const f_pi = 3.1415926535897932384626433; +double const f_twopi = 2.0 * f_pi; +double const f_two_over_pi = 2.0 / f_pi; +double const f_halfpi = f_pi / 2.0; +double const f_threehalfpi = 3.0 * f_pi / 2.0; +double const f_four_over_pi = 4.0 / f_pi; +double const f_qtrpi = f_pi / 4.0; +double const f_sixthpi = f_pi / 6.0; +double const f_tansixthpi = tan(f_sixthpi); +double const f_twelfthpi = f_pi / 12.0; +double const f_tantwelfthpi = tan(f_twelfthpi); +float const f_180pi = 180 / f_pi; +# 191 "/workspace/Tasmota/tasmota/support_float.ino" +float cos_52s(float x) +{ + const float c1 = 0.9999932946; + const float c2 = -0.4999124376; + const float c3 = 0.0414877472; + const float c4 = -0.0012712095; + + float x2 = x * x; + return (c1 + x2 * (c2 + x2 * (c3 + c4 * x2))); +} + + + + + + +float cos_52(float x) +{ + x = fmodf(x, f_twopi); + if (x < 0) { x = -x; } + int quad = int(x * (float)f_two_over_pi); + switch (quad) { + case 0: return cos_52s(x); + case 1: return -cos_52s((float)f_pi - x); + case 2: return -cos_52s(x-(float)f_pi); + case 3: return cos_52s((float)f_twopi - x); + } + return 0.0; +} + + + + +float sin_52(float x) +{ + return cos_52((float)f_halfpi - x); +} +# 245 "/workspace/Tasmota/tasmota/support_float.ino" +float tan_56s(float x) +{ + const float c1 = -3.16783027; + const float c2 = 0.134516124; + const float c3 = -4.033321984; + + float x2 = x * x; + return (x * (c1 + c2 * x2) / (c3 + x2)); +} +# 265 "/workspace/Tasmota/tasmota/support_float.ino" +float tan_56(float x) +{ + x = fmodf(x, (float)f_twopi); + int octant = int(x * (float)f_four_over_pi); + switch (octant){ + case 0: return tan_56s(x * (float)f_four_over_pi); + case 1: return 1.0f / tan_56s(((float)f_halfpi - x) * (float)f_four_over_pi); + case 2: return -1.0f / tan_56s((x-(float)f_halfpi) * (float)f_four_over_pi); + case 3: return - tan_56s(((float)f_pi - x) * (float)f_four_over_pi); + case 4: return tan_56s((x-(float)f_pi) * (float)f_four_over_pi); + case 5: return 1.0f / tan_56s(((float)f_threehalfpi - x) * (float)f_four_over_pi); + case 6: return -1.0f / tan_56s((x-(float)f_threehalfpi) * (float)f_four_over_pi); + case 7: return - tan_56s(((float)f_twopi - x) * (float)f_four_over_pi); + } + return 0.0; +} +# 295 "/workspace/Tasmota/tasmota/support_float.ino" +float atan_66s(float x) +{ + const float c1 = 1.6867629106; + const float c2 = 0.4378497304; + const float c3 = 1.6867633134; + + float x2 = x * x; + return (x * (c1 + x2 * c2) / (c3 + x2)); +} + + + + + +float atan_66(float x) +{ + float y; + bool complement= false; + bool region= false; + bool sign= false; + + if (x < 0) { + x = -x; + sign = true; + } + if (x > 1.0) { + x = 1.0 / x; + complement = true; + } + if (x > (float)f_tantwelfthpi) { + x = (x - (float)f_tansixthpi) / (1 + (float)f_tansixthpi * x); + region = true; + } + + y = atan_66s(x); + if (region) { y += (float)f_sixthpi; } + if (complement) { y = (float)f_halfpi-y; } + if (sign) { y = -y; } + return (y); +} + +float asinf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + return 2 * atan_66(x / (1 + sqrt1(d))); +} + +float acosf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + float y = asinf1(sqrt1(d)); + if (x >= 0.0f) { + return y; + } else { + return (float)f_pi - y; + } +} + + +float sqrt1(const float x) +{ + union { + int i; + float x; + } u; + u.x = x; + u.i = (1 << 29) + (u.i >> 1) - (1 << 22); + + + + + u.x = u.x + x / u.x; + u.x = 0.25f * u.x + x / u.x; + + return u.x; +} +# 386 "/workspace/Tasmota/tasmota/support_float.ino" +uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, + uint16_t ito_min, uint16_t ito_max) { + + if (ifrom_min >= ifrom_max) { + if (ito_min > ito_max) { + return ito_max; + } else { + return ito_min; + } + } + + uint32_t num = inum; + uint32_t from_min = ifrom_min; + uint32_t from_max = ifrom_max; + uint32_t to_min = ito_min; + uint32_t to_max = ito_max; + + + num = (num > from_max ? from_max : (num < from_min ? from_min : num)); + + + if (to_min > to_max) { + + num = (from_max - num) + from_min; + to_min = ito_max; + to_max = ito_min; + } + + uint32_t numerator = (num - from_min) * (to_max - to_min); + uint32_t result; + if (numerator >= 0x80000000L) { + + result = numerator / (from_max - from_min) + to_min; + } else { + result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; + } + return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); +} + + +float ModulusRangef(float f, float a, float b) { + if (b <= a) { return a; } + float range = b - a; + float x = f - a; + x = fmodf(x, range); + if (x < 0.0f) { x += range; } + return x + a; +} + + + + + + +float Polynomialf(const float *factors, uint32_t degree, float x) { + float r = 0.0f; + for (uint32_t i = degree - 1; i >= 0; i--) { + r = r * x + factors[i]; + } + return r; +} +# 1 "/workspace/Tasmota/tasmota/support_jpeg.ino" +# 21 "/workspace/Tasmota/tasmota/support_jpeg.ino" +#ifdef ESP32 +#ifdef JPEG_PICTS + +#include "img_converters.h" +#include "esp_jpg_decode.h" + +void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len) { +uint8_t red, grn, blu; +uint16_t b , g, r; + + for (uint32_t cnt=0; cnt> 3) & 0x1f; + g = ((grn >> 2) & 0x3f) << 5; + r = ((red >> 3) & 0x1f) << 11; + *out++ = (r | g | b); + } +} + +typedef struct { + uint16_t width; + uint16_t height; + uint16_t data_offset; + const uint8_t *input; + uint8_t *output; +} rgb_jpg_decoder; + + +static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len) +{ + rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; + if(buf) { + memcpy(buf, jpeg->input + index, len); + } + return len; +} + + +static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data) +{ + rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; + if(!data){ + if(x == 0 && y == 0){ + + jpeg->width = w; + jpeg->height = h; + + if(!jpeg->output){ + jpeg->output = (uint8_t *)malloc((w*h*3)+jpeg->data_offset); + if(!jpeg->output){ + return false; + } + } + } else { + + } + return true; + } + + size_t jw = jpeg->width*3; + size_t t = y * jw; + size_t b = t + (h * jw); + size_t l = x * 3; + uint8_t *out = jpeg->output+jpeg->data_offset; + uint8_t *o = out; + size_t iy, ix; + + w = w * 3; + + for(iy=t; iy= data_size) return false; + if(data[i] != 0xFF) return false; + if(data[i+1] == 0xC0) { + + *height = data[i+5]*256 + data[i+6]; + *width = data[i+7]*256 + data[i+8]; + return true; + } + else + { + i+=2; + block_length = data[i] * 256 + data[i+1]; + } + } + return false; + }else{ return false; } + + }else{ return false; } +} + + +#endif +#endif + +#ifdef USE_DISPLAY_DUMP +#define bytesPerPixel 3 +#define fileHeaderSize 14 +#define infoHeaderSize 40 + +void createBitmapFileHeader(uint32_t height, uint32_t width, uint8_t *fileHeader) { + int paddingSize = (4 - (width*bytesPerPixel) % 4) % 4; + + int fileSize = fileHeaderSize + infoHeaderSize + (bytesPerPixel*width+paddingSize) * height; + memset(fileHeader,0,fileHeaderSize); + fileHeader[ 0] = (unsigned char)('B'); + fileHeader[ 1] = (unsigned char)('M'); + fileHeader[ 2] = (unsigned char)(fileSize ); + fileHeader[ 3] = (unsigned char)(fileSize>> 8); + fileHeader[ 4] = (unsigned char)(fileSize>>16); + fileHeader[ 5] = (unsigned char)(fileSize>>24); + fileHeader[10] = (unsigned char)(fileHeaderSize + infoHeaderSize); + +} + +void createBitmapInfoHeader(uint32_t height, uint32_t width, uint8_t *infoHeader ) { + memset(infoHeader,0,infoHeaderSize); + + infoHeader[ 0] = (unsigned char)(infoHeaderSize); + infoHeader[ 4] = (unsigned char)(width ); + infoHeader[ 5] = (unsigned char)(width>> 8); + infoHeader[ 6] = (unsigned char)(width>>16); + infoHeader[ 7] = (unsigned char)(width>>24); + infoHeader[ 8] = (unsigned char)(height ); + infoHeader[ 9] = (unsigned char)(height>> 8); + infoHeader[10] = (unsigned char)(height>>16); + infoHeader[11] = (unsigned char)(height>>24); + infoHeader[12] = (unsigned char)(1); + infoHeader[14] = (unsigned char)(bytesPerPixel*8); + infoHeader[24] = (unsigned char)0x13; + infoHeader[25] = (unsigned char)0x0b; + infoHeader[28] = (unsigned char)0x13; + infoHeader[29] = (unsigned char)0x0b; + +} +#endif +# 1 "/workspace/Tasmota/tasmota/support_light_list.ino" +# 25 "/workspace/Tasmota/tasmota/support_light_list.ino" +template +class LList; + +template +class LList_elt { +public: + + LList_elt() : _next(nullptr), _val() {} + + inline T & val(void) { return _val; } + inline LList_elt * next(void) { return _next; } + inline void next(LList_elt * next) { _next = next; } + + friend class LList; + +protected: + LList_elt * _next; + T _val; +}; + + + + + + +template +class LList { +public: + LList() : _head(nullptr) {} + ~LList() { reset(); } + + + void removeHead(void); + void reset(void); + void remove(const T * val); + + + inline bool isEmpty(void) const { return (_head == nullptr) ? true : false; } + size_t length(void) const; + inline T * head(void) { return _head ? &_head->_val : nullptr; } + inline const T * head(void) const { return _head ? &_head->_val : nullptr; } + const T * at(size_t index) const ; + + + inline T * at(size_t index) { return (T*) ((const LList*)this)->at(index); } + + + T & addHead(void); + T & addHead(const T &val); + T & addToLast(void); + + + T & addHead(LList_elt * elt); + T & addToLast(LList_elt * elt); + + + + class iterator { + public: + iterator(LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } } + iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; } + bool operator!=(const iterator & other) const { return cur != other.cur; } + T & operator*() const { return cur->_val; } + private: + LList_elt *cur; + LList_elt *next; + }; + iterator begin() { return iterator(this->_head); } + iterator end() { return iterator(nullptr); } + + + class const_iterator { + public: + const_iterator(const LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } } + const_iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; } + bool operator!=(const_iterator & other) const { return cur != other.cur; } + const T & operator*() const { return cur->_val; } + private: + const LList_elt *cur; + const LList_elt *next; + }; + const_iterator begin() const { return const_iterator(this->_head); } + const_iterator end() const { return const_iterator(nullptr); } + +protected: + LList_elt * _head; +}; + +template +size_t LList::length(void) const { + size_t count = 0; + for (auto & elt : *this) {count++; } + return count; +} + + +template +const T * LList::at(size_t index) const { + size_t count = 0; + for (const auto & elt : *this) { + if (index == count++) { return &elt; } + } + return nullptr; +} + +template +void LList::reset(void) { + while (_head) { + LList_elt * next = _head->next(); + delete _head; + _head = next; + } +} + +template +void LList::removeHead(void) { + if (_head) { + LList_elt * next = _head->next(); + delete _head; + _head = next; + } +} + +template +void LList::remove(const T * val) { + if (nullptr == val) { return; } + + LList_elt **curr_ptr = &_head; + while (*curr_ptr) { + LList_elt * curr_elt = *curr_ptr; + if ( &(curr_elt->_val) == val) { + *curr_ptr = curr_elt->_next; + delete curr_elt; + break; + } + curr_ptr = &((*curr_ptr)->_next); + } +} + +template +T & LList::addHead(void) { + LList_elt * elt = new LList_elt(); + elt->next(_head); + _head = elt; + return elt->_val; +} + +template +T & LList::addHead(const T &val) { + LList_elt * elt = new LList_elt(); + elt->next(_head); + elt->_val = val; + _head = elt; + return elt->_val; +} + +template +T & LList::addHead(LList_elt * elt) { + elt->next(_head); + _head = elt; + return elt->_val; +} + +template +T & LList::addToLast(void) { + LList_elt **curr_ptr = &_head; + while (*curr_ptr) { + curr_ptr = &((*curr_ptr)->_next); + } + LList_elt * elt = new LList_elt(); + *curr_ptr = elt; + return elt->_val; +} + +template +T & LList::addToLast(LList_elt * elt) { + LList_elt **curr_ptr = &_head; + while (*curr_ptr) { + curr_ptr = &((*curr_ptr)->_next); + } + *curr_ptr = elt; + elt->_next = nullptr; + return elt->_val; +} +# 1 "/workspace/Tasmota/tasmota/support_network.ino" +# 24 "/workspace/Tasmota/tasmota/support_network.ino" +struct { + uint8_t begun = 0; +} Mdns; + +#ifdef USE_DISCOVERY +void StartMdns(void) { + if (Settings.flag3.mdns_enabled) { + if (!Mdns.begun) { + + + + + + Mdns.begun = (uint8_t)MDNS.begin(my_hostname); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? D_INITIALIZED : D_FAILED); + + } + } +} + +#ifdef MQTT_HOST_DISCOVERY +void MqttDiscoverServer(void) +{ + if (!Mdns.begun) { return; } + + int n = MDNS.queryService("mqtt", "tcp"); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); + + if (n > 0) { + uint32_t i = 0; +#ifdef MDNS_HOSTNAME + for (i = n; i > 0; i--) { + if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { + break; + } + } +#endif + SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); + Settings.mqtt_port = MDNS.port(i); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + } +} +#endif + +#ifdef WEBSERVER_ADVERTISE +void MdnsAddServiceHttp(void) { + if (1 == Mdns.begun) { + Mdns.begun = 2; + MDNS.addService("http", "tcp", WEB_PORT); + } +} + +void MdnsUpdate(void) { + if (2 == Mdns.begun) { + MDNS.update(); + AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); + } +} +#endif +#endif + + + + + +char* NetworkHostname(void) { +#ifdef ESP32 +#ifdef USE_ETHERNET + if (!global_state.eth_down) { + return EthernetHostname(); + } +#endif +#endif + return my_hostname; +} + +IPAddress NetworkAddress(void) { +#ifdef ESP32 +#ifdef USE_ETHERNET + if (!global_state.eth_down) { + return EthernetLocalIP(); + } +#endif +#endif + return WiFi.localIP(); +} + +String NetworkMacAddress(void) { +#ifdef ESP32 +#ifdef USE_ETHERNET + if (!global_state.eth_down) { + return EthernetMacAddress(); + } +#endif +#endif + return WiFi.macAddress(); +} +# 1 "/workspace/Tasmota/tasmota/support_rotary.ino" +# 20 "/workspace/Tasmota/tasmota/support_rotary.ino" +#ifdef ROTARY_V1 +# 36 "/workspace/Tasmota/tasmota/support_rotary.ino" +#ifndef ROTARY_MAX_STEPS +#define ROTARY_MAX_STEPS 10 +#endif +#ifndef ROTARY_START_DIM +#define ROTARY_START_DIM 1 +#endif +#ifndef ROTARY_TIMEOUT +#define ROTARY_TIMEOUT 2 +#endif +#ifndef ROTARY_DEBOUNCE +#define ROTARY_DEBOUNCE 10 +#endif + + + +const uint8_t rotary_dimmer_increment[2] = { 100 / (ROTARY_MAX_STEPS * 3), 100 / ROTARY_MAX_STEPS }; +const uint8_t rotary_ct_increment[2] = { 350 / (ROTARY_MAX_STEPS * 3), 350 / ROTARY_MAX_STEPS }; +const uint8_t rotary_color_increment[2] = { 360 / (ROTARY_MAX_STEPS * 3), 360 / ROTARY_MAX_STEPS }; +const uint8_t rotary_offset = 128; +const int8_t rotary_state_pos[16] = { 0, 1, -1, 2, -1, 0, -2, 1, 1, -2, 0, -1, 2, -1, 1, 0 }; + +struct ROTARY { + uint8_t model; + bool present; +} Rotary; + +struct tEncoder { + volatile uint32_t debounce = 0; + volatile uint8_t state = 0; + volatile uint8_t position; + volatile int8_t direction = 0; + volatile int8_t pina; + volatile int8_t pinb; + uint8_t timeout = 0; + int8_t abs_position[2] = { 0 }; + bool changed = false; +}; +tEncoder Encoder[MAX_ROTARIES]; + + + +bool RotaryButtonPressed(uint32_t button_index) { + if (!Rotary.present) { return false; } + + for (uint32_t index = 0; index < MAX_ROTARIES; index++) { + if (-1 == Encoder[index].pinb) { continue; } + if (index != button_index) { continue; } + + bool powered_on = (power); +#ifdef USE_LIGHT + if (!Settings.flag4.rotary_uses_rules) { + powered_on = LightPower(); + } +#endif + if (Encoder[index].changed && powered_on) { + Encoder[index].changed = false; + return true; + } + return false; + } + return false; +} + +void ICACHE_RAM_ATTR RotaryIsrArgMiDesk(void *arg) { + tEncoder* encoder = static_cast(arg); + + + uint32_t state = encoder->state & 3; + if (digitalRead(encoder->pina)) { state |= 4; } + if (digitalRead(encoder->pinb)) { state |= 8; } + encoder->position += rotary_state_pos[state]; + encoder->state = (state >> 2); +} + +void ICACHE_RAM_ATTR RotaryIsrArg(void *arg) { + tEncoder* encoder = static_cast(arg); + + + uint32_t time = millis(); + if ((encoder->debounce < time) || (encoder->debounce > time + ROTARY_DEBOUNCE)) { + int direction = (digitalRead(encoder->pinb)) ? -1 : 1; + if ((0 == encoder->direction) || (direction == encoder->direction)) { + encoder->position += direction; + encoder->direction = direction; + } + encoder->debounce = time + ROTARY_DEBOUNCE; + } +} + +void RotaryInit(void) { + Rotary.present = false; + Rotary.model = 1; +#ifdef ESP8266 + if (MI_DESK_LAMP == my_module_type) { + Rotary.model = 0; + } +#endif + for (uint32_t index = 0; index < MAX_ROTARIES; index++) { + Encoder[index].pinb = -1; + if (PinUsed(GPIO_ROT1A, index) && PinUsed(GPIO_ROT1B, index)) { + Encoder[index].position = rotary_offset; + Encoder[index].pina = Pin(GPIO_ROT1A, index); + Encoder[index].pinb = Pin(GPIO_ROT1B, index); + pinMode(Encoder[index].pina, INPUT_PULLUP); + pinMode(Encoder[index].pinb, INPUT_PULLUP); + if (0 == Rotary.model) { + attachInterruptArg(Encoder[index].pina, RotaryIsrArgMiDesk, &Encoder[index], CHANGE); + attachInterruptArg(Encoder[index].pinb, RotaryIsrArgMiDesk, &Encoder[index], CHANGE); + } else { + attachInterruptArg(Encoder[index].pina, RotaryIsrArg, &Encoder[index], FALLING); + } + } + Rotary.present |= (Encoder[index].pinb > -1); + } +} + + + + + +void RotaryHandler(void) { + if (!Rotary.present) { return; } + + for (uint32_t index = 0; index < MAX_ROTARIES; index++) { + if (-1 == Encoder[index].pinb) { continue; } + + if (Encoder[index].timeout) { + Encoder[index].timeout--; + if (!Encoder[index].timeout) { +#ifdef USE_LIGHT + if (!Settings.flag4.rotary_uses_rules) { + ResponseLightState(0); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_STATE)); + } +#endif + Encoder[index].direction = 0; + } + } + if (rotary_offset == Encoder[index].position) { continue; } + + Encoder[index].timeout = ROTARY_TIMEOUT; + + noInterrupts(); + int rotary_position = Encoder[index].position - rotary_offset; + Encoder[index].position = rotary_offset; + interrupts(); + + if (Settings.save_data && (save_data_counter < 2)) { + save_data_counter = 3; + } + + bool button_pressed = (Button.hold_timer[index]); + if (button_pressed) { Encoder[index].changed = true; } + + +#ifdef USE_LIGHT + if (!Settings.flag4.rotary_uses_rules) { + bool second_rotary = (Encoder[1].pinb > -1); + if (0 == index) { + if (button_pressed) { + if (second_rotary) { + LightColorOffset(rotary_position * rotary_color_increment[Rotary.model]); + } else { + if (!LightColorTempOffset(rotary_position * rotary_ct_increment[Rotary.model])) { + LightColorOffset(rotary_position * rotary_color_increment[Rotary.model]); + } + } + } else { + uint32_t dimmer_index = second_rotary ? 1 : 0; + if (!Settings.flag4.rotary_poweron_dimlow || power) { + LightDimmerOffset(dimmer_index, rotary_position * rotary_dimmer_increment[Rotary.model]); + } else { + if (rotary_position > 0) { + LightDimmerOffset(dimmer_index, -LightGetDimmer(dimmer_index) + ROTARY_START_DIM); + } + } + } + } else { + if (button_pressed) { + LightColorTempOffset(rotary_position * rotary_ct_increment[Rotary.model]); + } else { + LightDimmerOffset(2, rotary_position * rotary_dimmer_increment[Rotary.model]); + } + } + } else { +#endif + Encoder[index].abs_position[button_pressed] += rotary_position; + if (Encoder[index].abs_position[button_pressed] < 0) { + Encoder[index].abs_position[button_pressed] = 0; + } + if (Encoder[index].abs_position[button_pressed] > ROTARY_MAX_STEPS) { + Encoder[index].abs_position[button_pressed] = ROTARY_MAX_STEPS; + } + Response_P(PSTR("{\"Rotary%d\":{\"Pos1\":%d,\"Pos2\":%d}}"), index +1, Encoder[index].abs_position[0], Encoder[index].abs_position[1]); + XdrvRulesProcess(); +#ifdef USE_LIGHT + } +#endif + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_rtc.ino" +# 25 "/workspace/Tasmota/tasmota/support_rtc.ino" +const uint32_t SECS_PER_MIN = 60UL; +const uint32_t SECS_PER_HOUR = 3600UL; +const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; +const uint32_t MINS_PER_HOUR = 60UL; + +#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) + +extern "C" { +#include "sntp.h" +} +#include + +Ticker TickerRtc; + +static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +struct RTC { + uint32_t utc_time = 0; + uint32_t local_time = 0; + uint32_t daylight_saving_time = 0; + uint32_t standard_time = 0; + uint32_t ntp_time = 0; + uint32_t midnight = 0; + uint32_t restart_time = 0; + uint32_t millis = 0; + uint32_t last_sync = 0; + int32_t time_timezone = 0; + uint8_t ntp_sync_minute = 0; + bool midnight_now = false; + bool user_time_entry = false; +} Rtc; + +uint32_t UtcTime(void) +{ + return Rtc.utc_time; +} + +uint32_t LocalTime(void) +{ + return Rtc.local_time; +} + +uint32_t Midnight(void) +{ + return Rtc.midnight; +} + +bool MidnightNow(void) +{ + if (Rtc.midnight_now) { + Rtc.midnight_now = false; + return true; + } + return false; +} + +bool IsDst(void) +{ + if (Rtc.time_timezone == Settings.toffset[1]) { + return true; + } + return false; +} + +String GetBuildDateAndTime(void) +{ + + char bdt[21]; + char *p; + char mdate[] = __DATE__; + char *smonth = mdate; + int day = 0; + int year = 0; + + + uint8_t i = 0; + for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { + switch (i++) { + case 0: + smonth = str; + break; + case 1: + day = atoi(str); + break; + case 2: + year = atoi(str); + } + } + int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1; + snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__); + return String(bdt); +} + +String GetMinuteTime(uint32_t minutes) +{ + char tm[6]; + snprintf_P(tm, sizeof(tm), PSTR("%02d:%02d"), minutes / 60, minutes % 60); + + return String(tm); +} + +String GetTimeZone(void) +{ + char tz[7]; + snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), Rtc.time_timezone / 60, abs(Rtc.time_timezone % 60)); + + return String(tz); +} + +String GetDuration(uint32_t time) +{ + char dt[16]; + + TIME_T ut; + BreakTime(time, ut); + + + + + + + snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); + + return String(dt); +} + +String GetDT(uint32_t time) +{ + + + char dt[20]; + TIME_T tmpTime; + + BreakTime(time, tmpTime); + snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), + tmpTime.year +1970, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); + + return String(dt); +} +# 177 "/workspace/Tasmota/tasmota/support_rtc.ino" +String GetDateAndTime(uint8_t time_type) +{ + + uint32_t time = Rtc.local_time; + + switch (time_type) { + case DT_UTC: + time = Rtc.utc_time; + break; + + + + case DT_DST: + time = Rtc.daylight_saving_time; + break; + case DT_STD: + time = Rtc.standard_time; + break; + case DT_RESTART: + if (Rtc.restart_time == 0) { + return ""; + } + time = Rtc.restart_time; + break; + case DT_ENERGY: + time = Settings.energy_kWhtotal_time; + break; + case DT_BOOTCOUNT: + time = Settings.bootcount_reset_time; + break; + } + String dt = GetDT(time); + + if (DT_LOCAL_MILLIS == time_type) { + char ms[10]; + snprintf_P(ms, sizeof(ms), PSTR(".%03d"), RtcMillis()); + dt += ms; + time_type = DT_LOCAL; + } + + if (Settings.flag3.time_append_timezone && (DT_LOCAL == time_type)) { + dt += GetTimeZone(); + } + return dt; +} + +uint32_t UpTime(void) +{ + if (Rtc.restart_time) { + return Rtc.utc_time - Rtc.restart_time; + } else { + return uptime; + } +} + +uint32_t MinutesUptime(void) +{ + return (UpTime() / 60); +} + +String GetUptime(void) +{ + return GetDuration(UpTime()); +} + +uint32_t MinutesPastMidnight(void) +{ + uint32_t minutes = 0; + + if (RtcTime.valid) { + minutes = (RtcTime.hour *60) + RtcTime.minute; + } + return minutes; +} + +uint32_t RtcMillis(void) { + return (millis() - Rtc.millis) % 1000; +} + +void BreakTime(uint32_t time_input, TIME_T &tm) +{ + + + + + uint8_t year; + uint8_t month; + uint8_t month_length; + uint32_t time; + unsigned long days; + + time = time_input; + tm.second = time % 60; + time /= 60; + tm.minute = time % 60; + time /= 60; + tm.hour = time % 24; + time /= 24; + tm.days = time; + tm.day_of_week = ((time + 4) % 7) + 1; + + year = 0; + days = 0; + while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { + year++; + } + tm.year = year; + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; + tm.day_of_year = time; + + for (month = 0; month < 12; month++) { + if (1 == month) { + if (LEAP_YEAR(year)) { + month_length = 29; + } else { + month_length = 28; + } + } else { + month_length = kDaysInMonth[month]; + } + + if (time >= month_length) { + time -= month_length; + } else { + break; + } + } + strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); + tm.month = month + 1; + tm.day_of_month = time + 1; + tm.valid = (time_input > START_VALID_TIME); +} + +uint32_t MakeTime(TIME_T &tm) +{ + + + + int i; + uint32_t seconds; + + + seconds = tm.year * (SECS_PER_DAY * 365); + for (i = 0; i < tm.year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; + } + } + + + for (i = 1; i < tm.month; i++) { + if ((2 == i) && LEAP_YEAR(tm.year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * kDaysInMonth[i-1]; + } + } + seconds+= (tm.day_of_month - 1) * SECS_PER_DAY; + seconds+= tm.hour * SECS_PER_HOUR; + seconds+= tm.minute * SECS_PER_MIN; + seconds+= tm.second; + return seconds; +} + +uint32_t RuleToTime(TimeRule r, int yr) +{ + TIME_T tm; + uint32_t t; + uint8_t m; + uint8_t w; + + m = r.month; + w = r.week; + if (0 == w) { + if (++m > 12) { + m = 1; + yr++; + } + w = 1; + } + + tm.hour = r.hour; + tm.minute = 0; + tm.second = 0; + tm.day_of_month = 1; + tm.month = m; + tm.year = yr - 1970; + t = MakeTime(tm); + BreakTime(t, tm); + t += (7 * (w - 1) + (r.dow - tm.day_of_week + 7) % 7) * SECS_PER_DAY; + if (0 == r.week) { + t -= 7 * SECS_PER_DAY; + } + return t; +} + +void RtcSecond(void) +{ + TIME_T tmpTime; + + Rtc.millis = millis(); + + if (!Rtc.user_time_entry) { + if (!global_state.network_down) { + uint8_t uptime_minute = (uptime / 60) % 60; + if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) { + Rtc.ntp_sync_minute = 1; + } + uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP_getChipId() & 0xF) * 3) + 3) ; + if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || + (Rtc.ntp_sync_minute == uptime_minute))) || + ntp_force_sync ) ) { + Rtc.ntp_time = sntp_get_current_timestamp(); + if (Rtc.ntp_time > START_VALID_TIME) { + ntp_force_sync = false; + Rtc.utc_time = Rtc.ntp_time; + Rtc.last_sync = Rtc.ntp_time; + Rtc.ntp_sync_minute = 60; + if (Rtc.restart_time == 0) { + Rtc.restart_time = Rtc.utc_time - uptime; + } + BreakTime(Rtc.utc_time, tmpTime); + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); + + if (Rtc.local_time < START_VALID_TIME) { + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } + } else { + Rtc.ntp_sync_minute++; + } + } + } + if ((Rtc.utc_time > (2 * 60 * 60)) && (Rtc.last_sync < Rtc.utc_time - (2 * 60 * 60))) { + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Not synced")); + Rtc.last_sync = Rtc.utc_time; + } + } + + Rtc.utc_time++; + Rtc.local_time = Rtc.utc_time; + if (Rtc.local_time > START_VALID_TIME) { + int16_t timezone_minutes = Settings.timezone_minutes; + if (Settings.timezone < 0) { timezone_minutes *= -1; } + Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); + if (99 == Settings.timezone) { + int32_t dstoffset = Settings.toffset[1] * SECS_PER_MIN; + int32_t stdoffset = Settings.toffset[0] * SECS_PER_MIN; + if (Settings.tflag[1].hemis) { + + if ((Rtc.utc_time >= (Rtc.standard_time - dstoffset)) && (Rtc.utc_time < (Rtc.daylight_saving_time - stdoffset))) { + Rtc.time_timezone = stdoffset; + } else { + Rtc.time_timezone = dstoffset; + } + } else { + + if ((Rtc.utc_time >= (Rtc.daylight_saving_time - stdoffset)) && (Rtc.utc_time < (Rtc.standard_time - dstoffset))) { + Rtc.time_timezone = dstoffset; + } else { + Rtc.time_timezone = stdoffset; + } + } + } + Rtc.local_time += Rtc.time_timezone; + Rtc.time_timezone /= 60; + if (!Settings.energy_kWhtotal_time) { + Settings.energy_kWhtotal_time = Rtc.local_time; + } + if (Settings.bootcount_reset_time < START_VALID_TIME) { + Settings.bootcount_reset_time = Rtc.local_time; + } + } + + BreakTime(Rtc.local_time, RtcTime); + if (RtcTime.valid) { + if (!Rtc.midnight) { + Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; + } + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { + Rtc.midnight = Rtc.local_time; + Rtc.midnight_now = true; + } + } + + RtcTime.year += 1970; +} + +void RtcSetTime(uint32_t epoch) +{ + if (epoch < START_VALID_TIME) { + Rtc.user_time_entry = false; + ntp_force_sync = true; + sntp_init(); + } else { + sntp_stop(); + Rtc.user_time_entry = true; + Rtc.utc_time = epoch -1; + } +} + +void RtcInit(void) +{ + sntp_setservername(0, SettingsText(SET_NTPSERVER1)); + sntp_setservername(1, SettingsText(SET_NTPSERVER2)); + sntp_setservername(2, SettingsText(SET_NTPSERVER3)); + sntp_stop(); + sntp_set_timezone(0); + sntp_init(); + Rtc.utc_time = 0; + BreakTime(Rtc.utc_time, RtcTime); + TickerRtc.attach(1, RtcSecond); +} +# 1 "/workspace/Tasmota/tasmota/support_static_buffer.ino" +# 20 "/workspace/Tasmota/tasmota/support_static_buffer.ino" +typedef struct SBuffer_impl { + uint16_t size; + uint16_t len; + uint8_t buf[]; +} SBuffer_impl; + + + +typedef class SBuffer { + +protected: + SBuffer(void) { + + } + +public: + SBuffer(const size_t size) { + _buf = (SBuffer_impl*) new char[size+4]; + _buf->size = size; + _buf->len = 0; + + } + + inline size_t getSize(void) const { return _buf->size; } + inline size_t size(void) const { return _buf->size; } + inline size_t getLen(void) const { return _buf->len; } + inline size_t len(void) const { return _buf->len; } + inline uint8_t *getBuffer(void) const { return _buf->buf; } + inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } + inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } + + virtual ~SBuffer(void) { + delete[] _buf; + } + + inline void setLen(const size_t len) { + uint16_t old_len = _buf->len; + _buf->len = (len <= _buf->size) ? len : _buf->size; + if (old_len < _buf->len) { + memset((void*) &_buf->buf[old_len], 0, _buf->len - old_len); + } + } + + void set8(const size_t offset, const uint8_t data) { + if (offset < _buf->len) { + _buf->buf[offset] = data; + } + } + + size_t add8(const uint8_t data) { + if (_buf->len < _buf->size) { + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } + size_t add16(const uint16_t data) { + if (_buf->len < _buf->size - 1) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + } + return _buf->len; + } + size_t add16BigEndian(const uint16_t data) { + if (_buf->len < _buf->size - 1) { + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } + size_t add32(const uint32_t data) { + if (_buf->len < _buf->size - 3) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 24; + } + return _buf->len; + } + size_t add32BigEndian(const uint32_t data) { + if (_buf->len < _buf->size - 3) { + _buf->buf[_buf->len++] = data >> 24; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } + size_t add64(const uint64_t data) { + if (_buf->len < _buf->size - 7) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 24; + _buf->buf[_buf->len++] = data >> 32; + _buf->buf[_buf->len++] = data >> 40; + _buf->buf[_buf->len++] = data >> 48; + _buf->buf[_buf->len++] = data >> 56; + } + return _buf->len; + } + + size_t addBuffer(const SBuffer &buf2) { + if (len() + buf2.len() <= size()) { + for (uint32_t i = 0; i < buf2.len(); i++) { + _buf->buf[_buf->len++] = buf2.buf()[i]; + } + } + return _buf->len; + } + + size_t addBuffer(const uint8_t *buf2, size_t len2) { + if ((buf2) && (len() + len2 <= size())) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + size_t addBuffer(const char *buf2, size_t len2) { + if ((buf2) && (len() + len2 <= size())) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + uint8_t get8(size_t offset) const { + if (offset < _buf->len) { + return _buf->buf[offset]; + } else { + return 0; + } + } + uint8_t read8(const size_t offset) const { + if (offset < len()) { + return _buf->buf[offset]; + } + return 0; + } + uint16_t get16(const size_t offset) const { + if (offset < len() - 1) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8); + } + return 0; + } + uint16_t get16BigEndian(const size_t offset) const { + if (offset < len() - 1) { + return _buf->buf[offset+1] | (_buf->buf[offset] << 8); + } + return 0; + } + uint32_t get32(const size_t offset) const { + if (offset < len() - 3) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8) | + (_buf->buf[offset+2] << 16) | (_buf->buf[offset+3] << 24); + } + return 0; + } + int32_t get32IBigEndian(const size_t offset) const { + if (offset < len() - 3) { + return _buf->buf[offset+3] | (_buf->buf[offset+2] << 8) | + (_buf->buf[offset+1] << 16) | (_buf->buf[offset] << 24); + } + return 0; + } + uint64_t get64(const size_t offset) const { + if (offset < len() - 7) { + return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | + ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | + ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | + ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); + } + return 0; + } + + + inline size_t strlen(const size_t offset) const { + return strnlen((const char*) &_buf->buf[offset], len() - offset); + } + + size_t strlen_s(const size_t offset) const { + size_t slen = this->strlen(offset); + if (slen == len() - offset) { + return 0; + } else { + return slen; + } + } + + SBuffer subBuffer(const size_t start, size_t len) const { + if (start >= _buf->len) { + len = 0; + } else if (start + len > _buf->len) { + len = _buf->len - start; + } + + SBuffer buf2(len); + memcpy(buf2.buf(), buf()+start, len); + buf2._buf->len = len; + return buf2; + } + + static SBuffer SBufferFromHex(const char *hex, size_t len) { + size_t buf_len = (len + 3) / 2; + SBuffer buf2(buf_len); + uint8_t val; + + for (; len > 1; len -= 2) { + val = asc2byte(*hex++) << 4; + val |= asc2byte(*hex++); + buf2.add8(val); + } + return buf2; + } + +protected: + + static uint8_t asc2byte(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { rVal = chr - '0'; } + else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; + } + + static void unHex(const char* in, uint8_t *out, size_t len) { + } + +protected: + SBuffer_impl * _buf; + +} SBuffer; + +typedef class PreAllocatedSBuffer : public SBuffer { + +public: + PreAllocatedSBuffer(const size_t size, void * buffer) { + _buf = (SBuffer_impl*) buffer; + _buf->size = size - 4; + _buf->len = 0; + } + + ~PreAllocatedSBuffer(void) { + + _buf = nullptr; + } +} PreAllocatedSBuffer; + + +bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) { + if (buf1 == buf2) { return true; } + if (!buf1 && (buf2->len() == 0)) { return true; } + if (!buf2 && (buf1->len() == 0)) { return true; } + if (!buf1 || !buf2) { return false; } + + if (buf1->len() != buf2->len()) { return false; } + size_t len = buf1->len(); + for (uint32_t i=0; iget8(i) != buf2->get8(i)) { return false; } + } + return true; +} +# 1 "/workspace/Tasmota/tasmota/support_statistics.ino" +# 20 "/workspace/Tasmota/tasmota/support_statistics.ino" +#define USE_STATS_CODE + +#ifdef USE_STATS_CODE + + + + +String GetStatistics(void) +{ + char data[40]; + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); + return String(data); +} + +#else + +String GetStatistics(void) +{ + return String(""); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_switch.ino" +# 20 "/workspace/Tasmota/tasmota/support_switch.ino" +#define SWITCH_V3 +#ifdef SWITCH_V3 + + + + + + +const uint8_t SWITCH_PROBE_INTERVAL = 10; +const uint8_t SWITCH_FAST_PROBE_INTERVAL =2; +const uint8_t AC_PERIOD = (20 + SWITCH_FAST_PROBE_INTERVAL - 1) / SWITCH_FAST_PROBE_INTERVAL; + + +#define SM_TIMER_MASK 0x3F +#define SM_NO_TIMER_MASK 0xFF +#define SM_FIRST_PRESS 0x40 +#define SM_SECOND_PRESS 0x80 + +#include + +Ticker TickerSwitch; + +struct SWITCH { + unsigned long debounce = 0; + uint16_t no_pullup_mask = 0; + uint8_t state[MAX_SWITCHES] = { 0 }; + uint8_t last_state[MAX_SWITCHES]; + uint8_t hold_timer[MAX_SWITCHES] = { 0 }; + uint8_t virtual_state[MAX_SWITCHES]; + uint8_t first_change = 0; + uint8_t present = 0; +} Switch; + + + +void SwitchPullupFlag(uint16 switch_bit) +{ + bitSet(Switch.no_pullup_mask, switch_bit); +} + +void SwitchSetVirtual(uint32_t index, uint8_t state) +{ + Switch.virtual_state[index] = state; +} + +uint8_t SwitchGetVirtual(uint32_t index) +{ + return Switch.virtual_state[index]; +} + +uint8_t SwitchLastState(uint32_t index) +{ + return Switch.last_state[index]; +} + +bool SwitchState(uint32_t index) +{ + uint32_t switchmode = Settings.switchmode[index]; + return ((FOLLOW_INV == switchmode) || + (PUSHBUTTON_INV == switchmode) || + (PUSHBUTTONHOLD_INV == switchmode) || + (FOLLOWMULTI_INV == switchmode) || + (PUSHHOLDMULTI_INV == switchmode) || + (PUSHON_INV == switchmode) + ) ^ Switch.last_state[index]; +} + + + +void SwitchProbe(void) +{ + if (uptime < 4) { return; } + + uint8_t state_filter; + uint8_t debounce_flags = Settings.switch_debounce % 10; + uint8_t force_high = debounce_flags &1; + uint8_t force_low = debounce_flags &2; + uint8_t ac_detect = debounce_flags == 9; + uint8_t switch_probe_interval; + uint8_t first_change = Switch.first_change; + + if (ac_detect) { + switch_probe_interval = SWITCH_FAST_PROBE_INTERVAL; + if (Settings.switch_debounce < 2 * AC_PERIOD * SWITCH_FAST_PROBE_INTERVAL + 9) { + state_filter = 2 * AC_PERIOD; + } else if (Settings.switch_debounce > (0x7f - 2 * AC_PERIOD) * SWITCH_FAST_PROBE_INTERVAL) { + state_filter = 0x7f; + } else { + state_filter = (Settings.switch_debounce - 9) / SWITCH_FAST_PROBE_INTERVAL; + } + } else { + switch_probe_interval = SWITCH_PROBE_INTERVAL; + state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; + } + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + if (PinUsed(GPIO_SWT1, i)) { + + if (1 == digitalRead(Pin(GPIO_SWT1, i))) { + + if (ac_detect) { + Switch.state[i] |= 0x80; + if (Switch.state[i] > 0x80) { + Switch.state[i]--; + if (0x80 == Switch.state[i]) { + Switch.virtual_state[i] = 0; + Switch.first_change = false; + } + } + } else { + + if (force_high) { + if (1 == Switch.virtual_state[i]) { + Switch.state[i] = state_filter; + } + } + + if (Switch.state[i] < state_filter) { + Switch.state[i]++; + if (state_filter == Switch.state[i]) { + Switch.virtual_state[i] = 1; + } + } + } + } else { + + if (ac_detect) { + + + + + + + + if (Switch.state[i] & 0x80) { + Switch.state[i] &= 0x7f; + if (Switch.state[i] < state_filter - 2 * AC_PERIOD) { + Switch.state[i] += 2 * AC_PERIOD; + } else { + Switch.state[i] = state_filter; + Switch.virtual_state[i] = 1; + if (first_change) { + Switch.last_state[i] = 1; + Switch.first_change = false; + } + } + } else { + if (Switch.state[i] > 0x00) { + Switch.state[i]--; + if (0x00 == Switch.state[i]) { + Switch.virtual_state[i] = 0; + Switch.first_change = false; + } + } + } + } else { + + if (force_low) { + if (0 == Switch.virtual_state[i]) { + Switch.state[i] = 0; + } + } + + if (Switch.state[i] > 0) { + Switch.state[i]--; + if (0 == Switch.state[i]) { + Switch.virtual_state[i] = 0; + } + } + } + } + } + } + TickerSwitch.attach_ms(switch_probe_interval, SwitchProbe); +} + +void SwitchInit(void) +{ + uint8_t ac_detect = Settings.switch_debounce % 10 == 9; + + Switch.present = 0; + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + Switch.last_state[i] = 1; + if (PinUsed(GPIO_SWT1, i)) { + Switch.present++; +#ifdef ESP8266 + pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_SWT1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); +#else + pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : INPUT_PULLUP); +#endif + if (ac_detect) { + Switch.state[i] = 0x80 + 2 * AC_PERIOD; + Switch.last_state[i] = 0; + } else { + Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); + } + } + Switch.virtual_state[i] = Switch.last_state[i]; + } + if (Switch.present) { + if (ac_detect) { + TickerSwitch.attach_ms(SWITCH_FAST_PROBE_INTERVAL, SwitchProbe); + Switch.first_change = true; + } else { + TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); + } + } +} + + + + + +void SwitchHandler(uint8_t mode) +{ + if (uptime < 4) { return; } + + uint16_t loops_per_second = 1000 / Settings.switch_debounce; + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + if (PinUsed(GPIO_SWT1, i) || (mode)) { + uint8_t button = Switch.virtual_state[i]; + uint8_t switchflag = POWER_TOGGLE +1; + + if (Switch.hold_timer[i] & (((Settings.switchmode[i] == PUSHHOLDMULTI) | (Settings.switchmode[i] == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK)) { + Switch.hold_timer[i]--; + if ((Switch.hold_timer[i] & SM_TIMER_MASK) == loops_per_second * Settings.param[P_HOLD_TIME] / 25) { + if ((Settings.switchmode[i] == PUSHHOLDMULTI) & (NOT_PRESSED == Switch.last_state[i])) { + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } + if ((Settings.switchmode[i] == PUSHHOLDMULTI_INV) & (PRESSED == Switch.last_state[i])) { + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } + } + if (0 == (Switch.hold_timer[i] & (((Settings.switchmode[i] == PUSHHOLDMULTI) | (Settings.switchmode[i] == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK))) { + switch (Settings.switchmode[i]) { + case TOGGLEMULTI: + switchflag = POWER_TOGGLE; + break; + case FOLLOWMULTI: + switchflag = button &1; + break; + case FOLLOWMULTI_INV: + switchflag = ~button &1; + break; + case PUSHHOLDMULTI: + if (NOT_PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } else { + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); + } + break; + case PUSHHOLDMULTI_INV: + if (PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } else { + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); + } + break; + default: + SendKey(KEY_SWITCH, i +1, POWER_HOLD); + break; + } + } + } + + if (button != Switch.last_state[i]) { + switch (Settings.switchmode[i]) { + case TOGGLE: + case PUSHBUTTON_TOGGLE: + switchflag = POWER_TOGGLE; + break; + case FOLLOW: + switchflag = button &1; + break; + case FOLLOW_INV: + switchflag = ~button &1; + break; + case PUSHBUTTON: + if (PRESSED == button) { + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTON_INV: + if (NOT_PRESSED == button) { + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTONHOLD: + if (PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((NOT_PRESSED == button) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTONHOLD_INV: + if (NOT_PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((PRESSED == button) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; + } + break; + case TOGGLEMULTI: + case FOLLOWMULTI: + case FOLLOWMULTI_INV: + if (Switch.hold_timer[i]) { + Switch.hold_timer[i] = 0; + SendKey(KEY_SWITCH, i +1, POWER_HOLD); + } else { + Switch.hold_timer[i] = loops_per_second / 2; + } + break; + case PUSHHOLDMULTI: + if (NOT_PRESSED == button) { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { + Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; + SendKey(KEY_SWITCH, i +1, POWER_INV); + } + } else { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) > loops_per_second * Settings.param[P_HOLD_TIME] / 25) { + if((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { + Switch.hold_timer[i]= SM_FIRST_PRESS; + switchflag = POWER_TOGGLE; + } + else{ + SendKey(KEY_SWITCH, i +1, POWER_100); + Switch.hold_timer[i]= 0; + } + } else { + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_RELEASE); + } + } + Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings.param[P_HOLD_TIME] / 10; + break; + case PUSHHOLDMULTI_INV: + if (PRESSED == button) { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { + Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; + SendKey(KEY_SWITCH, i +1, POWER_INV); + } + } else { + if ((Switch.hold_timer[i] & SM_TIMER_MASK)> loops_per_second * Settings.param[P_HOLD_TIME] / 25) { + if((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { + Switch.hold_timer[i]= SM_FIRST_PRESS; + switchflag = POWER_TOGGLE; + } + else{ + SendKey(KEY_SWITCH, i +1, POWER_100); + Switch.hold_timer[i]= 0; + } + } else { + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_RELEASE); + } + } + Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings.param[P_HOLD_TIME] / 10; + break; + case PUSHON: + if (PRESSED == button) { + switchflag = POWER_ON; + } + break; + case PUSHON_INV: + if (NOT_PRESSED == button) { + switchflag = POWER_ON; + } + break; + case PUSH_IGNORE: + MqttPublishSensor(); + break; + } + Switch.last_state[i] = button; + } + if (switchflag <= POWER_TOGGLE) { + if (!SendKey(KEY_SWITCH, i +1, switchflag)) { + ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); + } + } + } + } +} + +void SwitchLoop(void) +{ + if (Switch.present) { + if (TimeReached(Switch.debounce)) { + SetNextTimeInterval(Switch.debounce, Settings.switch_debounce); + SwitchHandler(0); + } + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_tasmota.ino" +# 20 "/workspace/Tasmota/tasmota/support_tasmota.ino" +const char kSleepMode[] PROGMEM = "Dynamic|Normal"; +const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; + +char* Format(char* output, const char* input, int size) +{ + char *token; + uint32_t digits = 0; + + if (strstr(input, "%") != nullptr) { + strlcpy(output, input, size); + token = strtok(output, "%"); + if (strstr(input, "%") == input) { + output[0] = '\0'; + } else { + token = strtok(nullptr, ""); + } + if (token != nullptr) { + digits = atoi(token); + if (digits) { + char tmp[size]; + if (strchr(token, 'd')) { + snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); + snprintf_P(output, size, tmp, ESP_getChipId() & 0x1fff); + } else { + String mac_address = WiFi.macAddress(); + mac_address.replace(":", ""); + if (digits > 12) { digits = 12; } + String mac_part = mac_address.substring(12 - digits); + snprintf_P(output, size, PSTR("%s%s"), output, mac_part.c_str()); + } + } else { + if (strchr(token, 'd')) { + snprintf_P(output, size, PSTR("%s%d"), output, ESP_getChipId()); + digits = 8; + } + } + } + } + if (!digits) { + strlcpy(output, input, size); + } + return output; +} + +char* GetOtaUrl(char *otaurl, size_t otaurl_size) +{ + if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { + snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId() & 0x1fff); + } + else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { + snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId()); + } + else { + strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); + } + + return otaurl; +} + +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) +{ +# 91 "/workspace/Tasmota/tasmota/support_tasmota.ino" + char romram[CMDSZ]; + String fulltopic; + + snprintf_P(romram, sizeof(romram), subtopic); + if (fallback_topic_flag || (prefix > 3)) { + bool fallback = (prefix < 8); + prefix &= 3; + char stemp[11]; + fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes); + fulltopic += F("/"); + if (fallback) { + fulltopic += mqtt_client; + fulltopic += F("_fb"); + } else { + fulltopic += topic; + } + } else { + fulltopic = SettingsText(SET_MQTT_FULLTOPIC); + if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { + fulltopic += F("/"); + fulltopic += FPSTR(MQTT_TOKEN_PREFIX); + } + for (uint32_t i = 0; i < MAX_MQTT_PREFIXES; i++) { + if (!strlen(SettingsText(SET_MQTTPREFIX1 + i))) { + char temp[TOPSZ]; + SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes)); + } + } + fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); + + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); + fulltopic.replace(F("%hostname%"), my_hostname); + String token_id = WiFi.macAddress(); + token_id.replace(":", ""); + fulltopic.replace(F("%id%"), token_id); + } + fulltopic.replace(F("#"), ""); + fulltopic.replace(F("//"), "/"); + if (!fulltopic.endsWith("/")) { + fulltopic += "/"; + } + snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); + return stopic; +} + +char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic) +{ + + + return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(itopic), subtopic); +} + +char* GetFallbackTopic_P(char *stopic, const char* subtopic) +{ + return GetTopic_P(stopic, CMND +4, nullptr, subtopic); +} + +char* GetStateText(uint32_t state) +{ + if (state >= MAX_STATE_TEXT) { + state = 1; + } + return SettingsText(SET_STATE_TXT1 + state); +} + + + +void SetLatchingRelay(power_t lpower, uint32_t state) +{ + + + + + + if (state && !latching_relay_pulse) { + latching_power = lpower; + latching_relay_pulse = 2; + } + + for (uint32_t i = 0; i < devices_present; i++) { + uint32_t port = (i << 1) + ((latching_power >> i) &1); + DigitalWrite(GPIO_REL1, port, bitRead(rel_inverted, port) ? !state : state); + } +} + +void SetDevicePower(power_t rpower, uint32_t source) +{ + ShowSource(source); + last_source = source; + + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + power = (1 << devices_present) -1; + rpower = power; + } + + if (Settings.flag.interlock) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + power_t mask = 1; + uint32_t count = 0; + for (uint32_t j = 0; j < devices_present; j++) { + if ((Settings.interlock[i] & mask) && (rpower & mask)) { + count++; + } + mask <<= 1; + } + if (count > 1) { + mask = ~Settings.interlock[i]; + power &= mask; + rpower &= mask; + } + } + } + + if (rpower) { + last_power = rpower; + } + + XdrvMailbox.index = rpower; + XdrvCall(FUNC_SET_POWER); + XsnsCall(FUNC_SET_POWER); + + XdrvMailbox.index = rpower; + XdrvMailbox.payload = source; + if (XdrvCall(FUNC_SET_DEVICE_POWER)) { + + } +#ifdef ESP8266 + else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + Serial.write(0xA0); + Serial.write(0x04); + Serial.write(rpower &0xFF); + Serial.write(0xA1); + Serial.write('\n'); + Serial.flush(); + } + else if (EXS_RELAY == my_module_type) { + SetLatchingRelay(rpower, 1); + } +#endif + else + { + for (uint32_t i = 0; i < devices_present; i++) { + power_t state = rpower &1; + if (i < MAX_RELAYS) { + DigitalWrite(GPIO_REL1, i, bitRead(rel_inverted, i) ? !state : state); + } + rpower >>= 1; + } + } +} + +void RestorePower(bool publish_power, uint32_t source) +{ + if (power != last_power) { + power = last_power; + SetDevicePower(power, source); + if (publish_power) { + MqttPublishAllPowerState(); + } + } +} + +void SetAllPower(uint32_t state, uint32_t source) +{ +# 263 "/workspace/Tasmota/tasmota/support_tasmota.ino" + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; + publish_power = false; + } + if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) { + power_t all_on = (1 << devices_present) -1; + switch (state) { + case POWER_OFF: + power = 0; + break; + case POWER_ON: + power = all_on; + break; + case POWER_TOGGLE: + power ^= all_on; + } + SetDevicePower(power, source); + } + if (publish_power) { + MqttPublishAllPowerState(); + } +} + +void SetPowerOnState(void) +{ +#ifdef ESP8266 + if (MOTOR == my_module_type) { + Settings.poweronstate = POWER_ALL_ON; + } +#endif + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + SetDevicePower(1, SRC_RESTART); + } else { + if ((ResetReason() == REASON_DEFAULT_RST) || (ResetReason() == REASON_EXT_SYS_RST)) { + switch (Settings.poweronstate) { + case POWER_ALL_OFF: + case POWER_ALL_OFF_PULSETIME_ON: + power = 0; + SetDevicePower(power, SRC_RESTART); + break; + case POWER_ALL_ON: + power = (1 << devices_present) -1; + SetDevicePower(power, SRC_RESTART); + break; + case POWER_ALL_SAVED_TOGGLE: + power = (Settings.power & ((1 << devices_present) -1)) ^ POWER_MASK; + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + break; + case POWER_ALL_SAVED: + power = Settings.power & ((1 << devices_present) -1); + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + break; + } + } else { + power = Settings.power & ((1 << devices_present) -1); + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + } + } + + + for (uint32_t i = 0; i < devices_present; i++) { + if (!Settings.flag3.no_power_feedback) { + if ((i < MAX_RELAYS) && PinUsed(GPIO_REL1, i)) { + bitWrite(power, i, digitalRead(Pin(GPIO_REL1, i)) ^ bitRead(rel_inverted, i)); + } + } + if (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate)) { + SetPulseTimer(i % MAX_PULSETIMERS, Settings.pulse_timer[i % MAX_PULSETIMERS]); + } + } + blink_powersave = power; +} + +void UpdateLedPowerAll() +{ + for (uint32_t i = 0; i < leds_present; i++) { + SetLedPowerIdx(i, bitRead(led_power, i)); + } +} + +void SetLedPowerIdx(uint32_t led, uint32_t state) +{ + if (!PinUsed(GPIO_LEDLNK) && (0 == led)) { + if (PinUsed(GPIO_LED1, 1)) { + led = 1; + } + } + if (PinUsed(GPIO_LED1, led)) { + uint32_t mask = 1 << led; + if (state) { + state = 1; + led_power |= mask; + } else { + led_power &= (0xFF ^ mask); + } + uint16_t pwm = 0; + if (bitRead(Settings.ledpwm_mask, led)) { +#ifdef USE_LIGHT + pwm = changeUIntScale(ledGamma10(state ? Settings.ledpwm_on : Settings.ledpwm_off), 0, 1023, 0, Settings.pwm_range); +#else + pwm = changeUIntScale((uint16_t)(state ? Settings.ledpwm_on : Settings.ledpwm_off), 0, 255, 0, Settings.pwm_range); +#endif + analogWrite(Pin(GPIO_LED1, led), bitRead(led_inverted, led) ? Settings.pwm_range - pwm : pwm); + } else { + DigitalWrite(GPIO_LED1, led, bitRead(led_inverted, led) ? !state : state); + } + } +#ifdef USE_BUZZER + if (led == 0) { + BuzzerSetStateToLed(state); + } +#endif +} + +void SetLedPower(uint32_t state) +{ + if (!PinUsed(GPIO_LEDLNK)) { + SetLedPowerIdx(0, state); + } else { + power_t mask = 1; + for (uint32_t i = 0; i < leds_present; i++) { + bool tstate = (power & mask); + SetLedPowerIdx(i, tstate); + mask <<= 1; + } + } +} + +void SetLedPowerAll(uint32_t state) +{ + for (uint32_t i = 0; i < leds_present; i++) { + SetLedPowerIdx(i, state); + } +} + +void SetLedLink(uint32_t state) +{ + uint32_t led_pin = Pin(GPIO_LEDLNK); + uint32_t led_inv = ledlnk_inverted; + if (99 == led_pin) { + SetLedPowerIdx(0, state); + } + else if (led_pin < 99) { + if (state) { state = 1; } + digitalWrite(led_pin, (led_inv) ? !state : state); + } +#ifdef USE_BUZZER + BuzzerSetStateToLed(state); +#endif +} + +void SetPulseTimer(uint32_t index, uint32_t time) +{ + pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; +} + +uint32_t GetPulseTimer(uint32_t index) +{ + long time = TimePassedSince(pulse_timer[index]); + if (time < 0) { + time *= -1; + return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; + } + return 0; +} + + + +bool SendKey(uint32_t key, uint32_t device, uint32_t state) +{ +# 452 "/workspace/Tasmota/tasmota/support_tasmota.ino" + char stopic[TOPSZ]; + char scommand[CMDSZ]; + char key_topic[TOPSZ]; + bool result = false; + uint32_t device_save = device; + + char *tmp = (key) ? SettingsText(SET_MQTT_SWITCH_TOPIC) : SettingsText(SET_MQTT_BUTTON_TOPIC); + Format(key_topic, tmp, sizeof(key_topic)); + if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { + if (!key && (device > devices_present)) { + device = 1; + } + GetTopic_P(stopic, CMND, key_topic, + GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); + if (CLEAR_RETAIN == state) { + mqtt_data[0] = '\0'; + } else { + if ((Settings.flag3.button_switch_force_local || + !strcmp(mqtt_topic, key_topic) || + !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) && + (POWER_TOGGLE == state)) { + state = ~(power >> (device -1)) &1; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); + } +#ifdef USE_DOMOTICZ + if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { +#endif + MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain + : Settings.flag.mqtt_button_retain) && + (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); +#ifdef USE_DOMOTICZ + } +#endif + result = !Settings.flag3.button_switch_force_local; + } else { + Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); + result = XdrvRulesProcess(); + } +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type && !result) { +#endif + int32_t payload_save = XdrvMailbox.payload; + XdrvMailbox.payload = device_save << 24 | key << 16 | state << 8 | device; + XdrvCall(FUNC_ANY_KEY); + XdrvMailbox.payload = payload_save; +#ifdef USE_PWM_DIMMER + result = true; + } +#endif + return result; +} + +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) +{ +# 520 "/workspace/Tasmota/tasmota/support_tasmota.ino" +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + blink_mask &= 1; + Settings.flag.interlock = 0; + Settings.pulse_timer[1] = 0; + Settings.pulse_timer[2] = 0; + Settings.pulse_timer[3] = 0; + } +#endif + + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; + publish_power = false; + } + + if ((device < 1) || (device > devices_present)) { + device = 1; + } + active_device = device; + + SetPulseTimer((device -1) % MAX_PULSETIMERS, 0); + + power_t mask = 1 << (device -1); + if (state <= POWER_TOGGLE) { + if ((blink_mask & mask)) { + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(device); + } + + if (Settings.flag.interlock && + !interlock_mutex && + ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) + ) { + interlock_mutex = true; + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i] & mask) { + for (uint32_t j = 0; j < devices_present; j++) { + power_t imask = 1 << j; + if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { + ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); + delay(50); + } + } + break; + } + } + interlock_mutex = false; + } + + switch (state) { + case POWER_OFF: { + power &= (POWER_MASK ^ mask); + break; } + case POWER_ON: + power |= mask; + break; + case POWER_TOGGLE: + power ^= mask; + } +#ifdef USE_DEVICE_GROUPS + if (SRC_REMOTE != source && SRC_RETRY != source) { + if (Settings.flag4.multiple_device_groups) + SendDeviceGroupMessage(device - 1, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, (power >> (device - 1)) & 1 | 0x01000000); + else + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, power); + } +#endif + SetDevicePower(power, source); +#ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(device); +#endif +#ifdef USE_KNX + KnxUpdatePowerState(device, power); +#endif + if (publish_power && Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + + + SetPulseTimer((device -1) % MAX_PULSETIMERS, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[(device -1) % MAX_PULSETIMERS] : 0); + } + else if (POWER_BLINK == state) { + if (!(blink_mask & mask)) { + blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); + blink_power = (power >> (device -1))&1; + } + blink_timer = millis() + 100; + blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1; + blink_mask |= mask; + MqttPublishPowerBlinkState(device); + return; + } + else if (POWER_BLINK_STOP == state) { + bool flag = (blink_mask & mask); + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(device); + if (flag) { + ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); + } + return; + } + if (publish_power) { + MqttPublishPowerState(device); + } +} + +void StopAllPowerBlink(void) +{ + power_t mask; + + for (uint32_t i = 1; i <= devices_present; i++) { + mask = 1 << (i -1); + if (blink_mask & mask) { + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(i); + ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); + } + } +} + +void MqttShowPWMState(void) +{ + ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); + bool first = true; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (PinUsed(GPIO_PWM1, i)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); + first = false; + } + } + ResponseJsonEnd(); +} + +void MqttShowState(void) +{ + char stemp1[TOPSZ]; + + ResponseAppendTime(); + ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime()); + +#ifdef ESP8266 +#ifdef USE_ADC_VCC + dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); + ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); +#endif +#endif + + ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), + ESP_getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), + ssleep, loop_load_avg, MqttConnectCount()); + + for (uint32_t i = 1; i <= devices_present; i++) { +#ifdef USE_LIGHT + if ((LightDevice()) && (i >= LightDevice())) { + if (i == LightDevice()) { ResponseLightState(1); } + } else { +#endif + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), + GetStateText(bitRead(power, i-1))); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); + break; + } +#endif +#ifdef USE_LIGHT + } +#endif + } + + if (pwm_present) { + ResponseAppend_P(PSTR(",")); + MqttShowPWMState(); + } + + if (!global_state.wifi_down) { + int32_t rssi = WiFi.RSSI(); + ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_SIGNAL "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}"), + Settings.sta_active +1, EscapeJSONString(SettingsText(SET_STASSID1 + Settings.sta_active)).c_str(), WiFi.BSSIDstr().c_str(), WiFi.channel(), + WifiGetRssiAsQuality(rssi), rssi, WifiLinkCount(), WifiDowntime().c_str()); + } + + ResponseJsonEnd(); +} + +void MqttPublishTeleState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); +#if defined(USE_RULES) || defined(USE_SCRIPT) + RulesTeleperiod(); +#endif +} + +void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperature, float f_humidity) +{ + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), types); + ResponseAppendTHD(f_temperature, f_humidity); + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (pass_on) { + DomoticzTempHumPressureSensor(f_temperature, f_humidity); + } +#endif +#ifdef USE_KNX + if (pass_on) { + KnxSensor(KNX_TEMPERATURE, f_temperature); + KnxSensor(KNX_HUMIDITY, f_humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD(types, f_temperature, f_humidity); +#endif + } +} + +bool MqttShowSensor(void) +{ + ResponseAppendTime(); + + int json_data_start = strlen(mqtt_data); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { +#else + if (PinUsed(GPIO_SWT1, i)) { +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); + } + } + XsnsCall(FUNC_JSON_APPEND); + XdrvCall(FUNC_JSON_APPEND); + + bool json_data_available = (strlen(mqtt_data) - json_data_start); + if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); + } + if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); + } + if ((strstr_P(mqtt_data, PSTR(D_JSON_SPEED)) != nullptr) && Settings.flag2.speed_conversion) { + ResponseAppend_P(PSTR(",\"" D_JSON_SPEED_UNIT "\":\"%s\""), SpeedUnit().c_str()); + } + ResponseJsonEnd(); + + if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } + return json_data_available; +} + +void MqttPublishSensor(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishTeleSensor(); + } +} +# 788 "/workspace/Tasmota/tasmota/support_tasmota.ino" +void PerformEverySecond(void) +{ + uptime++; + + if (POWER_CYCLE_TIME == uptime) { + UpdateQuickPowerCycle(false); + } + + if (BOOT_LOOP_TIME == uptime) { + RtcRebootReset(); + +#ifdef USE_DEEPSLEEP + if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) { +#endif + Settings.bootcount++; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); +#ifdef USE_DEEPSLEEP + } +#endif + } + + if (mqtt_cmnd_blocked_reset) { + mqtt_cmnd_blocked_reset--; + if (!mqtt_cmnd_blocked_reset) { + mqtt_cmnd_blocked = 0; + } + } + + if (seriallog_timer) { + seriallog_timer--; + if (!seriallog_timer) { + if (seriallog_level) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED)); + } + seriallog_level = 0; + } + } + + if (syslog_timer) { + syslog_timer--; + if (!syslog_timer) { + syslog_level = Settings.syslog_level; + if (Settings.syslog_level) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); + } + } + } + + ResetGlobalValues(); + + if (Settings.tele_period) { + if (tele_period >= 9999) { + if (!global_state.network_down) { + tele_period = 0; + } + } else { + tele_period++; + if (tele_period >= Settings.tele_period) { + tele_period = 0; + + MqttPublishTeleState(); + + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#if defined(USE_RULES) || defined(USE_SCRIPT) + RulesTeleperiod(); +#endif + } + + XsnsCall(FUNC_AFTER_TELEPERIOD); + XdrvCall(FUNC_AFTER_TELEPERIOD); + } + } + } + + + wifiKeepAlive(); + +#ifdef ESP32 + if (11 == uptime) { + ESP_getSketchSize(); + } +#endif +} + + + + + +void Every100mSeconds(void) +{ + + power_t power_now; + + if (prepped_loglevel) { + AddLog(prepped_loglevel); + } + + if (latching_relay_pulse) { + latching_relay_pulse--; + if (!latching_relay_pulse) SetLatchingRelay(0, 0); + } + + for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { + if (pulse_timer[i] != 0L) { + if (TimeReached(pulse_timer[i])) { + pulse_timer[i] = 0L; + for (uint32_t j = 0; j < devices_present; j = j +MAX_PULSETIMERS) { + ExecuteCommandPower(i + j +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER); + } + } + } + } + + if (blink_mask) { + if (TimeReached(blink_timer)) { + SetNextTimeInterval(blink_timer, 100 * Settings.blinktime); + blink_counter--; + if (!blink_counter) { + StopAllPowerBlink(); + } else { + blink_power ^= 1; + power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0); + SetDevicePower(power_now, SRC_IGNORE); + } + } + } +} + + + + + +void Every250mSeconds(void) +{ + + + uint32_t blinkinterval = 1; + + state_250mS++; + state_250mS &= 0x3; + + global_state.network_down = (global_state.wifi_down && global_state.eth_down) ? 1 : 0; + + if (!Settings.flag.global_state) { + if (global_state.data &0x03) { + if (global_state.mqtt_down) { blinkinterval = 7; } + if (global_state.network_down) { blinkinterval = 3; } + blinks = 201; + } + } + if (blinks || restart_flag || ota_state_flag) { + if (restart_flag || ota_state_flag) { + blinkstate = true; + } else { + blinkspeed--; + if (!blinkspeed) { + blinkspeed = blinkinterval; + blinkstate ^= 1; + } + } + if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { + SetLedLink(blinkstate); + } + if (!blinkstate) { + blinks--; + if (200 == blinks) blinks = 0; + } + } + if (Settings.ledstate &1 && (PinUsed(GPIO_LEDLNK) || !(blinks || restart_flag || ota_state_flag)) ) { + bool tstate = power & Settings.ledmask; +#ifdef ESP8266 + if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { + tstate = (!power) ? 1 : 0; + } +#endif + SetLedPower(tstate); + } + + + + + + switch (state_250mS) { + case 0: + if (ota_state_flag && BACKLOG_EMPTY) { + ota_state_flag--; + if (2 == ota_state_flag) { + RtcSettings.ota_loader = 0; + ota_retry_counter = OTA_ATTEMPTS; + ESPhttpUpdate.rebootOnUpdate(false); + SettingsSave(1); + } + if (ota_state_flag <= 0) { +#ifdef USE_COUNTER + CounterInterruptDisable(true); +#endif +#ifdef USE_WEBSERVER + if (Settings.webserver) StopWebserver(); +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + ota_state_flag = 92; + ota_result = 0; + ota_retry_counter--; + if (ota_retry_counter) { + strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data)); +#ifndef FIRMWARE_MINIMAL + if (RtcSettings.ota_loader) { +# 1015 "/workspace/Tasmota/tasmota/support_tasmota.ino" + char *bch = strrchr(mqtt_data, '/'); + if (bch == nullptr) { bch = mqtt_data; } + char *ech = strchr(bch, '.'); + if (ech == nullptr) { ech = mqtt_data + strlen(mqtt_data); } + + + + char ota_url_type[strlen(ech) +1]; + strncpy(ota_url_type, ech, sizeof(ota_url_type)); + + char *pch = strrchr(bch, '-'); + if (pch == nullptr) { pch = ech; } + *pch = '\0'; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ota_url_type); + } +#endif + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data); + WiFiClient OTAclient; + ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data)); + if (!ota_result) { +#ifndef FIRMWARE_MINIMAL + int ota_error = ESPhttpUpdate.getLastError(); + DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error); + if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { + RtcSettings.ota_loader = 1; + } +#endif + ota_state_flag = 2; + } + } + } + if (90 == ota_state_flag) { + ota_state_flag = 0; + Response_P(PSTR("{\"" D_CMND_UPGRADE "\":\"")); + if (ota_result) { + + if (!VersionCompatible()) { + ResponseAppend_P(PSTR(D_JSON_FAILED " " D_UPLOAD_ERR_14)); + } else { + ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); + restart_flag = 2; + } + } else { + ResponseAppend_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); + } + ResponseAppend_P(PSTR("\"}")); + + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); +#ifdef USE_COUNTER + CounterInterruptDisable(false); +#endif + } + } + break; + case 1: + if (MidnightNow()) { + XsnsCall(FUNC_SAVE_AT_MIDNIGHT); + } + if (save_data_counter && BACKLOG_EMPTY) { + save_data_counter--; + if (save_data_counter <= 0) { + if (Settings.flag.save_state) { + power_t mask = POWER_MASK; + for (uint32_t i = 0; i < devices_present; i++) { + if ((Settings.pulse_timer[i % MAX_PULSETIMERS] > 0) && (Settings.pulse_timer[i % MAX_PULSETIMERS] < 30)) { + mask &= ~(1 << i); + } + } + if (!((Settings.power &mask) == (power &mask))) { + Settings.power = power; + } + } else { + Settings.power = 0; + } + if (!restart_flag) { SettingsSave(0); } + save_data_counter = Settings.save_data; + } + } + if (restart_flag && BACKLOG_EMPTY) { + if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { + + char storage_ssid1[strlen(SettingsText(SET_STASSID1)) +1]; + strncpy(storage_ssid1, SettingsText(SET_STASSID1), sizeof(storage_ssid1)); + char storage_ssid2[strlen(SettingsText(SET_STASSID2)) +1]; + strncpy(storage_ssid2, SettingsText(SET_STASSID2), sizeof(storage_ssid2)); + char storage_pass1[strlen(SettingsText(SET_STAPWD1)) +1]; + strncpy(storage_pass1, SettingsText(SET_STAPWD1), sizeof(storage_pass1)); + char storage_pass2[strlen(SettingsText(SET_STAPWD2)) +1]; + strncpy(storage_pass2, SettingsText(SET_STAPWD2), sizeof(storage_pass2)); + + char storage_mqtthost[strlen(SettingsText(SET_MQTT_HOST)) +1]; + strncpy(storage_mqtthost, SettingsText(SET_MQTT_HOST), sizeof(storage_mqtthost)); + char storage_mqttuser[strlen(SettingsText(SET_MQTT_USER)) +1]; + strncpy(storage_mqttuser, SettingsText(SET_MQTT_USER), sizeof(storage_mqttuser)); + char storage_mqttpwd[strlen(SettingsText(SET_MQTT_PWD)) +1]; + strncpy(storage_mqttpwd, SettingsText(SET_MQTT_PWD), sizeof(storage_mqttpwd)); + char storage_mqtttopic[strlen(SettingsText(SET_MQTT_TOPIC)) +1]; + strncpy(storage_mqtttopic, SettingsText(SET_MQTT_TOPIC), sizeof(storage_mqtttopic)); + uint16_t mqtt_port = Settings.mqtt_port; + + + + + if ((215 == restart_flag) || (216 == restart_flag)) { + SettingsErase(0); + } + SettingsDefault(); + + SettingsUpdateText(SET_STASSID1, storage_ssid1); + SettingsUpdateText(SET_STASSID2, storage_ssid2); + SettingsUpdateText(SET_STAPWD1, storage_pass1); + SettingsUpdateText(SET_STAPWD2, storage_pass2); + if (216 == restart_flag) { + + SettingsUpdateText(SET_MQTT_HOST, storage_mqtthost); + SettingsUpdateText(SET_MQTT_USER, storage_mqttuser); + SettingsUpdateText(SET_MQTT_PWD, storage_mqttpwd); + SettingsUpdateText(SET_MQTT_TOPIC, storage_mqtttopic); + Settings.mqtt_port = mqtt_port; + } + restart_flag = 2; + } + else if (213 == restart_flag) { + SettingsSdkErase(); + restart_flag = 2; + } + else if (212 == restart_flag) { + SettingsErase(0); + restart_flag = 211; + } + if (211 == restart_flag) { + SettingsDefault(); + restart_flag = 2; + } + if (2 == restart_flag) { + SettingsSaveAll(); + } + restart_flag--; + if (restart_flag <= 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "%s"), (restart_halt) ? "Halted" : D_RESTARTING); + EspRestart(); + } + } + break; + case 2: + if (Settings.flag4.network_wifi) { + WifiCheck(wifi_state_flag); + wifi_state_flag = WIFI_RESTART; + } + break; + case 3: + if (!global_state.network_down) { +#ifdef FIRMWARE_MINIMAL + if (1 == RtcSettings.ota_loader) { + RtcSettings.ota_loader = 0; + ota_state_flag = 3; + } +#endif + +#ifdef USE_DISCOVERY + StartMdns(); +#endif + +#ifdef USE_WEBSERVER + if (Settings.webserver) { + +#ifdef ESP8266 + StartWebserver(Settings.webserver, WiFi.localIP()); +#else +#ifdef USE_ETHERNET + StartWebserver(Settings.webserver, (EthernetLocalIP()) ? EthernetLocalIP() : WiFi.localIP()); +#else + StartWebserver(Settings.webserver, WiFi.localIP()); +#endif +#endif + +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + MdnsAddServiceHttp(); +#endif +#endif + } else { + StopWebserver(); + } +#ifdef USE_EMULATION + if (Settings.flag2.emulation) { UdpConnect(); } +#endif +#endif + +#ifdef USE_DEVICE_GROUPS + DeviceGroupsStart(); +#endif + +#ifdef USE_KNX + if (!knx_started && Settings.flag.knx_enabled) { + KNXStart(); + knx_started = true; + } +#endif + + MqttCheck(); + } else { +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + +#ifdef USE_DEVICE_GROUPS + DeviceGroupsStop(); +#endif + +#ifdef USE_KNX + knx_started = false; +#endif + } + break; + } +} + +#ifdef USE_ARDUINO_OTA + + + + + + + +bool arduino_ota_triggered = false; +uint16_t arduino_ota_progress_dot_count = 0; + +void ArduinoOTAInit(void) +{ + ArduinoOTA.setPort(8266); + ArduinoOTA.setHostname(NetworkHostname()); + if (strlen(SettingsText(SET_WEBPWD))) { + ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); + } + + ArduinoOTA.onStart([]() + { + SettingsSave(1); +#ifdef USE_WEBSERVER + if (Settings.webserver) { StopWebserver(); } +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); + arduino_ota_triggered = true; + arduino_ota_progress_dot_count = 0; + delay(100); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) + { + if ((LOG_LEVEL_DEBUG <= seriallog_level)) { + arduino_ota_progress_dot_count++; + Serial.printf("."); + if (!(arduino_ota_progress_dot_count % 80)) { Serial.println(); } + } + }); + + ArduinoOTA.onError([](ota_error_t error) + { + + + + + char error_str[100]; + + if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) { Serial.println(); } + switch (error) { + case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break; + case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break; + case OTA_END_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_7), sizeof(error_str)); break; + default: + snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); + EspRestart(); + }); + + ArduinoOTA.onEnd([]() + { + if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); + EspRestart(); + }); + + ArduinoOTA.begin(); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); +} + +void ArduinoOtaLoop(void) +{ + MDNS.update(); + ArduinoOTA.handle(); + + while (arduino_ota_triggered) { ArduinoOTA.handle(); } +} +#endif + + + +void SerialInput(void) +{ + while (Serial.available()) { + + delay(0); + serial_in_byte = Serial.read(); + + if (0 == serial_in_byte_counter) { + serial_buffer_overrun = false; + } + else if ((serial_in_byte_counter == INPUT_BUFFER_SIZE) +#ifdef ESP8266 + || Serial.hasOverrun() +#endif + ) { + serial_buffer_overrun = true; + } + +#ifdef ESP8266 + + + + if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + serial_in_byte = ButtonSerial(serial_in_byte); + } +#endif + + + if (XdrvCall(FUNC_SERIAL)) { + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + + + + if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + if (!Settings.flag.mqtt_serial) { + if (isprint(serial_in_byte)) { + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } else { + serial_buffer_overrun = true; + } + } + } else { + if (serial_in_byte || Settings.flag.mqtt_serial_raw) { + bool in_byte_is_delimiter = + (((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || + ((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && + !Settings.flag.mqtt_serial_raw; + + if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && + !in_byte_is_delimiter) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } + + if ((serial_in_byte_counter >= INPUT_BUFFER_SIZE -1) || + in_byte_is_delimiter) { + serial_polling_window = 0; + break; + } + + serial_polling_window = millis(); + } + } + +#ifdef USE_SONOFF_SC + + + + if (SONOFF_SC == my_module_type) { + if (serial_in_byte == '\x1B') { + serial_in_buffer[serial_in_byte_counter] = 0; + SonoffScSerialInput(serial_in_buffer); + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + } else +#endif + + + if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { + serial_in_buffer[serial_in_byte_counter] = 0; + seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; + if (serial_buffer_overrun) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "Serial buffer overrun")); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); + ExecuteCommand(serial_in_buffer, SRC_SERIAL); + } + serial_in_byte_counter = 0; + serial_polling_window = 0; + Serial.flush(); + return; + } + } + + if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { + serial_in_buffer[serial_in_byte_counter] = 0; + bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); + + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":")); + if (assume_json) { + ResponseAppend_P(serial_in_buffer); + } else { + ResponseAppend_P(PSTR("\"")); + if (Settings.flag.mqtt_serial_raw) { + char hex_char[(serial_in_byte_counter * 2) + 2]; + ResponseAppend_P(ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char))); + } else { + ResponseAppend_P(EscapeJSONString(serial_in_buffer).c_str()); + } + ResponseAppend_P(PSTR("\"")); + } + ResponseJsonEnd(); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); + serial_in_byte_counter = 0; + } +} + + + +void ResetPwm(void) +{ + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (PinUsed(GPIO_PWM1, i)) { + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); + + } + } +} + + + +void GpioInit(void) +{ + if (!ValidModule(Settings.module)) { + uint32_t module = MODULE; + if (!ValidModule(MODULE)) { +#ifdef ESP8266 + module = SONOFF_BASIC; +#endif +#ifdef ESP32 + module = WEMOS; +#endif + } + + Settings.module = module; + Settings.last_module = module; + } + SetModuleType(); + + if (Settings.module != Settings.last_module) { + Settings.baudrate = APP_BAUDRATE / 300; + Settings.serial_config = TS_SERIAL_8N1; + SetSerialBegin(); + } + + + +#ifdef ESP8266 + ConvertGpios(); +#endif + + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + if ((Settings.user_template.gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.user_template.gp.io[i] < AGPIO(GPIO_USER))) { + Settings.user_template.gp.io[i] = AGPIO(GPIO_USER); + } + } + + myio def_gp; + ModuleGpios(&def_gp); + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if ((Settings.my_gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.my_gp.io[i] < AGPIO(GPIO_USER))) { + Settings.my_gp.io[i] = GPIO_NONE; + } + else if (Settings.my_gp.io[i] > GPIO_NONE) { + my_module.io[i] = Settings.my_gp.io[i]; + } + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < AGPIO(GPIO_USER))) { + my_module.io[i] = def_gp.io[i]; + } + } + + for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { + uint32_t mpin = ValidPin(i, my_module.io[i]); + + DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); + + if (mpin) { + XdrvMailbox.index = mpin; + XdrvMailbox.payload = i; + + if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES))) { + SwitchPullupFlag(mpin - AGPIO(GPIO_SWT1_NP)); + mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); + } + else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_NP)); + mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); + } + else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS))) { + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV)); + mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); + } + else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); + mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); + } +#ifdef ESP32 + else if ((mpin >= AGPIO(GPIO_KEY1_TC)) && (mpin < (AGPIO(GPIO_KEY1_TC) + MAX_KEYS))) { + ButtonTouchFlag(mpin - AGPIO(GPIO_KEY1_TC)); + mpin -= (AGPIO(GPIO_KEY1_TC) - AGPIO(GPIO_KEY1)); + } +#endif + else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS))) { + bitSet(rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); + mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); + } + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { + bitSet(led_inverted, mpin - AGPIO(GPIO_LED1_INV)); + mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); + } + else if (mpin == AGPIO(GPIO_LEDLNK_INV)) { + ledlnk_inverted = 1; + mpin -= (AGPIO(GPIO_LEDLNK_INV) - AGPIO(GPIO_LEDLNK)); + } + else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) { + bitSet(pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); + mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1)); + } + else if (XdrvCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; + } + else if (XsnsCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; + }; + } + if (mpin) { SetPin(i, mpin); } + } + + + + analogWriteRange(Settings.pwm_range); + analogWriteFreq(Settings.pwm_frequency); + +#ifdef ESP8266 + if ((2 == Pin(GPIO_TXD)) || (H801 == my_module_type)) { Serial.set_tx(2); } + +#ifdef USE_SPI + spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); + if (spi_flg) { + my_module.io[12] = GPIO_SPI_MISO; + SetPin(12, GPIO_SPI_MISO); + my_module.io[13] = GPIO_SPI_MOSI; + SetPin(13, GPIO_SPI_MOSI); + my_module.io[14] = GPIO_SPI_CLK; + SetPin(14, GPIO_SPI_CLK); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK)")); + } +#endif +#else +#ifdef USE_SPI + if (PinUsed(GPIO_SPI_CS) || PinUsed(GPIO_SPI_DC)) { + if ((15 == Pin(GPIO_SPI_CS)) && (!GetPin(12) && !GetPin(13) && !GetPin(14))) { + my_module.io[12] = AGPIO(GPIO_SPI_MISO); + SetPin(12, AGPIO(GPIO_SPI_MISO)); + my_module.io[13] = AGPIO(GPIO_SPI_MOSI); + SetPin(13, AGPIO(GPIO_SPI_MOSI)); + my_module.io[14] = AGPIO(GPIO_SPI_CLK); + SetPin(14, AGPIO(GPIO_SPI_CLK)); + } + else if ((5 == Pin(GPIO_SPI_CS)) && (!GetPin(19) && !GetPin(23) && !GetPin(18))) { + my_module.io[19] = AGPIO(GPIO_SPI_MISO); + SetPin(19, AGPIO(GPIO_SPI_MISO)); + my_module.io[23] = AGPIO(GPIO_SPI_MOSI); + SetPin(23, AGPIO(GPIO_SPI_MOSI)); + my_module.io[18] = AGPIO(GPIO_SPI_CLK); + SetPin(18, AGPIO(GPIO_SPI_CLK)); + } + else if ((12 == Pin(GPIO_SPI_MISO)) || (13 == Pin(GPIO_SPI_MOSI)) || (14 == Pin(GPIO_SPI_CLK))) { + my_module.io[12] = AGPIO(GPIO_SPI_MISO); + SetPin(12, AGPIO(GPIO_SPI_MISO)); + my_module.io[13] = AGPIO(GPIO_SPI_MOSI); + SetPin(13, AGPIO(GPIO_SPI_MOSI)); + my_module.io[14] = AGPIO(GPIO_SPI_CLK); + SetPin(14, AGPIO(GPIO_SPI_CLK)); + } + else if ((19 == Pin(GPIO_SPI_MISO)) || (23 == Pin(GPIO_SPI_MOSI)) || (18 == Pin(GPIO_SPI_CLK))) { + my_module.io[19] = AGPIO(GPIO_SPI_MISO); + SetPin(19, AGPIO(GPIO_SPI_MISO)); + my_module.io[23] = AGPIO(GPIO_SPI_MOSI); + SetPin(23, AGPIO(GPIO_SPI_MOSI)); + my_module.io[18] = AGPIO(GPIO_SPI_CLK); + SetPin(18, AGPIO(GPIO_SPI_CLK)); + } + spi_flg = (PinUsed(GPIO_SPI_CLK) && (PinUsed(GPIO_SPI_MOSI) || PinUsed(GPIO_SPI_MISO))); + if (spi_flg) { + if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MISO), GPIO%02d(MOSI) and GPIO%02d(CLK)"), + Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_CLK)); + } + else if (PinUsed(GPIO_SPI_MOSI)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MOSI) and GPIO%02d(CLK)"), + Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_CLK)); + } + else if (PinUsed(GPIO_SPI_MISO)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MISO) and GPIO%02d(CLK)"), + Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_CLK)); + } + } + } +#endif +#endif + soft_spi_flg = (PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); + + for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { + uint32_t mpin = ValidPin(i, my_module.io[i]); + + if (AGPIO(GPIO_OUTPUT_HI) == mpin) { + pinMode(i, OUTPUT); + digitalWrite(i, 1); + } + else if (AGPIO(GPIO_OUTPUT_LO) == mpin) { + pinMode(i, OUTPUT); + digitalWrite(i, 0); + } + + + else if (((i < 6) || (i > 11)) && (GPIO_NONE == mpin)) { + if (!((1 == i) || (3 == i))) { + pinMode(i, INPUT); + } + } + } + +#ifdef USE_I2C + i2c_flg = (PinUsed(GPIO_I2C_SCL) && PinUsed(GPIO_I2C_SDA)); + if (i2c_flg) { + Wire.begin(Pin(GPIO_I2C_SDA), Pin(GPIO_I2C_SCL)); + } +#endif + + devices_present = 0; + light_type = LT_BASIC; + + XsnsCall(FUNC_MODULE_INIT); + + if (XdrvCall(FUNC_MODULE_INIT)) { + + } +#ifdef ESP8266 + else if (YTF_IR_BRIDGE == my_module_type) { + ClaimSerial(); + + } + else if (SONOFF_DUAL == my_module_type) { + devices_present = 2; + SetSerial(19200, TS_SERIAL_8N1); + } + else if (CH4 == my_module_type) { + devices_present = 4; + SetSerial(19200, TS_SERIAL_8N1); + } +#ifdef USE_SONOFF_SC + else if (SONOFF_SC == my_module_type) { + SetSerial(19200, TS_SERIAL_8N1); + } +#endif +#endif + + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (PinUsed(GPIO_PWM1, i)) { +#ifdef ESP8266 + pinMode(Pin(GPIO_PWM1, i), OUTPUT); +#else + analogAttach(Pin(GPIO_PWM1, i), i); +#endif + if (light_type) { + + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); + } else { + pwm_present = true; + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } + } + + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (PinUsed(GPIO_REL1, i)) { + pinMode(Pin(GPIO_REL1, i), OUTPUT); + devices_present++; +#ifdef ESP8266 + if (EXS_RELAY == my_module_type) { + digitalWrite(Pin(GPIO_REL1, i), bitRead(rel_inverted, i) ? 1 : 0); + if (i &1) { devices_present--; } + } +#endif + } + } + + for (uint32_t i = 0; i < MAX_LEDS; i++) { + if (PinUsed(GPIO_LED1, i)) { +#ifdef USE_ARILUX_RF + if ((3 == i) && (leds_present < 2) && !PinUsed(GPIO_ARIRFSEL)) { + SetPin(Pin(GPIO_LED1, i), AGPIO(GPIO_ARIRFSEL)); + } else { +#endif + pinMode(Pin(GPIO_LED1, i), OUTPUT); + leds_present++; + digitalWrite(Pin(GPIO_LED1, i), bitRead(led_inverted, i)); +#ifdef USE_ARILUX_RF + } +#endif + } + } + if (PinUsed(GPIO_LEDLNK)) { + pinMode(Pin(GPIO_LEDLNK), OUTPUT); + digitalWrite(Pin(GPIO_LEDLNK), ledlnk_inverted); + } + +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type && PinUsed(GPIO_REL1)) { devices_present--; } +#endif + + ButtonInit(); + SwitchInit(); +#ifdef ROTARY_V1 + RotaryInit(); +#endif + + SetLedPower(Settings.ledstate &8); + SetLedLink(Settings.ledstate &8); + + XdrvCall(FUNC_PRE_INIT); + XsnsCall(FUNC_PRE_INIT); +} +# 1 "/workspace/Tasmota/tasmota/support_udp.ino" +# 20 "/workspace/Tasmota/tasmota/support_udp.ino" +#ifdef USE_EMULATION + +#ifndef UDP_BUFFER_SIZE +#define UDP_BUFFER_SIZE 120 +#endif +#define UDP_MSEARCH_SEND_DELAY 1500 + +#include +Ticker TickerMSearch; + +IPAddress udp_remote_ip; +uint16_t udp_remote_port; + +bool udp_connected = false; +bool udp_response_mutex = false; + +#ifdef ESP8266 +#ifndef UDP_MAX_PACKETS +#define UDP_MAX_PACKETS 3 +#endif + +#include "UdpListener.h" +UdpListener UdpCtx(UDP_MAX_PACKETS); +#endif + + + + + +const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; +const char URN_BELKIN_DEVICE_CAP[] PROGMEM = "urn:Belkin:device:**"; +const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; +const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; +const char SSDP_ALL[] PROGMEM = "ssdp:all"; + + + + + +bool UdpDisconnect(void) +{ + if (udp_connected) { + + PortUdp.flush(); +#ifdef ESP8266 + UdpCtx.disconnect(); +#endif +#ifdef USE_DEVICE_GROUPS + + PortUdp.stop(); +#else + + WiFiUDP::stopAll(); +#endif + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); + udp_connected = false; + } + return udp_connected; +} + +bool UdpConnect(void) +{ + if (!udp_connected && !restart_flag) { + +#ifdef ESP8266 + UdpCtx.reset(); + if (igmp_joingroup(WiFi.localIP(), IPAddress(239,255,255,250)) == ERR_OK) { + ip_addr_t addr = IPADDR4_INIT(INADDR_ANY); + if (UdpCtx.listen(&addr, 1900)) { + + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); + udp_response_mutex = false; + udp_connected = true; + } +#else + if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); + udp_response_mutex = false; + udp_connected = true; +#endif + } + if (!udp_connected) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); + } + } + return udp_connected; +} + +void PollUdp(void) +{ + if (udp_connected) { +#ifdef ESP8266 + while (UdpCtx.next()) { + UdpPacket *packet; + packet = UdpCtx.read(); + if (packet->len >= UDP_BUFFER_SIZE) { + packet->len--; + } + packet->buf[packet->len] = 0; + char * packet_buffer = (char*) &packet->buf; + int32_t len = packet->len; +#else + while (PortUdp.parsePacket()) { + char packet_buffer[UDP_BUFFER_SIZE]; + + int32_t len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1); + packet_buffer[len] = 0; +#endif + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len); + + + + if (Settings.flag2.emulation) { +#if defined(USE_SCRIPT_HUE) || defined(USE_ZIGBEE) + if (!udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#else + if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#endif + udp_response_mutex = true; + +#ifdef ESP8266 + udp_remote_ip = packet->srcaddr; + udp_remote_port = packet->srcport; +#else + udp_remote_ip = PortUdp.remoteIP(); + udp_remote_port = PortUdp.remotePort(); +#endif + + + + + uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); + + LowerCase(packet_buffer, packet_buffer); + RemoveSpace(packet_buffer); + +#ifdef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { + if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1); + return; + } + else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2); + return; + } + } +#endif + +#ifdef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { + if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) || + (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, HueRespondToMSearch); + return; + } + } +#endif + + udp_response_mutex = false; + continue; + } + } + } + optimistic_yield(100); + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/support_wifi.ino" +# 29 "/workspace/Tasmota/tasmota/support_wifi.ino" +#ifndef WIFI_RSSI_THRESHOLD +#define WIFI_RSSI_THRESHOLD 10 +#endif +#ifndef WIFI_RESCAN_MINUTES +#define WIFI_RESCAN_MINUTES 44 +#endif + +const uint8_t WIFI_CONFIG_SEC = 180; +const uint8_t WIFI_CHECK_SEC = 20; +const uint8_t WIFI_RETRY_OFFSET_SEC = 12; + +#include +#if LWIP_IPV6 +#include +#endif + +struct WIFI { + uint32_t last_event = 0; + uint32_t downtime = 0; + uint16_t link_count = 0; + uint8_t counter; + uint8_t retry_init; + uint8_t retry; + uint8_t status; + uint8_t config_type = 0; + uint8_t config_counter = 0; + uint8_t scan_state; + uint8_t bssid[6]; + int8_t best_network_db; +} Wifi; + +int WifiGetRssiAsQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) { + quality = 0; + } else if (rssi >= -50) { + quality = 100; + } else { + quality = 2 * (rssi + 100); + } + return quality; +} + +bool WifiConfigCounter(void) +{ + if (Wifi.config_counter) { + Wifi.config_counter = WIFI_CONFIG_SEC; + } + return (Wifi.config_counter); +} + +void WifiConfig(uint8_t type) +{ + if (!Wifi.config_type) { + if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + WiFi.disconnect(); + Wifi.config_type = type; + +#ifndef USE_WEBSERVER + if (WIFI_MANAGER == Wifi.config_type) { + Wifi.config_type = WIFI_SERIAL; + } +#endif + + Wifi.config_counter = WIFI_CONFIG_SEC; + Wifi.counter = Wifi.config_counter +5; + blinks = 1999; + if (WIFI_RESTART == Wifi.config_type) { + restart_flag = 2; + } + else if (WIFI_SERIAL == Wifi.config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); + } +#ifdef USE_WEBSERVER + else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); + WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type); + } +#endif + } +} + +void WifiSetMode(WiFiMode_t wifi_mode) +{ + if (WiFi.getMode() == wifi_mode) { return; } + + if (wifi_mode != WIFI_OFF) { + + WiFi.forceSleepWake(); + delay(100); + } + + uint32_t retry = 2; + while (!WiFi.mode(wifi_mode) && retry--) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Retry set Mode...")); + delay(100); + } + + if (wifi_mode == WIFI_OFF) { + delay(1000); + WiFi.forceSleepBegin(); + delay(1); + } else { + delay(30); + } +} + +void WiFiSetSleepMode(void) +{ +# 156 "/workspace/Tasmota/tasmota/support_wifi.ino" + if (ssleep && Settings.flag3.sleep_normal) { + WiFi.setSleepMode(WIFI_LIGHT_SLEEP); + } else { + WiFi.setSleepMode(WIFI_MODEM_SLEEP); + } + WifiSetOutputPower(); +} + +void WifiBegin(uint8_t flag, uint8_t channel) +{ + const char kWifiPhyMode[] = " BGN"; + +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + + WiFi.persistent(false); + WiFi.disconnect(true); + delay(200); + + WifiSetMode(WIFI_STA); + WiFiSetSleepMode(); + + + if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } + + switch (flag) { + case 0: + case 1: + Settings.sta_active = flag; + break; + case 2: + Settings.sta_active ^= 1; + } + if (!strlen(SettingsText(SET_STASSID1 + Settings.sta_active))) { + Settings.sta_active ^= 1; + } + if (Settings.ip_address[0]) { + WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); + } + WiFi.hostname(my_hostname); + + char stemp[40] = { 0 }; + if (channel) { + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid); + + char hex_char[18]; + snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':')); + } else { + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."), + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); + +#if LWIP_IPV6 + for (bool configured = false; !configured;) { + uint16_t cfgcnt = 0; + for (auto addr : addrList) { + if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); + break; + } + delay(500); + cfgcnt++; + } + } +#endif +} + +void WifiBeginAfterScan(void) +{ + + if (0 == Wifi.scan_state) { return; } + + if (1 == Wifi.scan_state) { + memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid)); + Wifi.best_network_db = -127; + Wifi.scan_state = 3; + } + + if (2 == Wifi.scan_state) { + uint8_t* bssid = WiFi.BSSID(); + memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid)); + Wifi.best_network_db = WiFi.RSSI(); + if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) { + Wifi.best_network_db += WIFI_RSSI_THRESHOLD; + } + Wifi.scan_state = 3; + } + + if (3 == Wifi.scan_state) { + if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { + WiFi.scanNetworks(true); + Wifi.scan_state++; + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR("Network (re)scan started...")); + return; + } + } + int8_t wifi_scan_result = WiFi.scanComplete(); + + if (4 == Wifi.scan_state) { + if (wifi_scan_result != WIFI_SCAN_RUNNING) { + Wifi.scan_state++; + } + } + + if (5 == Wifi.scan_state) { + int32_t channel = 0; + int8_t ap = 3; + uint8_t last_bssid[6]; + memcpy((void*) &last_bssid, (void*) &Wifi.bssid, sizeof(last_bssid)); + + if (wifi_scan_result > 0) { + + for (uint32_t i = 0; i < wifi_scan_result; ++i) { + + String ssid_scan; + int32_t rssi_scan; + uint8_t sec_scan; + uint8_t* bssid_scan; + int32_t chan_scan; + bool hidden_scan; + + WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); + + bool known = false; + uint32_t j; + for (j = 0; j < MAX_SSIDS; j++) { + if (ssid_scan == SettingsText(SET_STASSID1 + j)) { + known = true; + if (rssi_scan > Wifi.best_network_db) { + if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { + Wifi.best_network_db = (int8_t)rssi_scan; + channel = chan_scan; + ap = j; + memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid)); + } + } + break; + } + } + char hex_char[18]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"), + i, + (known) ? (j) ? '2' : '1' : '-', + ssid_scan.c_str(), + chan_scan, + ToHex_P((unsigned char*)bssid_scan, 6, hex_char, sizeof(hex_char), ':'), + rssi_scan, + (sec_scan == ENC_TYPE_NONE) ? 0 : 1); + delay(0); + } + WiFi.scanDelete(); + delay(0); + } + Wifi.scan_state = 0; + + for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) { + if (last_bssid[i] != Wifi.bssid[i]) { + WifiBegin(ap, channel); + break; + } + } + } +} + +uint16_t WifiLinkCount(void) +{ + return Wifi.link_count; +} + +String WifiDowntime(void) +{ + return GetDuration(Wifi.downtime); +} + +void WifiSetState(uint8_t state) +{ + if (state == global_state.wifi_down) { + if (state) { + rules_flag.wifi_connected = 1; + Wifi.link_count++; + Wifi.downtime += UpTime() - Wifi.last_event; + } else { + rules_flag.wifi_disconnected = 1; + Wifi.last_event = UpTime(); + } + } + global_state.wifi_down = state ^1; + if (!global_state.wifi_down) { + global_state.network_down = 0; + } +} + +#if LWIP_IPV6 +bool WifiCheckIPv6(void) +{ + bool ipv6_global=false; + + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) ipv6_global=true; + } + return ipv6_global; +} + +String WifiGetIPv6(void) +{ + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) return a.toString(); + } + return ""; +} + +bool WifiCheckIPAddrStatus(void) +{ + bool ip_global=false; + + for (auto a : addrList) { + if(!a.isLocal()) ip_global=true; + } + return ip_global; +} +#endif + +void WifiCheckIp(void) +{ +#if LWIP_IPV6 + if(WifiCheckIPAddrStatus()) { + Wifi.status = WL_CONNECTED; +#else + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { +#endif + WifiSetState(1); + Wifi.counter = WIFI_CHECK_SEC; + Wifi.retry = Wifi.retry_init; + if (Wifi.status != WL_CONNECTED) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECTED)); + + Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); + Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); + Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); + + + Settings.wifi_channel = WiFi.channel(); + uint8_t *bssid = WiFi.BSSID(); + memcpy((void*) &Settings.wifi_bssid, (void*) bssid, sizeof(Settings.wifi_bssid)); + } + Wifi.status = WL_CONNECTED; +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + MdnsUpdate(); +#endif +#endif + } else { + WifiSetState(0); + uint8_t wifi_config_tool = Settings.sta_config; + Wifi.status = WiFi.status(); + switch (Wifi.status) { + case WL_CONNECTED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); + Wifi.status = 0; + Wifi.retry = Wifi.retry_init; + break; + case WL_NO_SSID_AVAIL: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); + Settings.wifi_channel = 0; + if (WIFI_WAIT == Settings.sta_config) { + Wifi.retry = Wifi.retry_init; + } else { + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; + } + else if (Wifi.retry) { + Wifi.retry = 0; + } + } + break; + case WL_CONNECT_FAILED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); + Settings.wifi_channel = 0; + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; + } + else if (Wifi.retry) { + Wifi.retry = 0; + } + break; + default: + if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); + Settings.wifi_channel = 0; + } else { + if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) { + Settings.wifi_channel = 0; + wifi_config_tool = WIFI_MANAGER; + Wifi.retry = 0; + } else { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); + } + } + } + if (Wifi.retry) { + if (Settings.flag3.use_wifi_scan) { + if (Wifi.retry_init == Wifi.retry) { + Wifi.scan_state = 1; + } + } else { + if (Wifi.retry_init == Wifi.retry) { + WifiBegin(3, Settings.wifi_channel); + } + if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { + WifiBegin(2, 0); + } + } + Wifi.counter = 1; + Wifi.retry--; + } else { + WifiConfig(wifi_config_tool); + Wifi.counter = 1; + Wifi.retry = Wifi.retry_init; + } + } +} + +void WifiCheck(uint8_t param) +{ + Wifi.counter--; + switch (param) { + case WIFI_SERIAL: + case WIFI_MANAGER: + WifiConfig(param); + break; + default: + if (Wifi.config_counter) { + Wifi.config_counter--; + Wifi.counter = Wifi.config_counter +5; + if (Wifi.config_counter) { + if (!Wifi.config_counter) { + if (strlen(WiFi.SSID().c_str())) { + SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str()); + } + if (strlen(WiFi.psk().c_str())) { + SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str()); + } + Settings.sta_active = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1)); + } + } + if (!Wifi.config_counter) { + + restart_flag = 2; + } + } else { + if (Wifi.scan_state) { WifiBeginAfterScan(); } + + if (Wifi.counter <= 0) { + AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); + Wifi.counter = WIFI_CHECK_SEC; + WifiCheckIp(); + } +#if LWIP_IPV6 + if (WifiCheckIPAddrStatus()) { +#else + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { +#endif + WifiSetState(1); + if (Settings.flag3.use_wifi_rescan) { + if (!(uptime % (60 * WIFI_RESCAN_MINUTES))) { + Wifi.scan_state = 2; + } + } + } else { + WifiSetState(0); + Mdns.begun = 0; + } + } + } +} + +int WifiState(void) +{ + int state = -1; + + if (!global_state.wifi_down) { state = WIFI_RESTART; } + if (Wifi.config_type) { state = Wifi.config_type; } + return state; +} + +String WifiGetOutputPower(void) +{ + char stemp1[TOPSZ]; + dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1); + return String(stemp1); +} + +void WifiSetOutputPower(void) +{ + WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10); +} +# 563 "/workspace/Tasmota/tasmota/support_wifi.ino" +#ifdef WIFI_RF_MODE_RF_CAL +#ifndef USE_DEEPSLEEP +RF_MODE(RF_CAL); +#endif +#endif + +#ifdef WIFI_RF_PRE_INIT +bool rf_pre_init_flag = false; +RF_PRE_INIT() +{ +#ifndef USE_DEEPSLEEP + system_deep_sleep_set_option(1); + system_phy_set_rfoption(RF_CAL); +#endif + system_phy_set_powerup_option(3); + rf_pre_init_flag = true; +} +#endif + +void WifiConnect(void) +{ + if (!Settings.flag4.network_wifi) { return; } + + WifiSetState(0); + WifiSetOutputPower(); + WiFi.persistent(false); + Wifi.status = 0; + Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP_getChipId() & 0xF); + Wifi.retry = Wifi.retry_init; + Wifi.counter = 1; + + memcpy((void*) &Wifi.bssid, (void*) Settings.wifi_bssid, sizeof(Wifi.bssid)); + +#ifdef WIFI_RF_PRE_INIT + if (rf_pre_init_flag) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Pre-init done")); + } +#endif +} + +void WifiShutdown(bool option = false) +{ + + + delay(100); + +#ifdef USE_EMULATION + UdpDisconnect(); + delay(100); +#endif + + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + delay(100); + } + +#ifdef WIFI_FORCE_RF_CAL_ERASE + if (option) { + WiFi.disconnect(false); + SettingsErase(4); + } else +#endif + { + + + + + ETS_UART_INTR_DISABLE(); + wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); + + } + delay(100); +} + +void EspRestart(void) +{ + ResetPwm(); + WifiShutdown(true); + CrashDumpClear(); + + if (restart_halt) { + while (1) { + OsWatchLoop(); + SetLedLink(1); + delay(200); + SetLedLink(0); + delay(800); + } + } else { + ESP_Restart(); + } +} + + + + +extern "C" { +#if LWIP_VERSION_MAJOR == 1 +#include "netif/wlan_lwip_if.h" +#include "netif/etharp.h" +#else +#include "lwip/etharp.h" +#endif +} + +unsigned long wifiTimer = 0; + +void stationKeepAliveNow(void) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI "Sending Gratuitous ARP")); + for (netif* interface = netif_list; interface != nullptr; interface = interface->next) + if ( + (interface->flags & NETIF_FLAG_LINK_UP) + && (interface->flags & NETIF_FLAG_UP) +#if LWIP_VERSION_MAJOR == 1 + && interface == eagle_lwip_getif(STATION_IF) + && (!ip_addr_isany(&interface->ip_addr)) +#else + && interface->num == STATION_IF + && (!ip4_addr_isany_val(*netif_ip4_addr(interface))) +#endif + ) + { + etharp_gratuitous(interface); + break; + } +} + +void wifiKeepAlive(void) { + uint32_t wifiTimerSec = Settings.param[P_ARP_GRATUITOUS]; + + if ((WL_CONNECTED != Wifi.status) || (0 == wifiTimerSec)) { return; } + + if (TimeReached(wifiTimer)) { + stationKeepAliveNow(); + if (wifiTimerSec > 100) { + wifiTimerSec = (wifiTimerSec - 100) * 60; + } + SetNextTimeInterval(wifiTimer, wifiTimerSec * 1000); + } +} +# 1 "/workspace/Tasmota/tasmota/tasmota_ca.ino" +# 24 "/workspace/Tasmota/tasmota/tasmota_ca.ino" +#if defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT) +# 37 "/workspace/Tasmota/tasmota/tasmota_ca.ino" +static const unsigned char PROGMEM LetsEncrypt_DN[] = { + 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x58, 0x33 +}; + +static const unsigned char PROGMEM LetsEncrypt_RSA_N[] = { + 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, + 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, + 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, + 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, + 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, + 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, + 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, + 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, + 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, + 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, + 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, + 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, + 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, + 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, + 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, + 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, + 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, + 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, + 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, + 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, + 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, + 0xD8, 0x7D, 0xC3, 0x93 +}; + +static const unsigned char LetsEncrypt_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { + { (unsigned char *)LetsEncrypt_DN, sizeof LetsEncrypt_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)LetsEncrypt_RSA_N, sizeof LetsEncrypt_RSA_N, + (unsigned char *)LetsEncrypt_RSA_E, sizeof LetsEncrypt_RSA_E, + } } + } +}; +# 100 "/workspace/Tasmota/tasmota/tasmota_ca.ino" +const unsigned char PROGMEM AmazonRootCA1_DN[] = { + 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, + 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 +}; + +const unsigned char PROGMEM AmazonRootCA1_RSA_N[] = { + 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, + 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, + 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, + 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, + 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, + 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, + 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, + 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, + 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, + 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, + 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, + 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, + 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, + 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, + 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, + 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, + 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, + 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, + 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, + 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, + 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, + 0x9A, 0xC8, 0xAA, 0x0D +}; + +static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { + { (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N, + (unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E, + } } + } +}; + + +const br_x509_trust_anchor PROGMEM Tasmota_TA[] = { + { + { (unsigned char *)LetsEncrypt_DN, sizeof LetsEncrypt_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)LetsEncrypt_RSA_N, sizeof LetsEncrypt_RSA_N, + (unsigned char *)LetsEncrypt_RSA_E, sizeof LetsEncrypt_RSA_E, + } } + } + } + , + { + { (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N, + (unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E, + } } + } + } +}; + +const size_t Tasmota_TA_size = ARRAY_SIZE(Tasmota_TA); +# 187 "/workspace/Tasmota/tasmota/tasmota_ca.ino" +const unsigned char GoDaddyCAG2_DN[] PROGMEM = { + 0x30, 0x3E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, + 0x18, 0x44, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x43, 0x6F, 0x6E, 0x74, + 0x72, 0x6F, 0x6C, 0x20, 0x56, 0x61, 0x6C, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, + 0x61, 0x70, 0x69, 0x2E, 0x74, 0x65, 0x6C, 0x65, 0x67, 0x72, 0x61, 0x6D, + 0x2E, 0x6F, 0x72, 0x67 +}; + +const unsigned char GoDaddyCAG2_RSA_N[] PROGMEM = { + 0xB4, 0xA3, 0x16, 0x9E, 0x5C, 0x57, 0xC9, 0x89, 0x65, 0xED, 0xEA, 0x78, + 0x0B, 0xAE, 0x8A, 0x58, 0x2F, 0xAE, 0x5A, 0xC8, 0x6E, 0x49, 0x8D, 0xFC, + 0x57, 0xA5, 0x98, 0x88, 0x78, 0x2E, 0x0B, 0x3C, 0x40, 0x3C, 0x21, 0x2E, + 0x9A, 0x94, 0x98, 0x33, 0xA7, 0xE3, 0x42, 0xA7, 0x85, 0xFA, 0xD0, 0x73, + 0x84, 0x01, 0x1C, 0x72, 0x39, 0x37, 0x23, 0xB5, 0x56, 0x1D, 0x43, 0xA5, + 0x71, 0x14, 0x08, 0x24, 0xA5, 0x39, 0xCC, 0xDE, 0x58, 0x53, 0x94, 0x8E, + 0x2A, 0x42, 0xA7, 0x4E, 0x2D, 0x07, 0x32, 0x9E, 0xBA, 0x8B, 0xD3, 0x2A, + 0xA9, 0x9E, 0xC0, 0xE3, 0xCE, 0x9A, 0x10, 0x96, 0x45, 0x58, 0x7A, 0xC7, + 0x1E, 0x45, 0x14, 0x23, 0x92, 0xBB, 0x54, 0x82, 0x88, 0x94, 0x49, 0xB6, + 0xBE, 0x81, 0x21, 0x00, 0x29, 0x6D, 0xC9, 0xCE, 0x8B, 0x39, 0x3A, 0xDC, + 0x35, 0x15, 0xD9, 0xEB, 0x47, 0x9C, 0xEF, 0xBA, 0x09, 0x0E, 0x16, 0xE4, + 0xD9, 0xEB, 0x72, 0x30, 0xFA, 0x49, 0xAB, 0x98, 0x31, 0x7C, 0xB3, 0xAC, + 0x2B, 0x29, 0x91, 0x87, 0x08, 0x41, 0x72, 0x5E, 0x35, 0xC7, 0x87, 0x04, + 0x22, 0xF5, 0x48, 0x76, 0x30, 0x6D, 0x88, 0xDF, 0xF2, 0xA5, 0x29, 0x13, + 0x70, 0xB3, 0x87, 0x02, 0xD5, 0x6B, 0x58, 0xB1, 0xE8, 0x73, 0xC7, 0xE4, + 0xEF, 0x79, 0x86, 0xA4, 0x07, 0x5F, 0x67, 0xB4, 0x79, 0x8D, 0xA4, 0x25, + 0x01, 0x82, 0x8C, 0xE0, 0x30, 0x17, 0xCB, 0x4B, 0x5C, 0xFB, 0xEB, 0x4C, + 0x12, 0x51, 0xB9, 0xC9, 0x04, 0x1F, 0x7E, 0xD2, 0xF8, 0xBA, 0xF5, 0x35, + 0x8D, 0x8A, 0x1C, 0x37, 0x82, 0xF0, 0x15, 0x73, 0x00, 0x6E, 0x3D, 0x1C, + 0x76, 0x8B, 0x01, 0x74, 0x81, 0x3D, 0xE4, 0x2C, 0xA7, 0xCC, 0x2F, 0x66, + 0xDC, 0x44, 0xA8, 0x27, 0x3F, 0xEA, 0xD0, 0xA7, 0xA8, 0xF1, 0xCB, 0xEA, + 0xDA, 0x07, 0x38, 0xBD +}; + +const unsigned char GoDaddyCAG2_RSA_E[] PROGMEM = { + 0x01, 0x00, 0x01 +}; + +const br_x509_trust_anchor GoDaddyCAG2_TA PROGMEM = { + { (unsigned char *)GoDaddyCAG2_DN, sizeof GoDaddyCAG2_DN }, + 0, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)GoDaddyCAG2_RSA_N, sizeof GoDaddyCAG2_RSA_N, + (unsigned char *)GoDaddyCAG2_RSA_E, sizeof GoDaddyCAG2_RSA_E, + } } + } +}; + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_01_webserver.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_01_webserver.ino" +#ifdef USE_WEBSERVER + + + + + + + +#define XDRV_01 1 + +#ifndef WIFI_SOFT_AP_CHANNEL +#define WIFI_SOFT_AP_CHANNEL 1 +#endif + +const uint16_t CHUNKED_BUFFER_SIZE = (MESSZ / 2) - 100; + +const uint16_t HTTP_REFRESH_TIME = 2345; +const uint16_t HTTP_RESTART_RECONNECT_TIME = 9000; +const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 20000; + +#include +#include + +#ifdef USE_RF_FLASH +uint8_t *efm8bb1_update = nullptr; +#endif + +enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTACLIENT, UPL_EFR32 }; + +#ifdef USE_UNISHOX_COMPRESSION +#ifdef USE_JAVASCRIPT_ES6 + +const size_t HTTP_HEADER1_SIZE = 377; +const char HTTP_HEADER1_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C" + "\x3C\x7C\x3F\xB2\xEC\xD7\xE6\x86\x7D\x78\xFE\xCB\xB3\x5F\x9A\x1A\x0C\x2B\xF7\x8F" + "\x87\xB0\xF6\x1F\x87\xA0\xA7\x62\x1F\x87\xA0\xD7\x56\x83\x15\x7F\xF3\xA3\xE1\xF6" + "\x2E\x8C\x1D\x67\x3E\x7D\x90\x21\x52\xEB\x1A\xCF\x87\xB0\xCF\x58\xF8\xCC\xFD\x1E" + "\xC4\x1E\x75\x3E\xA3\xE1\xEC\x1F\xD1\x28\x51\xF0\x46\x67\xA1\xB3\xAC\x7F\x44\xA1" + "\x47\x56\xF6\xD6\xD8\x47\x5F\x83\xB0\x99\xF0\xE4\x3A\x88\x5F\x9F\xCE\xBF\x07\x61" + "\x58\xE0\x99\xF3\xB0\xF6\x1D\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A\xD1\xF5\xE3" + "\xD0\xEC\x04\x19\x67\xA7\x83\xFE\x8C\xA3\xF0\xCE\xFE\x8D\x87\xCE\x16\x10\x47\x50" + "\x54\x75\x56\x1D\x54\x30\xEA\x18\x19\xF0\xFB\x3E\xCF\x0C\x71\xF3\xC7\xC3\xF0\x4C" + "\x0C\x58\xD7\xD4\x74\x1E\x74\x4C\x26\x35\xF5\x10\xE3\x22\xD1\x0E\xEF\x8E\xF1\xE0" + "\xD5\xE0\x48\xBA\x6A\x16\xFE\x64\x5E\x61\x30\xEB\x3E\x77\x7C\x77\x8F\x1E\x18\x7C" + "\xD3\xE1\xF8\xC7\x1D\xDD\x3B\xC7\x4A\x32\x18\xCF\x87\x74\x11\xA4\x1F\x0F\x87\xDD" + "\x33\x65\x1F\x67\x68\xFB\x19\x7E\xF0\xFE\x7C\x43\xEC\xF3\x04\x19\xC7\x78\xF0\x3E" + "\x11\xF0\xC1\xF0\xFC\x1F\xDE\x13\x07\xCE\x96\x20\x84\xCC\xDF\x51\x05\xBE\xA7\xCF" + "\xE7\x74\xFB\x0B\x2C\x43\xEC\xEA\x30\x77\x8F\x06"; +#define HTTP_HEADER1 Decompress(HTTP_HEADER1_COMPRESSED,HTTP_HEADER1_SIZE).c_str() +#else +const size_t HTTP_HEADER1_SIZE = 431; +const char HTTP_HEADER1_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C" + "\x3C\x7C\x3F\xB2\xEC\xD7\xE6\x86\x7D\x78\xFE\xCB\xB3\x5F\x9A\x1A\x0C\x2B\xF7\x8F" + "\x87\xB0\xF6\x1F\x87\xA0\xA7\x62\x1F\x87\xA0\xD7\x56\x83\x15\x7F\xF3\xA3\xE1\xF6" + "\x2E\x8C\x1D\x67\x3E\x7D\x90\x21\x52\xEB\x1A\xCF\x87\xB0\xCF\x58\xF8\xCC\xFD\x1E" + "\xC4\x1E\x75\x3E\xA3\xE1\xEC\x1F\xD1\x28\x51\xF0\x46\x67\xA1\xB3\xAC\x7F\x44\xA1" + "\x47\x56\xF6\xD6\xD8\x47\x5F\x83\xB0\x99\xF0\xE4\x3A\x88\x5F\x9F\xCE\xBF\x07\x61" + "\x58\xE0\x99\xF3\xB0\xF6\x1D\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A\xD1\xF5\xE3" + "\xD0\xEC\x04\x19\x67\xA7\x83\xFE\x8C\xA3\xF0\xCE\xFE\x8D\x87\xCE\x16\x10\x47\x50" + "\x54\x75\x56\x1D\x54\x30\xEA\x18\x19\xF0\xFB\x3E\xCF\x06\x05\xF0\x75\xB9\xC9\x8E" + "\x3B\xBE\x3B\xC7\xB7\xEE\x85\xFF\x90\x98\x18\xB1\xAF\xA8\xE8\x3C\xE8\x98\x4C\x6B" + "\xEA\x21\xC6\x45\xA2\x1D\xDF\x1D\xE3\xC1\xEE\x04\x4C\x38\xD5\xE0\x4F\xC3\x8D\x42" + "\xDF\xCC\x8B\xCC\x26\x1D\x67\xC1\x27\x0D\xF0\xC3\xBB\xA7\x78\xF6\xB1\xC7\x77\x4E" + "\xF1\xD2\x8C\x86\x33\xE1\xDD\x04\x69\x07\xC3\xE1\xF7\x4C\xD9\x47\xD9\xDA\x3E\xC6" + "\x5F\xBC\x3F\x9F\x10\xFB\x3C\xC1\x06\x70\x23\xE3\xE3\xE1\x1D\xD3\x07\x78\xF6\x8F" + "\xEF\x09\x83\xE7\x4B\x10\x42\x66\x6F\xA8\x82\xDF\x53\xE7\xF3\xBA\x7D\x85\x96\x21" + "\xF6\x75\x18\x3B\xC7\x83\xDC"; +#define HTTP_HEADER1 Decompress(HTTP_HEADER1_COMPRESSED,HTTP_HEADER1_SIZE).c_str() +#endif +#else +const char HTTP_HEADER1[] PROGMEM = + "" + "" + "" + "" + "%s - %s" + + ""; +#endif + +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_HEAD_STYLE1_SIZE = 591; +const char HTTP_HEAD_STYLE1_COMPRESSED[] PROGMEM = "\x3D\x3D\x46\x41\x33\xF0\x4D\x33\x3A\x8C\x6B\x08\x4F\x3A\x3A\xB7\x86\x0B\xA3\xAB" + "\xCC\x26\x1D\x1E\xD1\x96\x20\x9B\xC3\xC7\x99\xCD\x21\x86\xC3\xC1\x8C\xEA\x3A\xFD" + "\xA6\xD6\x79\x9C\x84\xC6\x9E\x0F\x70\x21\xE1\xA7\xB4\x75\x86\x68\x3D\xFC\x17\xC2" + "\x1E\x67\x91\xF4\x71\xF1\x1B\x0F\x07\xB8\x61\xED\x1B\x7F\x1E\xDE\x3C\xCE\x33\xA6" + "\x93\x1A\x8E\x33\xC1\xEE\x2D\xE1\x82\xE8\xF6\x8F\xE8\x94\x28\xF3\x39\x1B\x3E\x8F" + "\xA3\xC1\x0E\xC3\x61\xD7\xED\x36\xEF\x0F\x1E\x63\xB3\xE2\x3F\x9D\x63\xB0\xD8\x78" + "\x3A\xC7\xD8\xE3\x4D\xA3\xAC\x14\xAD\x0D\xC3\x68\x29\x57\x04\xCD\x84\x3C\x0B\x3E" + "\x08\x7B\x6E\xF0\xC1\x74\x7B\xD4\x64\x31\x9F\x03\x14\xC3\x34\x1D\x86\xC3\xDF\x04" + "\x1E\x11\x41\x06\x8F\xEC\x4D\xC3\xDF\x04\x3D\xF1\x8D\x3C\x02\x0F\x03\x87\x5F\xF4" + "\x78\x55\x1E\x67\x38\x86\x1B\x0F\x06\x6F\xF5\xA1\xD8\x47\x5D\x85\xA3\xDC\x79\x9D" + "\x67\x21\x0C\x04\x9C\xCF\xF7\xC3\xCC\x10\xF1\xE3\x89\x1F\x47\xD1\xE0\xF7\x10\x21" + "\x71\x3E\x09\x1C\x28\x82\xC7\x2A\x01\x54\xCD\x95\x7F\x76\x7B\x7E\xFD\xA6\xD6\x79" + "\x82\x1E\xA0\x78\x04\x2C\xC8\xE7\xCF\xA3\xE8\xF0\x42\x9E\x8F\x0A\xA3\xCC\xE5\xCF" + "\x90\xC3\x61\xE0\x11\xF8\xFA\xC3\x37\xF3\x01\x60\xF9\xE7\x62\xEB\x01\x6B\x45\x1D" + "\x82\x19\x1E\xDA\x66\xCA\x04\x2E\x0A\x83\x7D\x4F\xE0\x83\xC9\xE9\x8B\x1B\xA1\x19" + "\x1E\x66\x6F\xE2\x5F\x59\xD5\xEB\xEF\x1D\x7E\x7F\xD3\x2A\x01\x9B\x98\x1E\xEA\x10" + "\x11\x39\x7D\x38\xC8\x61\xB0\xF0\x7B\x8D"; +#define HTTP_HEAD_STYLE1 Decompress(HTTP_HEAD_STYLE1_COMPRESSED,HTTP_HEAD_STYLE1_SIZE).c_str() +#else +const char HTTP_HEAD_STYLE1[] PROGMEM = + "" + + "" + "" + "
" +#ifdef FIRMWARE_MINIMAL + "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" +#endif + "
" +#ifdef LANGUAGE_MODULE_NAME + "

" D_MODULE " %s

" +#else + "

%s " D_MODULE "

" +#endif + "

%s

"; + +const char HTTP_MSG_SLIDER_GRADIENT[] PROGMEM = + "
" + "" + "
"; +const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM = + "
" D_CLOSE "" D_OPEN "
" + "
"; + +const char HTTP_MSG_RSTRT[] PROGMEM = + "
" D_DEVICE_WILL_RESTART "

"; + +const char HTTP_FORM_LOGIN[] PROGMEM = + "
" + "
" + "

" D_USER "

" + "

" D_PASSWORD "

" + "
" + "" + "
"; + +const char HTTP_FORM_TEMPLATE[] PROGMEM = + "
 " D_TEMPLATE_PARAMETERS " " + "
"; +const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM = + "

" + "
 " D_TEMPLATE_FLAGS " 

" + + "

"; + +const char HTTP_FORM_MODULE[] PROGMEM = + "
 " D_MODULE_PARAMETERS " " + "" + "

" D_MODULE_TYPE " (%s)

" + "
"; + +const char HTTP_FORM_WIFI[] PROGMEM = + "
 " D_WIFI_PARAMETERS " " + "" + "

" D_AP1_SSID " (" STA_SSID1 ")

" + "


" + "

" D_AP2_SSID " (" STA_SSID2 ")

" + "


" + "

" D_HOSTNAME " (%s)

" + "

" D_CORS_DOMAIN "

"; + +const char HTTP_FORM_LOG1[] PROGMEM = + "
 " D_LOGGING_PARAMETERS " " + ""; +const char HTTP_FORM_LOG2[] PROGMEM = + "

" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" + "

" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" + "

" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; + +const char HTTP_FORM_OTHER[] PROGMEM = + "
 " D_OTHER_PARAMETERS " " + "" + "

" + "
 " D_TEMPLATE " " + "

" + "

" + "
" + "
" + "

" + "
" + "
" + "
" + "

" + "
"; + +const char HTTP_FORM_END[] PROGMEM = + "
" + "" + "
"; + +const char HTTP_FORM_RST[] PROGMEM = + "
" + "
 " D_RESTORE_CONFIGURATION " "; +const char HTTP_FORM_UPG[] PROGMEM = + "
" + "
 " D_UPGRADE_BY_WEBSERVER " " + "
" + "
" D_OTA_URL "

" + "
" + "


" + "
 " D_UPGRADE_BY_FILE_UPLOAD " "; +const char HTTP_FORM_RST_UPG[] PROGMEM = + "
" + "

" + "
" + "
" + "
" + ""; + +const char HTTP_FORM_CMND[] PROGMEM = + "


" + "
" + "
" + + ""; + +const char HTTP_TABLE100[] PROGMEM = + "
"; + +const char HTTP_COUNTER[] PROGMEM = + "
"; + +const char HTTP_END[] PROGMEM = + "" + "" + "" + ""; + +const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; +const char HTTP_DEVICE_STATE[] PROGMEM = ""; + +enum ButtonTitle { + BUTTON_RESTART, BUTTON_RESET_CONFIGURATION, + BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE, + BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE }; +const char kButtonTitle[] PROGMEM = + D_RESTART "|" D_RESET_CONFIGURATION "|" + D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|" + D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION; +const char kButtonAction[] PROGMEM = + ".|rt|" + ".|cn|in|up|cs|" + "md|wi|lg|co|tp|dl|rs"; +const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION; + +enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; +const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; + +const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; +const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; + +const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; + +const char kUploadErrors[] PROGMEM = + D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "|" D_UPLOAD_ERR_5 "|" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9 +#ifdef USE_RF_FLASH + "|" D_UPLOAD_ERR_10 "|" D_UPLOAD_ERR_11 "|" D_UPLOAD_ERR_12 "|" D_UPLOAD_ERR_13 +#endif + "|" D_UPLOAD_ERR_14 + ; + +const uint16_t DNS_PORT = 53; +enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; + +DNSServer *DnsServer; +ESP8266WebServer *Webserver; + +struct WEB { + String chunk_buffer = ""; + bool reset_web_log_flag = false; + uint8_t state = HTTP_OFF; + uint8_t upload_error = 0; + uint8_t upload_file_type; + uint8_t upload_progress_dot_count; + uint8_t config_block_count = 0; + uint8_t config_xor_on = 0; + uint8_t config_xor_on_set = CONFIG_FILE_XOR; +} Web; + + +static void WebGetArg(const char* arg, char* out, size_t max) +{ + String s = Webserver->arg(arg); + strlcpy(out, s.c_str(), max); + +} + +static bool WifiIsInManagerMode(){ + return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); +} + +void ShowWebSource(uint32_t source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), Webserver->client().remoteIP().toString().c_str()); + } +} + +void ExecuteWebCommand(char* svalue, uint32_t source) +{ + ShowWebSource(source); + last_source = source; + ExecuteCommand(svalue, SRC_IGNORE); +} + + +typedef struct WebServerDispatch_t { + char uri[3]; + uint8_t method; + void (*handler)(void); +} WebServerDispatch_t; + +const WebServerDispatch_t WebServerDispatch[] PROGMEM = { + { "", HTTP_ANY, HandleRoot }, + { "up", HTTP_ANY, HandleUpgradeFirmware }, + { "u1", HTTP_ANY, HandleUpgradeFirmwareStart }, + { "u2", HTTP_OPTIONS, HandlePreflightRequest }, + { "u3", HTTP_ANY, HandleUploadDone }, + { "cs", HTTP_GET, HandleConsole }, + { "cs", HTTP_OPTIONS, HandlePreflightRequest }, + { "cm", HTTP_ANY, HandleHttpCommand }, +#ifndef FIRMWARE_MINIMAL + { "cn", HTTP_ANY, HandleConfiguration }, + { "md", HTTP_ANY, HandleModuleConfiguration }, + { "wi", HTTP_ANY, HandleWifiConfiguration }, + { "lg", HTTP_ANY, HandleLoggingConfiguration }, + { "tp", HTTP_ANY, HandleTemplateConfiguration }, + { "co", HTTP_ANY, HandleOtherConfiguration }, + { "dl", HTTP_ANY, HandleBackupConfiguration }, + { "rs", HTTP_ANY, HandleRestoreConfiguration }, + { "rt", HTTP_ANY, HandleResetConfiguration }, + { "in", HTTP_ANY, HandleInformation }, +#endif +}; + +void WebServer_on(const char * prefix, void (*func)(void), uint8_t method = HTTP_ANY) { + Webserver->on((const __FlashStringHelper *) prefix, (HTTPMethod) method, func); +} + +void StartWebserver(int type, IPAddress ipweb) +{ + if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } + if (!Web.state) { + if (!Webserver) { + Webserver = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); + + for (uint32_t i=0; ionNotFound(HandleNotFound); + Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); +#ifndef FIRMWARE_MINIMAL + XdrvCall(FUNC_WEB_ADD_HANDLER); + XsnsCall(FUNC_WEB_ADD_HANDLER); +#endif + } + Web.reset_web_log_flag = false; + + Webserver->begin(); + } + if (Web.state != type) { +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if (ipv6_addr!="") { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), + NetworkHostname(), (Mdns.begun) ? ".local" : "", ipweb.toString().c_str(), ipv6_addr.c_str()); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), + NetworkHostname(), (Mdns.begun) ? ".local" : "", ipweb.toString().c_str()); + } +#else + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), + NetworkHostname(), (Mdns.begun) ? ".local" : "", ipweb.toString().c_str()); +#endif + rules_flag.http_init = 1; + } + if (type) { Web.state = type; } +} + +void StopWebserver(void) +{ + if (Web.state) { + Webserver->close(); + Web.state = HTTP_OFF; + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); + } +} + +void WifiManagerBegin(bool reset_only) +{ + + if (!global_state.wifi_down) { + + WifiSetMode(WIFI_AP_STA); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION)); + } else { + + WifiSetMode(WIFI_AP); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT)); + } + + StopWebserver(); + + DnsServer = new DNSServer(); + + int channel = WIFI_SOFT_AP_CHANNEL; + if ((channel < 1) || (channel > 13)) { channel = 1; } + + + WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0, 1); + delay(500); + + DnsServer->setErrorReplyCode(DNSReplyCode::NoError); + DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); +} + +void PollDnsWebserver(void) +{ + if (DnsServer) { DnsServer->processNextRequest(); } + if (Webserver) { Webserver->handleClient(); } +} + + + +bool WebAuthenticate(void) +{ + if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { + return Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); + } else { + return true; + } +} + +bool HttpCheckPriviledgedAccess(bool autorequestauth = true) +{ + if (HTTP_USER == Web.state) { + HandleRoot(); + return false; + } + if (autorequestauth && !WebAuthenticate()) { + Webserver->requestAuthentication(); + return false; + } + return true; +} + +void HttpHeaderCors(void) +{ + if (strlen(SettingsText(SET_CORS))) { + Webserver->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); + } +} + +void WSHeaderSend(void) +{ + Webserver->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + Webserver->sendHeader(F("Pragma"), F("no-cache")); + Webserver->sendHeader(F("Expires"), F("-1")); + HttpHeaderCors(); +} + + + + + +void WSSend(int code, int ctype, const String& content) +{ + char ct[25]; + Webserver->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); +} + + + + + +void WSContentBegin(int code, int ctype) +{ + Webserver->client().flush(); + WSHeaderSend(); + Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + WSSend(code, ctype, ""); + Web.chunk_buffer = ""; +} + +void _WSContentSend(const String& content) +{ + size_t len = content.length(); + Webserver->sendContent(content); + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("WSContentSend")); +#endif + DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d/%d"), len, sizeof(mqtt_data)); +} + +void WSContentFlush(void) +{ + if (Web.chunk_buffer.length() > 0) { + _WSContentSend(Web.chunk_buffer); + Web.chunk_buffer = ""; + } +} + +void _WSContentSendBuffer(void) +{ + int len = strlen(mqtt_data); + + if (0 == len) { + return; + } + else if (len == sizeof(mqtt_data)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); + } + else if (len < CHUNKED_BUFFER_SIZE) { + Web.chunk_buffer += mqtt_data; + len = Web.chunk_buffer.length(); + } + + if (len >= CHUNKED_BUFFER_SIZE) { + WSContentFlush(); + } + if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { + _WSContentSend(mqtt_data); + } +} + +void WSContentSend_P(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); +} + +void WSContentSend_PD(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_PD size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + if (D_DECIMAL_SEPARATOR[0] != '.') { + for (uint32_t i = 0; i < len; i++) { + if ('.' == mqtt_data[i]) { + mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; + } + } + } + + _WSContentSendBuffer(); +} + +void WSContentStart_P(const char* title, bool auth) +{ + if (auth && strlen(SettingsText(SET_WEBPWD)) && !Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { + return Webserver->requestAuthentication(); + } + + WSContentBegin(200, CT_HTML); + + if (title != nullptr) { +#ifdef USE_UNISHOX_COMPRESSION + WSContentSend_P(HTTP_HEADER1, D_HTML_LANGUAGE, SettingsText(SET_DEVICENAME), title); +#else + WSContentSend_P(HTTP_HEADER1, SettingsText(SET_DEVICENAME), title); +#endif + } +} + +void WSContentStart_P(const char* title) +{ + WSContentStart_P(title, true); +} + +void WSContentSendStyle_P(const char* formatP, ...) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_SCRIPT_COUNTER); + } + } + WSContentSend_P(HTTP_HEAD_LAST_SCRIPT); + + WSContentSend_P(HTTP_HEAD_STYLE1, WebColor(COL_FORM), WebColor(COL_INPUT), WebColor(COL_INPUT_TEXT), WebColor(COL_INPUT), + WebColor(COL_INPUT_TEXT), WebColor(COL_CONSOLE), WebColor(COL_CONSOLE_TEXT), WebColor(COL_BACKGROUND)); + WSContentSend_P(HTTP_HEAD_STYLE2, WebColor(COL_BUTTON), WebColor(COL_BUTTON_TEXT), WebColor(COL_BUTTON_HOVER), + WebColor(COL_BUTTON_RESET), WebColor(COL_BUTTON_RESET_HOVER), WebColor(COL_BUTTON_SAVE), WebColor(COL_BUTTON_SAVE_HOVER), + WebColor(COL_BUTTON)); +#ifdef USE_ZIGBEE + WSContentSend_P(HTTP_HEAD_STYLE_ZIGBEE); +#endif + if (formatP != nullptr) { + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSendStyle_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); + } + WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT), +#ifdef FIRMWARE_MINIMAL + WebColor(COL_TEXT_WARNING), +#endif + WebColor(COL_TITLE), + ModuleName().c_str(), SettingsText(SET_DEVICENAME)); + if (Settings.flag3.gui_hostname_ip) { + bool lip = (static_cast(WiFi.localIP()) != 0); + bool sip = (static_cast(WiFi.softAPIP()) != 0); + WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), + NetworkHostname(), + (Mdns.begun) ? ".local" : "", + (lip) ? WiFi.localIP().toString().c_str() : "", + (lip && sip) ? ", " : "", + (sip) ? WiFi.softAPIP().toString().c_str() : ""); + } + WSContentSend_P(PSTR("")); +} + +void WSContentSendStyle(void) +{ + WSContentSendStyle_P(nullptr); +} + +void WSContentButton(uint32_t title_index) +{ + char action[4]; + char title[100]; + + if (title_index <= BUTTON_RESET_CONFIGURATION) { + char confirm[100]; + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), + (!title_index) ? "rst" : "non", + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } else { + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } +} + +void WSContentSpaceButton(uint32_t title_index) +{ + WSContentSend_P(PSTR("
")); + WSContentButton(title_index); +} + +void WSContentSend_THD(const char *types, float f_temperature, float f_humidity) +{ + char parameter[FLOATSZ]; + dtostrfd(f_temperature, Settings.flag2.temperature_resolution, parameter); + WSContentSend_PD(HTTP_SNS_TEMP, types, parameter, TempUnit()); + dtostrfd(f_humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_PD(HTTP_SNS_HUM, types, parameter); + dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, parameter); + WSContentSend_PD(HTTP_SNS_DEW, types, parameter, TempUnit()); +} + +void WSContentEnd(void) +{ + WSContentFlush(); + _WSContentSend(""); + Webserver->client().stop(); +} + +void WSContentStop(void) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_COUNTER); + } + } + WSContentSend_P(HTTP_END, my_version); + WSContentEnd(); +} + + + +void WebRestart(uint32_t type) +{ + + + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); + + bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); + + WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); + WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_RESTART_RECONNECT_TIME); + WSContentSendStyle(); + if (type) { + WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); + if (2 == type) { + WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); + } + WSContentSend_P(PSTR("
")); + } + WSContentSend_P(HTTP_MSG_RSTRT); + if (HTTP_MANAGER == Web.state || reset_only) { + Web.state = HTTP_ADMIN; + } else { + WSContentSpaceButton(BUTTON_MAIN); + } + WSContentStop(); + + ShowWebSource(SRC_WEBGUI); + restart_flag = 2; +} + + + +void HandleWifiLogin(void) +{ + WSContentStart_P(S_CONFIGURE_WIFI, false); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOGIN); + + if (HTTP_MANAGER_RESET_ONLY == Web.state) { + WSContentSpaceButton(BUTTON_RESTART); +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); +#endif + } + + WSContentStop(); +} + +#ifdef USE_LIGHT +void WebSliderColdWarm(void) +{ + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "a", + "#eff", "#f81", + 1, + 153, 500, + LightGetColorTemp(), + 't', 0); +} +#endif + +void HandleRoot(void) +{ + if (CaptivePortal()) { return; } + + if (Webserver->hasArg("rst")) { + WebRestart(0); + return; + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { + HandleWifiLogin(); + } else { + if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { + HandleWifiConfiguration(); + } else { + + HandleWifiLogin(); + } + } +#endif + return; + } + + if (HandleRootStatusRefresh()) { + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); + + char stemp[33]; + + WSContentStart_P(S_MAIN_MENU); +#ifdef USE_SCRIPT_WEB_DISPLAY + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); +#else + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); +#endif + WSContentSend_P(HTTP_SCRIPT_ROOT_PART2); + + WSContentSendStyle(); + + WSContentSend_P(PSTR("
")); + if (devices_present) { +#ifdef USE_LIGHT + if (light_type) { + uint8_t light_subtype = light_type &7; + if (!Settings.flag3.pwm_multi_channels) { + bool split_white = ((LST_RGBW <= light_subtype) && (devices_present > 1)); + + if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { + WebSliderColdWarm(); + } + + if (light_subtype > 2) { + uint16_t hue; + uint8_t sat; + LightGetHSB(&hue, &sat, nullptr); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "b", + "#800", PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), + 2, + 1, 359, + hue, + 'h', 0); + + uint8_t dcolor = changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255); + char scolor[8]; + snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); + uint8_t red, green, blue; + LightHsToRgb(hue, 255, &red, &green, &blue); + snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "s", + scolor, stemp, + 3, + 0, 100, + changeUIntScale(sat, 0, 255, 0, 100), + 'n', 0); + } + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "c", + "#000", "#fff", + 4, + Settings.flag3.slider_dimmer_stay_on, 100, + Settings.light_dimmer, + 'd', 0); + + if (split_white) { + if (LST_RGBCW == light_subtype) { + WebSliderColdWarm(); + } + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "f", + "#000", "#fff", + 5, + Settings.flag3.slider_dimmer_stay_on, 100, + LightGetDimmer(2), + 'w', 0); + } + } else { + uint32_t pwm_channels = light_subtype > LST_MAX ? LST_MAX : light_subtype; + stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; + for (uint32_t i = 0; i < pwm_channels; i++) { + stemp[1]++; + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + stemp, + "#000", "#fff", + i+1, + 1, 100, + changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), + 'e', i+1); + } + } + } +#endif +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < shutters_present; i++) { + WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, Settings.shutter_position[i], i+1); + } + } +#endif + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, + (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, + ""); + for (uint32_t i = 0; i < MaxFanspeed(); i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, + (strlen(SettingsText(SET_BUTTON2 + i))) ? SettingsText(SET_BUTTON2 + i) : stemp, + ""); + } + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + bool set_button = ((idx <= MAX_BUTTON_TEXT) && strlen(SettingsText(SET_BUTTON1 + idx -1))); +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(idx)) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) ? "-" : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 8) ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))), + ""); + continue; + } +#endif + snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "", + (set_button) ? "" : (devices_present > 1) ? stemp : ""); + } +#ifdef USE_SONOFF_IFAN + } +#endif + WSContentSend_P(PSTR("
%s
")); + } +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint8_t modeset = 0; + if (AsModuleTuyaMS()) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); + snprintf_P(stemp, sizeof(stemp), PSTR("" D_JSON_IRHVAC_MODE "")); + WSContentSend_P(HTTP_DEVICE_CONTROL, 26, devices_present + 1, + (strlen(SettingsText(SET_BUTTON1 + devices_present))) ? SettingsText(SET_BUTTON1 + devices_present) : stemp, ""); + WSContentSend_P(PSTR("")); + modeset = 1; + } + if (IsTuyaFanCtrl()) { + uint8_t device = devices_present + modeset; + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); + for (uint32_t i = device + 1; i <= (TuyaFanSpeeds() + device) + 1; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i - (device + 1)); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i, + (strlen(SettingsText(SET_BUTTON1 + i))) ? SettingsText(SET_BUTTON1 + i) : stemp, ""); + } + WSContentSend_P(PSTR("")); + } + } +#endif +#ifdef USE_SONOFF_RF + if (SONOFF_BRIDGE == my_module_type) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("")); + uint32_t idx = 0; + for (uint32_t i = 0; i < 4; i++) { + if (idx > 0) { WSContentSend_P(PSTR("")); } + for (uint32_t j = 0; j < 4; j++) { + idx++; + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), idx); + WSContentSend_P(PSTR(""), idx, + (strlen(SettingsText(SET_BUTTON1 + idx -1))) ? SettingsText(SET_BUTTON1 + idx -1) : stemp); + } + } + WSContentSend_P(PSTR("")); + } +#endif + +#ifndef FIRMWARE_MINIMAL + XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); + XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); +#endif + + if (HTTP_ADMIN == Web.state) { +#ifdef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); +#else + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentButton(BUTTON_INFORMATION); + WSContentButton(BUTTON_FIRMWARE_UPGRADE); +#endif + WSContentButton(BUTTON_CONSOLE); + WSContentButton(BUTTON_RESTART); + } + WSContentStop(); +} + +bool HandleRootStatusRefresh(void) +{ + if (!WebAuthenticate()) { + Webserver->requestAuthentication(); + return true; + } + + if (!Webserver->hasArg("m")) { + return false; + } + + #ifdef USE_SCRIPT_WEB_DISPLAY + Script_Check_HTML_Setvars(); + #endif + + char tmp[8]; + char svalue[32]; + char webindex[5]; + + WebGetArg("o", tmp, sizeof(tmp)); + if (strlen(tmp)) { + ShowWebSource(SRC_WEBGUI); + uint32_t device = atoi(tmp); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + if (device < 2) { + ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2); + ExecuteCommand(svalue, SRC_WEBGUI); + } + } else { +#endif +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint8_t FuncIdx = 0; + if (device <= devices_present) { + ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); + } else { + if (AsModuleTuyaMS() && device == devices_present + 1) { + uint8_t dpId = TuyaGetDpId(TUYA_MCU_FUNC_MODESET); + snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend4 %d,%d"), dpId, !TuyaModeSet()); + ExecuteCommand(svalue, SRC_WEBGUI); + } + if (IsTuyaFanCtrl()) { + uint8_t dpId = 0; + for (uint32_t i = 0; i <= 3; i++) { + if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) { + dpId = TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i); + } + } + if ((AsModuleTuyaMS() && device != devices_present + 1) || !AsModuleTuyaMS()) { + if (AsModuleTuyaMS()) {FuncIdx = 1;} + snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend2 %d,%d"), dpId, (device - (devices_present + FuncIdx) - 1)); + ExecuteCommand(svalue, SRC_WEBGUI); + } + } + } + } else { +#endif +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(device)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), abs(ShutterWebButton), (ShutterWebButton>0) ? PSTR(D_CMND_SHUTTER_STOPOPEN) : PSTR(D_CMND_SHUTTER_STOPCLOSE)); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } else { +#endif + ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); +#ifdef USE_SHUTTER + } +#endif +#ifdef USE_SONOFF_IFAN + } +#endif +#ifdef USE_TUYA_MCU + } +#endif + } +#ifdef USE_LIGHT + WebGetArg("d0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("w0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + uint32_t light_device = LightDevice(); + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t j = 0; j < pwm_channels; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j +light_device, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } + WebGetArg("t0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("h0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("n0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif +#ifdef USE_SHUTTER + for (uint32_t j = 1; j <= shutters_present; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } +#endif +#ifdef USE_SONOFF_RF + WebGetArg("k", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif + WSContentBegin(200, CT_HTML); + WSContentSend_P(PSTR("{t}")); + XsnsCall(FUNC_WEB_SENSOR); + XdrvCall(FUNC_WEB_SENSOR); + + WSContentSend_P(PSTR("")); + + if (devices_present) { + WSContentSend_P(PSTR("{t}")); + uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); + uint32_t fanspeed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); + WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); + WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); + } +#ifdef USE_SONOFF_IFAN + } +#endif + + WSContentSend_P(PSTR("")); + } +#ifdef USE_TUYA_MCU + if (IsModuleTuya()) { + uint32_t fanspeed = TuyaFanState(); + uint32_t modeset = TuyaModeSet(); + if (IsTuyaFanCtrl() && !AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_FANSPEED ": %d
"), fanspeed); + } else if (!IsTuyaFanCtrl() && AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_MODE ": %d
"), modeset); + } else if (IsTuyaFanCtrl() && AsModuleTuyaMS()) { + WSContentSend_P(PSTR("
" D_JSON_IRHVAC_MODE ": %d - " D_JSON_IRHVAC_FANSPEED ": %d
"), modeset, fanspeed); + } + } +#endif + WSContentEnd(); + + return true; +} + +#ifdef USE_SHUTTER +int32_t IsShutterWebButton(uint32_t idx) { + + int32_t ShutterWebButton = 0; + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (Settings.shutter_startrelay[i] && ((Settings.shutter_startrelay[i] == idx) || (Settings.shutter_startrelay[i] == (idx-1)))) { + ShutterWebButton = (Settings.shutter_startrelay[i] == idx) ? (i+1): (-1-i); + break; + } + } + } + return ShutterWebButton; +} +#endif + + + +#ifndef FIRMWARE_MINIMAL + +void HandleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); + + WSContentStart_P(S_CONFIGURATION); + WSContentSendStyle(); + + WSContentButton(BUTTON_MODULE); + WSContentButton(BUTTON_WIFI); + + XdrvCall(FUNC_WEB_ADD_BUTTON); + XsnsCall(FUNC_WEB_ADD_BUTTON); + + WSContentButton(BUTTON_LOGGING); + WSContentButton(BUTTON_OTHER); + WSContentButton(BUTTON_TEMPLATE); + + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); + WSContentButton(BUTTON_BACKUP); + WSContentButton(BUTTON_RESTORE); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + + + +void WSContentSendNiceLists(uint32_t option) { + char stemp[30]; + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { + if (option && (1 == i)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); + } + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + uint32_t midx = BGPIO(ridx); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames)); + } + WSContentSend_P(PSTR("\";")); + + WSContentSend_P(PSTR("hs=[")); + uint32_t midx; + bool first_done = false; + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { + midx = pgm_read_word(kGpioNiceList + i); + if (midx & 0x001F) { + if (first_done) { WSContentSend_P(PSTR(",")); } + WSContentSend_P(PSTR("%d"), midx); + first_done = true; + } + } +#ifdef ESP8266 +#ifdef USE_ADC + for (uint32_t i = 0; i < ARRAY_SIZE(kAdcNiceList); i++) { + midx = pgm_read_word(kAdcNiceList + i); + if (midx & 0x001F) { + if (first_done) { WSContentSend_P(PSTR(",")); } + WSContentSend_P(PSTR("%d"), midx); + first_done = true; + } + } +#endif +#endif + WSContentSend_P(PSTR("];")); +} + +#ifdef ESP8266 +#ifdef USE_ADC +void WSContentSendAdcNiceList(uint32_t option) { + char stemp[30]; + WSContentSend_P(PSTR("os=\"")); + for (uint32_t i = 0; i < ARRAY_SIZE(kAdcNiceList); i++) { + if (option && (1 == i)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); + } + uint32_t ridx = pgm_read_word(kAdcNiceList + i) & 0xFFE0; + uint32_t midx = BGPIO(ridx); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames)); + } +} +#endif +#endif + + + +void HandleTemplateConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + TemplateSaveSettings(); + WebRestart(1); + return; + } + + char stemp[30]; + + WebGetArg("t", stemp, sizeof(stemp)); + if (strlen(stemp)) { + uint32_t module = atoi(stemp); + uint32_t module_save = Settings.module; + Settings.module = module; + myio cmodule; + ModuleGpios(&cmodule); + gpio_flag flag = ModuleFlag(); + Settings.module = module_save; + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { + if (!FlashPin(i)) { + WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); + } + } + WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); + WSContentEnd(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); + + WSContentStart_P(S_CONFIGURE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + + WSContentSend_P(HTTP_SCRIPT_TEMPLATE); + + WSContentSendNiceLists(1); + + WSContentSend_P(HTTP_SCRIPT_TEMPLATE2); + +#ifdef ESP8266 +#ifdef USE_ADC + WSContentSendAdcNiceList(1); + WSContentSend_P(HTTP_SCRIPT_TEMPLATE3); +#endif +#endif + + WSContentSend_P(HTTP_SCRIPT_TEMPLATE4); + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + uint32_t midx = pgm_read_byte(kModuleNiceList + i); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_INDEX, midx, AnyModuleName(midx).c_str(), midx +1); + } + WSContentSend_P(HTTP_SCRIPT_TEMPLATE5); + + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_TEMPLATE); + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("" D_TEMPLATE_NAME "" + "" D_BASE_TYPE "" + "" + "
")); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { + if (!FlashPin(i)) { + WSContentSend_P(PSTR("" D_GPIO "%d"), + ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:150px'" : "", i, i); + WSContentSend_P(PSTR(""), i); + } + } + WSContentSend_P(PSTR("")); + + gpio_flag flag = ModuleFlag(); + if (flag.data) { + WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); + } + + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +uint16_t WebGetGpioArg(uint32_t i) { + char webindex[5]; + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); + char tmp[8]; + WebGetArg(webindex, tmp, sizeof(tmp)); + uint32_t gpio = (!strlen(tmp)) ? 0 : atoi(tmp); + char webindex2[5]; + snprintf_P(webindex2, sizeof(webindex2), PSTR("h%d"), i); + char tmp2[8]; + WebGetArg(webindex2, tmp2, sizeof(tmp2)); + uint32_t value2 = (!strlen(tmp2)) ? 0 : atoi(tmp2) -1; + gpio += value2; + return gpio; +} + +void TemplateSaveSettings(void) +{ + char tmp[TOPSZ]; + char svalue[300]; + + WebGetArg("s1", tmp, sizeof(tmp)); + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); + + uint32_t j = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", WebGetGpioArg(j)); + j++; + } + + uint32_t flag = 0; + char webindex[5]; + for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); + uint32_t state = Webserver->hasArg(webindex) << i; + flag += state; + } + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t base = atoi(tmp) +1; + + snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); + ExecuteWebCommand(svalue, SRC_WEBGUI); +} + + + +void HandleModuleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + ModuleSaveSettings(); + WebRestart(1); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); + + char stemp[30]; + uint32_t midx; + myio cmodule; + ModuleGpios(&cmodule); + + WSContentStart_P(S_CONFIGURE_MODULE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + + WSContentSend_P(PSTR("function sl(){os=\"")); + uint32_t vidx = 0; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (0 == i) { + midx = USER_MODULE; + vidx = 0; + } else { + midx = pgm_read_byte(kModuleNiceList + i -1); + vidx = midx +1; + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_INDEX, midx, AnyModuleName(midx).c_str(), vidx); + } + WSContentSend_P(PSTR("\";sk(%d,99);os=\""), Settings.module); + + WSContentSendNiceLists(0); + + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); + } + } + +#ifdef ESP8266 +#ifdef USE_ADC + WSContentSendAdcNiceList(0); + WSContentSend_P(PSTR("\";sk(%d," STR(ADC0_PIN) ");"), Settings.my_gp.io[(sizeof(myio) / 2) -1]); +#endif +#endif + + WSContentSend_P(PSTR("}wl(sl);")); + + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(stemp, 3, PINS_WEMOS +i*2); + WSContentSend_P(PSTR("%s " D_GPIO "%d"), + (WEMOS==my_module_type)?stemp:"", i, i, i); + WSContentSend_P(PSTR(""), i); + } + } + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void ModuleSaveSettings(void) +{ + char tmp[8]; + + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); + Settings.last_module = Settings.module; + Settings.module = new_module; + SetModuleType(); + myio cmodule; + ModuleGpios(&cmodule); + String gpios = ""; + for (uint32_t i = 0; i < ARRAY_SIZE(cmodule.io); i++) { + if (Settings.last_module != new_module) { + Settings.my_gp.io[i] = GPIO_NONE; + } else { + if (ValidGPIO(i, cmodule.io[i])) { + Settings.my_gp.io[i] = WebGetGpioArg(i); + gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); + } + } + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); +} + + + +const char kUnescapeCode[] = "&><\"\'\\"; +const char kEscapeCode[] PROGMEM = "&|>|<|"|'|\"; + +String HtmlEscape(const String unescaped) { + char escaped[10]; + size_t ulen = unescaped.length(); + String result = ""; + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + char *p = strchr(kUnescapeCode, c); + if (p != nullptr) { + result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); + } else { + result += c; + } + } + return result; +} + + +const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; + +void HandleWifiConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); + + if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { + WifiSaveSettings(); + WebRestart(2); + return; + } + + WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); + WSContentSend_P(HTTP_SCRIPT_WIFI); + WSContentSendStyle(); + + if (HTTP_MANAGER_RESET_ONLY != Web.state) { + if (Webserver->hasArg("scan")) { +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + int n = WiFi.scanNetworks(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); + + if (0 == n) { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); + WSContentSend_P(S_NO_NETWORKS_FOUND); + WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); + } else { + + int indices[n]; + for (uint32_t i = 0; i < n; i++) { + indices[i] = i; + } + + + for (uint32_t i = 0; i < n; i++) { + for (uint32_t j = i + 1; j < n; j++) { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); + } + } + } + + + String cssid; + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + cssid = WiFi.SSID(indices[i]); + uint32_t cschn = WiFi.channel(indices[i]); + for (uint32_t j = i + 1; j < n; j++) { + if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) { + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); + indices[j] = -1; + } + } + } + + + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + int32_t rssi = WiFi.RSSI(indices[i]); + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), + WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), rssi); + int quality = WifiGetRssiAsQuality(rssi); + int auth = WiFi.encryptionType(indices[i]); + char encryption[20]; + WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), + HtmlEscape(WiFi.SSID(indices[i])).c_str(), + WiFi.channel(indices[i]), + GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), + quality, rssi + ); + delay(0); + + } + WSContentSend_P(PSTR("
")); + } + } else { + WSContentSend_P(PSTR("
")); + } + + + WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); + WSContentSend_P(HTTP_FORM_END); + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESTORE); + WSContentButton(BUTTON_RESET_CONFIGURATION); +#endif + WSContentSpaceButton(BUTTON_RESTART); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); +} + +void WifiSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("h", tmp, sizeof(tmp)); + SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + } + WebGetArg("c", tmp, sizeof(tmp)); + SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); + WebGetArg("s1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); + WebGetArg("s2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); + WebGetArg("p1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); + WebGetArg("p2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), + SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); +} + + + +void HandleLoggingConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); + + if (Webserver->hasArg("save")) { + LoggingSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_LOGGING); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOG1); + char stemp1[45]; + char stemp2[32]; + uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; + for (uint32_t idx = 0; idx < 4; idx++) { + if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } + uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; + WSContentSend_P(PSTR("

%s (%s)

")); + } + WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void LoggingSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("l0", tmp, sizeof(tmp)); + SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); + WebGetArg("l1", tmp, sizeof(tmp)); + Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); + WebGetArg("l2", tmp, sizeof(tmp)); + Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); + WebGetArg("l3", tmp, sizeof(tmp)); + SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); + WebGetArg("lh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); + WebGetArg("lp", tmp, sizeof(tmp)); + Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); + WebGetArg("lt", tmp, sizeof(tmp)); + Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { + Settings.tele_period = 10; + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); +} + + + +void HandleOtherConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); + + if (Webserver->hasArg("save")) { + OtherSaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(S_CONFIGURE_OTHER); + WSContentSendStyle(); + + TemplateJson(); + char stemp[strlen(mqtt_data) +1]; + strlcpy(stemp, mqtt_data, sizeof(stemp)); + WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", + (Settings.flag.mqtt_enabled) ? " checked" : "", + SettingsText(SET_FRIENDLYNAME1), SettingsText(SET_DEVICENAME)); + + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); + WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), + i +1, + (i) ? stemp : "", + i, + (i) ? stemp : "", + SettingsText(SET_FRIENDLYNAME1 + i)); + } + +#ifdef USE_EMULATION +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) + WSContentSend_P(PSTR("

 " D_EMULATION " 

")); + for (uint32_t i = 0; i < EMUL_MAX; i++) { +#ifndef USE_EMULATION_WEMO + if (i == EMUL_WEMO) { i++; } +#endif +#ifndef USE_EMULATION_HUE + if (i == EMUL_HUE) { i++; } +#endif + if (i < EMUL_MAX) { + WSContentSend_P(PSTR("%s %s
"), + i, i, + (i == Settings.flag2.emulation) ? " checked" : "", + GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), + (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); + } + } + WSContentSend_P(PSTR("

")); +#endif +#endif + + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void OtherSaveSettings(void) +{ + char tmp[300]; + char webindex[5]; + char friendlyname[TOPSZ]; + char message[LOGSZ]; + + WebGetArg("dn", tmp, sizeof(tmp)); + SettingsUpdateText(SET_DEVICENAME, (!strlen(tmp)) ? "" : (!strcmp(tmp,"1")) ? SettingsText(SET_FRIENDLYNAME1) : tmp); + WebGetArg("wp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); + Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); +#ifdef USE_EMULATION + UdpDisconnect(); +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) + WebGetArg("b2", tmp, sizeof(tmp)); + Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); +#endif +#endif + + snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_DEVICENAME " %s, " D_CMND_FRIENDLYNAME), + GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation, SettingsText(SET_DEVICENAME)); + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); + SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); + snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); + } + AddLog_P(LOG_LEVEL_INFO, message); + + WebGetArg("t1", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); + ExecuteWebCommand(message, SRC_WEBGUI); + } +} + + + +void HandleBackupConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); + + if (!SettingsBufferAlloc()) { return; } + + WiFiClient myClient = Webserver->client(); + Webserver->setContentLength(sizeof(Settings)); + + char attachment[TOPSZ]; + + + + + char hostname[sizeof(my_hostname)]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); + + Webserver->sendHeader(F("Content-Disposition"), attachment); + + WSSend(200, CT_STREAM, ""); + + uint32_t cfg_crc32 = Settings.cfg_crc32; + Settings.cfg_crc32 = GetSettingsCrc32(); + + memcpy(settings_buffer, &Settings, sizeof(Settings)); + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + + myClient.write((const char*)settings_buffer, sizeof(Settings)); + + SettingsBufferFree(); + + Settings.cfg_crc32 = cfg_crc32; +} + + + +void HandleResetConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); + + WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + char command[CMDSZ]; + snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleRestoreConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); + + WSContentStart_P(S_RESTORE_CONFIGURATION); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_RST); + WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); + if (WifiIsInManagerMode()) { + WSContentSpaceButton(BUTTON_MAIN); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_SETTINGS; +} + + + +void HandleInformation(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); + + char stopic[TOPSZ]; + + int freeMem = ESP_getFreeHeap(); + + WSContentStart_P(S_INFORMATION); + + + + WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); + WSContentSend_P(PSTR("
")); + WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); + WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); + WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion()); + WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); +#ifdef ESP8266 + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); +#else + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d"), Settings.save_flag); +#endif + WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); + WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); + } + WSContentSend_P(PSTR("}1}2 ")); +#ifdef ESP32 +#ifdef USE_ETHERNET + if (static_cast(EthernetLocalIP()) != 0) { + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), EthernetHostname(), (Mdns.begun) ? ".local" : ""); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), EthernetMacAddress().c_str()); + WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (eth)}2%s"), EthernetLocalIP().toString().c_str()); + WSContentSend_P(PSTR("}1
}2
")); + } +#endif +#endif + if (Settings.flag4.network_wifi) { + int32_t rssi = WiFi.RSSI(); + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, HtmlEscape(SettingsText(SET_STASSID1 + Settings.sta_active)).c_str(), WifiGetRssiAsQuality(rssi), rssi); + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Mdns.begun) ? ".local" : ""); +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if (ipv6_addr != "") { + WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); + } +#endif + if (static_cast(WiFi.localIP()) != 0) { + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); + WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (wifi)}2%s"), WiFi.localIP().toString().c_str()); + WSContentSend_P(PSTR("}1
}2
")); + } + } + if (!global_state.network_down) { + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); + } + if ((WiFi.getMode() >= WIFI_AP) && (static_cast(WiFi.softAPIP()) != 0)) { + WSContentSend_P(PSTR("}1
}2
")); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); + WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (AP)}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); + } + WSContentSend_P(PSTR("}1}2 ")); + if (Settings.flag.mqtt_enabled) { + WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); + WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); +#ifdef USE_MQTT_TLS + WSContentSend_P(PSTR("}1" D_MQTT_TLS_ENABLE "}2%s"), Settings.flag4.mqtt_tls ? PSTR(D_ENABLED) : PSTR(D_DISABLED)); +#endif + WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); + WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); + WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 1 +i, GetGroupTopic_P(stopic, "", real_index +i)); + } + } + WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); + WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); + WSContentSend_P(PSTR("}1" D_MQTT_NO_RETAIN "}2%s"), Settings.flag4.mqtt_no_retain ? PSTR(D_ENABLED) : PSTR(D_DISABLED)); + } else { + WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); + } + WSContentSend_P(PSTR("}1}2 ")); + +#ifdef USE_EMULATION + WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); +#else + WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); +#endif + +#ifdef USE_DISCOVERY + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); + if (Settings.flag3.mdns_enabled) { +#ifdef WEBSERVER_ADVERTISE + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); +#else + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); +#endif + } +#else + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); +#endif + + WSContentSend_P(PSTR("}1}2 ")); + WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); +#ifdef ESP8266 + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP.getFlashChipId()); +#endif + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP_getSketchSize() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); +#ifdef ESP32 + if (psramFound()) { + WSContentSend_P(PSTR("}1" D_PSR_MAX_MEMORY "}2%dkB"), ESP.getPsramSize() / 1024); + WSContentSend_P(PSTR("}1" D_PSR_FREE_MEMORY "}2%dkB"), ESP.getFreePsram() / 1024); + } +#endif + WSContentSend_P(PSTR("
")); + + WSContentSend_P(HTTP_SCRIPT_INFO_END); + WSContentSendStyle(); + + WSContentSend_P(PSTR("" + "
")); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} +#endif + + + +void HandleUpgradeFirmware(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); + + WSContentStart_P(S_FIRMWARE_UPGRADE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); + WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_TASMOTA; +} + +void HandleUpgradeFirmwareStart(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + char command[TOPSZ + 10]; + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); + WifiConfigCounter(); + + char otaurl[TOPSZ]; + WebGetArg("o", otaurl, sizeof(otaurl)); + if (strlen(otaurl)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); + ExecuteWebCommand(command, SRC_WEBGUI); + } + + WSContentStart_P(S_INFORMATION); + WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_OTA_RESTART_RECONNECT_TIME); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleUploadDone(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + +#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP) + if (!Web.upload_error) { + + if (ZigbeeUploadOtaReady()) { + HandleZigbeeXfer(); + return; + } + } +#endif + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); + + char error[100]; + + WifiConfigCounter(); + restart_flag = 0; + MqttRetryCounter(0); +#ifdef USE_COUNTER + CounterInterruptDisable(false); +#endif + + WSContentStart_P(S_INFORMATION); + if (!Web.upload_error) { + uint32_t javascript_settimeout = HTTP_OTA_RESTART_RECONNECT_TIME; +#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP) + if (ZigbeeUploadFinish()) { + javascript_settimeout = 10000; + } +#endif + WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, javascript_settimeout); + } + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_FAILED "

"), WebColor(COL_TEXT_WARNING)); +#ifdef USE_RF_FLASH + if (Web.upload_error < 15) { +#else + if ((Web.upload_error < 10) || (14 == Web.upload_error)) { + if (14 == Web.upload_error) { Web.upload_error = 10; } +#endif + GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); + } else { + snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); + } + WSContentSend_P(error); + DEBUG_CORE_LOG(PSTR("UPL: %s"), error); + stop_flash_rotate = Settings.flag.stop_flash_rotate; + } else { + WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + restart_flag = 2; +#ifdef USE_TASMOTA_CLIENT + if (TasmotaClient_GetFlagFlashing()) { + WSContentSend_P(PSTR("
" D_TRANSFER_STARTED " ...
")); + restart_flag = 0; + } +#endif + if (restart_flag) { + WSContentSend_P(HTTP_MSG_RSTRT); + ShowWebSource(SRC_WEBGUI); + } + } + SettingsBufferFree(); + WSContentSend_P(PSTR("

")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + +#ifdef USE_TASMOTA_CLIENT + if (TasmotaClient_GetFlagFlashing()) { + TasmotaClient_Flash(); + } +#endif +} + +void HandleUploadLoop(void) +{ + + bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); + + if (HTTP_USER == Web.state) { return; } + if (Web.upload_error) { + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + return; + } + + HTTPUpload& upload = Webserver->upload(); + + + if (UPLOAD_FILE_START == upload.status) { + restart_flag = 60; + if (0 == upload.filename.c_str()[0]) { + Web.upload_error = 1; + return; + } + SettingsSave(1); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); + if (UPL_SETTINGS == Web.upload_file_type) { + if (!SettingsBufferAlloc()) { + Web.upload_error = 2; + return; + } + } else { + MqttRetryCounter(60); +#ifdef USE_COUNTER + CounterInterruptDisable(true); +#endif +#ifdef USE_EMULATION + UdpDisconnect(); +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { + + + + + + + Web.upload_error = 2; + return; + } + } + Web.upload_progress_dot_count = 0; + } + + + else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { + if (0 == upload.totalSize) { + if (UPL_SETTINGS == Web.upload_file_type) { + Web.config_block_count = 0; + } + else { +#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP) +#ifdef ESP8266 + if ((SONOFF_ZB_BRIDGE == my_module_type) && (upload.buf[0] == 0xEB)) { +#else + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX) && (upload.buf[0] == 0xEB)) { +#endif + Update.end(); + Web.upload_file_type = UPL_EFR32; + + Web.upload_error = ZigbeeUploadInit(); + if (Web.upload_error != 0) { return; } + } else +#endif +#ifdef USE_RF_FLASH + if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_EFM8BB1; + + Web.upload_error = SnfBrUpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif +#ifdef USE_TASMOTA_CLIENT + if (TasmotaClient_Available() && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_TASMOTACLIENT; + + Web.upload_error = TasmotaClient_UpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif + { + if ((upload.buf[0] != 0xE9) && (upload.buf[0] != 0x1F)) { + Web.upload_error = 3; + return; + } + if (0xE9 == upload.buf[0]) { + uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); + if (bin_flash_size > ESP.getFlashChipRealSize()) { + Web.upload_error = 4; + return; + } + + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "File type %d"), Web.upload_file_type); + } + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (!Web.upload_error) { + if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { + Web.upload_error = 9; + return; + } + memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); + Web.config_block_count++; + } + } +#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP) + else if (UPL_EFR32 == Web.upload_file_type) { + + if (!ZigbeeUploadWriteBuffer(upload.buf, upload.currentSize)) { + Web.upload_error = 9; + return; + } + } +#endif +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + if (efm8bb1_update != nullptr) { + ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); + free(efm8bb1_update); + efm8bb1_update = nullptr; + if (result != 0) { + Web.upload_error = abs(result); + return; + } + } + ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); + if (result < 0) { + Web.upload_error = abs(result); + return; + } else if (result > 0) { + if ((size_t)result > upload.currentSize) { + + Web.upload_error = 9; + return; + } + + size_t remnant_sz = upload.currentSize - result; + efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); + if (efm8bb1_update == nullptr) { + Web.upload_error = 2; + return; + } + memcpy(efm8bb1_update, upload.buf + result, remnant_sz); + + efm8bb1_update[remnant_sz] = '\0'; + } + } +#endif +#ifdef USE_TASMOTA_CLIENT + else if (UPL_TASMOTACLIENT == Web.upload_file_type) { + TasmotaClient_WriteBuffer(upload.buf, upload.currentSize); + } +#endif + else { + if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { + Web.upload_error = 5; + return; + } + if (_serialoutput) { + Serial.printf("."); + Web.upload_progress_dot_count++; + if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } + } + } + } + + + else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { + if (_serialoutput && (Web.upload_progress_dot_count % 80)) { + Serial.println(); + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + bool valid_settings = false; + unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8]; + if (buffer_version > 0x06000000) { + uint32_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; + if (buffer_version > 0x0606000A) { + uint32_t buffer_crc32 = settings_buffer[4095] << 24 | settings_buffer[4094] << 16 | settings_buffer[4093] << 8 | settings_buffer[4092]; + valid_settings = (GetCfgCrc32(settings_buffer, buffer_size -4) == buffer_crc32); + } else { + uint16_t buffer_crc16 = settings_buffer[15] << 8 | settings_buffer[14]; + valid_settings = (GetCfgCrc16(settings_buffer, buffer_size) == buffer_crc16); + } + } else { + valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); + } + + if (valid_settings) { +#ifdef ESP8266 + valid_settings = (0 == settings_buffer[0xF36]); +#endif +#ifdef ESP32 + valid_settings = (1 == settings_buffer[0xF36]); +#endif + } + + if (valid_settings) { + SettingsDefaultSet2(); + memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); + Settings.version = buffer_version; + SettingsBufferFree(); + } else { + Web.upload_error = 8; + return; + } + } +#if defined(USE_ZIGBEE) && defined(USE_ZIGBEE_EZSP) + else if (UPL_EFR32 == Web.upload_file_type) { + + ZigbeeUploadDone(); + Web.upload_file_type = UPL_TASMOTA; + } +#endif +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + + Web.upload_file_type = UPL_TASMOTA; + } +#endif +#ifdef USE_TASMOTA_CLIENT + else if (UPL_TASMOTACLIENT == Web.upload_file_type) { + + TasmotaClient_SetFlagFlashing(true); + Web.upload_file_type = UPL_TASMOTA; + } +#endif + else { + if (!Update.end(true)) { + if (_serialoutput) { Update.printError(Serial); } + Web.upload_error = 6; + return; + } + if (!VersionCompatible()) { + Web.upload_error = 14; + return; + } + } + if (!Web.upload_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes"), upload.totalSize); + } + } + + + else if (UPLOAD_FILE_ABORTED == upload.status) { + restart_flag = 0; + MqttRetryCounter(0); +#ifdef USE_COUNTER + CounterInterruptDisable(false); +#endif + Web.upload_error = 7; + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + } + delay(0); +} + + + +void HandlePreflightRequest(void) +{ + HttpHeaderCors(); + Webserver->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); + Webserver->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); + WSSend(200, CT_HTML, ""); +} + + + +void HandleHttpCommand(void) +{ + if (!HttpCheckPriviledgedAccess(false)) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); + + if (strlen(SettingsText(SET_WEBPWD))) { + char tmp1[33]; + WebGetArg("user", tmp1, sizeof(tmp1)); + char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; + WebGetArg("password", tmp2, sizeof(tmp2)); + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { + WSContentBegin(401, CT_JSON); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); + WSContentEnd(); + return; + } + } + + WSContentBegin(200, CT_JSON); + uint32_t curridx = web_log_index; + String svalue = Webserver->arg("cmnd"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); + if (web_log_index != curridx) { + uint32_t counter = curridx; + WSContentSend_P(PSTR("{")); + bool cflg = false; + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { + size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); + cflg = true; + } + } + counter++; + counter &= 0xFF; + if (!counter) counter++; + } while (counter != web_log_index); + WSContentSend_P(PSTR("}")); + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); + } + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); + } + WSContentEnd(); +} + + + +void HandleConsole(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("c2")) { + HandleConsoleRefresh(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); + + WSContentStart_P(S_CONSOLE); + WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_CMND); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +void HandleConsoleRefresh(void) +{ + bool cflg = true; + uint32_t counter = 0; + + String svalue = Webserver->arg("c1"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); + } + + char stmp[8]; + WebGetArg("c2", stmp, sizeof(stmp)); + if (strlen(stmp)) { counter = atoi(stmp); } + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); + if (!Web.reset_web_log_flag) { + counter = 0; + Web.reset_web_log_flag = true; + } + if (counter != web_log_index) { + if (!counter) { + counter = web_log_index; + cflg = false; + } + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } + char stemp[len +1]; + strlcpy(stemp, tmp, len); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); + cflg = true; + } + counter++; + counter &= 0xFF; + if (!counter) { counter++; } + } while (counter != web_log_index); + } + WSContentSend_P(PSTR("}1")); + WSContentEnd(); +} + + + +void HandleNotFound(void) +{ + + + if (CaptivePortal()) { return; } + +#ifdef USE_EMULATION +#ifdef USE_EMULATION_HUE + String path = Webserver->uri(); + if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { + HandleHueApi(&path); + } else +#endif +#endif + { + WSContentBegin(404, CT_PLAIN); + WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args()); + for (uint32_t i = 0; i < Webserver->args(); i++) { + WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str()); + } + WSContentEnd(); + } +} + + +bool CaptivePortal(void) +{ + + if ((WifiIsInManagerMode()) && !ValidIpAddress(Webserver->hostHeader().c_str())) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); + + Webserver->sendHeader(F("Location"), String("http://") + Webserver->client().localIP().toString(), true); + WSSend(302, CT_PLAIN, ""); + Webserver->client().stop(); + return true; + } + return false; +} + + + +String UrlEncode(const String& text) +{ + const char hex[] = "0123456789ABCDEF"; + + String encoded = ""; + int len = text.length(); + int i = 0; + while (i < len) { + char decodedChar = text.charAt(i++); +# 3202 "/workspace/Tasmota/tasmota/xdrv_01_webserver.ino" + if ((' ' == decodedChar) || ('+' == decodedChar)) { + encoded += '%'; + encoded += hex[decodedChar >> 4]; + encoded += hex[decodedChar & 0xF]; + } else { + encoded += decodedChar; + } + + } + return encoded; +} + +int WebSend(char *buffer) +{ + + + + + + char *host; + char *user; + char *password; + char *command; + int status = 1; + + + host = strtok_r(buffer, "]", &command); + if (host && command) { + RemoveSpace(host); + host++; + host = strtok_r(host, ",", &user); + String url = F("http://"); + url += host; + + command = Trim(command); + if (command[0] != '/') { + url += F("/cm?"); + if (user) { + user = strtok_r(user, ":", &password); + if (user && password) { + char userpass[200]; + snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); + url += userpass; + } + } + url += F("cmnd="); + } + url += command; + + DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); + + WiFiClient http_client; + HTTPClient http; + if (http.begin(http_client, UrlEncode(url))) { + int http_code = http.GET(); + if (http_code > 0) { + if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { +#ifdef USE_WEBSEND_RESPONSE + + const char* read = http.getString().c_str(); + uint32_t j = 0; + char text = '.'; + while (text != '\0') { + text = *read++; + if (text > 31) { + mqtt_data[j++] = text; + if (j == sizeof(mqtt_data) -2) { break; } + } + } + mqtt_data[j] = '\0'; + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); +#ifdef USE_SCRIPT +extern uint8_t tasm_cmd_activ; + + tasm_cmd_activ=0; + XdrvRulesProcess(); +#endif +#endif + } + status = 0; + } else { + status = 2; + } + http.end(); + } else { + status = 3; + } + } + return status; +} + +bool JsonWebColor(const char* dataBuf) +{ + + + + + + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); + JsonParserArray arr = root[PSTR(D_CMND_WEBCOLOR)].getArray(); + if (arr) { + uint32_t i = 0; + for (auto color : arr) { + if (i < COL_LAST) { + WebHexCode(i, color.getStr()); + } else { + break; + } + i++; + } + } + return true; +} + +const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; + +const char kWebCommands[] PROGMEM = "|" +#ifdef USE_EMULATION + D_CMND_EMULATION "|" +#endif +#ifdef USE_SENDMAIL + D_CMND_SENDMAIL "|" +#endif + D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" + D_CMND_WEBSENSOR "|" D_CMND_WEBBUTTON "|" D_CMND_CORS; + +void (* const WebCommand[])(void) PROGMEM = { +#ifdef USE_EMULATION + &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, +#endif + &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, + &CmndWebSensor, &CmndWebButton, &CmndCors }; + + + + + +#ifdef USE_EMULATION +void CmndEmulation(void) +{ +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) +#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) + if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { +#else +#ifndef USE_EMULATION_WEMO + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { +#endif +#ifndef USE_EMULATION_HUE + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { +#endif +#endif + Settings.flag2.emulation = XdrvMailbox.payload; + restart_flag = 2; + } +#endif + ResponseCmndNumber(Settings.flag2.emulation); +} +#endif + +#ifdef USE_SENDMAIL +void CmndSendmail(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t result = SendMail(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} +#endif + + +void CmndWebServer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.webserver = XdrvMailbox.payload; + } + if (Settings.webserver) { + Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str()); + } else { + ResponseCmndStateText(0); + } +} + +void CmndWebPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_WEBPWD)); + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndWeblog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.weblog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.weblog_level); +} + +void CmndWebRefresh(void) +{ + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { + Settings.web_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.web_refresh); +} + +void CmndWebSend(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t result = WebSend(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} + +void CmndWebColor(void) +{ + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { + WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); + } + else if (0 == XdrvMailbox.payload) { + SettingsDefaultWebColor(); + } + } + else { + JsonWebColor(XdrvMailbox.data); + } + } + Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); + for (uint32_t i = 0; i < COL_LAST; i++) { + if (i) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); + } + ResponseAppend_P(PSTR("]}")); +} + +void CmndWebSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + } + } + Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); +} + +void CmndWebButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_BUTTON_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_BUTTON1, MAX_BUTTON_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_BUTTON1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } + ResponseCmndIdxChar(SettingsText(SET_BUTTON1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCors(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_CORS)); +} + + + + + +bool Xdrv01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + PollDnsWebserver(); +#ifdef USE_EMULATION + if (Settings.flag2.emulation) { PollUdp(); } +#endif + break; + case FUNC_COMMAND: + result = DecodeCommand(kWebCommands, WebCommand); + break; + } + return result; +} +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_02_mqtt.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_02_mqtt.ino" +#define XDRV_02 2 + + + +#ifdef USE_MQTT_TLS + #include "WiFiClientSecureLightBearSSL.h" + BearSSL::WiFiClientSecure_light *tlsClient; +#endif +WiFiClient EspClient; + +const char kMqttCommands[] PROGMEM = "|" +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + D_CMND_MQTTFINGERPRINT "|" +#endif + D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + D_CMND_TLSKEY "|" +#endif + D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" + D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" + D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; + +void (* const MqttCommand[])(void) PROGMEM = { +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + &CmndMqttFingerprint, +#endif + &CmndMqttUser, &CmndMqttPassword, +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + &CmndTlsKey, +#endif + &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, + &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, + &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; + +struct MQTT { + uint16_t connect_count = 0; + uint16_t retry_counter = 1; + uint8_t initial_connection_state = 2; + bool connected = false; + bool allowed = false; + bool mqtt_tls = false; +} Mqtt; + +#ifdef USE_MQTT_TLS + + +#ifdef USE_MQTT_AWS_IOT +#include + +const br_ec_private_key *AWS_IoT_Private_Key = nullptr; +const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; + +class tls_entry_t { +public: + uint32_t name; + uint16_t start; + uint16_t len; +}; + +const static uint32_t TLS_NAME_SKEY = 0x2079656B; +const static uint32_t TLS_NAME_CRT = 0x20747263; + +class tls_dir_t { +public: + tls_entry_t entry[4]; +}; + +tls_dir_t tls_dir; + +#endif + + + + +bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { + for (uint32_t i = 0; i<20; i++) { + if (finger[i] != value) { + return false; + } + } + return true; +} +#endif + +void MakeValidMqtt(uint32_t option, char* str) +{ + + + uint32_t i = 0; + while (str[i] > 0) { + + if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { + if (option) { + uint32_t j = i; + while (str[j] > 0) { + str[j] = str[j +1]; + j++; + } + i--; + } else { + str[i] = '_'; + } + } + i++; + } +} +# 136 "/workspace/Tasmota/tasmota/xdrv_02_mqtt.ino" +#include + + +#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ + #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1200" +#endif + +PubSubClient MqttClient; + +void MqttInit(void) +{ +#ifdef USE_MQTT_TLS + if ((8883 == Settings.mqtt_port) || (8884 == Settings.mqtt_port)) { + + Settings.flag4.mqtt_tls = true; + } + Mqtt.mqtt_tls = Settings.flag4.mqtt_tls; + + + String host = String(SettingsText(SET_MQTT_HOST)); + if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { + Settings.flag4.mqtt_no_retain = true; + } + + if (Mqtt.mqtt_tls) { + tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); + +#ifdef USE_MQTT_AWS_IOT + loadTlsDir(); + if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) { + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); + } +#endif + +#ifdef USE_MQTT_TLS_CA_CERT + tlsClient->setTrustAnchor(Tasmota_TA, ARRAY_SIZE(Tasmota_TA)); +#endif + + MqttClient.setClient(*tlsClient); + } else { + MqttClient.setClient(EspClient); + } +#else + MqttClient.setClient(EspClient); +#endif +} + +bool MqttIsConnected(void) +{ + return MqttClient.connected(); +} + +void MqttDisconnect(void) +{ + MqttClient.disconnect(); +} + +void MqttSubscribeLib(const char *topic) +{ + MqttClient.subscribe(topic); + MqttClient.loop(); +} + +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.unsubscribe(topic); + MqttClient.loop(); +} + +bool MqttPublishLib(const char* topic, bool retained) +{ + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1)); + if (str == topic) { + mqtt_cmnd_blocked_reset = 4; + mqtt_cmnd_blocked++; + } + } + + bool result = MqttClient.publish(topic, mqtt_data, retained); + yield(); + return result; +} + +void MqttDumpData(char* topic, char* data, uint32_t data_len) { + char dump_data[data_len +1]; + memcpy(dump_data, data, sizeof(dump_data)); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_DATA_SIZE " %d, \"%s %s\""), data_len, topic, RemoveControlCharacter(dump_data)); +} + +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttDataHandler")); +#endif + + + if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } + + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); + if ((str == mqtt_topic) && mqtt_cmnd_blocked) { + mqtt_cmnd_blocked--; + return; + } + } + + + char topic[TOPSZ]; + strlcpy(topic, mqtt_topic, sizeof(topic)); + mqtt_data[data_len] = 0; + char data[data_len +1]; + memcpy(data, mqtt_data, sizeof(data)); + + + + MqttDumpData(topic, data, data_len); + + + XdrvMailbox.index = strlen(topic); + XdrvMailbox.data_len = data_len; + XdrvMailbox.topic = topic; + XdrvMailbox.data = (char*)data; + if (XdrvCall(FUNC_MQTT_DATA)) { return; } + + ShowSource(SRC_MQTT); + + CommandHandler(topic, data, data_len); +} + + + +void MqttRetryCounter(uint8_t value) +{ + Mqtt.retry_counter = value; +} + +void MqttSubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); + MqttSubscribeLib(topic); +} + +void MqttUnsubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); + MqttUnsubscribeLib(topic); +} + +void MqttPublishLogging(const char *mxtime) +{ + char saved_mqtt_data[strlen(mqtt_data) +1]; + memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); + + + Response_P(PSTR("%s%s"), mxtime, log_data); + char stopic[TOPSZ]; + GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING")); + MqttPublishLib(stopic, false); + + memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); +} + +void MqttPublish(const char* topic, bool retained) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttPublish")); +#endif + + if (Settings.flag4.mqtt_no_retain) { + retained = false; + } + + char sretained[CMDSZ]; + sretained[0] = '\0'; + char slog_type[20]; + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); + + if (Settings.flag.mqtt_enabled) { + if (MqttPublishLib(topic, retained)) { + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); + if (retained) { + snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); + } + } + } + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data); + if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) { + log_data[sizeof(log_data) - strlen(sretained) -5] = '\0'; + snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); + AddLog(LOG_LEVEL_INFO); + + if (Settings.ledstate &0x04) { + blinks++; + } +} + +void MqttPublish(const char* topic) +{ + MqttPublish(topic, false); +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) +{ + + + + + + + + char romram[64]; + char stopic[TOPSZ]; + + snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); + for (uint32_t i = 0; i < strlen(romram); i++) { + romram[i] = toupper(romram[i]); + } + prefix &= 3; + GetTopic_P(stopic, prefix, mqtt_topic, romram); + MqttPublish(stopic, retained); + +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) + if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { + + char *topic = SettingsText(SET_MQTT_TOPIC); + char topic2[strlen(topic)+1]; + strcpy(topic2, topic); + + char *s = topic2; + while (*s) { + if ('/' == *s) { + *s = '_'; + } + s++; + } + + snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2); + + + char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1); + if (!mqtt_save) { return; } + strcpy(mqtt_save, mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save); + free(mqtt_save); + + bool result = MqttClient.publish(romram, mqtt_data, false); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); + yield(); + } +#endif +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) +{ + MqttPublishPrefixTopic_P(prefix, subtopic, false); +} + +void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic, bool retained) +{ + MqttPublishPrefixTopic_P(prefix, subtopic, retained); + XdrvRulesProcess(); +} + +void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic) +{ + MqttPublishPrefixTopicRulesProcess_P(prefix, subtopic, false); +} + +void MqttPublishTeleSensor(void) +{ + MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +} + +void MqttPublishPowerState(uint32_t device) +{ + char stopic[TOPSZ]; + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { device = 1; } + +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + if (GetFanspeed() < MaxFanspeed()) { +#ifdef USE_DOMOTICZ + DomoticzUpdateFanState(); +#endif + snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); + MqttPublish(stopic); + } + } else { +#endif + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); + MqttPublish(stopic); + + if (!Settings.flag4.only_json_message) { + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(GetStateText(bitRead(power, device -1))); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); + } +#ifdef USE_SONOFF_IFAN + } +#endif +} + +void MqttPublishAllPowerState(void) +{ + for (uint32_t i = 1; i <= devices_present; i++) { + MqttPublishPowerState(i); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { break; } +#endif + } +} + +void MqttPublishPowerBlinkState(uint32_t device) +{ + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { + device = 1; + } + Response_P(PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"), + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1))); + + MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER); +} + + + +uint16_t MqttConnectCount(void) +{ + return Mqtt.connect_count; +} + +void MqttDisconnected(int state) +{ + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + + MqttClient.disconnect(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); + rules_flag.mqtt_disconnected = 1; +} + +void MqttConnected(void) +{ + char stopic[TOPSZ]; + + if (Mqtt.allowed) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); + Mqtt.connected = true; + Mqtt.retry_counter = 0; + Mqtt.connect_count++; + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(PSTR(MQTT_LWT_ONLINE)); + MqttPublish(stopic, true); + + if (!Settings.flag4.only_json_message) { + + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + } + + GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); + MqttSubscribe(stopic); + if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + GetGroupTopic_P(stopic, PSTR("#"), real_index +i); + MqttSubscribe(stopic); + } + } + GetFallbackTopic_P(stopic, PSTR("#")); + MqttSubscribe(stopic); + } + + XdrvCall(FUNC_MQTT_SUBSCRIBE); + } + + if (Mqtt.initial_connection_state) { + if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { + char stopic2[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); +#ifdef USE_WEBSERVER + if (Settings.webserver) { +#if LWIP_IPV6 + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str()); +#else + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str()); +#endif + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); + } +#endif + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (CrashFlag()) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); + } + + MqttPublishAllPowerState(); + if (Settings.tele_period) { + tele_period = Settings.tele_period -5; + } + rules_flag.system_boot = 1; + XdrvCall(FUNC_MQTT_INIT); + } + Mqtt.initial_connection_state = 0; + + global_state.mqtt_down = 0; + if (Settings.flag.mqtt_enabled) { + rules_flag.mqtt_connected = 1; + } +} + +void MqttReconnect(void) +{ + char stopic[TOPSZ]; + + Mqtt.allowed = Settings.flag.mqtt_enabled; + if (Mqtt.allowed) { +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY + MqttDiscoverServer(); +#endif +#endif + if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { + Mqtt.allowed = false; + } +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + if (Mqtt.mqtt_tls) { + if (0 == strlen(SettingsText(SET_MQTT_PWD))) { + Mqtt.allowed = false; + } + } +#endif + } + if (!Mqtt.allowed) { + MqttConnected(); + return; + } + +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); + + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + global_state.mqtt_down = 1; + + char *mqtt_user = nullptr; + char *mqtt_pwd = nullptr; + if (strlen(SettingsText(SET_MQTT_USER))) { + mqtt_user = SettingsText(SET_MQTT_USER); + } + if (strlen(SettingsText(SET_MQTT_PWD))) { + mqtt_pwd = SettingsText(SET_MQTT_PWD); + } + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(S_LWT_OFFLINE); + + if (MqttClient.connected()) { MqttClient.disconnect(); } +#ifdef USE_MQTT_TLS + if (Mqtt.mqtt_tls) { + tlsClient->stop(); + } else { + EspClient = WiFiClient(); + MqttClient.setClient(EspClient); + } +#else + EspClient = WiFiClient(); + MqttClient.setClient(EspClient); +#endif + + if (2 == Mqtt.initial_connection_state) { + Mqtt.initial_connection_state = 1; + } + + MqttClient.setCallback(MqttDataHandler); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + if (Mqtt.mqtt_tls) { + if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) { + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); + } + } +#endif + MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + + uint32_t mqtt_connect_time = millis(); +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + bool allow_all_fingerprints; + bool learn_fingerprint1; + bool learn_fingerprint2; + if (Mqtt.mqtt_tls) { + allow_all_fingerprints = false; + learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00); + learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff); + allow_all_fingerprints |= learn_fingerprint1; + allow_all_fingerprints |= learn_fingerprint2; + tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); + } +#endif + bool lwt_retain = Settings.flag4.mqtt_no_retain ? false : true; +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + if (Mqtt.mqtt_tls) { + if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) { + + mqtt_user = nullptr; + mqtt_pwd = nullptr; + } + } +#endif + + if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, lwt_retain, mqtt_data, MQTT_CLEAN_SESSION)) { +#ifdef USE_MQTT_TLS + if (Mqtt.mqtt_tls) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"), + millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse()); + if (!tlsClient->getMFLNStatus()) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("MFLN not supported by TLS server")); + } +#ifndef USE_MQTT_TLS_CA_CERT +# 721 "/workspace/Tasmota/tasmota/xdrv_02_mqtt.ino" + const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint(); + + char buf_fingerprint[64]; + ToHex_P(recv_fingerprint, 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); + + bool learned = false; + + + + + + if (recv_fingerprint[20] & 0x1 || (learn_fingerprint1 && 0 != memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20))) { + memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20); + learned = true; + } + + if (recv_fingerprint[20] & 0x2 || (learn_fingerprint2 && 0 != memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20))) { + memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20); + learned = true; + } + + if (learned) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint); + + SettingsSaveAll(); + } + +#endif + } +#endif + MqttConnected(); + } else { +#ifdef USE_MQTT_TLS + if (Mqtt.mqtt_tls) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError()); + } +#endif + MqttDisconnected(MqttClient.state()); + } +} + +void MqttCheck(void) +{ + if (Settings.flag.mqtt_enabled) { + if (!MqttIsConnected()) { + global_state.mqtt_down = 1; + if (!Mqtt.retry_counter) { + MqttReconnect(); + } else { + Mqtt.retry_counter--; + } + } else { + global_state.mqtt_down = 0; + } + } else { + global_state.mqtt_down = 0; + if (Mqtt.initial_connection_state) { + MqttReconnect(); + } + } +} + +bool KeyTopicActive(uint32_t key) +{ + + + key &= 1; + char key_topic[TOPSZ]; + Format(key_topic, SettingsText(SET_MQTT_BUTTON_TOPIC + key), sizeof(key_topic)); + return ((strlen(key_topic) != 0) && strcmp(key_topic, "0")); +} + + + + + +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) +void CmndMqttFingerprint(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + char fingerprint[60]; + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { + if (SC_DEFAULT == Shortcut()) { + memcpy_P(Settings.mqtt_fingerprint[XdrvMailbox.index -1], (1 == XdrvMailbox.index) ? default_fingerprint1 : default_fingerprint2, sizeof(default_fingerprint1)); + } else { + strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : XdrvMailbox.data, sizeof(fingerprint)); + char *p = fingerprint; + for (uint32_t i = 0; i < 20; i++) { + Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); + } + } + restart_flag = 2; + } + ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); + } +} +#endif + +void CmndMqttUser(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_USER)); +} + +void CmndMqttPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_MQTT_PWD)); + restart_flag = 2; + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndMqttlog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.mqttlog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.mqttlog_level); +} + +void CmndMqttHost(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_HOST)); +} + +void CmndMqttPort(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.mqtt_port); +} + +void CmndMqttRetry(void) +{ + if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { + Settings.mqtt_retry = XdrvMailbox.payload; + Mqtt.retry_counter = Settings.mqtt_retry; + } + ResponseCmndNumber(Settings.mqtt_retry); +} + +void CmndStateText(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_STATE_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_STATE_TXT1, MAX_STATE_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { + if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; + } + SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); + } + ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); + } + } +} + +void CmndMqttClient(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); +} + +void CmndFullTopic(void) +{ + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(1, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, S_LWT, true); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); +} + +void CmndPrefix(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_MQTT_PREFIXES)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MQTTPREFIX1, MAX_MQTT_PREFIXES); + } else { + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(0, XdrvMailbox.data); + SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, + (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); + } + } +} + +void CmndPublish(void) +{ + if (XdrvMailbox.data_len > 0) { + char *payload_part; + char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part); + if (mqtt_part) { + char stemp1[TOPSZ]; + strlcpy(stemp1, mqtt_part, sizeof(stemp1)); + if ((payload_part != nullptr) && strlen(payload_part)) { + strlcpy(mqtt_data, payload_part, sizeof(mqtt_data)); + } else { + mqtt_data[0] = '\0'; + } + MqttPublish(stemp1, (XdrvMailbox.index == 2)); + + mqtt_data[0] = '\0'; + } + } +} + +void CmndGroupTopic(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_GROUP_TOPICS)) { + if (XdrvMailbox.data_len > 0) { + uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + + + char stemp[MAX_GROUP_TOPICS][TOPSZ]; + uint32_t read_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + bool not_equal = true; + for (uint32_t j = 0; j < read_index; j++) { + if (!strcmp(SettingsText(real_index +i), stemp[j])) { + not_equal = false; + } + } + if (not_equal) { + strncpy(stemp[read_index], SettingsText(real_index +i), sizeof(stemp[read_index])); + read_index++; + } + } + } + if (0 == read_index) { + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + } else { + uint32_t write_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (write_index < read_index) { + SettingsUpdateText(real_index +i, stemp[write_index]); + write_index++; + } else { + SettingsUpdateText(real_index +i, ""); + } + } + } + + restart_flag = 2; + } + ResponseCmndAll(SET_MQTT_GRP_TOPIC, MAX_GROUP_TOPICS); + } +} + +void CmndTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, S_LWT, true); + SettingsUpdateText(SET_MQTT_TOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); +} + +void CmndButtonTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); +} + +void CmndSwitchTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); +} + +void CmndButtonRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_KEYS; i++) { + SendKey(KEY_BUTTON, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_button_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_button_retain); +} + +void CmndSwitchRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { + SendKey(KEY_SWITCH, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_switch_retain); +} + +void CmndPowerRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + char stemp1[TOPSZ]; + char scommand[CMDSZ]; + for (uint32_t i = 1; i <= devices_present; i++) { + GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); + mqtt_data[0] = '\0'; + MqttPublish(stemp1, Settings.flag.mqtt_power_retain); + } + } + Settings.flag.mqtt_power_retain = XdrvMailbox.payload; + if (Settings.flag.mqtt_power_retain) { + Settings.flag4.only_json_message = 0; + } + } + ResponseCmndStateText(Settings.flag.mqtt_power_retain); +} + +void CmndSensorRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); + } + Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); +} + + + + +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + + +const static uint16_t tls_spi_start_sector = 0xFF; +const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; +const static size_t tls_spi_len = 0x1000; +const static size_t tls_block_offset = 0x0400; +const static size_t tls_block_len = 0x0400; +const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); + + +inline void TlsEraseBuffer(uint8_t *buffer) { + memset(buffer + tls_block_offset, 0xFF, tls_block_len); +} + + + +static br_ec_private_key EC = { + 23, + nullptr, 0 +}; + +static br_x509_certificate CHAIN[] = { + { nullptr, 0 } +}; + + + +void loadTlsDir(void) { + memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); + + + if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { + EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); + EC.xlen = tls_dir.entry[0].len; + AWS_IoT_Private_Key = &EC; + } else { + AWS_IoT_Private_Key = nullptr; + } + if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { + CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); + CHAIN[0].data_len = tls_dir.entry[1].len; + AWS_IoT_Client_Certificate = CHAIN; + } else { + AWS_IoT_Client_Certificate = nullptr; + } + +} + +const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; + +void CmndTlsKey(void) { +#ifdef DEBUG_DUMP_TLS + if (0 == XdrvMailbox.index){ + CmndTlsDump(); + } +#endif + if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { + tls_dir_t *tls_dir_write; + + if (XdrvMailbox.data_len > 0) { + + uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); + if (!spi_buffer) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + return; + } + memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); + + + RemoveSpace(XdrvMailbox.data); + + + uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); + uint8_t *bin_buf = nullptr; + if (bin_len > 0) { + bin_buf = (uint8_t*) malloc(bin_len + 4); + if (!bin_buf) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + free(spi_buffer); + return; + } + } + + + if (bin_len > 0) { + decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); + } + + + tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); + + if (1 == XdrvMailbox.index) { + + + TlsEraseBuffer(spi_buffer); + if (bin_len > 0) { + if (bin_len != 32) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[0]; + entry->name = TLS_NAME_SKEY; + entry->start = 0; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } else { + + } + } else if (2 == XdrvMailbox.index) { + + if (TLS_NAME_SKEY != tls_dir.entry[0].name) { + + AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); + free(spi_buffer); + free(bin_buf); + return; + } + if (bin_len <= 256) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[1]; + entry->name = TLS_NAME_CRT; + entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } + + if (ESP.flashEraseSector(tls_spi_start_sector)) { + ESP.flashWrite(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + free(spi_buffer); + free(bin_buf); + } + + loadTlsDir(); + Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), + XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, + XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); + } +} + +#ifdef DEBUG_DUMP_TLS + +uint32_t bswap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +void CmndTlsDump(void) { + uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; + uint32_t end = start + tls_block_len -1; + for (uint32_t pos = start; pos < end; pos += 0x10) { + uint32_t* values = (uint32_t*)(pos); + Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); + } +} +#endif +#endif + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_MQTT "mq" + +const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; + +const char HTTP_BTN_MENU_MQTT[] PROGMEM = + "

"; + +const char HTTP_FORM_MQTT1[] PROGMEM = + "
 " D_MQTT_PARAMETERS " " + "
" + "

" D_HOST " (" MQTT_HOST ")

" + "

" D_PORT " (" STR(MQTT_PORT) ")

" +#ifdef USE_MQTT_TLS + "


" +#endif + "

" D_CLIENT " (%s)

"; +const char HTTP_FORM_MQTT2[] PROGMEM = + "

" D_USER " (" MQTT_USER ")

" + "


" + "

" D_TOPIC " = %%topic%% (%s)

" + "

" D_FULL_TOPIC " (%s)

"; + +void HandleMqttConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); + + if (Webserver->hasArg("save")) { + MqttSaveSettings(); + WebRestart(1); + return; + } + + char str[TOPSZ]; + + WSContentStart_P(S_CONFIGURE_MQTT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MQTT1, + SettingsText(SET_MQTT_HOST), + Settings.mqtt_port, +#ifdef USE_MQTT_TLS + Mqtt.mqtt_tls ? " checked" : "", +#endif + Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); + WSContentSend_P(HTTP_FORM_MQTT2, + (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), + Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), + MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void MqttSaveSettings(void) +{ + char tmp[TOPSZ]; + char stemp[TOPSZ]; + char stemp2[TOPSZ]; + + WebGetArg("mt", tmp, sizeof(tmp)); + strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); + MakeValidMqtt(0, stemp); + WebGetArg("mf", tmp, sizeof(tmp)); + strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); + MakeValidMqtt(1, stemp2); + if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { + Response_P((Settings.flag.mqtt_offline) ? S_LWT_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, S_LWT, true); + } + SettingsUpdateText(SET_MQTT_TOPIC, stemp); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); + WebGetArg("mh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("ml", tmp, sizeof(tmp)); + Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); +#ifdef USE_MQTT_TLS + Mqtt.mqtt_tls = Webserver->hasArg("b3"); +#endif + WebGetArg("mc", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); +#if defined(USE_MQTT_TLS) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)) + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#else + WebGetArg("mu", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("mp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#endif +} +#endif + + + + + +bool Xdrv02(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_PRE_INIT: + MqttInit(); + break; + case FUNC_EVERY_50_MSECOND: + MqttClient.loop(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MQTT); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_MQTT), HandleMqttConfiguration); + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kMqttCommands, MqttCommand); + break; + } + } + return result; +} +# 1 "/workspace/Tasmota/tasmota/xdrv_03_energy.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_03_energy.ino" +#ifdef USE_ENERGY_SENSOR + + + + +#define XDRV_03 3 +#define XSNS_03 3 + + + + +#define ENERGY_NONE 0 +#define ENERGY_WATCHDOG 4 + +#include + +#define D_CMND_POWERCAL "PowerCal" +#define D_CMND_VOLTAGECAL "VoltageCal" +#define D_CMND_CURRENTCAL "CurrentCal" +#define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" + +enum EnergyCommands { + CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; + +const char kEnergyCommands[] PROGMEM = "|" + D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" +#ifdef USE_ENERGY_MARGIN_DETECTION + D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" +#ifdef USE_ENERGY_POWER_LIMIT + D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" + D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" + D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" +#endif +#endif + D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; + +void (* const EnergyCommand[])(void) PROGMEM = { + &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, +#ifdef USE_ENERGY_MARGIN_DETECTION + &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, +#ifdef USE_ENERGY_POWER_LIMIT + &CmndMaxEnergy, &CmndMaxEnergyStart, + &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, + &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, +#endif +#endif + &CmndEnergyReset, &CmndTariff }; + +const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; + +struct ENERGY { + float voltage[3] = { 0, 0, 0 }; + float current[3] = { 0, 0, 0 }; + float active_power[3] = { 0, 0, 0 }; + float apparent_power[3] = { NAN, NAN, NAN }; + float reactive_power[3] = { NAN, NAN, NAN }; + float power_factor[3] = { NAN, NAN, NAN }; + float frequency[3] = { NAN, NAN, NAN }; + +#ifdef SDM630_IMPORT + float import_active[3] = { NAN, NAN, NAN }; +#endif + float export_active[3] = { NAN, NAN, NAN }; + + float start_energy = 0; + float daily = 0; + float total = 0; + + unsigned long kWhtoday_delta = 0; + unsigned long kWhtoday_offset = 0; + unsigned long kWhtoday; + unsigned long period = 0; + + uint8_t fifth_second = 0; + uint8_t command_code = 0; + uint8_t data_valid[3] = { 0, 0, 0 }; + + uint8_t phase_count = 1; + bool voltage_common = false; + bool frequency_common = false; + bool kWhtoday_offset_init = false; + + bool voltage_available = true; + bool current_available = true; + + bool type_dc = false; + bool power_on = true; + +#ifdef USE_ENERGY_MARGIN_DETECTION + uint16_t power_history[3][3] = {{ 0 }, { 0 }, { 0 }}; + uint8_t power_steady_counter = 8; + bool min_power_flag = false; + bool max_power_flag = false; + bool min_voltage_flag = false; + bool max_voltage_flag = false; + bool min_current_flag = false; + bool max_current_flag = false; + +#ifdef USE_ENERGY_POWER_LIMIT + uint16_t mplh_counter = 0; + uint16_t mplw_counter = 0; + uint8_t mplr_counter = 0; + uint8_t max_energy_state = 0; +#endif +#endif +} Energy; + +Ticker ticker_energy; + + + +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) +{ + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; + return EnergyFormatIndex(result, input, json, index, single); +} + + + +bool EnergyTariff1Active() +{ + uint8_t dst = 0; + if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { + dst = 1; + } + if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { + if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || + (RtcTime.day_of_week == 7))) { + return true; + } + uint32_t minutes = MinutesPastMidnight(); + if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { + + return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); + } else { + + return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); + } + } else { + return false; + } +} + +void EnergyUpdateToday(void) +{ + if (Energy.kWhtoday_delta > 1000) { + unsigned long delta = Energy.kWhtoday_delta / 1000; + Energy.kWhtoday_delta -= (delta * 1000); + Energy.kWhtoday += delta; + } + + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; + Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; + Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; + + if (RtcTime.valid){ + + uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); + + uint32_t return_diff = 0; + if (!isnan(Energy.export_active[0])) { + + + + float export_active = 0.0; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (!isnan(Energy.export_active[i])) { + export_active += Energy.export_active[i]; + } + } + return_diff = (uint32_t)(export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; + RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(export_active * 100000); + } + + if (EnergyTariff1Active()) { + RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; + RtcSettings.energy_usage.return1_kWhtotal += return_diff; + } else { + RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; + RtcSettings.energy_usage.return2_kWhtotal += return_diff; + } + } +} + +void EnergyUpdateTotal(float value, bool kwh) +{ + + + + + uint32_t multiplier = (kwh) ? 100000 : 100; + + if (0 == Energy.start_energy || (value < Energy.start_energy)) { + Energy.start_energy = value; + } + else if (value != Energy.start_energy) { + Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + } + + if ((Energy.total < (value - 0.01)) && + Settings.flag3.hardware_energy_total) { + RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + + } + EnergyUpdateToday(); +} + + + +void Energy200ms(void) +{ + Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; + + Energy.fifth_second++; + if (5 == Energy.fifth_second) { + Energy.fifth_second = 0; + + XnrgCall(FUNC_ENERGY_EVERY_SECOND); + + if (RtcTime.valid) { + + if (!Energy.kWhtoday_offset_init && (RtcTime.day_of_year == Settings.energy_kWhdoy)) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + Energy.kWhtoday_offset_init = true; + } + + if (LocalTime() == Midnight()) { + Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; + + RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Energy.period -= RtcSettings.energy_kWhtoday; + Energy.kWhtoday = 0; + Energy.kWhtoday_offset = 0; + RtcSettings.energy_kWhtoday = 0; + Energy.start_energy = 0; + + + EnergyUpdateToday(); +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + Energy.max_energy_state = 3; +#endif + } +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { + Energy.max_energy_state = 0; + } +#endif + + } + } + + XnrgCall(FUNC_EVERY_200_MSECOND); +} + +void EnergySaveState(void) +{ + Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; + + Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_usage = RtcSettings.energy_usage; +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) +{ + bool change; + + if (!margin) return false; + change = save_flag; + if (type) { + flag = (value > margin); + } else { + flag = (value < margin); + } + save_flag = flag; + return (change != save_flag); +} + +void EnergyMarginCheck(void) +{ + if (Energy.power_steady_counter) { + Energy.power_steady_counter--; + return; + } + + bool jsonflg = false; + Response_P(PSTR("{\"" D_RSLT_MARGINS "\":{")); + + int16_t power_diff[3] = { 0 }; + for (uint32_t phase = 0; phase < Energy.phase_count; phase++) { + uint16_t active_power = (uint16_t)(Energy.active_power[phase]); + + if (Settings.energy_power_delta[phase]) { + power_diff[phase] = active_power - Energy.power_history[phase][0]; + uint16_t delta = abs(power_diff[phase]); + bool threshold_met = false; + if (delta > 0) { + if (Settings.energy_power_delta[phase] < 101) { + uint16_t min_power = (Energy.power_history[phase][0] > active_power) ? active_power : Energy.power_history[phase][0]; + if (0 == min_power) { min_power++; } + delta = (delta * 100) / min_power; + if (delta > Settings.energy_power_delta[phase]) { + threshold_met = true; + } + } else { + if (delta > (Settings.energy_power_delta[phase] -100)) { + threshold_met = true; + } + } + } + if (threshold_met) { + Energy.power_history[phase][1] = active_power; + Energy.power_history[phase][2] = active_power; + jsonflg = true; + } else { + power_diff[phase] = 0; + } + } + Energy.power_history[phase][0] = Energy.power_history[phase][1]; + Energy.power_history[phase][1] = Energy.power_history[phase][2]; + Energy.power_history[phase][2] = active_power; + } + if (jsonflg) { + char power_diff_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t phase = 0; phase < Energy.phase_count; phase++) { + dtostrfd(power_diff[phase], 0, power_diff_chr[phase]); + } + char value_chr[FLOATSZ *3]; + ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_chr[0], 1)); + } + + uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); + + if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { + uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); + uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); + + DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); + + bool flag; + if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + } + if (jsonflg) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_MARGINS), MQTT_TELE_RETAIN); + EnergyMqttShow(); + } + +#ifdef USE_ENERGY_POWER_LIMIT + + if (Settings.energy_max_power_limit) { + if (Energy.active_power[0] > Settings.energy_max_power_limit) { + if (!Energy.mplh_counter) { + Energy.mplh_counter = Settings.energy_max_power_limit_hold; + } else { + Energy.mplh_counter--; + if (!Energy.mplh_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + if (!Energy.mplr_counter) { + Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; + } + Energy.mplw_counter = Settings.energy_max_power_limit_window; + } + } + } + else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { + Energy.mplh_counter = 0; + Energy.mplr_counter = 0; + Energy.mplw_counter = 0; + } + if (!power) { + if (Energy.mplw_counter) { + Energy.mplw_counter--; + } else { + if (Energy.mplr_counter) { + Energy.mplr_counter--; + if (Energy.mplr_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); + RestorePower(true, SRC_MAXPOWER); + } else { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + } + } + } + } + } + + + if (Settings.energy_max_energy) { + uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000); + if (!Energy.max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { + Energy.max_energy_state = 1; + ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); + RestorePower(true, SRC_MAXENERGY); + } + else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { + Energy.max_energy_state = 2; + char stemp[FLOATSZ]; + dtostrfd(Energy.daily, 3, stemp); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); + } + } +#endif +} + +void EnergyMqttShow(void) +{ + + int tele_period_save = tele_period; + tele_period = 2; + mqtt_data[0] = '\0'; + ResponseAppendTime(); + EnergyShow(true); + tele_period = tele_period_save; + ResponseJsonEnd(); + MqttPublishTeleSensor(); +} +#endif + +void EnergyEverySecond(void) +{ + + if (global_update) { + if (power && !isnan(global_temperature_celsius) && (global_temperature_celsius > (float)Settings.param[P_OVER_TEMP])) { + + char temperature[33]; + dtostrfd(global_temperature_celsius, 1, temperature); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: GlobTemp %s"), temperature); + + SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); + } + } + + + uint32_t data_valid = Energy.phase_count; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { + Energy.data_valid[i]++; + if (Energy.data_valid[i] > ENERGY_WATCHDOG) { + + Energy.voltage[i] = 0; + Energy.current[i] = 0; + Energy.active_power[i] = 0; + if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } + if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } + if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } + if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } + if (!isnan(Energy.export_active[i])) { Energy.export_active[i] = 0; } + + data_valid--; + } + } + } + if (!data_valid) { + Energy.start_energy = 0; + + XnrgCall(FUNC_ENERGY_RESET); + } + +#ifdef USE_ENERGY_MARGIN_DETECTION + EnergyMarginCheck(); +#endif +} + + + + + +void EnergyCommandCalResponse(uint32_t nvalue) +{ + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); + ResponseCmndNumber(nvalue); +} + +void CmndEnergyReset(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + char *p; + unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); + if (p != XdrvMailbox.data) { + switch (XdrvMailbox.index) { + case 1: + + Energy.kWhtoday_offset = lnum *100; + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.start_energy = 0; + Energy.period = Energy.kWhtoday_offset; + Settings.energy_kWhtoday = Energy.kWhtoday_offset; + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; + Energy.daily = (float)Energy.kWhtoday_offset / 100000; + if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { + Settings.energy_kWhtotal_time = LocalTime(); + } + break; + case 2: + + Settings.energy_kWhyesterday = lnum *100; + break; + case 3: + + RtcSettings.energy_kWhtotal = lnum *100; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); + break; + } + } + } + else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { + uint32_t values[2] = { 0 }; + uint32_t position = ParseParameters(2, values); + values[0] *= 100; + values[1] *= 100; + + switch (XdrvMailbox.index) + { + case 4: + + if (position > 0) { + RtcSettings.energy_usage.usage1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.usage2_kWhtotal = values[1]; + } + Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; + Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; + break; + case 5: + + if (position > 0) { + RtcSettings.energy_usage.return1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.return2_kWhtotal = values[1]; + } + Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; + Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; + break; + } + } + + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + + char energy_total_chr[FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_usage1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); + char energy_usage2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); + char energy_return1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); + char energy_return2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); + + Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), + XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); +} + +void CmndTariff(void) +{ + + + + + + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + uint32_t tariff = XdrvMailbox.index -1; + uint32_t time_type = 0; + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + while ((str != nullptr) && (time_type < 2)) { + char *q; + uint32_t value = strtol(str, &q, 10); + Settings.tariff[tariff][time_type] = value; + if (value < 24) { + Settings.tariff[tariff][time_type] *= 60; + char *minute = strtok_r(nullptr, ":", &q); + if (minute) { + value = strtol(minute, nullptr, 10); + if (value > 59) { + value = 59; + } + Settings.tariff[tariff][time_type] += value; + } + } + if (Settings.tariff[tariff][time_type] > 1439) { + Settings.tariff[tariff][time_type] = 1439; + } + str = strtok_r(nullptr, ", ", &p); + time_type++; + } + } + else if (XdrvMailbox.index == 9) { + Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; + } + Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), + XdrvMailbox.command, + GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), + GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), + GetStateText(Settings.flag3.energy_weekend)); +} + +void CmndPowerCal(void) +{ + Energy.command_code = CMND_POWERCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_power_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_calibration); + } +} + +void CmndVoltageCal(void) +{ + Energy.command_code = CMND_VOLTAGECAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_voltage_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentCal(void) +{ + Energy.command_code = CMND_CURRENTCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_current_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_current_calibration); + } +} + +void CmndPowerSet(void) +{ + Energy.command_code = CMND_POWERSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_power_calibration); + } +} + +void CmndVoltageSet(void) +{ + Energy.command_code = CMND_VOLTAGESET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentSet(void) +{ + Energy.command_code = CMND_CURRENTSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_current_calibration); + } +} + +void CmndFrequencySet(void) +{ + Energy.command_code = CMND_FREQUENCYSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_frequency_calibration); + } +} + +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { + ResponseCmndDone(); + } + } +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +void CmndPowerDelta(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { + Settings.energy_power_delta[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.energy_power_delta[XdrvMailbox.index -1]); + } +} + +void CmndPowerLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_min_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_power); +} + +void CmndPowerHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power); +} + +void CmndVoltageLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_min_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_voltage); +} + +void CmndVoltageHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_max_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_voltage); +} + +void CmndCurrentLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_min_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_current); +} + +void CmndCurrentHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_max_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_current); +} + +#ifdef USE_ENERGY_POWER_LIMIT +void CmndMaxPower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit); +} + +void CmndMaxPowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_hold); +} + +void CmndMaxPowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_window); +} + +void CmndSafePower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit); +} + +void CmndSafePowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); +} + +void CmndSafePowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { + Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); +} + +void CmndMaxEnergy(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_energy = XdrvMailbox.payload; + Energy.max_energy_state = 3; + } + ResponseCmndNumber(Settings.energy_max_energy); +} + +void CmndMaxEnergyStart(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { + Settings.energy_max_energy_start = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_energy_start); +} +#endif +#endif + +void EnergySnsInit(void) +{ + XnrgCall(FUNC_INIT); + + if (energy_flg) { + Energy.kWhtoday_offset = 0; + + if ((ResetReason() != REASON_DEFAULT_RST) && RtcSettingsValid()) { + Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + Energy.kWhtoday_offset_init = true; + } + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday_offset; + EnergyUpdateToday(); + ticker_energy.attach_ms(200, Energy200ms); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SNS1[] PROGMEM = + "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" + "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" + "{s}" D_POWER_FACTOR "{m}%s{e}"; + +const char HTTP_ENERGY_SNS2[] PROGMEM = + "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; + +const char HTTP_ENERGY_SNS3[] PROGMEM = + "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; + +#ifdef SDM630_IMPORT +const char HTTP_ENERGY_SNS4[] PROGMEM = + "{s}" D_IMPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; +#endif +#endif + +void EnergyShow(bool json) +{ + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.voltage_common) { + Energy.voltage[i] = Energy.voltage[0]; + } + } + + float power_factor_knx = Energy.power_factor[0]; + + char apparent_power_chr[Energy.phase_count][FLOATSZ]; + char reactive_power_chr[Energy.phase_count][FLOATSZ]; + char power_factor_chr[Energy.phase_count][FLOATSZ]; + char frequency_chr[Energy.phase_count][FLOATSZ]; + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float apparent_power = Energy.apparent_power[i]; + if (isnan(apparent_power)) { + apparent_power = Energy.voltage[i] * Energy.current[i]; + } + if (apparent_power < Energy.active_power[i]) { + Energy.active_power[i] = apparent_power; + } + + float power_factor = Energy.power_factor[i]; + if (isnan(power_factor)) { + power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; + if (power_factor > 1) { + power_factor = 1; + } + } + if (0 == i) { power_factor_knx = power_factor; } + + float reactive_power = Energy.reactive_power[i]; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + + + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); + dtostrfd(power_factor, 2, power_factor_chr[i]); + } + } + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float frequency = Energy.frequency[i]; + if (isnan(Energy.frequency[i])) { + frequency = 0; + } + dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); + } + } + + char voltage_chr[Energy.phase_count][FLOATSZ]; + char current_chr[Energy.phase_count][FLOATSZ]; + char active_power_chr[Energy.phase_count][FLOATSZ]; +#ifdef SDM630_IMPORT + char import_active_chr[Energy.phase_count][FLOATSZ]; +#endif + char export_active_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); + dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); + dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); +#ifdef SDM630_IMPORT + dtostrfd(Energy.import_active[i], Settings.flag2.energy_resolution, import_active_chr[i]); +#endif + dtostrfd(Energy.export_active[i], Settings.flag2.energy_resolution, export_active_chr[i]); + } + + char energy_total_chr[FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + + bool energy_tariff = false; + char energy_usage_chr[2][FLOATSZ]; + char energy_return_chr[2][FLOATSZ]; + if (Settings.tariff[0][0] != Settings.tariff[1][0]) { + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage_chr[0]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return_chr[0]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return_chr[1]); + energy_tariff = true; + } + + char value_chr[FLOATSZ *3]; + char value2_chr[FLOATSZ *3]; + char value3_chr[FLOATSZ *3]; + + if (json) { + bool show_energy_period = (0 == tele_period); + + ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s"), + GetDateAndTime(DT_ENERGY).c_str(), + energy_total_chr); + + if (energy_tariff) { + ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL D_CMND_TARIFF "\":%s"), + EnergyFormatIndex(value_chr, energy_usage_chr[0], json, 2)); + } + + ResponseAppend_P(PSTR(",\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), + energy_yesterday_chr, + energy_daily_chr); + + #ifdef SDM630_IMPORT + if (!isnan(Energy.import_active[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s"), + EnergyFormat(value_chr, import_active_chr[0], json)); + if (energy_tariff) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT D_CMND_TARIFF "\":%s"), + EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2)); + } + } +#endif + + if (!isnan(Energy.export_active[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), + EnergyFormat(value_chr, export_active_chr[0], json)); + if (energy_tariff) { + ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT D_CMND_TARIFF "\":%s"), + EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2)); + } + } + + if (show_energy_period) { + float energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; + Energy.period = RtcSettings.energy_kWhtoday; + char energy_period_chr[FLOATSZ]; + dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + } + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), + EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), + EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common)); + } + } + if (Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), + EnergyFormat(value_chr, current_chr[0], json)); + } + XnrgCall(FUNC_JSON_APPEND); + ResponseJsonEnd(); + +#ifdef USE_DOMOTICZ + if (show_energy_period) { + dtostrfd(Energy.total * 1000, 1, energy_total_chr); + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr); + + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_usage_chr[0]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_usage_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, energy_return_chr[0]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, energy_return_chr[1]); + DomoticzSensorP1SmartMeter(energy_usage_chr[0], energy_usage_chr[1], energy_return_chr[0], energy_return_chr[1], (int)Energy.active_power[0]); + + if (Energy.voltage_available) { + DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); + } + if (Energy.current_available) { + DomoticzSensor(DZ_CURRENT, current_chr[0]); + } + } +#endif +#ifdef USE_KNX + if (show_energy_period) { + if (Energy.voltage_available) { + KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); + } + if (Energy.current_available) { + KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); + } + KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + if (!Energy.type_dc) { + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); + } + KnxSensor(KNX_ENERGY_DAILY, Energy.daily); + KnxSensor(KNX_ENERGY_TOTAL, Energy.total); + KnxSensor(KNX_ENERGY_START, Energy.start_energy); + } +#endif +#ifdef USE_WEBSERVER + } else { + if (Energy.voltage_available) { + WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); + } + WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common)); + } + } + WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr); + if (!isnan(Energy.export_active[0])) { + WSContentSend_PD(HTTP_ENERGY_SNS3, EnergyFormat(value_chr, export_active_chr[0], json)); + } +#ifdef SDM630_IMPORT + if (!isnan(Energy.import_active[0])) { + WSContentSend_PD(HTTP_ENERGY_SNS4, EnergyFormat(value_chr, import_active_chr[0], json)); + } +#endif + + XnrgCall(FUNC_WEB_SENSOR); +#endif + } +} + + + + + +bool Xdrv03(uint8_t function) +{ + bool result = false; + + if (FUNC_PRE_INIT == function) { + energy_flg = ENERGY_NONE; + XnrgCall(FUNC_PRE_INIT); + } + else if (energy_flg) { + switch (function) { + case FUNC_LOOP: + XnrgCall(FUNC_LOOP); + break; + case FUNC_EVERY_250_MSECOND: + XnrgCall(FUNC_EVERY_250_MSECOND); + break; + case FUNC_EVERY_SECOND: + XnrgCall(FUNC_EVERY_SECOND); + break; + case FUNC_SERIAL: + result = XnrgCall(FUNC_SERIAL); + break; +#ifdef USE_ENERGY_MARGIN_DETECTION + case FUNC_SET_POWER: + Energy.power_steady_counter = 2; + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kEnergyCommands, EnergyCommand); + break; + } + } + return result; +} + +bool Xsns03(uint8_t function) +{ + bool result = false; + + if (energy_flg) { + switch (function) { + case FUNC_EVERY_SECOND: + EnergyEverySecond(); + break; + case FUNC_JSON_APPEND: + EnergyShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + EnergyShow(false); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + EnergySaveState(); + break; + case FUNC_INIT: + EnergySnsInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +#ifdef USE_LIGHT +# 124 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +#define XDRV_04 4 + + +enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; + +const uint8_t LIGHT_COLOR_SIZE = 25; + +const char kLightCommands[] PROGMEM = "|" + D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" + D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR +#ifdef USE_LIGHT_PALETTE + "|" D_CMND_PALETTE +#endif +#ifdef USE_DGR_LIGHT_SEQUENCE + "|" D_CMND_SEQUENCE_OFFSET +#endif + "|UNDOCA" ; + +void (* const LightCommand[])(void) PROGMEM = { + &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, + &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, + &CmndWhite, &CmndChannel, &CmndHsbColor, +#ifdef USE_LIGHT_PALETTE + &CmndPalette, +#endif +#ifdef USE_DGR_LIGHT_SEQUENCE + &CmndSequenceOffset, +#endif + &CmndUndocA }; + + +enum LightColorModes { + LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 }; + +struct LRgbColor { + uint8_t R, G, B; +}; +const uint8_t MAX_FIXED_COLOR = 12; +const LRgbColor kFixedColor[MAX_FIXED_COLOR] PROGMEM = + { 255,0,0, 0,255,0, 0,0,255, 228,32,0, 0,228,32, 0,32,228, 188,64,0, 0,160,96, 160,32,240, 255,255,0, 255,0,170, 255,255,255 }; + +struct LWColor { + uint8_t W; +}; +const uint8_t MAX_FIXED_WHITE = 4; +const LWColor kFixedWhite[MAX_FIXED_WHITE] PROGMEM = { 0, 255, 128, 32 }; + +struct LCwColor { + uint8_t C, W; +}; +const uint8_t MAX_FIXED_COLD_WARM = 4; +const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; + + +const uint16_t CT_MIN = 153; +const uint16_t CT_MAX = 500; + +const uint16_t CT_MIN_ALEXA = 200; +const uint16_t CT_MAX_ALEXA = 380; + + + + + + +typedef struct gamma_table_t { + uint16_t to_src; + uint16_t to_gamma; +} gamma_table_t; + +const gamma_table_t gamma_table[] = { + { 1, 1 }, + { 4, 1 }, + { 209, 13 }, + { 312, 41 }, + { 457, 106 }, + { 626, 261 }, + { 762, 450 }, + { 895, 703 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; + + +const gamma_table_t gamma_table_fast[] = { + { 384, 192 }, + { 768, 576 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; +# 262 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +struct LIGHT { + uint32_t strip_timer_counter = 0; + power_t power = 0; + + uint8_t entry_color[LST_MAX]; + uint8_t current_color[LST_MAX]; + uint8_t new_color[LST_MAX]; + uint8_t last_color[LST_MAX]; + uint8_t color_remap[LST_MAX]; + + uint8_t wheel = 0; + uint8_t random = 0; + uint8_t subtype = 0; + uint8_t device = 0; + uint8_t old_power = 1; + uint8_t wakeup_active = 0; + uint8_t fixed_color_index = 1; + uint8_t pwm_offset = 0; + uint8_t max_scheme = LS_MAX -1; + + uint32_t wakeup_start_time = 0; + + bool update = true; + bool pwm_multi_channels = false; + bool virtual_ct = false; + + bool fade_initialized = false; + bool fade_running = false; +#ifdef USE_DEVICE_GROUPS + uint8_t last_scheme = 0; + bool devgrp_no_channels_out = false; +#ifdef USE_DGR_LIGHT_SEQUENCE + uint8_t sequence_offset = 0; + uint8_t * channels_fifo; +#endif +#endif +#ifdef USE_LIGHT_PALETTE + uint8_t palette_count = 0; + uint8_t * palette; +#endif + uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; + uint16_t fade_cur_10[LST_MAX]; + uint16_t fade_end_10[LST_MAX]; + uint16_t fade_duration = 0; + uint32_t fade_start = 0; + + uint16_t pwm_min = 0; + uint16_t pwm_max = 1023; +} Light; + +power_t LightPower(void) +{ + return Light.power; +} + +uint8_t LightDevice(void) +{ + return Light.device; +} + +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { + return (a < b && a < c) ? a : (b < c) ? b : c; +} +# 363 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +class LightStateClass { + private: + uint16_t _hue = 0; + uint8_t _sat = 255; + uint8_t _briRGB = 255; + + uint8_t _r = 255; + uint8_t _g = 255; + uint8_t _b = 255; + + uint8_t _subtype = 0; + uint16_t _ct = CT_MIN; + uint8_t _wc = 255; + uint8_t _ww = 0; + uint8_t _briCT = 255; + + uint8_t _color_mode = LCM_RGB; + + + + + + uint16_t _ct_min_range = CT_MIN; + uint16_t _ct_max_range = CT_MAX; + + public: + LightStateClass() { + + } + + void setSubType(uint8_t sub_type) { + _subtype = sub_type; + } +# 405 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" + uint8_t setColorMode(uint8_t cm) { + uint8_t prev_cm = _color_mode; + if (cm < LCM_RGB) { cm = LCM_RGB; } + if (cm > LCM_BOTH) { cm = LCM_BOTH; } + uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT; + + switch (_subtype) { + case LST_COLDWARM: + _color_mode = LCM_CT; + break; + + case LST_NONE: + case LST_SINGLE: + case LST_RGB: + default: + _color_mode = LCM_RGB; + break; + + case LST_RGBW: + case LST_RGBCW: + _color_mode = cm; + break; + } + if (LCM_RGB == _color_mode) { + _briCT = 0; + if (0 == _briRGB) { _briRGB = maxbri; } + } + if (LCM_CT == _color_mode) { + _briRGB = 0; + if (0 == _briCT) { _briCT = maxbri; } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode); +#endif + return prev_cm; + } + + inline uint8_t getColorMode() { + return _color_mode; + } + + void addRGBMode() { + setColorMode(_color_mode | LCM_RGB); + } + void addCTMode() { + setColorMode(_color_mode | LCM_CT); + } + + + void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) { + if (r) { *r = _r; } + if (g) { *g = _g; } + if (b) { *b = _b; } + } + + + + void getCW(uint8_t *rc, uint8_t *rw) { + if (rc) { *rc = _wc; } + if (rw) { *rw = _ww; } + } + + + void getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) { + bool rgb_channels_on = _color_mode & LCM_RGB; + bool ct_channels_on = _color_mode & LCM_CT; + + if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; } + if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; } + if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; } + + if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; } + if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; } + } + + void getChannels(uint8_t *channels) { + getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); + } + + void getChannelsRaw(uint8_t *channels) { + channels[0] = _r; + channels[1] = _g; + channels[2] = _b; + channels[3] = _wc; + channels[4] = _ww; + } + + void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + if (hue) { *hue = _hue; } + if (sat) { *sat = _sat; } + if (bri) { *bri = _briRGB; } + } + + + uint8_t getBri(void) { + + return (_briRGB >= _briCT) ? _briRGB : _briCT; + } + + + inline uint8_t getBriCT() { + return _briCT; + } + + static inline uint8_t DimmerToBri(uint8_t dimmer) { + return changeUIntScale(dimmer, 0, 100, 0, 255); + } + static uint8_t BriToDimmer(uint8_t bri) { + uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); + + if ((dimmer == 0) && (bri > 0)) { dimmer = 1; } + return dimmer; + } + + uint8_t getDimmer(uint32_t mode = 0) { + uint8_t bri; + switch (mode) { + case 1: + bri = getBriRGB(); + break; + case 2: + bri = getBriCT(); + break; + default: + bri = getBri(); + break; + } + return BriToDimmer(bri); + } + + inline uint16_t getCT() const { + return _ct; + } + + + uint16_t getCT10bits() const { + return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); + } + + inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { + _ct_min_range = ct_min_range; + _ct_max_range = ct_max_range; + } + + inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { + if (ct_min_range) { *ct_min_range = _ct_min_range; } + if (ct_max_range) { *ct_max_range = _ct_max_range; } + } + + + void getXY(float *x, float *y) { + RgbToXy(_r, _g, _b, x, y); + } + + + + void setBri(uint8_t bri) { + setBriRGB(_color_mode & LCM_RGB ? bri : 0); + setBriCT(_color_mode & LCM_CT ? bri : 0); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + } + + + uint8_t setBriRGB(uint8_t bri_rgb) { + uint8_t prev_bri = _briRGB; + _briRGB = bri_rgb; + if (bri_rgb > 0) { addRGBMode(); } +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type) PWMDimmerSetBrightnessLeds(-1); +#endif + return prev_bri; + } + + + uint8_t setBriCT(uint8_t bri_ct) { + uint8_t prev_bri = _briCT; + _briCT = bri_ct; + if (bri_ct > 0) { addCTMode(); } +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type) PWMDimmerSetBrightnessLeds(-1); +#endif + return prev_bri; + } + + inline uint8_t getBriRGB() { + return _briRGB; + } + + void setDimmer(uint8_t dimmer) { + setBri(DimmerToBri(dimmer)); + } + + void setCT(uint16_t ct) { + if (0 == ct) { + + setColorMode(LCM_RGB); + } else { + ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); + _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); + _wc = 255 - _ww; + _ct = ct; + addCTMode(); + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct); +#endif + } +# 629 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" + void setCW(uint8_t c, uint8_t w, bool free_range = false) { + uint16_t max = (w > c) ? w : c; + uint16_t sum = c + w; + if (sum <= 257) { free_range = false; } + + if (0 == max) { + _briCT = 0; + setColorMode(LCM_RGB); + } else { + if (!free_range) { + + _ww = changeUIntScale(w, 0, sum, 0, 255); + _wc = 255 - _ww; + } else { + _ww = changeUIntScale(w, 0, max, 0, 255); + _wc = changeUIntScale(c, 0, max, 0, 255); + } + _ct = changeUIntScale(w, 0, sum, CT_MIN, CT_MAX); + addCTMode(); + if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT); +#endif + } + + + uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + uint16_t hue; + uint8_t sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b); +#endif + + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + + if (0 == max) { + r = g = b = 255; + setColorMode(LCM_CT); + } else { + if (255 > max) { + + r = changeUIntScale(r, 0, max, 0, 255); + g = changeUIntScale(g, 0, max, 0, 255); + b = changeUIntScale(b, 0, max, 0, 255); + } + addRGBMode(); + } + if (!keep_bri) { + _briRGB = (_color_mode & LCM_RGB) ? max : 0; + } + + RgbToHsb(r, g, b, &hue, &sat, nullptr); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + return max; + } + + void setHS(uint16_t hue, uint8_t sat) { + uint8_t r, g, b; + HsToRgb(hue, sat, &r, &g, &b); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; + addRGBMode(); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + } + + + + void setChannelsRaw(uint8_t *channels) { + _r = channels[0]; + _g = channels[1]; + _b = channels[2]; + _wc = channels[3]; + _ww = channels[4]; +} + + + + + void setChannels(uint8_t *channels) { + setRGB(channels[0], channels[1], channels[2]); + setCW(channels[3], channels[4], true); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", + channels[0], channels[1], channels[2], channels[3], channels[4]); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels Actuals (%d %d %d %d %d)", + _r, _g, _b, _wc, _ww); +#endif + } + + + static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); + static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); + static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); + static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); + +}; +# 748 "/workspace/Tasmota/tasmota/xdrv_04_light.ino" +void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { + uint32_t r = ir; + uint32_t g = ig; + uint32_t b = ib; + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; + uint32_t d = max - min; + + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = max; + + if (d != 0) { + sat = changeUIntScale(d, 0, max, 0, 255); + if (r == max) { + hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); + } else if (g == max) { + hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); + } else { + hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); + } + hue = hue % 360; + } + + if (r_hue) *r_hue = hue; + if (r_sat) *r_sat = sat; + if (r_bri) *r_bri = bri; + +} + +void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + uint32_t r = 255; + uint32_t g = 255; + uint32_t b = 255; + + hue = hue % 360; + + if (sat > 0) { + uint32_t i = hue / 60; + uint32_t f = hue % 60; + uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); + uint32_t p = 255 - sat; + uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); + + switch (i) { + case 0: + + g = t; + b = p; + break; + case 1: + r = q; + + b = p; + break; + case 2: + r = p; + + b = t; + break; + case 3: + r = p; + g = q; + + break; + case 4: + r = t; + g = p; + + break; + default: + + g = p; + b = q; + break; + } + } + if (r_r) *r_r = r; + if (r_g) *r_g = g; + if (r_b) *r_b = b; +} + +#define POW FastPrecisePowf + + + + +void mat3x3(const float *mat33, const float *vec3, float *res3) { + for (uint32_t i = 0; i < 3; i++) { + const float * v = vec3; + *res3 = 0.0f; + for (uint32_t j = 0; j < 3; j++) { + *res3 += *mat33++ * *v++; + } + res3++; + } +} + +void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { + float x = 0.31271f; + float y = 0.32902f; + + if (i_r + i_b + i_g > 0) { + float rgb[3] = { (float)i_r, (float)i_g, (float)i_b }; + + + for (uint32_t i = 0; i < 3; i++) { + rgb[i] = rgb[i] / 255.0f; + rgb[i] = (rgb[i] > 0.04045f) ? POW((rgb[i] + 0.055f) / (1.0f + 0.055f), 2.4f) : (rgb[i] / 12.92f); + } + + + + float XYZ[3]; + static const float XYZ_factors[] = { 0.649926f, 0.103455f, 0.197109f, + 0.234327f, 0.743075f, 0.022598f, + 0.000000f, 0.053077f, 1.035763f }; + mat3x3(XYZ_factors, rgb, XYZ); + + float XYZ_sum = XYZ[0] + XYZ[1] + XYZ[2]; + x = XYZ[0] / XYZ_sum; + y = XYZ[1] / XYZ_sum; + + } + if (r_x) *r_x = x; + if (r_y) *r_y = y; +} + +void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) +{ + float XYZ[3], rgb[3]; + x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); + y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); + float z = 1.0f - x - y; + XYZ[0] = x / y; + XYZ[1] = 1.0f; + XYZ[2] = z / y; + + static const float rgb_factors[] = { 3.2406f, -1.5372f, -0.4986f, + -0.9689f, 1.8758f, 0.0415f, + 0.0557f, -0.2040f, 1.0570f }; + mat3x3(rgb_factors, XYZ, rgb); + float max = (rgb[0] > rgb[1] && rgb[0] > rgb[2]) ? rgb[0] : (rgb[1] > rgb[2]) ? rgb[1] : rgb[2]; + + for (uint32_t i = 0; i < 3; i++) { + rgb[i] = rgb[i] / max; + rgb[i] = (rgb[i] <= 0.0031308f) ? 12.92f * rgb[i] : 1.055f * POW(rgb[i], (1.0f / 2.4f)) - 0.055f; + } + + int32_t irgb[3]; + for (uint32_t i = 0; i < 3; i++) { + irgb[i] = rgb[i] * 255.0f + 0.5f; + } + + if (rr) { *rr = (irgb[0] > 255 ? 255: (irgb[0] < 0 ? 0 : irgb[0])); } + if (rg) { *rg = (irgb[1] > 255 ? 255: (irgb[1] < 0 ? 0 : irgb[1])); } + if (rb) { *rb = (irgb[2] > 255 ? 255: (irgb[2] < 0 ? 0 : irgb[2])); } +} + +class LightControllerClass { +private: + LightStateClass *_state; + + + bool _ct_rgb_linked = true; + bool _pwm_multi_channels = false; + +public: + LightControllerClass(LightStateClass& state) { + _state = &state; + } + + void setSubType(uint8_t sub_type) { + _state->setSubType(sub_type); + } + + inline bool setCTRGBLinked(bool ct_rgb_linked) { + bool prev = _ct_rgb_linked; + if (_pwm_multi_channels) { + _ct_rgb_linked = false; + } else { + _ct_rgb_linked = ct_rgb_linked; + } + return prev; + } + + void setAlexaCTRange(bool alexa_ct_range) { + + if (alexa_ct_range) { + _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); + } else { + _state->setCTRange(CT_MIN, CT_MAX); + } + } + + inline bool isCTRGBLinked() { + return _ct_rgb_linked; + } + + inline bool setPWMMultiChannel(bool pwm_multi_channels) { + bool prev = _pwm_multi_channels; + _pwm_multi_channels = pwm_multi_channels; + if (pwm_multi_channels) setCTRGBLinked(false); + return prev; + } + + inline bool isPWMMultiChannel(void) { + return _pwm_multi_channels; + } + +#ifdef DEBUG_LIGHT + void debugLogs() { + uint8_t r,g,b,c,w; + _state->getActualRGBCW(&r,&g,&b,&c,&w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", + r, g, b, c, w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", + Light.current_color[0], Light.current_color[1], Light.current_color[2], + Light.current_color[3], Light.current_color[4]); + } +#endif + + void loadSettings() { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", + light_type, Light.subtype); +#endif + if (_pwm_multi_channels) { + _state->setChannelsRaw(Settings.light_color); + } else { + + _state->setCW(Settings.light_color[3], Settings.light_color[4], true); + _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + + + + uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); + + + + if ((DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && + (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + _state->setCW(255, 0); + } + _state->setBriCT(bri); + _state->setBriRGB(bri); + _state->setColorMode(LCM_RGB); + } + + if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { + _state->setBriRGB(bri); + } else { + _state->setBriCT(bri); + } + } + } + + void changeCTB(uint16_t new_ct, uint8_t briCT) { + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { + return; + } + _state->setCT(new_ct); + _state->setBriCT(briCT); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + saveSettings(); + calcLevels(); + + } + + void changeDimmer(uint8_t dimmer, uint32_t mode = 0) { + uint8_t bri = changeUIntScale(dimmer, 0, 100, 0, 255); + switch (mode) { + case 1: + changeBriRGB(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + break; + case 2: + changeBriCT(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + break; + default: + changeBri(bri); + break; + } + } + + void changeBri(uint8_t bri) { + _state->setBri(bri); + saveSettings(); + calcLevels(); + } + + void changeBriRGB(uint8_t bri) { + _state->setBriRGB(bri); + saveSettings(); + calcLevels(); + } + + void changeBriCT(uint8_t bri) { + _state->setBriCT(bri); + saveSettings(); + calcLevels(); + } + + void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + _state->setRGB(r, g, b, keep_bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + + void calcLevels(uint8_t *current_color = nullptr) { + uint8_t r,g,b,c,w,briRGB,briCT; + if (current_color == nullptr) { current_color = Light.current_color; } + + if (_pwm_multi_channels) { + _state->getChannelsRaw(current_color); + return; + } + + _state->getActualRGBCW(&r,&g,&b,&c,&w); + briRGB = _state->getBriRGB(); + briCT = _state->getBriCT(); + + current_color[0] = current_color[1] = current_color[2] = 0; + current_color[3] = current_color[4] = 0; + switch (Light.subtype) { + case LST_NONE: + current_color[0] = 255; + break; + case LST_SINGLE: + current_color[0] = briRGB; + break; + case LST_COLDWARM: + current_color[0] = c; + current_color[1] = w; + break; + case LST_RGBW: + case LST_RGBCW: + if (LST_RGBCW == Light.subtype) { + current_color[3] = c; + current_color[4] = w; + } else { + current_color[3] = briCT; + } + + case LST_RGB: + current_color[0] = r; + current_color[1] = g; + current_color[2] = b; + break; + } + } + + void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) { + _state->setHS(hue, sat); + _state->setBriRGB(briRGB); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + void saveSettings() { + if (Light.pwm_multi_channels) { + + _state->getChannelsRaw(Settings.light_color); + Settings.light_dimmer = 100; + } else { + uint8_t cm = _state->getColorMode(); + + memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); + if (LCM_RGB & cm) { + _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); + + if (LCM_BOTH == cm) { + + _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); + } + } else if (LCM_CT == cm) { + _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); + } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); +#endif + } + + + + + void changeChannels(uint8_t *channels) { + if (Light.pwm_multi_channels) { + _state->setChannelsRaw(channels); + } else if (LST_COLDWARM == Light.subtype) { + + uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; + _state->setChannels(remapped_channels); + } else { + _state->setChannels(channels); + } + + saveSettings(); + calcLevels(); + } +}; + + + +LightStateClass light_state = LightStateClass(); +LightControllerClass light_controller = LightControllerClass(light_state); + + + + + +uint16_t change8to10(uint8_t v) { + return changeUIntScale(v, 0, 255, 0, 1023); +} + +uint8_t change10to8(uint16_t v) { + return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); +} + + + + + +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (v <= to_src) { + return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (vg <= to_gamma) { + return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + + +uint16_t ledGamma10_10(uint16_t v) { + return ledGamma_internal(v, gamma_table); +} + +uint16_t ledGamma10(uint8_t v) { + return ledGamma10_10(change8to10(v)); +} + + +uint8_t ledGamma(uint8_t v) { + return change10to8(ledGamma10(v)); +} + + + +void LightPwmOffset(uint32_t offset) +{ + Light.pwm_offset = offset; +} + +bool LightModuleInit(void) +{ + light_type = LT_BASIC; + + if (Settings.flag.pwm_control) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (PinUsed(GPIO_PWM1, i)) { light_type++; } + } + } + + light_flg = 0; + if (XlgtCall(FUNC_MODULE_INIT)) { + + } +#ifdef ESP8266 + else if (SONOFF_BN == my_module_type) { + light_type = LT_PWM1; + } + else if (SONOFF_LED == my_module_type) { + if (!my_module.io[4]) { + pinMode(4, OUTPUT); + digitalWrite(4, LOW); + } + if (!my_module.io[5]) { + pinMode(5, OUTPUT); + digitalWrite(5, LOW); + } + if (!my_module.io[14]) { + pinMode(14, OUTPUT); + digitalWrite(14, LOW); + } + light_type = LT_PWM2; + } +#endif +#ifdef USE_PWM_DIMMER +#ifdef USE_DEVICE_GROUPS + else if (PWM_DIMMER == my_module_type) { + light_type = Settings.pwm_dimmer_cfg.pwm_count + 1; + } +#endif +#endif + + if (light_type > LT_BASIC) { + devices_present++; + } + + + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + if (Settings.flag3.pwm_multi_channels) { + if (0 == pwm_channels) { pwm_channels = 1; } + devices_present += pwm_channels - 1; + } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= pwm_channels)) { + + devices_present++; + } else if ((Settings.flag4.virtual_ct) && (LST_RGBW == pwm_channels)) { + Light.virtual_ct = true; + light_type++; + } + + return (light_type > LT_BASIC); +} + + + +void LightCalcPWMRange(void) { + uint16_t pwm_min, pwm_max; + + pwm_min = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_min)); + pwm_max = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_max)); + if (Settings.light_correction) { + pwm_min = ledGamma10_10(pwm_min); + pwm_max = ledGamma10_10(pwm_max); + } + pwm_min = pwm_min > 0 ? changeUIntScale(pwm_min, 1, 1023, 1, Settings.pwm_range) : 0; + pwm_max = changeUIntScale(pwm_max, 1, 1023, 1, Settings.pwm_range); + + Light.pwm_min = pwm_min; + Light.pwm_max = pwm_max; + +} + +void LightInit(void) +{ + + if (0 == Settings.rgbwwTable[4]) { + Settings.flag4.white_blend_mode = true; + Settings.rgbwwTable[4] = 255; + } + + Light.device = devices_present; + Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; + + if (LST_RGBW <= Light.subtype) { + + + bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); + light_controller.setCTRGBLinked(ct_rgb_linked); + } + + if ((LST_SINGLE <= Light.subtype) && Light.pwm_multi_channels) { + + light_controller.setPWMMultiChannel(true); + Light.device = devices_present - Light.subtype + 1; + } else if (!light_controller.isCTRGBLinked()) { + + Light.device--; + } + LightCalcPWMRange(); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", + Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); +#endif + + light_controller.setSubType(Light.subtype); + light_controller.loadSettings(); + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + light_controller.calcLevels(); + + if (LST_SINGLE == Light.subtype) { + Settings.light_color[0] = 255; + } + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < light_type; i++) { + Settings.pwm_value[i] = 0; + if (PinUsed(GPIO_PWM1, i)) { +#ifdef ESP8266 + pinMode(Pin(GPIO_PWM1, i), OUTPUT); +#else + analogAttach(Pin(GPIO_PWM1, i), i); +#endif + } + } + if (PinUsed(GPIO_ARIRFRCV)) { + if (PinUsed(GPIO_ARIRFSEL)) { + pinMode(Pin(GPIO_ARIRFSEL), OUTPUT); + digitalWrite(Pin(GPIO_ARIRFSEL), 1); + } + } + } + + uint32_t max_scheme = Light.max_scheme; + if (Light.subtype < LST_RGB) { + max_scheme = LS_POWER; + } + if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { + Settings.light_scheme = LS_POWER; + } + Light.power = 0; + Light.update = true; + Light.wakeup_active = 0; + if (0 == Settings.light_wakeup) { + Settings.light_wakeup = 60; + } + if (Settings.flag4.fade_at_startup) { + Light.fade_initialized = true; + } + + LightUpdateColorMapping(); +} + +void LightUpdateColorMapping(void) +{ + uint8_t param = Settings.param[P_RGB_REMAP] & 127; + if (param > 119){ param = 0; } + + uint8_t tmp[] = {0,1,2,3,4}; + Light.color_remap[0] = tmp[param / 24]; + for (uint32_t i = param / 24; i<4; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 24; + Light.color_remap[1] = tmp[(param / 6)]; + for (uint32_t i = param / 6; i<3; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 6; + Light.color_remap[2] = tmp[(param / 2)]; + for (uint32_t i = param / 2; i<2; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 2; + Light.color_remap[3] = tmp[param]; + Light.color_remap[4] = tmp[1-param]; + + Light.update = true; + +} + +uint8_t LightGetDimmer(uint8_t dimmer) { + return light_state.getDimmer(dimmer); +} + +void LightSetDimmer(uint8_t dimmer) { + light_controller.changeDimmer(dimmer); +} + +void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + light_state.getHSB(hue, sat, bri); +} + +void LightGetXY(float *X, float *Y) { + light_state.getXY(X, Y); +} + +void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + light_state.HsToRgb(hue, sat, r_r, r_g, r_b); +} + + +uint8_t LightGetBri(uint8_t device) { + uint8_t bri = 254; + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + bri = Light.current_color[device - Light.device]; + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + bri = light_state.getBri(); + } + } else { + if (device == Light.device) { + bri = light_state.getBriRGB(); + } else if (device == Light.device + 1) { + bri = light_state.getBriCT(); + } + } + return bri; +} + + +void LightSetBri(uint8_t device, uint8_t bri) { + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + Light.current_color[device - Light.device] = bri; + light_controller.changeChannels(Light.current_color); + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + light_controller.changeBri(bri); + } + } else { + if (device == Light.device) { + light_controller.changeBriRGB(bri); + } else if (device == Light.device + 1) { + light_controller.changeBriCT(bri); + } + } +} + +void LightColorOffset(int32_t offset) { + uint16_t hue; + uint8_t sat; + light_state.getHSB(&hue, &sat, nullptr); + hue += offset; + if (hue < 0) { hue += 359; } + if (hue > 359) { hue -= 359; } + if (!Light.pwm_multi_channels) { + light_state.setHS(hue, sat); + } else { + light_state.setHS(hue, 255); + light_state.setBri(255); + } + light_controller.calcLevels(Light.new_color); +} + +bool LightColorTempOffset(int32_t offset) { + int32_t ct = LightGetColorTemp(); + if (0 == ct) { return false; } + ct += offset; + if (ct < CT_MIN) { ct = CT_MIN; } + else if (ct > CT_MAX) { ct = CT_MAX; } + + LightSetColorTemp(ct); + return true; +} + +void LightSetColorTemp(uint16_t ct) +{ + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return; + } + light_controller.changeCTB(ct, light_state.getBriCT()); +} + +uint16_t LightGetColorTemp(void) +{ + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return 0; + } + return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; +} + +void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) +{ + + + + if (Settings.flag.light_signal) { + uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); + + light_controller.changeRGB(signal, 255 - signal, 0, true); + Settings.light_scheme = 0; + if (0 == light_state.getBri()) { + light_controller.changeBri(50); + } + } +} + + +char* LightGetColor(char* scolor, boolean force_hex = false) +{ + if ((0 == Settings.light_scheme) || (!Light.pwm_multi_channels)) { + light_controller.calcLevels(); + } + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (!force_hex && Settings.flag.decimal_text) { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); + } else { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); + } + } + return scolor; +} + +void LightPowerOn(void) +{ + if (light_state.getBri() && !(Light.power)) { + ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); + } +} + +void ResponseLightState(uint8_t append) +{ + char scolor[LIGHT_COLOR_SIZE]; + char scommand[33]; + bool unlinked = !light_controller.isCTRGBLinked() && (Light.subtype >= LST_RGBW); + + if (append) { + ResponseAppend_P(PSTR(",")); + } else { + Response_P(PSTR("{")); + } + if (!Light.pwm_multi_channels) { + if (unlinked) { + + ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "1\":%d" + ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "2\":%d"), + Light.device, GetStateText(Light.power & 1), light_state.getDimmer(1), + Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), light_state.getDimmer(2)); + } else { + GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1), + light_state.getDimmer()); + } + + + if (Light.subtype > LST_SINGLE) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + if (LST_RGB <= Light.subtype) { + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + + ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_WHITE "\":%d"), light_state.getDimmer(2)); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); + } + + ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); + for (uint32_t i = 0; i < Light.subtype; i++) { + uint8_t channel_raw = Light.current_color[i]; + uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); + + if ((0 == channel) && (channel_raw > 0)) { channel = 1; } + ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); + } + ResponseAppend_P(PSTR("]")); + } + + if (append) { + if (Light.subtype >= LST_RGB) { + ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); + } + if (Light.max_scheme > LS_MAX) { + ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); + } + ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), + GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); + } + } else { + for (uint32_t i = 0; i < Light.subtype; i++) { + GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); + uint32_t light_power_masked = Light.power & (1 << i); + light_power_masked = light_power_masked ? 1 : 0; + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, + changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); + } + ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + } + + if (!append) { + ResponseJsonEnd(); + } +} + +void LightPreparePower(power_t channels = 0xFFFFFFFF) { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); +#endif + + if (Light.pwm_multi_channels) { + for (uint32_t i = 0; i < Light.subtype; i++) { + if (bitRead(channels, i)) { + + if ((Light.current_color[i]) && (!bitRead(Light.power, i))) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else { + + if ((0 == Light.current_color[i]) && bitRead(Light.power, i)) { + ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + #ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device + i); + #endif + } + } + } else { + if (light_controller.isCTRGBLinked()) { + if (light_state.getBri() && !(Light.power)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBri() && Light.power) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } else { + + if (channels & 1) { + if (light_state.getBriRGB() && !(Light.power & 1)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriRGB() && (Light.power & 1)) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + + if (channels & 2) { + if (light_state.getBriCT() && !(Light.power & 2)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + 1, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriCT() && (Light.power & 2)) { + ExecuteCommandPower(Light.device + 1, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + } +#ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device); +#endif + } + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); +#endif + Light.power = power >> (Light.device - 1); + ResponseLightState(0); +} + +#ifdef USE_LIGHT_PALETTE +void LightSetPaletteEntry(void) +{ + uint8_t bri = light_state.getBri(); + uint8_t * palette_entry = &Light.palette[Light.wheel * LST_MAX]; + for (int i = 0; i < LST_MAX; i++) { + Light.new_color[i] = changeUIntScale(palette_entry[i], 0, 255, 0, bri); + } + light_state.setChannelsRaw(Light.new_color); + if (!Light.pwm_multi_channels) { + light_state.setCW(Light.new_color[3], Light.new_color[4], true); + if (Light.new_color[0] || Light.new_color[1] || Light.new_color[2]) light_state.addRGBMode(); + } +} +#endif + +void LightCycleColor(int8_t direction) +{ + + if (Settings.light_speed > 3) { + if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } + } + +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + if (!Light.fade_running) { + if (0 == direction) { + Light.wheel = random(Light.palette_count); + } + else { + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = 0; + if (direction < 0) Light.wheel = Light.palette_count - 1; + } + } + LightSetPaletteEntry(); + } + return; + } +#endif + + if (0 == direction) { + if (Light.random == Light.wheel) { + Light.random = random(255); + + uint8_t my_dir = (Light.random < Light.wheel -128) ? 1 : + (Light.random < Light.wheel ) ? 0 : + (Light.random > Light.wheel +128) ? 0 : 1; + Light.random = (Light.random & 0xFE) | my_dir; + + + } + + direction = (Light.random &0x01) ? 1 : -1; + } + + + if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } + Light.wheel += direction; + uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); + + + + if (!Light.pwm_multi_channels) { + uint8_t sat; + light_state.getHSB(nullptr, &sat, nullptr); + light_state.setHS(hue, sat); + } else { + light_state.setHS(hue, 255); + light_state.setBri(255); + } + light_controller.calcLevels(Light.new_color); +} + +void LightSetPower(void) +{ + + Light.old_power = Light.power; + + uint32_t mask = 1; + if (Light.pwm_multi_channels) { + mask = (1 << Light.subtype) - 1; + } else if (!light_controller.isCTRGBLinked()) { + mask = 3; + } + uint32_t shift = Light.device - 1; + + + + + + Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; + if (Light.wakeup_active) { + Light.wakeup_active--; + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", + XdrvMailbox.index, Light.old_power, Light.power, mask, shift); +#endif + if (Light.power != Light.old_power) { + Light.update = true; + } + LightAnimate(); +} + + + + +void LightAnimate(void) +{ + uint16_t light_still_on = 0; + bool power_off = false; + + + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + Light.strip_timer_counter++; + + + + if (Light.power || Light.fade_running) { + if (Settings.sleep > PWM_MAX_SLEEP) { + ssleep = PWM_MAX_SLEEP; + } else { + ssleep = Settings.sleep; + } + } else { + ssleep = Settings.sleep; + } + + if (!Light.power) { + Light.strip_timer_counter = 0; + if (Settings.light_scheme >= LS_MAX) { + power_off = true; + } + } else { + switch (Settings.light_scheme) { + case LS_POWER: + light_controller.calcLevels(Light.new_color); + break; + case LS_WAKEUP: + { + if (2 == Light.wakeup_active) { + Light.wakeup_active = 1; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; + } + Light.wakeup_start_time = millis(); + } + + uint32_t step_10 = ((millis() - Light.wakeup_start_time) * 1023) / (Settings.light_wakeup * 1000); + if (step_10 > 1023) { step_10 = 1023; } + uint8_t wakeup_bri = changeUIntScale(step_10, 0, 1023, 0, LightStateClass::DimmerToBri(Settings.light_dimmer)); + + if (wakeup_bri != light_state.getBri()) { + light_state.setBri(wakeup_bri); + light_controller.calcLevels(); + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = Light.current_color[i]; + } + } + if (1023 == step_10) { + Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); + ResponseLightState(1); + ResponseJsonEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP)); + + Light.wakeup_active = 0; + Settings.light_scheme = LS_POWER; + } + } + break; + case LS_CYCLEUP: + case LS_CYCLEDN: + case LS_RANDOM: + if (LS_CYCLEUP == Settings.light_scheme) { + LightCycleColor(1); + } else if (LS_CYCLEDN == Settings.light_scheme) { + LightCycleColor(-1); + } else { + LightCycleColor(0); + } + if (Light.pwm_multi_channels) { + Light.new_color[0] = changeUIntScale(Light.new_color[0], 0, 255, 0, Settings.light_color[0]); + Light.new_color[1] = changeUIntScale(Light.new_color[1], 0, 255, 0, Settings.light_color[1]); + Light.new_color[2] = changeUIntScale(Light.new_color[2], 0, 255, 0, Settings.light_color[2]); + } + break; + default: + XlgtCall(FUNC_SET_SCHEME); + } + +#ifdef USE_DEVICE_GROUPS + if (Settings.light_scheme != Light.last_scheme) { + Light.last_scheme = Settings.light_scheme; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + Light.devgrp_no_channels_out = false; + } +#endif + } + + if ((Settings.light_scheme < LS_MAX) || power_off) { + + + LightApplyPower(Light.new_color, Light.power); + + + + + + + + if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { + Light.update = true; + } + if (Light.update) { +#ifdef USE_DEVICE_GROUPS + if (Light.power) LightSendDeviceGroupStatus(false); +#endif + + uint16_t cur_col_10[LST_MAX]; + Light.update = false; + bool rgbwwtable_applied = false; + + + for (uint32_t i = 0; i < LST_MAX; i++) { + Light.last_color[i] = Light.new_color[i]; + + cur_col_10[i] = change8to10(Light.new_color[i]); + } + + if (Light.pwm_multi_channels) { + calcGammaMultiChannels(cur_col_10); + } else { + calcGammaBulbs(cur_col_10); + + + + if ((LST_RGBW <= Light.subtype) && (Settings.flag4.white_blend_mode) && (0 == cur_col_10[3]+cur_col_10[4])) { + uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); + for (uint32_t i=0; i<3; i++) { + + uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); + cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); + } + + + uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); + uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); + if (LST_RGBW == Light.subtype) { + + cur_col_10[3] = white_10; + } else { + + uint32_t ct = light_state.getCT10bits(); + cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); + cur_col_10[3] = white_10 - cur_col_10[4]; + } + rgbwwtable_applied = true; + } else if ((Light.virtual_ct) && (0 == cur_col_10[0]+cur_col_10[1]+cur_col_10[2])) { + + uint16_t sw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[4] : cur_col_10[3]; + uint16_t hw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[3] : cur_col_10[4]; + uint32_t adjust_sw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[4] : Settings.rgbwwTable[3]); + uint32_t adjust_hw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[3] : Settings.rgbwwTable[4]); + + cur_col_10[3] = changeUIntScale(hw_white, 0, 1023, 0, adjust_hw); + cur_col_10[4] = 0; + sw_white = changeUIntScale(sw_white, 0, 1023, 0, adjust_sw); + for (uint32_t i=0; i<3; i++) { + uint32_t adjust = change8to10(Settings.rgbwwTable[i]); + cur_col_10[i] = changeUIntScale(sw_white, 0, 1023, 0, adjust); + } + rgbwwtable_applied = true; + } + } + + + if (!rgbwwtable_applied) { + for (uint32_t i = 0; i 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; + } + + + uint16_t orig_col_10bits[LST_MAX]; + memcpy(orig_col_10bits, cur_col_10, sizeof(orig_col_10bits)); + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = orig_col_10bits[Light.color_remap[i]]; + } + + if (!Settings.light_fade || skip_light_fade || power_off || (!Light.fade_initialized)) { + + memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10)); + + LightSetOutputs(cur_col_10); + Light.fade_initialized = true; + } else { + if (Light.fade_running) { + + memcpy(Light.fade_start_10, Light.fade_cur_10, sizeof(Light.fade_start_10)); + } + memcpy(Light.fade_end_10, cur_col_10, sizeof(Light.fade_start_10)); + Light.fade_running = true; + Light.fade_duration = 0; + Light.fade_start = 0; + + } + } + if (Light.fade_running) { + if (LightApplyFade()) { + + + + LightSetOutputs(Light.fade_cur_10); + } + } +#ifdef USE_PWM_DIMMER + + if (PWM_DIMMER == my_module_type && !Light.power && !Light.fade_running) PWMDimmerSetPower(); +#endif + } +} + +bool isChannelGammaCorrected(uint32_t channel) { + if (!Settings.light_correction) { return false; } + if (channel >= Light.subtype) { return false; } +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } + } +#endif + return true; +} + + +bool isChannelCT(uint32_t channel) { +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return true; } + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return true; } + } +#endif + return false; +} + + +uint16_t fadeGamma(uint32_t channel, uint16_t v) { + if (isChannelGammaCorrected(channel)) { + return ledGamma_internal(v, gamma_table_fast); + } else { + return v; + } +} +uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { + if (isChannelGammaCorrected(channel)) { + return ledGammaReverse_internal(vg, gamma_table_fast); + } else { + return vg; + } +} + +bool LightApplyFade(void) { + static uint32_t last_millis = 0; + uint32_t now = millis(); + + if ((now - last_millis) <= 5) { + return false; + } + last_millis = now; + + + if (0 == Light.fade_duration) { + Light.fade_start = now; + + uint32_t distance = 0; + for (uint32_t i = 0; i < Light.subtype; i++) { + int32_t channel_distance = fadeGammaReverse(i, Light.fade_end_10[i]) - fadeGammaReverse(i, Light.fade_start_10[i]); + if (channel_distance < 0) { channel_distance = - channel_distance; } + if (channel_distance > distance) { distance = channel_distance; } + } + if (distance > 0) { + + + + Light.fade_duration = (distance * Settings.light_speed * 500) / 1023; + if (Settings.save_data) { + + uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; + + if (save_data_counter < delay_seconds) { + save_data_counter = delay_seconds; + } + } + } else { + + Light.fade_running = false; + } + } + + uint16_t fade_current = now - Light.fade_start; + if (fade_current <= Light.fade_duration) { + + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.fade_cur_10[i] = fadeGamma(i, + changeUIntScale(fadeGammaReverse(i, fade_current), + 0, Light.fade_duration, + fadeGammaReverse(i, Light.fade_start_10[i]), + fadeGammaReverse(i, Light.fade_end_10[i]))); + + + + } + } else { + + + Light.fade_running = false; + Light.fade_start = 0; + Light.fade_duration = 0; + + memcpy(Light.fade_cur_10, Light.fade_end_10, sizeof(Light.fade_end_10)); + + memcpy(Light.fade_start_10, Light.fade_end_10, sizeof(Light.fade_start_10)); + } + return true; +} + + + +void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) { + + if (Light.pwm_multi_channels) { + + for (uint32_t i = 0; i < LST_MAX; i++) { + if (0 == bitRead(power,i)) { + new_color[i] = 0; + } + } + + + + + + } else { + if (!light_controller.isCTRGBLinked()) { + + if (0 == (power & 1)) { + new_color[0] = new_color[1] = new_color[2] = 0; + } + if (0 == (power & 2)) { + new_color[3] = new_color[4] = 0; + } + } else if (!power) { + for (uint32_t i = 0; i < LST_MAX; i++) { + new_color[i] = 0; + } + } + } +} + +void LightSetOutputs(const uint16_t *cur_col_10) { + + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { + if (PinUsed(GPIO_PWM1, i)) { + + uint16_t cur_col = cur_col_10[i + Light.pwm_offset]; + if (!isChannelCT(i)) { + cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings.pwm_range, Light.pwm_min, Light.pwm_max) : 0; + } + if (!Settings.flag4.zerocross_dimmer) { + analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col : cur_col); + } + } + } + } + + + + + uint8_t cur_col[LST_MAX]; + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col[i] = change10to8(cur_col_10[i]); + } + + + uint8_t scale_col[3]; + uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; + for (uint32_t i = 0; i < 3; i++) { + scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; + } + + char *tmp_data = XdrvMailbox.data; + char *tmp_topic = XdrvMailbox.topic; + XdrvMailbox.data = (char*)cur_col; + XdrvMailbox.topic = (char*)scale_col; + if (XlgtCall(FUNC_SET_CHANNELS)) { } + else if (XdrvCall(FUNC_SET_CHANNELS)) { } + XdrvMailbox.data = tmp_data; + XdrvMailbox.topic = tmp_topic; +} + + +void calcGammaMultiChannels(uint16_t cur_col_10[5]) { + + if (Settings.light_correction) { + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } +} + +void calcGammaBulbs(uint16_t cur_col_10[5]) { + + + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + + uint32_t cw1 = Light.subtype - 1; + uint32_t cw0 = Light.subtype - 2; + uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; + uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; + +#ifdef ESP8266 + if ((PHILIPS == my_module_type) || (Settings.flag4.pwm_ct_mode)) { + + cur_col_10[cw1] = light_state.getCT10bits(); + + if (Settings.light_correction) { + cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); + } else { + cur_col_10[cw0] = white_bri10_1023; + } + } else +#endif + if (Settings.light_correction) { + + if (white_bri10 <= 1031) { + + uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); + + cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); + cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); + } else { + cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); + cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); + } + } + } + + if (Settings.light_correction) { + + if (LST_RGB <= Light.subtype) { + for (uint32_t i = 0; i < 3; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } + + if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { + cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); + } + } +} + +#ifdef USE_DEVICE_GROUPS +void LightSendDeviceGroupStatus(bool status) +{ + static uint8_t last_bri; + uint8_t bri = light_state.getBri(); + bool send_bri_update = (status || bri != last_bri); + if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { + static uint8_t channels[LST_MAX + 1] = { 0, 0, 0, 0, 0, 0 }; + if (status) { + light_state.getChannels(channels); + } + else { + memcpy(channels, Light.new_color, LST_MAX); + channels[LST_MAX]++; + } + SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + } + if (send_bri_update) { + last_bri = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, light_state.getBri()); + } +} + +void LightHandleDevGroupItem(void) +{ + static bool send_state = false; + static bool restore_power = false; + +#ifdef USE_PWM_DIMMER_REMOTE + if (!(XdrvMailbox.index & DGR_FLAG_LOCAL)) return; +#endif + bool more_to_come; + uint32_t value = XdrvMailbox.payload; + switch (XdrvMailbox.command_code) { + case DGR_ITEM_EOL: + more_to_come = (XdrvMailbox.index & DGR_FLAG_MORE_TO_COME); + if (restore_power && !more_to_come) { + restore_power = false; + Light.power = Light.old_power; + } + + LightAnimate(); + + if (send_state && !more_to_come) { + light_controller.saveSettings(); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + send_state = false; + } + break; + case DGR_ITEM_LIGHT_BRI: + if (light_state.getBri() != value) { + light_state.setBri(value); + send_state = true; + } + break; + case DGR_ITEM_LIGHT_SCHEME: + if (Settings.light_scheme != value) { + Light.last_scheme = Settings.light_scheme = value; + Light.devgrp_no_channels_out = (value != 0); + send_state = true; + } + break; + case DGR_ITEM_LIGHT_CHANNELS: +#ifdef USE_DGR_LIGHT_SEQUENCE + { + static uint8_t last_sequence = 0; + + + + if (Light.sequence_offset) { + light_controller.changeChannels(Light.channels_fifo); + + + + int last_entry = (Light.sequence_offset - 1) * LST_MAX; + for (uint8_t sequence = (uint8_t)XdrvMailbox.data[LST_MAX]; (uint8_t)(sequence - last_sequence) > 0; last_sequence++) { + memmove(Light.channels_fifo, &Light.channels_fifo[LST_MAX], last_entry); + memcpy(&Light.channels_fifo[last_entry], XdrvMailbox.data, LST_MAX); + } + } + else { +#endif + light_controller.changeChannels((uint8_t *)XdrvMailbox.data); +#ifdef USE_DGR_LIGHT_SEQUENCE + } + } +#endif + send_state = true; + break; + case DGR_ITEM_LIGHT_FIXED_COLOR: + if (Light.subtype >= LST_COLDWARM) { + send_state = true; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + Light.wheel = value % Light.palette_count; + LightSetPaletteEntry(); + break; + } +#endif + if (Light.subtype <= LST_COLDWARM) { + value = value % (MAX_FIXED_COLD_WARM - 1) + 201; + } + else { + uint32_t max = MAX_FIXED_COLOR; + if (Light.subtype >= LST_RGB) { + max++; + if (Light.subtype >= LST_RGBCW) max += (MAX_FIXED_COLD_WARM - 2); + } + value = value % max + 1; + if (value > MAX_FIXED_COLOR) value += 200 - MAX_FIXED_COLOR; + } + Light.fixed_color_index = value; + bool save_decimal_text = Settings.flag.decimal_text; + char str[16]; + LightColorEntry(str, sprintf_P(str, PSTR("%u"), value)); + Settings.flag.decimal_text = save_decimal_text; + uint32_t old_bri = light_state.getBri(); + light_controller.changeChannels(Light.entry_color); + light_controller.changeBri(old_bri); + Settings.light_scheme = 0; + Light.devgrp_no_channels_out = false; + if (!restore_power && !Light.power) { + Light.old_power = Light.power; + Light.power = 0xff; + restore_power = true; + } + } + break; + case DGR_ITEM_LIGHT_FADE: + if (Settings.light_fade != value) { + Settings.light_fade = value; + send_state = true; + } + break; + case DGR_ITEM_LIGHT_SPEED: + if (Settings.light_speed != value && value > 0 && value <= 40) { + Settings.light_speed = value; + send_state = true; + } + break; + case DGR_ITEM_STATUS: + SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, + DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + LightSendDeviceGroupStatus(true); + break; + } +} +#endif + + + + + +bool LightColorEntry(char *buffer, uint32_t buffer_length) +{ + char scolor[10]; + char *p; + char *str; + uint32_t entry_type = 0; + uint8_t value = Light.fixed_color_index; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = Light.wheel; +#endif + + if (buffer[0] == '#') { + buffer++; + buffer_length--; + } + + if (Light.subtype >= LST_RGB) { + char option = (1 == buffer_length) ? buffer[0] : '\0'; + if ('+' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index < MAX_FIXED_COLOR) { +#else + if (Light.fixed_color_index < MAX_FIXED_COLOR) { +#endif + value++; + } + } + else if ('-' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index > 1) { +#else + if (Light.fixed_color_index > 1) { +#endif + value--; + } + } else { + value = atoi(buffer); + } +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = value % Light.palette_count; +#endif + } + + memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); + + while ((buffer_length > 0) && ('=' == buffer[buffer_length - 1])) { + buffer_length--; + memcpy(&Light.entry_color, &Light.current_color, sizeof(Light.entry_color)); + } + if (strstr(buffer, ",") != nullptr) { + int8_t i = 0; + for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { + if (i < LST_MAX) { + Light.entry_color[i++] = atoi(str); + } + } + entry_type = 2; + } + else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { + for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { + strlcpy(scolor, buffer + (i *2), 3); + Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); + } + entry_type = 1; + } +#ifdef USE_LIGHT_PALETTE + else if (Light.palette_count) { + value--; + Light.wheel = value; + memcpy_P(&Light.entry_color, &Light.palette[value * LST_MAX], LST_MAX); + entry_type = 1; + } +#endif + else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { + Light.fixed_color_index = value; + memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); + entry_type = 1; + } + else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { + if (LST_RGBW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); + entry_type = 1; + } + else if (LST_COLDWARM == Light.subtype) { + memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + else if (LST_RGBCW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + } + if (entry_type) { + Settings.flag.decimal_text = entry_type -1; + } + return (entry_type); +} + + + +void CmndSupportColor(void) +{ + bool valid_entry = false; + bool coldim = false; + + if (XdrvMailbox.data_len > 0) { + valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); + if (valid_entry) { + if (XdrvMailbox.index <= 2) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count && XdrvMailbox.index == 2) { + LightSetPaletteEntry(); + } + else { +#endif + uint32_t old_bri = light_state.getBri(); + + light_controller.changeChannels(Light.entry_color); + if (2 == XdrvMailbox.index) { + + light_controller.changeBri(old_bri); + } +#ifdef USE_LIGHT_PALETTE + } +#endif + Settings.light_scheme = 0; + coldim = true; + } else { + for (uint32_t i = 0; i < LST_RGB; i++) { + Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; + } + } + } + } + char scolor[LIGHT_COLOR_SIZE]; + if (!valid_entry && (XdrvMailbox.index <= 2)) { + ResponseCmndChar(LightGetColor(scolor)); + } + if (XdrvMailbox.index >= 3) { + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGB; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); + } else { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); + } + } + ResponseCmndIdxChar(scolor); + } + if (coldim) { + LightPreparePower(); + } +} + +void CmndColor(void) +{ + + + + + + + + if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + CmndSupportColor(); + } +} + +void CmndWhite(void) +{ + + + if (Light.pwm_multi_channels) { return; } + if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(2); + } else { + ResponseCmndNumber(light_state.getDimmer(2)); + } + } +} + +void CmndChannel(void) +{ + + + + + if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { + uint32_t light_index = XdrvMailbox.index - Light.device; + power_t coldim = 0; + + + if (1 == XdrvMailbox.data_len) { + uint8_t channel = changeUIntScale(Light.current_color[light_index],0,255,0,100); + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel > 89) ? 100 : channel + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel < 11) ? 1 : channel - 10; + } + } + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Light.current_color[light_index] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); + if (Light.pwm_multi_channels) { + coldim = 1 << light_index; + } else { + if (light_controller.isCTRGBLinked()) { + + if ((light_index < 3) && (light_controller.isCTRGBLinked())) { + Light.current_color[3] = Light.current_color[4] = 0; + } else { + Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; + } + coldim = 1; + } else { + if (light_index < 3) { coldim = 1; } + else { coldim = 2; } + } + } + light_controller.changeChannels(Light.current_color); + } + ResponseCmndIdxNumber(changeUIntScale(Light.current_color[light_index],0,255,0,100)); + if (coldim) { + LightPreparePower(coldim); + } + } +} + +void CmndHsbColor(void) +{ + + + + + + + + if (Light.subtype >= LST_RGB) { + if (XdrvMailbox.data_len > 0) { + uint16_t c_hue; + uint8_t c_sat; + light_state.getHSB(&c_hue, &c_sat, nullptr); + uint32_t HSB[3]; + HSB[0] = c_hue; + HSB[1] = c_sat; + HSB[2] = light_state.getBriRGB(); + if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) { + if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; } + HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); + } else { + uint32_t paramcount = ParseParameters(3, HSB); + if (HSB[0] > 360) { HSB[0] = 360; } + for (uint32_t i = 1; i < paramcount; i++) { + if (HSB[i] > 100) { HSB[i] == 100; } + HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); + } + } + light_controller.changeHSB(HSB[0], HSB[1], HSB[2]); + LightPreparePower(1); + } else { + ResponseLightState(0); + } + } +} + +void CmndScheme(void) +{ + + + + + + if (Light.subtype >= LST_RGB) { + uint32_t max_scheme = Light.max_scheme; + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { + XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { + XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { + uint32_t parm[2]; + if (ParseParameters(2, parm) > 1) { + Light.wheel = parm[1]; +#ifdef USE_LIGHT_PALETTE + Light.wheel--; +#endif + } + Settings.light_scheme = XdrvMailbox.payload; + if (LS_WAKEUP == Settings.light_scheme) { + Light.wakeup_active = 3; + } + LightPowerOn(); + Light.strip_timer_counter = 0; + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + } + ResponseCmndNumber(Settings.light_scheme); + } +} + +void CmndWakeup(void) +{ + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload); + } + Light.wakeup_active = 3; + Settings.light_scheme = LS_WAKEUP; + LightPowerOn(); + ResponseCmndChar(D_JSON_STARTED); +} + +void CmndColorTemperature(void) +{ + + + + + if (Light.pwm_multi_channels) { return; } + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + uint32_t ct = light_state.getCT(); + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct > (CT_MAX-34)) ? CT_MAX : ct + 34; + } + else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct < (CT_MIN+34)) ? CT_MIN : ct - 34; + } + } + if ((XdrvMailbox.payload >= CT_MIN) && (XdrvMailbox.payload <= CT_MAX)) { + light_controller.changeCTB(XdrvMailbox.payload, light_state.getBriCT()); + LightPreparePower(2); + } else { + ResponseCmndNumber(ct); + } + } +} + +void LightDimmerOffset(uint32_t index, int32_t offset) { + int32_t dimmer = light_state.getDimmer(index) + offset; + if (dimmer < 1) { dimmer = Settings.flag3.slider_dimmer_stay_on; } + if (dimmer > 100) { dimmer = 100; } + + XdrvMailbox.index = index; + XdrvMailbox.payload = dimmer; + CmndDimmer(); +} + +void CmndDimmer(void) +{ + + + + + + + + uint32_t dimmer; + if (XdrvMailbox.index == 3) { + skip_light_fade = true; + XdrvMailbox.index = 0; + } + else if (XdrvMailbox.index > 2) { + XdrvMailbox.index = 1; + } + + if ((light_controller.isCTRGBLinked()) || (0 == XdrvMailbox.index)) { + dimmer = light_state.getDimmer(); + } else { + dimmer = light_state.getDimmer(XdrvMailbox.index); + } + + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; + } + } + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (light_controller.isCTRGBLinked()) { + + light_controller.changeDimmer(XdrvMailbox.payload); + LightPreparePower(); + } else { + if (0 != XdrvMailbox.index) { + light_controller.changeDimmer(XdrvMailbox.payload, XdrvMailbox.index); + LightPreparePower(1 << (XdrvMailbox.index - 1)); + } else { + + light_controller.changeDimmer(XdrvMailbox.payload, 1); + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(); + } + } +#if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) + uint8_t bri = light_state.getBri(); + if (bri != Settings.bri_power_on) { + Settings.bri_power_on = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); + } +#endif + Light.update = true; + if (skip_light_fade) LightAnimate(); + } else { + ResponseCmndNumber(dimmer); + } + skip_light_fade = false; +} + +void CmndDimmerRange(void) +{ + + + if (XdrvMailbox.data_len > 0) { + uint32_t parm[2]; + parm[0] = Settings.dimmer_hw_min; + parm[1] = Settings.dimmer_hw_max; + ParseParameters(2, parm); + if (parm[0] < parm[1]) { + Settings.dimmer_hw_min = parm[0]; + Settings.dimmer_hw_max = parm[1]; + } else { + Settings.dimmer_hw_min = parm[1]; + Settings.dimmer_hw_max = parm[0]; + } + LightCalcPWMRange(); + Light.update = true; + } + Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); +} + +void CmndLedTable(void) +{ + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_correction = XdrvMailbox.payload; + break; + case 2: + Settings.light_correction ^= 1; + break; + } + LightCalcPWMRange(); + Light.update = true; + } + ResponseCmndStateText(Settings.light_correction); +} + +void CmndRgbwwTable(void) +{ + + + if ((XdrvMailbox.data_len > 0)) { + uint32_t parm[LST_RGBCW -1]; + uint32_t parmcount = ParseParameters(LST_RGBCW, parm); + for (uint32_t i = 0; i < parmcount; i++) { + Settings.rgbwwTable[i] = parm[i]; + } + Light.update = true; + } + char scolor[LIGHT_COLOR_SIZE]; + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGBCW; i++) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + } + ResponseCmndChar(scolor); +} + +void CmndFade(void) +{ + + + + + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_fade = XdrvMailbox.payload; + break; + case 2: + Settings.light_fade ^= 1; + break; + } +#ifdef USE_DEVICE_GROUPS + if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade); +#endif +#ifdef USE_LIGHT + if (!Settings.light_fade) { Light.fade_running = false; } +#endif + ResponseCmndStateText(Settings.light_fade); +} + +void CmndSpeed(void) +{ + + + + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { + XdrvMailbox.payload = Settings.light_speed - 1; + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < 40)) { + XdrvMailbox.payload = Settings.light_speed + 1; + } + } + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 40)) { + Settings.light_speed = XdrvMailbox.payload; +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SPEED, Settings.light_speed); +#endif + } + ResponseCmndNumber(Settings.light_speed); +} + +void CmndWakeupDuration(void) +{ + + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { + Settings.light_wakeup = XdrvMailbox.payload; + Light.wakeup_active = 0; + } + ResponseCmndNumber(Settings.light_wakeup); +} + +#ifdef USE_LIGHT_PALETTE +void CmndPalette(void) +{ + uint8_t * palette_entry; + char * p; + + + if (XdrvMailbox.data_len) { + Light.wheel = 0; + Light.palette_count = 0; + if (Light.palette) { + free(Light.palette); + Light.palette = nullptr; + } + if (XdrvMailbox.data_len > 1 || XdrvMailbox.data[0] != '0') { + uint8_t palette_count = 0; + char * color = XdrvMailbox.data; + if (!(Light.palette = (uint8_t *)malloc(255 * Light.subtype))) return; + palette_entry = Light.palette; + for (;;) { + p = strchr(color, ' '); + if (p) *p = 0; + color = Trim(color); + if (*color && LightColorEntry(color, strlen(color))) { + memcpy(palette_entry, Light.entry_color, Light.subtype); + palette_entry += Light.subtype; + palette_count++; + } + if (!p) break; + color = p + 1; + } + if (!(Light.palette = (uint8_t *)realloc(Light.palette, palette_count * Light.subtype))) return; + Light.palette_count = palette_count; + } + } + + char palette_str[5 * Light.subtype * Light.palette_count + 3]; + p = palette_str; + *p++ = '['; + if (Light.palette_count) { + palette_entry = Light.palette; + for (int entry = 0; entry < Light.palette_count; entry++) { + if (Settings.flag.decimal_text) { + *p++ = '"'; + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%d,"), *palette_entry++); + } + *(p - 1) = '"'; + } + else { + for (uint32_t i = 0; i < Light.subtype; i++) { + p += sprintf_P(p, PSTR("%02X"), *palette_entry++); + } + } + *p++ = ','; + } + p--; + } + *p++ = ']'; + *p = 0; + ResponseCmndChar(palette_str); +} +#endif + +#ifdef USE_DGR_LIGHT_SEQUENCE +void CmndSequenceOffset(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + if (XdrvMailbox.payload != Light.sequence_offset) { + if (Light.sequence_offset) free(Light.channels_fifo); + Light.sequence_offset = XdrvMailbox.payload; + if (Light.sequence_offset) Light.channels_fifo = (uint8_t *)calloc(Light.sequence_offset, LST_MAX); + } + } + ResponseCmndNumber(Light.sequence_offset); +} +#endif + +void CmndUndocA(void) +{ + + char scolor[LIGHT_COLOR_SIZE]; + LightGetColor(scolor, true); + scolor[6] = '\0'; + Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); + MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); + mqtt_data[0] = '\0'; +} + + + + + +bool Xdrv04(uint8_t function) +{ + bool result = false; + + if (FUNC_MODULE_INIT == function) { + return LightModuleInit(); + } + else if (light_type) { + switch (function) { + case FUNC_SERIAL: + result = XlgtCall(FUNC_SERIAL); + break; + case FUNC_LOOP: + if (Light.fade_running) { + if (LightApplyFade()) { + LightSetOutputs(Light.fade_cur_10); + } + } + break; + case FUNC_EVERY_50_MSECOND: + LightAnimate(); + break; +#ifdef USE_DEVICE_GROUPS + case FUNC_DEVICE_GROUP_ITEM: + LightHandleDevGroupItem(); + break; +#endif + case FUNC_SET_POWER: + LightSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kLightCommands, LightCommand); + if (!result) { + result = XlgtCall(FUNC_COMMAND); + } + break; + case FUNC_PRE_INIT: + LightInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_05_irremote.ino" +# 55 "/workspace/Tasmota/tasmota/xdrv_05_irremote.ino" +#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) + + + + +#define XDRV_05 5 + +#include +#include + +enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; + +const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ; + + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrSend }; +# 81 "/workspace/Tasmota/tasmota/xdrv_05_irremote.ino" +class IRRawTable { +public: + IRRawTable() : timings() {} + + int32_t getTimingForLetter(uint8_t l) const { + l = toupper(l); + if ((l < 'A') || (l > 'Z')) { return -1; } + return timings[l - 'A']; + } + uint8_t findOrAdd(uint16_t t) { + if (0 == t) { return 0; } + + for (uint32_t i=0; i<26; i++) { + if (timings[i] == t) { return i + 'A'; } + if (timings[i] == 0) { timings[i] = t; break; } + } + return 0; + } + void add(uint16_t t) { + if (0 == t) { return; } + + for (uint32_t i=0; i<26; i++) { + if (timings[i] == 0) { timings[i] = t; break; } + } + } + +protected: + uint16_t timings[26]; +}; + + +static const uint8_t MAX_STANDARD_IR = NEC; +const char kIrRemoteProtocols[] PROGMEM = "UNKNOWN|RC5|RC6|NEC"; + + + + + +#include + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(Pin(GPIO_IRSEND)); + irsend->begin(); +} + +#ifdef USE_IR_RECEIVE + + + + +const bool IR_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + +#include + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(Pin(GPIO_IRRECV), IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); + + +} + +void IrReceiveCheck(void) +{ + char sirtype[8]; + int8_t iridx = 0; + + decode_results results; + + if (irrecv->decode(&results)) { + char hvalue[65]; + + iridx = results.decode_type; + if ((iridx < 0) || (iridx > MAX_STANDARD_IR)) { iridx = 0; } + + if (iridx) { + if (results.bits > 64) { + + uint32_t digits2 = results.bits / 8; + if (results.bits % 8) { digits2++; } + ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); + } else { + Uint64toHex(results.value, hvalue, results.bits); + } + } else { + Uint64toHex(results.value, hvalue, 32); + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), + irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); + + unsigned long now = millis(); + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + + char svalue[64]; + if (Settings.flag.ir_receive_decimal) { + ulltoa(results.value, svalue, 10); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); + } + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), + GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); + if (iridx) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); + } else { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); + } + + IRRawTable raw_table; + bool prev_number = false; + bool ir_high = true; + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":\"")); + size_t rawlen = results.rawlen; + uint32_t i; + + for (i = 1; i < rawlen; i++) { + + uint32_t raw_val_millis = results.rawbuf[i] * kRawTick; + uint16_t raw_dms = (raw_val_millis*2 + 5) / 10; + + uint8_t letter = raw_table.findOrAdd(raw_dms); + if (letter) { + if (!ir_high) { letter = tolower(letter); } + ResponseAppend_P(PSTR("%c"), letter); + prev_number = false; + } else { + + ResponseAppend_P(PSTR("%c%d"), ir_high ? '+' : '-', (uint32_t)raw_dms * 5); + prev_number = true; + } + ir_high = !ir_high; + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = getCorrectedRawLength(&results); + ResponseAppend_P(PSTR("\",\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + +#ifdef USE_DOMOTICZ + if (iridx) { + unsigned long value = results.value | (iridx << 28); + DomoticzSensor(DZ_COUNT, value); + } +#endif + } + + irrecv->resume(); + } +} +#endif + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + + + + RemoveSpace(XdrvMailbox.data); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } + + + + const char *protocol = root.getStr(PSTR(D_JSON_IR_PROTOCOL), ""); + uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); + uint64_t data = root.getULong(PSTR(D_JSON_IR_DATA), 0); + uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + + + if (XdrvMailbox.index > repeat + 1) { + repeat = XdrvMailbox.index - 1; + } + if (!(protocol && bits)) { + return IE_SYNTAX_IRSEND; + } + + char protocol_text[20]; + int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); + + char dvalue[64]; + char hvalue[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); + + irsend_active = true; + switch (protocol_code) { +#ifdef USE_IR_SEND_RC5 + case RC5: + irsend->sendRC5(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_RC6 + case RC6: + irsend->sendRC6(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_NEC + case NEC: + irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; +#endif + default: + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IE_INVALID_JSON; + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); + break; + case IE_INVALID_JSON: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { + switch (function) { + case FUNC_PRE_INIT: + if (PinUsed(GPIO_IRSEND)) { + IrSendInit(); + } +#ifdef USE_IR_RECEIVE + if (PinUsed(GPIO_IRRECV)) { + IrReceiveInit(); + } +#endif + break; + case FUNC_EVERY_50_MSECOND: +#ifdef USE_IR_RECEIVE + if (PinUsed(GPIO_IRRECV)) { + IrReceiveCheck(); + } +#endif + irsend_active = false; + break; + case FUNC_COMMAND: + if (PinUsed(GPIO_IRSEND)) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +# 55 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +#ifdef USE_IR_REMOTE_FULL + + + + +#define XDRV_05 5 + +#include +#include +#include +#include +#include + +enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, + IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL, IE_MEMORY }; + +const char kIrRemoteCommands[] PROGMEM = "|" + D_CMND_IRHVAC "|" D_CMND_IRSEND ; + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrHvac, &CmndIrSend }; +# 85 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +class IRRawTable { +public: + IRRawTable() : timings() {} + + int32_t getTimingForLetter(uint8_t l) const { + l = toupper(l); + if ((l < 'A') || (l > 'Z')) { return -1; } + return timings[l - 'A']; + } + uint8_t findOrAdd(uint16_t t) { + if (0 == t) { return 0; } + + for (uint32_t i=0; i<26; i++) { + if (timings[i] == t) { return i + 'A'; } + if (timings[i] == 0) { timings[i] = t; break; } + } + return 0; + } + void add(uint16_t t) { + if (0 == t) { return; } + + for (uint32_t i=0; i<26; i++) { + if (timings[i] == 0) { timings[i] = t; break; } + } + } + +protected: + uint16_t timings[26]; +}; + + + + + +IRsend *irsend = nullptr; +bool irsend_active = false; + + +bool irhvac_stateful = true; +stdAc::state_t irac_prev_state; + + +enum class StateModes { SEND_ONLY, + STORE_ONLY, + SEND_STORE }; +StateModes strToStateMode(class JsonParserToken token, StateModes def); + +void IrSendInit(void) +{ + irsend = new IRsend(Pin(GPIO_IRSEND)); + irsend->begin(); +} + + + +uint8_t reverseBitsInByte(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + + +uint64_t reverseBitsInBytes64(uint64_t b) { + union { + uint8_t b[8]; + uint64_t i; + } a; + a.i = b; + for (uint32_t i=0; i<8; i++) { + a.b[i] = reverseBitsInByte(a.b[i]); + } + return a.i; +} + + + + + +const bool IR_FULL_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + + + + +const uint16_t IR_FULL_BUFFER_SIZE = 1024; + + + +const uint8_t IR__FULL_RCV_TIMEOUT = 50; + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(Pin(GPIO_IRRECV), IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); +} + +String sendACJsonState(const stdAc::state_t &state) { + JsonGeneratorObject json; + json.add(PSTR(D_JSON_IRHVAC_VENDOR), typeToString(state.protocol)); + json.add(PSTR(D_JSON_IRHVAC_MODEL), state.model); + + + if (state.mode == stdAc::opmode_t::kOff || !state.power) { + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(stdAc::opmode_t::kOff)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(false)); + } else { + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(state.mode)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(state.power)); + } + json.add(PSTR(D_JSON_IRHVAC_CELSIUS), IRac::boolToString(state.celsius)); + if (floorf(state.degrees) == state.degrees) { + json.add(PSTR(D_JSON_IRHVAC_TEMP), (int32_t) floorf(state.degrees)); + } else { + + json.addStrRaw(PSTR(D_JSON_IRHVAC_TEMP), String(state.degrees, 1).c_str()); + } + + json.add(PSTR(D_JSON_IRHVAC_FANSPEED), IRac::fanspeedToString(state.fanspeed)); + json.add(PSTR(D_JSON_IRHVAC_SWINGV), IRac::swingvToString(state.swingv)); + json.add(PSTR(D_JSON_IRHVAC_SWINGH), IRac::swinghToString(state.swingh)); + json.add(PSTR(D_JSON_IRHVAC_QUIET), IRac::boolToString(state.quiet)); + json.add(PSTR(D_JSON_IRHVAC_TURBO), IRac::boolToString(state.turbo)); + json.add(PSTR(D_JSON_IRHVAC_ECONO), IRac::boolToString(state.econo)); + json.add(PSTR(D_JSON_IRHVAC_LIGHT), IRac::boolToString(state.light)); + json.add(PSTR(D_JSON_IRHVAC_FILTER), IRac::boolToString(state.filter)); + json.add(PSTR(D_JSON_IRHVAC_CLEAN), IRac::boolToString(state.clean)); + json.add(PSTR(D_JSON_IRHVAC_BEEP), IRac::boolToString(state.beep)); + json.add(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); + + String payload = json.toString(); + return payload; +} + +String sendIRJsonState(const struct decode_results &results) { + String json("{"); + json += "\"" D_JSON_IR_PROTOCOL "\":\""; + json += typeToString(results.decode_type); + json += "\",\"" D_JSON_IR_BITS "\":"; + json += results.bits; + + if (hasACState(results.decode_type)) { + json += ",\"" D_JSON_IR_DATA "\":\"0x"; + json += resultToHexidecimal(&results); + json += "\""; + } else { + if (UNKNOWN != results.decode_type) { + json += ",\"" D_JSON_IR_DATA "\":"; + } else { + json += ",\"" D_JSON_IR_HASH "\":"; + } + if (Settings.flag.ir_receive_decimal) { + char svalue[32]; + ulltoa(results.value, svalue, 10); + json += svalue; + } else { + char hvalue[64]; + if (UNKNOWN != results.decode_type) { + Uint64toHex(results.value, hvalue, results.bits); + json += "\"0x"; + json += hvalue; + json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; + Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); + json += hvalue; + json += "\""; + } else { + Uint64toHex(results.value, hvalue, 32); + json += "\"0x"; + json += hvalue; + json += "\""; + } + } + } + json += ",\"" D_JSON_IR_REPEAT "\":"; + json += results.repeat; + + stdAc::state_t new_state; + if (IRAcUtils::decodeToState(&results, &new_state, irhvac_stateful && irac_prev_state.protocol == results.decode_type ? &irac_prev_state : nullptr)) { + + json += ",\"" D_CMND_IRHVAC "\":"; + json += sendACJsonState(new_state); + irac_prev_state = new_state; + } + + return json; +} + +void IrReceiveCheck(void) +{ + decode_results results; + + if (irrecv->decode(&results)) { + uint32_t now = millis(); + + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); + + IRRawTable raw_table; + bool prev_number = false; + bool ir_high = true; + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":\"")); + size_t rawlen = results.rawlen; + uint32_t i; + + for (i = 1; i < rawlen; i++) { + + uint32_t raw_val_millis = results.rawbuf[i] * kRawTick; + uint16_t raw_dms = (raw_val_millis*2 + 5) / 10; + + uint8_t letter = raw_table.findOrAdd(raw_dms); + if (letter) { + if (!ir_high) { letter = tolower(letter); } + ResponseAppend_P(PSTR("%c"), letter); + prev_number = false; + } else { + + ResponseAppend_P(PSTR("%c%d"), ir_high ? '+' : '-', (uint32_t)raw_dms * 5); + prev_number = true; + } + ir_high = !ir_high; + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = getCorrectedRawLength(&results); + ResponseAppend_P(PSTR("\",\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + } + + irrecv->resume(); + } +} + + + + + + + +String listSupportedProtocols(bool hvac) { + String l(""); + bool first = true; + for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { + bool found = false; + if (hvac) { + found = IRac::isProtocolSupported((decode_type_t)i); + } else { + found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); + } + if (found) { + if (first) { + first = false; + } else { + l += "|"; + } + l += typeToString((decode_type_t)i); + } + } + return l; +} + +bool strToBool(class JsonParserToken token, bool def) { + if (token.isBool() || token.isNum()) { + return token.getBool(); + } else if (token.isStr()) { + return IRac::strToBool(token.getStr()); + } else { + return def; + } +} + +StateModes strToStateMode(class JsonParserToken token, StateModes def) { + if (token.isStr()) { + const char * str = token.getStr(); + if (!strcasecmp_P(str, PSTR(D_JSON_IRHVAC_STATE_MODE_SEND_ONLY))) + return StateModes::SEND_ONLY; + else if (!strcasecmp_P(str, PSTR(D_JSON_IRHVAC_STATE_MODE_STORE_ONLY))) + return StateModes::STORE_ONLY; + else if (!strcasecmp_P(str, PSTR(D_JSON_IRHVAC_STATE_MODE_SEND_STORE))) + return StateModes::SEND_STORE; + } + return def; +} + + +const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, + stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, + stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + stdAc::state_t state; + + + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } + + + state.protocol = decode_type_t::UNKNOWN; + state.model = 1; + state.mode = stdAc::opmode_t::kAuto; + state.power = false; + state.celsius = true; + state.degrees = 21.0f; + state.fanspeed = stdAc::fanspeed_t::kMedium; + state.swingv = stdAc::swingv_t::kOff; + state.swingh = stdAc::swingh_t::kOff; + state.light = false; + state.beep = false; + state.econo = false; + state.filter = false; + state.turbo = false; + state.quiet = false; + state.sleep = -1; + state.clean = false; + state.clock = -1; + + JsonParserToken val; + if (val = root[PSTR(D_JSON_IRHVAC_VENDOR)]) { state.protocol = strToDecodeType(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]) { state.protocol = strToDecodeType(val.getStr()); } + if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } + if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } + + + JsonParserToken tok_fan_speed = root[PSTR(D_JSON_IRHVAC_FANSPEED)]; + if (tok_fan_speed) { + uint32_t fan_speed = tok_fan_speed.getUInt(); + if ((fan_speed >= 1) && (fan_speed <= 5)) { + state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); + } else { + state.fanspeed = IRac::strToFanspeed(tok_fan_speed.getStr()); + } + } + + if (val = root[PSTR(D_JSON_IRHVAC_MODEL)]) { state.model = IRac::strToModel(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_MODE)]) { state.mode = IRac::strToOpmode(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGV)]) { state.swingv = IRac::strToSwingV(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGH)]) { state.swingh = IRac::strToSwingH(val.getStr()); } + state.degrees = root.getFloat(PSTR(D_JSON_IRHVAC_TEMP), state.degrees); + + + + + StateModes stateMode = StateModes::SEND_ONLY; + if (irhvac_stateful && (val = root[PSTR(D_JSON_IRHVAC_STATE_MODE)])) { stateMode = strToStateMode(val, stateMode); } + + + state.power = strToBool(root[PSTR(D_JSON_IRHVAC_POWER)], state.power); + state.celsius = strToBool(root[PSTR(D_JSON_IRHVAC_CELSIUS)], state.celsius); + state.light = strToBool(root[PSTR(D_JSON_IRHVAC_LIGHT)], state.light); + state.beep = strToBool(root[PSTR(D_JSON_IRHVAC_BEEP)], state.beep); + state.econo = strToBool(root[PSTR(D_JSON_IRHVAC_ECONO)], state.econo); + state.filter = strToBool(root[PSTR(D_JSON_IRHVAC_FILTER)], state.filter); + state.turbo = strToBool(root[PSTR(D_JSON_IRHVAC_TURBO)], state.turbo); + state.quiet = strToBool(root[PSTR(D_JSON_IRHVAC_QUIET)], state.quiet); + state.clean = strToBool(root[PSTR(D_JSON_IRHVAC_CLEAN)], state.clean); + + + state.sleep = root.getInt(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); + + + if (stateMode == StateModes::SEND_ONLY || stateMode == StateModes::SEND_STORE) { + IRac ac(Pin(GPIO_IRSEND)); + bool success = ac.sendAc(state, irhvac_stateful && irac_prev_state.protocol == state.protocol ? &irac_prev_state : nullptr); + if (!success) { return IE_SYNTAX_IRHVAC; } + } + if (stateMode == StateModes::STORE_ONLY || stateMode == StateModes::SEND_STORE) { + irac_prev_state = state; + } + + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); + return IE_RESPONSE_PROVIDED; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } +} + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + + + RemoveSpace(XdrvMailbox.data); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } + + + + JsonParserToken value; + + decode_type_t protocol = decode_type_t::UNKNOWN; + value = root[PSTR(D_JSON_IRHVAC_VENDOR)]; + if (root) { protocol = strToDecodeType(value.getStr()); } + value = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]; + if (root) { protocol = strToDecodeType(value.getStr()); } + if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } + + uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); + uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + + uint64_t data; + value = root[PSTR(D_JSON_IR_DATALSB)]; + if (root) { data = reverseBitsInBytes64(value.getULong()); } + value = root[PSTR(D_JSON_IR_DATA)]; + if (value) { data = value.getULong(); } + if (0 == bits) { return IE_SYNTAX_IRSEND; } + + + if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } + + char dvalue[32]; + char hvalue[32]; + + + + irsend_active = true; + bool success = irsend->send(protocol, data, bits, repeat); + + if (!success) { + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + return IE_NO_ERROR; +} +# 549 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +uint32_t IrRemoteSendGC(char ** pp, uint32_t count, uint32_t repeat) { + + uint16_t GC[count+1]; + for (uint32_t i = 0; i <= count; i++) { + GC[i] = strtol(strtok_r(nullptr, ",", pp), nullptr, 0); + if (!GC[i]) { return IE_INVALID_RAWDATA; } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendGC(GC, count+1); + } + return IE_NO_ERROR; +} + + + + +uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { + if (count < 2) { return IE_INVALID_RAWDATA; } + + + char * str = strtok_r(nullptr, ",", pp); + uint16_t freq = parsqeFreq(str); + + + + uint16_t parm[count-1]; + for (uint32_t i = 0; i < count-1; i++) { + parm[i] = strtol(strtok_r(nullptr, ",", pp), nullptr, 0); + if (0 == parm[i]) { return IE_INVALID_RAWDATA; } + } + + uint16_t i = 0; + if (count < 4) { + + + + + uint16_t mark, space; + space = parm[0]; + mark = space * 2; + if (3 == count) { + if (parm[1] <= 10) { + + mark = parm[0] * parm[1]; + } else { + + mark = parm[1]; + } + } + + + uint16_t raw_array[strlen(*pp)]; + for (; **pp; *(*pp)++) { + if (**pp == '0') { + raw_array[i++] = space; + } + else if (**pp == '1') { + raw_array[i++] = mark; + } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + + irsend->sendRaw(raw_array, i, freq); + if (r < repeat) { + irsend->space(40000); + } + } + } else if (6 == count) { + + uint16_t raw_array[strlen(*pp)*2+3]; + raw_array[i++] = parm[0]; + raw_array[i++] = parm[1]; + uint32_t inter_message_32 = (parm[0] + parm[1]) * 3; + uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; + for (; **pp; *(*pp)++) { + if (**pp == '0') { + raw_array[i++] = parm[2]; + raw_array[i++] = parm[3]; + } + else if (**pp == '1') { + raw_array[i++] = parm[2]; + raw_array[i++] = parm[4]; + } + } + raw_array[i++] = parm[2]; + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + + irsend->sendRaw(raw_array, i, freq); + if (r < repeat) { + irsend->space(inter_message); + } + } + } + else { return IE_INVALID_RAWDATA; } + return IE_NO_ERROR; +} +# 656 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +uint32_t IrRemoteParseRawCompact(char * str, uint16_t * arr, size_t arr_len) { + char *p = str; + size_t i = 0; + IRRawTable raw_table; + + for (char *p = str; *p; ) { + int32_t value = -1; + if ((arr_len > 0) && (i >= arr_len)) { return 0; } + + while ((*p == ',') || (*p == '+') || (*p == '-')) { p++; } + if ((*p >= '0') && (*p <= '9')) { + + value = strtoul(p, &p, 10); + raw_table.add(value); + } else { + value = raw_table.getTimingForLetter(*p); + p++; + } + if (value < 0) { return 0; } + if (nullptr != arr) { + arr[i] = value; + } + i++; + } + return i; +} +# 691 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" +uint32_t IrRemoteSendRawStandard(char ** pp, uint32_t count, uint32_t repeat) { + uint16_t freq = parsqeFreq(*pp); + + + + uint16_t * arr = nullptr; + if (count == 0) { + + count = IrRemoteParseRawCompact(*pp, nullptr, 0); + if (0 == count) { return IE_INVALID_RAWDATA; } + } else { + count++; + } + + arr = (uint16_t*) malloc(count * sizeof(uint16_t)); + if (nullptr == arr) { return IE_MEMORY; } + + count = IrRemoteParseRawCompact(*pp, arr, count); + + + if (0 == count) { return IE_INVALID_RAWDATA; } + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(arr, count, freq); + } + + if (nullptr != arr) { + free(arr); + } + return IE_NO_ERROR; + + + + count++; + if (count < 200) { + uint16_t raw_array[count]; + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", pp), nullptr, 0); + } + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", pp), nullptr, 0); + } + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + free(raw_array); + } +} + + +uint16_t parsqeFreq(char * str) { + uint16_t freq = atoi(str); + if (0 == freq) { freq = 38000; } + return freq; +} + +uint32_t IrRemoteCmndIrSendRaw(void) +{ +# 772 "/workspace/Tasmota/tasmota/xdrv_05_irremote_full.ino" + char *p; + char *str = strtok_r(XdrvMailbox.data, ",", &p); + if (p == nullptr) { return IE_INVALID_RAWDATA; } + + + uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; + + + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + + + if (strcasecmp(str, "gc") == 0) { + + + return IrRemoteSendGC(&p, count, repeat); + } else if (strcasecmp(str, "raw") == 0) { + + + + + return IrRemoteSendRawFormatted(&p, count, repeat); + } else { + + + + return IrRemoteSendRawStandard(&p, count, repeat); + } +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); + break; + case IE_INVALID_JSON: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + case IE_SYNTAX_IRHVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); + break; + case IE_UNSUPPORTED_HVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); + break; + case IE_UNSUPPORTED_PROTOCOL: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); + break; + case IE_MEMORY: + ResponseCmndChar_P(PSTR(D_JSON_MEMORY_ERROR)); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { + switch (function) { + case FUNC_PRE_INIT: + if (PinUsed(GPIO_IRSEND)) { + IrSendInit(); + } + if (PinUsed(GPIO_IRRECV)) { + IrReceiveInit(); + } + break; + case FUNC_EVERY_50_MSECOND: + if (PinUsed(GPIO_IRRECV)) { + IrReceiveCheck(); + } + irsend_active = false; + break; + case FUNC_COMMAND: + if (PinUsed(GPIO_IRSEND)) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_06_snfbridge.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_06_snfbridge.ino" +#ifdef USE_SONOFF_RF + + + + +#define XDRV_06 6 + +const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; + +enum SonoffBridgeCommands { + CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; + +const char kSonoffBridgeCommands[] PROGMEM = "|" + D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; + +void (* const SonoffBridgeCommand[])(void) PROGMEM = { + &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; + +struct SONOFFBRIDGE { + uint32_t last_received_id = 0; + uint32_t last_send_code = 0; + uint32_t last_time = 0; + uint32_t last_learn_time = 0; + uint8_t receive_flag = 0; + uint8_t receive_raw_flag = 0; + uint8_t learn_key = 1; + uint8_t learn_active = 0; + uint8_t expected_bytes = 0; +} SnfBridge; + +#ifdef USE_RF_FLASH + + + + + + + +#include "ihx.h" +#include "c2.h" + +const ssize_t RF_RECORD_NO_START_FOUND = -1; +const ssize_t RF_RECORD_NO_END_FOUND = -2; + +ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == ':') { + return i; + } + } + return RF_RECORD_NO_START_FOUND; +} + +ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == '\n') { + return i; + } + } + return RF_RECORD_NO_END_FOUND; +} + +ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len) +{ + ssize_t record_start; + ssize_t record_end; + ssize_t glue_record_sz; + uint8_t *glue_buf; + ssize_t result; + + if (remnant_data[0] != ':') { return -8; } + + + record_end = rf_find_hex_record_end(new_data, new_data_len); + record_start = rf_find_hex_record_start(new_data, new_data_len); + + + + + if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) { + return -8; + } + + glue_record_sz = strlen((const char *) remnant_data) + record_end; + + glue_buf = (uint8_t *) malloc(glue_record_sz); + if (glue_buf == nullptr) { return -2; } + + + memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data)); + memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end); + + result = rf_decode_and_write(glue_buf, glue_record_sz); + free(glue_buf); + return result; +} + +ssize_t rf_decode_and_write(uint8_t *record, size_t size) +{ + uint8_t err = ihx_decode(record, size); + if (err != IHX_SUCCESS) { return -13; } + + ihx_t *h = (ihx_t *) record; + if (h->record_type == IHX_RT_DATA) { + int retries = 5; + uint16_t address = h->address_high * 0x100 + h->address_low; + + do { + err = c2_programming_init(C2_DEVID_EFM8BB1); + err = c2_block_write(address, h->data, h->len); + } while (err != C2_SUCCESS && retries--); + } else if (h->record_type == IHX_RT_END_OF_FILE) { + + err = c2_reset(); + } + + if (err != C2_SUCCESS) { return -12; } + + return 0; +} + +ssize_t rf_search_and_write(uint8_t *buf, size_t size) +{ + + ssize_t rec_end; + ssize_t rec_start; + ssize_t err; + + for (size_t i = 0; i < size; i++) { + + rec_start = rf_find_hex_record_start(buf + i, size - i); + if (rec_start == RF_RECORD_NO_START_FOUND) { + + return -8; + } + + + rec_start += i; + rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start); + if (rec_end == RF_RECORD_NO_END_FOUND) { + + return rec_start; + } + + + rec_end += rec_start; + + err = rf_decode_and_write(buf + rec_start, rec_end - rec_start); + if (err < 0) { return err; } + i = rec_end; + } + + return 0; +} + +uint8_t rf_erase_flash(void) +{ + uint8_t err; + + for (uint32_t i = 0; i < 4; i++) { + err = c2_programming_init(C2_DEVID_EFM8BB1); + if (err != C2_SUCCESS) { + return 10; + } + err = c2_device_erase(); + if (err != C2_SUCCESS) { + if (i < 3) { + c2_reset(); + } else { + return 11; + } + } else { + break; + } + } + return 0; +} + +uint8_t SnfBrUpdateInit(void) +{ + pinMode(PIN_C2CK, OUTPUT); + pinMode(PIN_C2D, INPUT); + + return rf_erase_flash(); +} +#endif + + + +void SonoffBridgeReceivedRaw(void) +{ + + uint8_t buckets = 0; + + if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } + + ResponseTime_P(PSTR(",\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); + for (uint32_t i = 0; i < serial_in_byte_counter; i++) { + ResponseAppend_P(PSTR("%02X"), serial_in_buffer[i]); + if (0xB1 == serial_in_buffer[1]) { + if ((i > 3) && buckets) { buckets--; } + if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) { + ResponseAppend_P(PSTR(" ")); + } + } + } + ResponseAppend_P(PSTR("\"}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); +} + + + +void SonoffBridgeLearnFailed(void) +{ + SnfBridge.learn_active = 0; + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); +} + +void SonoffBridgeReceived(void) +{ + uint16_t sync_time = 0; + uint16_t low_time = 0; + uint16_t high_time = 0; + uint32_t received_id = 0; + char rfkey[8]; + char stemp[16]; + + AddLogSerial(LOG_LEVEL_DEBUG); + + if (0xA2 == serial_in_buffer[0]) { + SonoffBridgeLearnFailed(); + } + else if (0xA3 == serial_in_buffer[0]) { + SnfBridge.learn_active = 0; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + if (low_time && high_time) { + for (uint32_t i = 0; i < 9; i++) { + Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; + } + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); + } else { + SonoffBridgeLearnFailed(); + } + } + else if (0xA4 == serial_in_buffer[0]) { + if (SnfBridge.learn_active) { + SonoffBridgeLearnFailed(); + } else { + sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; + + unsigned long now = millis(); + if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { + SnfBridge.last_received_id = received_id; + SnfBridge.last_time = now; + strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); + for (uint32_t i = 1; i <= 16; i++) { + if (Settings.rf_code[i][0]) { + uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; + if (send_id == received_id) { + snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i); + break; + } + } + } + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); + } + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), + sync_time, low_time, high_time, stemp, rfkey); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); + #ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, received_id); + #endif + } + } + } +} + +bool SonoffBridgeSerialInput(void) +{ + + static int8_t receive_len = 0; + + if (SnfBridge.receive_flag) { + if (SnfBridge.receive_raw_flag) { + if (!serial_in_byte_counter) { + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 3) { + if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { + receive_len = serial_in_buffer[2] + 4; + } + } + if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { + SonoffBridgeReceivedRaw(); + SnfBridge.receive_flag = 0; + return 1; + } + } + else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { + if (0 == serial_in_byte_counter) { + SnfBridge.expected_bytes = 2; + if (serial_in_byte >= 0xA3) { + SnfBridge.expected_bytes = 11; + } + if (serial_in_byte == 0xA6) { + SnfBridge.expected_bytes = 0; + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + SnfBridge.receive_raw_flag = 1; + } + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { + SonoffBridgeReceived(); + SnfBridge.receive_flag = 0; + return 1; + } + } + serial_in_byte = 0; + } + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + serial_in_byte = 0; + SnfBridge.receive_flag = 1; + receive_len = 0; + } + return 0; +} + +void SonoffBridgeSendCommand(uint8_t code) +{ + Serial.write(0xAA); + Serial.write(code); + Serial.write(0x55); +} + +void SonoffBridgeSendAck(void) +{ + Serial.write(0xAA); + Serial.write(0xA0); + Serial.write(0x55); +} + +void SonoffBridgeSendCode(uint32_t code) +{ + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 6; i++) { + Serial.write(Settings.rf_code[0][i]); + } + Serial.write((code >> 16) & 0xff); + Serial.write((code >> 8) & 0xff); + Serial.write(code & 0xff); + Serial.write(0x55); + Serial.flush(); +} + +void SonoffBridgeSend(uint8_t idx, uint8_t key) +{ + uint8_t code; + + key--; + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 8; i++) { + Serial.write(Settings.rf_code[idx][i]); + } + if (0 == idx) { + code = (0x10 << (key >> 2)) | (1 << (key & 3)); + } else { + code = Settings.rf_code[idx][8]; + } + Serial.write(code); + Serial.write(0x55); + Serial.flush(); +#ifdef USE_DOMOTICZ + + +#endif +} + +void SonoffBridgeLearn(uint8_t key) +{ + SnfBridge.learn_key = key; + SnfBridge.learn_active = 1; + SnfBridge.last_learn_time = millis(); + Serial.write(0xAA); + Serial.write(0xA1); + Serial.write(0x55); +} + + + + + +void CmndRfBridge(void) +{ + char *p; + char stemp [10]; + uint32_t code = 0; + uint8_t radix = 10; + + uint32_t set_index = XdrvMailbox.command_code *2; + + if (XdrvMailbox.data[0] == '#') { + XdrvMailbox.data++; + XdrvMailbox.data_len--; + radix = 16; + } + + if (XdrvMailbox.data_len) { + code = strtol(XdrvMailbox.data, &p, radix); + if (code) { + if (CMND_RFCODE == XdrvMailbox.command_code) { + SnfBridge.last_send_code = code; + SonoffBridgeSendCode(code); + } else { + if (1 == XdrvMailbox.payload) { + code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); + } + uint8_t msb = code >> 8; + uint8_t lsb = code & 0xFF; + if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { + Settings.rf_code[0][set_index] = msb; + Settings.rf_code[0][set_index +1] = lsb; + } + } + } + } + if (CMND_RFCODE == XdrvMailbox.command_code) { + code = SnfBridge.last_send_code; + } else { + code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; + } + if (10 == radix) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"#%06X\""), code); + } + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); +} + +void CmndRfKey(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { + unsigned long now = millis(); + if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { + SnfBridge.learn_active = 0; + if (2 == XdrvMailbox.payload) { + SonoffBridgeLearn(XdrvMailbox.index); + ResponseCmndIdxChar(PSTR(D_JSON_START_LEARNING)); + } + else if (3 == XdrvMailbox.payload) { + Settings.rf_code[XdrvMailbox.index][0] = 0; + ResponseCmndIdxChar(PSTR(D_JSON_SET_TO_DEFAULT)); + } + else if (4 == XdrvMailbox.payload) { + for (uint32_t i = 0; i < 6; i++) { + Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; + } + Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; + Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; + Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; + ResponseCmndIdxChar(PSTR(D_JSON_SAVED)); + } else if (5 == XdrvMailbox.payload) { + uint8_t key = XdrvMailbox.index; + uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; + uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1]; + uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3]; + uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5]; + uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8); + if (0 == index) { + key--; + code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3))); + } else { + code |= Settings.rf_code[index][8]; + } + Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), + XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); + } else { + if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { + SonoffBridgeSend(0, XdrvMailbox.index); + ResponseCmndIdxChar(PSTR(D_JSON_DEFAULT_SENT)); + } else { + SonoffBridgeSend(XdrvMailbox.index, 0); + ResponseCmndIdxChar(PSTR(D_JSON_LEARNED_SENT)); + } + } + } else { + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); + } + } +} + +void CmndRfRaw(void) +{ + if (XdrvMailbox.data_len) { + if (XdrvMailbox.data_len < 6) { + switch (XdrvMailbox.payload) { + case 0: + SonoffBridgeSendCommand(0xA7); + case 1: + SnfBridge.receive_raw_flag = XdrvMailbox.payload; + break; + case 166: + case 167: + case 169: + case 176: + case 177: + case 255: + SonoffBridgeSendCommand(XdrvMailbox.payload); + SnfBridge.receive_raw_flag = 1; + break; + case 192: + char beep[] = "AAC000C055\0"; + SerialSendRaw(beep); + break; + } + } else { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + SnfBridge.receive_raw_flag = 1; + } + } + ResponseCmndStateText(SnfBridge.receive_raw_flag); +} + + + + + +bool Xdrv06(uint8_t function) +{ + bool result = false; + +#ifdef ESP8266 + if (SONOFF_BRIDGE == my_module_type) { + switch (function) { + case FUNC_SERIAL: + result = SonoffBridgeSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); + break; + case FUNC_INIT: + SnfBridge.receive_raw_flag = 0; + SonoffBridgeSendCommand(0xA7); + break; + case FUNC_PRE_INIT: + SetSerial(19200, TS_SERIAL_8N1); + break; + } + } +#endif + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_07_domoticz.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_07_domoticz.ino" +#ifdef USE_DOMOTICZ + +#define XDRV_07 7 + + +#define D_PRFX_DOMOTICZ "Dz" +#define D_CMND_IDX "Idx" +#define D_CMND_KEYIDX "KeyIdx" +#define D_CMND_SWITCHIDX "SwitchIdx" +#define D_CMND_SENSORIDX "SensorIdx" +#define D_CMND_UPDATETIMER "UpdateTimer" +#define D_CMND_DZSEND "Send" + +const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" + D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER "|" D_CMND_DZSEND ; + +void (* const DomoticzCommand[])(void) PROGMEM = { + &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer, &CmndDomoticzSend }; + +const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; + +#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS + #error "Domoticz: Too many sensors or change settings.h layout" +#endif + +const char kDomoticzSensors[] PROGMEM = + D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" + D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; + +const char kDomoticzCommand[] PROGMEM = "switchlight|switchscene"; + +char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + +int domoticz_update_timer = 0; +uint32_t domoticz_fan_debounce = 0; +bool domoticz_subscribe = false; +bool domoticz_update_flag = true; + +#ifdef USE_SHUTTER +bool domoticz_is_shutter = false; +#endif + +int DomoticzBatteryQuality(void) { + + + + + int quality = 100; + +#ifdef ESP8266 +#ifdef USE_ADC_VCC + uint16_t voltage = ESP.getVcc(); + if (voltage <= 2600) { + quality = 0; + } else if (voltage >= 4600) { + quality = 200; + } else { + quality = (voltage - 2600) / 10; + } +#endif +#endif + return quality; +} + +int DomoticzRssiQuality(void) { + + + return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; +} + +#ifdef USE_SONOFF_IFAN +void MqttPublishDomoticzFanState(void) { + if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { + char svalue[8]; + + int fan_speed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); + + domoticz_fan_debounce = millis(); + } +} + +void DomoticzUpdateFanState(void) { + if (domoticz_update_flag) { + MqttPublishDomoticzFanState(); + } + domoticz_update_flag = true; +} +#endif + +void MqttPublishDomoticzPowerState(uint8_t device) { + if (Settings.flag.mqtt_enabled) { + if (device < 1) { device = 1; } + if ((device > devices_present) || (device > MAX_DOMOTICZ_IDX)) { return; } + if (Settings.domoticz_relay_idx[device -1]) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) { + + } else { +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + + } else { +#endif + char svalue[8]; + + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); +#ifdef USE_SONOFF_IFAN + } +#endif +#ifdef USE_SHUTTER + } +#endif + } + } +} + +void DomoticzUpdatePowerState(uint8_t device) { + if (domoticz_update_flag) { + MqttPublishDomoticzPowerState(device); + } + domoticz_update_flag = true; +} + +void DomoticzMqttUpdate(void) { + if (domoticz_subscribe && (Settings.domoticz_update_timer || domoticz_update_timer)) { + domoticz_update_timer--; + if (domoticz_update_timer <= 0) { + domoticz_update_timer = Settings.domoticz_update_timer; + for (uint32_t i = 1; i <= devices_present; i++) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) + { + + break; + } +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (i > 1)) { + MqttPublishDomoticzFanState(); + break; + } else { +#endif + MqttPublishDomoticzPowerState(i); +#ifdef USE_SONOFF_IFAN + } +#endif + } + } + } +} + +void DomoticzMqttSubscribe(void) { + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (Settings.domoticz_relay_idx[i]) { + domoticz_subscribe = true; + } + } + + if (domoticz_subscribe) { + char stopic[TOPSZ]; + snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); + MqttSubscribe(stopic); + } +} + +bool DomoticzMqttData(void) { + + + + + + + domoticz_update_flag = true; + + if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { + return false; + } + + + if (XdrvMailbox.data_len < 20) { + return true; + } + + String domoticz_data = XdrvMailbox.data; + JsonParser parser((char*)domoticz_data.c_str()); + JsonParserObject domoticz = parser.getRootObject(); + if (!domoticz) { + return true; + } + + + + uint32_t idx = domoticz.getUInt(PSTR("idx"), 0); + int16_t nvalue = domoticz.getInt(PSTR("nvalue"), -1); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); + + bool found = false; + if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (idx == Settings.domoticz_relay_idx[i]) { + bool iscolordimmer = strcmp_P(domoticz.getStr(PSTR("dtype")), PSTR("Color Switch")) == 0; + bool isShutter = strcmp_P(domoticz.getStr(PSTR("dtype")), PSTR("Light/Switch")) == 0 & strncmp_P(domoticz.getStr(PSTR("switchType")),PSTR("Blinds"), 6) == 0; + + char stemp1[10]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { + JsonParserToken svalue_tok = domoticz[PSTR("svalue1")]; + if (!svalue_tok) { + return true; + } + uint8_t svalue = svalue_tok.getUInt(); + svalue = (2 == nvalue) ? svalue / 10 : 0; + if (GetFanspeed() == svalue) { + return true; + } + if (TimePassedSince(domoticz_fan_debounce) < 1000) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); + found = true; + } else +#endif +#ifdef USE_SHUTTER + if (isShutter) { + uint8_t position = domoticz.getUInt(PSTR("svalue1"), 0); + if (nvalue != 2) { + position = (0 == nvalue) ? 0 : 100; + } + + snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), position); + XdrvMailbox.data_len = position > 99 ? 3 : (position > 9 ? 2 : 1); + + found = true; + } else +#endif +#ifdef USE_LIGHT + if (iscolordimmer && 10 == nvalue) { + + JsonParserObject color = domoticz[PSTR("Color")].getObject(); + + uint16_t level = nvalue = domoticz.getUInt(PSTR("svalue1"), 0); + uint16_t r = color.getUInt(PSTR("r"), 0) * level / 100; + uint16_t g = color.getUInt(PSTR("g"), 0) * level / 100; + uint16_t b = color.getUInt(PSTR("b"), 0) * level / 100; + uint16_t cw = color.getUInt(PSTR("cw"), 0) * level / 100; + uint16_t ww = color.getUInt(PSTR("ww"), 0) * level / 100; + uint16_t m = color.getUInt(PSTR("m"), 0); + uint16_t t = color.getUInt(PSTR("t"), 0); + if (2 == m) { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); + } else { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); + } + found = true; + } + else if ((!iscolordimmer && 2 == nvalue) || + (iscolordimmer && 15 == nvalue)) { + if (domoticz[PSTR("svalue1")]) { + nvalue = domoticz.getUInt(PSTR("svalue1"), 0); + } else { + return true; + } + if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } else +#endif + if (1 == nvalue || 0 == nvalue) { + if (((power >> i) &1) == (power_t)nvalue) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + break; + } + } + } + if (!found) { return true; } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); + + domoticz_update_flag = false; + return false; +} + + + +void DomoticzSendSwitch(uint32_t type, uint32_t index, uint32_t state) { + char stemp[16]; + Response_P(PSTR("{\"command\":\"%s\",\"idx\":%d,\"switchcmd\":\"%s\"}"), + GetTextIndexed(stemp, sizeof(stemp), type, kDomoticzCommand), index, (state) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); + MqttPublish(domoticz_in_topic); +} + +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) { + bool result = false; + + if (device <= MAX_DOMOTICZ_IDX) { + if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { + DomoticzSendSwitch(0, (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], state); + result = true; + } + } + return result; +} +# 362 "/workspace/Tasmota/tasmota/xdrv_07_domoticz.ino" +void DomoticzSendData(uint32_t sensor_idx, uint32_t idx, char *data) { + if (DZ_AIRQUALITY == sensor_idx) { + Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), + idx, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } else { + uint8_t nvalue = 0; +#ifdef USE_SHUTTER + if (DZ_SHUTTER == sensor_idx) { + uint8_t position = atoi(data); + nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); + } +#endif + Response_P(DOMOTICZ_MESSAGE, + idx, nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } + MqttPublish(domoticz_in_topic); +} + +void DomoticzSensor(uint8_t idx, char *data) { + if (Settings.domoticz_sensor_idx[idx]) { + char dmess[128]; + + memcpy(dmess, mqtt_data, sizeof(dmess)); + DomoticzSendData(idx, Settings.domoticz_sensor_idx[idx], data); + memcpy(mqtt_data, dmess, sizeof(dmess)); + } +} + +uint8_t DomoticzHumidityState(float h) { + return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; +} + +void DomoticzSensor(uint8_t idx, uint32_t value) { + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d"), value); + DomoticzSensor(idx, data); +} + + +void DomoticzTempHumPressureSensor(float temp, float hum, float baro) { + char temperature[FLOATSZ]; + dtostrfd(temp, 2, temperature); + char humidity[FLOATSZ]; + dtostrfd(hum, 2, humidity); + + char data[32]; + if (baro > -1) { + char pressure[FLOATSZ]; + dtostrfd(baro, 2, pressure); + + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temperature, humidity, DomoticzHumidityState(hum), pressure); + DomoticzSensor(DZ_TEMP_HUM_BARO, data); + } else { + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temperature, humidity, DomoticzHumidityState(hum)); + DomoticzSensor(DZ_TEMP_HUM, data); + } +} + +void DomoticzSensorPowerEnergy(int power, char *energy) { + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); + DomoticzSensor(DZ_POWER_ENERGY, data); +} + +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) { + + + + + + int consumed = power; + int produced = 0; + if (power < 0) { + consumed = 0; + produced = -power; + } + char data[64]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%s;%s;%d;%d"), usage1, usage2, return1, return2, consumed, produced); + DomoticzSensor(DZ_P1_SMART_METER, data); +} + + + + + +void CmndDomoticzIdx(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzKeyIdx(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSwitchIdx(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSensorIdx(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzUpdateTimer(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.domoticz_update_timer = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.domoticz_update_timer); +} + +void CmndDomoticzSend(void) { + + + + + + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char *data; + uint32_t index = strtoul(strtok_r(XdrvMailbox.data, ",", &data), nullptr, 10); + if ((index > 0) && (data != nullptr)) { + if (XdrvMailbox.index > 3) { + uint32_t state = strtoul(data, nullptr, 10); + DomoticzSendSwitch(XdrvMailbox.index -4, index, state); + } else { + uint32_t type = DZ_TEMP; + if (2 == XdrvMailbox.index) { type = DZ_SHUTTER; } + else if (3 == XdrvMailbox.index) { type = DZ_AIRQUALITY; } + DomoticzSendData(type, index, data); + } + } + } + } + } +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_DOMOTICZ "dm" + +const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; + +const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = + "

"; + +const char HTTP_FORM_DOMOTICZ[] PROGMEM = + "
 " D_DOMOTICZ_PARAMETERS " " + "
" + ""; +const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = + "" + ""; +const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = + ""; + +void HandleDomoticzConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); + + if (Webserver->hasArg("save")) { + DomoticzSaveSettings(); + WebRestart(1); + return; + } + + char stemp[40]; + + WSContentStart_P(S_CONFIGURE_DOMOTICZ); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_DOMOTICZ); + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + if (i < devices_present) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, + i +1, i, Settings.domoticz_relay_idx[i], + i +1, i, Settings.domoticz_key_idx[i]); + } + if (PinUsed(GPIO_SWT1, i)) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, + i +1, i, Settings.domoticz_switch_idx[i]); + } +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { break; } +#endif + } + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, + i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, Settings.domoticz_sensor_idx[i]); + } + WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); + WSContentSend_P(PSTR("
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void DomoticzSaveSettings(void) { + char stemp[20]; + char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; + char tmp[100]; + + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + } + ssensor_indices[0] = '\0'; + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); + } + WebGetArg("ut", tmp, sizeof(tmp)); + Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), + Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], + Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], + Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], + ssensor_indices, Settings.domoticz_update_timer); +} +#endif + + + + + +bool Xdrv07(uint8_t function) { + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_EVERY_SECOND: + DomoticzMqttUpdate(); + break; + case FUNC_MQTT_DATA: + result = DomoticzMqttData(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_DOMOTICZ), HandleDomoticzConfiguration); + break; +#endif + case FUNC_MQTT_SUBSCRIBE: + DomoticzMqttSubscribe(); +#ifdef USE_SHUTTER + if (Settings.domoticz_sensor_idx[DZ_SHUTTER]) { domoticz_is_shutter = true; } +#endif + break; + case FUNC_MQTT_INIT: + domoticz_update_timer = 2; + break; + case FUNC_SHOW_SENSOR: + + break; + case FUNC_COMMAND: + result = DecodeCommand(kDomoticzCommands, DomoticzCommand); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +#ifdef USE_SERIAL_BRIDGE + + + + +#define XDRV_08 8 +#define HARDWARE_FALLBACK 2 + +const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; + +const char kSerialBridgeCommands[] PROGMEM = "|" + D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; + +void (* const SerialBridgeCommand[])(void) PROGMEM = { + &CmndSSerialSend, &CmndSBaudrate }; + +#include + +TasmotaSerial *SerialBridgeSerial = nullptr; + +unsigned long serial_bridge_polling_window = 0; +char *serial_bridge_buffer = nullptr; +int serial_bridge_in_byte_counter = 0; +bool serial_bridge_active = true; +bool serial_bridge_raw = false; + +void SerialBridgeInput(void) +{ + while (SerialBridgeSerial->available()) { + yield(); + uint8_t serial_in_byte = SerialBridgeSerial->read(); + + if ((serial_in_byte > 127) && !serial_bridge_raw) { + serial_bridge_in_byte_counter = 0; + SerialBridgeSerial->flush(); + return; + } + if (serial_in_byte || serial_bridge_raw) { + bool in_byte_is_delimiter = + (((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || + ((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && + !serial_bridge_raw; + + if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && + !in_byte_is_delimiter) { + serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; + } + + if ((serial_bridge_in_byte_counter >= SERIAL_BRIDGE_BUFFER_SIZE -1) || + in_byte_is_delimiter) { + serial_bridge_polling_window = 0; + break; + } + + serial_bridge_polling_window = millis(); + } + } + + if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { + serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; + bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); + + Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":")); + if (assume_json) { + ResponseAppend_P(serial_bridge_buffer); + } else { + ResponseAppend_P(PSTR("\"")); + if (serial_bridge_raw) { + char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; + ResponseAppend_P(ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char))); + } else { + ResponseAppend_P(EscapeJSONString(serial_bridge_buffer).c_str()); + } + ResponseAppend_P(PSTR("\"")); + } + ResponseJsonEnd(); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); + serial_bridge_in_byte_counter = 0; + } +} + + + +void SerialBridgeInit(void) +{ + serial_bridge_active = false; + if (PinUsed(GPIO_SBR_RX) && PinUsed(GPIO_SBR_TX)) { + SerialBridgeSerial = new TasmotaSerial(Pin(GPIO_SBR_RX), Pin(GPIO_SBR_TX), HARDWARE_FALLBACK); + if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { + if (SerialBridgeSerial->hardwareSerial()) { + ClaimSerial(); + serial_bridge_buffer = serial_in_buffer; + } else { + serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); + } + serial_bridge_active = true; + SerialBridgeSerial->flush(); + } + } +} + + + + + +void CmndSSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + serial_bridge_raw = (XdrvMailbox.index > 3); + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + SerialBridgeSerial->write("\n"); + } + else if ((2 == XdrvMailbox.index) || (4 == XdrvMailbox.index)) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + } + else if (3 == XdrvMailbox.index) { + SerialBridgeSerial->write(Unescape(XdrvMailbox.data, &XdrvMailbox.data_len), XdrvMailbox.data_len); + } + else if (5 == XdrvMailbox.index) { + char *p; + char stemp[3]; + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int size = strlen(XdrvMailbox.data); + + while (size > 1) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + SerialBridgeSerial->write(code); + size -= 2; + codes += 2; + } + } + else if (6 == XdrvMailbox.index) { + char *p; + uint8_t code; + char *values = XdrvMailbox.data; + for (char* str = strtok_r(values, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { + code = (uint8_t)atoi(str); + SerialBridgeSerial->write(code); + } + } + ResponseCmndDone(); + } + } +} + +void CmndSBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; + Settings.sbaudrate = XdrvMailbox.payload; + SerialBridgeSerial->begin(Settings.sbaudrate * 300); + } + ResponseCmndNumber(Settings.sbaudrate * 300); +} + + + + + +bool Xdrv08(uint8_t function) +{ + bool result = false; + + if (serial_bridge_active) { + switch (function) { + case FUNC_LOOP: + if (SerialBridgeSerial) { SerialBridgeInput(); } + break; + case FUNC_PRE_INIT: + SerialBridgeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_09_timers.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_09_timers.ino" +#ifdef USE_TIMERS +# 39 "/workspace/Tasmota/tasmota/xdrv_09_timers.ino" +#define XDRV_09 9 + +const char kTimerCommands[] PROGMEM = "|" + D_CMND_TIMER "|" D_CMND_TIMERS +#ifdef USE_SUNRISE + "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE +#endif + ; + +void (* const TimerCommand[])(void) PROGMEM = { + &CmndTimer, &CmndTimers +#ifdef USE_SUNRISE + , &CmndLatitude, &CmndLongitude +#endif + }; + +uint16_t timer_last_minute = 60; +int8_t timer_window[MAX_TIMERS] = { 0 }; + +#ifdef USE_SUNRISE +# 67 "/workspace/Tasmota/tasmota/xdrv_09_timers.ino" +const float pi2 = TWO_PI; +const float pi = PI; +const float RAD = DEG_TO_RAD; + + + +uint32_t JulianDate(const struct TIME_T &now) { + + + uint32_t Year = now.year; + uint32_t Month = now.month; + uint32_t Day = now.day_of_month; + uint32_t Julian; + + if (Month <= 2) { + Month += 12; + Year -= 1; + } + + + Julian = (1461 * Year + 6884416) / 4 + (153 * Month - 457) / 5 + Day; + return Julian; +} + + +float InPi(float x) +{ + return ModulusRangef(x, 0.0f, pi2); +} + + + +float TimeFormula(float *DK, uint32_t Tdays) { + float RA_Mean = 18.71506921f + (2400.0513369f / 36525.0f) * Tdays; + float M = InPi( (pi2 * 0.993133f) + (pi2 * 99.997361f / 36525.0f) * Tdays); + float L = InPi( (pi2 * 0.7859453f) + M + (6893.0f * sinf(M) + 72.0f * sinf(M+M) + (6191.2f / 36525.0f) * Tdays) * (pi2 / 1296.0e3f)); + + float eps = 0.40904f; + float cos_eps = 0.91750f; + float sin_eps = 0.39773f; + + float RA = atanf(tanf(L) * cos_eps); + if (RA < 0.0f) RA += pi; + if (L > pi) RA += pi; + RA = RA * (24.0f/pi2); + *DK = asinf(sin_eps * sinf(L)); + RA_Mean = ModulusRangef(RA_Mean, 0.0f, 24.0f); + float dRA = ModulusRangef(RA_Mean - RA, -12.0f, 12.0f); + dRA = dRA * 1.0027379f; + return dRA; +} + +void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) +{ + const uint32_t JD2000 = 2451545; + uint32_t JD = JulianDate(RtcTime); + uint32_t Tdays = JD - JD2000; + + + float DK; + + + + + + + const float h = SUNRISE_DAWN_ANGLE * RAD; + const float sin_h = sinf(h); + + float B = Settings.latitude / (1000000.0f / RAD); + + float GeographischeLaenge = ((float)Settings.longitude)/1000000; + + + + float Zeitzone = ((float)Rtc.time_timezone) / 60; + float Zeitgleichung = TimeFormula(&DK, Tdays); + float Zeitdifferenz = acosf((sin_h - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK))) * (12.0f / pi); + float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; + float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; + float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; + float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; + float Aufgang = AufgangWeltzeit + Zeitzone + (1/120.0f); + + Aufgang = ModulusRangef(Aufgang, 0.0f, 24.0f); + int AufgangStunden = (int)Aufgang; + int AufgangMinuten = (int)(60.0f * fmodf(Aufgang, 1.0f)); + float Untergang = UntergangWeltzeit + Zeitzone; + + Untergang = ModulusRangef(Untergang, 0.0f, 24.0f); + int UntergangStunden = (int)Untergang; + int UntergangMinuten = (int)(60.0f * fmodf(Untergang, 1.0f)); + + *hour_up = AufgangStunden; + *minute_up = AufgangMinuten; + *hour_down = UntergangStunden; + *minute_down = UntergangMinuten; +} + +void ApplyTimerOffsets(Timer *duskdawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + Timer stored = (Timer)*duskdawn; + + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + uint8_t mode = (duskdawn->mode -1) &1; + duskdawn->time = (hour[mode] *60) + minute[mode]; + + if (hour[mode]==255) { + + if ((Settings.latitude > 0) != (RtcTime.month>=4 && RtcTime.month<=9)) { + duskdawn->time=2046; + } else { + duskdawn->time=2047; + } + + return; + } + + + uint16_t timeBuffer; + if ((uint16_t)stored.time > 719) { + + timeBuffer = (uint16_t)stored.time - 720; + + if (timeBuffer > (uint16_t)duskdawn->time) { + timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time); + duskdawn->days = duskdawn->days >> 1; + duskdawn->days |= (stored.days << 6); + } else { + timeBuffer = (uint16_t)duskdawn->time - timeBuffer; + } + } else { + + timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time; + + if (timeBuffer >= 1440) { + timeBuffer -= 1440; + duskdawn->days = duskdawn->days << 1; + duskdawn->days |= (stored.days >> 6); + } + } + duskdawn->time = timeBuffer; +} + +String GetSun(uint32_t dawn) +{ + char stime[6]; + + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + snprintf_P(stime, sizeof(stime), PSTR("%02d:%02d"), hour[dawn], minute[dawn]); + return String(stime); +} + +uint16_t SunMinutes(uint32_t dawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + return (hour[dawn] *60) + minute[dawn]; +} + +#endif + + + +void TimerSetRandomWindow(uint32_t index) +{ + timer_window[index] = 0; + if (Settings.timer[index].window) { + timer_window[index] = (random(0, (Settings.timer[index].window << 1) +1)) - Settings.timer[index].window; + } +} + +void TimerSetRandomWindows(void) +{ + for (uint32_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } +} + +void TimerEverySecond(void) +{ + if (RtcTime.valid) { + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } + if (Settings.flag3.timers_enable && + (uptime > 60) && (RtcTime.minute != timer_last_minute)) { + timer_last_minute = RtcTime.minute; + int32_t time = (RtcTime.hour *60) + RtcTime.minute; + uint8_t days = 1 << (RtcTime.day_of_week -1); + + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + Timer xtimer = Settings.timer[i]; + if (xtimer.arm) { +#ifdef USE_SUNRISE + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + ApplyTimerOffsets(&xtimer); + if (xtimer.time>=2046) { continue; } + } +#endif + int32_t set_time = xtimer.time + timer_window[i]; + if (set_time < 0) { + set_time = abs(timer_window[i]); + } + if (set_time > 1439) { + set_time = xtimer.time - abs(timer_window[i]); + } + if (set_time > 1439) { set_time = 1439; } + + DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); + + if (time == set_time) { + if (xtimer.days & days) { + Settings.timer[i].arm = xtimer.repeat; +#if defined(USE_RULES) || defined(USE_SCRIPT) + if (POWER_BLINK == xtimer.power) { + Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); + XdrvRulesProcess(); + } else +#endif + if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power, SRC_TIMER); } + } + } + } + } + } + } +} + +void PrepShowTimer(uint32_t index) +{ + Timer xtimer = Settings.timer[index -1]; + + char days[8] = { 0 }; + for (uint32_t i = 0; i < 7; i++) { + uint8_t mask = 1 << i; + snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); + } + + char soutput[80]; + soutput[0] = '\0'; + if (devices_present) { + snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); + } +#ifdef USE_SUNRISE + char sign[2] = { 0 }; + int16_t hour = xtimer.time / 60; + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + if (hour > 11) { + hour -= 12; + sign[0] = '-'; + } + } + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%s%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.mode, sign, hour, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#else + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#endif +} + + + + + +void CmndTimer(void) +{ + uint32_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_TIMERS)) { + uint32_t error = 0; + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { + if (XdrvMailbox.payload == 0) { + Settings.timer[index -1].data = 0; + } else { + Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; + } + } else { + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + if (devices_present) { +#endif + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); + error = 1; + } + else { + char parm_uc[10]; + index--; + JsonParserToken val = root[PSTR(D_JSON_TIMER_ARM)]; + if (val) { + Settings.timer[index].arm = (val.getInt() != 0); + } +#ifdef USE_SUNRISE + val = root[PSTR(D_JSON_TIMER_MODE)]; + if (val) { + Settings.timer[index].mode = val.getUInt() & 0x03; + } +#endif + val = root[PSTR(D_JSON_TIMER_TIME)]; + if (val) { + uint16_t itime = 0; + int8_t value = 0; + uint8_t sign = 0; + char time_str[10]; + + strlcpy(time_str, val.getStr(), sizeof(time_str)); + const char *substr = strtok(time_str, ":"); + if (substr != nullptr) { + if (strchr(substr, '-')) { + sign = 1; + substr++; + } + value = atoi(substr); + if (sign) { value += 12; } + if (value > 23) { value = 23; } + itime = value * 60; + substr = strtok(nullptr, ":"); + if (substr != nullptr) { + value = atoi(substr); + if (value < 0) { value = 0; } + if (value > 59) { value = 59; } + itime += value; + } + } + Settings.timer[index].time = itime; + } + val = root[PSTR(D_JSON_TIMER_WINDOW)]; + if (val) { + Settings.timer[index].window = val.getUInt() & 0x0F; + TimerSetRandomWindow(index); + } + val = root[PSTR(D_JSON_TIMER_DAYS)]; + if (val) { + + Settings.timer[index].days = 0; + const char *tday = val.getStr(); + uint8_t i = 0; + char ch = *tday++; + while ((ch != '\0') && (i < 7)) { + if (ch == '-') { ch = '0'; } + uint8_t mask = 1 << i++; + Settings.timer[index].days |= (ch == '0') ? 0 : mask; + ch = *tday++; + } + } + val = root[PSTR(D_JSON_TIMER_REPEAT)]; + if (val) { + Settings.timer[index].repeat = (val.getUInt() != 0); + } + val = root[PSTR(D_JSON_TIMER_OUTPUT)]; + if (val) { + uint8_t device = (val.getUInt() -1) & 0x0F; + Settings.timer[index].device = (device < devices_present) ? device : 0; + } + val = root[PSTR(D_JSON_TIMER_ACTION)]; + if (val) { + uint8_t action = val.getUInt() & 0x03; + Settings.timer[index].power = (devices_present) ? action : 3; + } + + index++; + } + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + } else { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); + error = 1; + } +#endif + } + } + if (!error) { + Response_P(PSTR("{")); + PrepShowTimer(index); + ResponseJsonEnd(); + } + } +} + +void CmndTimers(void) +{ + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag3.timers_enable = XdrvMailbox.payload; + } + if (XdrvMailbox.payload == 2) { + Settings.flag3.timers_enable = !Settings.flag3.timers_enable; + } + } + + ResponseCmndStateText(Settings.flag3.timers_enable); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + + uint32_t jsflg = 0; + uint32_t lines = 1; + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg++; + PrepShowTimer(i +1); + if (jsflg > 3) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); + jsflg = 0; + } + } + mqtt_data[0] = '\0'; +} + +#ifdef USE_SUNRISE +void CmndLongitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.longitude) /1000000, 6); +} + +void CmndLatitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.latitude) /1000000, 6); +} +#endif + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + +#define WEB_HANDLE_TIMER "tm" + +const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; + +const char HTTP_BTN_MENU_TIMER[] PROGMEM = + "

"; + +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_TIMER_SCRIPT1_SIZE = 106; +const char HTTP_TIMER_SCRIPT1_COMPRESSED[] PROGMEM = "\x33\xBF\xA1\x94\x7C\x3D\xE3\xDF\x3A\x83\xA3\xE1\xC4\x8F\x04\x60\x5F\x07\x5B\x9C" + "\x83\x67\x77\x4E\xA3\x51\xDE\x3D\xA6\x77\xF5\x87\xC1\x30\x31\x63\x5F\x51\xD0\x3F" + "\xBB\xA6\x4C\x26\x35\xF5\x1D\xD3\xEF\x06\x56\xE7\x1F\x67\x78\xF1\x87\x4A\x66\xCA" + "\x20\xF3\xA9\xF5\x1F\x34\xF0\x6A\x3A\x58\xC1\x8F\x84\x20\xC5\x68\x42\x1D\xDC\x3B" + "\xC7\x83\xDC"; +#define HTTP_TIMER_SCRIPT1 Decompress(HTTP_TIMER_SCRIPT1_COMPRESSED,HTTP_TIMER_SCRIPT1_SIZE).c_str() +#else +const char HTTP_TIMER_SCRIPT1[] PROGMEM = + "var pt=[],ct=99;" + "function ce(i,q){" + "var o=document.createElement('option');" + "o.textContent=i;" + "q.appendChild(o);" + "}"; +#endif + +#ifdef USE_SUNRISE +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_TIMER_SCRIPT2_SIZE = 630; +const char HTTP_TIMER_SCRIPT2_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x43\xD4\x77\x4E\xF1\xED\x33\xBF\xA1\xA7\x50\xC3\xA8\xD4\x78" + "\x1A\x7C\x35\x78\xEE\x9F\x7B\xC3\x05\xD1\xEF\x75\x8D\x67\xC3\xD9\xF1\x0F\x61\xEF" + "\x9E\x61\x8A\x61\x9A\x31\x0F\xB3\xBC\x74\x33\xB0\x85\xB3\xC0\xC3\xE0\xCA\x3D\xE0" + "\xE8\xF7\xCF\xD1\xC6\x46\xC3\x9E\x22\x30\x46\x0F\x1A\x60\xEE\x8D\x3E\x1F\x0E\x33" + "\xBC\x7B\x4B\xD8\x77\x4E\x33\xBC\x78\x23\x51\xF0\x86\xDD\x0A\x3A\x18\x0B\x33\xE7" + "\x74\x61\xD8\x73\x99\xDE\x3C\x16\x98\x3B\xA6\xA3\xD0\xE4\x67\x78\xF6\x91\xA8\xF8" + "\x7D\x9C\x67\xD9\xDB\x23\x51\xE0\xF7\x1A\xBC\x77\x4F\xB3\xC8\x56\x02\x1E\x5E\x7C" + "\x35\x1E\x0D\x47\xC1\x87\xD1\xF4\x73\x99\x02\x9E\x10\x37\x41\x1B\x08\x3D\xDA\x60" + "\xEE\x9D\xD1\xA7\xC3\xE1\xC8\x77\x8F\xF1\xFE\x3B\xA4\x34\xF8\x7C\x39\x47\x78\xEF" + "\x1E\xD2\xF6\x1D\xD3\x90\x81\x53\x59\x3F\x0F\x87\x25\x1D\xE3\xDA\x46\xA3\xAC\xF8" + "\x72\x51\xE0\x8D\x5E\x3B\xA7\xD9\xE4\x27\xCF\xB3\xBC\x74\xF3\x09\x87\x4C\x42\xDE" + "\x11\x9B\x0F\x87\x21\xE0\xF7\x13\x0B\xCC\xF6\x82\x9D\xC3\x8C\xF0\x7B\x88\x19\x67" + "\x04\x87\xB8\x11\x38\xE6\xF6\x1D\xD1\xC7\x78\xF6\xE1\xF0\x11\x32\xD3\xC3\x3E\x61" + "\xD0\x31\x5A\x10\x84\xC2\x63\x5F\x51\x07\x82\xFA\x8F\x1A\x60\xEE\x8E\x3E\x1F\x0E" + "\x43\xBC\x40\x8F\xC0\x1D\x19\x04\xCE\x86\x7B\xED\x1D\xA1\x6D\x19\x1F\x0F\xB3\xEC" + "\xF1\xA6\x0E\xEB\x3F\x0E\x4A\x3B\xC7\xB4\x8C\x67\xCE\xEE\x9F\x0E\x4A\x3C\x16\x9E" + "\x87\xC3\x95\x67\x82\xD3\xB6\x76\xCE\xF1\xED\xC3\xA7\xD8\xDC\x33\x64\x18\xAD\x08" + "\x43\xBB\x87\x40\xAF\xD4\x08\x7A\x08\xAD\x08\x43\xBC\x78\x3D\xC7\xB8\x13\x38\x68" + "\x04\xCD\x04\x56\x88\x23\xE0\x41\xD1\xCF\x43\x95\x64\x0A\x3A\x38\x6C\xEE\xE9\xD5" + "\x87\x78\xF0\x7B\x8F\x71\xEE\x3D\xC6"; +#define HTTP_TIMER_SCRIPT2 Decompress(HTTP_TIMER_SCRIPT2_COMPRESSED,HTTP_TIMER_SCRIPT2_SIZE).c_str() +#else +const char HTTP_TIMER_SCRIPT2[] PROGMEM = + "function gt(){" + "var m,p,q;" + "m=qs('input[name=\"rd\"]:checked').value;" + "p=pt[ct]&0x7FF;" + "if(m==0){" + "so(0);" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "if((m==1)||(m==2)){" + "so(1);" + "q=Math.floor(p/60);" + "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" + "else{qs('#dr').selectedIndex=0;}" + "if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "}" + "function so(b){" + "o=qs('#ho');" + "e=o.childElementCount;" + "if(b==1){" + "qs('#dr').style.visibility='';" + "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" + "}else{" + "qs('#dr').style.visibility='hidden';" + "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" + "}" + "}"; +#endif +#endif + +#ifdef USE_UNISHOX_COMPRESSION +#ifdef USE_SUNRISE +const size_t HTTP_TIMER_SCRIPT3_SIZE = 587; +const char HTTP_TIMER_SCRIPT3_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x5E\xA3\xBA\x77\x8F\x69\x9D\xFD\x69\xD4\x11\xD4\x34\xEA\xE3" + "\xA8\x61\xD5\xE3\xC0\xD3\xE1\xC6\x78\x2F\x1F\x0E\x33\xC1\x71\xF0\xE4\x3D\x0F\x4B" + "\x87\x82\xD3\x07\x75\x8E\x3B\xA7\xDD\x9C\x67\xD9\xDE\x3A\x10\x62\x98\x66\x8C\x43" + "\xBC\x7B\x7C\x7F\x8F\x9C\x78\x3D\xDC\x7C\x39\x0F\x43\xD2\x69\x02\x1D\xFF\x82\x75" + "\xF3\x19\xF3\xBB\xA7\xC3\x8C\xF0\x5A\x7A\x1C\xF1\xE0\xB4\xED\x9D\xB3\xBC\x7B\x78" + "\xF8\x72\x1E\x87\xA1\xDD\x9C\x76\xCB\x4E\xF0\x21\xE2\x83\xE7\xD9\xDB\xD0\x4C\xC5" + "\x4F\x70\xD3\xE1\xAB\xC7\x74\xFB\xDE\x18\x2E\x8F\x7B\xAC\x6B\x3E\x1E\xCF\x88\x7B" + "\x0F\x7C\xF3\x04\x2C\x0C\xFB\x3B\xC7\x43\x3B\x08\x5B\x3C\x78\xFF\x1F\x0E\xE8\x2F" + "\xE0\xA7\xA1\xE8\x72\x91\xDE\x3C\x16\x98\x3B\xA7\xD0\x87\xE1\xC6\x77\x8F\x69\x69" + "\xF0\xD5\xE3\xBA\x7D\x9E\x42\x1C\x87\xD9\xDE\x3A\x17\x98\x4C\x3A\x62\x16\xF0\x8C" + "\xD8\x78\xD3\x07\x77\x4F\xC3\xE1\xC6\x77\x8F\x69\x78\xFF\x1F\x0E\xEE\x9E\x87\xA1" + "\xCA\xB3\xBC\x78\x3D\xC4\x08\x7A\x11\xE4\x30\x13\x30\xD3\xD0\xF4\x39\x5E\x3B\xC7" + "\x83\xDC\x4C\x2F\x33\xDB\xE3\xFC\x7C\x39\x67\xA1\xE9\x5E\x3C\x1E\xE2\x08\xF8\x77" + "\x41\x07\x0D\x15\x80\x97\x86\x9E\xB3\x9C\xCE\xF1\xDB\x23\x57\x8E\xE9\xF6\x79\x0D" + "\xD0\x4B\xB0\x77\x8F\xD1\xC6\x46\xC3\x9E\x22\x30\x46\x0F\x1A\x60\xEE\x8D\x3E\x02" + "\x16\xC2\x11\xE0\xF7\x69\x83\xBA\x77\x46\x9F\x0F\x87\x21\xDE\x3F\xC7\xF8\xEE\x90" + "\xD3\xE1\xF0\xE5\x1D\xE3\xBC\x7B\x4B\x4C\x02\x0E\x78\x27\xC1\x2F\x20\x3F\x0E\x33" + "\xBC\x7B\x4B\x4C\x1D\xD0\x8F\xC3\x8C\xEF\x1E\xD2\x08\xED\x9F\x0E\x7A\x99\xE0\xF7" + "\x1E\xE2\xF1\xFE\x3E\x04\x08\x59\xC1\xEE\xF1\xFE\x04\x3D\xE4\x68\xF8\x27\xEB\xA7" + "\x19\x11\x83\xBC\x7A\x1E\x87\x24\x3C\x10\xCA\x3D\xE0\xE8\xF7\xCF\x9E\x3C\x31\xC7" + "\x74\xFB\xA3\x8C\x81\x0F\x8A\x63\xE0\xCA\x3A\x1A\xF3\x78\xEE\x9D\xE3\xC1\xEE"; +#define HTTP_TIMER_SCRIPT3 Decompress(HTTP_TIMER_SCRIPT3_COMPRESSED,HTTP_TIMER_SCRIPT3_SIZE).c_str() +#else +const size_t HTTP_TIMER_SCRIPT3_SIZE = 424; +const char HTTP_TIMER_SCRIPT3_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x5E\xA3\xBA\x77\x8F\x69\x9D\xFD\x69\xD4\x11\xD4\x34\xEA\xE3" + "\xA8\x61\xD5\xE3\xC0\xD3\xE1\xC6\x78\x2F\x1F\x0E\x33\xC1\x71\xF0\xE4\x3D\x0F\x4B" + "\x87\x82\xD3\x07\x75\x8E\x3B\xA7\xDD\x9C\x67\xD9\xDE\x3A\x10\x62\x98\x66\x8C\x43" + "\xBC\x7B\x7C\x7F\x8F\x9C\x78\x3D\xDC\x7C\x39\x0F\x43\xD2\x69\x02\x1D\xFF\x82\x75" + "\xF3\x19\xF3\xBB\xA7\xC3\x8C\xF0\x5A\x7A\x1C\xF1\xE0\xB4\xED\x9D\xB3\xBC\x7B\x78" + "\xF8\x72\x1E\x87\xA1\xDD\x9C\x76\xCB\x4E\xF0\x21\xE2\x83\xE7\xD9\xDB\xD0\x4C\xC5" + "\x4F\x76\x98\x3B\xA7\xD0\x87\xE1\xC6\x77\x8F\x69\x69\xF0\xD5\xE3\xBA\x7D\x9E\x42" + "\x1C\x87\xD9\xDE\x3A\x17\x98\x4C\x3A\x62\x16\xF0\x8C\xD8\x78\xD3\x07\x77\x4F\xC3" + "\xE1\xC6\x77\x8F\x69\x78\xFF\x1F\x0E\xEE\x9E\x87\xA1\xCA\xB3\xBC\x78\x3D\xC5\xE3" + "\xFC\x7C\x3B\xA6\xAF\x1D\xD3\xEC\xF2\x18\x09\x98\x69\xE8\x7A\x1C\xAF\x1D\xE3\xC1" + "\xEE\x26\x17\x99\xED\xF1\xFE\x3E\x1C\xB3\xD0\xF4\xAF\x1E\x0F\x71\x04\x7C\x3B\xA0" + "\x83\x86\x8A\xC0\x4B\xC3\x4F\x59\xCE\x67\x78\xED\x91\xAB\xC7\x74\xFB\x3C\x86\xE8" + "\x25\xD8\x3B\xC7\xE8\xE3\x23\x61\xCF\x11\x18\x23\x07\x8D\x30\x77\x46\x9F\x01\x0B" + "\x61\x08\x10\x75\xB0\x41\xCA\xC6\x8F\x82\x7E\x1E\x71\x91\x18\x3B\xC7\xA1\xE8\x72" + "\x43\xC1\x0C\xA3\xDE\x0E\x8F\x7C\xF9\xE3\xC3\x1C\x77\x4F\xBA\x38\xCF\xB3\xBC\x74" + "\x23\x3B\x08\x5B\x3E\x0C\xA3\xA1\xAF\x37\x8E\xE9\xDE\x3C\x1E\xE3"; +#define HTTP_TIMER_SCRIPT3 Decompress(HTTP_TIMER_SCRIPT3_COMPRESSED,HTTP_TIMER_SCRIPT3_SIZE).c_str() +#endif +#else +const char HTTP_TIMER_SCRIPT3[] PROGMEM = + "function st(){" + "var i,l,m,n,p,s;" + "m=0;s=0;" + "n=1<<31;if(eb('a0').checked){s|=n;}" + "n=1<<15;if(eb('r0').checked){s|=n;}" + "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" +#ifdef USE_SUNRISE + "m=qs('input[name=\"rd\"]:checked').value;" + "s|=(qs('input[name=\"rd\"]:checked').value<<29);" +#endif + "if(%d>0){" + "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" + "s|=(qs('#p1').selectedIndex<<27);" + "}else{" + "s|=3<<27;" + "}" + "l=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" + "if(m==0){s|=l;}" +#ifdef USE_SUNRISE + "if((m==1)||(m==2)){" + "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" + "s|=l&0x7FF;" + "}" +#endif + "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" + "pt[ct]=s;" + "eb('t0').value=pt.join();" + "}"; +#endif + +#ifdef USE_UNISHOX_COMPRESSION +#ifdef USE_SUNRISE +const size_t HTTP_TIMER_SCRIPT4_SIZE = 548; +const char HTTP_TIMER_SCRIPT4_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x59\x47\x76\x8E\xA6\x77\x8F\x69\x9D\xFD\x69\xD5\xC7\x56\x1D" + "\x43\x0E\xA3\x51\xD5\xE3\xC6\x98\x3B\xA1\xD1\xE8\x71\x23\xBC\x7B\x4B\xD4\x77\x4E" + "\xF1\xE0\xF7\x07\x47\xCA\x3C\x61\xF0\x4C\x0C\x58\xD7\xD4\x74\x1E\x74\x4C\x26\x35" + "\xF5\x78\x87\x19\x10\x61\x5F\xBC\x5D\x63\x59\xDD\x3E\xE8\x23\xEC\xEF\x1E\x0C\x67" + "\xCE\xEE\x9F\x0E\x33\xC1\x69\xE9\x87\x40\x9F\x0F\x50\xA3\xC6\x9D\xB3\xB6\x77\x8F" + "\x6E\x1E\xF6\x9E\xF9\xD3\xD4\x64\x13\x3A\x07\xEF\x15\x33\x65\x1F\x0F\x60\xEB\x0C" + "\xD0\x7B\xF8\x2F\x84\x3C\xCF\x23\xE8\xE3\xE2\x36\x1E\x03\xC0\xB3\xE0\x85\x20\xC6" + "\x75\x1D\x63\xEF\x47\x85\x51\xE7\xD9\xF1\xB6\x11\xE0\xF6\x1E\xE6\x0C\x53\x1F\x1D" + "\x81\x08\x78\x3D\x87\x8F\x1F\x06\x51\xEF\x07\x47\xBE\x78\x18\x7C\x3B\xBE\x3F\x0F" + "\xC3\x94\x8E\xF1\xFA\xB3\xC1\x31\xC7\x74\xFB\x1C\x7D\x9D\xB1\x87\x78\xE8\x18\xA6" + "\x19\xA3\x10\xF8\x72\x1E\x08\x7A\x8E\xE9\xDE\x3C\x1A\x8F\x87\x77\xC7\xE1\xF8\x72" + "\x43\xBC\x7E\x99\x1B\x08\xC1\xE3\x4C\x1D\xD3\x51\xE8\x72\x33\xBC\x7B\x48\xD4\x7C" + "\x3E\xCE\x33\xEC\xED\x91\xA8\xF0\x7B\x8D\x5E\x3B\xA7\xD9\xE4\x34\x7C\xFB\x3B\xC7" + "\x43\x3B\x08\x5B\x3E\x1A\x81\x1B\x85\xB3\x9E\x20\x41\xE1\x50\x10\x74\x43\xBA\x72" + "\x71\xDB\x2D\x3B\xC7\x78\xFD\x1C\x87\x82\x63\x8E\xE9\xF6\x3E\x7D\x9D\xBD\x04\x5D" + "\x20\x61\xE0\xF7\x69\x83\xBA\x7D\x08\x7E\x1C\x64\x08\x78\x51\xCA\xB2\x04\x1D\x34" + "\xD5\xE3\xBA\x7D\x9E\x42\x1C\x84\x08\x99\xD8\xC3\xB6\x72\x10\x21\xF0\x28\x73\xC7" + "\x78\xFD\x59\x02\x0D\xC1\x87\x21\xF6\x77\x8E\x85\xE6\x13\x0E\x98\x85\xBC\x23\x36" + "\x1F\x06\x1E\x0F\x70\x20\xE0\x67\x26\x90\x21\xE9\xFF\x38\xCF\xB2\x04\x7D\x38\x10" + "\x6D\x9C\xB8\x40\x87\x6E\xC1\x26\xD9\xEE"; +#define HTTP_TIMER_SCRIPT4 Decompress(HTTP_TIMER_SCRIPT4_COMPRESSED,HTTP_TIMER_SCRIPT4_SIZE).c_str() +#else +const size_t HTTP_TIMER_SCRIPT4_SIZE = 620; +const char HTTP_TIMER_SCRIPT4_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x59\x47\x76\x8E\xA6\x77\x8F\x69\x9D\xFD\x69\xD5\xC7\x56\x1D" + "\x43\x0E\xA3\x51\xD5\xE3\xC6\x98\x3B\xA1\xD1\xE8\x71\x23\xBC\x7B\x4B\xD4\x77\x4E" + "\xF1\xE0\xF7\x07\x47\xCA\x3C\x61\xF0\x4C\x0C\x58\xD7\xD4\x74\x1E\x74\x4C\x26\x35" + "\xF5\x78\x87\x19\x10\x61\x5F\xBC\x5D\x63\x59\xDD\x3E\xE8\x23\xEC\xEF\x1E\x0C\x67" + "\xCE\xEE\x9F\x0E\x33\xC1\x69\xE9\x87\x40\x9F\x0F\x50\xA3\xC6\x9D\xB3\xB6\x77\x8F" + "\x6E\x1E\xF6\x9E\xF9\xD3\xD4\x64\x13\x3A\x07\xEF\x15\x33\x65\x1F\x0F\x60\xEB\x0C" + "\xD0\x7B\xF8\x2F\x84\x3C\xCF\x23\xE8\xE3\xE2\x36\x1E\x03\xC0\xB3\xE0\x85\x20\xC6" + "\x75\x1D\x63\xEF\x47\x85\x51\xE7\xD9\xF1\xB6\x11\xE0\xF6\x1E\xE6\x0C\x53\x1F\x1D" + "\x81\x08\x78\x3D\x87\x8F\x1F\x06\x51\xEF\x07\x47\xBE\x78\x18\x7C\xF1\xFA\x38\xC8" + "\xD8\x73\xC4\x46\x08\xC1\xE0\xD4\x7C\x21\xB7\x42\x8E\x86\x02\xCC\xF9\xDD\x18\x76" + "\x1C\xE6\x77\x8F\x05\xA6\x0E\xE9\xA8\xF4\x39\x19\xDE\x3D\xA4\x6A\x3E\x1F\x67\x19" + "\xF6\x76\xC8\xD4\x78\x3D\xC6\xAF\x1D\xD3\xEC\xF2\x15\x87\xD9\xDE\x3A\x19\xD8\x42" + "\xD9\xF0\xD4\x78\x35\x1F\x06\x1F\x47\xD1\xCE\x64\x0A\x78\x40\xDD\x04\x8C\x20\xEE" + "\xF8\xFC\x3F\x0E\x48\x77\x8F\xD3\x23\x61\x18\x05\x4C\x38\x7C\x11\xB0\xE0\x45\xE2" + "\x8C\xE7\x88\x10\x78\x9C\x18\x7C\x3B\xBE\x3F\x0F\xC3\xBA\x72\x71\xDB\x2D\x3B\xC7" + "\x78\xFD\x1C\x87\x82\x63\x8E\xE9\xF6\x3E\x7D\x9D\xBD\x3B\xC7\x40\xC5\x30\xCD\x18" + "\x87\xC1\x87\x83\xDD\xA6\x0E\xE9\xF4\x21\xF8\x71\x90\x21\xE1\x47\x2A\x2B\xC8\x10" + "\x74\xD3\x57\x8E\xE9\xF6\x79\x08\x72\x10\x22\x67\x63\x0E\xD9\xC8\x78\x20\x42\xBC" + "\x73\xC7\x78\xFD\x59\x02\x0D\xC1\x87\x21\xF6\x77\x8E\x85\xE6\x13\x0E\x98\x85\xBC" + "\x23\x36\x1F\x06\x1E\x0F\x70\x20\xE0\x67\x26\x90\x21\xE9\xFF\x38\xCF\xB2\x04\x7D" + "\x38\x10\x6D\x9C\xB8\x40\x87\x6E\xC1\x26\xD9\xEE"; +#define HTTP_TIMER_SCRIPT4 Decompress(HTTP_TIMER_SCRIPT4_COMPRESSED,HTTP_TIMER_SCRIPT4_SIZE).c_str() +#endif +#else +const char HTTP_TIMER_SCRIPT4[] PROGMEM = + "function ot(t,e){" + "var i,n,o,p,q,s;" + "if(ct<99){st();}" + "ct=t;" + "o=document.getElementsByClassName('tl');" + "for(i=0;i>29)&3;eb('b'+p).checked=1;" + "gt();" +#else + "p=s&0x7FF;" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" +#endif + "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" + "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" + "if(%d>0){" + "p=(s>>23)&0xF;qs('#d1').value=p+1;" + "p=(s>>27)&3;qs('#p1').selectedIndex=p;" + "}" + "p=(s>>15)&1;eb('r0').checked=p;" + "p=(s>>31)&1;eb('a0').checked=p;" + "}"; +#endif + +const char HTTP_TIMER_SCRIPT5[] PROGMEM = + "function it(){" + "var b,i,o,s;" + "pt=eb('t0').value.split(',').map(Number);" + "s='';" + "for(i=0;i<%d;i++){" + "b='';" + "if(0==i){b=\" id='dP'\";}" + "s+=\"\"" + "}" + "eb('bt').innerHTML=s;" + "if(%d>0){" + "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" + "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" +#if defined(USE_RULES) || defined(USE_SCRIPT) + "ce('" D_RULE "',o);" +#else + "ce('" D_BLINK "',o);" +#endif + "}else{" + "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" + "}"; +const char HTTP_TIMER_SCRIPT6[] PROGMEM = +#ifdef USE_SUNRISE + "o=qs('#dr');ce('+',o);ce('-',o);" +#endif + "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" + "var a='" D_DAY3LIST "';" + + + + "s='';for(i=0;i<7;i++){s+=\" \"}" + + "eb('ds').innerHTML=s;" + "eb('dP').click();" + "}" + "wl(it);"; +const char HTTP_TIMER_STYLE[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #%06x;padding:1px;width:6.25%%;}"; +const char HTTP_FORM_TIMER1[] PROGMEM = + "
" + " " D_TIMER_PARAMETERS " " + "
" + "



" + "



" + "

" + "
" + " " + "" + "

" + "
"; +#ifdef USE_SUNRISE +const char HTTP_FORM_TIMER3[] PROGMEM = + "
" + "
" + "
" + "
" + "
" + "

" + "" + " "; +#else +const char HTTP_FORM_TIMER3[] PROGMEM = + "" D_TIMER_TIME " "; +#endif + +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_FORM_TIMER4_SIZE = 249; +const char HTTP_FORM_TIMER4_COMPRESSED[] PROGMEM = "\x3D\x3C\x32\xF8\xFC\x3D\x3C\xC2\x61\xD2\xF5\x19\x04\xCF\x87\xD8\xFE\x89\x42\x8F" + "\x33\x9C\xC8\x61\xB0\xF0\x7D\xAD\x10\xF8\x7D\x8A\xC3\xEC\xFC\x3D\x0E\xC0\x41\xC0" + "\x4F\xC3\xD0\xEC\xF0\xCB\xE3\xF0\xFD\x70\xEF\x0C\x3C\x1F\x5E\x04\x18\x80\xC0\x72" + "\x41\xBA\x09\xD9\x23\x1B\xE1\x87\x83\xD0\x71\xF8\x76\xCE\xC3\xAC\xF4\x3B\x07\x02" + "\x16\x68\x0C\x0B\x2C\x1F\x04\xDC\xB0\xF4\x3B\x04\xD3\x33\xF0\xF4\x1D\xF3\xF0\xF4" + "\x13\x4C\xD6\x88\x7C\x3E\xC4\xF1\xF6\xBA\xC6\xB3\xE1\xF6\x27\x8F\xB0\x42\xBA"; +#define HTTP_FORM_TIMER4 Decompress(HTTP_FORM_TIMER4_COMPRESSED,HTTP_FORM_TIMER4_SIZE).c_str() +#else +const char HTTP_FORM_TIMER4[] PROGMEM = + "" + " " D_HOUR_MINUTE_SEPARATOR " " + "" + " +/- " + "" + "

" + "
"; +#endif + +void HandleTimerConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); + + if (Webserver->hasArg("save")) { + TimerSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_TIMER); + WSContentSend_P(HTTP_TIMER_SCRIPT1); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_TIMER_SCRIPT2); +#endif + WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT4, WebColor(COL_TIMER_TAB_BACKGROUND), WebColor(COL_TIMER_TAB_TEXT), WebColor(COL_FORM), WebColor(COL_TEXT), devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT5, MAX_TIMERS, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); + WSContentSendStyle_P(HTTP_TIMER_STYLE, WebColor(COL_FORM)); + WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); + } + WSContentSend_P(HTTP_FORM_TIMER2); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); +#else + WSContentSend_P(HTTP_FORM_TIMER3); +#endif +#ifdef USE_UNISHOX_COMPRESSION + WSContentSend_P(HTTP_FORM_TIMER4,D_HOUR_MINUTE_SEPARATOR); +#else + WSContentSend_P(HTTP_FORM_TIMER4); +#endif + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void TimerSaveSettings(void) +{ + char tmp[MAX_TIMERS *12]; + char message[LOGSZ]; + Timer timer; + + Settings.flag3.timers_enable = Webserver->hasArg("e0"); + WebGetArg("t0", tmp, sizeof(tmp)); + char *p = tmp; + snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + timer.data = strtol(p, &p, 10); + p++; + if (timer.time < 1440) { + bool flag = (timer.window != Settings.timer[i].window); + Settings.timer[i].data = timer.data; + if (flag) TimerSetRandomWindow(i); + } + snprintf_P(message, sizeof(message), PSTR("%s,0x%08X"), message, Settings.timer[i].data); + } + AddLog_P(LOG_LEVEL_DEBUG, message); +} +#endif +#endif + + + + + +bool Xdrv09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_PRE_INIT: + TimerSetRandomWindows(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + case FUNC_WEB_ADD_BUTTON: +#if defined(USE_RULES) || defined(USE_SCRIPT) + WSContentSend_P(HTTP_BTN_MENU_TIMER); +#else + if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } +#endif + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_TIMER), HandleTimerConfiguration); + break; +#endif +#endif + case FUNC_EVERY_SECOND: + TimerEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTimerCommands, TimerCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +#ifdef USE_RULES +#ifndef USE_SCRIPT +# 68 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +#define XDRV_10 10 + +#include + +#define D_CMND_RULE "Rule" +#define D_CMND_RULETIMER "RuleTimer" +#define D_CMND_EVENT "Event" +#define D_CMND_VAR "Var" +#define D_CMND_MEM "Mem" +#define D_CMND_ADD "Add" +#define D_CMND_SUB "Sub" +#define D_CMND_MULT "Mult" +#define D_CMND_SCALE "Scale" +#define D_CMND_CALC_RESOLUTION "CalcRes" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" +#define D_CMND_IF "If" + +#define D_JSON_INITIATED "Initiated" + +#define COMPARE_OPERATOR_NONE -1 +#define COMPARE_OPERATOR_EQUAL 0 +#define COMPARE_OPERATOR_BIGGER 1 +#define COMPARE_OPERATOR_SMALLER 2 +#define COMPARE_OPERATOR_EXACT_DIVISION 3 +#define COMPARE_OPERATOR_NUMBER_EQUAL 4 +#define COMPARE_OPERATOR_NOT_EQUAL 5 +#define COMPARE_OPERATOR_BIGGER_EQUAL 6 +#define COMPARE_OPERATOR_SMALLER_EQUAL 7 +#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL +const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; + +#ifdef USE_EXPRESSION + #include + + const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; + #define EXPRESSION_OPERATOR_ADD 0 + #define EXPRESSION_OPERATOR_SUBTRACT 1 + #define EXPRESSION_OPERATOR_MULTIPLY 2 + #define EXPRESSION_OPERATOR_DIVIDEDBY 3 + #define EXPRESSION_OPERATOR_MODULO 4 + #define EXPRESSION_OPERATOR_POWER 5 + + const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; + #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 + + + #define LOGIC_OPERATOR_AND 1 + #define LOGIC_OPERATOR_OR 2 + + #define IF_BLOCK_INVALID -1 + #define IF_BLOCK_ANY 0 + #define IF_BLOCK_ELSEIF 1 + #define IF_BLOCK_ELSE 2 + #define IF_BLOCK_ENDIF 3 +#endif + +const char kRulesCommands[] PROGMEM = "|" + D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" + D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION +#ifdef SUPPORT_MQTT_EVENT + "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE +#endif +#ifdef SUPPORT_IF_STATEMENT + "|" D_CMND_IF +#endif + ; + +void (* const RulesCommand[])(void) PROGMEM = { + &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, + &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution +#ifdef SUPPORT_MQTT_EVENT + , &CmndSubscribe, &CmndUnsubscribe +#endif +#ifdef SUPPORT_IF_STATEMENT + , &CmndIf +#endif + }; + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +struct RULES { + String event_value; + unsigned long timer[MAX_RULE_TIMERS] = { 0 }; + uint32_t triggers[MAX_RULE_SETS] = { 0 }; + uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; + + long new_power = -1; + long old_power = -1; + long old_dimm = -1; + + uint16_t last_minute = 60; + uint16_t vars_event = 0; + uint16_t mems_event = 0; + bool teleperiod = false; + bool busy = false; + + char event_data[100]; +} Rules; + +char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; + +#if (MAX_RULE_VARS>16) +#error MAX_RULE_VARS is bigger than 16 +#endif +#if (MAX_RULE_MEMS>16) +#error MAX_RULE_MEMS is bigger than 16 +#endif +# 214 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +#ifdef USE_UNISHOX_COMPRESSION + +String k_rules[MAX_RULE_SETS] = { String(), String(), String() }; + +#endif + + +inline bool IsRuleUncompressed(uint32_t idx) { +#ifdef USE_UNISHOX_COMPRESSION + return Settings.rules[idx][0] ? true : false; +#else + return true; +#endif +} + + +inline bool IsRuleEmpty(uint32_t idx) { +#ifdef USE_UNISHOX_COMPRESSION + return (Settings.rules[idx][0] == 0) && (Settings.rules[idx][1] == 0) ? true : false; +#else + return (Settings.rules[idx][0] == 0) ? true : false; +#endif +} + + +size_t GetRuleLen(uint32_t idx) { + + if (IsRuleUncompressed(idx)) { + return strlen(Settings.rules[idx]); + } else { + return Settings.rules[idx][1] * 8; + } +} + + +size_t GetRuleLenStorage(uint32_t idx) { +#ifdef USE_UNISHOX_COMPRESSION + if (Settings.rules[idx][0] || !Settings.rules[idx][1]) { + return 1 + strlen(Settings.rules[idx]); + } else { + return 2 + strlen(&Settings.rules[idx][1]); + } +#else + return 1 + strlen(Settings.rules[idx]); +#endif +} + +#ifdef USE_UNISHOX_COMPRESSION + +void GetRule_decompress(String &rule, const char *rule_head) { + size_t buf_len = 1 + *rule_head * 8; + rule_head++; + + rule = Decompress(rule_head, buf_len); +} +#endif + + + + + +String GetRule(uint32_t idx) { + if (IsRuleUncompressed(idx)) { + return String(Settings.rules[idx]); + } else { +#ifdef USE_UNISHOX_COMPRESSION + + String rule(""); + if (Settings.rules[idx][1] == 0) { return rule; } + + + if (0 == k_rules[idx].length() ) { + GetRule_decompress(rule, &Settings.rules[idx][1]); + if (!Settings.flag4.compress_rules_cpu) { + k_rules[idx] = rule; + } + } else { + + rule = k_rules[idx]; + } + return rule; +#endif + } + return ""; +} + +#ifdef USE_UNISHOX_COMPRESSION + + +int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len) { + int32_t len_compressed; + len_compressed = compressor.unishox_compress(in, in_len, out, out_len); + + if (len_compressed >= 0) { + + k_rules[idx] = (const char*) nullptr; + if ((!Settings.flag4.compress_rules_cpu) && out) { + + k_rules[idx] = in; + } + } + return len_compressed; +} +#endif + + + + +int32_t SetRule(uint32_t idx, const char *content, bool append = false) { + if (nullptr == content) { content = ""; } + size_t len_in = strlen(content); + bool needsCompress = false; + size_t offset = 0; + + if (len_in >= MAX_RULE_SIZE) { + needsCompress = true; + } + if (append) { + if (IsRuleUncompressed(idx) || IsRuleEmpty(idx)) { + offset = strlen(Settings.rules[idx]); + if (len_in + offset >= MAX_RULE_SIZE) { + needsCompress = true; + } + } else { + needsCompress = true; + } + } + + if (!needsCompress) { + strlcpy(Settings.rules[idx] + offset, content, sizeof(Settings.rules[idx])); + if (0 == Settings.rules[idx][0]) { + Settings.rules[idx][1] = 0; + } + +#ifdef USE_UNISHOX_COMPRESSION + if (0 != len_in + offset) { + + int32_t len_compressed, len_uncompressed; + + len_uncompressed = strlen(Settings.rules[idx]); + len_compressed = compressor.unishox_compress(Settings.rules[idx], len_uncompressed, nullptr , MAX_RULE_SIZE + 8); + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100)); + } + +#endif + + return len_in + offset; + } else { +#ifdef USE_UNISHOX_COMPRESSION + int32_t len_compressed; + + char *buf_out = (char*) malloc(MAX_RULE_SIZE + 8); + if (!buf_out) { return -1; } + + + if (append) { + String content_append = GetRule(idx); + content_append += content; + len_in = content_append.length(); + len_compressed = SetRule_compress(idx, content_append.c_str(), len_in, buf_out, MAX_RULE_SIZE + 8); + } else { + len_compressed = SetRule_compress(idx, content, len_in, buf_out, MAX_RULE_SIZE + 8); + } + + if ((len_compressed >= 0) && (len_compressed < MAX_RULE_SIZE - 2)) { + + Settings.rules[idx][0] = 0; + Settings.rules[idx][1] = (len_in + 7) / 8; + memcpy(&Settings.rules[idx][2], buf_out, len_compressed); + Settings.rules[idx][len_compressed + 2] = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Compressed from %d to %d (-%d%%)"), len_in, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_in, 0, 100)); + + + } else { + len_compressed = -1; + + k_rules[idx] = (const char *) nullptr; + } + free(buf_out); + return len_compressed; + +#else + return -1; +#endif + } + +} + + + +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all_rules) +{ + + + + + bool match = false; + char stemp[10]; + + + String rule_expr = rule; + if (Rules.teleperiod) { + int ppos = rule_expr.indexOf("TELE-"); + if (ppos == -1) { return false; } + rule_expr = rule.substring(5); + } + + String rule_name, rule_param; + int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); + + + + + + + char rule_svalue[80] = { 0 }; + float rule_value = 0; + if (compareOperator != COMPARE_OPERATOR_NONE) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = rules_vars[i]; + break; + } + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = SettingsText(SET_MEM1 + i); + break; + } + } + if (rule_param.startsWith(F("%TIME%"))) { + rule_param = String(MinutesPastMidnight()); + } + if (rule_param.startsWith(F("%UPTIME%"))) { + rule_param = String(MinutesUptime()); + } + if (rule_param.startsWith(F("%TIMESTAMP%"))) { + rule_param = GetDateAndTime(DT_LOCAL).c_str(); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + if (rule_param.startsWith(F("%SUNRISE%"))) { + rule_param = String(SunMinutes(0)); + } + if (rule_param.startsWith(F("%SUNSET%"))) { + rule_param = String(SunMinutes(1)); + } +#endif +# 478 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" + rule_param.toUpperCase(); + strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); + + int temp_value = GetStateNumber(rule_svalue); + if (temp_value > -1) { + rule_value = temp_value; + } else { + rule_value = CharToFloat((char*)rule_svalue); + } + } + + + int pos; + int rule_name_idx = 0; + if ((pos = rule_name.indexOf("[")) > 0) { + rule_name_idx = rule_name.substring(pos +1).toInt(); + if ((rule_name_idx < 1) || (rule_name_idx > 6)) { + rule_name_idx = 1; + } + rule_name = rule_name.substring(0, pos); + } + + String buf = event; + + + + JsonParser parser((char*)buf.c_str()); + JsonParserObject obj = parser.getRootObject(); + if (!obj) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: No valid JSON (%s)"), buf.c_str()); + return false; + } + String subtype; + uint32_t i = 0; + while ((pos = rule_name.indexOf("#")) > 0) { + subtype = rule_name.substring(0, pos); + obj = obj[subtype.c_str()].getObject(); + if (!obj) { return false; } + + rule_name = rule_name.substring(pos +1); + if (i++ > 10) { return false; } + + yield(); + } + + JsonParserToken val = obj[rule_name.c_str()]; + if (!val) { return false; } + const char* str_value; + if (rule_name_idx) { + if (val.isArray()) { + str_value = (val.getArray())[rule_name_idx -1].getStr(); + } else { + str_value = val.getStr(); + } + } else { + str_value = val.getStr(); + } + + + + + Rules.event_value = str_value; + + + float value = 0; + if (str_value) { + value = CharToFloat((char*)str_value); + int int_value = int(value); + int int_rule_value = int(rule_value); + switch (compareOperator) { + case COMPARE_OPERATOR_EXACT_DIVISION: + match = (int_rule_value && (int_value % int_rule_value) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + match = (!strcasecmp(str_value, rule_svalue)); + break; + case COMPARE_OPERATOR_BIGGER: + match = (value > rule_value); + break; + case COMPARE_OPERATOR_SMALLER: + match = (value < rule_value); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + match = (value == rule_value); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + match = (value != rule_value); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + match = (value >= rule_value); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + match = (value <= rule_value); + break; + default: + match = true; + } + } else match = true; + + if (stop_all_rules) { match = false; } + + + + if (bitRead(Settings.rule_once, rule_set)) { + if (match) { + if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { + bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } else { + match = false; + } + } else { + bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } + } + + + + return match; +} +# 614 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) +{ + char compare_operator[3]; + int8_t compare = COMPARE_OPERATOR_NONE; + leftExpr = expr; + int position; + for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { + snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); + if ((position = expr.indexOf(compare_operator)) > 0) { + compare = i; + leftExpr = expr.substring(0, position); + leftExpr.trim(); + rightExpr = expr.substring(position + strlen(compare_operator)); + rightExpr.trim(); + break; + } + } + return compare; +} + +void RulesVarReplace(String &commands, const String &sfind, const String &replace) +{ + + + + char *find = (char*)sfind.c_str(); + uint32_t flen = strlen(find); + + String ucommand = commands; + ucommand.toUpperCase(); + char *read_from = (char*)ucommand.c_str(); + char *write_to = (char*)commands.c_str(); + char *found_at; + while ((found_at = strstr(read_from, find)) != nullptr) { + write_to += (found_at - read_from); + memmove_P(write_to, find, flen); + write_to += flen; + read_from = found_at + flen; + } + + commands.replace(find, replace); +} + + + +bool RuleSetProcess(uint8_t rule_set, String &event_saved) +{ + bool serviced = false; + char stemp[10]; + + delay(0); + + + + String rules = GetRule(rule_set); + + Rules.trigger_count[rule_set] = 0; + int plen = 0; + int plen2 = 0; + bool stop_all_rules = false; + while (true) { + rules = rules.substring(plen); + rules.trim(); + if (!rules.length()) { return serviced; } + + String rule = rules; + rule.toUpperCase(); + if (!rule.startsWith("ON ")) { return serviced; } + + int pevt = rule.indexOf(" DO "); + if (pevt == -1) { return serviced; } + String event_trigger = rule.substring(3, pevt); + + plen = rule.indexOf(" ENDON"); + plen2 = rule.indexOf(" BREAK"); + if ((plen == -1) && (plen2 == -1)) { return serviced; } + + if (plen == -1) { plen = 9999; } + if (plen2 == -1) { plen2 = 9999; } + plen = tmin(plen, plen2); + + String commands = rules.substring(pevt +4, plen); + Rules.event_value = ""; + String event = event_saved; + + + + if (RulesRuleMatch(rule_set, event, event_trigger, stop_all_rules)) { + if (plen == plen2) { stop_all_rules = true; } + commands.trim(); + String ucommand = commands; + ucommand.toUpperCase(); + + + + if ((ucommand.indexOf("IF ") == -1) && + (ucommand.indexOf("EVENT ") != -1) && + (ucommand.indexOf("BACKLOG ") == -1)) { + commands = "backlog " + commands; + } + + RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + RulesVarReplace(commands, stemp, rules_vars[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); + } + RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); + RulesVarReplace(commands, F("%UTCTIME%"), String(UtcTime())); + RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); + RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); + RulesVarReplace(commands, F("%TOPIC%"), mqtt_topic); + snprintf_P(stemp, sizeof(stemp), PSTR("%06X"), ESP_getChipId()); + RulesVarReplace(commands, F("%DEVICEID%"), stemp); + String mac_address = WiFi.macAddress(); + mac_address.replace(":", ""); + RulesVarReplace(commands, F("%MACADDR%"), mac_address); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); + RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); +#endif +#ifdef USE_ZIGBEE + snprintf_P(stemp, sizeof(stemp), PSTR("0x%04X"), Z_GetLastDevice()); + RulesVarReplace(commands, F("%ZBDEVICE%"), String(stemp)); + RulesVarReplace(commands, F("%ZBGROUP%"), String(Z_GetLastGroup())); + RulesVarReplace(commands, F("%ZBCLUSTER%"), String(Z_GetLastCluster())); + RulesVarReplace(commands, F("%ZBENDPOINT%"), String(Z_GetLastEndpoint())); +#endif + + char command[commands.length() +1]; + strlcpy(command, commands.c_str(), sizeof(command)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); + + + +#ifdef SUPPORT_IF_STATEMENT + char *pCmd = command; + RulesPreprocessCommand(pCmd); +#endif + ExecuteCommand(command, SRC_RULE); + serviced = true; + } + plen += 6; + Rules.trigger_count[rule_set]++; + } + return serviced; +} + + + +bool RulesProcessEvent(char *json_event) +{ + if (Rules.busy) { return false; } + + Rules.busy = true; + bool serviced = false; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("RulesProcessEvent")); +#endif + + + + String event_saved = json_event; + + + + char *p = strchr(json_event, ':'); + if ((p != NULL) && !(strchr(++p, ':'))) { + event_saved.replace(F(":"), F(":{\"Data\":")); + event_saved += F("}"); + + } + event_saved.toUpperCase(); + + + + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (GetRuleLen(i) && bitRead(Settings.rule_enabled, i)) { + if (RuleSetProcess(i, event_saved)) { serviced = true; } + } + } + + Rules.busy = false; + + return serviced; +} + +bool RulesProcess(void) +{ + return RulesProcessEvent(mqtt_data); +} + +void RulesInit(void) +{ + + bitWrite(Settings.rule_once, 7, 0); + + bitWrite(Settings.rule_once, 6, 0); + + rules_flag.data = 0; + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (0 == GetRuleLen(i)) { + bitWrite(Settings.rule_enabled, i, 0); + bitWrite(Settings.rule_once, i, 0); + } + } + Rules.teleperiod = false; +} + +void RulesEvery50ms(void) +{ + if (Settings.rule_enabled && !Rules.busy) { + char json_event[120]; + + if (-1 == Rules.new_power) { Rules.new_power = power; } + if (Rules.new_power != Rules.old_power) { + if (Rules.old_power != -1) { + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + if (new_state != ((Rules.old_power >> i) &1)) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + } + } else { + + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { +#else + if (PinUsed(GPIO_SWT1, i)) { +#endif + snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); + RulesProcessEvent(json_event); + } + } + } + Rules.old_power = Rules.new_power; + } + else if (Rules.old_dimm != Settings.light_dimmer) { + if (Rules.old_dimm != -1) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); + } else { + + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); + } + RulesProcessEvent(json_event); + Rules.old_dimm = Settings.light_dimmer; + } + else if (Rules.event_data[0]) { + char *event; + char *parameter; + event = strtok_r(Rules.event_data, "=", ¶meter); + if (event) { + event = Trim(event); + if (parameter) { + parameter = Trim(parameter); + } else { + parameter = event + strlen(event); + } + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); + Rules.event_data[0] ='\0'; + RulesProcessEvent(json_event); + } else { + Rules.event_data[0] ='\0'; + } + } + else if (Rules.vars_event || Rules.mems_event){ + if (Rules.vars_event) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + if (bitRead(Rules.vars_event, i)) { + bitClear(Rules.vars_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":\"%s\"}}"), i+1, rules_vars[i]); + RulesProcessEvent(json_event); + break; + } + } + } + if (Rules.mems_event) { + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + if (bitRead(Rules.mems_event, i)) { + bitClear(Rules.mems_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":\"%s\"}}"), i+1, SettingsText(SET_MEM1 +i)); + RulesProcessEvent(json_event); + break; + } + } + } + } + else if (rules_flag.data) { + uint16_t mask = 1; + for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { + if (rules_flag.data & mask) { + rules_flag.data ^= mask; + json_event[0] = '\0'; + switch (i) { + case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Init\":1}}"), sizeof(json_event)); break; + case 1: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; + case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; + case 3: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; + case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 5: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 7: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 8: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; +#ifdef USE_SHUTTER + case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; + case 10: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; +#endif + } + if (json_event[0]) { + RulesProcessEvent(json_event); + break; + } + } + mask <<= 1; + } + } + } +} + +uint8_t rules_xsns_index = 0; + +void RulesEvery100ms(void) +{ + if (Settings.rule_enabled && !Rules.busy && (uptime > 4)) { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, rules_xsns_index); + tele_period = tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + ResponseJsonEnd(); + RulesProcessEvent(mqtt_data); + } + } +} + +void RulesEverySecond(void) +{ + if (Settings.rule_enabled && !Rules.busy) { + char json_event[120]; + + if (RtcTime.valid) { + if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { + Rules.last_minute = RtcTime.minute; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); + RulesProcessEvent(json_event); + } + } + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + if (Rules.timer[i] != 0L) { + if (TimeReached(Rules.timer[i])) { + Rules.timer[i] = 0L; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); + RulesProcessEvent(json_event); + } + } + } + } +} + +void RulesSaveBeforeRestart(void) +{ + if (Settings.rule_enabled && !Rules.busy) { + char json_event[32]; + + strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event)); + RulesProcessEvent(json_event); + } +} + +void RulesSetPower(void) +{ + Rules.new_power = XdrvMailbox.index; +} + +void RulesTeleperiod(void) +{ + Rules.teleperiod = true; + RulesProcess(); + Rules.teleperiod = false; +} + +#ifdef SUPPORT_MQTT_EVENT +# 1021 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool RulesMqttData(void) +{ + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + bool serviced = false; + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + if (event_item.Key.length() == 0) { + value = sData; + } else { + JsonParser parser((char*)sData.c_str()); + JsonParserObject jsonData = parser.getRootObject(); + + String key1 = event_item.Key; + String key2; + if (!jsonData) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + JsonParserToken value_tok = jsonData[key1.c_str()].getObject()[key2.c_str()]; + if (!value_tok) break; + value = value_tok.getStr(); + + + } else { + JsonParserToken value_tok = jsonData[key1.c_str()]; + if (!value_tok) break; + value = value_tok.getStr(); + + + } + } + value.trim(); + + snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + } + } + return serviced; +} +# 1090 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndSubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + event_name.toUpperCase(); + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + ResponseCmndChar(events.c_str()); +} +# 1170 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndUnsubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + ResponseCmndChar(events.c_str()); +} + +#endif + +#ifdef USE_EXPRESSION +# 1212 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +char * findClosureBracket(char * pStart) +{ + char * pointer = pStart + 1; + + bool bFindClosures = false; + uint8_t matchClosures = 1; + while (*pointer) + { + if (*pointer == ')') { + matchClosures--; + if (matchClosures == 0) { + bFindClosures = true; + break; + } + } else if (*pointer == '(') { + matchClosures++; + } + pointer++; + } + if (bFindClosures) { + return pointer; + } else { + return nullptr; + } +} +# 1251 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextNumber(char * &pNumber, float &value) +{ + bool bSucceed = false; + String sNumber = ""; + if (*pNumber == '-') { + sNumber = "-"; + pNumber++; + } + while (*pNumber) { + if (isdigit(*pNumber) || (*pNumber == '.')) { + sNumber += *pNumber; + pNumber++; + } else { + break; + } + } + if (sNumber.length() > 0) { + value = CharToFloat(sNumber.c_str()); + bSucceed = true; + } + return bSucceed; +} +# 1287 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextVariableValue(char * &pVarname, float &value) +{ + bool succeed = true; + value = 0; + String sVarName = ""; + while (*pVarname) { + if (isalpha(*pVarname) || isdigit(*pVarname)) { + sVarName.concat(*pVarname); + pVarname++; + } else { + break; + } + } + sVarName.toUpperCase(); + if (sVarName.startsWith(F("VAR"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_VARS) { + value = CharToFloat(rules_vars[index -1]); + } + } else if (sVarName.startsWith(F("MEM"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_MEMS) { + value = CharToFloat(SettingsText(SET_MEM1 + index -1)); + } + } else if (sVarName.equals(F("TIME"))) { + value = MinutesPastMidnight(); + } else if (sVarName.equals(F("UPTIME"))) { + value = MinutesUptime(); + } else if (sVarName.equals(F("UTCTIME"))) { + value = UtcTime(); + } else if (sVarName.equals(F("LOCALTIME"))) { + value = LocalTime(); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + } else if (sVarName.equals(F("SUNRISE"))) { + value = SunMinutes(0); + } else if (sVarName.equals(F("SUNSET"))) { + value = SunMinutes(1); +#endif +# 1335 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" + } else { + succeed = false; + } + + return succeed; +} +# 1359 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextObjectValue(char * &pointer, float &value) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + if (isdigit(*pointer) || (*pointer) == '-') { + bSucceed = findNextNumber(pointer, value); + break; + } else if (isalpha(*pointer)) { + bSucceed = findNextVariableValue(pointer, value); + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } else { + break; + } + } + return bSucceed; +} +# 1403 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + op = EXPRESSION_OPERATOR_ADD; + const char *pch = kExpressionOperators; + char ch; + while ((ch = pgm_read_byte(pch++)) != '\0') { + if (ch == *pointer) { + bSucceed = true; + pointer++; + break; + } + op++; + } + break; + } + return bSucceed; +} +# 1440 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +float calculateTwoValues(float v1, float v2, uint8_t op) +{ + switch (op) + { + case EXPRESSION_OPERATOR_ADD: + return v1 + v2; + case EXPRESSION_OPERATOR_SUBTRACT: + return v1 - v2; + case EXPRESSION_OPERATOR_MULTIPLY: + return v1 * v2; + case EXPRESSION_OPERATOR_DIVIDEDBY: + return (0 == v2) ? 0 : (v1 / v2); + case EXPRESSION_OPERATOR_MODULO: + return (0 == v2) ? 0 : (int(v1) % int(v2)); + case EXPRESSION_OPERATOR_POWER: + return FastPrecisePow(v1, v2); + } + return 0; +} +# 1493 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +float evaluateExpression(const char * expression, unsigned int len) +{ + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + char * scan_pointer = expbuf; + + LinkedList object_values; + LinkedList operators; + int8_t op; + float va; + + if (findNextObjectValue(scan_pointer, va)) { + object_values.add(va); + } else { + return 0; + } + while (*scan_pointer) + { + if (findNextOperator(scan_pointer, op) + && *scan_pointer + && findNextObjectValue(scan_pointer, va)) + { + operators.add(op); + object_values.add(va); + } else { + + break; + } + } + + + + for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { + int index = 0; + while (index < operators.size()) { + if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { + + va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); + + object_values.set(index, va); + } else { + index++; + } + } + } + return object_values.get(0); +} +#endif + +#ifdef SUPPORT_IF_STATEMENT +void CmndIf(void) +{ + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + ProcessIfStatement(parameters); + } + ResponseCmndDone(); +} +# 1567 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateComparisonExpression(const char *expression, int len) +{ + bool bResult = true; + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + String compare_expression = expbuf; + String leftExpr, rightExpr; + int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); + + double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); + double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); + switch (compareOp) { + case COMPARE_OPERATOR_EXACT_DIVISION: + bResult = (rightValue != 0 && leftValue == int(leftValue) + && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + bResult = leftExpr.equalsIgnoreCase(rightExpr); + break; + case COMPARE_OPERATOR_BIGGER: + bResult = (leftValue > rightValue); + break; + case COMPARE_OPERATOR_SMALLER: + bResult = (leftValue < rightValue); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + bResult = (leftValue == rightValue); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + bResult = (leftValue != rightValue); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + bResult = (leftValue >= rightValue); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + bResult = (leftValue <= rightValue); + break; + } + return bResult; +} +# 1623 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + if (*pointer) { + if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { + op = LOGIC_OPERATOR_AND; + pointer += 4; + bSucceed = true; + } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { + op = LOGIC_OPERATOR_OR; + pointer += 3; + bSucceed = true; + } + } + return bSucceed; +} +# 1660 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicObjectValue(char * &pointer, bool &value) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + char * pExpr = pointer; + while (*pointer) { + if (isalpha(*pointer) + && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 + || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) + { + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } + pointer++; + } + if (!bSucceed && pointer > pExpr) { + + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + } + return bSucceed; +} +# 1709 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateLogicalExpression(const char * expression, int len) +{ + bool bResult = false; + + char expbuff[len + 1]; + memcpy(expbuff, expression, len); + expbuff[len] = '\0'; + + + char * pointer = expbuff; + LinkedList values; + LinkedList logicOperators; + + bool bValue; + if (findNextLogicObjectValue(pointer, bValue)) { + values.add(bValue); + } else { + return false; + } + int8_t op; + while (*pointer) { + if (findNextLogicOperator(pointer, op) + && (*pointer) && findNextLogicObjectValue(pointer, bValue)) + { + logicOperators.add(op); + values.add(bValue); + } else { + break; + } + } + + int index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { + values.set(index, values.get(index) && values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + + index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { + values.set(index, values.get(index) || values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + return values.get(0); +} +# 1780 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) +{ + int8_t foundBlock = IF_BLOCK_INVALID; + + const char * word; + while (*pointer) { + if (!isalpha(*pointer)) { + pointer++; + continue; + } + word = pointer; + while (*pointer && isalpha(*pointer)) { + pointer++; + } + lenWord = pointer - word; + + if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { + + + if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { + + break; + } + } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) + && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) + { + + foundBlock = IF_BLOCK_ENDIF; + break; + } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) + && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) + { + + foundBlock = IF_BLOCK_ELSEIF; + break; + } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) + && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) + { + + foundBlock = IF_BLOCK_ELSE; + break; + } + } + return foundBlock; +} +# 1837 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +void ExecuteCommandBlock(const char * commands, int len) +{ + char cmdbuff[len + 1]; + memcpy(cmdbuff, commands, len); + cmdbuff[len] = '\0'; + + + char oneCommand[len + 1]; + int insertPosition = 0; + char * pos = cmdbuff; + int lenEndBlock = 0; + while (*pos) { + if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { + pos++; + continue; + } + if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { + + pos += 8; + continue; + } + if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { + + + char *pEndif = pos + 3; + if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { + + break; + } + + memcpy(oneCommand, pos, pEndif - pos); + oneCommand[pEndif - pos] = '\0'; + pos = pEndif; + } else { + + char *pEndOfCommand = strpbrk(pos, "\x1e;"); + if (NULL == pEndOfCommand) { + pEndOfCommand = pos + strlen(pos); + } + memcpy(oneCommand, pos, pEndOfCommand - pos); + oneCommand[pEndOfCommand - pos] = '\0'; + pos = pEndOfCommand; + } + + + String sCurrentCommand = oneCommand; + sCurrentCommand.trim(); + if (sCurrentCommand.length() > 0 + && backlog.size() < MAX_BACKLOG && !backlog_mutex) + { + + backlog_mutex = true; + backlog.add(insertPosition, sCurrentCommand); + backlog_mutex = false; + insertPosition++; + } + } + return; +} +# 1907 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +void ProcessIfStatement(const char* statements) +{ + String conditionExpression; + int len = strlen(statements); + char statbuff[len + 1]; + memcpy(statbuff, statements, len + 1); + char *pos = statbuff; + int lenEndBlock = 0; + while (true) { + + + while (*pos && *pos != '(') { + pos++; + } + if (0 == *pos) { break; } + char * posEnd = findClosureBracket(pos); + + if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { + + char * cmdBlockStart = posEnd + 1; + char * cmdBlockEnd = cmdBlockStart; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_INVALID == nextBlock) { + + break; + } + ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); + pos = cmdBlockEnd; + break; + } else { + pos = posEnd + 1; + int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_ELSEIF == nextBlock) { + + continue; + } else if (IF_BLOCK_ELSE == nextBlock) { + + char * cmdBlockEnd = pos; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); + if (IF_BLOCK_ENDIF != nextBlock) { + + break; + } + ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); + break; + } else { + + break; + } + } + } +} +# 1971 "/workspace/Tasmota/tasmota/xdrv_10_rules.ino" +void RulesPreprocessCommand(char *pCommands) +{ + char * cmd = pCommands; + int lenEndBlock = 0; + while (*cmd) { + + if (';' == *cmd || isspace(*cmd)) { + cmd++; + } + else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { + + char * pIfStart = cmd; + char * pIfEnd = pIfStart + 3; + + if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { + + cmd = pIfEnd; + + + while (pIfStart < pIfEnd) { + if (';' == *pIfStart) + *pIfStart = '\x1e'; + pIfStart++; + } + } + else { + break; + } + } + else { + while (*cmd && ';' != *cmd) { + cmd++; + } + } + } + return; +} +#endif + + + + + +void CmndRule(void) +{ + if (0 == XdrvMailbox.index) { + char data = '\0'; + if (XdrvMailbox.data_len > 0) { + if (!((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10))) { + if ('"' == XdrvMailbox.data[0]) { + data = '"'; + } else { + XdrvMailbox.data_len = 0; + } + } + } + for (uint32_t i = 1; i <= MAX_RULE_SETS; i++) { + XdrvMailbox.index = i; + XdrvMailbox.data[0] = data; + CmndRule(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + } + mqtt_data[0] = '\0'; + return; + } + uint8_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_RULE_SETS)) { + + if (XdrvMailbox.data_len > 0) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; + case 2: + bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); + break; + case 4: + case 5: + bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); + break; + case 6: + bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); + break; + case 8: + case 9: + bitWrite(Settings.rule_stop, index -1, XdrvMailbox.payload &1); + break; + case 10: + bitWrite(Settings.rule_stop, index -1, bitRead(Settings.rule_stop, index -1) ^1); + break; + } + } else { + bool append = false; + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.data[0] = ' '; + append = true; + } + int32_t res = SetRule(index - 1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, append); + if (res < 0) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("RUL: Not enough space")); + } + } + Rules.triggers[index -1] = 0; + } + String rule = GetRule(index - 1); + size_t rule_len = rule.length(); + if (rule_len > MAX_RULE_SIZE - 3) { + + size_t start_index = 0; + while (start_index < rule_len) { + size_t last_index = start_index + MAX_RULE_SIZE - 3; + if (last_index < rule_len) { + int32_t next_index = rule.lastIndexOf(" ", last_index); + if (next_index > 0) { + last_index = next_index; + } + } else { + last_index = rule_len; + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Rule%d %s%s"), + index, 0 == start_index ? PSTR("") : PSTR("+"), + rule.substring(start_index, last_index).c_str()); + start_index = last_index + 1; + } + + + rule = rule.substring(0, MAX_RULE_SIZE); + rule += F("..."); + } + + + + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Length\":%d,\"Free\":%d,\"Rules\":\"%s\"}"), + XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), + GetStateText(bitRead(Settings.rule_stop, index -1)), + rule_len, MAX_RULE_SIZE - GetRuleLenStorage(index - 1), + EscapeJSONString(rule.c_str()).c_str()); + } +} + +void CmndRuleTimer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); + Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; +#else + Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; +#endif + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); + } + ResponseJsonEnd(); + } +} + +void CmndEvent(void) +{ + if (XdrvMailbox.data_len > 0) { + strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_EVENT, XdrvMailbox.data); +#endif + } + if (XdrvMailbox.command) ResponseCmndDone(); +} + +void CmndVariable(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (!XdrvMailbox.usridx) { + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); + } + ResponseJsonEnd(); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + } else { + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); + } +#else + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); +#endif + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } + } +} + +void CmndMemory(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MEM1, MAX_RULE_MEMS); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } else { + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } +#else + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); +#endif + bitSet(Rules.mems_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCalcResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { + Settings.flag2.calc_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.calc_resolution); +} + +void CmndAddition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndSubtract(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndMultiply(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndScale(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len +1]; + + float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); + float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); + float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); + float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); + float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); + float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); + dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +float map_double(float x, float in_min, float in_max, float out_min, float out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + + + + + +bool Xdrv10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + RulesEvery50ms(); + break; + case FUNC_EVERY_100_MSECOND: + RulesEvery100ms(); + break; + case FUNC_EVERY_SECOND: + RulesEverySecond(); + break; + case FUNC_SET_POWER: + RulesSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kRulesCommands, RulesCommand); + break; + case FUNC_RULES_PROCESS: + result = RulesProcess(); + break; + case FUNC_SAVE_BEFORE_RESTART: + RulesSaveBeforeRestart(); + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + result = RulesMqttData(); + break; +#endif + case FUNC_PRE_INIT: + RulesInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +# 21 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#ifdef USE_SCRIPT +#ifndef USE_RULES +# 41 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#define XDRV_10 10 +#define XI2C_37 37 + +#define SCRIPT_DEBUG 0 + +#define FORMAT_SPIFFS_IF_FAILED true + + +#ifndef MAXVARS +#define MAXVARS 50 +#endif +#ifndef MAXSVARS +#define MAXSVARS 5 +#endif +#define MAXNVARS MAXVARS-MAXSVARS + +#ifndef MAXFILT +#define MAXFILT 5 +#endif +#define SCRIPT_SVARSIZE 20 +#define SCRIPT_MAXSSIZE 48 +#define SCRIPT_EOL '\n' +#define SCRIPT_FLOAT_PRECISION 2 +#define PMEM_SIZE sizeof(Settings.script_pram) +#define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) +#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + +#define MAX_SARRAY_NUM 32 + +uint32_t EncodeLightId(uint8_t relay_id); +uint32_t DecodeLightId(uint32_t hue_id); + +#ifdef USE_UNISHOX_COMPRESSION +#define USE_SCRIPT_COMPRESSION +#endif + + + +#ifdef USE_SCRIPT_FATFS +#undef LITTLEFS_SCRIPT_SIZE +#undef EEP_SCRIPT_SIZE +#undef USE_SCRIPT_COMPRESSION + +#if USE_SCRIPT_FATFS==-1 + +#ifdef ESP32 + +# 87 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script fat file option -1 used" +# 87 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#else + +# 89 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script fat file option -1 used" +# 89 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#endif + +#else + +# 93 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script fat file SDC option used" +# 93 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#endif +#endif + + +#ifdef LITTLEFS_SCRIPT_SIZE +#undef EEP_SCRIPT_SIZE +#undef USE_SCRIPT_COMPRESSION + +# 101 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script little file system option used" +# 101 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#endif + + +#ifdef EEP_SCRIPT_SIZE +#undef USE_SCRIPT_COMPRESSION +#ifdef USE_24C256 + +# 108 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script 24c256 file option used" +# 108 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#else + + +# 111 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "internal eeprom script buffer used" +# 111 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + + +#endif +#endif + + +#ifdef USE_SCRIPT_COMPRESSION + +# 118 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +#pragma message "script compression option used" +# 118 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + +#endif + + +#ifdef USE_SCRIPT_COMPRESSION +#include + +#define SCRIPT_COMPRESS compressor.unishox_compress +#define SCRIPT_DECOMPRESS compressor.unishox_decompress +#ifndef UNISHOXRSIZE +#define UNISHOXRSIZE 2560 +#endif +#endif + +#ifndef STASK_PRIO +#define STASK_PRIO 1 +#endif + +#ifdef USE_SCRIPT_TIMER +#include +Ticker Script_ticker1; +Ticker Script_ticker2; +Ticker Script_ticker3; +Ticker Script_ticker4; + +void Script_ticker1_end(void) { + Script_ticker1.detach(); + Run_Scripter(">ti1", 4, 0); +} +void Script_ticker2_end(void) { + Script_ticker2.detach(); + Run_Scripter(">ti2", 4, 0); +} +void Script_ticker3_end(void) { + Script_ticker3.detach(); + Run_Scripter(">ti3", 4, 0); +} +void Script_ticker4_end(void) { + Script_ticker4.detach(); + Run_Scripter(">ti4", 4, 0); +} +#endif + + +#if defined(LITTLEFS_SCRIPT_SIZE) || (USE_SCRIPT_FATFS==-1) +#ifdef ESP32 +#include "FS.h" +#ifdef LITTLEFS_SCRIPT_SIZE +#include "SPIFFS.h" +#else +#include "FFat.h" +#endif +#else +#include +#endif +FS *fsp; +#endif + + +#ifdef LITTLEFS_SCRIPT_SIZE +void SaveFile(const char *name, const uint8_t *buf, uint32_t len) { + File file = fsp->open(name, "w"); + if (!file) return; + file.write(buf, len); + file.close(); +} + + +uint8_t fs_mounted=0; + +void LoadFile(const char *name, uint8_t *buf, uint32_t len) { + if (!fs_mounted) { +#ifdef ESP32 + if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) { +#else + if (!fsp->begin()) { +#endif + + return; + } + fs_mounted=1; + } + File file = fsp->open(name, "r"); + if (!file) return; + file.read(buf, len); + file.close(); +} +#endif + + +#define EPOCH_OFFSET 1546300800 + +enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; +enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD,SCRIPT_EVENT_HANDLED}; + +#ifdef USE_SCRIPT_FATFS + +#if USE_SCRIPT_FATFS>=0 +#include +#include +#ifdef ESP32 +FS *fsp; +#else +SDClass *fsp; +#endif +#endif + +#ifndef ESP32 + + +#if USE_SCRIPT_FATFS>=0 + +#undef FILE_WRITE +#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT) +#define FILE_APPEND (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND) + +#else + +#undef FILE_WRITE +#define FILE_WRITE "w" +#undef FILE_READ +#define FILE_READ "r" +#undef FILE_APPEND +#define FILE_APPEND "a" +#endif + +#endif + + +#ifndef FAT_SCRIPT_SIZE +#define FAT_SCRIPT_SIZE 4096 +#endif + +#ifdef ESP32 +#undef FAT_SCRIPT_NAME +#define FAT_SCRIPT_NAME "/script.txt" +#else +#undef FAT_SCRIPT_NAME +#define FAT_SCRIPT_NAME "script.txt" +#endif + + + + + +#endif + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS +#include +extern VButton *buttons[MAXBUTTONS]; +#endif +#endif + +typedef union { +#ifdef USE_SCRIPT_GLOBVARS + uint16_t data; +#else + uint8_t data; +#endif + struct { + uint8_t is_string : 1; + uint8_t is_permanent : 1; + uint8_t is_timer : 1; + uint8_t is_autoinc : 1; + uint8_t changed : 1; + uint8_t settable : 1; + uint8_t is_filter : 1; + uint8_t constant : 1; +#ifdef USE_SCRIPT_GLOBVARS + uint8_t global : 1; +#endif + }; +} SCRIPT_TYPE; + +struct T_INDEX { + uint8_t index; + SCRIPT_TYPE bits; +}; + +struct M_FILT { +#ifdef LARGE_ARRAYS + uint16_t numvals; + uint16_t index; +#else + uint8_t numvals; + uint8_t index; +#endif + float maccu; + float rbuff[1]; +}; + + +#ifdef LARGE_ARRAYS +#undef AND_FILT_MASK +#undef OR_FILT_MASK +#define AND_FILT_MASK 0x7fff +#define OR_FILT_MASK 0x8000 +#undef MAX_ARRAY_SIZE +#define MAX_ARRAY_SIZE 1000 +#else +#undef AND_FILT_MASK +#undef OR_FILT_MASK +#define AND_FILT_MASK 0x7f +#define OR_FILT_MASK 0x80 +#undef MAX_ARRAY_SIZE +#define MAX_ARRAY_SIZE 127 +#endif + + +typedef union { + uint8_t data; + struct { + uint8_t nutu8 : 1; + uint8_t nutu7 : 1; + uint8_t nutu6 : 1; + uint8_t nutu5 : 1; + uint8_t nutu4 : 1; + uint8_t nutu3 : 1; + uint8_t is_dir : 1; + uint8_t is_open : 1; + }; +} FILE_FLAGS; + +typedef union { + uint8_t data; + struct { + uint8_t nutu8 : 1; + uint8_t nutu7 : 1; + uint8_t nutu6 : 1; + uint8_t nutu5 : 1; + uint8_t nutu4 : 1; + uint8_t nutu3 : 1; + uint8_t udp_connected : 1; + uint8_t udp_used : 1; + }; +} UDP_FLAGS; + + +#define NUM_RES 0xfe +#define STR_RES 0xfd +#define VAR_NV 0xff + +#define NTYPE 0 +#define STYPE 0x80 + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +#define SFS_MAX 4 + +struct SCRIPT_MEM { + float *fvars; + float *s_fvars; + struct T_INDEX *type; + struct M_FILT *mfilt; + char *glob_vnp; +#ifdef SCRIPT_LARGE_VNBUFF + uint16_t *vnp_offset; +#else + uint8_t *vnp_offset; +#endif + char *glob_snp; + char *scriptptr; + char *section_ptr; + char *scriptptr_bu; + char *script_ram; + uint16_t script_size; + uint8_t *script_pram; + uint16_t script_pram_size; + uint8_t numvars; + void *script_mem; + uint16_t script_mem_size; + uint8_t script_dprec; + uint8_t script_lzero; + uint8_t var_not_found; + uint8_t glob_error; + uint8_t max_ssize; + uint8_t script_loglevel; + uint8_t flags; + uint8_t si_num[3]; + uint8_t siro_num[3]; + uint8_t sind_num; + char *last_index_string[3]; + +#ifdef USE_SCRIPT_FATFS + File files[SFS_MAX]; + FILE_FLAGS file_flags[SFS_MAX]; + uint8_t script_sd_found; + char flink[2][14]; +#endif +#ifdef USE_SCRIPT_GLOBVARS + UDP_FLAGS udp_flags; +#endif + char web_mode; +} glob_script_mem; + + + +bool event_handeled = false; + + +#ifdef USE_SCRIPT_GLOBVARS +IPAddress last_udp_ip; +WiFiUDP Script_PortUdp; + +#ifndef USE_DEVICE_GROUPS +char * IPAddressToString(const IPAddress& ip_address) { + static char ipbuffer[16]; + sprintf_P(ipbuffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); + return ipbuffer; +} +#endif +#endif + +int16_t last_findex; +int16_t last_sindex; +uint8_t tasm_cmd_activ=0; +uint8_t fast_script=0; +uint8_t glob_script=0; +uint32_t script_lastmillis; + +void flt2char(float num, char *nbuff) { + dtostrfd(num, glob_script_mem.script_dprec, nbuff); +} + +void f2char(float num, uint32_t dprec, uint32_t lzeros, char *nbuff) { + dtostrfd(num, dprec, nbuff); + if (lzeros>1) { + + uint32_t nd = num; + nd/=10; + nd+=1; + if (lzeros>nd) { + + char cpbuf[24]; + char *cp = cpbuf; + for (uint32_t cnt = 0; cnt < lzeros - nd; cnt++) { + *cp++='0'; + } + *cp=0; + strcat(cpbuf,nbuff); + strcpy(nbuff,cpbuf); + } + } +} + +#ifdef USE_BUTTON_EVENT +int8_t script_button[MAX_KEYS]; +#endif + +char *GetNumericArgument(char *lp,uint8_t lastop,float *fp, JsonParserObject *jo); +char *GetStringArgument(char *lp,uint8_t lastop,char *cp, JsonParserObject *jo); +char *ForceStringVar(char *lp,char *dstr); +void send_download(void); +uint8_t reject(char *name); + +void ScriptEverySecond(void) { + + if (bitRead(Settings.rule_enabled, 0)) { + struct T_INDEX *vtp = glob_script_mem.type; + float delta = (millis() - script_lastmillis) / 1000.0; + script_lastmillis = millis(); + for (uint8_t count=0; count0) { + + *fp -= delta; + if (*fp<0) *fp = 0; + } + } + if (vtp[count].bits.is_autoinc) { + + float *fp = &glob_script_mem.fvars[vtp[count].index]; + if (*fp>=0) { + *fp += delta; + } + } + } + Run_Scripter(">S", 2, 0); + } +} + +void RulesTeleperiod(void) { + if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T", 2, mqtt_data); +} + + + +#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); +#define EEP_READ(A,B,C) eeprom_readBytes(A, B, (uint8_t*)C); + + +#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; +#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; + +float *Get_MFAddr(uint8_t index, uint16_t *len, uint16_t *ipos); + + +int16_t Init_Scripter(void) { +char *script; + + script = glob_script_mem.script_ram; + + + uint16_t lines = 0; + uint16_t nvars = 0; + uint16_t svars = 0; + uint16_t vars = 0; + char *lp = script; + char vnames[MAXVARS*10]; + char *vnames_p = vnames; + char *vnp[MAXVARS]; + char **vnp_p = vnp; + char strings[MAXSVARS*SCRIPT_MAXSSIZE]; + struct M_FILT mfilt[MAXFILT]; + + char *strings_p = strings; + char *snp[MAXSVARS]; + char **snp_p = snp; + uint8_t numperm = 0; + uint8_t numflt = 0; + uint16_t count; + + glob_script_mem.max_ssize = SCRIPT_SVARSIZE; + glob_script_mem.scriptptr = 0; + + if (!*script) return -999; + + float fvalues[MAXVARS]; + struct T_INDEX vtypes[MAXVARS]; + char init = 0; + while (1) { + + + SCRIPT_SKIP_SPACES + + if (*lp=='\n' || *lp=='\r') goto next_line; + + if (*lp==';') goto next_line; + if (init) { + + if (*lp=='>' || !*lp) { + init = 0; + break; + } + char *op = strchr(lp, '='); + if (op) { + vtypes[vars].bits.data = 0; + + if (*lp=='p' && *(lp+1)==':') { + lp += 2; + if (numpermMAXFILT) { + return -6; + } + } else { + vtypes[vars].bits.is_filter = 0; + } + *vnp_p++ = vnames_p; + while (lpMAXNVARS) { + return -1; + } + if (vtypes[vars].bits.is_filter) { + while (isdigit(*op) || *op=='.' || *op=='-') { + op++; + } + while (*op==' ') op++; + if (isdigit(*op)) { + + uint16_t flen = atoi(op); + if (flen>MAX_ARRAY_SIZE) { + + flen = MAX_ARRAY_SIZE; + } + mfilt[numflt-1].numvals &= OR_FILT_MASK; + mfilt[numflt-1].numvals |= flen&AND_FILT_MASK; + } + } + + } else { + + op++; + *snp_p ++= strings_p; + while (*op!='\"') { + if (*op==SCRIPT_EOL) break; + *strings_p++ = *op++; + } + *strings_p++ = 0; + vtypes[vars].bits.is_string = 1; + vtypes[vars].index = svars; + svars++; + if (svars>MAXSVARS) { + return -2; + } + } + vars++; + if (vars>MAXVARS) { + return -3; + } + } + } else { + if (!strncmp(lp, ">D", 2)) { + lp += 2; + SCRIPT_SKIP_SPACES + if (isdigit(*lp)) { + uint8_t ssize = atoi(lp)+1; + if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; + glob_script_mem.max_ssize = ssize; + } + init = 1; + } + } + + next_line: + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + + uint16_t fsize = 0; + for (count=0; count MAXVNSIZ) { + free(glob_script_mem.script_mem); + return -5; + } + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); + + + char *cp1 = glob_script_mem.glob_snp; + char *sp = strings; + for (count = 0; countnumvals = mfilt[count].numvals; + mp += sizeof(struct M_FILT) + ((mfilt[count].numvals & AND_FILT_MASK) - 1) * sizeof(float); + } + + glob_script_mem.numvars = vars; + glob_script_mem.script_dprec = SCRIPT_FLOAT_PRECISION; + glob_script_mem.script_lzero = 0; + glob_script_mem.script_loglevel = LOG_LEVEL_INFO; + + +#if SCRIPT_DEBUG>2 + struct T_INDEX *dvtp = glob_script_mem.type; + for (uint8_t count = 0; count=0 + + fsp = &SD; + if (SD.begin(USE_SCRIPT_FATFS)) { +#else + +#ifdef ESP32 + + if (FFat.begin(true)) { +#else + if (fsp->begin()) { +#endif + +#endif + + glob_script_mem.script_sd_found = 1; + } else { + glob_script_mem.script_sd_found = 0; + } + } + for (uint8_t cnt = 0; cnt0 + ClaimSerial(); + SetSerialBaudrate(9600); +#endif + + + glob_script_mem.scriptptr = lp - 1; + glob_script_mem.scriptptr_bu = glob_script_mem.scriptptr; + +#ifdef USE_SCRIPT_GLOBVARS + if (glob_script_mem.udp_flags.udp_used) { + Script_Init_UDP(); + glob_script = Run_Scripter(">G", -2, 0); + } +#endif + + return 0; + +} + +#ifdef USE_SCRIPT_FATFS +uint32_t get_fsinfo(uint32_t sel) { +uint32_t result = 0; +#ifdef ESP32 +#if USE_SCRIPT_FATFS >=0 + if (sel == 0) { + result = SD.totalBytes()/1000; + } else if (sel == 1) { + result = (SD.totalBytes() - SD.usedBytes())/1000; + } +#else + if (sel == 0) { + result = FFat.totalBytes()/1000; + } else if (sel == 1) { + result = FFat.freeBytes()/1000; + } +#endif +#else + + FSInfo64 fsinfo; + fsp->info64(fsinfo); + if (sel == 0) { + result = fsinfo.totalBytes/1000; + } else if (sel == 1) { + result = (fsinfo.totalBytes - fsinfo.usedBytes)/1000; + } +#endif + return result; +} + + +void form1000(uint32_t number, char *dp, char sc) { + char str[32]; + sprintf(str, "%d", number); + char *sp = str; + uint32_t inum = strlen(sp)/3; + uint32_t fnum = strlen(sp)%3; + if (!fnum) inum--; + for (uint32_t count=0; count<=inum; count++) { + if (fnum){ + memcpy(dp,sp,fnum); + dp+=fnum; + sp+=fnum; + fnum=0; + } else { + memcpy(dp,sp,3); + dp+=3; + sp+=3; + } + if (count!=inum) { + *dp++=sc; + } + } + *dp=0; +} + +#endif + +#ifdef USE_SCRIPT_GLOBVARS +#define SCRIPT_UDP_BUFFER_SIZE 128 +#define SCRIPT_UDP_PORT 1999 +IPAddress script_udp_remote_ip; + +void Restart_globvars(void) { + Script_Stop_UDP(); + Script_Init_UDP(); +} + +void Script_Stop_UDP(void) { + if (!glob_script_mem.udp_flags.udp_used) return; + if (glob_script_mem.udp_flags.udp_connected) { + Script_PortUdp.flush(); + Script_PortUdp.stop(); + glob_script_mem.udp_flags.udp_connected = 0; + } +} + +void Script_Init_UDP() { + if (global_state.network_down) return; + if (!glob_script_mem.udp_flags.udp_used) return; + if (glob_script_mem.udp_flags.udp_connected) return; + + if (Script_PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), SCRIPT_UDP_PORT)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP started")); + glob_script_mem.udp_flags.udp_connected = 1; + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP failed")); + glob_script_mem.udp_flags.udp_connected = 0; + } +} + +void Script_PollUdp(void) { + if (global_state.network_down) return; + if (!glob_script_mem.udp_flags.udp_used) return; + if (glob_script_mem.udp_flags.udp_connected ) { + while (Script_PortUdp.parsePacket()) { + char packet_buffer[SCRIPT_UDP_BUFFER_SIZE]; + int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE - 1); + packet_buffer[len] = 0; + script_udp_remote_ip = Script_PortUdp.remoteIP(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str()); + char *lp=packet_buffer; + if (!strncmp(lp,"=>", 2)) { + lp += 2; + char *cp=strchr(lp, '='); + if (cp) { + char vnam[32]; + for (uint32_t count = 0; countG", 2, 0); + } + } + } + } + optimistic_yield(100); + } + } else { + Script_Init_UDP(); + } +} + +void script_udp_sendvar(char *vname,float *fp,char *sp) { + if (!glob_script_mem.udp_flags.udp_used) return; + if (!glob_script_mem.udp_flags.udp_connected) return; + + char sbuf[SCRIPT_MAXSSIZE + 4]; + strcpy(sbuf, "=>"); + strcat(sbuf, vname); + strcat(sbuf, "="); + if (fp) { + char flstr[16]; + dtostrfd(*fp, 8, flstr); + strcat(sbuf, flstr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"), sbuf); + } else { + strcat(sbuf, sp); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"), sbuf); + } + Script_PortUdp.beginPacket(IPAddress(239, 255, 255, 250), SCRIPT_UDP_PORT); + + Script_PortUdp.write((const uint8_t*)sbuf, strlen(sbuf)); + Script_PortUdp.endPacket(); +} + +#endif + +#ifdef USE_LIGHT +#ifdef USE_WS2812 +void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) { + + Ws2812ForceSuspend(); + for (uint32_t cnt = 0; cntSettings.light_pixels) break; + uint32_t col = array[cnt]; + Ws2812SetColor(index + 1, col>>16, col>>8, col, 0); + } + Ws2812ForceUpdate(); +} +#endif +#endif + + + +float median_array(float *array, uint16_t len) { + uint8_t ind[len]; + uint8_t mind = 0; + uint8_t index = 0; + uint8_t flg; + float min = FLT_MAX; + + for (uint8_t hcnt = 0; hcntnumvals & AND_FILT_MASK; + if (ipos) *ipos = mflp->index; + return mflp->rbuff; + } + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); + } + return 0; +} + +char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonParserObject *jo); + + +float *get_array_by_name(char *name, uint16_t *alen) { + struct T_INDEX ind; + uint8_t vtype; + isvar(name, &vtype, &ind, 0, 0, 0); + if (vtype==VAR_NV) return 0; + if (vtype&STYPE) return 0; + uint16_t index = glob_script_mem.type[ind.index].index; + + if (glob_script_mem.type[ind.index].bits.is_filter) { + float *fa = Get_MFAddr(index, alen, 0); + return fa; + } + return 0; +} + +float Get_MFVal(uint8_t index, int16_t bind) { + uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; + for (uint8_t count = 0; countnumvals & AND_FILT_MASK; + if (!bind) { + return mflp->index; + } + if (bind<0) { + return maxind; + } + if (bind<1 || bind>maxind) bind = maxind; + return mflp->rbuff[bind - 1]; + } + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); + } + return 0; +} + +void Set_MFVal(uint8_t index, uint16_t bind, float val) { + uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; + for (uint8_t count = 0; countnumvals & AND_FILT_MASK; + if (!bind) { + mflp->index = val; + } else { + if (bind<1 || bind>maxind) bind = maxind; + mflp->rbuff[bind-1] = val; + } + return; + } + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); + } +} + + +float Get_MFilter(uint8_t index) { + uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; + for (uint8_t count = 0; countnumvals & OR_FILT_MASK) { + + return mflp->maccu / (mflp->numvals & AND_FILT_MASK); + } else { + + return median_array(mflp->rbuff, mflp->numvals); + } + } + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); + } + return 0; +} + +void Set_MFilter(uint8_t index, float invar) { + uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; + for (uint8_t count = 0; countnumvals & OR_FILT_MASK) { + + mflp->maccu -= mflp->rbuff[mflp->index]; + mflp->maccu += invar; + mflp->rbuff[mflp->index] = invar; + mflp->index++; + if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index = 0; + } else { + + mflp->rbuff[mflp->index] = invar; + mflp->index++; + if (mflp->index>=mflp->numvals) mflp->index = 0; + } + break; + } + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); + } +} + +#define MEDIAN_SIZE 5 +#define MEDIAN_FILTER_NUM 2 + +struct MEDIAN_FILTER { +float buffer[MEDIAN_SIZE]; +int8_t index; +} script_mf[MEDIAN_FILTER_NUM]; + +float DoMedian5(uint8_t index, float in) { + + if (index>=MEDIAN_FILTER_NUM) index = 0; + + struct MEDIAN_FILTER* mf = &script_mf[index]; + mf->buffer[mf->index] = in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index = 0; + return median_array(mf->buffer, MEDIAN_SIZE); +} + +#ifdef USE_LIGHT +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { +float r = 0, g = 0, b = 0; +struct HSV { + float H; + float S; + float V; +} hsv; + +hsv.H = hue; +hsv.S = (float)saturation / 100.0; +hsv.V = (float)value / 100.0; + +if (hsv.S == 0) { + r = hsv.V; + g = hsv.V; + b = hsv.V; + } else { + int i; + float f, p, q, t; + + if (hsv.H == 360) + hsv.H = 0; + else + hsv.H = hsv.H / 60; + + i = (int)trunc(hsv.H); + f = hsv.H - i; + + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * f)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.V; + g = t; + b = p; + break; + + case 1: + r = q; + g = hsv.V; + b = p; + break; + + case 2: + r = p; + g = hsv.V; + b = t; + break; + + case 3: + r = p; + g = q; + b = hsv.V; + break; + + case 4: + r = t; + g = p; + b = hsv.V; + break; + + default: + r = hsv.V; + g = p; + b = q; + break; + } + + } + + uint8_t ir, ig, ib; + ir = r * 255; + ig = g * 255; + ib = b * 255; + + uint32_t rgb = (ir<<16) | (ig<<8) | ib; + return rgb; +} +#endif + + +#ifdef USE_ANGLE_FUNC +uint32_t pulse_time_hl; +uint32_t pulse_time_lh; +uint32_t pulse_ltime_hl; +uint32_t pulse_ltime_lh; +uint8_t pt_pin; + +#define MPT_DEBOUNCE 10 + +void ICACHE_RAM_ATTR MP_Timer(void) { + uint32_t level = digitalRead(pt_pin&0x3f); + uint32_t ms = millis(); + uint32_t time; + if (level) { + + pulse_ltime_lh = ms; + time = ms - pulse_ltime_hl; + if (time>MPT_DEBOUNCE) pulse_time_hl = time; + } else { + + pulse_ltime_hl = ms; + time = ms - pulse_ltime_lh; + if (time>MPT_DEBOUNCE) pulse_time_lh = time; + } +} + +uint32_t MeasurePulseTime(int32_t in) { + if (in >= 0) { + + pt_pin = in; + pinMode(pt_pin & 0x3f, INPUT_PULLUP); + attachInterrupt(pt_pin & 0x3f, MP_Timer, CHANGE); + pulse_ltime_lh = millis(); + pulse_ltime_hl = millis(); + return 0; + } + uint32_t ptime; + if (in==-1) { + ptime = pulse_time_lh; + pulse_time_lh = 0; + } else { + ptime = pulse_time_hl; + pulse_time_hl = 0; + } + return ptime; +} +#endif + +#ifdef USE_SCRIPT_GLOBVARS +uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) { + uint16_t olen = strlen(dvnam); + struct T_INDEX *vtp = glob_script_mem.type; + for (uint32_t count = 0; count0 && glob_script_mem.last_index_string[isind]) { + free(glob_script_mem.last_index_string[isind]); + } + char *sstart = lp; + uint8_t slen = 0; + for (uint32_t cnt = 0; cnt<256; cnt++) { + if (*lp=='\n' || *lp=='"' || *lp==0) { + lp++; + if (cnt>0 && !slen) { + slen++; + } + glob_script_mem.siro_num[isind] = slen; + break; + } + if (*lp=='|') { + slen++; + } + lp++; + } + + glob_script_mem.si_num[isind] = fvar; + if (glob_script_mem.si_num[isind]>0) { + if (glob_script_mem.si_num[isind]>MAX_SARRAY_NUM) { + glob_script_mem.si_num[isind] = MAX_SARRAY_NUM; + } + + glob_script_mem.last_index_string[isind] = (char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num[isind], 1); + for (uint32_t cnt = 0; cntglob_script_mem.si_num[isind]) { + index = glob_script_mem.si_num[isind]; + } + strlcpy(str,glob_script_mem.last_index_string[isind] + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); + } + } + lp++; + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + return lp; +} + + + +char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonParserObject *jo) { + uint16_t count,len = 0; + uint8_t nres = 0; + char vname[32]; + float fvar = 0; + tind->index = 0; + tind->bits.data = 0; + + if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { + + if (fp) { + if (*lp=='0' && *(lp+1)=='x') { + lp += 2; + *fp = strtol(lp, 0, 16); + } else { + *fp = CharToFloat(lp); + } + } + if (*lp=='-') lp++; + while (isdigit(*lp) || *lp=='.') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + lp++; + } + tind->bits.constant = 1; + tind->bits.is_string = 0; + *vtype = NUM_RES; + return lp; + } + if (*lp=='"') { + lp++; + while (*lp!='"') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + uint8_t iob = *lp; + if (iob=='\\') { + lp++; + if (*lp=='t') { + iob = '\t'; + } else if (*lp=='n') { + iob = '\n'; + } else if (*lp=='r') { + iob = '\r'; + } else if (*lp=='\\') { + iob = '\\'; + } else { + lp--; + } + if (sp) *sp++ = iob; + } else { + if (sp) *sp++ = iob; + } + lp++; + } + if (sp) *sp = 0; + *vtype = STR_RES; + tind->bits.constant = 1; + tind->bits.is_string = 1; + return lp + 1; + } + + if (*lp=='-') { + + nres = 1; + lp++; + } + + const char *term="\n\r ])=+-/*%>index = VAR_NV; + glob_script_mem.var_not_found = 1; + return lp; + } + + struct T_INDEX *vtp = glob_script_mem.type; + char dvnam[32]; + strcpy (dvnam, vname); + uint8_t olen = len; + last_findex = -1; + last_sindex = -1; + char *ja = strchr(dvnam, '['); + if (ja) { + *ja = 0; + ja++; + olen = strlen(dvnam); + } + for (count = 0; countindex = count; + if (vtp[count].bits.is_string==0) { + *vtype = NTYPE | index; + if (vtp[count].bits.is_filter) { + if (ja) { + lp += olen + 1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + last_findex = fvar; + fvar = Get_MFVal(index, fvar); + len = 1; + } else { + fvar = Get_MFilter(index); + } + } else { + fvar = glob_script_mem.fvars[index]; + } + if (nres) fvar = -fvar; + if (fp) *fp = fvar; + } else { + *vtype = STYPE|index; + if (sp) strlcpy(sp, glob_script_mem.glob_snp + (index * glob_script_mem.max_ssize), SCRIPT_MAXSSIZE); + } + return lp + len; + } + } + } + + if (jo) { + + char jvname[32]; + strcpy(jvname, vname); + const char* str_value; + uint8_t aindex; + String vn; + char *ja=strchr(jvname, '['); + if (ja) { + + *ja = 0; + ja++; + + float fvar; + GetNumericArgument(ja, OPER_EQU, &fvar, 0); + aindex = fvar; + if (aindex<1 || aindex>6) aindex = 1; + aindex--; + } + if (jo->isValid()) { + char *subtype = strchr(jvname, '#'); + char *subtype2; + if (subtype) { + *subtype = 0; + subtype++; + subtype2 = strchr(subtype, '#'); + if (subtype2) { + *subtype2 = 0; + *subtype2++; + } + } + vn = jvname; + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { + if (subtype) { + JsonParserObject jobj1 = (*jo)[vn]; + if (jobj1.isValid()) { + vn = subtype; + jo = &jobj1; + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { + + if (subtype2) { + JsonParserObject jobj2 = (*jo)[vn]; + if ((*jo)[vn].isValid()) { + vn = subtype2; + jo = &jobj2; + str_value = (*jo)[vn].getStr(); + if ((*jo)[vn].isValid()) { + goto skip; + } else { + goto chknext; + } + } else { + goto chknext; + } + } + + goto skip; + } + } else { + goto chknext; + } + } + skip: + if (ja) { + + str_value = (*jo)[vn].getArray()[aindex].getStr(); + } + if (str_value && *str_value) { + if ((*jo)[vn].isStr()) { + if (!strncmp(str_value, "ON", 2)) { + if (fp) *fp = 1; + goto nexit; + } else if (!strncmp(str_value, "OFF", 3)) { + if (fp) *fp = 0; + goto nexit; + } else { + *vtype = STR_RES; + tind->bits.constant = 1; + tind->bits.is_string = 1; + if (sp) strlcpy(sp, str_value, SCRIPT_MAXSSIZE); + return lp + len; + } + + } else { + if (fp) { + if (!strncmp(vn.c_str(), "Epoch", 5)) { + *fp = atoi(str_value) - (uint32_t)EPOCH_OFFSET; + } else { + *fp = CharToFloat((char*)str_value); + } + } + nexit: + *vtype = NUM_RES; + tind->bits.constant = 1; + tind->bits.is_string = 0; + return lp + len; + } + } + } + } + } + +chknext: + switch (vname[0]) { + case 'a': +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname, "acos(", 5)) { + lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = acosf(fvar); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "abs(", 4)) { + lp=GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = fabs(fvar); + lp++; + len = 0; + goto exit; + } +#endif + if (!strncmp(vname, "asc(", 4)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = str[0]; + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "adc(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + while (*lp==' ') lp++; + float fvar1 = 1; + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + if (fvar1<32 || fvar1>39) fvar1 = 32; + } + lp++; + if (fvar > 7) fvar = 7; +#ifdef ESP32 + +#ifdef USE_ADC + fvar = AdcRead(fvar1, fvar); +#else + fvar = 999.999; +#endif +#else + +#ifndef USE_ADC_VCC + fvar = AdcRead(17, fvar); +#else + fvar = (float)ESP.getVcc() / 1000.0; +#endif +#endif + len = 0; + goto exit; + } + break; + + case 'b': + if (!strncmp(vname, "boot", 4)) { + if (rules_flag.system_boot) { + rules_flag.system_boot = 0; + fvar = 1; + } + goto exit; + } +#ifdef USE_BUTTON_EVENT + if (!strncmp(vname, "bt[", 3)) { + + GetNumericArgument(vname+3, OPER_EQU, &fvar, 0); + uint32_t index = fvar; + if (index<1 || index>MAX_KEYS) index = 1; + fvar=script_button[index - 1]; + script_button[index - 1] |= 0x80; + len++; + goto exit; + } +#endif + break; + case 'c': + if (!strncmp(vname, "chg[", 4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname + 4, &vtype, &ind, 0, 0, 0); + if (!ind.bits.constant) { + uint8_t index = glob_script_mem.type[ind.index].index; + if (glob_script_mem.fvars[index] != glob_script_mem.s_fvars[index]) { + + glob_script_mem.s_fvars[index] = glob_script_mem.fvars[index]; + fvar = 1; + len++; + goto exit; + } else { + fvar = 0; + len++; + goto exit; + } + } + } +#ifdef ESP32 + if (!strncmp(vname, "core", 4)) { + fvar = xPortGetCoreID(); + goto exit; + } +#ifdef USE_SCRIPT_TASK + if (!strncmp(vname, "ct(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + while (*lp==' ') lp++; + float fvar1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + while (*lp==' ') lp++; + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + float prio = STASK_PRIO; + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &prio, 0); + } + lp++; + fvar = scripter_create_task(fvar, fvar1, fvar2, prio); + len = 0; + goto exit; + } +#endif +#endif + break; + case 'd': + if (!strncmp(vname, "day", 3)) { + fvar = RtcTime.day_of_month; + goto exit; + } + break; + case 'e': + if (!strncmp(vname, "epoch", 5)) { + fvar = UtcTime() - (uint32_t)EPOCH_OFFSET; + goto exit; + } + if (!strncmp(vname, "eres", 4)) { + fvar = event_handeled; + tind->index = SCRIPT_EVENT_HANDLED; + goto exit_settable; + } +#ifdef USE_ENERGY_SENSOR + if (!strncmp(vname, "enrg[", 5)) { + lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + while (*lp==' ') lp++; + switch ((uint32_t)fvar) { + case 0: + fvar = Energy.total; + break; + case 1: + fvar = Energy.voltage[0]; + break; + case 2: + fvar = Energy.voltage[1]; + break; + case 3: + fvar = Energy.voltage[2]; + break; + case 4: + fvar = Energy.current[0]; + break; + case 5: + fvar = Energy.current[1]; + break; + case 6: + fvar = Energy.current[2]; + break; + case 7: + fvar = Energy.active_power[0]; + break; + case 8: + fvar = Energy.active_power[1]; + break; + case 9: + fvar = Energy.active_power[2]; + break; + + default: + fvar = 99999; + break; + } + len = 0; + lp++; + goto exit; + } +#endif + break; + case 'f': + +#ifdef USE_SCRIPT_FATFS + if (!strncmp(vname, "fo(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + while (*lp==' ') lp++; + uint8_t mode = 0; + if ((*lp=='r') || (*lp=='w') || (*lp=='a')) { + switch (*lp) { + case 'r': + mode = 0; + break; + case 'w': + mode = 1; + break; + case 'a': + mode = 2; + break; + } + lp++; + } else { + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + mode = fvar; + } + fvar = -1; + for (uint8_t cnt = 0;cntopen(str, FILE_READ); + if (glob_script_mem.files[cnt].isDirectory()) { + glob_script_mem.files[cnt].rewindDirectory(); + glob_script_mem.file_flags[cnt].is_dir = 1; + } else { + glob_script_mem.file_flags[cnt].is_dir = 0; + } + } + else { + if (mode==1) { + glob_script_mem.files[cnt] = fsp->open(str,FILE_WRITE); +#ifdef DEBUG_FS + AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for write %d"), cnt); +#endif + } else { + glob_script_mem.files[cnt] = fsp->open(str,FILE_APPEND); +#ifdef DEBUG_FS + AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for append %d"), cnt); +#endif + } + } + if (glob_script_mem.files[cnt]) { + fvar = cnt; + glob_script_mem.file_flags[cnt].is_open = 1; + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("file open failed")); + } + break; + } + } + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "fc(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + if (fvar>=0) { + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; +#ifdef DEBUG_FS + AddLog_P2(LOG_LEVEL_INFO, PSTR("closing file %d"), ind); +#endif + glob_script_mem.files[ind].close(); + glob_script_mem.file_flags[ind].is_open = 0; + } + fvar = 0; + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "ff(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; + glob_script_mem.files[ind].flush(); + fvar = 0; + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "fw(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = ForceStringVar(lp + 3, str); + while (*lp==' ') lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; + if (glob_script_mem.file_flags[ind].is_open) { + fvar = glob_script_mem.files[ind].print(str); + } else { + fvar = 0; + } + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "fr(", 3)) { + struct T_INDEX ind; + uint8_t vtype; + uint8_t sindex = 0; + lp = isvar(lp + 3, &vtype, &ind, 0, 0, 0); + if (vtype!=VAR_NV) { + + if ((vtype&STYPE)==0) { + + fvar = 0; + goto exit; + } else { + + sindex = glob_script_mem.type[ind.index].index; + } + } else { + + fvar = 0; + goto exit; + } + while (*lp==' ') lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t find = fvar; + if (find>=SFS_MAX) find = SFS_MAX - 1; + uint8_t index = 0; + char str[glob_script_mem.max_ssize + 1]; + char *cp = str; + if (glob_script_mem.file_flags[find].is_open) { + if (glob_script_mem.file_flags[find].is_dir) { + while (true) { + File entry = glob_script_mem.files[find].openNextFile(); + if (entry) { + if (!reject((char*)entry.name())) { + char *ep = (char*)entry.name(); + if (*ep=='/') ep++; + char *lcp = strrchr(ep,'/'); + if (lcp) { + ep = lcp + 1; + } + strcpy(str, ep); + entry.close(); + break; + } + } else { + *cp = 0; + break; + } + entry.close(); + } + index = strlen(str); + } else { + while (glob_script_mem.files[find].available()) { + uint8_t buf[1]; + glob_script_mem.files[find].read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++ = buf[0]; + index++; + if (index>=glob_script_mem.max_ssize - 1) break; + } + } + *cp = 0; + } + } else { + strcpy(str, "file error"); + } + lp++; + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + fvar = index; + len = 0; + goto exit; + } + if (!strncmp(vname, "fd(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + fsp->remove(str); + lp++; + len = 0; + goto exit; + } +#if defined(ESP32) && defined(USE_WEBCAM) + if (!strncmp(vname, "fwp(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + while (*lp==' ') lp++; + float fvar1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + uint8_t ind = fvar1; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; + if (glob_script_mem.file_flags[ind].is_open) { + uint8_t *buff; + float maxps = WcGetPicstore(-1, 0); + if (fvar<1 || fvar>maxps) fvar = 1; + uint32_t len = WcGetPicstore(fvar - 1, &buff); + if (len) { + + fvar = glob_script_mem.files[ind].write(buff, len); + } else { + fvar = 0; + } + + } else { + fvar = 0; + } + lp++; + len = 0; + goto exit; + } +#endif +#ifdef USE_SCRIPT_FATFS_EXT + if (!strncmp(vname, "fe(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + + File ef = fsp->open(str, FILE_READ); + if (ef) { + uint16_t fsiz = ef.size(); + if (fsiz<2048) { + char *script = (char*)calloc(fsiz + 16, 1); + if (script) { + ef.read((uint8_t*)script,fsiz); + execute_script(script); + free(script); + fvar = 1; + } + } + ef.close(); + } + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "fmd(", 4)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = fsp->mkdir(str); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "frd(", 4)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = fsp->rmdir(str); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "fx(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + if (fsp->exists(str)) fvar = 1; + else fvar = 0; + lp++; + len = 0; + goto exit; + } + + if (!strncmp(vname, "fsi(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = get_fsinfo(fvar); + lp++; + len = 0; + goto exit; + } + + if (!strncmp(vname, "fwa(", 4)) { + struct T_INDEX ind; + uint8_t vtype; + lp = isvar(lp + 4, &vtype, &ind, 0, 0, 0); + if (vtype!=VAR_NV && (vtype&STYPE)==0 && glob_script_mem.type[ind.index].bits.is_filter) { + + + } else { + + fvar = 0; + goto exit; + } + + while (*lp==' ') lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index>=SFS_MAX) index = SFS_MAX - 1; + if (glob_script_mem.file_flags[index].is_open) { + uint16_t len = 0; + float *fa = Get_MFAddr(glob_script_mem.type[ind.index].index, &len, 0); + char dstr[24]; + for (uint32_t cnt = 0; cnt=SFS_MAX) find = SFS_MAX - 1; + char str[glob_script_mem.max_ssize + 1]; + if (glob_script_mem.file_flags[find].is_open) { + uint16_t len = 0; + float *fa = Get_MFAddr(glob_script_mem.type[ind.index].index, &len, 0); + char dstr[24]; + for (uint32_t cnt = 0; cnt=glob_script_mem.max_ssize - 1) break; + } + } + *cp = 0; + *fa++=CharToFloat(str); + } + } else { + fvar = 0; + } + lp++; + len = 0; + goto exit; + } + +#endif + if (!strncmp(vname, "fl1(", 4) || !strncmp(vname, "fl2(", 4) ) { + uint8_t lknum = *(lp+2)&3; + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + if (lknum<1 || lknum>2) lknum = 1; + strlcpy(glob_script_mem.flink[lknum - 1], str, 14); + lp++; + fvar = 0; + len = 0; + goto exit; + } + if (!strncmp(vname, "fsm", 3)) { + fvar=glob_script_mem.script_sd_found; + + goto exit; + } +#endif + if (!strncmp(vname, "freq", 4)) { +#ifdef ESP32 + fvar = getCpuFrequencyMhz(); +#else + fvar = ESP.getCpuFreqMHz(); +#endif + goto exit; + } + break; + case 'g': + if (!strncmp(vname, "gtmp", 4)) { + fvar = global_temperature_celsius; + goto exit; + } + if (!strncmp(vname, "ghum", 4)) { + fvar = global_humidity; + goto exit; + } + if (!strncmp(vname, "gprs", 4)) { + fvar = global_pressure_hpa; + goto exit; + } + if (!strncmp(vname, "gtopic", 6)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTT_GRP_TOPIC), glob_script_mem.max_ssize); + goto strexit; + } + +#ifdef SCRIPT_GET_HTTPS_JP + if (!strncmp(vname, "gjp(", 4)) { + char host[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 4, OPER_EQU, host, 0); + SCRIPT_SKIP_SPACES + char path[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, path, 0); + fvar = call2https(host, path); + lp++; + len = 0; + goto exit; + } +#endif + break; + case 'h': + if (!strncmp(vname, "hours", 5)) { + fvar = RtcTime.hour; + goto exit; + } + if (!strncmp(vname, "heap", 4)) { + fvar = ESP_getFreeHeap(); + goto exit; + } + if (!strncmp(vname, "hn(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + if (fvar<0 || fvar>255) fvar = 0; + lp++; + len = 0; + if (sp) { + sprintf(sp, "%02x", (uint8_t)fvar); + } + goto strexit; + } + if (!strncmp(vname, "hx(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + lp++; + len = 0; + if (sp) { + sprintf(sp, "%08x", (uint32_t)fvar); + } + goto strexit; + } + if (!strncmp(vname, "hd(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + fvar = strtol(str, NULL, 16); + lp++; + len = 0; + goto exit; + } +#ifdef USE_LIGHT + if (!strncmp(vname, "hsvrgb(", 7)) { + lp = GetNumericArgument(lp + 7, OPER_EQU, &fvar, 0); + if (fvar<0 || fvar>360) fvar = 0; + SCRIPT_SKIP_SPACES + + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + if (fvar2<0 || fvar2>100) fvar2 = 0; + SCRIPT_SKIP_SPACES + + float fvar3; + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + if (fvar3<0 || fvar3>100) fvar3 = 0; + + fvar = HSVToRGB(fvar, fvar2, fvar3); + lp++; + len = 0; + goto exit; + } +#endif + break; + case 'i': + if (!strncmp(vname, "int(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = floor(fvar); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "is(", 3)) { + lp = isargs(lp + 3, 0); + fvar = 0; + len = 0; + goto exit; + } + if (!strncmp(vname, "is1(", 4)) { + lp = isargs(lp + 4, 1); + fvar = 0; + len = 0; + goto exit; + } + if (!strncmp(vname, "is2(", 4)) { + lp = isargs(lp + 4, 2); + fvar = 0; + len = 0; + goto exit; + } + if (!strncmp(vname, "is[", 3)) { + lp = isget(lp + 3, sp, 0); + len = 0; + goto strexit; + } + if (!strncmp(vname, "is1[", 4)) { + lp = isget(lp + 4, sp, 1); + len = 0; + goto strexit; + } + if (!strncmp(vname, "is2[", 4)) { + lp = isget(lp + 4, sp, 2); + len = 0; + goto strexit; + } + break; + case 'l': + if (!strncmp(vname, "lip", 3)) { + if (sp) strlcpy(sp, (const char*)WiFi.localIP().toString().c_str(), glob_script_mem.max_ssize); + goto strexit; + } +#ifdef USE_SCRIPT_GLOBVARS + if (!strncmp(vname, "luip", 4)) { + if (sp) strlcpy(sp, IPAddressToString(last_udp_ip), glob_script_mem.max_ssize); + goto strexit; + } +#endif + if (!strncmp(vname, "loglvl", 6)) { + fvar = glob_script_mem.script_loglevel; + tind->index = SCRIPT_LOGLEVEL; + exit_settable: + if (fp) *fp = fvar; + *vtype = NTYPE; + tind->bits.settable = 1; + tind->bits.is_string = 0; + return lp + len; + } + break; + case 'm': + if (!strncmp(vname, "med(", 4)) { + float fvar1; + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = DoMedian5(fvar1, fvar2); + lp++; + len = 0; + goto exit; + } +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname, "mpt(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = MeasurePulseTime(fvar); + lp++; + len = 0; + goto exit; + } +#endif + if (!strncmp(vname, "micros", 6)) { + fvar = micros(); + goto exit; + } + if (!strncmp(vname, "millis", 6)) { + fvar = millis(); + goto exit; + } + if (!strncmp(vname, "mins", 4)) { + fvar = RtcTime.minute; + goto exit; + } + if (!strncmp(vname, "month", 5)) { + fvar = RtcTime.month; + goto exit; + } + if (!strncmp(vname, "mqttc", 5)) { + if (rules_flag.mqtt_connected) { + rules_flag.mqtt_connected = 0; + fvar = 1; + } + goto exit; + } + if (!strncmp(vname, "mqttd", 5)) { + if (rules_flag.mqtt_disconnected) { + rules_flag.mqtt_disconnected = 0; + fvar = 1; + } + goto exit; + } + if (!strncmp(vname, "mqtts", 5)) { + fvar = !global_state.mqtt_down; + goto exit; + } + if (!strncmp(vname, "mp(", 3)) { + float fvar1; + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + while (*lp!=')') { + char *opp = lp; + lp++; + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + SCRIPT_SKIP_SPACES + fvar = fvar1; + if ((*opp=='<' && fvar1' && fvar1>fvar2) || + (*opp=='=' && fvar1==fvar2)) { + if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { + float fvar3; + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + SCRIPT_SKIP_SPACES + fvar=fvar3; + } else { + fvar = fvar2; + } + break; + } + while (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) lp++; + } + len = 0; + goto exit; + } +#ifdef USE_MORITZ + if (!strncmp(vname, "mo(", 3)) { + float fvar1; + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + SCRIPT_SKIP_SPACES + char rbuff[64]; + fvar = mo_getvars(fvar1, fvar2, rbuff); + lp++; + if (sp) strlcpy(sp, rbuff, glob_script_mem.max_ssize); + len = 0; + goto strexit; + } +#endif + break; + case 'p': + if (!strncmp(vname, "pin[", 4)) { + + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + fvar = digitalRead((uint8_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname, "pn[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + fvar = Pin(fvar); + + len++; + goto exit; + } +#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH)) + if (!strncmp(vname, "pl(", 3)) { + char path[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, path, 0); + Play_mp3(path); + len++; + len = 0; + goto exit; + } +#endif + if (!strncmp(vname, "pd[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + uint8_t gpiopin = fvar; +# 2640 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + if ((gpiopin < ARRAY_SIZE(gpio_pin)) && (gpio_pin[gpiopin] > 0)) { + fvar = gpio_pin[gpiopin]; + + len++; + goto exit; + } + fvar = 999; + goto exit; + } +#ifdef ESP32 + if (!strncmp(vname, "pheap", 5)) { + fvar = ESP.getFreePsram(); + goto exit; + } +#endif + if (!strncmp(vname, "prefix1", 7)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTTPREFIX1), glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname, "prefix2", 7)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTTPREFIX2), glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname, "prefix3", 7)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTTPREFIX3), glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname, "pow(", 4)) { + + float fvar1; + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + lp++; + fvar = FastPrecisePowf(fvar1, fvar2); + len = 0; + goto exit; + } + if (!strncmp(vname, "pwr[", 4)) { + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<=devices_present) { + fvar = bitRead(power, index - 1); + } else { + fvar = -1; + } + len += 1; + goto exit; + } + if (!strncmp(vname, "pc[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<1 || index>MAX_COUNTERS) index = 1; + fvar = RtcSettings.pulse_counter[index - 1]; + len += 1; + goto exit; + } + break; + + case 'r': + if (!strncmp(vname, "ram", 3)) { + fvar = glob_script_mem.script_mem_size + (glob_script_mem.script_size) + (PMEM_SIZE); + goto exit; + } + if (!strncmp(vname, "rnd(", 4)) { + + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + if (fvar<0) { + randomSeed(-fvar); + fvar = 0; + } else { + fvar = random(fvar); + } + + len++; + goto exit; + } + break; + case 's': + if (!strncmp(vname, "secs", 4)) { + fvar = RtcTime.second; + goto exit; + } + if (!strncmp(vname, "sw[", 3)) { + + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + fvar = SwitchLastState((uint32_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname, "stack", 5)) { + fvar = GetStack(); + goto exit; + } + if (!strncmp(vname, "slen", 4)) { + fvar = strlen(glob_script_mem.script_ram); + goto exit; + } + if (!strncmp(vname, "sl(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + lp++; + len = 0; + fvar = strlen(str); + goto exit; + } + if (!strncmp(vname, "sb(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + SCRIPT_SKIP_SPACES + float fvar1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + lp++; + len = 0; + if (fvar1<0) { + fvar1 = strlen(str) + fvar1; + } + memcpy(sp, &str[(uint8_t)fvar1], (uint8_t)fvar2); + sp[(uint8_t)fvar2] = '\0'; + goto strexit; + } + if (!strncmp(vname, "st(", 3)) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + while (*lp==' ') lp++; + char token[2]; + token[0] = *lp++; + token[1] = 0; + while (*lp==' ') lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + + lp++; + len = 0; + if (sp) { + + char *st = strtok(str, token); + if (!st) { + *sp = 0; + } else { + for (uint8_t cnt = 1; cnt<=fvar; cnt++) { + if (cnt==fvar) { + strcpy(sp, st); + break; + } + st = strtok(NULL, token); + if (!st) { + *sp = 0; + break; + } + } + } + } + goto strexit; + } + if (!strncmp(vname, "s(", 2)) { + lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, 0); + char str[glob_script_mem.max_ssize + 1]; + f2char(fvar, glob_script_mem.script_dprec, glob_script_mem.script_lzero, str); + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + lp++; + len = 0; + goto strexit; + } +#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH)) + if (!strncmp(vname, "say(", 4)) { + char text[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 4, OPER_EQU, text, 0); + Say(text); + len++; + len = 0; + goto exit; + } +#endif + +#ifdef ESP32 + if (!strncmp(vname, "sf(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + if (fvar<80) fvar = 80; + if (fvar>240) fvar = 240; + setCpuFrequencyMhz(fvar); + fvar = getCpuFrequencyMhz(); + lp++; + len = 0; + goto exit; + } +#endif +#ifdef USE_TTGO_WATCH + if (!strncmp(vname, "slp(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + SCRIPT_SKIP_SPACES + TTGO_Sleep(fvar); + lp++; + len = 0; + goto exit; + } +#endif +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + if (!strncmp(vname, "sunrise", 7)) { + fvar = SunMinutes(0); + goto exit; + } + if (!strncmp(vname, "sunset", 6)) { + fvar = SunMinutes(1); + goto exit; + } +#endif + +#ifdef USE_SHUTTER + if (!strncmp(vname, "sht[", 4)) { + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<=shutters_present) { + fvar = Settings.shutter_position[index - 1]; + } else { + fvar = -1; + } + len += 1; + goto exit; + } +#endif +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname, "sin(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = sinf(fvar); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "sqrt(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = sqrtf(fvar); + lp++; + len = 0; + goto exit; + } +#endif + +#if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD) + if (!strncmp(vname, "sml[", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + SCRIPT_SKIP_SPACES + fvar = SML_GetVal(fvar); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "sml(", 4)) { + float fvar1; + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + SCRIPT_SKIP_SPACES + if (fvar2==0) { + float fvar3; + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + fvar = SML_SetBaud(fvar1, fvar3); + } else if (fvar2==1) { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, str, 0); + fvar = SML_Write(fvar1, str); + } else if (fvar2==2) { + char str[SCRIPT_MAXSSIZE]; + str[0] = 0; + fvar = SML_Read(fvar1, str, SCRIPT_MAXSSIZE); + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + lp++; + len = 0; + goto strexit; + + } else { +#ifdef ED300L + fvar = SML_Status(fvar1); +#else + fvar = 0; +#endif + } + lp++; + len = 0; + goto exit; + } +#endif + break; + case 't': + if (!strncmp(vname, "time", 4)) { + fvar = MinutesPastMidnight(); + goto exit; + } + if (!strncmp(vname, "tper", 4)) { + fvar = Settings.tele_period; + tind->index = SCRIPT_TELEPERIOD; + goto exit_settable; + } + if (!strncmp(vname, "tinit", 5)) { + fvar = rules_flag.time_init; + goto exit; + } + if (!strncmp(vname, "tset", 4)) { + fvar = rules_flag.time_set; + goto exit; + } + if (!strncmp(vname, "tstamp", 6)) { + if (sp) strlcpy(sp, GetDateAndTime(DT_LOCAL).c_str(), glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname, "topic", 5)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTT_TOPIC), glob_script_mem.max_ssize); + goto strexit; + } +#ifdef USE_SCRIPT_TIMER + if (!strncmp(vname, "ts1(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; + Script_ticker1.attach_ms(fvar, Script_ticker1_end); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "ts2(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; + Script_ticker2.attach_ms(fvar, Script_ticker2_end); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "ts3(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; + Script_ticker3.attach_ms(fvar, Script_ticker3_end); + lp++; + len = 0; + goto exit; + } + if (!strncmp(vname, "ts4(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; + Script_ticker4.attach_ms(fvar, Script_ticker4_end); + lp++; + len = 0; + goto exit; + } +#endif + +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS + if (!strncmp(vname, "tbut[", 5)) { + GetNumericArgument(vname + 5, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<1 || index>MAXBUTTONS) index = 1; + index--; + if (buttons[index]) { + fvar = buttons[index]->vpower.on_off; + } else { + fvar = -1; + } + len += 1; + goto exit; + } + +#endif +#endif + break; + case 'u': + if (!strncmp(vname, "uptime", 6)) { + fvar = MinutesUptime(); + goto exit; + } + if (!strncmp(vname, "upsecs", 6)) { + fvar = uptime; + goto exit; + } + if (!strncmp(vname, "upd[", 4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname + 4, &vtype, &ind, 0, 0, 0); + if (!ind.bits.constant) { + if (!ind.bits.changed) { + fvar = 0; + len++; + goto exit; + } else { + glob_script_mem.type[ind.index].bits.changed = 0; + fvar = 1; + len++; + goto exit; + } + } + goto notfound; + } + break; + + case 'w': +#if defined(ESP32) && defined(USE_WEBCAM) + if (!strncmp(vname, "wc(", 3)) { + float fvar1; + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); + SCRIPT_SKIP_SPACES + switch ((uint32)fvar1) { + case 0: + { float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetup(fvar2); + } + break; + case 1: + { float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcGetFrame(fvar2); + } + break; + case 2: + { float fvar2,fvar3; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + fvar = WcSetOptions(fvar2, fvar3); + } + break; + case 3: + fvar = WcGetWidth(); + break; + case 4: + fvar = WcGetHeight(); + break; + case 5: + { float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetStreamserver(fvar2); + } + break; + case 6: + { float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetMotionDetect(fvar2); + } + break; +#ifdef USE_FACE_DETECT + case 7: + { float fvar2; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetFaceDetect(fvar2); + } + break; +#endif + default: + fvar = 0; + } + lp++; + len = 0; + goto exit; + } +#endif +#if defined(USE_TTGO_WATCH) && defined(USE_BMA423) + if (!strncmp(vname, "wdclk", 5)) { + fvar = TTGO_doubleclick(); + goto exit; + } + if (!strncmp(vname, "wbut", 4)) { + fvar = TTGO_button(); + goto exit; + } +#endif +#if defined(USE_TTGO_WATCH) && defined(USE_FT5206) + if (!strncmp(vname, "wtch(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = Touch_Status(fvar); + lp++; + len = 0; + goto exit; + } +#endif + if (!strncmp(vname, "wm", 2)) { + fvar = glob_script_mem.web_mode; + goto exit; + } + if (!strncmp(vname, "wday", 4)) { + fvar = RtcTime.day_of_week; + goto exit; + } + if (!strncmp(vname, "wific", 5)) { + if (rules_flag.wifi_connected) { + rules_flag.wifi_connected = 0; + fvar = 1; + } + goto exit; + } + if (!strncmp(vname, "wifid", 5)) { + if (rules_flag.wifi_disconnected) { + rules_flag.wifi_disconnected = 0; + fvar = 1; + } + goto exit; + } + if (!strncmp(vname, "wifis", 5)) { + fvar = !global_state.wifi_down; + goto exit; + } + break; + case 'y': + if (!strncmp(vname, "year", 4)) { + fvar = RtcTime.year; + goto exit; + } + break; + default: + break; + } + +notfound: + if (fp) *fp=0; + *vtype = VAR_NV; + tind->index = VAR_NV; + glob_script_mem.var_not_found = 1; + return lp; + +exit: + if (fp) *fp = fvar; + *vtype = NUM_RES; + tind->bits.constant = 1; + tind->bits.is_string = 0; + return lp + len; + +strexit: + *vtype = STYPE; + tind->bits.constant = 1; + tind->bits.is_string = 1; + return lp + len; +} + + + +char *getop(char *lp, uint8_t *operand) { + switch (*lp) { + case '=': + if (*(lp + 1)=='=') { + *operand = OPER_EQUEQU; + return lp + 2; + } else { + *operand = OPER_EQU; + return lp + 1; + } + break; + case '+': + if (*(lp + 1)=='=') { + *operand = OPER_PLSEQU; + return lp + 2; + } else { + *operand = OPER_PLS; + return lp + 1; + } + break; + case '-': + if (*(lp + 1)=='=') { + *operand = OPER_MINEQU; + return lp + 2; + } else { + *operand = OPER_MIN; + return lp + 1; + } + break; + case '*': + if (*(lp + 1)=='=') { + *operand = OPER_MULEQU; + return lp + 2; + } else { + *operand = OPER_MUL; + return lp + 1; + } + break; + case '/': + if (*(lp + 1)=='=') { + *operand = OPER_DIVEQU; + return lp + 2; + } else { + *operand = OPER_DIV; + return lp + 1; + } + break; + case '!': + if (*(lp + 1)=='=') { + *operand = OPER_NOTEQU; + return lp + 2; + } + break; + case '>': + if (*(lp + 1)=='=') { + *operand = OPER_GRTEQU; + return lp + 2; + } else { + *operand = OPER_GRT; + return lp + 1; + + } + break; + case '<': + if (*(lp + 1)=='=') { + *operand = OPER_LOWEQU; + return lp + 2; + } else { + *operand = OPER_LOW; + return lp + 1; + } + break; + case '%': + if (*(lp + 1)=='=') { + *operand = OPER_PERCEQU; + return lp + 2; + } else { + *operand = OPER_PERC; + return lp + 1; + } + break; + case '^': + if (*(lp + 1)=='=') { + *operand = OPER_XOREQU; + return lp + 2; + } else { + *operand = OPER_XOR; + return lp + 1; + } + break; + case '&': + if (*(lp + 1)=='=') { + *operand = OPER_ANDEQU; + return lp + 2; + } else { + *operand = OPER_AND; + return lp + 1; + } + break; + case '|': + if (*(lp + 1)=='=') { + *operand = OPER_OREQU; + return lp + 2; + } else { + *operand = OPER_OR; + return lp + 1; + } + break; + } + *operand = 0; + return lp; +} + + +#ifdef ESP8266 +extern "C" { +#include + extern cont_t* g_pcont; +} +uint16_t GetStack(void) { + register uint32_t *sp asm("a1"); + return (4 * (sp - g_pcont->stack)); +} +#else +uint16_t GetStack(void) { + register uint8_t *sp asm("a1"); + return (sp - pxTaskGetStackStart(NULL)); +} +#endif + +char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonParserObject *jo) { + uint8_t operand = 0; + uint8_t vtype; + char *slp; + struct T_INDEX ind; + char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; + while (1) { + lp=isvar(lp, &vtype, &ind, 0, str1, jo); + if (vtype!=STR_RES && !(vtype & STYPE)) { + + glob_script_mem.glob_error = 1; + return lp; + } + switch (lastop) { + case OPER_EQU: + strlcpy(str, str1, sizeof(str)); + break; + case OPER_PLS: + strncat(str, str1, sizeof(str) - strlen(str1)); + break; + } + slp = lp; + lp = getop(lp, &operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp = slp; + strcpy(cp, str); + return lp; + break; + default: + break; + } + lastop = operand; + if (!operand) { + strcpy(cp, str); + return lp; + } + } + return lp; +} + +char *GetNumericArgument(char *lp, uint8_t lastop, float *fp, JsonParserObject *jo) { +uint8_t operand = 0; +float fvar1,fvar; +char *slp; +uint8_t vtype; +struct T_INDEX ind; + while (1) { + + if (*lp=='(') { + lp++; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo); + lp++; + + } else { + lp = isvar(lp, &vtype, &ind, &fvar1, 0, jo); + if ((vtype!=NUM_RES) && (vtype&STYPE)) { + + glob_script_mem.glob_error = 1; + } + } + switch (lastop) { + case OPER_EQU: + fvar = fvar1; + break; + case OPER_PLS: + fvar += fvar1; + break; + case OPER_MIN: + fvar -= fvar1; + break; + case OPER_MUL: + fvar *= fvar1; + break; + case OPER_DIV: + fvar /= fvar1; + break; + case OPER_PERC: + fvar = fmodf(fvar, fvar1); + break; + case OPER_XOR: + fvar = (uint32_t)fvar ^ (uint32_t)fvar1; + break; + case OPER_AND: + fvar = (uint32_t)fvar & (uint32_t)fvar1; + break; + case OPER_OR: + fvar = (uint32_t)fvar | (uint32_t)fvar1; + break; + default: + break; + + } + slp = lp; + lp = getop(lp, &operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp = slp; + *fp = fvar; + return lp; + break; + default: + break; + } + lastop = operand; + if (!operand) { + *fp = fvar; + return lp; + } + } +} + + +char *ForceStringVar(char *lp, char *dstr) { + float fvar; + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetStringArgument(lp, OPER_EQU, dstr, 0); + if (glob_script_mem.glob_error) { + + lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0); + dtostrfd(fvar, 6, dstr); + glob_script_mem.glob_error = 0; + } + return lp; +} + + +void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) { + char *cp; + uint16_t count; + uint8_t vtype; + uint8_t dprec = glob_script_mem.script_dprec; + uint8_t lzero = glob_script_mem.script_lzero; + float fvar; + cp = srcbuf; + struct T_INDEX ind; + char string[SCRIPT_MAXSSIZE]; + dstsize -= 2; + for (count = 0; count=sizeof(str)) len = sizeof(str); + strlcpy(str, cp, len); + toSLog(str); +} + +void toLogEOL(const char *s1,const char *str) { + if (!str) return; + uint8_t index = 0; + char *cp = log_data; + strcpy(cp, s1); + cp += strlen(s1); + while (*str) { + if (*str==SCRIPT_EOL) break; + *cp++ = *str++; + } + *cp = 0; + AddLog(LOG_LEVEL_INFO); +} + + +void toSLog(const char *str) { + if (!str) return; +#if SCRIPT_DEBUG>0 + while (*str) { + Serial.write(*str); + str++; + } +#endif +} + +char *Evaluate_expression(char *lp, uint8_t and_or, uint8_t *result, JsonParserObject *jo) { + float fvar,*dfvar,fvar1; + uint8_t numeric; + struct T_INDEX ind; + uint8_t vtype = 0,lastop; + uint8_t res = 0; + char *llp = lp; + char *slp; + + SCRIPT_SKIP_SPACES + if (*lp=='(') { + uint8_t res = 0; + uint8_t xand_or = 0; + lp++; + +loop: + SCRIPT_SKIP_SPACES + lp = Evaluate_expression(lp, xand_or, &res, jo); + if (*lp==')') { + lp++; + goto exit0; + } + + SCRIPT_SKIP_SPACES + if (!strncmp(lp, "or", 2)) { + lp += 2; + xand_or = 1; + goto loop; + } else if (!strncmp(lp, "and", 3)) { + lp += 3; + xand_or = 2; + goto loop; + } +exit0: + if (!and_or) { + *result = res; + } else if (and_or==1) { + *result|=res; + } else { + *result &= res; + } + goto exit10; + } + + llp = lp; + + dfvar = &fvar; + glob_script_mem.glob_error = 0; + slp = lp; + numeric = 1; + lp = GetNumericArgument(lp, OPER_EQU, dfvar, 0); + if (glob_script_mem.glob_error==1) { + + char cmpstr[SCRIPT_MAXSSIZE]; + lp = slp; + numeric = 0; + + lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0); + lp = getop(lp, &lastop); + + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, str, jo); + if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { + res = strcmp(cmpstr, str); + if (lastop==OPER_EQUEQU) res=!res; + goto exit; + } + + } else { + + + lp = getop(lp, &lastop); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo); + switch (lastop) { + case OPER_EQUEQU: + res = (*dfvar==fvar1); + break; + case OPER_NOTEQU: + res = (*dfvar!=fvar1); + break; + case OPER_LOW: + res = (*dfvarfvar1); + break; + case OPER_GRTEQU: + res = (*dfvar>=fvar1); + break; + default: + + break; + } + +exit: + if (!and_or) { + *result = res; + } else if (and_or==1) { + *result |= res; + } else { + *result &= res; + } + } + + +exit10: +#if IFTHEN_DEBUG>0 + char tbuff[128]; + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ", (int32_t)*dfvar, (int32_t)fvar1, *result, and_or); + toLogEOL(tbuff, llp); +#endif + return lp; +} + +#ifdef ESP32 + +TimerHandle_t beep_th; +void StopBeep( TimerHandle_t xTimer ); + +void StopBeep( TimerHandle_t xTimer ) { + ledcWriteTone(7, 0); + xTimerStop(xTimer, 0); +} + +void esp32_beep(int32_t freq ,uint32_t len) { + if (freq<0) { + ledcSetup(7, 500, 10); + ledcAttachPin(-freq, 7); + ledcWriteTone(7, 0); + if (!beep_th) { + beep_th = xTimerCreate("beep", 100, pdFALSE, ( void * ) 0, StopBeep); + } + } else { + if (!beep_th) return; + if (!freq) { + ledcWriteTone(7, 0); + xTimerStop(beep_th, 10); + return; + } + if (len < 10) return; + if (xTimerIsTimerActive(beep_th)) return; + ledcWriteTone(7, freq); + uint32_t ticks = pdMS_TO_TICKS(len); + xTimerChangePeriod( beep_th, ticks, 10); + } +} +#endif + + + +char *scripter_sub(char *lp, uint8_t fromscriptcmd) { + lp += 1; + char *slp = lp; + uint8_t plen = 0; + while (*lp) { + if (*lp=='\n'|| *lp=='\r'|| *lp=='(') { + break; + } + lp++; + plen++; + } + if (fromscriptcmd) { + char *sp = glob_script_mem.scriptptr; + glob_script_mem.scriptptr = glob_script_mem.scriptptr_bu; + Run_Scripter(slp, plen, 0); + glob_script_mem.scriptptr = sp; + } else { + Run_Scripter(slp, plen, 0); + } + lp = slp; + return lp; +} + +#define IF_NEST 8 + +int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { +int16_t retval; + + if (!glob_script_mem.scriptptr) { + return -99; + } + + if (tasm_cmd_activ && tlen>0) return 0; + + JsonParserObject jo; + if (js) { + + + JsonParser parser(js); + jo = parser.getRootObject(); + retval = Run_script_sub(type, tlen, &jo); + } else { + retval = Run_script_sub(type, tlen, 0); + } + return retval; +} + +int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) { + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; + char *lp_next; + int16_t globaindex,saindex; + struct T_INDEX ind; + uint8_t operand,lastop,numeric = 1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck = 0; + if_state[ifstck] = 0; + if_result[ifstck] = 0; + if_exe[ifstck] = 1; + char cmpstr[SCRIPT_MAXSSIZE]; + uint8_t check = 0; + if (tlen<0) { + tlen = abs(tlen); + check = 1; + } + + float *dfvar,*cv_count,cv_max,cv_inc; + char *cv_ptr; + float fvar = 0,fvar1,sysvar,swvar; + uint8_t section = 0,sysv_type = 0,swflg = 0; + + char *lp = glob_script_mem.scriptptr; + + while (1) { + + + startline: + SCRIPT_SKIP_SPACES + + SCRIPT_SKIP_EOL + + if (*lp==';') goto next_line; + if (!*lp) break; + + if (section) { + + if (*lp=='>') { + return 0; + } + if (*lp=='#') { + return 0; + } + glob_script_mem.var_not_found = 0; + + +#ifdef IFTHEN_DEBUG + char tbuff[128]; + sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]); + toLogEOL(tbuff, lp); +#endif + + + + + if (!strncmp(lp, "if", 2)) { + lp += 2; + if (ifstck=2) { + lp += 5; + if (ifstck>0) { + if_state[ifstck] = 0; + ifstck--; + } + goto next_line; + } else if (!strncmp(lp, "or", 2) && if_state[ifstck]==1) { + lp += 2; + and_or = 1; + } else if (!strncmp(lp, "and", 3) && if_state[ifstck]==1) { + lp += 3; + and_or = 2; + } + + if (*lp=='{' && if_state[ifstck]==1) { + lp += 1; + if_state[ifstck] = 2; + if (if_exe[ifstck - 1]) if_exe[ifstck]=if_result[ifstck]; + } else if (*lp=='{' && if_state[ifstck]==3) { + lp += 1; + + } else if (*lp=='}' && if_state[ifstck]>=2) { + lp++; + char *slp = lp; + uint8_t iselse = 0; + for (uint8_t count = 0; count<8;count++) { + if (*lp=='}') { + + break; + } + if (!strncmp(lp, "else", 4)) { + + if_state[ifstck] = 3; + if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; + lp += 4; + iselse = 1; + SCRIPT_SKIP_SPACES + if (*lp=='{') lp++; + break; + } + lp++; + } + if (!iselse) { + lp = slp; + + if (ifstck>0) { + if_state[ifstck] = 0; + ifstck--; + } + goto next_line; + } + } + + if (!strncmp(lp, "for", 3)) { + + + lp += 3; + SCRIPT_SKIP_SPACES + lp_next = 0; + lp = isvar(lp, &vtype, &ind, 0, 0, 0); + if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { + + uint8_t index = glob_script_mem.type[ind.index].index; + cv_count = &glob_script_mem.fvars[index]; + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp, OPER_EQU, cv_count, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp, OPER_EQU, &cv_max, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp, OPER_EQU, &cv_inc, 0); + + cv_ptr = lp; + if (*cv_count<=cv_max && cv_inc>0) { + + floop = 1; + } else { + + floop = 2; + if (cv_inc>0) { + floop = 1; + } + } + } else { + + toLogEOL("for error", lp); + } + } else if (!strncmp(lp, "next", 4)) { + lp_next = lp; + if (floop>0) { + + *cv_count += cv_inc; + if (floop==1) { + if (*cv_count<=cv_max) { + lp = cv_ptr; + } else { + lp += 4; + floop = 0; + } + } else { + if (*cv_count>=cv_max) { + lp = cv_ptr; + } else { + lp += 4; + floop = 0; + } + } + } + } + + if (!strncmp(lp, "switch", 6)) { + lp += 6; + SCRIPT_SKIP_SPACES + char *slp = lp; + lp = GetNumericArgument(lp, OPER_EQU, &swvar, 0); + if (glob_script_mem.glob_error==1) { + + lp = slp; + + lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0); + swflg = 0x81; + } else { + swflg = 1; + } + } else if (!strncmp(lp, "case", 4) && swflg>0) { + lp += 4; + SCRIPT_SKIP_SPACES + float cvar; + if (!(swflg & 0x80)) { + lp = GetNumericArgument(lp, OPER_EQU, &cvar, 0); + if (swvar!=cvar) { + swflg = 2; + } else { + swflg = 1; + } + } else { + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, str, 0); + if (!strcmp(cmpstr, str)) { + swflg = 0x81; + } else { + swflg = 0x82; + } + } + } else if (!strncmp(lp, "ends", 4) && swflg>0) { + lp += 4; + swflg = 0; + } + if ((swflg & 3)==2) goto next_line; + + SCRIPT_SKIP_SPACES + + if (*lp==SCRIPT_EOL) { + goto next_line; + } + + + if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; + +#ifdef IFTHEN_DEBUG + sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d execute line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]); + toLogEOL(tbuff, lp); +#endif + + if (!strncmp(lp, "break", 5)) { + lp += 5; + if (floop) { + + if (lp_next) { + lp = lp_next; + } + floop = 0; + } else { + section = 0; + } + goto next_line; + } else if (!strncmp(lp, "dp", 2) && isdigit(*(lp + 2))) { + lp += 2; + + if (*(lp + 1)== '.') { + glob_script_mem.script_lzero = atoi(lp); + lp+=2; + } + glob_script_mem.script_dprec = atoi(lp); + goto next_line; + } +#ifdef USE_DISPLAY + else if (!strncmp(lp, "dt", 2)) { + char dstbuf[256]; + lp += 2; + SCRIPT_SKIP_SPACES + Replace_Cmd_Vars(lp, 1, dstbuf, sizeof(dstbuf)); + char *savptr = XdrvMailbox.data; + XdrvMailbox.data = dstbuf; + XdrvMailbox.data_len = 0; + DisplayText(); + XdrvMailbox.data = savptr; + goto next_line; + } +#endif + else if (!strncmp(lp, "delay(", 6)) { + + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + delay(fvar); + goto next_line; + } else if (!strncmp(lp, "spinm(", 6)) { + + lp = GetNumericArgument(lp + 6, OPER_EQU, &fvar, 0); + int8_t pinnr = fvar; + SCRIPT_SKIP_SPACES + uint8_t mode = 0; + if ((*lp=='I') || (*lp=='O') || (*lp=='P')) { + switch (*lp) { + case 'I': + mode = 0; + break; + case 'O': + mode = 1; + break; + case 'P': + mode = 2; + break; + } + lp++; + } else { + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + mode = fvar; + } + uint8_t pm=0; + if (mode==0) pm = INPUT; + if (mode==1) pm = OUTPUT; + if (mode==2) pm = INPUT_PULLUP; + pinMode(pinnr, pm); + goto next_line; + } else if (!strncmp(lp, "spin(", 5)) { + + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + int8_t pinnr = fvar; + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + int8_t mode = fvar; + digitalWrite(pinnr, mode & 1); + goto next_line; + } else if (!strncmp(lp, "svars", 5)) { + lp += 5; + + Scripter_save_pvars(); + goto next_line; + } +#ifdef USE_SCRIPT_GLOBVARS + else if (!strncmp(lp, "gvr", 3)) { + lp += 3; + + Restart_globvars(); + goto next_line; + } +#endif +#ifdef USE_LIGHT +#ifdef USE_WS2812 + else if (!strncmp(lp, "ws2812(", 7)) { + lp = isvar(lp + 7, &vtype, &ind, 0, 0, 0); + if (vtype!=VAR_NV) { + SCRIPT_SKIP_SPACES + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + } else { + fvar = 0; + } + + uint8_t index = glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + if (glob_script_mem.type[ind.index].bits.is_filter) { + uint16_t len = 0; + float *fa = Get_MFAddr(index, &len, 0); + + if (fa && len) ws2812_set_array(fa, len, fvar); + } + } + } + goto next_line; + } +#endif +#endif +#ifdef ESP32 + else if (!strncmp(lp, "beep(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + SCRIPT_SKIP_SPACES + float fvar1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + esp32_beep(fvar, fvar1); + lp++; + goto next_line; + } +#endif + else if (!strncmp(lp, "wcs", 3)) { + lp+=4; + + char tmp[256]; + Replace_Cmd_Vars(lp ,1 , tmp, sizeof(tmp)); + WSContentFlush(); + WSContentSend_P(PSTR("%s"),tmp); + WSContentFlush(); + goto next_line; + } + else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { + + uint8_t sflag = 0,pflg = 0,svmqtt,swll; + if (*lp=='p') { + pflg = 1; + lp += 5; + } + else { + if (*lp=='-') sflag = 1; + if (*lp=='+') sflag = 2; + lp += 2; + } + char *slp = lp; + SCRIPT_SKIP_SPACES + #define SCRIPT_CMDMEM 512 + char *cmdmem = (char*)malloc(SCRIPT_CMDMEM); + if (cmdmem) { + char *cmd = cmdmem; + uint16_t count; + for (count = 0; count", 1, jo); + glob_script_mem.scriptptr = svd_sp; + } + + + if (if_state[ifstck]==1) { + + lp = Evaluate_expression(lp, and_or, &if_result[ifstck], jo); + SCRIPT_SKIP_SPACES + if (*lp=='{' && if_state[ifstck]==1) { + lp += 1; + if_state[ifstck] = 2; + if (if_exe[ifstck - 1]) if_exe[ifstck] = if_result[ifstck]; + } + goto next_line; + } else { + char *vnp = lp; + lp = isvar(lp, &vtype, &ind, &sysvar, 0, 0); + if (vtype!=VAR_NV) { +#ifdef USE_SCRIPT_GLOBVARS + char varname[16]; + uint32_t vnl = (uint32_t)lp - (uint32)vnp; + strncpy(varname, vnp, vnl); + varname[vnl] = 0; +#endif + + + globvindex = ind.index; + globaindex = last_findex; + uint8_t index = glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + if (ind.bits.settable || ind.bits.is_filter) { + dfvar = &sysvar; + if (ind.bits.settable) { + sysv_type = ind.index; + } else { + sysv_type = 0; + } + + } else { + dfvar = &glob_script_mem.fvars[index]; + sysv_type = 0; + } + numeric = 1; + lp = getop(lp, &lastop); + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, jo); + if (glob_script_mem.glob_error==1) { + + + lp = isvar(slp, &vtype, &ind, 0, cmpstr, jo); + fvar = CharToFloat(cmpstr); + } + switch (lastop) { + case OPER_EQU: + if (glob_script_mem.var_not_found) { + if (!jo) toLogEOL("var not found: ",lp); + goto next_line; + } + *dfvar = fvar; + break; + case OPER_PLSEQU: + *dfvar += fvar; + break; + case OPER_MINEQU: + *dfvar -= fvar; + break; + case OPER_MULEQU: + *dfvar *= fvar; + break; + case OPER_DIVEQU: + *dfvar /= fvar; + break; + case OPER_PERCEQU: + *dfvar = fmodf(*dfvar, fvar); + break; + case OPER_ANDEQU: + *dfvar = (uint32_t)*dfvar & (uint32_t)fvar; + break; + case OPER_OREQU: + *dfvar = (uint32_t)*dfvar | (uint32_t)fvar; + break; + case OPER_XOREQU: + *dfvar = (uint32_t)*dfvar ^ (uint32_t)fvar; + break; + default: + + break; + } + + glob_script_mem.type[globvindex].bits.changed = 1; +#ifdef USE_SCRIPT_GLOBVARS + if (glob_script_mem.type[globvindex].bits.global) { + script_udp_sendvar(varname, dfvar, 0); + } +#endif + if (glob_script_mem.type[globvindex].bits.is_filter) { + if (globaindex>=0) { + Set_MFVal(glob_script_mem.type[globvindex].index, globaindex, *dfvar); + } else { + Set_MFilter(glob_script_mem.type[globvindex].index, *dfvar); + } + } + + if (sysv_type) { + switch (sysv_type) { + case SCRIPT_LOGLEVEL: + glob_script_mem.script_loglevel = *dfvar; + break; + case SCRIPT_TELEPERIOD: + if (*dfvar<10) *dfvar = 10; + if (*dfvar>300) *dfvar = 300; + Settings.tele_period = *dfvar; + break; + case SCRIPT_EVENT_HANDLED: + event_handeled = *dfvar; + break; + } + sysv_type = 0; + } + } else { + + numeric = 0; + sindex = index; + saindex = last_sindex; + + char str[SCRIPT_MAXSSIZE]; + lp = getop(lp, &lastop); + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetStringArgument(lp, OPER_EQU, str, jo); + if (!jo && glob_script_mem.glob_error) { + + lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0); + dtostrfd(fvar, 6, str); + glob_script_mem.glob_error = 0; + } + + if (!glob_script_mem.var_not_found) { + + glob_script_mem.type[globvindex].bits.changed = 1; +#ifdef USE_SCRIPT_GLOBVARS + if (glob_script_mem.type[globvindex].bits.global) { + script_udp_sendvar(varname, 0, str); + } +#endif + if (saindex>=0) { + if (lastop==OPER_EQU) { + strlcpy(glob_script_mem.last_index_string[glob_script_mem.sind_num] + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + } else if (lastop==OPER_PLSEQU) { + strncat(glob_script_mem.last_index_string[glob_script_mem.sind_num] + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + } + last_sindex = -1; + } else { + if (lastop==OPER_EQU) { + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + } else if (lastop==OPER_PLSEQU) { + strncat(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + } + } + } + } + + } + SCRIPT_SKIP_SPACES + if (*lp=='{' && if_state[ifstck]==3) { + lp += 1; + + } + goto next_line; + } + } else { + + + if (*lp=='>' && tlen==1) { + + lp++; + section = 1; + fromscriptcmd = 1; + goto startline; + } + if (!strncmp(lp, type, tlen)) { + + section = 1; + glob_script_mem.section_ptr = lp; + if (check) { + return 99; + } + + char *ctype = (char*)type; + if (*ctype=='#') { + + ctype += tlen; + if (*ctype=='(' && *(lp+tlen)=='(') { + float fparam; + numeric = 1; + glob_script_mem.glob_error = 0; + GetNumericArgument((char*)ctype, OPER_EQU, &fparam, 0); + if (glob_script_mem.glob_error==1) { + + numeric = 0; + + GetStringArgument((char*)ctype + 1, OPER_EQU, cmpstr, 0); + } + lp += tlen; + if (*lp=='(') { + + lp++; + lp = isvar(lp, &vtype, &ind, 0, 0, 0); + if (vtype!=VAR_NV) { + + uint8_t index = glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + dfvar = &glob_script_mem.fvars[index]; + if (numeric) { + *dfvar = fparam; + } else { + + *dfvar = CharToFloat(cmpstr); + } + } else { + + sindex = index; + if (!numeric) { + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), cmpstr, glob_script_mem.max_ssize); + } else { + + dtostrfd(fparam, 6, glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize)); + } + } + } + } + } else { + lp += tlen; + if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { + + section = 0; + } + } + } + } + } + + next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) { + if (section) { + return 0; + } else { + return -1; + } + } + lp++; + } + } + return -1; +} + +uint8_t script_xsns_index = 0; + + +void ScripterEvery100ms(void) { + + if (Settings.rule_enabled && (uptime > 4)) { + mqtt_data[0] = '\0'; + uint16_t script_tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index); + tele_period = script_tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + Run_Scripter(">T", 2, mqtt_data); + } + } + if (Settings.rule_enabled) { + if (fast_script==99) Run_Scripter(">F", 2, 0); + } +} + + + + +void Scripter_save_pvars(void) { + int16_t mlen = 0; + float *fp = (float*)glob_script_mem.script_pram; + mlen+=sizeof(float); + struct T_INDEX *vtp = glob_script_mem.type; + for (uint8_t count = 0; countglob_script_mem.script_pram_size) { + vtp[count].bits.is_permanent = 0; + return; + } + while (len--) { + *fp++ = *fa++; + } + } else { + mlen += sizeof(float); + if (mlen>glob_script_mem.script_pram_size) { + vtp[count].bits.is_permanent = 0; + return; + } + *fp++ = glob_script_mem.fvars[index]; + } + } + } + char *cp = (char*)fp; + for (uint8_t count = 0; countglob_script_mem.script_pram_size) { + vtp[count].bits.is_permanent = 0; + return; + } + strcpy(cp, sp); + cp += slen + 1; + } + } +} + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_SCRIPT "s10" + +const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; + +const char HTTP_BTN_MENU_RULES[] PROGMEM = + "

"; + + +const char HTTP_FORM_SCRIPT[] PROGMEM = + "
 " D_SCRIPT " " + "
"; + +const char HTTP_FORM_SCRIPT1[] PROGMEM = + "
" + "
" + "
" + ""; + +const char HTTP_SCRIPT_FORM_END[] PROGMEM = + "
" + "" + "
"; + +#ifdef USE_SCRIPT_FATFS +const char HTTP_FORM_SCRIPT1c[] PROGMEM = + ""; +#ifdef SDCARD_DIR +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#else +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#endif + +#ifdef SDCARD_DIR +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_DIR; +#else +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_UPLOAD; +#endif + +const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = +"
" +"
 %s" " "; +const char HTTP_FORM_FILE_UPG[] PROGMEM = +"
" +"

" +"
"; + +const char HTTP_FORM_FILE_UPGb[] PROGMEM = +"
" +"
" +""; + +const char HTTP_FORM_FILE_UPGc[] PROGMEM = +"
total size: %s kB - free: %s kB
"; + +const char HTTP_FORM_SDC_DIRa[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_DIRb[] PROGMEM = + "
%s     %s : %8d
"; +const char HTTP_FORM_SDC_DIRd[] PROGMEM = +"
%s
"; +const char HTTP_FORM_SDC_DIRc[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_HREF[] PROGMEM = +"http://%s/upl?download=%s/%s"; +#endif + + +uint8_t *script_ex_ptr; +uint16_t uplsize; +uint8_t sc_state; + + +void script_upload_start(void) { + + + + HTTPUpload& upload = Webserver->upload(); + if (upload.status == UPLOAD_FILE_START) { + + script_ex_ptr = (uint8_t*)glob_script_mem.script_ram; + + + if (strcmp(upload.filename.c_str(), "execute_script")) { + Web.upload_error = 1; + WSSend(500, CT_PLAIN, F("500: wrong filename")); + return; + } + if (upload.totalSize>=glob_script_mem.script_size) { + Web.upload_error = 1; + WSSend(500, CT_PLAIN, F("500: file to large")); + return; + } + uplsize = 0; + + sc_state = bitRead(Settings.rule_enabled, 0); + bitWrite(Settings.rule_enabled, 0, 0); + + } else if(upload.status == UPLOAD_FILE_WRITE) { + + uint32_t csiz = upload.currentSize; + uint32_t tsiz = glob_script_mem.script_size - 1; + if (uplsize0 +#undef REJCMPL +#define REJCMPL 6 +#else +#undef REJCMPL +#define REJCMPL 8 +#endif + +uint8_t reject(char *name) { + + char *lcp = strrchr(name,'/'); + if (lcp) { + name = lcp + 1; + } + + while (*name=='/') name++; + if (*name=='_') return 1; + if (*name=='.') return 1; + + if (!strncasecmp(name, "SPOTLI~1", REJCMPL)) return 1; + if (!strncasecmp(name, "TRASHE~1", REJCMPL)) return 1; + if (!strncasecmp(name, "FSEVEN~1", REJCMPL)) return 1; + if (!strncasecmp(name, "SYSTEM~1", REJCMPL)) return 1; + if (!strncasecmp(name, "System Volume", 13)) return 1; + return 0; +} + +void ListDir(char *path, uint8_t depth) { + char name[32]; + char npath[128]; + char format[12]; + sprintf(format, "%%-%ds", 24 - depth); + + File dir = fsp->open(path, FILE_READ); + if (dir) { + dir.rewindDirectory(); + if (strlen(path)>1) { + snprintf_P(npath, sizeof(npath), PSTR("http://%s/upl?download=%s"), WiFi.localIP().toString().c_str(),path); + for (uint8_t cnt = strlen(npath) - 1; cnt>0; cnt--) { + if (npath[cnt]=='/') { + if (npath[cnt - 1]=='=') npath[cnt + 1] = 0; + else npath[cnt] = 0; + break; + } + } + WSContentSend_P(HTTP_FORM_SDC_DIRd, npath,path, ".."); + } + char *ep; + while (true) { + File entry = dir.openNextFile(); + if (!entry) { + break; + } + + ep = (char*)entry.name(); + if (*ep=='/') ep++; + char *lcp = strrchr(ep,'/'); + if (lcp) { + ep = lcp + 1; + } + + time_t tm = entry.getLastWrite(); + char tstr[24]; + strftime(tstr, 22, "%d-%m-%Y - %H:%M:%S ", localtime(&tm)); + + char *pp = path; + if (!*(pp + 1)) pp++; + char *cp = name; + + if (reject((char*)ep)) goto fclose; + + for (uint8_t cnt = 0; cnt1) { + strcat(path, "/"); + } + strcat(path, ep); + ListDir(path, depth + 4); + path[plen] = 0; + } else { + snprintf_P(npath, sizeof(npath), HTTP_FORM_SDC_HREF, WiFi.localIP().toString().c_str(), pp,ep); + WSContentSend_P(HTTP_FORM_SDC_DIRb, npath, ep, name, tstr, entry.size()); + } + fclose: + entry.close(); + } + dir.close(); + } +} + +char path[48]; + +void Script_FileUploadConfiguration(void) { + uint8_t depth = 0; + + strcpy(path, "/"); + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("download")) { + String stmp = Webserver->arg("download"); + char *cp = (char*)stmp.c_str(); + if (DownloadFile(cp)) { + + strcpy(path, cp); + } + } + + WSContentStart_P(S_SCRIPT_FILE_UPLOAD); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); + WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); +#ifdef SDCARD_DIR + char ts[16]; + char fs[16]; + form1000(get_fsinfo(0), ts, '.'); + form1000(get_fsinfo(1), fs, '.'); + WSContentSend_P(HTTP_FORM_FILE_UPGc, ts, fs); + WSContentSend_P(HTTP_FORM_SDC_DIRa); + if (glob_script_mem.script_sd_found) { + ListDir(path, depth); + } + WSContentSend_P(HTTP_FORM_SDC_DIRc); +#endif + WSContentSend_P(HTTP_FORM_FILE_UPGb); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + Web.upload_error = 0; +} + +void ScriptFileUploadSuccess(void) { + WSContentStart_P(S_INFORMATION); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + WSContentSend_P(PSTR("

")); + WSContentSend_P(PSTR("

"),"/upl",D_UPL_DONE); + + WSContentStop(); +} + + +File upload_file; + +void script_upload(void) { + + HTTPUpload& upload = Webserver->upload(); + if (upload.status == UPLOAD_FILE_START) { + char npath[48]; +#if defined(ESP32) && defined(USE_SCRIPT_FATFS) && USE_SCRIPT_FATFS==-1 + + sprintf(npath, "%s/%s", path, upload.filename.c_str()); +#else + sprintf(npath, "%s/%s", path, upload.filename.c_str()); +#endif + fsp->remove(npath); + upload_file = fsp->open(npath, FILE_WRITE); + if (!upload_file) Web.upload_error = 1; + } else if(upload.status == UPLOAD_FILE_WRITE) { + if (upload_file) upload_file.write(upload.buf, upload.currentSize); + } else if(upload.status == UPLOAD_FILE_END) { + if (upload_file) upload_file.close(); + if (Web.upload_error) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); + } + } else { + Web.upload_error=1; + Webserver->send(500, "text/plain", "500: couldn't create file"); + } +} + +uint8_t DownloadFile(char *file) { + File download_file; + WiFiClient download_Client; + + if (!fsp->exists(file)) { + AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); + return 0; + } + + download_file = fsp->open(file, FILE_READ); + if (!download_file) { + AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); + return 0; + } + + if (download_file.isDirectory()) { + download_file.close(); + return 1; + } + + uint32_t flen = download_file.size(); + + download_Client = Webserver->client(); + Webserver->setContentLength(flen); + + char attachment[100]; + char *cp; + for (uint8_t cnt = strlen(file); cnt>=0; cnt--) { + if (file[cnt]=='/') { + cp = &file[cnt + 1]; + break; + } + } + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"), cp); + Webserver->sendHeader(F("Content-Disposition"), attachment); + WSSend(200, CT_STREAM, ""); + + uint8_t buff[512]; + uint16_t bread; + + + uint8_t cnt = 0; + while (download_file.available()) { + bread = download_file.read(buff, sizeof(buff)); + uint16_t bw = download_Client.write((const char*)buff, bread); + if (!bw) break; + cnt++; + if (cnt>7) { + cnt = 0; + if (glob_script_mem.script_loglevel & 0x80) { + + loop(); + } + } + } + download_file.close(); + download_Client.stop(); + return 0; +} + +#endif + + +void HandleScriptTextareaConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + ScriptSaveSettings(); + HandleConfiguration(); + return; + } +} + + +void HandleScriptConfiguration(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); + +#ifdef USE_SCRIPT_FATFS + if (Webserver->hasArg("d1")) { + DownloadFile(glob_script_mem.flink[0]); + } + if (Webserver->hasArg("d2")) { + DownloadFile(glob_script_mem.flink[1]); + } + if (Webserver->hasArg("upl")) { + Script_FileUploadConfiguration(); + } +#endif + + WSContentStart_P(S_CONFIGURE_SCRIPT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_SCRIPT); + + +#ifdef xSCRIPT_STRIP_COMMENTS + uint16_t ssize = glob_script_mem.script_size; + if (bitRead(Settings.rule_enabled, 1)) ssize *= 2; + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); +#else + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); +#endif + + + if (glob_script_mem.script_ram[0]) { + _WSContentSend(glob_script_mem.script_ram); + } + WSContentSend_P(HTTP_FORM_SCRIPT1b); + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.script_sd_found) { + WSContentSend_P(HTTP_FORM_SCRIPT1d); + if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 1, glob_script_mem.flink[0]); + if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 2, glob_script_mem.flink[1]); + } +#endif + + WSContentSend_P(HTTP_SCRIPT_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void SaveScript(void) { + +#ifdef EEP_SCRIPT_SIZE + if (glob_script_mem.flags&1) { + EEP_WRITE(0, EEP_SCRIPT_SIZE, glob_script_mem.script_ram); + } +#endif + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.flags & 1) { + fsp->remove(FAT_SCRIPT_NAME); + File file = fsp->open(FAT_SCRIPT_NAME, FILE_WRITE); + file.write((const uint8_t*)glob_script_mem.script_ram, FAT_SCRIPT_SIZE); + file.close(); + } +#endif + +#ifdef LITTLEFS_SCRIPT_SIZE + if (glob_script_mem.flags&1) { + SaveFile("/script.txt", (uint8_t*)glob_script_mem.script_ram, LITTLEFS_SCRIPT_SIZE); + } +#endif +} + +void ScriptSaveSettings(void) { + + if (Webserver->hasArg("c1")) { + bitWrite(Settings.rule_enabled, 0, 1); + } else { + bitWrite(Settings.rule_enabled, 0, 0); + } + + + String str = Webserver->arg("t1"); + + if (*str.c_str()) { + + str.replace("\r\n", "\n"); + str.replace("\r", "\n"); + +#ifdef xSCRIPT_STRIP_COMMENTS + if (bitRead(Settings.rule_enabled, 1)) { + char *sp = (char*)str.c_str(); + char *sp1 = sp; + char *dp = sp; + uint8_t flg = 0; + while (*sp) { + while (*sp==' ') sp++; + sp1 = sp; + sp = strchr(sp,'\n'); + if (!sp) { + flg = 1; + } else { + *sp = 0; + } + if (*sp1!=';') { + uint8_t slen = strlen(sp1); + if (slen) { + strcpy(dp, sp1); + dp += slen; + *dp++ = '\n'; + } + } + if (flg) { + *dp = 0; + break; + } + sp++; + } + } +#endif + + strlcpy(glob_script_mem.script_ram, str.c_str(), glob_script_mem.script_size); + + if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') { + AddLog_P2(LOG_LEVEL_INFO, PSTR("script error: must start with >D")); + bitWrite(Settings.rule_enabled, 0, 0); + } + + SaveScript(); + + } + + SaveScriptEnd(); +} + +void SaveScriptEnd(void) { + +#ifdef USE_SCRIPT_GLOBVARS + Script_Stop_UDP(); +#endif + + if (glob_script_mem.script_mem) { + Scripter_save_pvars(); + free(glob_script_mem.script_mem); + glob_script_mem.script_mem = 0; + glob_script_mem.script_mem_size = 0; + } + +#ifdef USE_SCRIPT_COMPRESSION + + uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), Settings.rules[0], MAX_SCRIPT_SIZE-1); + if (len_compressed > 0) { + Settings.rules[0][len_compressed] = 0; + AddLog_P2(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram)); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed); + } +#endif + + if (bitRead(Settings.rule_enabled, 0)) { + + + + int16_t res = Init_Scripter(); + if (res) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); + return; + } + + Run_Scripter(">B\n", 3, 0); + Run_Scripter(">BS", 3, 0); + + fast_script = Run_Scripter(">F", -2, 0); + } +} + +#endif + + +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +#define HUE_DEV_MVNUM 5 +#define HUE_DEV_NSIZE 16 +struct HUE_SCRIPT { + char name[HUE_DEV_NSIZE]; + uint8_t type; + uint8_t index[HUE_DEV_MVNUM]; + uint8_t vindex[HUE_DEV_MVNUM]; +} hue_script[32]; + + +const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = + "{\"state\":" + "{\"on\":{state}," + "{light_status}" + "\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}" + ",\"type\":\"{type}\"," + "\"name\":\"{j1\"," + "\"modelid\":\"{m1}\"," + "\"uniqueid\":\"{j2\"," + "\"swversion\":\"5.50.1.19085\"}"; +# 5264 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = +"{\"state\":{" +"\"presence\":{state}," +"\"lastupdated\":\"2017-10-01T12:37:30\"" +"}," +"\"swupdate\":{" +"\"state\":\"noupdates\"," +"\"lastinstall\": null" +"}," +"\"config\":{" +"\"on\":true," +"\"battery\":100," +"\"reachable\":true," +"\"alert\":\"none\"," +"\"ledindication\":false," +"\"usertest\":false," +"\"sensitivity\":2," +"\"sensitivitymax\":2," +"\"pending\":[]" +"}," +"\"name\":\"{j1\"," +"\"type\":\"ZLLPresence\"," +"\"modelid\":\"SML001\"," +"\"manufacturername\":\"Philips\"," +"\"swversion\":\"6.1.0.18912\"," +"\"uniqueid\":\"{j2\"" +"}"; +# 5345 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +void Script_HueStatus(String *response, uint16_t hue_devs) { + + if (hue_script[hue_devs].type=='p') { + *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1", hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1]; + response->replace("{state}", (pwr ? "true" : "false")); + return; + } + + *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); + uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1]; + response->replace("{state}", (pwr ? "true" : "false")); + String light_status = ""; + if (hue_script[hue_devs].index[1]>0) { + + light_status += "\"bri\":"; + uint32_t bri = glob_script_mem.fvars[hue_script[hue_devs].index[1] - 1]; + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + light_status += String(bri); + light_status += ","; + } + if (hue_script[hue_devs].index[2]>0) { + + uint32_t hue = glob_script_mem.fvars[hue_script[hue_devs].index[2] - 1]; + + light_status += "\"hue\":"; + light_status += String(hue); + light_status += ","; + } + if (hue_script[hue_devs].index[3]>0) { + + uint32_t sat = glob_script_mem.fvars[hue_script[hue_devs].index[3] - 1] ; + if (sat > 254) sat = 254; + if (sat < 1) sat = 1; + light_status += "\"sat\":"; + light_status += String(sat); + light_status += ","; + } + if (hue_script[hue_devs].index[4]>0) { + + uint32_t ct = glob_script_mem.fvars[hue_script[hue_devs].index[4] - 1]; + light_status += "\"ct\":"; + light_status += String(ct); + light_status += ","; + } + + float temp; + switch (hue_script[hue_devs].type) { + case 'C': + response->replace("{type}","Color Ligh"); + response->replace("{m1","LST001"); + break; + case 'D': + response->replace("{type}","Dimmable Light"); + response->replace("{m1","LWB004"); + break; + case 'T': + response->replace("{type}","Color Temperature Light"); + response->replace("{m1","LTW011"); + break; + case 'E': + response->replace("{type}","Extended color light"); + response->replace("{m1","LCT007"); + break; + case 'S': + response->replace("{type}","On/Off light"); + response->replace("{m1","LCT007"); + break; + default: + response->replace("{type}","color light"); + response->replace("{m1","LST001"); + break; + } + + response->replace("{light_status}", light_status); + response->replace("{j1", hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + +} + +void Script_Check_Hue(String *response) { + if (!bitRead(Settings.rule_enabled, 0)) return; + + uint8_t hue_script_found = Run_Scripter(">H", -2, 0); + if (hue_script_found!=99) return; + + char tmp[256]; + uint8_t hue_devs = 0; + uint8_t vindex = 0; + char *cp; + char *lp = glob_script_mem.section_ptr + 2; + while (lp) { + SCRIPT_SKIP_SPACES + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + + + cp = tmp; + cp = strchr(cp,','); + if (!cp) break; + *cp = 0; + + strlcpy(hue_script[hue_devs].name, tmp, HUE_DEV_NSIZE); + cp++; + while (*cp==' ') cp++; + + hue_script[hue_devs].type = *cp; + + for (vindex = 0; vindex0) *response += ",\""; + else *response += "\""; + } + *response += String(EncodeLightId(hue_devs + devices_present + 1))+"\":"; + Script_HueStatus(response, hue_devs); + + } + + hue_devs++; + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } +#if 0 + if (response) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); + toLog(">>>>"); + toLog(response->c_str()); + toLog(response->c_str()+LOGSZ); + } +#endif +} + +const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +void Script_Handle_Hue(String *path) { + String response; + int code = 200; + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool resp = false; + + uint8_t device = DecodeLightId(atoi(path->c_str())); + uint8_t index = device - devices_present - 1; + + if (Webserver->args()) { + response = "["; + + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { + + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "on"); + + bool on = hue_on.getBool(); + if (on==false) { + glob_script_mem.fvars[hue_script[index].index[0] - 1] = 0; + response.replace("{re", "false"); + } else { + glob_script_mem.fvars[hue_script[index].index[0] - 1] = 1; + response.replace("{re", "true"); + } + glob_script_mem.type[hue_script[index].vindex[0]].bits.changed = 1; + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { + tmp = hue_bri.getUInt(); + bri = tmp; + if (254 <= bri) { bri = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "bri"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[1] - 1] = bri; + glob_script_mem.type[hue_script[index].vindex[1]].bits.changed = 1; + resp = true; + } + + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { + float x, y; + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + x = tok_x.getFloat(); + y = tok_y.getFloat(); + String x_str = tok_x.getStr(); + String y_str = tok_y.getStr(); + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "xy"); + response.replace("{re", "[" + x_str + "," + y_str + "]"); + glob_script_mem.fvars[hue_script[index].index[2]-1] = hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; + glob_script_mem.fvars[hue_script[index].index[3]-1] = sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; + resp = true; + } + + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { + tmp = hue_hue.getUInt(); + + + hue = tmp; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "hue"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[2] - 1] = hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; + resp = true; + } + + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { + tmp = hue_sat.getUInt(); + sat = tmp; + if (254 <= sat) { sat = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "sat"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[3] - 1] = sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; + resp = true; + } + + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { + ct = hue_ct.getUInt(); + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "ct"); + response.replace("{re", String(ct)); + glob_script_mem.fvars[hue_script[index].index[4] - 1] = ct; + glob_script_mem.type[hue_script[index].vindex[4]].bits.changed = 1; + resp = true; + } + response += "]"; + + } else { + response = FPSTR(sHUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + if (resp) { + Run_Scripter(">E", 2, 0); + } +} +#endif + + +#ifdef USE_SCRIPT_SUB_COMMAND +bool Script_SubCmd(void) { + if (!bitRead(Settings.rule_enabled, 0)) return false; + + if (tasm_cmd_activ) return false; + + char command[CMDSZ]; + strlcpy(command, XdrvMailbox.topic, CMDSZ); + uint32_t pl = XdrvMailbox.payload; + char pld[64]; + strlcpy(pld, XdrvMailbox.data, sizeof(pld)); + + char cmdbuff[128]; + char *cp = cmdbuff; + *cp++ = '#'; + strcpy(cp, XdrvMailbox.topic); + uint8_t tlen = strlen(XdrvMailbox.topic); + cp += tlen; + if (XdrvMailbox.index > 0) { + *cp++ = XdrvMailbox.index | 0x30; + tlen++; + } + if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { + *cp++ = '('; + strncpy(cp, XdrvMailbox.data,XdrvMailbox.data_len); + cp += XdrvMailbox.data_len; + *cp++ = ')'; + *cp = 0; + } + + uint32_t res = Run_Scripter(cmdbuff, tlen + 1, 0); + + if (res) return false; + else { + if (pl>=0) { + Response_P(S_JSON_COMMAND_NVALUE, command, pl); + } else { + Response_P(S_JSON_COMMAND_SVALUE, command, pld); + } + } + return true; +} +#endif + +void execute_script(char *script) { + char *svd_sp = glob_script_mem.scriptptr; + strcat(script, "\n#"); + glob_script_mem.scriptptr = script; + Run_Scripter(">", 1, 0); + glob_script_mem.scriptptr = svd_sp; +} +#define D_CMND_SCRIPT "Script" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" + +enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; +const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; + +bool ScriptCommand(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t index = XdrvMailbox.index; + + if (tasm_cmd_activ) return false; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); + if (-1 == command_code) { + serviced = false; + } + else if ((CMND_SCRIPT == command_code) && (index > 0)) { + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; +#ifdef xSCRIPT_STRIP_COMMENTS + case 2: + bitWrite(Settings.rule_enabled, 1, 0); + break; + case 3: + bitWrite(Settings.rule_enabled, 1, 1); + break; +#endif + } + } else { + if ('>' == XdrvMailbox.data[0]) { + + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"), command,XdrvMailbox.data); + if (bitRead(Settings.rule_enabled, 0)) { + for (uint8_t count = 0; count> 1; +} + +void dateTime(uint16_t* date, uint16_t* time) { + + *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); + + *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); +} + +#endif + + + +#ifdef SUPPORT_MQTT_EVENT + +#ifndef MQTT_EVENT_MSIZE +#define MQTT_EVENT_MSIZE 256 +#endif +#ifndef MQTT_EVENT_JSIZE +#define MQTT_EVENT_JSIZE 400 +#endif +# 5867 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +bool ScriptMqttData(void) +{ + bool serviced = false; + + + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > MQTT_EVENT_MSIZE) { + return false; + } + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + String lkey; + if (event_item.Key.length() == 0) { + value = sData; + } else { + JsonParser parser((char*)sData.c_str()); + JsonParserObject jsonData = parser.getRootObject(); + String key1 = event_item.Key; + String key2; + if (!jsonData) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + lkey = key2; + JsonParserToken val = jsonData[key1.c_str()].getObject()[key2.c_str()]; + if (!val) break; + value = val.getStr(); + } else { + JsonParserToken val = jsonData[key1.c_str()]; + if (!val) break; + value = val.getStr(); + lkey = key1; + } + } + value.trim(); + char sbuffer[128]; + + if (!strncmp(lkey.c_str(), "Epoch", 5)) { + uint32_t ep = atoi(value.c_str()) - (uint32_t)EPOCH_OFFSET; + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(), ep); + } else { + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); + } + + execute_script(sbuffer); + } + } + return serviced; +} +# 5944 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptSubscribe(const char *data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + char parameters[data_len + 1]; + memcpy(parameters, data, data_len); + parameters[data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + return events; +} +# 6024 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptUnsubscribe(const char * data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + return events; +} +#endif + + + +#ifdef USE_SCRIPT_WEB_DISPLAY + +#ifdef SCRIPT_FULL_WEBPAGE +const char HTTP_WEB_FULL_DISPLAY[] PROGMEM = + "

"; + +const char HTTP_SCRIPT_FULLPAGE1[] PROGMEM = + "" + "" + "" + "" + "%s - %s" + ""; + + +#ifdef USE_SCRIPT_FATFS + +const char HTTP_SCRIPT_MIMES[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "Content-disposition: inline; filename=%s" + "Content-type: %s\r\n\r\n"; + +void ScriptGetSDCard(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + String stmp = Webserver->uri(); + char *cp = strstr_P(stmp.c_str(), PSTR("/sdc/")); + + if (cp) { +#ifdef ESP32 + cp += 4; +#else + cp += 5; +#endif + if (strstr_P(cp, PSTR("scrdmp.bmp"))) { + SendFile(cp); + return; + } else { + if (fsp->exists(cp)) { + SendFile(cp); + return; + } + } + } + HandleNotFound(); +} + +extern uint8_t *buffer; + +void SendFile(char *fname) { +char buff[512]; + const char *mime; + uint8_t sflg = 0; + char *jpg = strstr(fname,".jpg"); + if (jpg) { + mime = "image/jpeg"; + } + +#ifdef USE_DISPLAY_DUMP + char *sbmp = strstr_P(fname, PSTR("scrdmp.bmp")); + if (sbmp) { + mime = "image/bmp"; + sflg = 1; + } +#endif + + char *bmp = strstr(fname, ".bmp"); + if (bmp) { + mime = "image/bmp"; + } + char *html = strstr(fname, ".html"); + if (html) { + mime = "text/html"; + } + char *txt = strstr(fname, ".txt"); + if (txt) { + mime = "text/plain"; + } + + WSContentSend_P(HTTP_SCRIPT_MIMES, fname, mime); + + if (sflg) { +#ifdef USE_DISPLAY_DUMP + + #define fileHeaderSize 14 + #define infoHeaderSize 40 + if (buffer) { + uint8_t *bp = buffer; + uint8_t *lbuf = (uint8_t*)calloc(Settings.display_width + 2, 3); + uint8_t *lbp; + uint8_t fileHeader[fileHeaderSize]; + createBitmapFileHeader(Settings.display_height , Settings.display_width , fileHeader); + Webserver->client().write((uint8_t *)fileHeader, fileHeaderSize); + uint8_t infoHeader[infoHeaderSize]; + createBitmapInfoHeader(Settings.display_height, Settings.display_width, infoHeader ); + Webserver->client().write((uint8_t *)infoHeader, infoHeaderSize); + for (uint32_t lins = 0; lins>1; + } + bp++; + } + Webserver->client().write((const char*)lbuf, Settings.display_width * 3); + } + if (lbuf) free(lbuf); + Webserver->client().stop(); + } +#endif + } else { + File file = fsp->open(fname,FILE_READ); + uint32_t siz = file.size(); + uint32_t len = sizeof(buff); + while (siz > 0) { + if (len>siz) len = siz; + file.read((uint8_t *)buff, len); + Webserver->client().write((const char*)buff, len); + siz -= len; + } + file.close(); + } + Webserver->client().stop(); +} +#endif + +void ScriptFullWebpage(void) { + uint32_t fullpage_refresh=10000; + if (!HttpCheckPriviledgedAccess()) { return; } + + String stmp = Webserver->uri(); + + if (Webserver->hasArg("m")) { + if (Webserver->hasArg("sv")) { + Script_Check_HTML_Setvars(); + } + WSContentBegin(200, CT_HTML); + ScriptWebShow('w'); + WSContentEnd(); + Serial.printf("fwp update sv %s\n",stmp.c_str() ); + return; + + + + + return; + } else { + Serial.printf("fwp other %s\n",stmp.c_str() ); + } + + WSContentBegin(200, CT_HTML); + const char *title = "Full Screen"; + WSContentSend_P(HTTP_SCRIPT_FULLPAGE1, SettingsText(SET_DEVICENAME), title, fullpage_refresh); + WSContentSend_P(HTTP_SCRIPT_FULLPAGE2, fullpage_refresh); + + + + + + WSContentSend_P(PSTR("
")); + ScriptWebShow('w'); + WSContentSend_P(PSTR("
")); + + ScriptWebShow('x'); + + WSContentStop(); +} +#endif + +void Script_Check_HTML_Setvars(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("sv")) { + String stmp = Webserver->arg("sv"); + Serial.printf("fwp has arg dv %s\n", stmp.c_str()); + char cmdbuf[64]; + memset(cmdbuf, 0, sizeof(cmdbuf)); + char *cp = cmdbuf; + *cp++ = '>'; + strncpy(cp, stmp.c_str(), sizeof(cmdbuf) - 1); + char *cp1 = strchr(cp, '_'); + if (!cp1) return; + *cp1 = 0; + char vname[32]; + strncpy(vname, cp, sizeof(vname)); + *cp1 = '='; + cp1++; + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname, &vtype, &ind, 0, 0, 0); + if (vtype!=NUM_RES && vtype&STYPE) { + + uint8_t tlen = strlen(cp1); + memmove(cp1 + 1, cp1, tlen); + *cp1 = '\"'; + *(cp1 + tlen +1 ) = '\"'; + } + + + execute_script(cmdbuf); + Run_Scripter(">E", 2, 0); + } +} + + +const char SCRIPT_MSG_BUTTONa[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONb[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_START[] PROGMEM = + "
"; +const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_STOP[] PROGMEM = + ""; +const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = + "
"; + +const char SCRIPT_MSG_SLIDER[] PROGMEM = + "
%s
%s%s
" + "
"; + +const char SCRIPT_MSG_CHKBOX[] PROGMEM = + "
"; + +const char SCRIPT_MSG_TEXTINP[] PROGMEM = + "
"; + +const char SCRIPT_MSG_NUMINP[] PROGMEM = + "
"; + + +#ifdef USE_GOOGLE_CHARTS +const char SCRIPT_MSG_GTABLE[] PROGMEM = + "" + "" + "" + ""; + +const char SCRIPT_MSG_TABLE[] PROGMEM = + ""; +const char SCRIPT_MSG_GAUGE[] PROGMEM = + ""; +const char SCRIPT_MSG_TIMELINE[] PROGMEM = + ""; + + +const char SCRIPT_MSG_GTABLEa[] PROGMEM = + ""; + +const char SCRIPT_MSG_GOPT1[] PROGMEM = +"title:'%s',isStacked:false"; + +const char SCRIPT_MSG_GAUGEOPT[] PROGMEM = +"max:%d,redFrom:%d,redTo:%d,yellowFrom:%d,yellowTo:%d"; + +const char SCRIPT_MSG_GOPT2[] PROGMEM = +"showRowNumber:true,sort:'disable',allowHtml:true,width:'100%%',height:'100%%',cssClassNames:cssc"; + +const char SCRIPT_MSG_GOPT3[] PROGMEM = +"title:'%s',isStacked:false,vAxes:{0:{maxValue:%d},1:{maxValue:%d}},series:{0:{targetAxisIndex:0},1:{targetAxisIndex:1}}%s"; + +const char SCRIPT_MSG_GOPT4[] PROGMEM = + +"hAxis:{minValue:new Date(0,1,1,0,0),maxValue:new Date(0,1,2,0,0),format:'HH:mm'},theme: 'maximized'"; + +const char SCRIPT_MSG_GOPT5[] PROGMEM = +"new Date(0,1,1,%d,%d)"; + +const char SCRIPT_MSG_GOPT6[] PROGMEM = +"title:'%s',isStacked:false,vAxis:{viewWindow:{min:%d,max:%d}}%s"; + +const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'"; + +#define GLIBS_MAIN 1<<0 +#define GLIBS_TABLE 1<<1 +#define GLIBS_GAUGE 1<<2 +#define GLIBS_TIMELINE 1<<3 + +#define MAX_GARRAY 4 + + +char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint16_t *rentries, uint16_t *ipos) { +struct T_INDEX ind; +uint8_t vtype; +uint16 entries = 0; +uint16_t cipos = 0; + + uint8_t anum = 0; + while (anum=entries) { + if (!entries) { + entries = len; + } + + arrays[anum] = fa; + anum++; + } + } else { + + arrays[anum] = &glob_script_mem.fvars[index]; + anum++; + entries = 1; + } + } else { + lp = lp1; + break; + } + } + } + + *ranum = anum; + *rentries = entries; + *ipos = cipos; + return lp; +} + +char *gc_send_labels(char *lp,uint32_t anum) { + WSContentSend_PD("["); + for (uint32_t cnt = 0; cnt < anum + 1; cnt++) { + char label[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, label, 0); + SCRIPT_SKIP_SPACES + WSContentSend_PD(SCRIPT_MSG_GTE1, label); + + if (cntw", -2, 0); + } else { + web_script = Run_Scripter(">W", -2, 0); + } + + if (web_script==99) { + char tmp[256]; + uint8_t optflg = 0; + uint8_t chartindex = 1; + uint8_t google_libs = 0; + char *lp = glob_script_mem.section_ptr + 2; + if (mc=='w') { + while (*lp) { + if (*lp=='\n') break; + lp++; + } + } + char *cv_ptr; + float cv_max=0; + float cv_inc=0; + float *cv_count=0; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + SCRIPT_SKIP_SPACES + if (!strncmp(lp, "%for ", 5)) { + + struct T_INDEX ind; + uint8_t vtype; + lp = isvar(lp + 5, &vtype, &ind, 0, 0, 0); + if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { + uint16_t index = glob_script_mem.type[ind.index].index; + cv_count = &glob_script_mem.fvars[index]; + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, cv_count, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, &cv_max, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, &cv_inc, 0); + cv_ptr = lp; + goto nextwebline; + } else { + continue; + } + } else if (!strncmp(lp, "%next", 5)) { + if (cv_count) { + + *cv_count += cv_inc; + if (*cv_count<=cv_max) { + lp = cv_ptr; + } else { + cv_count = 0; + goto nextwebline; + } + } else { + goto nextwebline; + } + } else if (!strncmp(lp, "%=#", 3)) { + + lp = scripter_sub(lp + 1, 0); + goto nextwebline; + } + + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + char *lin = tmp; + if ((!mc && (*lin!='$')) || (mc=='w' && (*lin!='$'))) { +# 6589 "/workspace/Tasmota/tasmota/xdrv_10_scripter.ino" + if (*lin=='@') { + lin++; + optflg = 1; + } else { + optflg = 0; + } + + if (!strncmp(lin, "sl(", 3)) { + + char *lp = lin; + float min; + lp = GetNumericArgument(lp + 3, OPER_EQU, &min, 0); + SCRIPT_SKIP_SPACES + + float max; + lp = GetNumericArgument(lp, OPER_EQU, &max, 0); + SCRIPT_SKIP_SPACES + float val; + char *slp = lp; + lp = GetNumericArgument(lp, OPER_EQU, &val, 0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname, slp, sizeof(vname)); + + char left[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, left, 0); + SCRIPT_SKIP_SPACES + char mid[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, mid, 0); + SCRIPT_SKIP_SPACES + char right[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, right, 0); + SCRIPT_SKIP_SPACES + + WSContentSend_PD(SCRIPT_MSG_SLIDER, left,mid, right, (uint32_t)min, (uint32_t)max, (uint32_t)val, vname); + + } else if (!strncmp(lin, "ck(", 3)) { + char *lp = lin + 3; + char *slp = lp; + float val; + lp = GetNumericArgument(lp, OPER_EQU, &val, 0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname, slp, sizeof(vname)); + + char label[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, label, 0); + const char *cp; + uint8_t uval; + if (val>0) { + cp = "checked='checked'"; + uval = 0; + } else { + cp = ""; + uval = 1; + } + WSContentSend_PD(SCRIPT_MSG_CHKBOX, label, (char*)cp, uval, vname); + + } else if (!strncmp(lin, "bu(", 3)) { + char *lp = lin + 3; + uint8_t bcnt = 0; + char *found = lin; + while (bcnt<4) { + found = strstr(found, "bu("); + if (!found) break; + found += 3; + bcnt++; + } + uint8_t proz = 100 / bcnt; + if (!optflg && bcnt>1) proz -= 2; + if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); + else WSContentSend_PD(SCRIPT_MSG_BUT_START); + for (uint32_t cnt = 0; cnt < bcnt; cnt++) { + float val; + char *slp = lp; + lp = GetNumericArgument(lp, OPER_EQU, &val, 0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname, slp, sizeof(vname)); + + SCRIPT_SKIP_SPACES + char ontxt[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, ontxt, 0); + SCRIPT_SKIP_SPACES + char offtxt[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, offtxt, 0); + + char *cp; + uint8_t uval; + if (val>0) { + cp = ontxt; + uval = 0; + } else { + cp = offtxt; + uval = 1; + } + if (bcnt>1 && cnt==bcnt-1) { + if (!optflg) proz += 2; + } + if (!optflg) { + WSContentSend_PD(SCRIPT_MSG_BUTTONa, proz, uval, vname, cp); + } else { + WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL, proz, uval, vname, cp); + } + if (bcnt>1 && cnt%s
"), lin); + } else { + WSContentSend_PD(PSTR("{s}%s{e}"), lin); + } + } + } + + } else { + + if (*lin==mc) { + +#ifdef USE_GOOGLE_CHARTS + lin++; +exgc: + char *lp; + if (!strncmp(lin, "gc(", 3)) { + + lp = lin + 3; + SCRIPT_SKIP_SPACES + const char *type; + const char *func; + char options[312]; + uint8_t nanum = MAX_GARRAY; + uint8_t y2f = 0; + uint8_t tonly = 0; + char ctype; + ctype = *lp; + lp++; + if (!(google_libs & GLIBS_MAIN)) { + google_libs |= GLIBS_MAIN; + WSContentSend_PD(SCRIPT_MSG_GTABLE); + } + switch (ctype) { + case 'l': + type = PSTR("LineChart"); + break; + case 'b': + type = PSTR("BarChart"); + break; + case 'p': + type = PSTR("PieChart"); + break; + case 'g': + type = PSTR("Gauge"); + if (!(google_libs & GLIBS_GAUGE)) { + google_libs |= GLIBS_GAUGE; + WSContentSend_PD(SCRIPT_MSG_GAUGE); + } + break; + case 't': + type = PSTR("Table"); + if (!(google_libs & GLIBS_TABLE)) { + google_libs |= GLIBS_TABLE; + WSContentSend_PD(SCRIPT_MSG_TABLE); + } + break; + case 'T': + type = PSTR("Timeline"); + if (!(google_libs & GLIBS_TIMELINE)) { + google_libs |= GLIBS_TIMELINE; + WSContentSend_PD(SCRIPT_MSG_TIMELINE); + } + break; + case 'h': + type = PSTR("Histogram"); + break; + case 'c': + type = PSTR("ColumnChart"); + break; + case 'C': + type = PSTR("ComboChart"); + break; + case 'e': + WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex); + chartindex++; + goto nextwebline; + break; + default: + + goto nextwebline; + break; + } + if (ctype=='l' && *lp=='f') { + lp++; + func = PSTR(",curveType:'function'"); + } else { + func = ""; + } + if (*lp=='2') { + lp++; + nanum = 2; + y2f = 1; + } + if (*lp=='t') { + lp++; + tonly = 1; + } + SCRIPT_SKIP_SPACES + + + + float *arrays[MAX_GARRAY]; + uint8_t anum = 0; + uint16_t entries = 0; + uint16_t ipos = 0; + lp = gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos); + + if (anum>nanum) { + goto nextwebline; + } + + + + if (ctype=='T') { + if (anum && !(entries & 1)) { + WSContentSend_PD(SCRIPT_MSG_GTABLEa); + WSContentSend_PD(SCRIPT_MSG_GTABLEd); + char label[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, label, 0); + SCRIPT_SKIP_SPACES + for (uint32_t ind = 0; ind < anum; ind++) { + char lbl[16]; + GetTextIndexed(lbl, sizeof(lbl), ind, label); + for (uint32_t cnt = 0; cnt < entries; cnt += 2) { + WSContentSend_PD("['%s',",lbl); + float *fp = arrays[ind]; + uint32_t time = fp[cnt]; + WSContentSend_PD(SCRIPT_MSG_GOPT5, time / 60, time % 60); + WSContentSend_PD(","); + time = fp[cnt + 1]; + WSContentSend_PD(SCRIPT_MSG_GOPT5, time / 60, time % 60); + WSContentSend_PD("]"); + if (cnt < entries - 2) { WSContentSend_PD(","); } + } + if (ind < anum - 1) { WSContentSend_PD(","); } + } + snprintf_P(options,sizeof(options), SCRIPT_MSG_GOPT4); + } + } else { + + WSContentSend_PD(SCRIPT_MSG_GTABLEa); + lp = gc_send_labels(lp, anum); + + + + char label[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, label, 0); + SCRIPT_SKIP_SPACES + + int16_t divflg = 1; + int16_t todflg = -1; + if (!strncmp(label, "cnt", 3)) { + char *cp = &label[3]; + + todflg = strtol(cp, &cp, 10); + if (todflg>=entries) todflg = entries - 1; + if (*cp=='/') { + cp++; + divflg = strtol(cp, &cp, 10); + } + } else { + char *lp = label; + if (!strncmp(label, "wdh:", 4)) { + + todflg = -2; + lp += 4; + } + uint16 segments = 1; + for (uint32_t cnt = 0; cnt < strlen(lp); cnt++) { + if (lp[cnt]=='|') { + segments++; + } + } + divflg = entries / segments; + } + + uint32_t aind = ipos; + if (aind>=entries) aind = entries - 1; + for (uint32_t cnt = 0; cnt < entries; cnt++) { + WSContentSend_PD("['"); + char lbl[16]; + if (todflg>=0) { + sprintf(lbl, "%d", todflg / divflg); + todflg++; + if (todflg >= entries) { + todflg = 0; + } + } else { + if (todflg==-1) { + GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label); + } else { + + GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label + 4); + sprintf(lbl, "%s-%02d", lbl, aind % divflg); + } + } + WSContentSend_PD(lbl); + WSContentSend_PD("',"); + for (uint32_t ind = 0; ind < anum; ind++) { + char acbuff[32]; + float *fp = arrays[ind]; + f2char(fp[aind], glob_script_mem.script_dprec, glob_script_mem.script_lzero, acbuff); + WSContentSend_PD("%s", acbuff); + if (ind=entries) { + aind = 0; + } + } + + if (tonly) { + WSContentSend_PD("]);"); + goto nextwebline; + } + + char header[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, header, 0); + SCRIPT_SKIP_SPACES + + switch (ctype) { + case 't': + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT2); + break; + default: + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT1, header); + break; + } + + if (y2f) { + + SCRIPT_SKIP_SPACES + float max1; + lp = GetNumericArgument(lp, OPER_EQU, &max1, 0); + SCRIPT_SKIP_SPACES + float max2; + lp = GetNumericArgument(lp, OPER_EQU, &max2, 0); + SCRIPT_SKIP_SPACES + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT3, header, (uint32_t)max1, (uint32_t)max2, func); + } else { + SCRIPT_SKIP_SPACES + if (ctype!='g') { + if (*lp!=')') { + float max1; + lp = GetNumericArgument(lp, OPER_EQU, &max1, 0); + SCRIPT_SKIP_SPACES + float max2; + lp = GetNumericArgument(lp, OPER_EQU, &max2, 0); + SCRIPT_SKIP_SPACES + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT6, header, (uint32_t)max1, (uint32_t)max2, func); + } + } + } + + if (ctype=='g') { + float yellowFrom; + lp = GetNumericArgument(lp, OPER_EQU, &yellowFrom, 0); + SCRIPT_SKIP_SPACES + float redFrom; + lp = GetNumericArgument(lp, OPER_EQU, &redFrom, 0); + SCRIPT_SKIP_SPACES + float maxValue; + lp = GetNumericArgument(lp, OPER_EQU, &maxValue, 0); + SCRIPT_SKIP_SPACES + float redTo = maxValue; + float yellowTo = redFrom; + snprintf_P(options, sizeof(options), SCRIPT_MSG_GAUGEOPT, (uint32_t)maxValue, (uint32_t)redFrom, (uint32_t)redTo, + (uint32_t)yellowFrom, (uint32_t)yellowTo); + } + } + WSContentSend_PD(SCRIPT_MSG_GTABLEb, options); + WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex); + chartindex++; + } else { + WSContentSend_PD(PSTR("%s"), lin); + } +#else + lin++; + WSContentSend_PD(PSTR("%s"), lin); + } else { + +#endif + } + } + } +nextwebline: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } +} +#endif + + +#ifdef USE_SENDMAIL + +void script_send_email_body(void(*func)(char *)) { +uint8_t msect = Run_Scripter(">m", -2, 0); + if (msect==99) { + char tmp[256]; + char *lp = glob_script_mem.section_ptr + 2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + + func(tmp); + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } else { + + func((char*)"*"); + } +} +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT +void ScriptJsonAppend(void) { + uint8_t web_script = Run_Scripter(">J", -2, 0); + if (web_script==99) { + char tmp[256]; + char *lp = glob_script_mem.section_ptr + 2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + ResponseAppend_P(PSTR("%s"), tmp); + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } +} +#endif + + +bool RulesProcessEvent(char *json_event) { + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E", 2, json_event); + return true; +} + +#ifdef ESP32 +#ifdef USE_SCRIPT_TASK + +#ifndef STASK_STACK +#define STASK_STACK 8192 +#endif + +#if 1 + +struct ESP32_Task { + uint16_t task_timer; + TaskHandle_t task_t; +} esp32_tasks[2]; + + +void script_task1(void *arg) { + + + while (1) { + + + + + + delay(esp32_tasks[0].task_timer); + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">t1", 3, 0); + } + } +} + +void script_task2(void *arg) { + + + while (1) { + + + + + + delay(esp32_tasks[1].task_timer); + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">t2", 3, 0); + } + } +} +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio) { + + BaseType_t res = 0; + if (core > 1) { core = 1; } + if (num == 1) { + if (esp32_tasks[0].task_t) { vTaskDelete(esp32_tasks[0].task_t); } + res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, prio, &esp32_tasks[0].task_t, core); + esp32_tasks[0].task_timer = time; + } else { + if (esp32_tasks[1].task_t) { vTaskDelete(esp32_tasks[1].task_t); } + res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, prio, &esp32_tasks[1].task_t, core); + esp32_tasks[1].task_timer = time; + } + return res; +} +#else + +uint16_t task_timer1; +uint16_t task_timer2; +TaskHandle_t task_t1; +TaskHandle_t task_t2; + +void script_task1(void *arg) { + while (1) { + delay(task_timer1); + Run_Scripter(">t1", 3, 0); + } +} + +void script_task2(void *arg) { + while (1) { + delay(task_timer2); + Run_Scripter(">t2", 3, 0); + } +} + +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio) { + + BaseType_t res = 0; + if (core > 1) { core = 1; } + if (num == 1) { + if (task_t1) { vTaskDelete(task_t1); } + res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, prio, &task_t1, core); + task_timer1 = time; + } else { + if (task_t2) { vTaskDelete(task_t2); } + res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, prio, &task_t2, core); + task_timer2 = time; + } + return res; +} +#endif + +#endif +#endif + +#ifdef SCRIPT_GET_HTTPS_JP +#ifdef ESP8266 +#include "WiFiClientSecureLightBearSSL.h" +#else +#include +#endif + + +uint32_t call2https(const char *host, const char *path) { + if (global_state.wifi_down) return 1; + uint32_t status = 0; +#ifdef ESP32 + WiFiClientSecure *httpsClient; + httpsClient = new WiFiClientSecure; +#else + BearSSL::WiFiClientSecure_light *httpsClient; + httpsClient = new BearSSL::WiFiClientSecure_light(1024, 1024); +#endif + + httpsClient->setTimeout(1500); + + uint32_t retry = 0; + while ((!httpsClient->connect(host, 443)) && (retry < 5)) { + delay(100); + retry++; + } + if (retry == 5) { + return 2; + } + String request = String("GET ") + path + + " HTTP/1.1\r\n" + + "Host: " + host + + "\r\n" + "Connection: close\r\n\r\n"; + httpsClient->print(request); + + while (httpsClient->connected()) { + String line = httpsClient->readStringUntil('\n'); + if (line == "\r") { + break; + } + } + String result; + while (httpsClient->available()) { + String line = httpsClient->readStringUntil('\n'); + if (line!="") { + result += line; + } + } + httpsClient->stop(); + delete httpsClient; + Run_Scripter(">jp", 3, (char*)result.c_str()); + return 0; +} +#endif + + +void cpy2lf(char *dst, uint32_t dstlen, char *src) { + for (uint32_t cnt=0; cnt0) glob_script_mem.script_ram[len_decompressed] = 0; + + bitWrite(Settings.rule_once, 6, 1); + +#else + + bitWrite(Settings.rule_once, 6, 0); +#endif + +#ifdef USE_BUTTON_EVENT + for (uint32_t cnt = 0; cnt < MAX_KEYS; cnt++) { + script_button[cnt] = -1; + } +#endif + +#ifdef EEP_SCRIPT_SIZE + if (eeprom_init(EEP_SCRIPT_SIZE)) { + + char *script; + script = (char*)calloc(EEP_SCRIPT_SIZE + 4, 1); + if (!script) break; + glob_script_mem.script_ram = script; + glob_script_mem.script_size = EEP_SCRIPT_SIZE; + EEP_READ(0, EEP_SCRIPT_SIZE, script); + if (*script==0xff) { + memset(script, EEP_SCRIPT_SIZE, 0); + } + script[EEP_SCRIPT_SIZE - 1] = 0; + + glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; + + glob_script_mem.flags = 1; + } +#endif + + +#ifdef USE_SCRIPT_FATFS + +#if USE_SCRIPT_FATFS>=0 + +#ifdef ESP32 + if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO) && PinUsed(GPIO_SPI_CLK)) { + SPI.begin(Pin(GPIO_SPI_CLK),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_MOSI), -1); + } +#endif + fsp = &SD; + if (SD.begin(USE_SCRIPT_FATFS)) { +#else + +#ifdef ESP32 + + + fsp = &FFat; + if (FFat.begin(true)) { +#else + + fsp = &LittleFS; + if (fsp->begin()) { +#endif + +#endif + AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount OK!")); + + glob_script_mem.script_sd_found = 1; + char *script; + script = (char*)calloc(FAT_SCRIPT_SIZE + 4, 1); + if (!script) break; + glob_script_mem.script_ram = script; + glob_script_mem.script_size = FAT_SCRIPT_SIZE; + if (fsp->exists(FAT_SCRIPT_NAME)) { + File file = fsp->open(FAT_SCRIPT_NAME, FILE_READ); + file.read((uint8_t*)script, FAT_SCRIPT_SIZE); + file.close(); + } + script[FAT_SCRIPT_SIZE - 1] = 0; + + glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; + + glob_script_mem.flags = 1; + + } else { + AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount failed!")); + glob_script_mem.script_sd_found = 0; + } +#endif + + +#ifdef LITTLEFS_SCRIPT_SIZE + +#ifdef ESP32 + + fsp = &SPIFFS; + + +#else + + fsp = &LittleFS; +#endif + char *script; + script = (char*)calloc(LITTLEFS_SCRIPT_SIZE + 4, 1); + if (!script) break; + LoadFile("/script.txt", (uint8_t*)script, LITTLEFS_SCRIPT_SIZE); + + glob_script_mem.script_ram = script; + glob_script_mem.script_size = LITTLEFS_SCRIPT_SIZE; + script[LITTLEFS_SCRIPT_SIZE-1] = 0; + + glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; + glob_script_mem.flags = 1; +#endif + + + if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') { + + memset(glob_script_mem.script_ram, 0 ,glob_script_mem.script_size); + strcpy_P(glob_script_mem.script_ram, PSTR(">D\nscript error must start with >D")); + bitWrite(Settings.rule_enabled, 0, 0); + } + + + { uint32_t ptr = (uint32_t)glob_script_mem.script_pram; + ptr &= 0xfffffffc; + ptr += 4; + glob_script_mem.script_pram = (uint8_t*)ptr; + glob_script_mem.script_pram_size -= 4; + } + + if (bitRead(Settings.rule_enabled, 0)) Init_Scripter(); + break; + case FUNC_INIT: + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">B\n", 3, 0); + fast_script = Run_Scripter(">F", -2, 0); +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + Script_Check_Hue(0); +#endif + } + break; + case FUNC_EVERY_100_MSECOND: + ScripterEvery100ms(); + break; + case FUNC_EVERY_SECOND: + ScriptEverySecond(); + break; + case FUNC_COMMAND: + result = ScriptCommand(); + break; + case FUNC_SET_POWER: +#ifdef SCRIPT_POWER_SECTION + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P", 2, 0); +#else + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">E", 2, 0); + result = event_handeled; + } +#endif + break; + case FUNC_RULES_PROCESS: + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">E", 2, mqtt_data); + result = event_handeled; + } + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_RULES); + break; +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_ADD_MAIN_BUTTON: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow('$'); +#ifdef SCRIPT_FULL_WEBPAGE + uint8_t web_script = Run_Scripter(">w", -2, 0); + if (web_script==99) { + char bname[48]; + cpy2lf(bname, sizeof(bname), glob_script_mem.section_ptr + 3); + WSContentSend_PD(HTTP_WEB_FULL_DISPLAY, bname); + Webserver->on("/sfd", ScriptFullWebpage); +#ifdef USE_SCRIPT_FATFS + Webserver->onNotFound(ScriptGetSDCard); +#endif + } +#endif + } + break; +#endif + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); + Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); + Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);}, script_upload_start); + Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess); + +#ifdef USE_SCRIPT_FATFS + Webserver->on("/u13", HTTP_POST,[]() { Webserver->sendHeader("Location","/u13");Webserver->send(303);}, script_upload); + Webserver->on("/u13", HTTP_GET, ScriptFileUploadSuccess); + Webserver->on("/upl", HTTP_GET, Script_FileUploadConfiguration); +#endif + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">R", 2, 0); + Scripter_save_pvars(); + } +#ifdef USE_SCRIPT_GLOBVARS + Script_Stop_UDP(); +#endif + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + if (bitRead(Settings.rule_enabled, 0)) { + result = ScriptMqttData(); + } + break; +#endif +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_SENSOR: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow(0); + } + break; +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT + case FUNC_JSON_APPEND: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptJsonAppend(); + } + break; +#endif + +#ifdef USE_BUTTON_EVENT + case FUNC_BUTTON_PRESSED: + if (bitRead(Settings.rule_enabled, 0)) { + if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { + script_button[XdrvMailbox.index] = XdrvMailbox.payload; + Run_Scripter(">b", 2, 0); + } + } + break; +#endif + +#ifdef USE_SCRIPT_GLOBVARS + case FUNC_LOOP: + Script_PollUdp(); + break; +#endif + + } + return result; +} + + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_11_knx.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_11_knx.ino" +#ifdef USE_KNX +# 51 "/workspace/Tasmota/tasmota/xdrv_11_knx.ino" +#define XDRV_11 11 + +#include + +address_t KNX_physs_addr; +address_t KNX_addr; + +#define KNX_Empty 255 + +#define TOGGLE_INHIBIT_TIME 15 + +float last_temp; +float last_hum; +uint8_t toggle_inhibit; + +typedef struct __device_parameters +{ + uint8_t type; + + + + + bool show; + + bool last_state; + + callback_id_t CB_id; + + + + + +} device_parameters_t; + + +device_parameters_t device_param[] = { + { 1, false, false, KNX_Empty }, + { 2, false, false, KNX_Empty }, + { 3, false, false, KNX_Empty }, + { 4, false, false, KNX_Empty }, + { 5, false, false, KNX_Empty }, + { 6, false, false, KNX_Empty }, + { 7, false, false, KNX_Empty }, + { 8, false, false, KNX_Empty }, + { 9, false, false, KNX_Empty }, + { 10, false, false, KNX_Empty }, + { 11, false, false, KNX_Empty }, + { 12, false, false, KNX_Empty }, + { 13, false, false, KNX_Empty }, + { 14, false, false, KNX_Empty }, + { 15, false, false, KNX_Empty }, + { 16, false, false, KNX_Empty }, + { KNX_TEMPERATURE, false, false, KNX_Empty }, + { KNX_HUMIDITY , false, false, KNX_Empty }, + { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, + { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, + { KNX_ENERGY_POWER , false, false, KNX_Empty }, + { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, + { KNX_ENERGY_DAILY , false, false, KNX_Empty }, + { KNX_ENERGY_START , false, false, KNX_Empty }, + { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, + { KNX_SLOT1 , false, false, KNX_Empty }, + { KNX_SLOT2 , false, false, KNX_Empty }, + { KNX_SLOT3 , false, false, KNX_Empty }, + { KNX_SLOT4 , false, false, KNX_Empty }, + { KNX_SLOT5 , false, false, KNX_Empty }, + { KNX_SCENE , false, false, KNX_Empty }, + { KNX_Empty, false, false, KNX_Empty} +}; + + +const char * device_param_ga[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_SENSOR_BUTTON " 1", + D_SENSOR_BUTTON " 2", + D_SENSOR_BUTTON " 3", + D_SENSOR_BUTTON " 4", + D_SENSOR_BUTTON " 5", + D_SENSOR_BUTTON " 6", + D_SENSOR_BUTTON " 7", + D_SENSOR_BUTTON " 8", + D_TEMPERATURE , + D_HUMIDITY , + D_VOLTAGE , + D_CURRENT , + D_POWERUSAGE , + D_POWER_FACTOR , + D_ENERGY_TODAY , + D_ENERGY_YESTERDAY , + D_ENERGY_TOTAL , + D_KNX_TX_SLOT " 1", + D_KNX_TX_SLOT " 2", + D_KNX_TX_SLOT " 3", + D_KNX_TX_SLOT " 4", + D_KNX_TX_SLOT " 5", + D_KNX_TX_SCENE , + nullptr +}; + + +const char *device_param_cb[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_TIMER_OUTPUT " 1 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 2 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 3 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 4 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 5 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 6 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 7 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, + D_REPLY " " D_TEMPERATURE, + D_REPLY " " D_HUMIDITY, + D_REPLY " " D_VOLTAGE , + D_REPLY " " D_CURRENT , + D_REPLY " " D_POWERUSAGE , + D_REPLY " " D_POWER_FACTOR , + D_REPLY " " D_ENERGY_TODAY , + D_REPLY " " D_ENERGY_YESTERDAY , + D_REPLY " " D_ENERGY_TOTAL , + D_KNX_RX_SLOT " 1", + D_KNX_RX_SLOT " 2", + D_KNX_RX_SLOT " 3", + D_KNX_RX_SLOT " 4", + D_KNX_RX_SLOT " 5", + D_KNX_RX_SCENE , + nullptr +}; + + +#define D_PRFX_KNX "Knx" +#define D_CMND_KNXTXCMND "Tx_Cmnd" +#define D_CMND_KNXTXVAL "Tx_Val" +#define D_CMND_KNX_ENABLED "_Enabled" +#define D_CMND_KNX_ENHANCED "_Enhanced" +#define D_CMND_KNX_PA "_PA" +#define D_CMND_KNX_GA "_GA" +#define D_CMND_KNX_CB "_CB" +#define D_CMND_KNXTXSCENE "Tx_Scene" + + +const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" + D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB "|" D_CMND_KNXTXSCENE ; + +void (* const KnxCommand[])(void) PROGMEM = { + &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb, &CmndKnxTxScene }; + +uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] == param ) + { + if ( Settings.knx_GA_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] == param ) + { + if ( Settings.knx_CB_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) +{ + + if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } + if ( GA_FNUM == 0 && GA_AREA == 0 && GA_FDEF == 0 ) { return; } + + + Settings.knx_GA_param[Settings.knx_GA_registered] = GAop; + KNX_addr.ga.area = GA_FNUM; + KNX_addr.ga.line = GA_AREA; + KNX_addr.ga.member = GA_FDEF; + Settings.knx_GA_addr[Settings.knx_GA_registered] = KNX_addr.value; + + Settings.knx_GA_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), + Settings.knx_GA_registered, + device_param_ga[GAop-1], + GA_FNUM, GA_AREA, GA_FDEF ); +} + + +void KNX_DEL_GA( uint8_t GAnum ) +{ + + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + Settings.knx_GA_param[GAnum-1] = 0; + + if (GAnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_GA_registered - 1); + } + else if (GAnum == Settings.knx_GA_registered) + { + + } + else + { + + + + + dest_offset = GAnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_GA_registered - GAnum); + } + + if (len > 0) + { + memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_GA_registered--; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), + GAnum ); +} + + +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) +{ + + if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } + if ( CB_FNUM == 0 && CB_AREA == 0 && CB_FDEF == 0 ) { return; } + + + if ( device_param[CBop-1].CB_id == KNX_Empty ) + { + + device_param[CBop-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[CBop-1]); + + + + + } + + Settings.knx_CB_param[Settings.knx_CB_registered] = CBop; + KNX_addr.ga.area = CB_FNUM; + KNX_addr.ga.line = CB_AREA; + KNX_addr.ga.member = CB_FDEF; + Settings.knx_CB_addr[Settings.knx_CB_registered] = KNX_addr.value; + + knx.callback_assign( device_param[CBop-1].CB_id, KNX_addr ); + + Settings.knx_CB_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), + Settings.knx_CB_registered, + CB_FNUM, CB_AREA, CB_FDEF, + device_param_cb[CBop-1] ); +} + + +void KNX_DEL_CB( uint8_t CBnum ) +{ + uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + knx.callback_unassign(CBnum-1); + Settings.knx_CB_param[CBnum-1] = 0; + + if (CBnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_CB_registered - 1); + } + else if (CBnum == Settings.knx_CB_registered) + { + + } + else + { + + + + + dest_offset = CBnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_CB_registered - CBnum); + } + + if (len > 0) + { + memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_CB_registered--; + + + if ( KNX_CB_Search( oldparam ) == KNX_Empty ) { + knx.callback_deregister( device_param[oldparam-1].CB_id ); + device_param[oldparam-1].CB_id = KNX_Empty; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); +} + + +bool KNX_CONFIG_NOT_MATCH(void) +{ + + for (uint32_t i = 0; i < KNX_MAX_device_param; ++i) + { + if ( !device_param[i].show ) { + + + + if ( KNX_GA_Search(i+1) != KNX_Empty ) { return true; } + + if ( i < 8 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + if ( KNX_CB_Search(i+9) != KNX_Empty ) { return true; } + } + + if ( i > 15 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + } + } + } + + + for (uint32_t i = 0; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] != 0 ) + { + if ( Settings.knx_GA_addr[i] == 0 ) + { + return true; + } + } + } + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] != 0 ) + { + if ( Settings.knx_CB_addr[i] == 0 ) + { + return true; + } + } + } + + return false; +} + + +void KNXStart(void) +{ + knx.start(nullptr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); +} + + +void KNX_INIT(void) +{ + + if (Settings.knx_GA_registered > MAX_KNX_GA) { Settings.knx_GA_registered = MAX_KNX_GA; } + if (Settings.knx_CB_registered > MAX_KNX_CB) { Settings.knx_CB_registered = MAX_KNX_CB; } + + + KNX_physs_addr.value = Settings.knx_physsical_addr; + knx.physical_address_set( KNX_physs_addr ); +# 477 "/workspace/Tasmota/tasmota/xdrv_11_knx.ino" + for (uint32_t i = 0; i < devices_present; ++i) + { + device_param[i].show = true; + } + for (uint32_t i = GPIO_SWT1; i < GPIO_SWT1 + 4; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1; i < GPIO_KEY1 + 4; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } + } + for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT1_NP + 4; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY1_NP + 4; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } + } + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#ifdef USE_DS18x20 + if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#endif + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + +#if defined(USE_ENERGY_SENSOR) + + if ( energy_flg != ENERGY_NONE ) { + device_param[KNX_ENERGY_POWER-1].show = true; + device_param[KNX_ENERGY_DAILY-1].show = true; + device_param[KNX_ENERGY_START-1].show = true; + device_param[KNX_ENERGY_TOTAL-1].show = true; + device_param[KNX_ENERGY_VOLTAGE-1].show = true; + device_param[KNX_ENERGY_CURRENT-1].show = true; + device_param[KNX_ENERGY_POWERFACTOR-1].show = true; + } +#endif + +#ifdef USE_RULES + device_param[KNX_SLOT1-1].show = true; + device_param[KNX_SLOT2-1].show = true; + device_param[KNX_SLOT3-1].show = true; + device_param[KNX_SLOT4-1].show = true; + device_param[KNX_SLOT5-1].show = true; + device_param[KNX_SCENE-1].show = true; +#endif + + + if (KNX_CONFIG_NOT_MATCH()) { + Settings.knx_GA_registered = 0; + Settings.knx_CB_registered = 0; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); + } + + + + + uint8_t j; + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + j = Settings.knx_CB_param[i]; + if ( j > 0 ) + { + device_param[j-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[j-1]); + + + + KNX_addr.value = Settings.knx_CB_addr[i]; + knx.callback_assign( device_param[j-1].CB_id, KNX_addr ); + } + } +} + + +void KNX_CB_Action(message_t const &msg, void *arg) +{ + device_parameters_t *chan = (device_parameters_t *)arg; + if (!(Settings.flag.knx_enabled)) { return; } + + char tempchar[33]; + + if (msg.data_len == 1) { + + sprintf(tempchar,"%d",msg.data[0]); + } else if (chan->type == KNX_SCENE) { + + uint8_t tempvar = knx.data_to_1byte_uint(msg.data); + dtostrfd(tempvar,0,tempchar); + } else { + + float tempvar = knx.data_to_2byte_float(msg.data); + dtostrfd(tempvar,2,tempchar); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), + msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, + (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, + tempchar, + device_param_cb[(chan->type)-1]); + + switch (msg.ct) + { + case KNX_CT_WRITE: + if (chan->type < 9) + { + ExecuteCommandPower(chan->type, msg.data[0], SRC_KNX); + } + else if (chan->type < 17) + { + if (!toggle_inhibit) { + ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + if (msg.data_len == 1) { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); + } else { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); + } + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } + else if (chan->type == KNX_SCENE) + { + if (!toggle_inhibit) { + char command[25]; + + snprintf_P(command, sizeof(command), PSTR("event KNX_SCENE=%s"), tempchar); + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + + case KNX_CT_READ: + if (chan->type < 9) + { + knx.answer_1bit(msg.received_on, chan->last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_1bit(msg.received_on, chan->last_state); + knx.answer_1bit(msg.received_on, chan->last_state); + } + } + else if (chan->type == KNX_TEMPERATURE) + { + knx.answer_2byte_float(msg.received_on, last_temp); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_temp); + knx.answer_2byte_float(msg.received_on, last_temp); + } + } + else if (chan->type == KNX_HUMIDITY) + { + knx.answer_2byte_float(msg.received_on, last_hum); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_hum); + knx.answer_2byte_float(msg.received_on, last_hum); + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + } +} + + +void KnxUpdatePowerState(uint8_t device, power_t state) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + device_param[device -1].last_state = bitRead(state, device -1); + + + uint8_t i = KNX_GA_Search(device); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device -1], device_param[device -1].last_state, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device, i + 1); + } +} + + +void KnxSendButtonPower(void) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; +# 714 "/workspace/Tasmota/tasmota/xdrv_11_knx.ino" + uint8_t i = KNX_GA_Search(device + 8); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(state == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(state == 0)); + knx.write_1bit(KNX_addr, !(state == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device + 7], !(state == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device + 8, i + 1); + } + +} + + +void KnxSensor(uint8_t sensor_type, float value) +{ + if (sensor_type == KNX_TEMPERATURE) + { + last_temp = value; + } else if (sensor_type == KNX_HUMIDITY) + { + last_hum = value; + } + + if (!(Settings.flag.knx_enabled)) { return; } + + uint8_t i = KNX_GA_Search(sensor_type); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_2byte_float(KNX_addr, value); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, value); + knx.write_2byte_float(KNX_addr, value); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), + device_param_ga[sensor_type -1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(sensor_type, i+1); + } +} + + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU +const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; + +const char HTTP_BTN_MENU_KNX[] PROGMEM = + "

"; + +const char HTTP_FORM_KNX[] PROGMEM = + "
" + " " D_KNX_PARAMETERS " " + "
" + "
" + "" D_KNX_PHYSICAL_ADDRESS " " + " . " + " . " + "" + "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" + "

" + + "
" + "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" + + " / " + " / " + " "; + +const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = + "

" + ""; + +const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = + "" + ""; + +const char HTTP_FORM_KNX3[] PROGMEM = + "
%s -> %d / %d / %d

" + "
" + "" D_KNX_GROUP_ADDRESS_TO_READ "
"; + +const char HTTP_FORM_KNX4[] PROGMEM = + "-> -> ")); + WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_AREA", "GA_FDEF"); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + if ( Settings.knx_GA_param[i] ) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); + } + } + + WSContentSend_P(HTTP_FORM_KNX3); + WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_AREA", "CB_FDEF"); + WSContentSend_P(HTTP_FORM_KNX4); + + uint8_t j; + for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) + { + + if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } + if ( i == 8 ) { j = 0; } + if ( device_param[j].show ) + { + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); + } + } + WSContentSend_P(PSTR(" ")); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); + + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + if ( Settings.knx_CB_param[i] ) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); + } + } + WSContentSend_P(PSTR("
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + } + +} + + +void KNX_Save_Settings(void) +{ + String stmp; + address_t KNX_addr; + + Settings.flag.knx_enabled = Webserver->hasArg("b1"); + Settings.flag.knx_enable_enhancement = Webserver->hasArg("b2"); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), + Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); + + stmp = Webserver->arg("area"); + KNX_addr.pa.area = stmp.toInt(); + stmp = Webserver->arg("line"); + KNX_addr.pa.line = stmp.toInt(); + stmp = Webserver->arg("member"); + KNX_addr.pa.member = stmp.toInt(); + Settings.knx_physsical_addr = KNX_addr.value; + knx.physical_address_set( KNX_addr ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), + KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), + Settings.knx_GA_registered ); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), + i+1, device_param_ga[Settings.knx_GA_param[i]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), + Settings.knx_CB_registered ); + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), + i+1, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, + device_param_cb[Settings.knx_CB_param[i]-1] ); + } +} + +#endif +#endif + + + + + +void CmndKnxTxCmnd(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxTxVal(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + + float tempvar = CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar,2,XdrvMailbox.data); + + knx.write_2byte_float(KNX_addr, tempvar); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, tempvar); + knx.write_2byte_float(KNX_addr, tempvar); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxTxScene(void) +{ + if ( (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled ) { + + uint8_t i = KNX_GA_Search(KNX_SCENE); + if ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + + uint8_t tempvar = TextToInt(XdrvMailbox.data); + dtostrfd(tempvar,0,XdrvMailbox.data); + + knx.write_1byte_uint(KNX_addr, tempvar); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1byte_uint(KNX_addr, tempvar); + knx.write_1byte_uint(KNX_addr, tempvar); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), + device_param_ga[KNX_SCENE-1], XdrvMailbox.data, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + ResponseCmndIdxChar (XdrvMailbox.data); + } + } +} + +void CmndKnxEnabled(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enabled = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); +} + +void CmndKnxEnhanced(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); +} + +void CmndKnxPa(void) +{ + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ".") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); + int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); + int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); + + if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) + || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.pa.area = pa_area; + KNX_addr.pa.line = pa_line; + KNX_addr.pa.member = pa_member; + Settings.knx_physsical_addr = KNX_addr.value; + } + } + KNX_addr.value = Settings.knx_physsical_addr; + Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), + XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); +} + +void CmndKnxGa(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0)) + || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) + || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) + || (!device_param[ga_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = ga_area; + KNX_addr.ga.line = ga_line; + KNX_addr.ga.member = ga_member; + + if ( XdrvMailbox.index > Settings.knx_GA_registered ) { + Settings.knx_GA_registered ++; + XdrvMailbox.index = Settings.knx_GA_registered; + } + + Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { + KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_GA_registered ); + } + } +} + +void CmndKnxCb(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0)) + || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) + || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) + || (!device_param[cb_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = cb_area; + KNX_addr.ga.line = cb_line; + KNX_addr.ga.member = cb_member; + + if ( XdrvMailbox.index > Settings.knx_CB_registered ) { + Settings.knx_CB_registered ++; + XdrvMailbox.index = Settings.knx_CB_registered; + } + + Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { + KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_CB_registered ); + } + } +} + + + + + +bool Xdrv11(uint8_t function) +{ + bool result = false; + switch (function) { + case FUNC_LOOP: + if (!global_state.network_down) { knx.loop(); } + break; + case FUNC_EVERY_50_MSECOND: + if (toggle_inhibit) { + toggle_inhibit--; + } + break; + case FUNC_ANY_KEY: + KnxSendButtonPower(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_KNX); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/kn"), HandleKNXConfiguration); + break; +#endif +#endif + case FUNC_COMMAND: + result = DecodeCommand(kKnxCommands, KnxCommand); + break; + case FUNC_PRE_INIT: + KNX_INIT(); + break; + + + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_12_home_assistant.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_12_home_assistant.ino" +#ifdef USE_HOME_ASSISTANT + +#define XDRV_12 12 + + +const char kHAssJsonSensorTypes[] PROGMEM = + D_JSON_TEMPERATURE "|" D_JSON_DEWPOINT "|" D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" + D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" D_JSON_TOTAL_START_TIME "|" + D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|" + D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|Red|Green|Blue|CCT|" D_PROXIMITY "|"; + + +const char kHAssJsonSensorUnits[] PROGMEM = + "||||" + "VA|%|A|cm|Hz|%|lux|" + "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W| |" + "VAr|kWh|kWh|V|kg|kWh|" + "ppm|ppm|ppb|R|G|B|" D_UNIT_KELVIN "| |"; + +const char kHAssJsonSensorDevCla[] PROGMEM = + "dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|" + "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" + "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" + "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|ic\":\"mdi:progress-clock|" + "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power|" + "ic\":\"mdi:molecule-co2|ic\":\"mdi:molecule-co2|ic\":\"mdi:air-filter|" + "ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:temperature-kelvin|ic\":\"mdi:ruler|dev_cla\":\"illuminance|"; + + + +const char HASS_DISCOVER_BASE[] PROGMEM = + "{\"name\":\"%s\"," + "\"stat_t\":\"%s\""; + +const char HASS_DISCOVER_SENSOR[] PROGMEM = + ",\"unit_of_meas\":\"%s\",\"%s\"," + "\"frc_upd\":true," + "\"val_tpl\":\"{{value_json['%s']['%s']"; + +const char HASS_DISCOVER_SENSOR_LWT[] PROGMEM = + ",\"avty_t\":\"%s\"," + "\"pl_avail\":\"" MQTT_LWT_ONLINE "\"," + "\"pl_not_avail\":\"" MQTT_LWT_OFFLINE "\""; + +const char HASS_DISCOVER_RELAY[] PROGMEM = + ",\"cmd_t\":\"%s\"," + "\"val_tpl\":\"{{value_json.%s}}\"," + "\"pl_off\":\"%s\"," + "\"pl_on\":\"%s\""; + +const char HASS_DISCOVER_BIN_SWITCH[] PROGMEM = + ",\"val_tpl\":\"{{value_json.%s}}\"," + "\"frc_upd\":true," + "\"pl_on\":\"%s\"," + "\"pl_off\":\"%s\""; + +const char HASS_DISCOVER_BIN_PIR[] PROGMEM = + ",\"val_tpl\":\"{{value_json.%s}}\"," + "\"frc_upd\":true," + "\"pl_on\":\"%s\"," + "\"off_dly\":1"; + +const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM = + ",\"bri_cmd_t\":\"%s\"," + "\"bri_stat_t\":\"%s\"," + "\"bri_scl\":100," + "\"on_cmd_type\":\"%s\"," + "\"bri_val_tpl\":\"{{value_json.%s}}\""; + +const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = + ",\"rgb_cmd_t\":\"%s2\"," + "\"rgb_stat_t\":\"%s\"," + "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; + +const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = + ",\"whit_val_cmd_t\":\"%s\"," + "\"whit_val_stat_t\":\"%s\"," + "\"whit_val_scl\":100," + "\"whit_val_tpl\":\"{{value_json." D_CMND_WHITE "}}\""; + +const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = + ",\"clr_temp_cmd_t\":\"%s\"," + "\"clr_temp_stat_t\":\"%s\"," + "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; + +const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = + ",\"fx_cmd_t\":\"%s\"," + "\"fx_stat_t\":\"%s\"," + "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," + "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; + +const char HASS_DISCOVER_SHUTTER_BASE[] PROGMEM = + ",\"cmd_t\":\"%s\"," + "\"pl_open\":\"ShutterOpen%d\"," + "\"pl_cls\":\"ShutterClose%d\"," + "\"pl_stop\":\"ShutterStop%d\"," + "\"opt\":false," + "\"ret\":false," + "\"qos\":1"; + +const char HASS_DISCOVER_SHUTTER_POS[] PROGMEM = + ",\"pos_t\":\"%s%d\"," + "\"pos_clsd\":0," + "\"pos_open\":100," + "\"set_pos_t\":\"%s%d\""; + +const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = + ",\"json_attr_t\":\"%s\"," + "\"unit_of_meas\":\"%%\"," + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," + "\"ic\":\"mdi:information-outline\""; + +const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]," + "\"name\":\"%s\"," + "\"mdl\":\"%s\"," + "\"sw\":\"%s%s\"," + "\"mf\":\"Tasmota\"}"; + +const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]}"; + +const char HASS_TRIGGER_TYPE[] PROGMEM = + "{\"atype\":\"trigger\"," + "\"t\":\"%sT\"," + "\"pl\":\"{\\\"TRIG\\\":\\\"%s\\\"}\"," + "\"type\":\"%s\"," + "\"stype\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]}}"; + +const char kHAssTriggerType[] PROGMEM = + "none|button_short_press|button_long_press|button_double_press"; + +const char kHAssTriggerTypeButtons[] PROGMEM = + "|button_short_press|button_double_press|button_triple_press|button_quadruple_press|button_quintuple_press|button_long_press|"; + +const char kHAssTriggerStringButtons[] PROGMEM = + "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD|"; + +const char kHAssRelayType[] PROGMEM = + "|RL|LI|SHT|FAN"; + +const char kHAssError1[] PROGMEM = + "HASS: MQTT discovery failed due to too long topic or device/friendly name. Please shorten topic and/or device/friendly name. Failed to format"; + +const char kHAssError2[] PROGMEM = + "HASS: The configuration of the Relays is wrong, there is a Light that is using an index higher than the number of the validated relay.\n " + "The Relays have priority over the Lights, an incorrect order could lead to an erroneous Light control.\n " + "Please update your configuration to avoid inconsistent results.\n " + "Entities for Relays and Lights will not be available in Home Assistant until the configuration will be updated."; + +const char kHAssError3[] PROGMEM = + "HASS: Unable to create one or more entities from Json data, please check your configuration. Failed to parse"; + +uint8_t hass_init_step = 0; +uint8_t hass_mode = 0; +int hass_tele_period = 0; + + + +const char HASS_DISCOVER_DEVICE[] PROGMEM = + "{\"ip\":\"%s\"," + "\"dn\":\"%s\"," + "\"fn\":[%s]," + "\"hn\":\"%s\"," + "\"mac\":\"%s\"," + "\"md\":\"%s\"," + "\"ty\":%d," + "\"ofln\":\"" MQTT_LWT_OFFLINE "\"," + "\"onln\":\"" MQTT_LWT_ONLINE "\"," + "\"state\":[\"%s\",\"%s\",\"%s\",\"%s\"]," + "\"sw\":\"%s\"," + "\"t\":\"%s\"," + "\"ft\":\"%s\"," + "\"tp\":[\"%s\",\"%s\",\"%s\"]," + "\"rl\":[%s],\"swc\":[%s],\"btn\":[%s]," + "\"so\":{\"11\":%d,\"13\":%d,\"17\":%d,\"20\":%d," + "\"30\":%d,\"68\":%d,\"73\":%d,\"80\":%d,\"82\":%d}," + "\"lk\":%d,\"lt_st\":%d,\"ver\":1}"; + +typedef struct HASS { + uint16_t Relay[MAX_RELAYS]; + char RelLst[MAX_RELAYS*2]; + bool RelPst; +} HASS; + +void HassDiscoveryRelays(struct HASS &Hass) +{ + Hass = {.Relay={0,0,0,0,0,0,0,0}, .RelLst={'\0'}}; + uint16_t Shutter[8] = {0,0,0,0,0,0,0,0}; + uint8_t lightidx = MAX_RELAYS + 1; + bool iFan = false; + + Hass.RelPst = devices_present > 0; + +#ifdef ESP8266 + if (SONOFF_IFAN02 == my_module_type || SONOFF_IFAN03 == my_module_type) { iFan = true;} +#endif + + if (Light.subtype > LST_NONE) { + if (!light_controller.isCTRGBLinked()) { + lightidx = devices_present - 2; + } else { + lightidx = devices_present - 1; + } + } + + if (Light.device > 0 && Settings.flag3.pwm_multi_channels) { + lightidx = devices_present - Light.subtype; + } + + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + + if (i < devices_present) { + +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t k = 0; k < MAX_SHUTTERS; k++) { + if (0 == Settings.shutter_startrelay[k]) { + break; + } else { + if (Settings.shutter_startrelay[k] > 0 && Settings.shutter_startrelay[k] <= MAX_RELAYS) { + Shutter[Settings.shutter_startrelay[k]-1] = Shutter[Settings.shutter_startrelay[k]] = 1; + } + } + } + } +#endif + + if (Shutter[i] != 0) { + Hass.Relay[i] = 3; + } else { + if (i >= lightidx || (iFan && i == 0)) { + Hass.Relay[i] = 2; + } else { + if (!iFan) { + Hass.Relay[i] = 1; + } + } + } + } + snprintf_P(Hass.RelLst, sizeof(Hass.RelLst), PSTR("%s%s%d"), Hass.RelLst, (i > 0 ? "," : ""), Hass.Relay[i]); + } +} + +void NewHAssDiscovery(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[200]; + char stemp3[TOPSZ]; + char stemp4[TOPSZ]; + char unique_id[30]; + char relays[TOPSZ]; + char *state_topic = stemp1; + bool SerialButton = false; + bool TuyaMod = false; + + stemp2[0] = '\0'; + struct HASS Hass; + HassDiscoveryRelays(Hass); + + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + char fname[TOPSZ]; + snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : "null"); + } + + stemp3[0] = '\0'; + + auto discover_switches = ((KeyTopicActive(1) && (strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic))) || !Hass.RelPst); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & discover_switches) ? Settings.switchmode[i] : -1); + } + + stemp4[0] = '\0'; + + for (uint32_t i = 0; i < MAX_KEYS; i++) { + +#ifdef ESP8266 + if (i == 0 && (SONOFF_DUAL == my_module_type )) { SerialButton = true; } + if (TUYA_DIMMER == my_module_type || SK03_TUYA == my_module_type) { TuyaMod = true; } +#endif + + snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%d"), stemp4, (i > 0 ? "," : ""), (SerialButton ? 1 : (PinUsed(GPIO_KEY1, i)) & Settings.flag3.mqtt_buttons)); + SerialButton = false; + } + + mqtt_data[0] = '\0'; + + + String mac_address = WiFi.macAddress(); + mac_address.replace(":", ""); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str()); + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), unique_id); + + + masterlog_level = 4; + if (!Settings.flag.hass_discovery) { + Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME), + stemp2, my_hostname, unique_id, ModuleName().c_str(), TuyaMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), + my_version, mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, Hass.RelLst, stemp3, stemp4, Settings.flag.button_swap, + Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, + Settings.flag3.mqtt_buttons, Settings.flag3.shutter_mode, Settings.flag4.alexa_ct_range, light_controller.isCTRGBLinked(), Light.subtype); + } + MqttPublish(stopic, true); + + if (!Settings.flag.hass_discovery) { + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/sensors"), unique_id); + Response_P(PSTR("{\"sn\":")); + MqttShowSensor(); + ResponseAppend_P(PSTR(",\"ver\":1}")); + MqttPublish(stopic, true); + } + masterlog_level = 0; +} + + + +void TryResponseAppend_P(const char *format, ...) +{ + va_list args; + va_start(args, format); + char dummy[2]; + int dlen = vsnprintf_P(dummy, 1, format, args); + + int mlen = strlen(mqtt_data); + int slen = sizeof(mqtt_data) - 1 - mlen; + if (dlen >= slen) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s (%u/%u):"), kHAssError1, dlen, slen); + va_start(args, format); + vsnprintf_P(log_data, sizeof(log_data), format, args); + AddLog(LOG_LEVEL_ERROR); + } + else + { + va_start(args, format); + vsnprintf_P(mqtt_data + mlen, slen, format, args); + } + va_end(args); +} + +void HAssAnnounceRelayLight(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char stemp3[TOPSZ]; + char unique_id[30]; + + bool LightControl = light_controller.isCTRGBLinked(); + bool PwmMulti = Settings.flag3.pwm_multi_channels; + bool is_topic_light = false; + bool ind_light = false; + bool ct_light = false; + bool wt_light = false; + bool err_flag = false; + bool TuyaMod = false; + bool PwmMod = false; + bool FanMod = false; + uint8_t ShowTopic; + + uint8_t dimmer = 1; + uint8_t valid_relay = 0; + uint8_t max_lights = 1; + uint8_t TuyaRel = 0; + uint8_t TuyaRelInv = 0; + uint8_t TuyaDim = 0; + uint8_t shutter_mask = 0; + + #ifdef ESP8266 + if (PWM_DIMMER == my_module_type ) { PwmMod = true; } + if (SONOFF_IFAN02 == my_module_type || SONOFF_IFAN03 == my_module_type) { FanMod = true; } + if (SONOFF_DUAL == my_module_type) { valid_relay = 2; } + if (TUYA_DIMMER == my_module_type || SK03_TUYA == my_module_type) { TuyaMod = true; } + #endif + + + + if (PwmMulti) { max_lights = Light.subtype; } + + if (!LightControl) { + ind_light = true; + if (!PwmMulti) { max_lights = 2;} + } + +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (Settings.shutter_startrelay[i] > 0 && Settings.shutter_startrelay[i] <= MAX_RELAYS) { + bitSet(shutter_mask, Settings.shutter_startrelay[i] -1); + bitSet(shutter_mask, Settings.shutter_startrelay[i]); + } + } + } +#endif + + for (uint32_t i = 1; i <= MAX_RELAYS; i++) + { + +#ifdef USE_TUYA_MCU + TuyaRel = TuyaGetDpId((TUYA_MCU_FUNC_REL1+ i-1) + active_device - 1); + TuyaRelInv = TuyaGetDpId((TUYA_MCU_FUNC_REL1_INV+ i-1) + active_device - 1); + TuyaDim = TuyaGetDpId((TUYA_MCU_FUNC_DIMMER) + active_device - 1); +#endif + + masterlog_level = ShowTopic = 4; + + bool RelayX = PinUsed(GPIO_REL1, i-1) || (valid_relay >= i) || (TuyaRel > 0 && TuyaMod) || (TuyaRelInv > 0 && TuyaMod); + is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX || PwmMod || (TuyaDim > 0 && TuyaMod); + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "RL" : "LI", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "switch" : "light", unique_id); + MqttPublish(stopic, true); + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "LI" : "RL", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "light" : "switch", unique_id); + + if (bitRead(shutter_mask, i-1)) { + + } else if ((i < Light.device) && !RelayX) { + err_flag = true; + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s"), kHAssError2); + } else { + if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag ) + { + char name[TOPSZ]; + char value_template[33]; + char prefix[TOPSZ]; + char *command_topic = stemp1; + char *state_topic = stemp2; + char *availability_topic = stemp3; + + ShowTopic = 0; + + if (i > MAX_FRIENDLYNAMES) { + snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1); + } else { + snprintf_P(name, sizeof(name), PSTR ("%s"), SettingsText(SET_FRIENDLYNAME1 + i-1)); + } + + GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); + GetTopic_P(command_topic, CMND, mqtt_topic, value_template); + GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + Response_P(HASS_DISCOVER_BASE, name, state_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + + #ifdef USE_LIGHT + if (i >= Light.device) { + if (!RelayX || PwmMod || (TuyaDim > 0 && TuyaMod)) { + char *brightness_command_topic = stemp1; + strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); + char channel_num[9]; + if (PwmMulti) { + snprintf_P(channel_num, sizeof(channel_num), PSTR("Channel%d"), i); + } else { + if (!LightControl) { + snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "%d"), dimmer); + dimmer ++; + } else { + snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "")); + } + } + GetTopic_P(brightness_command_topic, CMND, mqtt_topic, channel_num); + TryResponseAppend_P(HASS_DISCOVER_BASE_LIGHT, brightness_command_topic, state_topic, stemp3, channel_num); + } + if ((ind_light && !PwmMulti) || LightControl) { + + if (Light.subtype >= LST_RGB) { + char *rgb_command_topic = stemp1; + + GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); + + char *effect_command_topic = stemp1; + GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); + } + if (LST_RGBW <= Light.subtype) { wt_light = true; } + if (LST_RGBCW == Light.subtype) { ct_light = true; } + } + + if ((!ind_light && ct_light) || (LST_COLDWARM == Light.subtype && + !PwmMulti && LightControl)) { + char *color_temp_command_topic = stemp1; + + GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); + ct_light = false; + } + if ((!ind_light && wt_light) || (LST_RGBW <= Light.subtype && + !PwmMulti && LightControl)) { + char *white_temp_command_topic = stemp1; + + GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); + wt_light = false; + } + ind_light = false; + max_lights--; + } + #endif + TryResponseAppend_P(PSTR("}")); + } + } + masterlog_level = ShowTopic; + MqttPublish(stopic, true); + } +} + +void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold, uint8_t single, uint8_t trg_start, uint8_t trg_end) +{ + + + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + char trigger2[8]; + uint8_t ShowTopic; + mqtt_data[0] = '\0'; + + for (uint8_t i = trg_start; i <= trg_end; i++) { + GetTextIndexed(trigger2, sizeof(trigger2), i, kHAssTriggerStringButtons); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d_%s"), ESP_getChipId(), key ? "SW" : "BTN", device + 1, key ? GetStateText(i) : trigger2); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id); + + masterlog_level = ShowTopic = 4; + + if (Settings.flag.hass_discovery && present) { + char name[TOPSZ]; + char value_template[33]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + char jsoname[8]; + ShowTopic = 0; + + GetPowerDevice(value_template, device + 1, sizeof(value_template), key + Settings.flag.device_index_enable); + snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); + GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + char param[21]; + char subtype[9]; + uint8_t pload = toggle; + if (key) { + if ((i == 2 && toggle != 0) || (i == 3 && hold != 0)) { + if (i == 3) { pload = hold; } + GetTextIndexed(param, sizeof(param), pload, kHAssTriggerType); + snprintf_P(subtype, sizeof(subtype), PSTR("switch_%d"), device + 1); + Response_P(HASS_TRIGGER_TYPE, state_topic, GetStateText(i), param, subtype, ESP_getChipId()); + } else { mqtt_data[0] = '\0'; } + } else { + char trigger1[24]; + GetTextIndexed(trigger1, sizeof(trigger1), i, kHAssTriggerTypeButtons); + snprintf_P(subtype, sizeof(subtype), PSTR("button_%d"), device + 1); + if (i > 1 && single) { + mqtt_data[0] = '\0'; + } else { + Response_P(HASS_TRIGGER_TYPE, state_topic, trigger2, trigger1, subtype, ESP_getChipId()); + } + } + } + masterlog_level = ShowTopic; + MqttPublish(stopic, true); + } +} + +void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint8_t toggle, uint8_t pir) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + uint8_t ShowTopic; + + mqtt_data[0] = '\0'; + masterlog_level = 4; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SW_%d"), ESP_getChipId(), device + 1); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + + masterlog_level = ShowTopic = 4; + + if (Settings.flag.hass_discovery && present ) { + if (!toggle || dual) { + char name[TOPSZ]; + char value_template[33]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + char jsoname[8]; + + ShowTopic = 0; + + GetPowerDevice(value_template, device + 1, sizeof(value_template), 1 + Settings.flag.device_index_enable); + snprintf_P(jsoname, sizeof(jsoname), PSTR("SWITCH%d"), device + 1); + GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_DEVICENAME), device + 1); + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + if (!pir) { + TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); + } else { + TryResponseAppend_P(HASS_DISCOVER_BIN_PIR, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2)); + } + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); +#ifdef DEEPSLEEP_LWT_HA_DISCOVERY + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); +#else + if (Settings.deepsleep == 0) + { + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + } +#endif + TryResponseAppend_P(PSTR("}")); + } + } + masterlog_level = ShowTopic; + MqttPublish(stopic, true); + +} + +void HAssAnnounceSwitches(void) +{ + for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) + { + uint8_t switch_present = 0; + uint8_t dual = 0; + uint8_t toggle = 1; + uint8_t hold = 0; + uint8_t pir = 0; + + if (PinUsed(GPIO_SWT1, switch_index)) { switch_present = 1; } + + if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) + { +# 696 "/workspace/Tasmota/tasmota/xdrv_12_home_assistant.ino" + uint8_t swmode = Settings.switchmode[switch_index]; + + switch (swmode) { + case FOLLOW: + case FOLLOW_INV: + toggle = 0; + break; + case PUSHBUTTON: + case PUSHBUTTON_INV: + dual = 1; + break; + case PUSHBUTTONHOLD: + case PUSHBUTTONHOLD_INV: + dual = 1; + hold = 2; + break; + case TOGGLEMULTI: + hold = 3; + break; + case FOLLOWMULTI: + case FOLLOWMULTI_INV: + dual = 1; + toggle = 0; + hold = 3; + break; + case PUSHON: + case PUSHON_INV: + toggle = 0; + pir = 1; + } + + } else { switch_present = 0;} + + HAssAnnouncerTriggers(switch_index, switch_present, 1, toggle, hold, 0, 2, 3); + HAssAnnouncerBinSensors(switch_index, switch_present, dual, toggle, pir); + } +} + +void HAssAnnounceButtons(void) +{ + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) + { + uint8_t button_present = 0; + uint8_t single = 0; + +#ifdef ESP8266 + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) + { + button_present = 1; + } else +#endif + { + if (PinUsed(GPIO_KEY1, button_index)) { + button_present = 1; + } + } +# 766 "/workspace/Tasmota/tasmota/xdrv_12_home_assistant.ino" + if (!Settings.flag3.mqtt_buttons) { + button_present = 0; + } else { + if (Settings.flag.button_single) { + single = 1; + } + } + HAssAnnouncerTriggers(button_index, button_present, 0, 0, 0, single, 1, 6); + } +} + +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, bool nested, const char* SubKey) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + char subname[20]; + + mqtt_data[0] = '\0'; + + + NoAlNumToUnderscore(subname, MultiSubName); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP_getChipId(), sensorname, subname); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + if (Settings.flag.hass_discovery) + { + char name[TOPSZ]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + masterlog_level = 0; + + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_DEVICENAME), sensorname, MultiSubName); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic); +#ifdef DEEPSLEEP_LWT_HA_DISCOVERY + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); +#else + if (Settings.deepsleep == 0) + { + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + } +#endif + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + + + char jname[32]; + int sensor_index = GetCommandCode(jname, sizeof(jname), SubKey, kHAssJsonSensorTypes); + if (sensor_index > -1) { + + char param1[20]; + GetTextIndexed(param1, sizeof(param1), sensor_index, kHAssJsonSensorUnits); + switch (sensor_index) { + case 0: + case 1: + snprintf_P(param1, sizeof(param1), PSTR("°%c"),TempUnit()); + break; + case 2: + case 3: + snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); + break; + } + char param2[50]; + GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); + TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); + + } else { + TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); + } + + if (nested) { TryResponseAppend_P(PSTR("['%s']"), SubKey); } + + if (subqty != 0) { TryResponseAppend_P(PSTR("[%d]"), subqty -1); } + + TryResponseAppend_P(PSTR("}}\"}")); + } + MqttPublish(stopic, true); +} + +void HAssAnnounceSensors(void) +{ + uint8_t hass_xsns_index = 0; + do + { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); + tele_period = tele_period_save; + size_t sensordata_len = strlen(mqtt_data); + char sensordata[sensordata_len+2]; + strcpy(sensordata, mqtt_data); + + + + + + + + if (sensordata_len > 0) + { + + sensordata[0] = '{'; + + sensordata[sensordata_len] = '}'; + sensordata[sensordata_len+1] = '\0'; + + JsonParser parser(sensordata); + JsonParserObject root = parser.getRootObject(); + if (!root) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s' (ERR1)"), kHAssError3, sensordata); + continue; + } + for (auto sensor_key : root) + { + + const char *sensorname = sensor_key.getStr(); + JsonParserObject sensors = sensor_key.getValue().getObject(); + + if (!sensors) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s '%s' (ERR2)"), kHAssError3, sensorname); + continue; + } + + for (auto subsensor_key_token : sensors) + { + const char * subsensor_key = subsensor_key_token.getStr(); + JsonParserToken subsensor = subsensor_key_token.getValue(); + if (subsensor.isObject()) { + + JsonParserObject subsensors = subsensor.getObject(); + char NewSensorName[20]; + for (auto subsensor2_key : subsensors) { + snprintf_P(NewSensorName, sizeof(NewSensorName), PSTR("%s %s"), subsensor_key, subsensor2_key.getStr()); + HAssAnnounceSensor(sensorname, subsensor_key, NewSensorName, 0, 1, subsensor2_key.getStr()); + } + } else if (subsensor.isArray()) { + + JsonParserArray subsensors = subsensor.getArray(); + uint8_t subqty = subsensors.size(); + char MultiSubName[20]; + for (int i = 1; i <= subqty; i++) { + snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor_key, i); + HAssAnnounceSensor(sensorname, subsensor_key, MultiSubName, i, 0, subsensor_key); + } + } else { + HAssAnnounceSensor(sensorname, subsensor_key, subsensor_key, 0, 0, subsensor_key);} + } + } + } + yield(); + } while (hass_xsns_index != 0); +} + +void HAssAnnounceShutters(void) +{ +#ifdef USE_SHUTTER + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + uint8_t ShowTopic; + + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + mqtt_data[0] = '\0'; + masterlog_level = ShowTopic = 4; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SHT_%d"), ESP_getChipId(), i + 1); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/cover/%s/config"), unique_id); + + if (Settings.flag.hass_discovery && Settings.flag3.shutter_mode && Settings.shutter_startrelay[i] > 0 && Settings.shutter_startrelay[i] <= MAX_RELAYS) { + ShowTopic = 0; + if (i > MAX_FRIENDLYNAMES) { + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s Shutter %d"), SettingsText(SET_DEVICENAME), i + 1); + } else { + snprintf_P(stemp1, sizeof(stemp1), PSTR("%s"), SettingsText(SET_FRIENDLYNAME1 + i)); + } + GetTopic_P(stemp2, TELE, mqtt_topic, D_RSLT_STATE); + Response_P(HASS_DISCOVER_BASE, stemp1, stemp2); + + GetTopic_P(stemp1, TELE, mqtt_topic, S_LWT); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, stemp1); + + GetTopic_P(stemp1, CMND, mqtt_topic, PSTR("Backlog")); + TryResponseAppend_P(HASS_DISCOVER_SHUTTER_BASE, stemp1, i + 1, i + 1, i + 1); + + GetTopic_P(stemp1, STAT, mqtt_topic, PSTR("SHUTTER")); + GetTopic_P(stemp2, CMND, mqtt_topic, PSTR("ShutterPosition")); + TryResponseAppend_P(HASS_DISCOVER_SHUTTER_POS, stemp1, i + 1, stemp2, i + 1); + + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + TryResponseAppend_P(PSTR("}")); + } + + masterlog_level = ShowTopic; + MqttPublish(stopic, true); + } +#endif +} + +void HAssAnnounceDeviceInfoAndStatusSensor(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + uint8_t ShowTopic; + + + mqtt_data[0] = '\0'; + masterlog_level = ShowTopic = 4; + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP_getChipId()); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) + { + char name[TOPSZ]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + ShowTopic = 0; + snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_DEVICENAME)); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_DEVICENAME), + ModuleName().c_str(), my_version, my_image); + TryResponseAppend_P(PSTR("}")); + } + masterlog_level = ShowTopic; + MqttPublish(stopic, true); + + if (!Settings.flag.hass_discovery) { + masterlog_level = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant MQTT Discovery disabled.")); + } +} + +void HAssPublishStatus(void) +{ + Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_CMND_MODULE " or " D_CMND_TEMPLATE"\":\"%s\"," + "\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\"," + "\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\"," + "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"), + my_version, my_image, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(), + GetUptime().c_str(), my_hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()), + WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), loop_load_avg); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); +} + +void HAssDiscovery(void) +{ + + if (Settings.flag.hass_discovery) + { + Settings.flag.mqtt_response = 0; + Settings.flag.decimal_text = 1; + Settings.flag3.hass_tele_on_power = 1; + + + + } + + if (Settings.flag.hass_discovery || (1 == hass_mode)) + { + hass_mode = 2; + + HAssAnnounceButtons(); + + + HAssAnnounceSwitches(); + + + HAssAnnounceSensors(); + + + HAssAnnounceShutters(); + + + HAssAnnounceRelayLight(); + + + HAssAnnounceDeviceInfoAndStatusSensor(); + masterlog_level = 0; + hass_mode = 3; + } +} + +void HAssDiscover(void) +{ + hass_mode = 1; + hass_init_step = 1; +} + +void HAssAnyKey(void) +{ + if (!Settings.flag.hass_discovery) { return; } + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; + + if (!key && KeyTopicActive(0)) { + device = (XdrvMailbox.payload >> 24) & 0xFF; + } + + char scommand[CMDSZ]; + char sw_topic[TOPSZ]; + char key_topic[TOPSZ]; + char trg_state[8]; + char *tmpbtn = SettingsText(SET_MQTT_BUTTON_TOPIC); + char *tmpsw = SettingsText(SET_MQTT_SWITCH_TOPIC); + uint8_t evkey = 0; + Format(sw_topic, tmpsw, sizeof(sw_topic)); + Format(key_topic, tmpbtn, sizeof(key_topic)); + + if (state >= 2) { evkey = 1;} + snprintf_P(scommand, sizeof(scommand), PSTR("%s%d%s"), (key) ? "SWITCH" : "BUTTON", device, (evkey) ? "T" : ""); + + char stopic[TOPSZ]; + + if (!key) { + if (state == 3) { + snprintf_P(trg_state, sizeof(trg_state), GetStateText(3)); + } else { + if (state == 2) { state = 10; } + GetTextIndexed(trg_state, sizeof(trg_state), state -9, kHAssTriggerStringButtons); + } + } + + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), (key) ? GetStateText(state) : trg_state); + MqttPublish(stopic); +} + +bool HAssMqttLWT(void) +{ + if (strncasecmp_P(XdrvMailbox.topic, PSTR(HOME_ASSISTANT_LWT_TOPIC), strlen(HOME_ASSISTANT_LWT_TOPIC)) != 0) { + return false; + } + if (Settings.flag.hass_discovery && (strncasecmp_P(XdrvMailbox.data, PSTR("online"), strlen("online")) == 0) && (XdrvMailbox.data_len == 6)) { + MqttPublishTeleState(); + return true; + } else { return false; } +} + +void HassLwtSubscribe(bool hasslwt) +{ + char htopic[TOPSZ]; + snprintf_P(htopic, sizeof(htopic), PSTR(HOME_ASSISTANT_LWT_TOPIC)); + if (hasslwt && (Settings.flag.hass_discovery)) { + MqttSubscribe(htopic); + } else { MqttUnsubscribe(htopic); } +} + + + + + +bool Xdrv12(uint8_t function) +{ + bool result = false; + bool hasslwt = HOME_ASSISTANT_LWT_SUBSCRIBE; + if (Settings.flag.mqtt_enabled) + { + switch (function) + { + case FUNC_EVERY_SECOND: + if (hass_init_step) + { + hass_init_step--; + if (!hass_init_step) + { + HAssDiscovery(); + NewHAssDiscovery(); + } + } + else if (Settings.flag.hass_discovery && Settings.tele_period) + { + hass_tele_period++; + if (hass_tele_period >= Settings.tele_period) + { + hass_tele_period = 0; + mqtt_data[0] = '\0'; + HAssPublishStatus(); + } + } + break; + case FUNC_ANY_KEY: + HAssAnyKey(); + break; + case FUNC_MQTT_INIT: + hass_mode = 0; + hass_init_step = 2; + + + + break; + + case FUNC_MQTT_SUBSCRIBE: + HassLwtSubscribe(hasslwt); + break; + case FUNC_MQTT_DATA: + result = HAssMqttLWT(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_13_display.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_13_display.ino" +#if defined(USE_I2C) || defined(USE_SPI) +#ifdef USE_DISPLAY + +#define XDRV_13 13 + +#include + +Renderer *renderer; + +enum ColorType { COLOR_BW, COLOR_COLOR }; + +#ifndef MAXBUTTONS +#define MAXBUTTONS 16 +#endif + +#ifdef USE_TOUCH_BUTTONS +VButton *buttons[MAXBUTTONS]; +#endif + + + +uint16_t fg_color = 1; +uint16_t bg_color = 0; +uint8_t color_type = COLOR_BW; +uint8_t auto_draw=1; + +const uint8_t DISPLAY_MAX_DRIVERS = 16; +const uint8_t DISPLAY_MAX_COLS = 64; +const uint8_t DISPLAY_MAX_ROWS = 64; + +const uint8_t DISPLAY_LOG_ROWS = 32; + +#define D_PRFX_DISPLAY "Display" +#define D_CMND_DISP_ADDRESS "Address" +#define D_CMND_DISP_COLS "Cols" +#define D_CMND_DISP_DIMMER "Dimmer" +#define D_CMND_DISP_MODE "Mode" +#define D_CMND_DISP_MODEL "Model" +#define D_CMND_DISP_REFRESH "Refresh" +#define D_CMND_DISP_ROWS "Rows" +#define D_CMND_DISP_SIZE "Size" +#define D_CMND_DISP_FONT "Font" +#define D_CMND_DISP_ROTATE "Rotate" +#define D_CMND_DISP_TEXT "Text" +#define D_CMND_DISP_WIDTH "Width" +#define D_CMND_DISP_HEIGHT "Height" + +enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, + FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, + FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME, + FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE, + FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, + FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, + FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING }; + +enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; + +const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" + "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" + D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" + D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; + +void (* const DisplayCommand[])(void) PROGMEM = { + &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, + &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, + &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; + +char *dsp_str; + +uint16_t dsp_x; +uint16_t dsp_y; +uint16_t dsp_x2; +uint16_t dsp_y2; +uint16_t dsp_rad; +uint16_t dsp_color; +int16_t dsp_len; +int16_t disp_xpos = 0; +int16_t disp_ypos = 0; + +uint8_t disp_power = 0; +uint8_t disp_device = 0; +uint8_t disp_refresh = 1; +uint8_t disp_autodraw = 1; +uint8_t dsp_init; +uint8_t dsp_font; +uint8_t dsp_flag; +uint8_t dsp_on; + +#ifdef USE_DISPLAY_MODES1TO5 + +char **disp_log_buffer; +char **disp_screen_buffer; +char disp_temp[2]; +char disp_pres[5]; + +uint8_t disp_log_buffer_cols = 0; +uint8_t disp_log_buffer_idx = 0; +uint8_t disp_log_buffer_ptr = 0; +uint8_t disp_screen_buffer_cols = 0; +uint8_t disp_screen_buffer_rows = 0; +bool disp_subscribed = false; + +#endif + + + +void DisplayInit(uint8_t mode) +{ + if (renderer) { + renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); + } + else { + dsp_init = mode; + XdspCall(FUNC_DISPLAY_INIT); + } +} + +void DisplayClear(void) +{ + XdspCall(FUNC_DISPLAY_CLEAR); +} + +void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_HLINE); +} + +void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_VLINE); +} + +void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_LINE); +} + +void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_CIRCLE); +} + +void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_CIRCLE); +} + +void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE); +} + +void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_RECTANGLE); +} + +void DisplayDrawFrame(void) +{ + XdspCall(FUNC_DISPLAY_DRAW_FRAME); +} + +void DisplaySetSize(uint8_t size) +{ + Settings.display_size = size &3; + XdspCall(FUNC_DISPLAY_TEXT_SIZE); +} + +void DisplaySetFont(uint8_t font) +{ + Settings.display_font = font &3; + XdspCall(FUNC_DISPLAY_FONT_SIZE); +} + +void DisplaySetRotation(uint8_t rotation) +{ + Settings.display_rotate = rotation &3; + XdspCall(FUNC_DISPLAY_ROTATION); +} + +void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + dsp_x = x; + dsp_y = y; + dsp_str = str; + dsp_color = color; + dsp_flag = flag; + XdspCall(FUNC_DISPLAY_DRAW_STRING); +} + +void DisplayOnOff(uint8_t on) +{ + ExecuteCommandPower(disp_device, on, SRC_DISPLAY); +} + + + + +uint8_t fatoiv(char *cp,float *res) { + uint8_t index=0; + *res=CharToFloat(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiv(char *cp, int16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiV(char *cp, uint16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if (*cp>='0' && *cp<='9') { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +void alignright(char *string) { + uint16_t slen=strlen(string); + uint16_t len=slen; + while (len) { + + if (string[len-1]!=' ') { + break; + } + len--; + } + uint16_t diff=slen-len; + if (diff>0) { + + memmove(&string[diff],string,len); + memset(string,' ',diff); + } +} + +char *get_string(char *buff,uint8_t len,char *cp) { +uint8_t index=0; + while (*cp!=':') { + buff[index]=*cp++; + index++; + if (index>=len) break; + } + buff[index]=0; + cp++; + return cp; +} + +#define ESCAPE_CHAR '~' + + +uint32_t decode_te(char *line) { + uint32_t skip = 0; + char sbuf[3],*cp; + while (*line) { + if (*line==ESCAPE_CHAR) { + cp=line+1; + if (*cp!=0 && *cp==ESCAPE_CHAR) { + + memmove(cp,cp+1,strlen(cp)); + skip++; + } else { + + if (strlen(cp)<2) { + + return skip; + } + + sbuf[0]=*(cp); + sbuf[1]=*(cp+1); + sbuf[2]=0; + *line=strtol(sbuf,0,16); + + memmove(cp,cp+2,strlen(cp)-1); + skip += 2; + } + } + line++; + } + return skip; +} + + + +#define DISPLAY_BUFFER_COLS 128 + +void DisplayText(void) +{ + uint8_t lpos; + uint8_t escape = 0; + uint8_t var; + int16_t lin = 0; + int16_t col = 0; + int16_t fill = 0; + int16_t temp; + int16_t temp1; + float ftemp; + + char linebuf[DISPLAY_BUFFER_COLS]; + char *dp = linebuf; + char *cp = XdrvMailbox.data; + + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + *dp = 0; + + while (*cp) { + if (!escape) { + + if (*cp == '[') { + escape = 1; + cp++; + + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { *dp = 0; } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + dp = linebuf; + } + } else { + + if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; } + } + } else { + + if (*cp == ']') { + escape = 0; + cp++; + } else { + + switch (*cp++) { + case 'z': + + if (!renderer) DisplayClear(); + else renderer->fillScreen(bg_color); + disp_xpos = 0; + disp_ypos = 0; + col = 0; + lin = 0; + break; + case 'i': + + DisplayInit(DISPLAY_INIT_PARTIAL); + break; + case 'I': + + DisplayInit(DISPLAY_INIT_FULL); + break; + case 'o': + DisplayOnOff(0); + break; + case 'O': + DisplayOnOff(1); + break; + case 'x': + + var = atoiv(cp, &disp_xpos); + cp += var; + break; + case 'y': + + var = atoiv(cp, &disp_ypos); + cp += var; + break; + case 'l': + + var = atoiv(cp, &lin); + cp += var; + + break; + case 'c': + + var = atoiv(cp, &col); + cp += var; + + break; + case 'C': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + + var = fatoiv(cp,&ftemp); + } + fg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'B': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + var = fatoiv(cp,&ftemp); + } + bg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'p': + + var = atoiv(cp, &fill); + cp += var; + linebuf[fill] = 0; + break; +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + case 'P': + { char *ep=strchr(cp,':'); + if (ep) { + *ep=0; + ep++; + Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); + cp=ep; + } + } + break; +#endif + case 'h': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + } else { + if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_xpos += temp; + break; + case 'v': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + } else { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_ypos += temp; + break; + case 'L': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + disp_xpos += temp; + disp_ypos += temp1; + break; + case 'k': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'K': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'r': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'R': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'u': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + case 'U': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + + case 't': + if (*cp=='S') { + cp++; + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + dp += 8; + } + } else { + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { + snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); + dp += 5; + } + } + break; + case 'T': { + uint8_t param1 = RtcTime.day_of_month; + uint8_t param2 = RtcTime.month; + if (*cp=='U') { + cp++; + param1 = RtcTime.month; + param2 = RtcTime.day_of_month; + } + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), param1, param2, RtcTime.year%2000); + dp += 8; + } + break; } + case 'd': + + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + break; + case 'D': + + auto_draw=*cp&3; + if (renderer) renderer->setDrawMode(auto_draw>>1); + cp += 1; + break; + case 's': + + if (renderer) renderer->setTextSize(*cp&7); + else DisplaySetSize(*cp&3); + cp += 1; + break; + case 'f': + + if (renderer) renderer->setTextFont(*cp&7); + else DisplaySetFont(*cp&7); + cp += 1; + break; + case 'a': + + if (renderer) renderer->setRotation(*cp&3); + else DisplaySetRotation(*cp&3); + cp+=1; + break; + +#ifdef USE_GRAPH + case 'G': + + if (*cp=='d') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + var=atoiv(cp,&temp1); + cp+=var; + RedrawGraph(temp,temp1); + break; + } +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*cp=='s') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Save_graph(temp,bbuff); + break; + } + if (*cp=='r') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Restore_graph(temp,bbuff); + break; + } +#endif + { int16_t num,gxp,gyp,gxs,gys,dec,icol; + float ymin,ymax; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&dec); + cp+=var; + cp++; + var=fatoiv(cp,&ymin); + cp+=var; + cp++; + var=fatoiv(cp,&ymax); + cp+=var; + if (color_type==COLOR_COLOR) { + + cp++; + var=atoiv(cp,&icol); + cp+=var; + } else { + icol=0; + } + DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); + } + break; + case 'g': + { float temp; + int16_t num; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=fatoiv(cp,&temp); + cp+=var; + AddValue(num,temp); + } + break; +#endif + +#ifdef USE_AWATCH + case 'w': + var = atoiv(cp, &temp); + cp += var; + DrawAClock(temp); + break; +#endif + +#ifdef USE_TOUCH_BUTTONS + case 'b': + { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; uint8_t dflg=1; + if (*cp=='e' || *cp=='d') { + + uint8_t dis=0; + if (*cp=='d') dis=1; + cp++; + var=atoiv(cp,&num); + num=num%MAXBUTTONS; + cp+=var; + if (buttons[num]) { + buttons[num]->vpower.disable=dis; + if (!dis) { + if (buttons[num]->vpower.is_virtual) buttons[num]->xdrawButton(buttons[num]->vpower.on_off); + else buttons[num]->xdrawButton(bitRead(power,num)); + } + } + break; + } + if (*cp=='-') { + cp++; + dflg=0; + } + var=atoiv(cp,&num); + cp+=var; + cp++; + uint8_t bflags=num>>8; + num=num%MAXBUTTONS; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&outline); + cp+=var; + cp++; + var=atoiv(cp,&fill); + cp+=var; + cp++; + var=atoiv(cp,&textcolor); + cp+=var; + cp++; + var=atoiv(cp,&textsize); + cp+=var; + cp++; + + char bbuff[32]; + cp=get_string(bbuff,sizeof(bbuff),cp); + + if (buttons[num]) { + delete buttons[num]; + } + if (renderer) { + buttons[num]= new VButton(); + if (buttons[num]) { + buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ + renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); + if (!bflags) { + + if (dflg) buttons[num]->xdrawButton(bitRead(power,num)); + buttons[num]->vpower.is_virtual=0; + } else { + + buttons[num]->vpower.is_virtual=1; + if (bflags==2) { + + buttons[num]->vpower.is_pushbutton=1; + } else { + + buttons[num]->vpower.is_pushbutton=0; + } + if (dflg) buttons[num]->xdrawButton(buttons[num]->vpower.on_off); + buttons[num]->vpower.disable=!dflg; + } + } + } + } + break; +#endif + default: + + Response_P(PSTR("Unknown Escape")); + goto exit; + break; + } + } + } + } + exit: + + dp -= decode_te(linebuf); + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { + *dp = 0; + } else { + linebuf[abs(int(fill))] = 0; + } + if (fill<0) { + + alignright(linebuf); + } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + } + + if (auto_draw&1) { + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void DisplayClearScreenBuffer(void) +{ + if (disp_screen_buffer_cols) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); + } + } +} + +void DisplayFreeScreenBuffer(void) +{ + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + if (disp_screen_buffer[i] != nullptr) { free(disp_screen_buffer[i]); } + } + free(disp_screen_buffer); + disp_screen_buffer_cols = 0; + disp_screen_buffer_rows = 0; + } +} + +void DisplayAllocScreenBuffer(void) +{ + if (!disp_screen_buffer_cols) { + disp_screen_buffer_rows = Settings.display_rows; + disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_screen_buffer[i] == nullptr) { + DisplayFreeScreenBuffer(); + break; + } + } + } + if (disp_screen_buffer != nullptr) { + disp_screen_buffer_cols = Settings.display_cols[0] +1; + DisplayClearScreenBuffer(); + } + } +} + +void DisplayReAllocScreenBuffer(void) +{ + DisplayFreeScreenBuffer(); + DisplayAllocScreenBuffer(); +} + +void DisplayFillScreen(uint32_t line) +{ + uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); + if (len) { + memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); + disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; + } +} + + + +void DisplayClearLogBuffer(void) +{ + if (disp_log_buffer_cols) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + memset(disp_log_buffer[i], 0, disp_log_buffer_cols); + } + } +} + +void DisplayFreeLogBuffer(void) +{ + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + if (disp_log_buffer[i] != nullptr) { free(disp_log_buffer[i]); } + } + free(disp_log_buffer); + disp_log_buffer_cols = 0; + } +} + +void DisplayAllocLogBuffer(void) +{ + if (!disp_log_buffer_cols) { + disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_log_buffer[i] == nullptr) { + DisplayFreeLogBuffer(); + break; + } + } + } + if (disp_log_buffer != nullptr) { + disp_log_buffer_cols = Settings.display_cols[0] +1; + DisplayClearLogBuffer(); + } + } +} + +void DisplayReAllocLogBuffer(void) +{ + DisplayFreeLogBuffer(); + DisplayAllocLogBuffer(); +} + +void DisplayLogBufferAdd(char* txt) +{ + if (disp_log_buffer_cols) { + strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); + disp_log_buffer_idx++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; } + } +} + +char* DisplayLogBuffer(char temp_code) +{ + char* result = nullptr; + if (disp_log_buffer_cols) { + if (disp_log_buffer_idx != disp_log_buffer_ptr) { + result = disp_log_buffer[disp_log_buffer_ptr]; + disp_log_buffer_ptr++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; } + + char *pch = strchr(result, '~'); + if (pch != nullptr) { result[pch - result] = temp_code; } + } + } + return result; +} + +void DisplayLogBufferInit(void) +{ + if (Settings.display_mode) { + disp_log_buffer_idx = 0; + disp_log_buffer_ptr = 0; + disp_refresh = Settings.display_refresh; + + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); + snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); + + DisplayReAllocLogBuffer(); + + char buffer[40]; + snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode); + DisplayLogBufferAdd(buffer); + + snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), NetworkHostname()); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), NetworkMacAddress().c_str()); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), NetworkAddress().toString().c_str()); + DisplayLogBufferAdd(buffer); + if (!global_state.wifi_down) { + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI())); + DisplayLogBufferAdd(buffer); + } + } +} + + + + + +enum SensorQuantity { + JSON_TEMPERATURE, + JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY, + JSON_PRESSURE, JSON_PRESSUREATSEALEVEL, + JSON_ILLUMINANCE, + JSON_GAS, + JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY, + JSON_PERIOD, + JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL, + JSON_CURRENT, + JSON_VOLTAGE, + JSON_POWERUSAGE, + JSON_CO2, + JSON_FREQUENCY }; +const char kSensorQuantity[] PROGMEM = + D_JSON_TEMPERATURE "|" + D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" + D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_ILLUMINANCE "|" + D_JSON_GAS "|" + D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" + D_JSON_PERIOD "|" + D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" + D_JSON_CURRENT "|" + D_JSON_VOLTAGE "|" + D_JSON_POWERUSAGE "|" + D_JSON_CO2 "|" + D_JSON_FREQUENCY ; + +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) +{ + char quantity[TOPSZ]; + char buffer[Settings.display_cols[0] +1]; + char spaces[Settings.display_cols[0]]; + char source[Settings.display_cols[0] - Settings.display_cols[1]]; + char svalue[Settings.display_cols[1] +1]; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("DisplayJsonValue")); +#endif + + memset(spaces, 0x20, sizeof(spaces)); + spaces[sizeof(spaces) -1] = '\0'; + snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); + + int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); + if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { + return; + } + if (JSON_TEMPERATURE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp); + } + else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); + } + else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); + } + else if (JSON_ILLUMINANCE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); + } + else if (JSON_GAS == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value); + } + else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value); + } + else if (JSON_PERIOD == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value); + } + else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value); + } + else if (JSON_CURRENT == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value); + } + else if (JSON_VOLTAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value); + } + else if (JSON_POWERUSAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value); + } + else if (JSON_CO2 == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value); + } + else if (JSON_FREQUENCY == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value); + } + snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); + + + + DisplayLogBufferAdd(buffer); +} + +void DisplayAnalyzeJson(char *topic, char *json) +{ +# 1168 "/workspace/Tasmota/tasmota/xdrv_13_display.ino" + String jsonStr = json; + JsonParser parser((char*)jsonStr.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { + + const char *unit = root.getStr(PSTR(D_JSON_TEMPERATURE_UNIT), nullptr); + if (unit) { + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); + } + unit = root.getStr(PSTR(D_JSON_PRESSURE_UNIT), nullptr); + if (unit) { + snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); + } + for (auto key1 : root) { + JsonParserToken value1 = key1.getValue(); + if (value1.isObject()) { + JsonParserObject Object2 = value1.getObject(); + for (auto key2 : Object2) { + JsonParserToken value2 = key2.getValue(); + if (value2.isObject()) { + JsonParserObject Object3 = value2.getObject(); + for (auto key3 : Object3) { + const char* value3 = key3.getValue().getStr(nullptr); + if (value3 != nullptr) { + DisplayJsonValue(topic, key1.getStr(), key3.getStr(), value3); + } + } + } else { + const char* value = value2.getStr(nullptr); + if (value != nullptr) { + DisplayJsonValue(topic, key1.getStr(), key2.getStr(), value); + } + } + } + } else { + const char* value = value1.getStr(nullptr); + if (value != nullptr) { + DisplayJsonValue(topic, key1.getStr(), key1.getStr(), value); + } + } + } + } +} + +void DisplayMqttSubscribe(void) +{ + + + + + + + if (Settings.display_model && (Settings.display_mode &0x04)) { + + char stopic[TOPSZ]; + char ntopic[TOPSZ]; + + ntopic[0] = '\0'; + strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); + char *tp = strtok(stopic, "/"); + while (tp != nullptr) { + if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { + break; + } + strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); + tp = strtok(nullptr, "/"); + } + strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); + strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); + MqttSubscribe(ntopic); + disp_subscribed = true; + } else { + disp_subscribed = false; + } +} + +bool DisplayMqttData(void) +{ + if (disp_subscribed) { + char stopic[TOPSZ]; + + snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); + char *tp = strstr(XdrvMailbox.topic, stopic); + if (tp) { + if (Settings.display_mode &0x04) { + tp = tp + strlen(stopic); + char *topic = strtok(tp, "/"); + DisplayAnalyzeJson(topic, XdrvMailbox.data); + } + return true; + } + } + return false; +} + +void DisplayLocalSensor(void) +{ + if ((Settings.display_mode &0x02) && (0 == tele_period)) { + char no_topic[1] = { 0 }; + + DisplayAnalyzeJson(no_topic, mqtt_data); + } +} + +#endif + + + + + +void DisplayInitDriver(void) +{ + XdspCall(FUNC_DISPLAY_INIT_DRIVER); + + if (renderer) { + renderer->setTextFont(Settings.display_font); + renderer->setTextSize(Settings.display_size); + } + + + + + if (Settings.display_model) { + devices_present++; + if (!PinUsed(GPIO_BACKLIGHT)) { + if (light_type && (4 == Settings.display_model)) { + devices_present--; + } + } + disp_device = devices_present; + +#ifndef USE_DISPLAY_MODES1TO5 + Settings.display_mode = 0; +#else + DisplayLogBufferInit(); +#endif + } +} + +void DisplaySetPower(void) +{ + disp_power = bitRead(XdrvMailbox.index, disp_device -1); + + + + if (Settings.display_model) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_POWER); + } else { + renderer->DisplayOnff(disp_power); + } + } +} + + + + + +void CmndDisplay(void) +{ + Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" + D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" + D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), + Settings.display_model, Settings.display_width, Settings.display_height, + Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, + Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); +} + +void CmndDisplayModel(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { + uint32_t last_display_model = Settings.display_model; + Settings.display_model = XdrvMailbox.payload; + if (XdspCall(FUNC_DISPLAY_MODEL)) { + restart_flag = 2; + } else { + Settings.display_model = last_display_model; + } + } + ResponseCmndNumber(Settings.display_model); +} + +void CmndDisplayWidth(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_width) { + Settings.display_width = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_width); +} + +void CmndDisplayHeight(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_height) { + Settings.display_height = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_height); +} + +void CmndDisplayMode(void) +{ +#ifdef USE_DISPLAY_MODES1TO5 + + + + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + uint32_t last_display_mode = Settings.display_mode; + Settings.display_mode = XdrvMailbox.payload; + + if (disp_subscribed != (Settings.display_mode &0x04)) { + restart_flag = 2; + } else { + if (last_display_mode && !Settings.display_mode) { + DisplayInit(DISPLAY_INIT_MODE); + if (renderer) renderer->fillScreen(bg_color); + else DisplayClear(); + } else { + DisplayLogBufferInit(); + DisplayInit(DISPLAY_INIT_MODE); + } + } + } +#endif + ResponseCmndNumber(Settings.display_mode); +} + +void CmndDisplayDimmer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; + if (Settings.display_dimmer && !(disp_power)) { + ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); + } + else if (!Settings.display_dimmer && disp_power) { + ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); + } + if (renderer) renderer->dim(Settings.display_dimmer); + } + ResponseCmndNumber(Settings.display_dimmer); +} + +void CmndDisplaySize(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { + Settings.display_size = XdrvMailbox.payload; + if (renderer) renderer->setTextSize(Settings.display_size); + else DisplaySetSize(Settings.display_size); + } + ResponseCmndNumber(Settings.display_size); +} + +void CmndDisplayFont(void) +{ + if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { + Settings.display_font = XdrvMailbox.payload; + if (renderer) renderer->setTextFont(Settings.display_font); + else DisplaySetFont(Settings.display_font); + } + ResponseCmndNumber(Settings.display_font); +} + +void CmndDisplayRotate(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + if (Settings.display_rotate != XdrvMailbox.payload) { +# 1453 "/workspace/Tasmota/tasmota/xdrv_13_display.ino" + Settings.display_rotate = XdrvMailbox.payload; + DisplayInit(DISPLAY_INIT_MODE); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); +#endif + } + } + ResponseCmndNumber(Settings.display_rotate); +} + +void CmndDisplayText(void) +{ + if (disp_device && XdrvMailbox.data_len > 0) { +#ifndef USE_DISPLAY_MODES1TO5 + DisplayText(); +#else + if (!Settings.display_mode) { + DisplayText(); + } else { + DisplayLogBufferAdd(XdrvMailbox.data); + } +#endif + ResponseCmndChar(XdrvMailbox.data); + } +} + +void CmndDisplayAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRefresh(void) +{ + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { + Settings.display_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.display_refresh); +} + +void CmndDisplayColumns(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { + Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + if (1 == XdrvMailbox.index) { + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); + } +#endif + } + ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRows(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { + Settings.display_rows = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); +#endif + } + ResponseCmndNumber(Settings.display_rows); +} + + + + +#ifdef ESP32 +#ifdef JPEG_PICTS +#include "img_converters.h" +#include "esp_jpg_decode.h" +bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale); +char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *width, unsigned short *height); +void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len); +#endif +#endif + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +extern FS *fsp; +#define XBUFF_LEN 128 +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { + if (!renderer) return; + File fp; + char *ending = strrchr(file,'.'); + if (!ending) return; + ending++; + char estr[8]; + memset(estr,0,sizeof(estr)); + for (uint32_t cnt=0; cntopen(file,FILE_READ); + if (!fp) return; + uint16_t xsize; + fp.read((uint8_t*)&xsize,2); + uint16_t ysize; + fp.read((uint8_t*)&ysize,2); +#if 1 + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + uint16_t rgb[xsize]; + for (int16_t j=0; jpushColors(rgb,xsize,true); + + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); +#else + for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + } + delay(0); + OsWatchLoop(); + yp++; + } +#endif + fp.close(); + } else if (!strcmp(estr,"jpg")) { + +#ifdef ESP32 +#ifdef JPEG_PICTS + if (psramFound()) { + fp=fsp->open(file,FILE_READ); + if (!fp) return; + uint32_t size = fp.size(); + uint8_t *mem = (uint8_t *)heap_caps_malloc(size+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (mem) { + uint8_t res=fp.read(mem, size); + if (res) { + uint16_t xsize; + uint16_t ysize; + if (mem[0]==0xff && mem[1]==0xd8) { + get_jpeg_size(mem, size, &xsize, &ysize); + + if (xsize && ysize) { + uint8_t *out_buf = (uint8_t *)heap_caps_malloc((xsize*ysize*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (out_buf) { + uint8_t *ob=out_buf; + jpg2rgb888(mem, size, out_buf, (jpg_scale_t)JPG_SCALE_NONE); + uint16_t pixels=xsize*ysize/XBUFF_LEN; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int32_t j=0; jpushColors(rbuff,XBUFF_LEN,true); + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); + free(out_buf); + } + } + } + } + free(mem); + } + fp.close(); + } +#endif +#endif + } +} +#endif + +#ifdef USE_AWATCH +#define MINUTE_REDUCT 4 + +#ifndef pi +#define pi 3.14159265359 +#endif + + +void DrawAClock(uint16_t rad) { + if (!renderer) return; + float frad=rad; + uint16_t hred=frad/3.0; + renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); + renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); + renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); + for (uint8_t count=0; count<60; count+=5) { + float p1=((float)count*(pi/30)-(pi/2)); + uint8_t len; + if ((count%15)==0) { + len=4; + } else { + len=2; + } + renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); + } + + + float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; + float temp=(hour*(pi/6.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); + + + temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); +} +#endif + + +#ifdef USE_GRAPH + +typedef union { + uint8_t data; + struct { + uint8_t overlay : 1; + uint8_t draw : 1; + uint8_t nu3 : 1; + uint8_t nu4 : 1; + uint8_t nu5 : 1; + uint8_t nu6 : 1; + uint8_t nu7 : 1; + uint8_t nu8 : 1; + }; +} GFLAGS; + +struct GRAPH { + uint16_t xp; + uint16_t yp; + uint16_t xs; + uint16_t ys; + float ymin; + float ymax; + float range; + uint32_t x_time; + uint32_t last_ms; + uint32_t last_ms_redrawn; + int16_t decimation; + uint16_t dcnt; + uint32_t summ; + uint16_t xcnt; + uint8_t *values; + uint8_t xticks; + uint8_t yticks; + uint8_t last_val; + uint8_t color_index; + GFLAGS flags; +}; + + +struct GRAPH *graph[NUM_GRAPHS]; + +#define TICKLEN 4 +void ClrGraph(uint16_t num) { + struct GRAPH *gp=graph[num]; + + uint16_t xticks=gp->xticks; + uint16_t yticks=gp->yticks; + uint16_t count; + + + if (gp->flags.overlay) return; + + renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); + + if (xticks) { + float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; + for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); + cxp+=xd; + } + } + if (yticks) { + if (gp->ymin<0 && gp->ymax>0) { + + float cxp=0; + float czp=gp->yp+(gp->ymax/gp->range); + while (cxpxs) { + renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); + cxp+=6.0; + } + + float cyp=0,yd=gp->ys/yticks; + for (count=0; countgp->yp) { + renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); + } + if ((czp+cyp)<(gp->yp+gp->ys)) { + renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); + } + cyp+=yd; + } + } else { + float cyp=gp->yp,yd=gp->ys/yticks; + for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); + cyp+=yd; + } + } + } +} + + +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { + if (!renderer) return; + uint8_t rflg=0; + if (xs<0) { + rflg=1; + xs=abs(xs); + } + struct GRAPH *gp; + uint16_t count; + uint16_t index=num%NUM_GRAPHS; + if (!graph[index]) { + gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); + if (!gp) return; + graph[index]=gp; + } else { + gp=graph[index]; + if (rflg) { + RedrawGraph(index,1); + return; + } + } + + + gp->xticks=(num>>4)&0x3f; + gp->yticks=(num>>10)&0x3f; + gp->xp=xp; + gp->yp=yp; + gp->xs=xs; + gp->ys=ys; + if (!dec) dec=1; + gp->decimation=dec; + if (dec>0) { + + gp->x_time=((float)dec*60000.0)/(float)xs; + gp->last_ms=millis()+gp->x_time; + } + gp->ymin=ymin; + gp->ymax=ymax; + gp->range=(ymax-ymin)/ys; + gp->xcnt=0; + gp->dcnt=0; + gp->summ=0; + if (gp->values) free(gp->values); + gp->values=(uint8_t*) calloc(1,xs+2); + if (!gp->values) { + free(gp); + graph[index]=0; + return; + } + + gp->values[0]=0; + + gp->last_ms_redrawn=millis(); + + if (!icol) icol=1; + gp->color_index=icol; + gp->flags.overlay=0; + gp->flags.draw=1; + + + if (index>0) { + for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { + gp->flags.overlay=1; + break; + } + } + } + } + + + renderer->drawRect(xp,yp,xs,ys,fg_color); + + ClrGraph(index); + +} + + +void DisplayCheckGraph() { + int16_t count; + struct GRAPH *gp; + for (count=0;countdecimation>0) { + + while (millis()>gp->last_ms) { + gp->last_ms+=gp->x_time; + uint8_t val; + if (gp->dcnt) { + val=gp->summ/gp->dcnt; + gp->dcnt=0; + gp->summ=0; + gp->last_val=val; + } else { + val=gp->last_val; + } + AddGraph(count,val); + } + } + } + } +} + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#ifdef ESP32 +#include +#endif + +void Save_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + fsp->remove(path); + fp=fsp->open(path,FILE_WRITE); + if (!fp) return; + char str[32]; + sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); + fp.print(str); + dtostrfd(gp->ymin,2,str); + fp.print(str); + fp.print("\t"); + dtostrfd(gp->ymax,2,str); + fp.print(str); + fp.print("\t"); + for (uint32_t count=0;countxs;count++) { + dtostrfd(gp->values[count],0,str); + fp.print(str); + fp.print("\t"); + } + fp.print("\n"); + fp.close(); +} +void Restore_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + fp=fsp->open(path,FILE_READ); + if (!fp) return; + char vbuff[32]; + char *cp=vbuff; + uint8_t buf[2]; + uint8_t findex=0; + + for (uint32_t count=0;count<=gp->xs+4;count++) { + cp=vbuff; + findex=0; + while (fp.available()) { + fp.read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + findex++; + if (findex>=sizeof(vbuff)-1) break; + } + } + *cp=0; + if (count<=4) { + if (count==0) gp->xcnt=atoi(vbuff); + } else { + gp->values[count-5]=atoi(vbuff); + } + } + fp.close(); + RedrawGraph(num,1); +} +#endif + +void RedrawGraph(uint8_t num, uint8_t flags) { + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + if (!flags) { + gp->flags.draw=0; + return; + } + if (!renderer) return; + + gp->flags.draw=1; + uint16_t linecol=fg_color; + + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(index); + } + + for (uint16_t count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } +} + + +void AddGraph(uint8_t num,uint8_t val) { + struct GRAPH *gp=graph[num]; + if (!renderer) return; + + uint16_t linecol=fg_color; + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + gp->xcnt++; + if (gp->xcnt>gp->xs) { + gp->xcnt=gp->xs; + int16_t count; + + for (count=0;countxs-1;count++) { + gp->values[count]=gp->values[count+1]; + } + gp->values[gp->xcnt-1]=val; + + if (!gp->flags.draw) return; + + + if (millis()-gp->last_ms_redrawn>1000) { + gp->last_ms_redrawn=millis(); + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(num); + } + + for (count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } + } + } else { + + gp->values[gp->xcnt]=val; + if (!gp->flags.draw) return; + renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); + } +} + + + +void AddValue(uint8_t num,float fval) { + + num=num%NUM_GRAPHS; + struct GRAPH *gp=graph[num]; + if (!gp) return; + + if (fval>gp->ymax) fval=gp->ymax; + if (fvalymin) fval=gp->ymin; + + int16_t val; + val=(fval-gp->ymin)/gp->range; + + if (val>gp->ys-1) val=gp->ys-1; + if (val<0) val=0; + + + gp->summ+=val; + gp->dcnt++; + + + if (gp->decimation<0) { + if (gp->dcnt>=-gp->decimation) { + gp->dcnt=0; + + val=gp->summ/-gp->decimation; + gp->summ=0; + + AddGraph(num,val); + } + } +} +#endif + +#ifdef USE_FT5206 + +#include + +#undef FT5206_address +#define FT5206_address 0x38 + +FT5206_Class *touchp; +TP_Point pLoc; +bool FT5206_found; + +bool Touch_Init(TwoWire &i2c) { + FT5206_found = false; + touchp = new FT5206_Class(); + if (touchp->begin(i2c, FT5206_address)) { + I2cSetActiveFound(FT5206_address, "FT5206"); + FT5206_found = true; + } + return FT5206_found; +} + +uint32_t Touch_Status(uint32_t sel) { + if (FT5206_found) { + switch (sel) { + case 0: + return touchp->touched(); + case 1: + return pLoc.x; + case 2: + return pLoc.y; + } + return 0; + } else { + return 0; + } +} + + +#ifdef USE_TOUCH_BUTTONS +void Touch_MQTT(uint8_t index, const char *cp) { + ResponseTime_P(PSTR(",\"FT5206\":{\"%s%d\":\"%d\"}}"), cp, index+1, buttons[index]->vpower.on_off); + MqttPublishTeleSensor(); +} + +void Touch_RDW_BUTT(uint32_t count, uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower.on_off = 1; + else buttons[count]->vpower.on_off = 0; +} + + +void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) { +uint16_t temp; +uint8_t rbutt=0; +uint8_t vbutt=0; + + + if (touchp->touched()) { + + pLoc = touchp->getPoint(0); + + if (renderer) { + + rotconvert(&pLoc.x, &pLoc.y); + + + + for (uint8_t count=0; countvpower.disable) { + if (buttons[count]->contains(pLoc.x, pLoc.y)) { + + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!buttons[count]->vpower.is_virtual) { + uint8_t pwr=bitRead(power, rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + Touch_RDW_BUTT(count, !pwr); + } + } else { + + const char *cp; + if (!buttons[count]->vpower.is_pushbutton) { + + buttons[count]->vpower.on_off ^= 1; + cp="TBT"; + } else { + + buttons[count]->vpower.on_off = 1; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower.on_off); + Touch_MQTT(count,cp); + } + } + } + if (!buttons[count]->vpower.is_virtual) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + + for (uint8_t count=0; countpress(false); + if (buttons[count]->justReleased()) { + if (buttons[count]->vpower.is_virtual) { + if (buttons[count]->vpower.is_pushbutton) { + + buttons[count]->vpower.on_off = 0; + Touch_MQTT(count,"PBT"); + buttons[count]->xdrawButton(buttons[count]->vpower.on_off); + } + } + } + if (!buttons[count]->vpower.is_virtual) { + + uint8_t pwr = bitRead(power, rbutt); + uint8_t vpwr = buttons[count]->vpower.on_off; + if (pwr != vpwr) { + Touch_RDW_BUTT(count, pwr); + } + rbutt++; + } + } + } + pLoc.x = 0; + pLoc.y = 0; + } +} + +#endif +#endif + + + + + +bool Xdrv13(uint8_t function) +{ + bool result = false; + + if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { + switch (function) { + case FUNC_PRE_INIT: + DisplayInitDriver(); +#ifdef USE_GRAPH + for (uint8_t count=0;count + +TasmotaSerial *MP3Player; + + + + + +#define D_CMND_MP3 "MP3" + +const char S_JSON_MP3_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MP3 "%s\":%d}"; +const char S_JSON_MP3_COMMAND[] PROGMEM = "{\"" D_CMND_MP3 "%s\"}"; +const char kMP3_Commands[] PROGMEM = "Track|Play|Pause|Stop|Volume|EQ|Device|Reset|DAC"; + + + + + +enum MP3_Commands { + CMND_MP3_TRACK, + CMND_MP3_PLAY, + CMND_MP3_PAUSE, + CMND_MP3_STOP, + CMND_MP3_VOLUME, + CMND_MP3_EQ, + CMND_MP3_DEVICE, + CMND_MP3_RESET, + CMND_MP3_DAC }; + + + + + + +#define MP3_CMD_RESET_VALUE 0 + +#define MP3_CMD_TRACK 0x03 +#define MP3_CMD_PLAY 0x0d +#define MP3_CMD_PAUSE 0x0e +#define MP3_CMD_STOP 0x16 +#define MP3_CMD_VOLUME 0x06 +#define MP3_CMD_EQ 0x07 +#define MP3_CMD_DEVICE 0x09 +#define MP3_CMD_RESET 0x0C +#define MP3_CMD_DAC 0x1A + + + + + + +uint16_t MP3_Checksum(uint8_t *array) +{ + uint16_t checksum = 0; + for (uint32_t i = 0; i < 6; i++) { + checksum += array[i]; + } + checksum = checksum^0xffff; + return (checksum+1); +} + + + + + + +void MP3PlayerInit(void) { + MP3Player = new TasmotaSerial(-1, Pin(GPIO_MP3_DFR562)); + + if (MP3Player->begin(9600)) { + MP3Player->flush(); + delay(1000); + MP3_CMD(MP3_CMD_RESET, MP3_CMD_RESET_VALUE); + delay(3000); + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} +# 159 "/workspace/Tasmota/tasmota/xdrv_14_mp3.ino" +void MP3_CMD(uint8_t mp3cmd,uint16_t val) { + uint8_t i = 0; + uint8_t cmd[10] = {0x7e,0xff,6,0,0,0,0,0,0,0xef}; + cmd[3] = mp3cmd; + cmd[4] = 0; + cmd[5] = val>>8; + cmd[6] = val; + uint16_t chks = MP3_Checksum(&cmd[1]); + cmd[7] = chks>>8; + cmd[8] = chks; + MP3Player->write(cmd, sizeof(cmd)); + delay(1000); + if (mp3cmd == MP3_CMD_RESET) { + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} + + + + + +bool MP3PlayerCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_MP3); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMP3_Commands); + + switch (command_code) { + case CMND_MP3_TRACK: + case CMND_MP3_VOLUME: + case CMND_MP3_EQ: + case CMND_MP3_DEVICE: + case CMND_MP3_DAC: + + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_MP3_TRACK) { MP3_CMD(MP3_CMD_TRACK, XdrvMailbox.payload); } + if (command_code == CMND_MP3_VOLUME) { MP3_CMD(MP3_CMD_VOLUME, XdrvMailbox.payload * 30 / 100); } + if (command_code == CMND_MP3_EQ) { MP3_CMD(MP3_CMD_EQ, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DEVICE) { MP3_CMD(MP3_CMD_DEVICE, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DAC) { MP3_CMD(MP3_CMD_DAC, XdrvMailbox.payload); } + } + Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MP3_PLAY: + case CMND_MP3_PAUSE: + case CMND_MP3_STOP: + case CMND_MP3_RESET: + + if (command_code == CMND_MP3_PLAY) { MP3_CMD(MP3_CMD_PLAY, 0); } + if (command_code == CMND_MP3_PAUSE) { MP3_CMD(MP3_CMD_PAUSE, 0); } + if (command_code == CMND_MP3_STOP) { MP3_CMD(MP3_CMD_STOP, 0); } + if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); } + Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +bool Xdrv14(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_MP3_DFR562)) { + switch (function) { + case FUNC_PRE_INIT: + MP3PlayerInit(); + break; + case FUNC_COMMAND: + result = MP3PlayerCmd(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_15_pca9685.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_15_pca9685.ino" +#ifdef USE_I2C +#ifdef USE_PCA9685 + + + + + + +#define XDRV_15 15 +#define XI2C_01 1 + +#define PCA9685_REG_MODE1 0x00 +#define PCA9685_REG_LED0_ON_L 0x06 +#define PCA9685_REG_PRE_SCALE 0xFE + +#ifndef USE_PCA9685_ADDR + #define USE_PCA9685_ADDR 0x40 +#endif +#ifndef USE_PCA9685_FREQ + #define USE_PCA9685_FREQ 50 +#endif + +bool pca9685_detected = false; +uint16_t pca9685_freq = USE_PCA9685_FREQ; +uint16_t pca9685_pin_pwm_value[16]; + +void PCA9685_Detect(void) +{ + if (I2cActive(USE_PCA9685_ADDR)) { return; } + + uint8_t buffer; + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + if (0x20 == buffer) { + pca9685_detected = true; + I2cSetActiveFound(USE_PCA9685_ADDR, "PCA9685"); + PCA9685_Reset(); + } + } + } +} + +void PCA9685_Reset(void) +{ + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); + PCA9685_SetPWMfreq(USE_PCA9685_FREQ); + for (uint32_t pin=0;pin<16;pin++) { + PCA9685_SetPWM(pin,0,false); + pca9685_pin_pwm_value[pin] = 0; + } + Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); +} + +void PCA9685_SetPWMfreq(double freq) { + + + + + if (freq > 23 && freq < 1527) { + pca9685_freq=freq; + } else { + pca9685_freq=50; + } + uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; + if (1526 == pca9685_freq) pre_scale_osc=0xFF; + uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); + uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); +} + +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { + uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; + uint32_t led_data = 0; + I2cWrite8(USE_PCA9685_ADDR, led_reg, on); + I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); + I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); + I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); +} + +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { + if (4096 == pwm) { + PCA9685_SetPWM_Reg(pin, 4096, 0); + } else { + PCA9685_SetPWM_Reg(pin, 0, pwm); + } + pca9685_pin_pwm_value[pin] = pwm; +} + +bool PCA9685_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((new_freq >= 24) && (new_freq <= 1526)) { + PCA9685_SetPWMfreq(new_freq); + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); + return serviced; + } + } else { + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); + return serviced; + } + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (paramcount > 2) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { + PCA9685_SetPWM(pin, 4096, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); + serviced = true; + return serviced; + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { + PCA9685_SetPWM(pin, 0, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); + serviced = true; + return serviced; + } + uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { + PCA9685_SetPWM(pin, pwm, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); + serviced = true; + return serviced; + } + } + } + } + return serviced; +} + +void PCA9685_OutputTelemetry(bool telemetry) +{ + ResponseTime_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); + for (uint32_t pin=0;pin<16;pin++) { + ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pca9685_pin_pwm_value[pin]); + } + ResponseAppend_P(PSTR("\"END\":1}}")); + if (telemetry) { + MqttPublishTeleSensor(); + } +} + +bool Xdrv15(uint8_t function) +{ + if (!I2cEnabled(XI2C_01)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PCA9685_Detect(); + } + else if (pca9685_detected) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == 0) { + PCA9685_OutputTelemetry(true); + } + break; + case FUNC_COMMAND_DRIVER: + if (XDRV_15 == XdrvMailbox.index) { + result = PCA9685_Command(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +#ifdef USE_LIGHT +#ifdef USE_TUYA_MCU + +#define XDRV_16 16 +#define XNRG_32 32 + +#ifndef TUYA_DIMMER_ID +#define TUYA_DIMMER_ID 0 +#endif + +#define TUYA_CMD_HEARTBEAT 0x00 +#define TUYA_CMD_QUERY_PRODUCT 0x01 +#define TUYA_CMD_MCU_CONF 0x02 +#define TUYA_CMD_WIFI_STATE 0x03 +#define TUYA_CMD_WIFI_RESET 0x04 +#define TUYA_CMD_WIFI_SELECT 0x05 +#define TUYA_CMD_SET_DP 0x06 +#define TUYA_CMD_STATE 0x07 +#define TUYA_CMD_QUERY_STATE 0x08 +#define TUYA_CMD_SET_TIME 0x1C + +#define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 +#define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 +#define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04 +#define TUYA_LOW_POWER_CMD_STATE 0x05 + +#define TUYA_TYPE_BOOL 0x01 +#define TUYA_TYPE_VALUE 0x02 +#define TUYA_TYPE_STRING 0x03 +#define TUYA_TYPE_ENUM 0x04 + +#define TUYA_BUFFER_SIZE 256 + +#include + +TasmotaSerial *TuyaSerial = nullptr; + +struct TUYA { + uint16_t Levels[5] = {0,0,0,0,0}; + uint16_t Snapshot[5] = {0,0,0,0,0}; + char HSBColor[13]; + uint16_t CTMin = 153; + uint16_t CTMax = 500; + bool ModeSet = false; + uint8_t FanState = 0; + bool SuspendTopic = false; + uint32_t ignore_topic_timeout = 0; + bool ignore_dim = false; + uint8_t cmd_status = 0; + uint8_t cmd_checksum = 0; + uint8_t data_len = 0; + uint8_t wifi_state = -2; + uint8_t heartbeat_timer = 0; +#ifdef USE_ENERGY_SENSOR + uint32_t lastPowerCheckTime = 0; +#endif + char *buffer = nullptr; + int byte_counter = 0; + bool low_power_mode = false; + bool send_success_next_second = false; + uint32_t ignore_dimmer_cmd_timeout = 0; + bool ignore_tuyareceived = false; +} Tuya; + +const char kTuyaCommand[] PROGMEM = "|" + D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; + +void (* const TuyaCommand[])(void) PROGMEM = { + &CmndTuyaMcu, &CmndTuyaSend +}; + + + + +bool IsModuleTuya(void) +{ + return ((TUYA_DIMMER == my_module_type) || (SK03_TUYA == my_module_type)); +} + +bool AsModuleTuyaMS(void) +{ + return ((light_type > LT_RGB) && TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0); +} + +bool IsTuyaFanCtrl(void) +{ + return ((TuyaGetDpId(TUYA_MCU_FUNC_FAN3) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN4) != 0) || + (TuyaGetDpId(TUYA_MCU_FUNC_FAN5) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN6) != 0)); +} + +bool TuyaModeSet(void) +{ + return Tuya.ModeSet; +} + +uint8_t TuyaFanSpeeds(void) +{ + uint8_t FanSpeeds = 0; + for (uint32_t i = 0; i <= 3; i++) { + if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) { + FanSpeeds = i + 2; + } + } + return FanSpeeds; +} + +uint8_t TuyaFanState(void) +{ + return Tuya.FanState; +} +# 143 "/workspace/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +void CmndTuyaSend(void) { + if (XdrvMailbox.index > 4 && XdrvMailbox.index < 8) { + return; + } + if (XdrvMailbox.index == 0) { + TuyaRequestState(0); + } else if (XdrvMailbox.index == 8) { + TuyaRequestState(8); + } else if (XdrvMailbox.index == 9) { + if (Settings.tuyamcu_topic) { Settings.tuyamcu_topic = 0; } else { Settings.tuyamcu_topic = 1; } + AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: TuyaMCU Stat Topic %s"), (Settings.tuyamcu_topic ? PSTR("enabled") : PSTR("disabled"))); + + } else { + if (XdrvMailbox.data_len > 0) { + char *p; + char *data; + uint8_t i = 0; + uint8_t dpId = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + if ( i == 0) { + dpId = strtoul(str, nullptr, 0); + } else { + data = str; + } + i++; + } + + if (1 == XdrvMailbox.index) { + TuyaSendBool(dpId, strtoul(data, nullptr, 0)); + } else if (2 == XdrvMailbox.index) { + TuyaSendValue(dpId, strtoull(data, nullptr, 0)); + } else if (3 == XdrvMailbox.index) { + TuyaSendString(dpId, data); + } else if (4 == XdrvMailbox.index) { + TuyaSendEnum(dpId, strtoul(data, nullptr, 0)); + } + } + } + ResponseCmndDone(); +} + + + +void CmndTuyaMcu(void) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint8_t i = 0; + uint8_t parm[3] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; + } + + if (TuyaFuncIdValid(parm[0])) { + + + + + + bool DualDim; + if (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { DualDim = true; } + } else if (TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) { DualDim = true; } + } else if ((TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] == 0) || (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] == 0)) { DualDim = false; }; + if (DualDim) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_CT, 0); } + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_RGB, 0); } + if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_WHITE, 0); } + Settings.flag3.pwm_multi_channels = 1; + } else { Settings.flag3.pwm_multi_channels = 0; } + TuyaAddMcuFunc(parm[0], parm[1]); + restart_flag = 2; + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); + } + } + + Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":[")); + bool added = false; + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid != 0) { + if (added) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"fnId\":%d,\"dpId\":%d}" ), Settings.tuya_fnid_map[i].fnid, Settings.tuya_fnid_map[i].dpid); + added = true; + } + } + ResponseAppend_P(PSTR("]}")); +} + + + + + +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { + bool added = false; + + if (fnId == 0 || dpId == 0) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if ((dpId > 0 && Settings.tuya_fnid_map[i].dpid == dpId) || (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].fnid == fnId)) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + break; + } + } + } else { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].dpid == 0 || Settings.tuya_fnid_map[i].fnid == fnId || Settings.tuya_fnid_map[i].fnid == 0) { + if (!added) { + Settings.tuya_fnid_map[i].fnid = fnId; + Settings.tuya_fnid_map[i].dpid = dpId; + added = true; + } else if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].fnid == fnId) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + } + } + } + } + UpdateDevices(); +} + +void UpdateDevices() { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + uint8_t fnId = Settings.tuya_fnid_map[i].fnid; + if (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].dpid > 0) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + bitSet(rel_inverted, fnId - TUYA_MCU_FUNC_REL1_INV); + } + + } + } +} + +inline bool TuyaFuncIdValid(uint8_t fnId) { + return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || + (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || + (fnId >= TUYA_MCU_FUNC_DIMMER && fnId <= TUYA_MCU_FUNC_REPORT2) || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_BATTERY_PERCENTAGE) || + (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || + (fnId >= TUYA_MCU_FUNC_FAN3 && fnId <= TUYA_MCU_FUNC_FAN6) || + (fnId >= TUYA_MCU_FUNC_MOTOR_DIR && fnId <= TUYA_MCU_FUNC_DUMMY) || + (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); +} + +uint8_t TuyaGetFuncId(uint8_t dpid) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpid) { + return Settings.tuya_fnid_map[i].fnid; + } + } + return TUYA_MCU_FUNC_NONE; +} + +uint8_t TuyaGetDpId(uint8_t fnId) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid == fnId) { + return Settings.tuya_fnid_map[i].dpid; + } + } + return 0; +} + +void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) +{ + uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); + TuyaSerial->write(0x55); + TuyaSerial->write(0xAA); + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write(cmd); + TuyaSerial->write(payload_len >> 8); + TuyaSerial->write(payload_len & 0xFF); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); + for (uint32_t i = 0; i < payload_len; ++i) { + TuyaSerial->write(payload[i]); + checksum += payload[i]; + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); + } + TuyaSerial->write(checksum); + TuyaSerial->flush(); + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); + AddLog(LOG_LEVEL_DEBUG); +} + +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) +{ + uint16_t payload_len = 4; + uint8_t payload_buffer[8]; + payload_buffer[0] = id; + payload_buffer[1] = type; + switch (type) { + case TUYA_TYPE_BOOL: + case TUYA_TYPE_ENUM: + payload_len += 1; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x01; + payload_buffer[4] = value[0]; + break; + case TUYA_TYPE_VALUE: + payload_len += 4; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x04; + payload_buffer[4] = value[3]; + payload_buffer[5] = value[2]; + payload_buffer[6] = value[1]; + payload_buffer[7] = value[0]; + break; + + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +void TuyaSendBool(uint8_t id, bool value) +{ + TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); +} + +void TuyaSendValue(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); +} + +void TuyaSendEnum(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_ENUM, (uint8_t*)(&value)); +} + +void TuyaSendString(uint8_t id, char data[]) { + + uint16_t len = strlen(data); + uint16_t payload_len = 4 + len; + uint8_t payload_buffer[payload_len]; + payload_buffer[0] = id; + payload_buffer[1] = TUYA_TYPE_STRING; + payload_buffer[2] = len >> 8; + payload_buffer[3] = len & 0xFF; + + for (uint16_t i = 0; i < len; i++) { + payload_buffer[4+i] = data[i]; + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +bool TuyaSetPower(void) +{ + bool status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); + if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); + + if (source != SRC_SWITCH && TuyaSerial) { + TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); + delay(20); + status = true; + } + return status; +} + +bool TuyaSetChannels(void) +{ + uint16_t hue, TuyaData; + uint8_t sat, bri; + uint8_t TuyaIdx = 0; + char hex_char[13]; + bool noupd = false; + bool LightMode = TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0; + uint8_t idx = 0; + snprintf_P(hex_char, sizeof(hex_char), PSTR("000000000000")); + + if (LT_SERIAL1 == light_type) { + Tuya.Snapshot[0] = light_state.getDimmer(); + } + if (LT_SERIAL2 == light_type || LT_RGBWC == light_type) { + idx = 1; + if (LT_SERIAL2 == light_type && Settings.flag3.pwm_multi_channels && (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0)) { + + Tuya.Snapshot[0] = changeUIntScale(Light.current_color[0], 0, 255, 0, 100); + Tuya.Snapshot[1] = changeUIntScale(Light.current_color[1], 0, 255, 0, 100); + } else { + light_state.getCTRange(&Tuya.CTMin, &Tuya.CTMax); + Tuya.Snapshot[0] = light_state.getDimmer(); + Tuya.Snapshot[1] = light_state.getCT(); + } + } + if (LT_RGBW == light_type) { + idx = 1; + Tuya.Snapshot[0] = light_state.getDimmer(1); + Tuya.Snapshot[1] = light_state.getDimmer(2); + } + + if (light_type > LT_BASIC) { + + if (LT_RGB != light_type) { + for (uint8_t i = 0; i <= idx; i++) { + if (Tuya.Snapshot[i] != Tuya.Levels[i]) { + if (i == 0 && LightMode && Tuya.ModeSet ) { noupd = true;} + if (!noupd) { + LightSerialDuty(Tuya.Snapshot[i], &hex_char[0], i+1); + Tuya.Levels[i] = Tuya.Snapshot[i]; + } + noupd = false; + } + } + } + + if (light_type >= LT_RGB) { + + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + if (hue != Tuya.Snapshot[2] || sat != Tuya.Snapshot[3] || bri != Tuya.Snapshot[4]) { + if ((LightMode && Tuya.ModeSet) || LT_RGB == light_type) { + snprintf_P(hex_char, sizeof(hex_char), PSTR("%04X%04X%04X"), hue, sat * 10, bri * 10); + LightSerialDuty(0, &hex_char[0], 3); + memcpy_P(Tuya.HSBColor, hex_char, strlen(hex_char)); + Tuya.Snapshot[2] = hue; + Tuya.Snapshot[3] = sat; + Tuya.Snapshot[4] = bri; + } + } + } + } + return true; +} + +void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx) +{ + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); + bool CTLight = false; + + if (TuyaIdx > 0 && TuyaIdx <= 2) { + + if (TuyaIdx == 2) { + if (!Settings.flag3.pwm_multi_channels) { + CTLight = true; + dpid = TuyaGetDpId(TUYA_MCU_FUNC_CT); + } else { dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2); } + } + + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { + if (TuyaIdx == 2 && CTLight) { + duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0); + } else { duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max); } + + if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } + Tuya.ignore_dimmer_cmd_timeout = millis() + 250; + if (Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) { + TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 0); + } + TuyaSendValue(dpid, duty); + + } else if (dpid > 0 && TuyaIdx <= 2) { + + Tuya.ignore_dim = false; + + if (TuyaIdx == 2 && CTLight) { + duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0); + } else { + duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value %d for dpid %d"), duty, dpid); + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); + } + } + + if (TuyaIdx == 3) { + dpid = TuyaGetDpId(TUYA_MCU_FUNC_RGB); + if (!Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) { + TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 1); + } + TuyaSendString(dpid, hex_char); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: TX RGB hex %s to dpId %d"), hex_char, dpid); + } +} + +void TuyaRequestState(uint8_t state_type) +{ + if (TuyaSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); + Tuya.SuspendTopic = true; + Tuya.ignore_topic_timeout = millis() + 1000; + switch (state_type) { + case 0: + TuyaSendCmd(TUYA_CMD_QUERY_STATE); + break; + case 8: + TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + break; + } + } +} + +void TuyaResetWifi(void) +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +void TuyaProcessStatePacket(void) { + char scmnd[20]; + uint8_t dpidStart = 6; + uint8_t fnId; + uint16_t dpDataLen; + + while (dpidStart + 4 < Tuya.byte_counter) { + dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); + + if (Tuya.buffer[dpidStart + 1] == 1) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); + if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); + } + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); + if (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); + } + } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); + + if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) { + SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); + SwitchHandler(1); + } + } + } + else if (Tuya.buffer[dpidStart + 1] == 2) { + bool tuya_energy_enabled = (XNRG_32 == energy_flg); + uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; + uint8_t dimIndex; + if ((fnId == TUYA_MCU_FUNC_FAN3) || (fnId == TUYA_MCU_FUNC_FAN4) || + (fnId == TUYA_MCU_FUNC_FAN5) || (fnId == TUYA_MCU_FUNC_FAN6)) { + Tuya.FanState = packetValue; + } + + if (fnId == TUYA_MCU_FUNC_DIMMER || fnId == TUYA_MCU_FUNC_REPORT1) { dimIndex = 0; } + + if (fnId == TUYA_MCU_FUNC_DIMMER2 || fnId == TUYA_MCU_FUNC_REPORT2 || fnId == TUYA_MCU_FUNC_CT) { dimIndex = 1; } + + if (dimIndex == 1 && !Settings.flag3.pwm_multi_channels) { + Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, Tuya.CTMax, Tuya.CTMin); + } else { + Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]); + + if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1) || + (fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2) || + (fnId == TUYA_MCU_FUNC_CT) || (fnId == TUYA_MCU_FUNC_WHITE)) { + + if (Tuya.ignore_dimmer_cmd_timeout < millis()) { + + if ((power || Settings.flag3.tuya_apply_o20) && ((Tuya.Levels[dimIndex] > 0) && (Tuya.Levels[dimIndex] != Tuya.Snapshot[dimIndex]))) { + + Tuya.ignore_dim = true; + skip_light_fade = true; + + scmnd[0] = '\0'; + if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) { + if (Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[0] - changeUIntScale(Light.current_color[0], 0, 255, 0, 100))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "1 %d"), Tuya.Levels[0]); + } + else if ((abs(Tuya.Levels[0] - light_state.getDimmer())) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.Levels[0]); + } + } + if (((fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2)) && + Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[1] - changeUIntScale(Light.current_color[1], 0, 255, 0, 100))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "2 %d"), Tuya.Levels[1]); + } + if ((fnId == TUYA_MCU_FUNC_CT) && (abs(Tuya.Levels[1] - light_state.getCT())) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLORTEMPERATURE " %d"), Tuya.Levels[1]); + } + if ((fnId == TUYA_MCU_FUNC_WHITE) && (abs(Tuya.Levels[1] - light_state.getDimmer(2))) > 1) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WHITE " %d"), Tuya.Levels[1]); + } + if (scmnd[0] != '\0') { + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + } + } + #ifdef USE_ENERGY_SENSOR + else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { + Energy.voltage[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { + Energy.current[0] = (float)packetValue / 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { + Energy.active_power[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue); + + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; + } + #endif + } + else if (Tuya.buffer[dpidStart + 1] == 3) { + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + if ((TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) && dpDataLen == 12) { + + + char RgbData[13]; + snprintf_P(RgbData, sizeof(RgbData), PSTR("%.*s"), dpDataLen, dpData); + char HSB1[5], HSB2[5], HSB3[5]; + snprintf_P(HSB1, sizeof(HSB1), PSTR("%.4s\n"), &RgbData[0]); + snprintf_P(HSB2, sizeof(HSB2), PSTR("%.4s\n"), &RgbData[4]); + snprintf_P(HSB3, sizeof(HSB3), PSTR("%.4s\n"), &RgbData[8]); + + if ((Tuya.Snapshot[2] != ((int)strtol(HSB1, NULL, 16)) || + Tuya.Snapshot[3] != ((int)strtol(HSB2, NULL, 16)) / 10 || Tuya.Snapshot[4] !=((int)strtol(HSB3, NULL, 16)) / 10)) { + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_HSBCOLOR " %d,%d,%d"), ((int)strtol(HSB1, NULL, 16)), + ((int)strtol(HSB2, NULL, 16)) / 10, ((int)strtol(HSB3, NULL, 16)) / 10); + ExecuteCommand(scmnd, SRC_SWITCH); + + memcpy_P(Tuya.HSBColor, RgbData, strlen(RgbData)); + } + } + } + else if (Tuya.buffer[dpidStart + 1] == 4) { + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + if (fnId == TUYA_MCU_FUNC_MODESET) { + Tuya.ModeSet = dpData[0]; + Tuya.Levels[3] = dpData[0]; + } + } + dpidStart += dpDataLen + 4; + } +} +void TuyaLowPowerModePacketProcess(void) { + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSetWifiLed(); + break; + + case TUYA_LOW_POWER_CMD_STATE: + TuyaProcessStatePacket(); + Tuya.send_success_next_second = true; + break; + } + +} + +void TuyaHandleProductInfoPacket(void) { + uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + char *data = &Tuya.buffer[6]; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data); +} + +void TuyaSendLowPowerSuccessIfNeeded(void) { + uint8_t success = 1; + + if (Tuya.send_success_next_second) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_STATE, &success, 1); + Tuya.send_success_next_second = false; + } +} + +void TuyaNormalPowerModePacketProcess(void) +{ + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSendCmd(TUYA_CMD_MCU_CONF); + break; + + case TUYA_CMD_HEARTBEAT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); + if (Tuya.buffer[6] == 0) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); + Tuya.wifi_state = -2; + } + break; + + case TUYA_CMD_STATE: + TuyaProcessStatePacket(); + break; + + case TUYA_CMD_WIFI_RESET: + case TUYA_CMD_WIFI_SELECT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); + TuyaResetWifi(); + break; + + case TUYA_CMD_WIFI_STATE: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); + Tuya.wifi_state = TuyaGetTuyaWifiState(); + break; + + case TUYA_CMD_MCU_CONF: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); + + if (Tuya.buffer[5] == 2) { + uint8_t led1_gpio = Tuya.buffer[6]; + uint8_t key1_gpio = Tuya.buffer[7]; + bool key1_set = false; + bool led1_set = false; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { + if (Settings.my_gp.io[i] == AGPIO(GPIO_LED1)) led1_set = true; + else if (Settings.my_gp.io[i] == AGPIO(GPIO_KEY1)) key1_set = true; + } + if (!Settings.my_gp.io[led1_gpio] && !led1_set) { + Settings.my_gp.io[led1_gpio] = AGPIO(GPIO_LED1); + restart_flag = 2; + } + if (!Settings.my_gp.io[key1_gpio] && !key1_set) { + Settings.my_gp.io[key1_gpio] = AGPIO(GPIO_KEY1); + restart_flag = 2; + } + } + TuyaRequestState(0); + break; + + default: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); + } +} + + + + + +bool TuyaModuleSelected(void) +{ + if (!PinUsed(GPIO_TUYA_RX) || !PinUsed(GPIO_TUYA_TX)) { + SetPin(1, AGPIO(GPIO_TUYA_TX)); + SetPin(3, AGPIO(GPIO_TUYA_RX)); + Settings.my_gp.io[1] = AGPIO(GPIO_TUYA_TX); + Settings.my_gp.io[3] = AGPIO(GPIO_TUYA_RX); + restart_flag = 2; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); + } + + bool relaySet = false; + + for (uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS; i++) { + if ((Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1 && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8 ) || + (Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1_INV && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8_INV )) { + relaySet = true; + devices_present++; + } + } + + if (!relaySet && TuyaGetDpId(TUYA_MCU_FUNC_DUMMY) == 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); + devices_present++; + SettingsSaveAll(); + } +# 831 "/workspace/Tasmota/tasmota/xdrv_16_tuyamcu.ino" + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) { + light_type = LT_RGBWC; + } else if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) { + light_type = LT_RGBW; + } else { light_type = LT_RGB; } + } else if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0 || TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { + light_type = LT_RGBWC; + } else { light_type = LT_SERIAL2; } + } else { light_type = LT_SERIAL1; } + } else { + light_type = LT_BASIC; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_LOWPOWER_MODE) != 0) { + Tuya.low_power_mode = true; + Settings.flag3.fast_power_cycle_disable = true; + } + + UpdateDevices(); + return true; +} + +void TuyaInit(void) +{ + int baudrate = 9600; + if (Settings.flag4.tuyamcu_baudrate) { baudrate = 115200; } + + Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); + if (Tuya.buffer != nullptr) { + TuyaSerial = new TasmotaSerial(Pin(GPIO_TUYA_RX), Pin(GPIO_TUYA_TX), 2); + if (TuyaSerial->begin(baudrate)) { + if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } + + Tuya.SuspendTopic = true; + Tuya.ignore_topic_timeout = millis() + 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d bps"), baudrate); + TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + + } + } + Tuya.heartbeat_timer = 0; +} + +void TuyaSerialInput(void) +{ + while (TuyaSerial->available()) { + yield(); + uint8_t serial_in_byte = TuyaSerial->read(); + + if (serial_in_byte == 0x55) { + Tuya.cmd_status = 1; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } + else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { + Tuya.cmd_status = 2; + + Tuya.byte_counter = 0; + Tuya.buffer[Tuya.byte_counter++] = 0x55; + Tuya.buffer[Tuya.byte_counter++] = 0xAA; + Tuya.cmd_checksum = 0xFF; + } + else if (Tuya.cmd_status == 2) { + if (Tuya.byte_counter == 5) { + Tuya.cmd_status = 3; + Tuya.data_len = serial_in_byte; + } + Tuya.cmd_checksum += serial_in_byte; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + } + else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + + char hex_char[(Tuya.byte_counter * 2) + 2]; + uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + + Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); + + uint16_t DataVal = 0; + uint8_t dpId = 0; + uint8_t dpDataType = 0; + char DataStr[13]; + + if (len > 0) { + ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); + if (TUYA_CMD_STATE == Tuya.buffer[3]) { + + + uint8_t dpidStart = 6; + + while (dpidStart + 4 < Tuya.byte_counter) { + dpId = Tuya.buffer[dpidStart]; + dpDataType = Tuya.buffer[dpidStart + 1]; + uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + const char *dpHexData = ToHex_P(dpData, dpDataLen, hex_char, sizeof(hex_char)); + + if (TUYA_CMD_STATE == Tuya.buffer[3]) { + ResponseAppend_P(PSTR(",\"DpType%uId%u\":"), dpDataType, dpId); + if (TUYA_TYPE_BOOL == dpDataType && dpDataLen == 1) { + ResponseAppend_P(PSTR("%u"), dpData[0]); + DataVal = dpData[0]; + } else if (TUYA_TYPE_VALUE == dpDataType && dpDataLen == 4) { + uint32_t dpValue = (uint32_t)dpData[0] << 24 | (uint32_t)dpData[1] << 16 | (uint32_t)dpData[2] << 8 | (uint32_t)dpData[3] << 0; + ResponseAppend_P(PSTR("%u"), dpValue); + DataVal = dpValue; + } else if (TUYA_TYPE_STRING == dpDataType) { + ResponseAppend_P(PSTR("\"%.*s\""), dpDataLen, dpData); + snprintf_P(DataStr, sizeof(DataStr), PSTR("%.*s"), dpDataLen, dpData); + } else if (TUYA_TYPE_ENUM == dpDataType && dpDataLen == 1) { + ResponseAppend_P(PSTR("%u"), dpData[0]); + DataVal = dpData[0]; + } else { + ResponseAppend_P(PSTR("\"0x%s\""), dpHexData); + snprintf_P(DataStr, sizeof(DataStr), PSTR("%s"), dpHexData); + } + } + + ResponseAppend_P(PSTR(",\"%d\":{\"DpId\":%d,\"DpIdType\":%d,\"DpIdData\":\"%s\""), dpId, dpId, dpDataType, dpHexData); + if (TUYA_TYPE_STRING == dpDataType) { + ResponseAppend_P(PSTR(",\"Type3Data\":\"%.*s\""), dpDataLen, dpData); + } + ResponseAppend_P(PSTR("}")); + dpidStart += dpDataLen + 4; + } + } + } + ResponseAppend_P(PSTR("}}")); + + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); + } else { + AddLog_P(LOG_LEVEL_DEBUG, mqtt_data); + } + XdrvRulesProcess(); + + if (dpId != 0 && Settings.tuyamcu_topic) { + if (!Tuya.SuspendTopic) { + char scommand[10]; + snprintf_P(scommand, sizeof(scommand), PSTR("TuyaSend%d"), dpDataType); + + if (dpDataType != 3 && dpDataType != 5) { Response_P(PSTR("%d,%u"), dpId, DataVal); } + else { Response_P(PSTR("%d,%s"), dpId, DataStr); } + MqttPublishPrefixTopic_P(STAT, (PSTR("%s"), scommand)); + } + } + + if (!Tuya.low_power_mode) { + TuyaNormalPowerModePacketProcess(); + } else { + TuyaLowPowerModePacketProcess(); + } + + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } else { + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + } +} + +bool TuyaButtonPressed(void) +{ + if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); + TuyaResetWifi(); + return true; + } + return false; +} + +uint8_t TuyaGetTuyaWifiState(void) { + + uint8_t wifi_state = 0x02; + switch(WifiState()){ + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + if (MqttIsConnected()) { + wifi_state = 0x04; + } + + return wifi_state; +} + +void TuyaSetWifiLed(void) +{ + Tuya.wifi_state = TuyaGetTuyaWifiState(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState()); + + if (Tuya.low_power_mode) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } else { + TuyaSendCmd(TUYA_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } +} + +#ifdef USE_TUYA_TIME +void TuyaSetTime(void) { + if (!RtcTime.valid) { return; } + + uint16_t payload_len = 8; + uint8_t payload_buffer[8]; + payload_buffer[0] = 0x01; + payload_buffer[1] = RtcTime.year %100; + payload_buffer[2] = RtcTime.month; + payload_buffer[3] = RtcTime.day_of_month; + payload_buffer[4] = RtcTime.hour; + payload_buffer[5] = RtcTime.minute; + payload_buffer[6] = RtcTime.second; + payload_buffer[7] = RtcTime.day_of_week; + + TuyaSendCmd(TUYA_CMD_SET_TIME, payload_buffer, payload_len); +} +#endif + +#ifdef USE_ENERGY_SENSOR + + + + + +bool Xnrg32(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + if (FUNC_PRE_INIT == function) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { + Energy.current_available = false; + } + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { + Energy.voltage_available = false; + } + energy_flg = XNRG_32; + } + } + } + return result; +} +#endif + + + + + +bool Xdrv16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (TuyaSerial) { TuyaSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = TuyaModuleSelected(); + break; + case FUNC_PRE_INIT: + TuyaInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = TuyaSetPower(); + break; + case FUNC_BUTTON_PRESSED: + result = TuyaButtonPressed(); + break; + case FUNC_EVERY_SECOND: + TuyaSetChannels(); + if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } + if (!Tuya.low_power_mode) { + Tuya.heartbeat_timer++; + if (Tuya.heartbeat_timer > 10) { + Tuya.heartbeat_timer = 0; + TuyaSendCmd(TUYA_CMD_HEARTBEAT); + } +#ifdef USE_TUYA_TIME + if (!(uptime % 60)) { + TuyaSetTime(); + } +#endif + } else { + TuyaSendLowPowerSuccessIfNeeded(); + } + if (Tuya.ignore_topic_timeout < millis()) { Tuya.SuspendTopic = false; } + break; + + + + case FUNC_COMMAND: + result = DecodeCommand(kTuyaCommand, TuyaCommand); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_17_rcswitch.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_17_rcswitch.ino" +#ifdef USE_RC_SWITCH + + + + +#define XDRV_17 17 + +#define D_JSON_RF_PROTOCOL "Protocol" +#define D_JSON_RF_BITS "Bits" +#define D_JSON_RF_DATA "Data" + +#define D_CMND_RFSEND "RFSend" +#define D_JSON_RF_PULSE "Pulse" +#define D_JSON_RF_REPEAT "Repeat" + +const char kRfSendCommands[] PROGMEM = "|" + D_CMND_RFSEND; + +void (* const RfSendCommand[])(void) PROGMEM = { + &CmndRfSend }; + +#include + +RCSwitch mySwitch = RCSwitch(); + +#define RF_TIME_AVOID_DUPLICATE 1000 + +uint32_t rf_lasttime = 0; + +void RfReceiveCheck(void) +{ + if (mySwitch.available()) { + + unsigned long data = mySwitch.getReceivedValue(); + unsigned int bits = mySwitch.getReceivedBitlength(); + int protocol = mySwitch.getReceivedProtocol(); + int delay = mySwitch.getReceivedDelay(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); + + uint32_t now = millis(); + if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { + rf_lasttime = now; + + char stemp[16]; + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); + } + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), + stemp, bits, protocol, delay); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, data); +#endif + } + mySwitch.resetAvailable(); + } +} + +void RfInit(void) +{ + if (PinUsed(GPIO_RFSEND)) { + mySwitch.enableTransmit(Pin(GPIO_RFSEND)); + } + if (PinUsed(GPIO_RFRECV)) { + pinMode( Pin(GPIO_RFRECV), INPUT); + mySwitch.enableReceive(Pin(GPIO_RFRECV)); + } +} + + + + + +void CmndRfSend(void) +{ + bool error = false; + + if (XdrvMailbox.data_len) { + unsigned long data = 0; + unsigned int bits = 24; + int protocol = 1; + int repeat = 10; + int pulse = 350; + + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (root) { + + char parm_uc[10]; + data = root.getUInt(PSTR(D_JSON_RF_DATA), data); + bits = root.getUInt(PSTR(D_JSON_RF_BITS), bits); + protocol = root.getInt(PSTR(D_JSON_RF_PROTOCOL), protocol); + repeat = root.getInt(PSTR(D_JSON_RF_REPEAT), repeat); + pulse = root.getInt(PSTR(D_JSON_RF_PULSE), pulse); + } else { + + char *p; + uint8_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { + switch (i++) { + case 0: + data = strtoul(str, nullptr, 0); + break; + case 1: + bits = atoi(str); + break; + case 2: + protocol = atoi(str); + break; + case 3: + repeat = atoi(str); + break; + case 4: + pulse = atoi(str); + } + } + } + + if (!protocol) { protocol = 1; } + mySwitch.setProtocol(protocol); + if (!pulse) { pulse = 350; } + mySwitch.setPulseLength(pulse); + if (!repeat) { repeat = 10; } + mySwitch.setRepeatTransmit(repeat); + if (!bits) { bits = 24; } + if (data) { + mySwitch.send(data, bits); + ResponseCmndDone(); + } else { + error = true; + } + } else { + error = true; + } + if (error) { + Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); + } +} + + + + + +bool Xdrv17(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_RFSEND) || PinUsed(GPIO_RFRECV)) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (PinUsed(GPIO_RFRECV)) { + RfReceiveCheck(); + } + break; + case FUNC_COMMAND: + if (PinUsed(GPIO_RFSEND)) { + result = DecodeCommand(kRfSendCommands, RfSendCommand); + } + break; + case FUNC_INIT: + RfInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +#ifdef USE_LIGHT +#ifdef USE_ARMTRONIX_DIMMERS + + + + + + + +#define XDRV_18 18 + +#include + +TasmotaSerial *ArmtronixSerial = nullptr; + +struct ARMTRONIX { + bool ignore_dim = false; + int8_t wifi_state = -2; + int8_t dim_state[2]; + int8_t knob_state[2]; +} Armtronix; + + + + + +bool ArmtronixSetChannels(void) +{ + LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); + return true; +} + +void LightSerial2Duty(uint8_t duty1, uint8_t duty2) +{ + if (ArmtronixSerial && !Armtronix.ignore_dim) { + duty1 = ((float)duty1)/2.575757; + duty2 = ((float)duty2)/2.575757; + Armtronix.dim_state[0] = duty1; + Armtronix.dim_state[1] = duty2; + ArmtronixSerial->print("Dimmer1:"); + ArmtronixSerial->print(duty1); + ArmtronixSerial->print("\nDimmer2:"); + ArmtronixSerial->println(duty2); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } else { + Armtronix.ignore_dim = false; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } +} + +void ArmtronixRequestState(void) +{ + if (ArmtronixSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); + ArmtronixSerial->println("Status"); + + } +} + + + + + +bool ArmtronixModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL2; + return true; +} + +void ArmtronixInit(void) +{ + Armtronix.dim_state[0] = -1; + Armtronix.dim_state[1] = -1; + Armtronix.knob_state[0] = -1; + Armtronix.knob_state[1] = -1; + ArmtronixSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); + if (ArmtronixSerial->begin(115200)) { + if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } + ArmtronixSerial->println("Status"); + } +} + +void ArmtronixSerialInput(void) +{ + String answer; + int8_t newDimState[2]; + uint8_t temp; + int commaIndex; + char scmnd[20]; + if (ArmtronixSerial->available()) { + yield(); + answer = ArmtronixSerial->readStringUntil('\n'); + if (answer.substring(0,7) == "Status:") { + commaIndex = 6; + for (uint32_t i =0; i<2; i++) { + newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + if (newDimState[i] != Armtronix.dim_state[i]) { + temp = ((float)newDimState[i])*1.01010101010101; + Armtronix.dim_state[i] = newDimState[i]; + Armtronix.ignore_dim = true; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); + ExecuteCommand(scmnd,SRC_SWITCH); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); + } + commaIndex = answer.indexOf(',',commaIndex+1); + } + Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + commaIndex = answer.indexOf(',',commaIndex+1); + Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + } + } +} + +void ArmtronixSetWifiLed(void) +{ + uint8_t wifi_state = 0x02; + + switch (WifiState()) { + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); + + char state = '0' + ((wifi_state & 1) > 0); + ArmtronixSerial->print("Setled:"); + ArmtronixSerial->write(state); + ArmtronixSerial->write(','); + state = '0' + ((wifi_state & 2) > 0); + ArmtronixSerial->write(state); + ArmtronixSerial->write(10); + Armtronix.wifi_state = WifiState(); +} + + + + + +bool Xdrv18(uint8_t function) +{ + bool result = false; + + if (ARMTRONIX_DIMMERS == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (ArmtronixSerial) { ArmtronixSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = ArmtronixModuleSelected(); + break; + case FUNC_INIT: + ArmtronixInit(); + break; + case FUNC_EVERY_SECOND: + if (ArmtronixSerial) { + if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if (uptime &1) { + ArmtronixSerial->println("Status"); + } + } + break; + case FUNC_SET_CHANNELS: + result = ArmtronixSetChannels(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_PS_16_DZ + + + + +#define XDRV_19 19 + +#define PS16DZ_BUFFER_SIZE 80 + +#include + +TasmotaSerial *PS16DZSerial = nullptr; + +struct PS16DZ { + char *rx_buffer = nullptr; + int byte_counter = 0; + uint8_t dimmer = 0; +} Ps16dz; + + + + + +void PS16DZSerialSend(const char *tx_buffer) +{ + + + PS16DZSerial->print(tx_buffer); + PS16DZSerial->write(0x1B); + PS16DZSerial->flush(); +} + +void PS16DZSerialSendOk(void) +{ + char tx_buffer[16]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); + PS16DZSerialSend(tx_buffer); +} + + + + +void PS16DZSerialSendUpdateCommand(void) +{ + uint8_t light_state_dimmer = light_state.getDimmer(); + + light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; + light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; + + char tx_buffer[80]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), + LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); + + PS16DZSerialSend(tx_buffer); +} + + + + + +void PS16DZSerialInput(void) +{ + char scmnd[20]; + while (PS16DZSerial->available()) { + yield(); + uint8_t serial_in_byte = PS16DZSerial->read(); + if (serial_in_byte != 0x1B) { + if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; + } + } else { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; + + + + + if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { + + } + else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { + + char *end_str; + char *string = Ps16dz.rx_buffer+10; + char *token = strtok_r(string, ",", &end_str); + + bool is_switch_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"switch\"", 8)) { + bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_switch_change = (switch_state != power); + if (is_switch_change) { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"bright\"", 8)) { + Ps16dz.dimmer = atoi(token3); + + + + is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; + if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"sequence\"", 10)) { + + + + } + token = strtok_r(nullptr, ",", &end_str); + } + + if (!is_brightness_change) { + + + + PS16DZSerialSendOk(); + } + } + else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { + + + if (!Settings.flag.button_restrict) { + int state = WIFI_MANAGER; + if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } + if (state != Settings.sta_config) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + } +} + +bool PS16DZSerialSendUpdateCommandIfRequired(void) +{ + if (!PS16DZSerial) { return true; } + + bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); + bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); + + if (is_switch_change || is_brightness_change) { + PS16DZSerialSendUpdateCommand(); + } + + return true; +} + +void PS16DZInit(void) +{ + Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); + if (Ps16dz.rx_buffer != nullptr) { + PS16DZSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); + if (PS16DZSerial->begin(19200)) { + if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } + } + } +} + +bool PS16DZModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + + + + + +bool Xdrv19(uint8_t function) +{ + bool result = false; + + if (PS_16_DZ == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (PS16DZSerial) { PS16DZSerialInput(); } + break; + case FUNC_SET_DEVICE_POWER: + case FUNC_SET_CHANNELS: + result = PS16DZSerialSendUpdateCommandIfRequired(); + break; + case FUNC_INIT: + PS16DZInit(); + break; + case FUNC_MODULE_INIT: + result = PS16DZModuleSelected(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +# 31 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +#define XDRV_20 20 + +const char HUE_RESPONSE[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "HOST: 239.255.255.250:1900\r\n" + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" + "hue-bridgeid: %s\r\n"; +const char HUE_ST1[] PROGMEM = + "ST: upnp:rootdevice\r\n" + "USN: uuid:%s::upnp:rootdevice\r\n" + "\r\n"; +const char HUE_ST2[] PROGMEM = + "ST: uuid:%s\r\n" + "USN: uuid:%s\r\n" + "\r\n"; +const char HUE_ST3[] PROGMEM = + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:%s\r\n" + "\r\n"; + +String HueBridgeId(void) +{ + String temp = WiFi.macAddress(); + temp.replace(":", ""); + String bridgeid = temp.substring(0, 6); + bridgeid += "FFFE"; + bridgeid += temp.substring(6); + return bridgeid; +} + +String HueSerialnumber(void) +{ + String serial = WiFi.macAddress(); + serial.replace(":", ""); + serial.toLowerCase(); + return serial; +} + +String HueUuid(void) +{ + String uuid = F("f6543a06-da50-11ba-8d8f-"); + uuid += HueSerialnumber(); + return uuid; +} + +void HueRespondToMSearch(void) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char response[320]; + snprintf_P(response, sizeof(response), HUE_RESPONSE, WiFi.localIP().toString().c_str(), HueBridgeId().c_str()); + int len = strlen(response); + String uuid = HueUuid(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST1, uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST2, uuid.c_str(), uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST3, uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(message, sizeof(message), PSTR(D_3_RESPONSE_PACKETS_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), + message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + + + +const size_t HUE_DESCRIPTION_XML_SIZE = 626; +const char HUE_DESCRIPTION_XML_COMPRESSED[] PROGMEM = "\x3D\x0E\xD1\xB0\x68\x48\xCD\xFF\xDB\x9C\x7C\x3D\x87\x21\xD1\x9E\xC3\xB4\x7E\x1E" + "\x85\xFC\xCA\x46\xC1\xA1\x77\x8F\x87\xB0\x5F\xF8\xF3\xF0\x62\x98\xDB\xF1\xD6\x2C" + "\x67\x0C\x3A\xF3\xE3\xC7\x98\x8C\xCF\x43\x67\x59\xC8\x75\xB3\xD8\x7E\x1E\x85\xE1" + "\x8C\x32\x33\x04\x1C\x78\xFC\x3D\x06\xD9\xAF\x3E\x7E\x1C\x87\xA1\xD8\x40\x83\x14" + "\xF4\x1B\xBD\x9F\x3F\x0E\x33\xD0\xEC\x20\x41\x8A\x7A\x1D\x80\x8F\x85\x1E\xC3\xD0" + "\x85\x97\xC8\x22\x1D\x7E\x67\xE0\xAA\xA1\x87\x99\xD8\x76\x1E\xD3\x61\xC8\x79\xFD" + "\x9D\x87\xA1\xD8\x40\x87\x50\xF4\x04\x1D\x18\x10\xE2\x15\x19\x0C\x67\xE0\x2B\x6D" + "\xC7\x99\x0E\xBF\x68\x67\x99\xC8\x7A\x1D\x84\x08\xD8\x61\xE8\x63\xFA\xF8\x40\x8C" + "\x8B\xAC\x6B\x3F\x0A\xC6\xD9\xB7\x38\xEB\x26\x18\xAC\x3A\xC8\x51\x59\xD6\x43\xBF" + "\xA2\x0F\x34\x77\x4F\x69\xB0\xE4\x3B\xC7\xA1\xD8\x40\x91\x83\x1E\x83\x6F\x85\x98" + "\xB0\xE8\x5F\xDF\xCF\xC2\xFE\x19\x58\x48\x86\x0A\xD0\xB4\x67\x91\x30\x98\x75\xFC" + "\xED\x0F\xC7\xA1\xD8\x09\x18\x20\x24\x62\x44\x2C\xBE\x41\x02\x1F\x06\xE3\xE3\xE3" + "\xE7\x41\x80\x83\x8D\x1D\x03\xC1\xA0\x93\x89\x10\xB2\xF9\x04\x7E\x1E\x83\x70\x46" + "\x11\x08\xFC\x1F\xF4\x65\x6E\x71\xF8\x08\x7A\x48\xA1\x6D\x10\xC7\xFF\x67\x58\x48" + "\x87\xF7\xEC\x27\xEF\x22\x0B\x47\x85\x56\xF0\xF1\xE8\x76\x02\x66\x2A\x2B\x08\x39" + "\x39\x75\x8D\x60\x91\x90\x0E\x04\x1E\x12\x0E\x53\x94\x40\x85\x88\x82\x17\x08\x98" + "\x23\x08\xB8\x58\xD1\xCF\xE7\xE1\xC5\x49\xB7\x55\xDA\xEC\x81\x0F\x01\x04\x1A\xC7" + "\xA7\x9F\xF6\xC1\x0E\x51\xED\x36\x1C\xB3\xD0\xEC\x20\x48\x9C\x7A\x10\xB2\x10\xB8" + "\xFC\x16\x2F\x44\x3C\xCF\x69\xB0\xE5\x1E\x87\x61\x10\xB2\x10\xB8\xFC\x04\x3E\x38" + "\xCF\xC3\xD0\xEC\xFE\x65\x1F\x93\x85\x23\x74\xE1\x48\xDC"; +# 167 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +const char HUE_LIGHTS_STATUS_JSON1_SUFFIX[] PROGMEM = + "%s\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}"; +# 179 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = + ",\"type\":\"Extended color light\"," + "\"name\":\"%s\"," + "\"modelid\":\"%s\"," + "\"manufacturername\":\"%s\"," + "\"uniqueid\":\"%s\"}"; + + + +const char HUE_GROUP0_STATUS_JSON[] PROGMEM = + "{\"name\":\"Group 0\"," + "\"lights\":[{l1]," + "\"type\":\"LightGroup\"," + "\"action\":"; +# 214 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" +const char HueConfigResponse_JSON[] PROGMEM = + "{\"name\":\"Philips hue\"," + "\"mac\":\"{ma\"," + "\"dhcp\":true," + "\"ipaddress\":\"{ip\"," + "\"netmask\":\"{ms\"," + "\"gateway\":\"{gw\"," + "\"proxyaddress\":\"none\"," + "\"proxyport\":0," + "\"bridgeid\":\"{br\"," + "\"UTC\":\"{dt\"," + "\"whitelist\":{\"{id\":{" + "\"last use date\":\"{dt\"," + "\"create date\":\"{dt\"," + "\"name\":\"Remote\"}}," + "\"swversion\":\"01041302\"," + "\"apiversion\":\"1.17.0\"," + "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," + "\"linkbutton\":false," + "\"portalservices\":false" + "}"; +const char HUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +String GetHueDeviceId(uint16_t id) +{ + String deviceid = WiFi.macAddress(); + deviceid += F(":00:11-"); + deviceid += String(id); + deviceid.toLowerCase(); + return deviceid; +} + +String GetHueUserId(void) +{ + char userid[7]; + + snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP_getChipId()); + return String(userid); +} + +void HandleUpnpSetupHue(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_HUE_BRIDGE_SETUP)); + String description_xml = Decompress(HUE_DESCRIPTION_XML_COMPRESSED,HUE_DESCRIPTION_XML_SIZE); + description_xml.replace("{x1", WiFi.localIP().toString()); + description_xml.replace("{x2", HueUuid()); + description_xml.replace("{x3", HueSerialnumber()); + WSSend(200, CT_XML, description_xml); +} + +void HueNotImplemented(String *path) +{ + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); + + WSSend(200, CT_JSON, "{}"); +} + +void HueConfigResponse(String *response) +{ + *response += FPSTR(HueConfigResponse_JSON); + response->replace("{ma", WiFi.macAddress()); + response->replace("{ip", WiFi.localIP().toString()); + response->replace("{ms", WiFi.subnetMask().toString()); + response->replace("{gw", WiFi.gatewayIP().toString()); + response->replace("{br", HueBridgeId()); + response->replace("{dt", GetDateAndTime(DT_UTC)); + response->replace("{id", GetHueUserId()); +} + +void HueConfig(String *path) +{ + String response = ""; + HueConfigResponse(&response); + WSSend(200, CT_JSON, response); +} + + + +bool g_gotct = false; + + + + +uint16_t prev_hue = 0; +uint8_t prev_sat = 0; +uint8_t prev_bri = 254; +uint16_t prev_ct = 254; +char prev_x_str[24] = "\0"; +char prev_y_str[24] = "\0"; + +uint8_t getLocalLightSubtype(uint8_t device) { + if (light_type) { + if (device >= Light.device) { + if (Settings.flag3.pwm_multi_channels) { + return LST_SINGLE; + } else { + return Light.subtype; + } + } else { + return LST_NONE; + } + } else { + return LST_NONE; + } +} + +void HueLightStatus1(uint8_t device, String *response) +{ + uint16_t ct = 0; + uint8_t color_mode; + String light_status = ""; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint32_t echo_gen = findEchoGeneration(); + + + uint8_t local_light_subtype = getLocalLightSubtype(device); + + bri = LightGetBri(device); + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + bri = (float)((Settings.shutter_options[device-1] & 1) ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; + } +#endif + + if (light_type) { + light_state.getHSB(&hue, &sat, nullptr); + if (sat > 254) sat = 254; + hue = changeUIntScale(hue, 0, 360, 0, 65535); + + if ((sat != prev_sat) || (hue != prev_hue)) { + prev_x_str[0] = prev_y_str[0] = 0; + } + + color_mode = light_state.getColorMode(); + ct = light_state.getCT(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + } + + const size_t buf_size = 256; + char * buf = (char*) malloc(buf_size); + + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (power & (1 << (device-1))) ? "true" : "false"); + + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { + snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs"); + } + if (LST_RGB <= local_light_subtype) { + if (prev_x_str[0] && prev_y_str[0]) { + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); + } else { + float x, y; + light_state.getXY(&x, &y); + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); + } + snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue, sat); + } + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX, buf); + + *response += buf; + free(buf); +} + + + +bool HueActive(uint8_t device) { + if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } + return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); +} + +void HueLightStatus2(uint8_t device, String *response) +{ + const size_t buf_size = 300; + char * buf = (char*) malloc(buf_size); + const size_t max_name_len = 32; + char fname[max_name_len + 1]; + + strlcpy(fname, SettingsText(device <= MAX_FRIENDLYNAMES ? SET_FRIENDLYNAME1 + device -1 : SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1), max_name_len + 1); + + if (device > MAX_FRIENDLYNAMES) { + uint32_t fname_len = strlen(fname); + if (fname_len > max_name_len - 2) { fname_len = max_name_len - 2; } + fname[fname_len++] = '-'; + if (device - MAX_FRIENDLYNAMES < 10) { + fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; + } else { + fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; + } + fname[fname_len] = 0x00; + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, + EscapeJSONString(fname).c_str(), + EscapeJSONString(Settings.user_template_name).c_str(), + PSTR("Tasmota"), + GetHueDeviceId(device).c_str()); + *response += buf; + free(buf); +} + + + + + +#ifndef USE_ZIGBEE +uint32_t EncodeLightId(uint8_t relay_id) +#else +uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0) +#endif +{ + uint8_t mac[6]; + WiFi.macAddress(mac); + uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4); + + if (relay_id >= 32) { + relay_id = 0; + } + if (relay_id > 15) { + id |= (1 << 28); + } + id |= (relay_id & 0xF); +#ifdef USE_ZIGBEE + if ((z_shortaddr) && (!relay_id)) { + + id = (1 << 29) | z_shortaddr; + } +#endif + + return id; +} + + + + + + + +#ifndef USE_ZIGBEE +uint32_t DecodeLightId(uint32_t hue_id) +#else +uint32_t DecodeLightId(uint32_t hue_id, uint16_t * shortaddr = nullptr) +#endif +{ + uint8_t relay_id = hue_id & 0xF; + if (hue_id & (1 << 28)) { + relay_id += 16; + } + if (0 == relay_id) { + relay_id = 32; + } +#ifdef USE_ZIGBEE + if (hue_id & (1 << 29)) { + + if (shortaddr) { *shortaddr = hue_id & 0xFFFF; } + relay_id = 0; + } +#endif + return relay_id; +} + + +inline uint32_t findEchoGeneration(void) { + + return Settings.flag4.alexa_gen_1 ? 1 : 2; +} + +void HueGlobalConfig(String *path) { + String response; + + path->remove(0,1); + response = F("{\"lights\":{"); + bool appending = false; + CheckHue(&response, appending); +#ifdef USE_ZIGBEE + ZigbeeCheckHue(&response, appending); +#endif + response += F("},\"groups\":{},\"schedules\":{},\"config\":"); + HueConfigResponse(&response); + response += "}"; + WSSend(200, CT_JSON, response); +} + +void HueAuthentication(String *path) +{ + char response[38]; + + snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); + WSSend(200, CT_JSON, response); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response); +} + + +void CheckHue(String * response, bool &appending) { + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + for (uint32_t i = 1; i <= maxhue; i++) { + if (HueActive(i)) { + if (appending) { *response += ","; } + *response += "\""; + *response += EncodeLightId(i); + *response += F("\":{\"state\":"); + HueLightStatus1(i, response); + HueLightStatus2(i, response); + appending = true; + } + } +} + +void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool on = false; + bool resp = false; + bool change = false; + uint8_t local_light_subtype = getLocalLightSubtype(device); + + const size_t buf_size = 100; + char * buf = (char*) malloc(buf_size); + + if (Webserver->args()) { + response = "["; + + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { + on = hue_on.getBool(); + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), + device_id, on ? "true" : "false"); + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + if (!change) { + bri = on ? 1.0f : 0.0f; + change = true; + resp = true; + response += buf; + } + } else { +#endif + ExecuteCommandPower(device, (on) ? POWER_ON : POWER_OFF, SRC_HUE); + response += buf; + resp = true; +#ifdef USE_SHUTTER + } +#endif + } + + if (light_type && (local_light_subtype >= LST_SINGLE)) { + if (!Settings.flag3.pwm_multi_channels) { + light_state.getHSB(&hue, &sat, nullptr); + bri = light_state.getBri(); + ct = light_state.getCT(); + uint8_t color_mode = light_state.getColorMode(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + + } else { + bri = LightGetBri(device); + } + } + prev_x_str[0] = prev_y_str[0] = 0; + + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { + bri = hue_bri.getUInt(); + prev_bri = bri; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "bri", bri); + response += buf; + if (LST_SINGLE <= Light.subtype) { + + if (254 <= bri) { bri = 255; } + change = true; + } + resp = true; + } + + + + parser.setCurrent(); + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + float x = tok_x.getFloat(); + float y = tok_y.getFloat(); + strlcpy(prev_x_str, tok_x.getStr(), sizeof(prev_x_str)); + strlcpy(prev_y_str, tok_y.getStr(), sizeof(prev_y_str)); + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); + prev_sat = (sat > 254 ? 254 : sat); + + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), + device_id, prev_x_str, prev_y_str); + response += buf; + g_gotct = false; + resp = true; + change = true; + } + + parser.setCurrent(); + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { + hue = hue_hue.getUInt(); + prev_hue = hue; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "hue", hue); + response += buf; + if (LST_RGB <= Light.subtype) { + + hue = changeUIntScale(hue, 0, 65535, 0, 360); + g_gotct = false; + change = true; + } + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { + sat = hue_sat.getUInt(); + prev_sat = sat; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "sat", sat); + response += buf; + if (LST_RGB <= Light.subtype) { + + if (254 <= sat) { sat = 255; } + g_gotct = false; + change = true; + } + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { + ct = hue_ct.getUInt(); + prev_ct = ct; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "ct", ct); + response += buf; + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + g_gotct = true; + change = true; + } + resp = true; + } + + if (change) { +#ifdef USE_SHUTTER + if (ShutterState(device)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1); + ShutterSetPosition(device, bri * 100.0f ); + } else +#endif + if (light_type && (local_light_subtype > LST_NONE)) { + if (!Settings.flag3.pwm_multi_channels) { + if (g_gotct) { + light_controller.changeCTB(ct, bri); + } else { + light_controller.changeHSB(hue, sat, bri); + } + LightPreparePower(); + } else { + LightSetBri(device, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); + } else { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); + } + XdrvRulesProcess(); + } + change = false; + } + response += "]"; + if (2 == response.length()) { + response = FPSTR(HUE_ERROR_JSON); + } + } + else { + response = FPSTR(HUE_ERROR_JSON); + } + free(buf); +} + +void HueLights(String *path) +{ + + + + String response; + int code = 200; + uint8_t device = 1; + uint32_t device_id; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + path->remove(0,path->indexOf(F("/lights"))); + if (path->endsWith(F("/lights"))) { + response = "{"; + bool appending = false; + CheckHue(&response, appending); +#ifdef USE_ZIGBEE + ZigbeeCheckHue(&response, appending); +#endif +#ifdef USE_SCRIPT_HUE + Script_Check_Hue(&response); +#endif + response += "}"; + } + else if (path->endsWith(F("/state"))) { + path->remove(0,8); + path->remove(path->indexOf(F("/state"))); + device_id = atoi(path->c_str()); + device = DecodeLightId(device_id); +#ifdef USE_ZIGBEE + uint16_t shortaddr; + device = DecodeLightId(device_id, &shortaddr); + if (shortaddr) { + return ZigbeeHandleHue(shortaddr, device_id, response); + } +#endif + +#ifdef USE_SCRIPT_HUE + if (device > devices_present) { + return Script_Handle_Hue(path); + } +#endif + if ((device >= 1) || (device <= maxhue)) { + HueLightsCommand(device, device_id, response); + } + + } + else if(path->indexOf(F("/lights/")) >= 0) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("/lights path=%s"), path->c_str()); + path->remove(0,8); + device_id = atoi(path->c_str()); + device = DecodeLightId(device_id); +#ifdef USE_ZIGBEE + uint16_t shortaddr; + device = DecodeLightId(device_id, &shortaddr); + if (shortaddr) { + ZigbeeHueStatus(&response, shortaddr); + goto exit; + } +#endif + +#ifdef USE_SCRIPT_HUE + if (device > devices_present) { + Script_HueStatus(&response, device-devices_present - 1); + goto exit; + } +#endif + + if ((device < 1) || (device > maxhue)) { + device = 1; + } + response += F("{\"state\":"); + HueLightStatus1(device, &response); + HueLightStatus2(device, &response); + } + else { + response = "{}"; + code = 406; + } + exit: + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); +} + +void HueGroups(String *path) +{ + + + + String response = "{}"; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + + if (path->endsWith("/0")) { + response = FPSTR(HUE_GROUP0_STATUS_JSON); + String lights = F("\"1\""); + for (uint32_t i = 2; i <= maxhue; i++) { + lights += ",\""; + lights += EncodeLightId(i); + lights += "\""; + } + +#ifdef USE_ZIGBEE + ZigbeeHueGroups(&response); +#endif + response.replace("{l1", lights); + HueLightStatus1(1, &response); + response += F("}"); + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups Result (%s)"), path->c_str()); + WSSend(200, CT_JSON, response); +} + +void HandleHueApi(String *path) +{ +# 861 "/workspace/Tasmota/tasmota/xdrv_20_hue.ino" + uint8_t args = 0; + + path->remove(0, 4); + uint16_t apilen = path->length(); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s) from %s"), path->c_str(), Webserver->client().remoteIP().toString().c_str()); + for (args = 0; args < Webserver->args(); args++) { + String json = Webserver->arg(args); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); + } + + if (path->endsWith(F("/invalid/"))) {} + else if (!apilen) HueAuthentication(path); + else if (path->endsWith(F("/"))) HueAuthentication(path); + else if (path->endsWith(F("/config"))) HueConfig(path); + else if (path->indexOf(F("/lights")) >= 0) HueLights(path); + else if (path->indexOf(F("/groups")) >= 0) HueGroups(path); + else if (path->endsWith(F("/schedules"))) HueNotImplemented(path); + else if (path->endsWith(F("/sensors"))) HueNotImplemented(path); + else if (path->endsWith(F("/scenes"))) HueNotImplemented(path); + else if (path->endsWith(F("/rules"))) HueNotImplemented(path); + else if (path->endsWith(F("/resourcelinks"))) HueNotImplemented(path); + else HueGlobalConfig(path); +} + + + + + +bool Xdrv20(uint8_t function) +{ + bool result = false; + +#if defined(USE_SCRIPT_HUE) || defined(USE_ZIGBEE) + if ((EMUL_HUE == Settings.flag2.emulation)) { +#else + if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { +#endif + switch (function) { + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/description.xml"), HandleUpnpSetupHue); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_21_wemo.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_21_wemo.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO) + + + + +#define XDRV_21 21 + +const char WEMO_MSEARCH[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/setup.xml\r\n" + "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" + "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" + "ST: %s\r\n" + "USN: uuid:%s::%s\r\n" + "X-User-Agent: redsonic\r\n" + "\r\n"; + +String WemoSerialnumber(void) +{ + char serial[16]; + + snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP_getChipId()); + return String(serial); +} + +String WemoUuid(void) +{ + char uuid[27]; + + snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str()); + return String(uuid); +} + +void WemoRespondToMSearch(int echo_type) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char type[24]; + if (1 == echo_type) { + strcpy_P(type, URN_BELKIN_DEVICE_CAP); + } else { + strcpy_P(type, UPNP_ROOTDEVICE); + } + char response[400]; + snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type); + PortUdp.write(response); + PortUdp.endPacket(); + snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), + echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + +#ifdef USE_UNISHOX_COMPRESSION + + + +const size_t WEMO_EVENTSERVICE_XML_SIZE = 779; +const char WEMO_EVENTSERVICE_XML[] PROGMEM = "\x3D\x3C\x18\xC1\x11\xB0\x68\x5D\xE3\xE1\xEC\x17\xFE\x3C\xC8\x73\x08\xD3\x78\xF3" + "\xF3\xF9\x9E\x86\xCE\xB3\x90\xEB\x67\xB0\xFC\x3D\x0A\xC3\xAD\xCE\x20\xB7\xD4\x08" + "\x72\x0F\xC3\xD3\xAC\x6B\x3F\x0B\xCE\x88\x76\xF5\xFC\xC8\xBD\x57\x4C\xF4\x3B\x3A" + "\xC6\xB3\xF0\xF4\xBF\x8F\x0B\x1A\xFA\x81\x0B\x0D\x04\x29\x47\xE1\xE9\xF7\x46\x76" + "\x11\xD8\x08\x58\xC0\x27\x62\xBF\x61\x5D\x31\x0B\xD5\x74\xC8\xCE\xFF\xB6\x38\x20" + "\x4A\xC1\x01\x42\xF1\xE8\x26\xFD\x82\x0E\xE7\xBC\x7A\x1D\x80\x8B\x28\xF4\x3B\x01" + "\x17\x59\x04\x48\xE0\x83\xB9\x1D\x80\x87\xC1\x20\x24\x70\x58\x43\xC0\xDA\xF8\x2C" + "\xC1\x74\x0C\x2F\x82\xD0\x42\x8A\x08\x34\x81\x0B\x92\x42\xF5\x5D\x32\xA0\x41\xCE" + "\x7C\x08\xFA\x42\xF3\xE1\x09\x99\xBE\xAF\x1F\x0F\x61\x93\xF1\xEC\x05\x5E\x0A\x44" + "\xBA\xB2\xA3\x21\x8C\xFC\x1D\x98\x11\xE8\x76\x02\x24\xB3\xD0\x46\x62\xC5\x85\x44" + "\x67\x61\x0B\x67\xE1\xC6\x7A\x1D\x84\x09\x13\x0F\x43\xB0\x12\x34\xC0\x60\x5A\xD8" + "\x4C\xCD\x84\x09\x9A\xAF\xAB\xFB\xC3\xC0\xC5\x75\x73\xB0\x13\xB8\x6A\x3B\x3C\x18" + "\xC1\x0F\xC9\xC2\x91\xBA\x70\xA4\x6E"; + + + +const size_t WEMO_METASERVICE_XML_SIZE = 479; +const char WEMO_METASERVICE_XML[] PROGMEM = "\x3D\x3C\x18\xC1\x11\xB0\x68\x5D\xE3\xE1\xEC\x17\xFE\x3C\xC8\x73\x08\xD3\x78\xF3" + "\xF3\xF9\x9E\x86\xCE\xB3\x90\xEB\x67\xB0\xFC\x3D\x0B\xC3\x18\x64\x66\xFF\xED\xCE" + "\x3F\x0F\x41\xB6\x6B\xCF\x9F\x87\x21\xE8\x76\x10\x20\xC5\x3D\x06\xEF\x67\xCF\xC3" + "\x8C\xF4\x3B\x08\x10\x62\x9E\x87\x60\x24\x61\x56\x1D\x6E\x71\x05\xBE\xA0\x43\x90" + "\x7E\x1E\x9D\x63\x59\xF8\x43\xCE\x88\x6B\xAB\x2D\xE3\x18\x7A\x1D\x9D\x63\x59\xF8" + "\x7A\x5F\xC7\x85\x8D\x7D\x40\x83\x85\x7D\xD1\x9D\x84\x8E\xC0\x55\xC3\x3E\xC2\xBA" + "\x62\x17\xAA\xE9\x91\x9D\xFF\x6C\x70\x4C\xFC\x04\x5C\x04\x14\x2D\x9E\x82\x6F\xD8" + "\x20\xEC\x9B\xC7\xA1\xD8\x08\xB2\x8F\x43\xB0\x12\x75\xB3\xB0\x10\xF8\x0A\x04\x28" + "\xA0\x83\x48\x10\xB8\x74\x2F\x55\xD3\x2A\x2B\x04\x1C\xB7\xC0\x8F\x9E\x2F\x3E\x10" + "\x99\x9B\xEA\xF1\xF0\xF6\x19\x3F\x1E\xC0\x42\xE0\x68\x12\xF8\x17\x12\xEA\xCA\x8C" + "\x86\x33\xF3\xD5\xFD\xE1\xE3\xD0\xEC\x04\x49\xA7\xA0\x8C\xC5\x8B\x0A\x88\xCE\xC2" + "\x16\xCF\xC3\x8C\xF4\x3B\x08\x12\x26\x1E\x87\x60\x24\x69\x67\xE1\xE8\x76\x02\x76" + "\xDC\x76\x78\x31\x82\x1F\x93\x85\x23\x74\xE1\x48\xDC"; + + + +const size_t WEMO_RESPONSE_STATE_SOAP_SIZE = 282; +const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = "\x3D\x3C\x79\x93\xE3\x36\x16\x0C\x68\xD8\x34\x2E\xF1\xE7\xE3\xE1\xEC\x15\x54\x30" + "\xF3\x3B\x0E\xCF\x06\x29\x8D\xBF\x1D\x0D\x83\x42\xF6\x58\xC3\xA6\x7C\x78\xEC\xF6" + "\x58\xC3\xB1\x82\x16\x1C\x76\x1E\xC5\xE3\xCD\xF0\x78\x26\xF0\xF1\x7A\x8C\x82\x60" + "\xBF\x8C\x02\x0E\x16\x76\x1E\xC3\xF0\xF4\xF1\xE6\x43\xB0\x43\x23\xF0\xF4\x16\x79" + "\x9F\x41\xBA\x21\xDB\xD7\xF3\x22\xF5\x5D\x32\xFB\xF0\xCC\xEF\x02\x1E\xDE\x2C\xF8" + "\x7B\x05\xFF\x8F\x32\x1C\xC2\x34\xDE\x3C\xFC\xFE\x67\xA1\xB3\xCC\x75\xFB\x43\x66" + "\x6F\xA8\xF3\x39\x0F\x61\xF8\x7A\x10\x23\x63\x67\xE1\xF4\x21\xE8\x76\x02\x3C\xC3" + "\xD0\xEC\x05\x4C\xFC\xFC\x3D\x0E\xC0\x43\xD8\xCE\xC0\x45\xE1\xA0\xFC\x9C\x29\x1B" + "\x8D"; + + + +const size_t WEMO_SETUP_XML_SIZE = 923; +const char WEMO_SETUP_XML[] PROGMEM = "\x3D\x0E\xD1\xB0\x68\x48\xCD\xFF\xDB\x9C\x7C\x3D\x87\x21\xD1\x9E\xC3\xB4\x7E\x1E" + "\x85\xFC\xCA\x46\xC1\xA1\x77\x8F\x87\xB0\x5F\xF8\xF3\x21\xCC\x23\x4D\xE3\xCC\x46" + "\x67\xA1\xB3\xAC\xE4\x3A\xD9\xEC\x3F\x0F\x42\x04\x19\x20\x87\x10\xA8\xC8\x63\x3F" + "\x01\x33\x07\x3C\xC3\xCE\xAF\xE0\x41\x36\x79\x9C\x87\xA1\xD8\x40\x8D\x83\x9E\x86" + "\x3F\xAF\x84\x08\xC8\xBA\xC6\xB3\xF0\xF6\x9B\x0E\x43\xD0\xEC\x20\x48\x9C\x7A\x0D" + "\xBE\x16\x62\xC3\xA1\x7F\x7F\x3F\x01\x07\x31\x45\xBD\x4F\xFD\x75\xB9\xD6\x12\x2D" + "\xE0\xCE\x87\xA1\xD8\x09\x18\x21\xE8\x37\x04\x61\x17\x58\xD6\x7E\x17\xB0\x33\x47" + "\x47\xA1\xD8\x08\xB3\x81\x0A\xC8\xB1\xA3\x9F\xCF\xC3\x96\x74\x99\x34\x81\x0E\xD8" + "\x20\xD0\x3D\x08\x59\x08\x5C\x7E\x0B\x17\xA2\x1E\x67\xB4\xD8\x72\x8F\x43\xB0\x88" + "\x59\x08\x5C\x7E\x1E\x9E\x7F\xDB\x04\x3B\xA7\xB4\xD8\x72\xCF\x43\xB0\x81\x22\x71" + "\xE8\x3B\x7A\xFE\x64\x5E\xAB\xA6\x7E\x1C\x67\xA1\xD8\x40\x8F\x2C\xF4\xF3\xF9\x9E" + "\x86\xC8\x2D\xF5\x02\x24\x90\x44\x8A\x09\x7C\x46\x82\x15\x33\xCC\x75\xFB\x43\x66" + "\x6F\xA8\xF3\x39\x0F\x43\xB0\x81\x1F\x09\x04\x3C\x58\xB4\x40\x4E\xC5\x0B\x44\x04" + "\x6C\x58\x11\x71\x52\xD1\x0F\xC3\xD0\x10\xB8\xE0\x21\x65\xF2\x08\xFC\x3B\x05\x8C" + "\xE1\x87\x60\x21\x4D\x3B\x01\x23\x0D\x04\x6C\x08\xF4\x66\x6F\xA8\xBC\x2C\x70\x22" + "\xE1\xEC\xCD\xF5\x02\x4E\x1A\x08\xF8\x09\xE8\x45\xE0\xC6\x08\x2F\xE1\x11\xF8\x08" + "\x34\x81\x0B\x59\x3A\x1B\x06\x84\x7A\x1D\x80\x87\x5C\x11\x37\x2A\x01\x60\xBC\x34" + "\x0D\x75\x7B\xC6\x30\x18\x5F\x0C\xC0\x87\x8A\x03\x02\xE1\x90\x11\xB0\xB0\x5F\xE1" + "\x88\x11\xB0\xB0\x51\xE1\x80\x10\xEE\x82\xDF\x0C\x60\x87\x18\x10\x79\x7D\x04\x2E" + "\x83\xD1\xF8\x7A\x1D\x9F\xCC\xA3\xF2\x70\xA4\x6E"; +#else +const char WEMO_EVENTSERVICE_XML[] PROGMEM = + "" + "" + "" + "SetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "in" + "" + "" + "" + "" + "GetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "out" + "" + "" + "" + "" + "" + "" + "BinaryState" + "bool" + "0" + "" + "" + "level" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_METASERVICE_XML[] PROGMEM = + "" + "" + "1" + "0" + "" + "" + "" + "GetMetaInfo" + "" + "" + "GetMetaInfo" + "MetaInfo" + "in" + "" + "" + "" + "" + "" + "MetaInfo" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = + "" + "" + "" + "%d" + "" + "" + "\r\n"; + +const char WEMO_SETUP_XML[] PROGMEM = + "" + "" + "" + "urn:Belkin:device:controllee:1" + "{x1" + "Belkin International Inc." + "Socket" + "3.1415" + "uuid:{x2" + "{x3" + "0" + "" + "" + "urn:Belkin:service:basicevent:1" + "urn:Belkin:serviceId:basicevent1" + "/upnp/control/basicevent1" + "/upnp/event/basicevent1" + "/eventservice.xml" + "" + "" + "urn:Belkin:service:metainfo:1" + "urn:Belkin:serviceId:metainfo1" + "/upnp/control/metainfo1" + "/upnp/event/metainfo1" + "/metainfoservice.xml" + "" + "" + "" + "\r\n"; +#endif + + + +void LogUpnpWithClient(const char *msg) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "%s from %s"), msg, Webserver->client().remoteIP().toString().c_str()); +} + +void HandleUpnpEvent(void) +{ + LogUpnpWithClient(PSTR(D_WEMO_BASIC_EVENT)); + + char event[500]; + strlcpy(event, Webserver->arg(0).c_str(), sizeof(event)); + + + + + char state = 'G'; + if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) { + state = 'S'; + uint8_t power = POWER_TOGGLE; + if (strstr_P(event, PSTR("State>10> 1; + if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } + return fanspeed; + } +} + + + +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) +{ + ifan_fanspeed_timer = 0; + ifan_fanspeed_goal = fanspeed; + + uint8_t fanspeed_now = GetFanspeed(); + + if (fanspeed == fanspeed_now) { return; } + + uint8_t fans = kIFan02Speed[fanspeed]; + if (SONOFF_IFAN03 == my_module_type) { + if (sequence) { + fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; + if (fanspeed != ifan_fanspeed_goal) { + if (0 == fanspeed_now) { + ifan_fanspeed_timer = 20; + } else { + ifan_fanspeed_timer = 2; + } + } + } + fans = kIFan03Speed[fanspeed]; + } + for (uint32_t i = 2; i < 5; i++) { + uint8_t state = (fans &1) + POWER_OFF_NO_STATE; + ExecuteCommandPower(i, state, SRC_IGNORE); + fans >>= 1; + } + +#ifdef USE_DOMOTICZ + if (sequence) { DomoticzUpdateFanState(); } +#endif +} + + + +void SonoffIfanReceived(void) +{ + char svalue[32]; + + uint8_t mode = serial_in_buffer[3]; + uint8_t action = serial_in_buffer[6]; + + if (4 == mode) { + if (action < 4) { + + + + + if (action != GetFanspeed()) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); + ExecuteCommand(svalue, SRC_REMOTE); +#ifdef USE_BUZZER + BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); +#endif + } + } else { + + ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); + } + } + if (6 == mode) { + + Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; + } + if (7 == mode) { + +#ifdef USE_BUZZER + BuzzerEnabledBeep(4, 1); +#endif + } + + + + serial_in_buffer[5] = 0; + serial_in_buffer[6] = 0; + for (uint32_t i = 0; i < 7; i++) { + if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } + Serial.write(serial_in_buffer[i]); + } +} + +bool SonoffIfanSerialInput(void) +{ + if (SONOFF_IFAN03 != my_module_type) { return false; } + + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + ifan_receive_flag = true; + } + if (ifan_receive_flag) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 8) { +# 177 "/workspace/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < 7; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[7]) { + SonoffIfanReceived(); + ifan_receive_flag = false; + return true; + } + } + serial_in_byte = 0; + } + return false; +} + + + + + +void CmndFanspeed(void) +{ + if (XdrvMailbox.data_len > 0) { + if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (int16_t)GetFanspeed() -1; + if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } + } + else if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = GetFanspeed() +1; + if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { + SonoffIFanSetFanspeed(XdrvMailbox.payload, true); + } + ResponseCmndNumber(GetFanspeed()); +} + + + +bool SonoffIfanInit(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + SetSerial(9600, TS_SERIAL_8N1); + } + return false; +} + +void SonoffIfanUpdate(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (ifan_fanspeed_timer) { + ifan_fanspeed_timer--; + if (!ifan_fanspeed_timer) { + SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); + } + } + } + + if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { + ifan_restart_flag = false; + SetDevicePower(1, SRC_RETRY); + SetDevicePower(power, SRC_RETRY); + } +} + + + + + +bool Xdrv22(uint8_t function) +{ + bool result = false; + + if (IsModuleIfan()) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + SonoffIfanUpdate(); + break; + case FUNC_SERIAL: + result = SonoffIfanSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); + break; + case FUNC_MODULE_INIT: + result = SonoffIfanInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +#ifdef USE_ZIGBEE + +#if defined(USE_ZIGBEE_ZNP) && defined(USE_ZIGBEE_EZSP) + #error "#define USE_ZIGBEE_ZNP and #define USE_ZIGBEE_EZSP are mutually incompatible" +#endif +#if !defined(USE_ZIGBEE_ZNP) && !defined(USE_ZIGBEE_EZSP) + #error "You must select one of: #define USE_ZIGBEE_ZNP or #define USE_ZIGBEE_EZSP" +#endif + + + +#define OCCUPANCY "Occupancy" +#define ZIGBEE_EZSP_RESET_LED 4 + +typedef uint64_t Z_IEEEAddress; +typedef uint16_t Z_ShortAddress; + +const uint16_t BAD_SHORTADDR = 0xFFFE; + +#ifdef USE_ZIGBEE_ZNP +enum ZnpCommandType { + Z_POLL = 0x00, + Z_SREQ = 0x20, + Z_AREQ = 0x40, + Z_SRSP = 0x60 +}; + +enum ZnpSubsystem { + Z_RPC_Error = 0x00, + Z_SYS = 0x01, + Z_MAC = 0x02, + Z_NWK = 0x03, + Z_AF = 0x04, + Z_ZDO = 0x05, + Z_SAPI = 0x06, + Z_UTIL = 0x07, + Z_DEBUG = 0x08, + Z_APP = 0x09 +}; +#endif + +#ifdef USE_ZIGBEE_EZSP + +enum EZSPNodeType { + EMBER_UNKNOWN_DEVICE = 0x00, + EMBER_COORDINATOR = 0x01, + EMBER_ROUTER = 0x02, + EMBER_END_DEVICE = 0x03, + EMBER_SLEEPY_END_DEVICE = 0x04 +}; + +enum EZSPDeviceUpdate { + EMBER_STANDARD_SECURITY_SECURED_REJOIN = 0x00, + EMBER_STANDARD_SECURITY_UNSECURED_JOIN = 0x01, + EMBER_DEVICE_LEFT = 0x02, + EMBER_STANDARD_SECURITY_UNSECURED_REJOIN = 0x03, +}; + +enum EZSPJoinDecision { + EMBER_USE_PRECONFIGURED_KEY = 0x00, + EMBER_SEND_KEY_IN_THE_CLEAR = 0x01, + EMBER_DENY_JOIN = 0x02, + EMBER_NO_ACTION = 0x03 +}; + +enum EZSPCurrentSecurytBitMask { + EMBER_STANDARD_SECURITY_MODE = 0x0000, + EMBER_DISTRIBUTED_TRUST_CENTER_MODE = 0x0002, + EMBER_GLOBAL_LINK_KEY = 0x0004, + EMBER_TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004, + EMBER_PRECONFIGURED_NETWORK_KEY_MODE = 0x0008, + EMBER_HAVE_TRUST_CENTER_LINK_KEY = 0x0010, + EMBER_TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084, + EMBER_HAVE_PRECONFIGURED_KEY = 0x0100, + EMBER_HAVE_NETWORK_KEY = 0x0200, + EMBER_GET_LINK_KEY_WHEN_JOINING = 0x0400, + EMBER_REQUIRE_ENCRYPTED_KEY = 0x0800, + EMBER_NO_FRAME_COUNTER_RESET = 0x1000, + EMBER_GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000, + EMBER_HAVE_TRUST_CENTER_EUI64 = 0x0040 +}; + +enum EZSPJoinMethod { + EMBER_USE_MAC_ASSOCIATION = 0x0, + EMBER_USE_NWK_REJOIN = 0x1, + EMBER_USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2, + EMBER_USE_CONFIGURED_NWK_STATE = 0x3 +}; + +enum EZSPConfigId { + EZSP_CONFIG_PACKET_BUFFER_COUNT = 0x01, + EZSP_CONFIG_NEIGHBOR_TABLE_SIZE = 0x02, + EZSP_CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03, + EZSP_CONFIG_BINDING_TABLE_SIZE = 0x04, + EZSP_CONFIG_ADDRESS_TABLE_SIZE = 0x05, + EZSP_CONFIG_MULTICAST_TABLE_SIZE = 0x06, + EZSP_CONFIG_ROUTE_TABLE_SIZE = 0x07, + EZSP_CONFIG_DISCOVERY_TABLE_SIZE = 0x08, + EZSP_CONFIG_STACK_PROFILE = 0x0C, + EZSP_CONFIG_SECURITY_LEVEL = 0x0D, + EZSP_CONFIG_MAX_HOPS = 0x10, + EZSP_CONFIG_MAX_END_DEVICE_CHILDREN = 0x11, + EZSP_CONFIG_INDIRECT_TRANSMISSION_TIMEOUT = 0x12, + EZSP_CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13, + EZSP_CONFIG_TX_POWER_MODE = 0x17, + EZSP_CONFIG_DISABLE_RELAY = 0x18, + EZSP_CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19, + EZSP_CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A, + EZSP_CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C, + EZSP_CONFIG_FRAGMENT_DELAY_MS = 0x1D, + EZSP_CONFIG_KEY_TABLE_SIZE = 0x1E, + EZSP_CONFIG_APS_ACK_TIMEOUT = 0x1F, + EZSP_CONFIG_BEACON_JITTER_DURATION = 0x20, + EZSP_CONFIG_END_DEVICE_BIND_TIMEOUT = 0x21, + EZSP_CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD = 0x22, + EZSP_CONFIG_REQUEST_KEY_TIMEOUT = 0x24, + EZSP_CONFIG_CERTIFICATE_TABLE_SIZE = 0x29, + EZSP_CONFIG_APPLICATION_ZDO_FLAGS = 0x2A, + EZSP_CONFIG_BROADCAST_TABLE_SIZE = 0x2B, + EZSP_CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C, + EZSP_CONFIG_SUPPORTED_NETWORKS = 0x2D, + EZSP_CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E, + EZSP_CONFIG_ZLL_GROUP_ADDRESSES = 0x2F, + EZSP_CONFIG_ZLL_RSSI_THRESHOLD = 0x30, + EZSP_CONFIG_MTORR_FLOW_CONTROL = 0x33, + EZSP_CONFIG_RETRY_QUEUE_SIZE = 0x34, + EZSP_CONFIG_NEW_BROADCAST_ENTRY_THRESHOLD = 0x35, + EZSP_CONFIG_BROADCAST_MIN_ACKS_NEEDED = 0x37, + EZSP_CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38, + EZSP_CONFIG_CTUNE_VALUE = 0x39 +}; + +enum EZSPValueId { + EZSP_VALUE_TOKEN_STACK_NODE_DATA = 0x00, + EZSP_VALUE_MAC_PASSTHROUGH_FLAGS = 0x01, + EZSP_VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02, + EZSP_VALUE_FREE_BUFFERS = 0x03, + EZSP_VALUE_UART_SYNCH_CALLBACKS = 0x04, + EZSP_VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05, + EZSP_VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06, + EZSP_VALUE_STACK_TOKEN_WRITING = 0x07, + EZSP_VALUE_STACK_IS_PERFORMING_REJOIN = 0x08, + EZSP_VALUE_MAC_FILTER_LIST = 0x09, + EZSP_VALUE_EXTENDED_SECURITY_BITMASK = 0x0A, + EZSP_VALUE_NODE_SHORT_ID = 0x0B, + EZSP_VALUE_DESCRIPTOR_CAPABILITY = 0x0C, + EZSP_VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D, + EZSP_VALUE_RADIO_HOLD_OFF = 0x0E, + EZSP_VALUE_ENDPOINT_FLAGS = 0x0F, + EZSP_VALUE_MFG_SECURITY_CONFIG = 0x10, + EZSP_VALUE_VERSION_INFO = 0x11, + EZSP_VALUE_NEXT_HOST_REJOIN_REASON = 0x12, + EZSP_VALUE_LAST_REJOIN_REASON = 0x13, + EZSP_VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14, + EZSP_VALUE_CCA_THRESHOLD = 0x15, + EZSP_VALUE_SET_COUNTER_THRESHOLD = 0x17, + EZSP_VALUE_RESET_COUNTER_THRESHOLDS = 0x18, + EZSP_VALUE_CLEAR_COUNTERS = 0x19, + EZSP_VALUE_CERTIFICATE_283K1 = 0x1A, + EZSP_VALUE_PUBLIC_KEY_283K1 = 0x1B, + EZSP_VALUE_PRIVATE_KEY_283K1 = 0x1C, + EZSP_VALUE_NWK_FRAME_COUNTER = 0x23, + EZSP_VALUE_APS_FRAME_COUNTER = 0x24, + EZSP_VALUE_RETRY_DEVICE_TYPE = 0x25, + EZSP_VALUE_ENABLE_R21_BEHAVIOR = 0x29, + EZSP_VALUE_ANTENNA_MODE = 0x30, + EZSP_VALUE_ENABLE_PTA = 0x31, + EZSP_VALUE_PTA_OPTIONS = 0x32, + EZSP_VALUE_MFGLIB_OPTIONS = 0x33, + EZSP_VALUE_USE_NEGOTIATED_POWER_BY_LPD = 0x34, + EZSP_VALUE_PTA_PWM_OPTIONS = 0x35, + EZSP_VALUE_PTA_DIRECTIONAL_PRIORITY_PULSE_WIDTH = 0x36, + EZSP_VALUE_PTA_PHY_SELECT_TIMEOUT = 0x37, + EZSP_VALUE_ANTENNA_RX_MODE = 0x38, + EZSP_VALUE_NWK_KEY_TIMEOUT = 0x39, + EZSP_VALUE_FORCE_TX_AFTER_FAILED_CCA_ATTEMPTS = 0x3A, + EZSP_VALUE_TRANSIENT_KEY_TIMEOUT_S = 0x3B, + ZSP_VALUE_COULOMB_COUNTER_USAGE = 0x3C, + EZSP_VALUE_MAX_BEACONS_TO_STORE = 0x3D, + EZSP_VALUE_END_DEVICE_TIMEOUT_OPTIONS_MASK = 0x3E, + EZSP_VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F, + EZSP_VALUE_GPIO_RADIO_POWER_MASK = 0x40, + EZSP_VALUE_ACTIVE_RADIO_CONFIG = 0x41 +}; + +enum EZSPEmberStatusId { + EMBER_SUCCESS = 0x00, + EMBER_ERR_FATAL = 0x01, + EMBER_BAD_ARGUMENT = 0x02, + EMBER_EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04, + EMBER_INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS = 0x05, + EMBER_EEPROM_MFG_VERSION_MISMATCH = 0x06, + EMBER_EEPROM_STACK_VERSION_MISMATCH = 0x07, + EMBER_NO_BUFFERS = 0x18, + EMBER_SERIAL_INVALID_BAUD_RATE = 0x20, + EMBER_SERIAL_INVALID_PORT = 0x21, + EMBER_SERIAL_TX_OVERFLOW = 0x22, + EMBER_SERIAL_RX_OVERFLOW = 0x23, + EMBER_SERIAL_RX_FRAME_ERROR = 0x24, + EMBER_SERIAL_RX_PARITY_ERROR = 0x25, + EMBER_SERIAL_RX_EMPTY = 0x26, + EMBER_SERIAL_RX_OVERRUN_ERROR = 0x27, + EMBER_MAC_TRANSMIT_QUEUE_FULL = 0x39, + EMBER_MAC_UNKNOWN_HEADER_TYPE = 0x3A, + EMBER_MAC_SCANNING = 0x3D, + EMBER_MAC_NO_DATA = 0x31, + EMBER_MAC_JOINED_NETWORK = 0x32, + EMBER_MAC_BAD_SCAN_DURATION = 0x33, + EMBER_MAC_INCORRECT_SCAN_TYPE = 0x34, + EMBER_MAC_INVALID_CHANNEL_MASK = 0x35, + EMBER_MAC_COMMAND_TRANSMIT_FAILURE = 0x36, + EMBER_MAC_NO_ACK_RECEIVED = 0x40, + EMBER_MAC_INDIRECT_TIMEOUT = 0x42, + EMBER_SIM_EEPROM_ERASE_PAGE_GREEN = 0x43, + EMBER_SIM_EEPROM_ERASE_PAGE_RED = 0x44, + EMBER_SIM_EEPROM_FULL = 0x45, + EMBER_ERR_FLASH_WRITE_INHIBITED = 0x46, + EMBER_ERR_FLASH_VERIFY_FAILED = 0x47, + EMBER_SIM_EEPROM_INIT_1_FAILED = 0x48, + EMBER_SIM_EEPROM_INIT_2_FAILED = 0x49, + EMBER_SIM_EEPROM_INIT_3_FAILED = 0x4A, + EMBER_ERR_FLASH_PROG_FAIL = 0x4B, + EMBER_ERR_FLASH_ERASE_FAIL = 0x4C, + EMBER_ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58, + EMBER_ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59, + EMBER_ERR_BOOTLOADER_NO_IMAGE = 0x5A, + EMBER_DELIVERY_FAILED = 0x66, + EMBER_BINDING_INDEX_OUT_OF_RANGE = 0x69, + EMBER_ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A, + EMBER_INVALID_BINDING_INDEX = 0x6C, + EMBER_INVALID_CALL = 0x70, + EMBER_COST_NOT_KNOWN = 0x71, + EMBER_MAX_MESSAGE_LIMIT_REACHED = 0x72, + EMBER_MESSAGE_TOO_LONG = 0x74, + EMBER_BINDING_IS_ACTIVE = 0x75, + EMBER_ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76, + EMBER_ADC_CONVERSION_DONE = 0x80, + EMBER_ADC_CONVERSION_BUSY = 0x81, + EMBER_ADC_CONVERSION_DEFERRED = 0x82, + EMBER_ADC_NO_CONVERSION_PENDING = 0x84, + EMBER_SLEEP_INTERRUPTED = 0x85, + EMBER_PHY_TX_UNDERFLOW = 0x88, + EMBER_PHY_TX_INCOMPLETE = 0x89, + EMBER_PHY_INVALID_CHANNEL = 0x8A, + EMBER_PHY_INVALID_POWER = 0x8B, + EMBER_PHY_TX_BUSY = 0x8C, + EMBER_PHY_TX_CCA_FAIL = 0x8D, + EMBER_PHY_OSCILLATOR_CHECK_FAILED = 0x8E, + EMBER_PHY_ACK_RECEIVED = 0x8F, + EMBER_NETWORK_UP = 0x90, + EMBER_NETWORK_DOWN = 0x91, + EMBER_JOIN_FAILED = 0x94, + EMBER_MOVE_FAILED = 0x96, + EMBER_CANNOT_JOIN_AS_ROUTER = 0x98, + EMBER_NODE_ID_CHANGED = 0x99, + EMBER_PAN_ID_CHANGED = 0x9A, + EMBER_NO_BEACONS = 0xAB, + EMBER_RECEIVED_KEY_IN_THE_CLEAR = 0xAC, + EMBER_NO_NETWORK_KEY_RECEIVED = 0xAD, + EMBER_NO_LINK_KEY_RECEIVED = 0xAE, + EMBER_PRECONFIGURED_KEY_REQUIRED = 0xAF, + EMBER_NOT_JOINED = 0x93, + EMBER_INVALID_SECURITY_LEVEL = 0x95, + EMBER_NETWORK_BUSY = 0xA1, + EMBER_INVALID_ENDPOINT = 0xA3, + EMBER_BINDING_HAS_CHANGED = 0xA4, + EMBER_INSUFFICIENT_RANDOM_DATA = 0xA5, + EMBER_APS_ENCRYPTION_ERROR = 0xA6, + EMBER_SECURITY_STATE_NOT_SET = 0xA8, + EMBER_KEY_TABLE_INVALID_ADDRESS = 0xB3, + EMBER_SECURITY_CONFIGURATION_INVALID = 0xB7, + EMBER_TOO_SOON_FOR_SWITCH_KEY = 0xB8, + EMBER_KEY_NOT_AUTHORIZED = 0xBB, + EMBER_SECURITY_DATA_INVALID = 0xBD, + EMBER_SOURCE_ROUTE_FAILURE = 0xA9, + EMBER_MANY_TO_ONE_ROUTE_FAILURE = 0xAA, + EMBER_STACK_AND_HARDWARE_MISMATCH = 0xB0, + EMBER_INDEX_OUT_OF_RANGE = 0xB1, + EMBER_TABLE_FULL = 0xB4, + EMBER_TABLE_ENTRY_ERASED = 0xB6, + EMBER_LIBRARY_NOT_PRESENT = 0xB5, + EMBER_OPERATION_IN_PROGRESS = 0xBA, +}; + +enum EZSPStatusId { + EZSP_SUCCESS = 0x00, + EZSP_SPI_ERR_FATAL = 0x10, + EZSP_SPI_ERR_NCP_RESET = 0x11, + EZSP_SPI_ERR_OVERSIZED_EZSP_FRAME = 0x12, + EZSP_SPI_ERR_ABORTED_TRANSACTION = 0x13, + EZSP_SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14, + EZSP_SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15, + EZSP_SPI_ERR_NO_FRAME_TERMINATOR = 0x16, + EZSP_SPI_ERR_EZSP_COMMAND_OVERSIZED = 0x17, + EZSP_SPI_ERR_EZSP_RESPONSE_OVERSIZED = 0x18, + EZSP_SPI_WAITING_FOR_RESPONSE = 0x19, + EZSP_SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A, + EZSP_SPI_ERR_STARTUP_TIMEOUT = 0x1B, + EZSP_SPI_ERR_STARTUP_FAIL = 0x1C, + EZSP_SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D, + EZSP_ASH_IN_PROGRESS = 0x20, + EZSP_HOST_FATAL_ERROR = 0x21, + EZSP_ASH_NCP_FATAL_ERROR = 0x22, + EZSP_DATA_FRAME_TOO_LONG = 0x23, + EZSP_DATA_FRAME_TOO_SHORT = 0x24, + EZSP_NO_TX_SPACE = 0x25, + EZSP_NO_RX_SPACE = 0x26, + EZSP_NO_RX_DATA = 0x27, + EZSP_NOT_CONNECTED = 0x28, + EZSP_ERROR_VERSION_NOT_SET = 0x30, + EZSP_ERROR_INVALID_FRAME_ID = 0x31, + EZSP_ERROR_WRONG_DIRECTION = 0x32, + EZSP_ERROR_TRUNCATED = 0x33, + EZSP_ERROR_OVERFLOW = 0x34, + EZSP_ERROR_OUT_OF_MEMORY = 0x35, + EZSP_ERROR_INVALID_VALUE = 0x36, + EZSP_ERROR_INVALID_ID = 0x37, + EZSP_ERROR_INVALID_CALL = 0x38, + EZSP_ERROR_NO_RESPONSE = 0x39, + EZSP_ERROR_COMMAND_TOO_LONG = 0x40, + EZSP_ERROR_QUEUE_FULL = 0x41, + EZSP_ERROR_COMMAND_FILTERED = 0x42, + EZSP_ERROR_SECURITY_KEY_ALREADY_SET = 0x43, + EZSP_ERROR_SECURITY_TYPE_INVALID = 0x44, + EZSP_ERROR_SECURITY_PARAMETERS_INVALID = 0x45, + EZSP_ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46, + EZSP_ERROR_SECURITY_KEY_NOT_SET = 0x47, + EZSP_ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48, + EZSP_ERROR_UNSUPPORTED_CONTROL = 0x49, + EZSP_ERROR_UNSECURE_FRAME = 0x4A, + EZSP_NO_ERROR = 0xFF +}; + +enum EZSPPolicyId { + EZSP_TRUST_CENTER_POLICY = 0x00, + EZSP_BINDING_MODIFICATION_POLICY = 0x01, + EZSP_UNICAST_REPLIES_POLICY = 0x02, + EZSP_POLL_HANDLER_POLICY = 0x03, + EZSP_MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04, + EZSP_TC_KEY_REQUEST_POLICY = 0x05, + EZSP_APP_KEY_REQUEST_POLICY = 0x06, + EZSP_PACKET_VALIDATE_LIBRARY_POLICY = 0x07, + EZSP_ZLL_POLICY = 0x08, + EZSP_TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 +}; + +enum EZSPDecisionBitmask { + EZSP_DECISION_BITMASK_DEFAULT_CONFIGURATION = 0x0000, + EZSP_DECISION_ALLOW_JOINS = 0x0001, + EZSP_DECISION_ALLOW_UNSECURED_REJOINS = 0x0002, + EZSP_DECISION_SEND_KEY_IN_CLEAR = 0x0004, + EZSP_DECISION_IGNORE_UNSECURED_REJOINS = 0x0008, + EZSP_DECISION_JOINS_USE_INSTALL_CODE_KEY = 0x0010, + EZSP_DECISION_DEFER_JOINS = 0x0020 +}; + +enum EZSPDecisionId { + EZSP_DEFER_JOINS_REJOINS_HAVE_LINK_KEY = 0x07, + EZSP_DISALLOW_BINDING_MODIFICATION = 0x10, + EZSP_ALLOW_BINDING_MODIFICATION = 0x11, + EZSP_CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS = 0x12, + EZSP_HOST_WILL_NOT_SUPPLY_REPLY = 0x20, + EZSP_HOST_WILL_SUPPLY_REPLY = 0x21, + EZSP_POLL_HANDLER_IGNORE = 0x30, + EZSP_POLL_HANDLER_CALLBACK = 0x31, + EZSP_MESSAGE_TAG_ONLY_IN_CALLBACK = 0x40, + EZSP_MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK = 0x41, + EZSP_DENY_TC_KEY_REQUESTS = 0x50, + EZSP_ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY = 0x51, + EZSP_ALLOW_TC_KEY_REQUEST_AND_GENERATE_NEW_KEY = 0x52, + EZSP_DENY_APP_KEY_REQUESTS = 0x60, + EZSP_ALLOW_APP_KEY_REQUESTS = 0x61, + EZSP_PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62, + EZSP_PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 +}; + +enum EZSP_ZdoConfigurationFlags { + EMBER_APP_RECEIVES_SUPPORTED_ZDO_REQUESTS = 0x01, + EMBER_APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS = 0x02, + EMBER_APP_HANDLES_ZDO_ENDPOINT_REQUESTS = 0x04, + EMBER_APP_HANDLES_ZDO_BINDING_REQUESTS = 0x08 +}; + +enum EZSP_EmberIncomingMessageType { + EMBER_INCOMING_UNICAST = 0x00, + EMBER_INCOMING_UNICAST_REPLY = 0x01, + EMBER_INCOMING_MULTICAST = 0x02, + EMBER_INCOMING_MULTICAST_LOOPBACK = 0x03, + EMBER_INCOMING_BROADCAST = 0x04, + EMBER_INCOMING_BROADCAST_LOOPBACK = 0x05, + EMBER_INCOMING_MANY_TO_ONE_ROUTE_REQUEST = 0x06 +}; + +enum EZSP_EmberApsOption { + EMBER_APS_OPTION_NONE = 0x0000, + EMBER_APS_OPTION_ENCRYPTION = 0x0020, + EMBER_APS_OPTION_RETRY = 0x0040, + EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY = 0x0100, + EMBER_APS_OPTION_FORCE_ROUTE_DISCOVERY = 0x0200, + EMBER_APS_OPTION_SOURCE_EUI64 = 0x0400, + EMBER_APS_OPTION_DESTINATION_EUI64 = 0x0800, + EMBER_APS_OPTION_ENABLE_ADDRESS_DISCOVERY = 0x1000, + EMBER_APS_OPTION_POLL_RESPONSE = 0x2000, + EMBER_APS_OPTION_ZDO_RESPONSE_REQUIRED = 0x4000, + EMBER_APS_OPTION_FRAGMENT = 0x8000 +}; + +enum EZSP_EmberOutgoingMessageType { + EMBER_OUTGOING_DIRECT = 0x00, + EMBER_OUTGOING_VIA_ADDRESS_TABLE = 0x01, + EMBER_OUTGOING_VIA_BINDING = 0x02, + EMBER_OUTGOING_MULTICAST = 0x03, + EMBER_OUTGOING_MULTICAST_WITH_ALIAS = 0x04, + EMBER_OUTGOING_BROADCAST_WITH_ALIAS = 0x05, + EMBER_OUTGOING_BROADCAST = 0x06 +}; + +enum EZSP_EmberKeyStructBitmask { + EMBER_KEY_HAS_SEQUENCE_NUMBER = 0x0001, + EMBER_KEY_HAS_OUTGOING_FRAME_COUNTER = 0x0002, + EMBER_KEY_HAS_INCOMING_FRAME_COUNTER = 0x0004, + EMBER_KEY_HAS_PARTNER_EUI64 = 0x0008, + EMBER_KEY_IS_AUTHORIZED = 0x0010, + EMBER_KEY_PARTNER_IS_SLEEPY = 0x0020, + EMBER_UNCONFIRMED_TRANSIENT_KEY = 0x0040 +}; + +enum EZSP_EmberKeyType { + EMBER_TRUST_CENTER_LINK_KEY = 1, + EMBER_CURRENT_NETWORK_KEY = 3, + EMBER_NEXT_NETWORK_KEY = 4, + EMBER_APPLICATION_LINK_KEY = 5 +}; + + +enum EZSP_ZDO { + ZDO_NWK_addr_req = 0x0000, + ZDO_IEEE_addr_req = 0x0001, + ZDO_Node_Desc_req = 0x0002, + ZDO_Power_Desc_req = 0x0003, + ZDO_Simple_Desc_req = 0x0004, + ZDO_Active_EP_req = 0x0005, + ZDO_Match_Desc_req = 0x0006, + ZDO_Complex_Desc_req = 0x0010, + ZDO_User_Desc_req = 0x0011, + ZDO_Discovery_Cache_req = 0x0012, + ZDO_Device_annce = 0x0013, + ZDO_User_Desc_set = 0x0014, + ZDO_System_Server_Discovery_req = 0x0015, + ZDO_Discovery_store_req = 0x0016, + ZDO_Node_Desc_store_req = 0x0017, + ZDO_Active_EP_store_req = 0x0019, + ZDO_Simple_Desc_store_req = 0x001A, + ZDO_Remove_node_cache_req = 0x001B, + ZDO_Find_node_cache_req = 0x001C, + ZDO_Extended_Simple_Desc_req = 0x001D, + ZDO_Extended_Active_EP_req = 0x001E, + ZDO_Parent_annce = 0x001F, + + ZDO_End_Device_Bind_req = 0x0020, + ZDO_Bind_req = 0x0021, + ZDO_Unbind_req = 0x0022, + + ZDO_Mgmt_Lqi_req = 0x0031, + ZDO_Mgmt_Rtg_req = 0x0032, + ZDO_Mgmt_Bind_req = 0x0033, + ZDO_Mgmt_Leave_req = 0x0034, + ZDO_Mgmt_Permit_Joining_req = 0x0036, + ZDO_Mgmt_NWK_Update_req = 0x0038, + + + + ZDO_NWK_addr_rsp = 0x8000, + ZDO_IEEE_addr_rsp = 0x8001, + ZDO_Node_Desc_rsp = 0x8002, + ZDO_Power_Desc_rsp = 0x8003, + ZDO_Simple_Desc_rsp = 0x8004, + ZDO_Active_EP_rsp = 0x8005, + ZDO_Match_Desc_rsp = 0x8006, + ZDO_Complex_Desc_rsp = 0x8010, + ZDO_User_Desc_rsp = 0x8011, + ZDO_Discovery_Cache_rsp = 0x8012, + ZDO_User_Desc_conf = 0x8014, + ZDO_System_Server_Discovery_rsp = 0x8015, + ZDO_Discovery_Store_rsp = 0x8016, + ZDO_Node_Desc_store_rsp = 0x8017, + ZDO_Power_Desc_store_rsp = 0x8018, + ZDO_Active_EP_store_rsp = 0x8019, + ZDO_Simple_Desc_store_rsp = 0x801A, + ZDO_Remove_node_cache_rsp = 0x801B, + ZDO_Find_node_cache_rsp = 0x801C, + ZDO_Extended_Simple_Desc_rsp = 0x801D, + ZDO_Extended_Active_EP_rsp = 0x801E, + ZDO_Parent_annce_rsp = 0x801F, + + ZDO_End_Device_Bind_rsp = 0x8020, + ZDO_Bind_rsp = 0x8021, + ZDO_Unbind_rsp = 0x8022, + + ZDO_Mgmt_Lqi_rsp = 0x8031, + ZDO_Mgmt_Rtg_rsp = 0x8032, + ZDO_Mgmt_Bind_rsp = 0x8033, + ZDO_Mgmt_Leave_rsp = 0x8034, + ZDO_Mgmt_Permit_Joining_rsp = 0x8036, + ZDO_Mgmt_NWK_Update_rsp = 0x8038, +}; + +enum EZSP_Commands { + EZSP_version = 0x0000, + EZSP_getLibraryStatus = 0x0001, + EZSP_addEndpoint = 0x0002, + EZSP_getExtendedValue = 0x0003, + EZSP_getNextBeacon = 0x0004, + EZSP_nop = 0x0005, + EZSP_callback = 0x0006, + EZSP_noCallbacks = 0x0007, + EZSP_getNumStoredBeacons = 0x0008, + EZSP_setToken = 0x0009, + EZSP_getToken = 0x000A, + EZSP_getMfgToken = 0x000B, + EZSP_setMfgToken = 0x000C, + EZSP_stackTokenChangedHandler = 0x000D, + EZSP_setTimer = 0x000E, + EZSP_timerHandler = 0x000F, + EZSP_setConcentrator = 0x0010, + EZSP_setBrokenRouteErrorCode = 0x0011, + EZSP_debugWrite = 0x0012, + EZSP_getXncpInfo = 0x0013, + EZSP_requestLinkKey = 0x0014, + EZSP_setManufacturerCode = 0x0015, + EZSP_setPowerDescriptor = 0x0016, + EZSP_networkInit = 0x0017, + EZSP_networkState = 0x0018, + EZSP_stackStatusHandler = 0x0019, + EZSP_startScan = 0x001A, + EZSP_networkFoundHandler = 0x001B, + EZSP_scanCompleteHandler = 0x001C, + EZSP_stopScan = 0x001D, + EZSP_formNetwork = 0x001E, + EZSP_joinNetwork = 0x001F, + EZSP_leaveNetwork = 0x0020, + EZSP_findAndRejoinNetwork = 0x0021, + EZSP_permitJoining = 0x0022, + EZSP_childJoinHandler = 0x0023, + EZSP_trustCenterJoinHandler = 0x0024, + EZSP_zllClearTokens = 0x0025, + EZSP_getEui64 = 0x0026, + EZSP_getNodeId = 0x0027, + EZSP_getNetworkParameters = 0x0028, + EZSP_getParentChildParameters = 0x0029, + EZSP_clearBindingTable = 0x002A, + EZSP_setBinding = 0x002B, + EZSP_getBinding = 0x002C, + EZSP_deleteBinding = 0x002D, + EZSP_bindingIsActive = 0x002E, + EZSP_getBindingRemoteNodeId = 0x002F, + EZSP_setBindingRemoteNodeId = 0x0030, + EZSP_remoteSetBindingHandler = 0x0031, + EZSP_remoteDeleteBindingHandler = 0x0032, + EZSP_maximumPayloadLength = 0x0033, + EZSP_sendUnicast = 0x0034, + EZSP_getDutyCycleState = 0x0035, + EZSP_sendBroadcast = 0x0036, + EZSP_proxyBroadcast = 0x0037, + EZSP_sendMulticast = 0x0038, + EZSP_sendReply = 0x0039, + EZSP_sendMulticastWithAlias = 0x003A, + EZSP_joinNetworkDirectly = 0x003B, + EZSP_clearStoredBeacons = 0x003C, + EZSP_getFirstBeacon = 0x003D, + EZSP_getNeighborFrameCounter = 0x003E, + EZSP_messageSentHandler = 0x003F, + EZSP_setDutyCycleLimitsInStack = 0x0040, + EZSP_sendManyToOneRouteRequest = 0x0041, + EZSP_pollForData = 0x0042, + EZSP_pollCompleteHandler = 0x0043, + EZSP_pollHandler = 0x0044, + EZSP_incomingMessageHandler = 0x0045, + EZSP_macFilterMatchMessageHandler = 0x0046, + EZSP_customFrame = 0x0047, + EZSP_energyScanResultHandler = 0x0048, + EZSP_getRandomNumber = 0x0049, + EZSP_getChildData = 0x004A, + EZSP_getDutyCycleLimits = 0x004B, + EZSP_getCurrentDutyCycle = 0x004C, + EZSP_dutyCycleHandler = 0x004D, + EZSP_getTimer = 0x004E, + EZSP_getTrueRandomEntropySource = 0x004F, + EZSP_unicastCurrentNetworkKey = 0x0050, + EZSP_sendRawMessageExtended = 0x0051, + EZSP_getConfigurationValue = 0x0052, + EZSP_setConfigurationValue = 0x0053, + EZSP_customFrameHandler = 0x0054, + EZSP_setPolicy = 0x0055, + EZSP_getPolicy = 0x0056, + EZSP_invalidCommand = 0x0058, + EZSP_setSourceRouteDiscoveryMode = 0x005A, + EZSP_addressTableEntryIsActive = 0x005B, + EZSP_setAddressTableRemoteEui64 = 0x005C, + EZSP_setAddressTableRemoteNodeId = 0x005D, + EZSP_getAddressTableRemoteEui64 = 0x005E, + EZSP_getAddressTableRemoteNodeId = 0x005F, + EZSP_lookupNodeIdByEui64 = 0x0060, + EZSP_lookupEui64ByNodeId = 0x0061, + EZSP_incomingSenderEui64Handler = 0x0062, + EZSP_getMulticastTableEntry = 0x0063, + EZSP_setMulticastTableEntry = 0x0064, + EZSP_readAndClearCounters = 0x0065, + EZSP_addOrUpdateKeyTableEntry = 0x0066, + EZSP_sendTrustCenterLinkKey = 0x0067, + EZSP_setInitialSecurityState = 0x0068, + EZSP_getCurrentSecurityState = 0x0069, + EZSP_getKey = 0x006A, + EZSP_clearTransientLinkKeys = 0x006B, + EZSP_updateTcLinkKey = 0x006C, + EZSP_getTransientKeyTableEntry = 0x006D, + EZSP_switchNetworkKeyHandler = 0x006E, + EZSP_aesMmoHash = 0x006F, + EZSP_gpSinkTableInit = 0x0070, + EZSP_getKeyTableEntry = 0x0071, + EZSP_setKeyTableEntry = 0x0072, + EZSP_broadcastNextNetworkKey = 0x0073, + EZSP_broadcastNetworkKeySwitch = 0x0074, + EZSP_findKeyTableEntry = 0x0075, + EZSP_eraseKeyTableEntry = 0x0076, + EZSP_becomeTrustCenter = 0x0077, + EZSP_dsaVerifyHandler = 0x0078, + EZSP_getNeighbor = 0x0079, + EZSP_neighborCount = 0x007A, + EZSP_getRouteTableEntry = 0x007B, + EZSP_idConflictHandler = 0x007C, + EZSP_incomingManyToOneRouteRequestHandler = 0x007D, + EZSP_setExtendedTimeout = 0x007E, + EZSP_getExtendedTimeout = 0x007F, + EZSP_incomingRouteErrorHandler = 0x0080, + EZSP_echo = 0x0081, + EZSP_replaceAddressTableEntry = 0x0082, + EZSP_mfglibStart = 0x0083, + EZSP_mfglibEnd = 0x0084, + EZSP_mfglibStartTone = 0x0085, + EZSP_mfglibStopTone = 0x0086, + EZSP_mfglibStartStream = 0x0087, + EZSP_mfglibStopStream = 0x0088, + EZSP_mfglibSendPacket = 0x0089, + EZSP_mfglibSetChannel = 0x008A, + EZSP_mfglibGetChannel = 0x008B, + EZSP_mfglibSetPower = 0x008C, + EZSP_mfglibGetPower = 0x008D, + EZSP_mfglibRxHandler = 0x008E, + EZSP_launchStandaloneBootloader = 0x008F, + EZSP_sendBootloadMessage = 0x0090, + EZSP_getStandaloneBootloaderVersionPlatMicroPhy = 0x0091, + EZSP_incomingBootloadMessageHandler = 0x0092, + EZSP_bootloadTransmitCompleteHandler = 0x0093, + EZSP_aesEncrypt = 0x0094, + EZSP_overrideCurrentChannel = 0x0095, + EZSP_sendRawMessage = 0x0096, + EZSP_macPassthroughMessageHandler = 0x0097, + EZSP_rawTransmitCompleteHandler = 0x0098, + EZSP_setRadioPower = 0x0099, + EZSP_setRadioChannel = 0x009A, + EZSP_zigbeeKeyEstablishmentHandler = 0x009B, + EZSP_energyScanRequest = 0x009C, + EZSP_delayTest = 0x009D, + EZSP_generateCbkeKeysHandler = 0x009E, + EZSP_calculateSmacs = 0x009F, + EZSP_calculateSmacsHandler = 0x00A0, + EZSP_clearTemporaryDataMaybeStoreLinkKey = 0x00A1, + EZSP_setPreinstalledCbkeData = 0x00A2, + EZSP_dsaVerify = 0x00A3, + EZSP_generateCbkeKeys = 0x00A4, + EZSP_getCertificate = 0x00A5, + EZSP_dsaSign = 0x00A6, + EZSP_dsaSignHandler = 0x00A7, + EZSP_removeDevice = 0x00A8, + EZSP_unicastNwkKeyUpdate = 0x00A9, + EZSP_getValue = 0x00AA, + EZSP_setValue = 0x00AB, + EZSP_setGpioCurrentConfiguration = 0x00AC, + EZSP_setGpioPowerUpDownConfiguration = 0x00AD, + EZSP_setGpioRadioPowerMask = 0x00AE, + EZSP_addTransientLinkKey = 0x00AF, + EZSP_dsaVerify283k1 = 0x00B0, + EZSP_clearKeyTable = 0x00B1, + EZSP_zllNetworkOps = 0x00B2, + EZSP_zllSetInitialSecurityState = 0x00B3, + EZSP_zllStartScan = 0x00B4, + EZSP_zllSetRxOnWhenIdle = 0x00B5, + EZSP_zllNetworkFoundHandler = 0x00B6, + EZSP_zllScanCompleteHandler = 0x00B7, + EZSP_zllAddressAssignmentHandler = 0x00B8, + EZSP_setLogicalAndRadioChannel = 0x00B9, + EZSP_getLogicalChannel = 0x00BA, + EZSP_zllTouchLinkTargetHandler = 0x00BB, + EZSP_zllGetTokens = 0x00BC, + EZSP_zllSetDataToken = 0x00BD, + EZSP_isZllNetwork = 0x00BE, + EZSP_zllSetNonZllNetwork = 0x00BF, + EZSP_gpProxyTableLookup = 0x00C0, + EZSP_getSourceRouteTableEntry = 0x00C1, + EZSP_getSourceRouteTableFilledSize = 0x00C2, + EZSP_getSourceRouteTableTotalSize = 0x00C3, + EZSP_gpepIncomingMessageHandler = 0x00C5, + EZSP_dGpSend = 0x00C6, + EZSP_dGpSentHandler = 0x00C7, + EZSP_gpProxyTableGetEntry = 0x00C8, + EZSP_gpProxyTableProcessGpPairing = 0x00C9, + EZSP_setSecurityKey = 0x00CA, + EZSP_setSecurityParameters = 0x00CB, + EZSP_resetToFactoryDefaults = 0x00CC, + EZSP_getSecurityKeyStatus = 0x00CD, + EZSP_getTransientLinkKey = 0x00CE, + EZSP_zllSetSecurityStateWithoutKey = 0x00CF, + EZSP_setRoutingShortcutThreshold = 0x00D0, + EZSP_getRoutingShortcutThreshold = 0x00D1, + EZSP_unusedPanIdFoundHandler = 0x00D2, + EZSP_findUnusedPanId = 0x00D3, + EZSP_zllSetRadioIdleMode = 0x00D4, + EZSP_setZllNodeType = 0x00D5, + EZSP_setZllAdditionalState = 0x00D6, + EZSP_zllOperationInProgress = 0x00D7, + EZSP_zllRxOnWhenIdleGetActive = 0x00D8, + EZSP_getZllPrimaryChannelMask = 0x00D9, + EZSP_getZllSecondaryChannelMask = 0x00DA, + EZSP_setZllPrimaryChannelMask = 0x00DB, + EZSP_setZllSecondaryChannelMask = 0x00DC, + EZSP_gpSinkTableGetEntry = 0x00DD, + EZSP_gpSinkTableLookup = 0x00DE, + EZSP_gpSinkTableSetEntry = 0x00DF, + EZSP_gpSinkTableRemoveEntry = 0x00E0, + EZSP_gpSinkTableFindOrAllocateEntry = 0x00E1, + EZSP_gpSinkTableClearAll = 0x00E2, + EZSP_setLongUpTime = 0x00E3, + EZSP_setHubConnectivity = 0x00E4, + EZSP_isUpTimeLong = 0x00E5, + EZSP_isHubConnected = 0x00E6, + EZSP_setParentClassificationEnabled = 0x00E7, + EZSP_generateCbkeKeys283k1 = 0x00E8, + EZSP_generateCbkeKeysHandler283k1 = 0x00E9, + EZSP_calculateSmacs283k1 = 0x00EA, + EZSP_calculateSmacsHandler283k1 = 0x00EB, + EZSP_getCertificate283k1 = 0x00EC, + EZSP_savePreinstalledCbkeData283k1 = 0x00ED, + EZSP_clearTemporaryDataMaybeStoreLinkKey283k1 = 0x00EE, + EZSP_setBeaconClassificationParams = 0x00EF, + EZSP_getParentClassificationEnabled = 0x00F0, + EZSP_readCounters = 0x00F1, + EZSP_counterRolloverHandler = 0x00F2, + EZSP_getBeaconClassificationParams = 0x00F3, + EZSP_setMacPollFailureWaitTime = 0x00F4, + EZSP_sendLinkPowerDeltaRequest = 0x00F7, + EZSP_multiPhyStart = 0x00F8, + EZSP_multiPhyStop = 0x00F9, + EZSP_multiPhySetRadioPower = 0x00FA, + EZSP_multiPhySetRadioChannel = 0x00FB, + EZSP_getPhyInterfaceCount = 0x00FC, + EZSP_getRadioParameters = 0x00FD, + EZSP_writeNodeData = 0x00FE, + + EZSP_rstAck = 0xFFFE, +}; + +#endif + + +enum SysCommand { + SYS_RESET = 0x00, + SYS_PING = 0x01, + SYS_VERSION = 0x02, + SYS_SET_EXTADDR = 0x03, + SYS_GET_EXTADDR = 0x04, + SYS_RAM_READ = 0x05, + SYS_RAM_WRITE = 0x06, + SYS_OSAL_NV_ITEM_INIT = 0x07, + SYS_OSAL_NV_READ = 0x08, + SYS_OSAL_NV_WRITE = 0x09, + SYS_OSAL_START_TIMER = 0x0A, + SYS_OSAL_STOP_TIMER = 0x0B, + SYS_RANDOM = 0x0C, + SYS_ADC_READ = 0x0D, + SYS_GPIO = 0x0E, + SYS_STACK_TUNE = 0x0F, + SYS_SET_TIME = 0x10, + SYS_GET_TIME = 0x11, + SYS_OSAL_NV_DELETE = 0x12, + SYS_OSAL_NV_LENGTH = 0x13, + SYS_TEST_RF = 0x40, + SYS_TEST_LOOPBACK = 0x41, + SYS_RESET_IND = 0x80, + SYS_OSAL_TIMER_EXPIRED = 0x81, +}; + +enum SapiCommand { + SAPI_START_REQUEST = 0x00, + SAPI_BIND_DEVICE = 0x01, + SAPI_ALLOW_BIND = 0x02, + SAPI_SEND_DATA_REQUEST = 0x03, + SAPI_READ_CONFIGURATION = 0x04, + SAPI_WRITE_CONFIGURATION = 0x05, + SAPI_GET_DEVICE_INFO = 0x06, + SAPI_FIND_DEVICE_REQUEST = 0x07, + SAPI_PERMIT_JOINING_REQUEST = 0x08, + SAPI_SYSTEM_RESET = 0x09, + SAPI_START_CONFIRM = 0x80, + SAPI_BIND_CONFIRM = 0x81, + SAPI_ALLOW_BIND_CONFIRM = 0x82, + SAPI_SEND_DATA_CONFIRM = 0x83, + SAPI_FIND_DEVICE_CONFIRM = 0x85, + SAPI_RECEIVE_DATA_INDICATION = 0x87, +}; +enum Z_configuration { + CONF_EXTADDR = 0x01, + CONF_BOOTCOUNTER = 0x02, + CONF_STARTUP_OPTION = 0x03, + CONF_START_DELAY = 0x04, + CONF_NIB = 0x21, + CONF_DEVICE_LIST = 0x22, + CONF_ADDRMGR = 0x23, + CONF_POLL_RATE = 0x24, + CONF_QUEUED_POLL_RATE = 0x25, + CONF_RESPONSE_POLL_RATE = 0x26, + CONF_REJOIN_POLL_RATE = 0x27, + CONF_DATA_RETRIES = 0x28, + CONF_POLL_FAILURE_RETRIES = 0x29, + CONF_STACK_PROFILE = 0x2A, + CONF_INDIRECT_MSG_TIMEOUT = 0x2B, + CONF_ROUTE_EXPIRY_TIME = 0x2C, + CONF_EXTENDED_PAN_ID = 0x2D, + CONF_BCAST_RETRIES = 0x2E, + CONF_PASSIVE_ACK_TIMEOUT = 0x2F, + CONF_BCAST_DELIVERY_TIME = 0x30, + CONF_NWK_MODE = 0x31, + CONF_CONCENTRATOR_ENABLE = 0x32, + CONF_CONCENTRATOR_DISCOVERY = 0x33, + CONF_CONCENTRATOR_RADIUS = 0x34, + CONF_CONCENTRATOR_RC = 0x36, + CONF_NWK_MGR_MODE = 0x37, + CONF_SRC_RTG_EXPIRY_TIME = 0x38, + CONF_ROUTE_DISCOVERY_TIME = 0x39, + CONF_NWK_ACTIVE_KEY_INFO = 0x3A, + CONF_NWK_ALTERN_KEY_INFO = 0x3B, + CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, + CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, + CONF_NWK_CHILD_AGE_ENABLE = 0x3E, + CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, + CONF_BINDING_TABLE = 0x41, + CONF_GROUP_TABLE = 0x42, + CONF_APS_FRAME_RETRIES = 0x43, + CONF_APS_ACK_WAIT_DURATION = 0x44, + CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, + CONF_BINDING_TIME = 0x46, + CONF_APS_USE_EXT_PANID = 0x47, + CONF_APS_USE_INSECURE_JOIN = 0x48, + CONF_COMMISSIONED_NWK_ADDR = 0x49, + CONF_APS_NONMEMBER_RADIUS = 0x4B, + CONF_APS_LINK_KEY_TABLE = 0x4C, + CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, + CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, + CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, + CONF_DIAGNOSTIC_STATS = 0x50, + CONF_SECURITY_LEVEL = 0x61, + CONF_PRECFGKEY = 0x62, + CONF_PRECFGKEYS_ENABLE = 0x63, + CONF_SECURITY_MODE = 0x64, + CONF_SECURE_PERMIT_JOIN = 0x65, + CONF_APS_LINK_KEY_TYPE = 0x66, + CONF_APS_ALLOW_R19_SECURITY = 0x67, + CONF_IMPLICIT_CERTIFICATE = 0x69, + CONF_DEVICE_PRIVATE_KEY = 0x6A, + CONF_CA_PUBLIC_KEY = 0x6B, + CONF_KE_MAX_DEVICES = 0x6C, + CONF_USE_DEFAULT_TCLK = 0x6D, + CONF_RNG_COUNTER = 0x6F, + CONF_RANDOM_SEED = 0x70, + CONF_TRUSTCENTER_ADDR = 0x71, + CONF_USERDESC = 0x81, + CONF_NWKKEY = 0x82, + CONF_PANID = 0x83, + CONF_CHANLIST = 0x84, + CONF_LEAVE_CTRL = 0x85, + CONF_SCAN_DURATION = 0x86, + CONF_LOGICAL_TYPE = 0x87, + CONF_NWKMGR_MIN_TX = 0x88, + CONF_NWKMGR_ADDR = 0x89, + CONF_ZDO_DIRECT_CB = 0x8F, + CONF_TCLK_TABLE_START = 0x0101, + ZNP_HAS_CONFIGURED = 0xF00 +}; + + +enum Z_Status { + Z_SUCCESS = 0x00, + Z_FAILURE = 0x01, + Z_INVALIDPARAMETER = 0x02, + Z_MEMERROR = 0x03, + Z_CREATED = 0x09, + Z_BUFFERFULL = 0x11 +}; + +enum Z_App_Profiles { + Z_PROF_IPM = 0x0101, + Z_PROF_HA = 0x0104, + Z_PROF_CBA = 0x0105, + Z_PROF_TA = 0x0107, + Z_PROF_PHHC = 0x0108, + Z_PROF_AMI = 0x0109, +}; + +enum Z_Device_Ids { + Z_DEVID_CONF_TOOL = 0x0005, +# 962 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +}; + + enum Z_AddrMode : uint8_t { + Z_Addr_NotPresent = 0, + Z_Addr_Group = 1, + Z_Addr_ShortAddress = 2, + Z_Addr_IEEEAddress = 3, + Z_Addr_Broadcast = 0xFF +}; + + +enum AfCommand : uint8_t { + AF_REGISTER = 0x00, + AF_DATA_REQUEST = 0x01, + AF_DATA_REQUEST_EXT = 0x02, + AF_DATA_REQUEST_SRC_RTG = 0x03, + AF_INTER_PAN_CTL = 0x10, + AF_DATA_STORE = 0x11, + AF_DATA_RETRIEVE = 0x12, + AF_APSF_CONFIG_SET = 0x13, + AF_DATA_CONFIRM = 0x80, + AF_REFLECT_ERROR = 0x83, + AF_INCOMING_MSG = 0x81, + AF_INCOMING_MSG_EXT = 0x82 +}; + + +enum : uint8_t { + ZDO_NWK_ADDR_REQ = 0x00, + ZDO_IEEE_ADDR_REQ = 0x01, + ZDO_NODE_DESC_REQ = 0x02, + ZDO_POWER_DESC_REQ = 0x03, + ZDO_SIMPLE_DESC_REQ = 0x04, + ZDO_ACTIVE_EP_REQ = 0x05, + ZDO_MATCH_DESC_REQ = 0x06, + ZDO_COMPLEX_DESC_REQ = 0x07, + ZDO_USER_DESC_REQ = 0x08, + ZDO_DEVICE_ANNCE = 0x0A, + ZDO_USER_DESC_SET = 0x0B, + ZDO_SERVER_DISC_REQ = 0x0C, + ZDO_END_DEVICE_BIND_REQ = 0x20, + ZDO_BIND_REQ = 0x21, + ZDO_UNBIND_REQ = 0x22, + ZDO_SET_LINK_KEY = 0x23, + ZDO_REMOVE_LINK_KEY = 0x24, + ZDO_GET_LINK_KEY = 0x25, + ZDO_MGMT_NWK_DISC_REQ = 0x30, + ZDO_MGMT_LQI_REQ = 0x31, + ZDO_MGMT_RTQ_REQ = 0x32, + ZDO_MGMT_BIND_REQ = 0x33, + ZDO_MGMT_LEAVE_REQ = 0x34, + ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, + ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, + ZDO_MGMT_NWK_UPDATE_REQ = 0x37, + ZDO_MSG_CB_REGISTER = 0x3E, + ZDO_MGS_CB_REMOVE = 0x3F, + ZDO_STARTUP_FROM_APP = 0x40, + ZDO_AUTO_FIND_DESTINATION = 0x41, + ZDO_EXT_REMOVE_GROUP = 0x47, + ZDO_EXT_REMOVE_ALL_GROUP = 0x48, + ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, + ZDO_EXT_FIND_GROUP = 0x4A, + ZDO_EXT_ADD_GROUP = 0x4B, + ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, + ZDO_NWK_ADDR_RSP = 0x80, + ZDO_IEEE_ADDR_RSP = 0x81, + ZDO_NODE_DESC_RSP = 0x82, + ZDO_POWER_DESC_RSP = 0x83, + ZDO_SIMPLE_DESC_RSP = 0x84, + ZDO_ACTIVE_EP_RSP = 0x85, + ZDO_MATCH_DESC_RSP = 0x86, + ZDO_COMPLEX_DESC_RSP = 0x87, + ZDO_USER_DESC_RSP = 0x88, + ZDO_USER_DESC_CONF = 0x89, + ZDO_SERVER_DISC_RSP = 0x8A, + ZDO_END_DEVICE_BIND_RSP = 0xA0, + ZDO_BIND_RSP = 0xA1, + ZDO_UNBIND_RSP = 0xA2, + ZDO_MGMT_NWK_DISC_RSP = 0xB0, + ZDO_MGMT_LQI_RSP = 0xB1, + ZDO_MGMT_RTG_RSP = 0xB2, + ZDO_MGMT_BIND_RSP = 0xB3, + ZDO_MGMT_LEAVE_RSP = 0xB4, + ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, + ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, + ZDO_STATE_CHANGE_IND = 0xC0, + ZDO_END_DEVICE_ANNCE_IND = 0xC1, + ZDO_MATCH_DESC_RSP_SENT = 0xC2, + ZDO_STATUS_ERROR_RSP = 0xC3, + ZDO_SRC_RTG_IND = 0xC4, + ZDO_LEAVE_IND = 0xC9, + ZDO_TC_DEV_IND = 0xCA, + ZDO_PERMIT_JOIN_IND = 0xCB, + ZDO_MSG_CB_INCOMING = 0xFF +}; + + +enum ZdoStates { + ZDO_DEV_HOLD = 0x00, + ZDO_DEV_INIT = 0x01, + ZDO_DEV_NWK_DISC = 0x02, + ZDO_DEV_NWK_JOINING = 0x03, + ZDO_DEV_NWK_REJOIN = 0x04, + ZDO_DEV_END_DEVICE_UNAUTH = 0x05, + ZDO_DEV_END_DEVICE = 0x06, + ZDO_DEV_ROUTER = 0x07, + ZDO_DEV_COORD_STARTING = 0x08, + ZDO_DEV_ZB_COORD = 0x09, + ZDO_DEV_NWK_ORPHAN = 0x0A, +}; + + +enum Z_Util { + Z_UTIL_GET_DEVICE_INFO = 0x00, + Z_UTIL_GET_NV_INFO = 0x01, + Z_UTIL_SET_PANID = 0x02, + Z_UTIL_SET_CHANNELS = 0x03, + Z_UTIL_SET_SECLEVEL = 0x04, + Z_UTIL_SET_PRECFGKEY = 0x05, + Z_UTIL_CALLBACK_SUB_CMD = 0x06, + Z_UTIL_KEY_EVENT = 0x07, + Z_UTIL_TIME_ALIVE = 0x09, + Z_UTIL_LED_CONTROL = 0x0A, + Z_UTIL_TEST_LOOPBACK = 0x10, + Z_UTIL_DATA_REQ = 0x11, + Z_UTIL_SRC_MATCH_ENABLE = 0x20, + Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, + Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, + Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, + Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, + Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, + Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, + Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, + Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, + Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, + Z_UTIL_ASSOC_COUNT = 0x48, + Z_UTIL_ASSOC_FIND_DEVICE = 0x49, + Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, + Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, + Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, + Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, + Z_UTIL_UTIL_SYNC_REQ = 0xE0, + Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 +}; + +enum ZCL_Global_Commands { + ZCL_READ_ATTRIBUTES = 0x00, + ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, + ZCL_WRITE_ATTRIBUTES = 0x02, + ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, + ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, + ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, + ZCL_CONFIGURE_REPORTING = 0x06, + ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, + ZCL_READ_REPORTING_CONFIGURATION = 0x08, + ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, + ZCL_REPORT_ATTRIBUTES = 0x0a, + ZCL_DEFAULT_RESPONSE = 0x0b, + ZCL_DISCOVER_ATTRIBUTES = 0x0c, + ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d +}; + +#define Z_(s) Zo_ ## s + + +String getZDPStatusMessage(uint8_t status) { + static const char StatusMsg[] PROGMEM = "SUCCESS|INV_REQUESTTYPE|DEVICE_NOT_FOUND|INVALID_EP|NOT_ACTIVE|NOT_SUPPORTED" + "|TIMEOUT|NO_MATCH|NO_ENTRY|NO_DESCRIPTOR|INSUFFICIENT_SPACE|NOT_PERMITTED" + "|TABLE_FULL|NOT_AUTHORIZED|DEVICE_BINDING_TABLE_FULL" + ; + static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x80, 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x88, 0x89, 0x8A, 0x8B, + 0x8C, 0x8D, 0x8E }; + + char msg[32]; + int32_t idx = -1; + for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { + if (status == pgm_read_byte(&StatusIdx[i])) { + idx = i; + break; + } + } + if (idx >= 0) { + GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); + } else { + *msg = 0x00; + } + return String(msg); +} + +String getEmberStatus(uint8_t status) { + static const char StatusMsg[] PROGMEM = "SUCCESS|ERR_FATAL|BAD_ARGUMENT|EEPROM_MFG_STACK_VERSION_MISMATCH|INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS|EEPROM_MFG_VERSION_MISMATCH|EEPROM_STACK_VERSION_MISMATCH" + "|NO_BUFFERS|SERIAL_INVALID_BAUD_RATE|SERIAL_INVALID_PORT|SERIAL_TX_OVERFLOW|SERIAL_RX_OVERFLOW|SERIAL_RX_FRAME_ERROR|SERIAL_RX_PARITY_ERROR|SERIAL_RX_EMPTY|SERIAL_RX_OVERRUN_ERROR" + "|MAC_TRANSMIT_QUEUE_FULL|MAC_UNKNOWN_HEADER_TYPE|MAC_SCANNING|MAC_NO_DATA|MAC_JOINED_NETWORK|MAC_BAD_SCAN_DURATION|MAC_INCORRECT_SCAN_TYPE|MAC_INVALID_CHANNEL_MASK|MAC_COMMAND_TRANSMIT_FAILURE" + "|MAC_NO_ACK_RECEIVED|MAC_INDIRECT_TIMEOUT|SIM_EEPROM_ERASE_PAGE_GREEN|SIM_EEPROM_ERASE_PAGE_RED|SIM_EEPROM_FULL|ERR_FLASH_WRITE_INHIBITED|ERR_FLASH_VERIFY_FAILED|SIM_EEPROM_INIT_1_FAILED|SIM_EEPROM_INIT_2_FAILED|SIM_EEPROM_INIT_3_FAILED|ERR_FLASH_PROG_FAIL|ERR_FLASH_ERASE_FAIL" + "|ERR_BOOTLOADER_TRAP_TABLE_BAD|ERR_BOOTLOADER_TRAP_UNKNOWN|ERR_BOOTLOADER_NO_IMAGE|DELIVERY_FAILED|BINDING_INDEX_OUT_OF_RANGE|ADDRESS_TABLE_INDEX_OUT_OF_RANGE|INVALID_BINDING_INDEX" + "|INVALID_CALL|COST_NOT_KNOWN|MAX_MESSAGE_LIMIT_REACHED|MESSAGE_TOO_LONG|BINDING_IS_ACTIVE|ADDRESS_TABLE_ENTRY_IS_ACTIVE" + "|ADC_CONVERSION_DONE|ADC_CONVERSION_BUSY|ADC_CONVERSION_DEFERRED|ADC_NO_CONVERSION_PENDING|SLEEP_INTERRUPTED|PHY_TX_UNDERFLOW|PHY_TX_INCOMPLETE|PHY_INVALID_CHANNEL|PHY_INVALID_POWER|PHY_TX_BUSY|PHY_TX_CCA_FAIL|PHY_OSCILLATOR_CHECK_FAILED|PHY_ACK_RECEIVED" + "|NETWORK_UP|NETWORK_DOWN|JOIN_FAILED|MOVE_FAILED|CANNOT_JOIN_AS_ROUTER|NODE_ID_CHANGED|PAN_ID_CHANGED|NO_BEACONS|RECEIVED_KEY_IN_THE_CLEAR|NO_NETWORK_KEY_RECEIVED|NO_LINK_KEY_RECEIVED|PRECONFIGURED_KEY_REQUIRED" + "|NOT_JOINED|INVALID_SECURITY_LEVEL|NETWORK_BUSY|INVALID_ENDPOINT|BINDING_HAS_CHANGED|INSUFFICIENT_RANDOM_DATA|APS_ENCRYPTION_ERROR|SECURITY_STATE_NOT_SET" + "|KEY_TABLE_INVALID_ADDRESS|SECURITY_CONFIGURATION_INVALID|TOO_SOON_FOR_SWITCH_KEY|KEY_NOT_AUTHORIZED|SECURITY_DATA_INVALID|SOURCE_ROUTE_FAILURE|MANY_TO_ONE_ROUTE_FAILURE" + "|STACK_AND_HARDWARE_MISMATCH|INDEX_OUT_OF_RANGE|TABLE_FULL|TABLE_ENTRY_ERASED|LIBRARY_NOT_PRESENT|OPERATION_IN_PROGRESS" + ; + static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, + 0x18, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x39, 0x3A, 0x3D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, + 0x58, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x6C, + 0x70, 0x71, 0x72, 0x74, 0x75, 0x76, + 0x80, 0x81, 0x82, 0x84, 0x85, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x94, 0x96, 0x98, 0x99, 0x9A, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0x93, 0x95, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, + 0xB3, 0xB7, 0xB8, 0xBB, 0xBD, 0xA9, 0xAA, + 0xB0, 0xB1, 0xB4, 0xB6, 0xB5, 0xBA }; + + char msg[32]; + int32_t idx = -1; + for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { + if (status == pgm_read_byte(&StatusIdx[i])) { + idx = i; + break; + } + } + if (idx >= 0) { + GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); + } else { + *msg = 0x00; + } + return String(msg); +} + + + +String getZigbeeStatusMessage(uint8_t status) { + static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND" + "|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY" + "|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY" + "|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA" + "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER|NO_ROUTE" + "|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE" + ; + static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, 0xCD, + 0xE1, 0xE9, 0xA7, 0xD0}; + + char msg[32]; + int32_t idx = -1; + for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { + if (status == pgm_read_byte(&StatusIdx[i])) { + idx = i; + break; + } + } + if (idx >= 0) { + GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); + } else { + *msg = 0x00; + } + return String(msg); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +#ifdef USE_ZIGBEE + + + + + + +class ZigbeeZCLSendMessage { +public: + uint16_t shortaddr; + uint16_t groupaddr; + uint16_t cluster; + uint8_t endpoint; + uint8_t cmd; + uint16_t manuf; + bool clusterSpecific; + bool needResponse; + bool direct; + uint8_t transacId; + const uint8_t *msg; + size_t len; +}; + +typedef int32_t (*ZB_Func)(uint8_t value); +typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); + + +const uint8_t ZIGBEE_LABEL_RESTART = 1; +const uint8_t ZIGBEE_LABEL_INIT_COORD = 10; +const uint8_t ZIGBEE_LABEL_START_COORD = 11; +const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; +const uint8_t ZIGBEE_LABEL_START_ROUTER = 13; +const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; +const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; +const uint8_t ZIGBEE_LABEL_START_ROUTER_DEVICE = 16; +const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST = 19; +const uint8_t ZIGBEE_LABEL_READY = 20; +const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; +const uint8_t ZIGBEE_LABEL_NETWORK_CONFIGURED = 22; +const uint8_t ZIGBEE_LABEL_BAD_CONFIG = 23; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; + +const uint8_t ZIGBEE_LABEL_FACT_RESET_COORD = 50; +const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER = 51; +const uint8_t ZIGBEE_LABEL_FACT_RESET_DEVICE = 52; +const uint8_t ZIGBEE_LABEL_CONFIGURE_EZSP = 53; + +const uint8_t ZIGBEE_LABEL_ABORT = 99; +const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; + +struct ZigbeeStatus { + bool active = true; + bool state_machine = false; + bool state_waiting = false; + bool state_no_timeout = false; + bool ready = false; + bool init_phase = true; + bool recv_until = false; + + uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; + uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; + uint8_t *recv_filter = nullptr; + uint8_t recv_filter_len = 0; + int16_t pc = 0; + uint32_t next_timeout = 0; + + ZB_RecvMsgFunc recv_func = nullptr; + ZB_RecvMsgFunc recv_unexpected = nullptr; + + uint32_t permit_end_time = 0; +}; +struct ZigbeeStatus zigbee; +SBuffer *zigbee_buffer = nullptr; + +void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl); +bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false); + + +uint32_t parseHex(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(**data); + if (v < 0) { break; } + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1z_libs.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1z_libs.ino" +#ifdef USE_ZIGBEE + + + + + + + +int strcmp_PP(const char *p1, const char *p2) { + if (p1 == p2) { return 0; } + if (!p1) { return -1; } + if (!p2) { return 1; } + const unsigned char *s1 = (const unsigned char *) p1; + const unsigned char *s2 = (const unsigned char *) p2; + unsigned char c1, c2; + do { + c1 = (unsigned char) pgm_read_byte(s1); + s1++; + c2 = (unsigned char) pgm_read_byte(s2); + s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + return c1 - c2; +} + + + + + + + +typedef struct Z_LastMessageVars { + uint16_t device; + uint16_t groupaddr; + uint16_t cluster; + uint8_t endpoint; +} Z_LastMessageVars; + +Z_LastMessageVars gZbLastMessage; + +uint16_t Z_GetLastDevice(void) { return gZbLastMessage.device; } +uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; } +uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; } +uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; } +# 74 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1z_libs.ino" +class Z_json_array { +public: + + Z_json_array(): val("[]") {} + void add(uint32_t uval32) { + + val.remove(val.length()-1); + if (val.length() > 1) { + val += ','; + } + val += uval32; + val += ']'; + } + void addStrRaw(const char * sval) { + + val.remove(val.length()-1); + if (val.length() > 1) { + val += ','; + } + val += sval; + val += ']'; + } + void addStr(const char * sval) { + addStrRaw(EscapeJSONString(sval).c_str()); + } + String &toString(void) { + return val; + } + +private : + String val; +}; + + + + + + + +enum class Za_type : uint8_t { + Za_none, + + Za_bool, + Za_uint, + Za_int, + Za_float, + + Za_raw, + Za_str, + + Za_obj, + Za_arr, +}; + +class Z_attribute { +public: + + + union { + struct { + uint16_t cluster; + uint16_t attr_id; + } id; + char * key; + } key; + + union { + uint32_t uval32; + int32_t ival32; + float fval; + SBuffer* bval; + char* sval; + class Z_attribute_list * objval; + class Z_json_array * arrval; + } val; + Za_type type; + bool key_is_str; + bool key_is_pmem; + bool val_str_raw; + uint8_t key_suffix; + uint8_t attr_type; + uint8_t attr_multiplier; + + + Z_attribute(): + key{ .id = { 0x0000, 0x0000 } }, + val{ .uval32 = 0x0000 }, + type(Za_type::Za_none), + key_is_str(false), + key_is_pmem(false), + val_str_raw(false), + key_suffix(1), + attr_type(0xFF), + attr_multiplier(1) + {}; + + Z_attribute(const Z_attribute & rhs) { + deepCopy(rhs); + } + + Z_attribute & operator = (const Z_attribute & rhs) { + freeKey(); + freeVal(); + deepCopy(rhs); + } + + + ~Z_attribute() { + freeKey(); + freeVal(); + } + + + void freeVal(void); + + + void freeKey(void); + + + void setKeyName(const char * _key, bool pmem = false); + + void setKeyName(const char * _key, const char * _key2); + + void setKeyId(uint16_t cluster, uint16_t attr_id); + + + void setNone(void); + void setUInt(uint32_t _val); + void setBool(bool _val); + void setInt(int32_t _val); + void setFloat(float _val); + + void setBuf(const SBuffer &buf, size_t index, size_t len); + + + + + + + void setStr(const char * _val); + inline void setStrRaw(const char * _val) { + setStr(_val); + val_str_raw = true; + } + + Z_attribute_list & newAttrList(void); + Z_json_array & newJsonArray(void); + + inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); } + inline bool isNone(void) const { return (type == Za_type::Za_none);} + + float getFloat(void) const; + int32_t getInt(void) const; + uint32_t getUInt(void) const; + bool getBool(void) const; + const SBuffer * getRaw(void) const; + + + + const char * getStr(void) const; + + bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const; + bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const; + bool equalsKey(const char * name, uint8_t suffix = 0) const; + bool equalsVal(const Z_attribute & attr2) const; + bool equals(const Z_attribute & attr2) const; + + String toString(bool prefix_comma = false) const; + + + void copyVal(const Z_attribute & rhs); + +protected: + void deepCopy(const Z_attribute & rhs); +}; +# 261 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1z_libs.ino" +class Z_attribute_list : public LList { +public: + uint8_t src_ep; + uint8_t lqi; + uint16_t group_id; + + Z_attribute_list(): + LList(), + src_ep(0xFF), + lqi(0xFF), + group_id(0xFFFF) + {}; + + + + + + void reset(void) { + LList::reset(); + src_ep = 0xFF; + lqi = 0xFF; + group_id = 0xFFFF; + } + + inline bool isValidSrcEp(void) const { return 0xFF != src_ep; } + inline bool isValidLQI(void) const { return 0xFF != lqi; } + inline bool isValidGroupId(void) const { return 0xFFFF != group_id; } + + + + Z_attribute & addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0); + + + Z_attribute & addAttribute(const char * name, bool pmem = false, uint8_t suffix = 0); + Z_attribute & addAttribute(const char * name, const char * name2, uint8_t suffix = 0); + inline Z_attribute & addAttribute(const __FlashStringHelper * name, uint8_t suffix = 0) { + return addAttribute((const char*) name, true, suffix); + } + + + inline void removeAttribute(const Z_attribute * attr) { remove(attr); } + + + + + String toString(bool enclose_brackets = false) const; + + + const Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const; + const Z_attribute * findAttribute(const char * name, uint8_t suffix = 0) const; + const Z_attribute * findAttribute(const Z_attribute &attr) const; + + inline Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) { + return (Z_attribute*) ((const Z_attribute_list*)this)->findAttribute(cluster, attr_id, suffix); + } + inline Z_attribute * findAttribute(const char * name, uint8_t suffix = 0) { + return (Z_attribute*) (((const Z_attribute_list*)this)->findAttribute(name, suffix)); + } + inline Z_attribute * findAttribute(const Z_attribute &attr) { + return (Z_attribute*) ((const Z_attribute_list*)this)->findAttribute(attr); + } + + + size_t countAttribute(uint16_t cluster, uint16_t attr_id) const ; + size_t countAttribute(const char * name) const ; + + + Z_attribute & findOrCreateAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0); + Z_attribute & findOrCreateAttribute(const char * name, uint8_t suffix = 0); + + Z_attribute & findOrCreateAttribute(const Z_attribute &attr); + + Z_attribute & replaceOrCreate(const Z_attribute &attr); + + + bool mergeList(const Z_attribute_list &list2); +}; +# 346 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_1z_libs.ino" +void Z_attribute::freeKey(void) { + if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; } + key.key = nullptr; +} + + +void Z_attribute::setKeyName(const char * _key, bool pmem) { + freeKey(); + key_is_str = true; + key_is_pmem = pmem; + if (pmem) { + key.key = (char*) _key; + } else { + setKeyName(_key, nullptr); + } +} + +void Z_attribute::setKeyName(const char * _key, const char * _key2) { + freeKey(); + key_is_str = true; + key_is_pmem = false; + if (_key) { + size_t key_len = strlen_P(_key); + if (_key2) { + key_len += strlen_P(_key2); + } + key.key = new char[key_len+1]; + strcpy_P(key.key, _key); + if (_key2) { + strcat_P(key.key, _key2); + } + } +} + +void Z_attribute::setKeyId(uint16_t cluster, uint16_t attr_id) { + freeKey(); + key_is_str = false; + key.id.cluster = cluster; + key.id.attr_id = attr_id; +} + + +void Z_attribute::setNone(void) { + freeVal(); + val.uval32 = 0; + type = Za_type::Za_none; +} +void Z_attribute::setUInt(uint32_t _val) { + freeVal(); + val.uval32 = _val; + type = Za_type::Za_uint; +} +void Z_attribute::setBool(bool _val) { + freeVal(); + val.uval32 = _val; + type = Za_type::Za_bool; +} +void Z_attribute::setInt(int32_t _val) { + freeVal(); + val.ival32 = _val; + type = Za_type::Za_int; +} +void Z_attribute::setFloat(float _val) { + freeVal(); + val.fval = _val; + type = Za_type::Za_float; +} + +void Z_attribute::setBuf(const SBuffer &buf, size_t index, size_t len) { + freeVal(); + if (len) { + val.bval = new SBuffer(len); + val.bval->addBuffer(buf.buf(index), len); + } + type = Za_type::Za_raw; +} + + + + + + +void Z_attribute::setStr(const char * _val) { + freeVal(); + val_str_raw = false; + + if (_val) { + size_t len = strlen_P(_val); + if (len) { + val.sval = new char[len+1]; + strcpy_P(val.sval, _val); + } + } + type = Za_type::Za_str; +} + +Z_attribute_list & Z_attribute::newAttrList(void) { + freeVal(); + val.objval = new Z_attribute_list(); + type = Za_type::Za_obj; + return *val.objval; +} + +Z_json_array & Z_attribute::newJsonArray(void) { + freeVal(); + val.arrval = new Z_json_array(); + type = Za_type::Za_arr; + return *val.arrval; +} + + +float Z_attribute::getFloat(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return (float) val.uval32; + case Za_type::Za_int: return (float) val.ival32; + case Za_type::Za_float: return val.fval; + default: return 0.0f; + } +} + +int32_t Z_attribute::getInt(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return (int32_t) val.uval32; + case Za_type::Za_int: return val.ival32; + case Za_type::Za_float: return (int32_t) val.fval; + default: return 0; + } +} + +uint32_t Z_attribute::getUInt(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return val.uval32; + case Za_type::Za_int: return (uint32_t) val.ival32; + case Za_type::Za_float: return (uint32_t) val.fval; + default: return 0; + } +} + +bool Z_attribute::getBool(void) const { + switch (type) { + case Za_type::Za_bool: + case Za_type::Za_uint: return val.uval32 ? true : false; + case Za_type::Za_int: return val.ival32 ? true : false; + case Za_type::Za_float: return val.fval ? true : false; + default: return false; + } +} + +const SBuffer * Z_attribute::getRaw(void) const { + if (Za_type::Za_raw == type) { return val.bval; } + return nullptr; +} + + + +const char * Z_attribute::getStr(void) const { + if (Za_type::Za_str == type) { return val.sval; } + return ""; +} + +bool Z_attribute::equalsKey(const Z_attribute & attr2, bool ignore_key_suffix) const { + + if (key_is_str != attr2.key_is_str) { return false; } + if (key_is_str) { + if (strcmp_PP(key.key, attr2.key.key)) { return false; } + } else { + if ((key.id.cluster != attr2.key.id.cluster) || + (key.id.attr_id != attr2.key.id.attr_id)) { return false; } + } + if (!ignore_key_suffix) { + if (key_suffix != attr2.key_suffix) { return false; } + } + return true; +} + +bool Z_attribute::equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix) const { + if (!key_is_str) { + if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) { + if (suffix) { + if (key_suffix == suffix) { return true; } + } else { + return true; + } + } + } + return false; +} + +bool Z_attribute::equalsKey(const char * name, uint8_t suffix) const { + if (key_is_str) { + if (0 == strcmp_PP(key.key, name)) { + if (suffix) { + if (key_suffix == suffix) { return true; } + } else { + return true; + } + } + } + return false; +} + +bool Z_attribute::equalsVal(const Z_attribute & attr2) const { + if (type != attr2.type) { return false; } + if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) { + + if (val.uval32 != attr2.val.uval32) { return false; } + } else if (type == Za_type::Za_raw) { + + return equalsSBuffer(val.bval, attr2.val.bval); + } else if (type == Za_type::Za_str) { + + if (strcmp_PP(val.sval, attr2.val.sval)) { return false; } + } else if (type == Za_type::Za_obj) { + return false; + } else if (type == Za_type::Za_arr) { + return false; + } + return true; +} + +bool Z_attribute::equals(const Z_attribute & attr2) const { + return equalsKey(attr2) && equalsVal(attr2); +} + +String Z_attribute::toString(bool prefix_comma) const { + String res(""); + if (prefix_comma) { res += ','; } + res += '"'; + + if (key_is_str) { + if (key.key) { res += EscapeJSONString(key.key); } + else { res += F("null"); } + if (key_suffix > 1) { + res += key_suffix; + } + } else { + char attr_name[12]; + snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id); + res += attr_name; + if (key_suffix > 1) { + res += '+'; + res += key_suffix; + } + } + res += F("\":"); + + switch (type) { + case Za_type::Za_none: + res += "null"; + break; + case Za_type::Za_bool: + res += val.uval32 ? F("true") : F("false"); + break; + case Za_type::Za_uint: + res += val.uval32; + break; + case Za_type::Za_int: + res += val.ival32; + break; + case Za_type::Za_float: + { + String fstr(val.fval, 2); + size_t last = fstr.length() - 1; + + while (fstr[last] == '0') { + fstr.remove(last--); + } + + if (fstr[last] == '.') { + fstr.remove(last); + } + res += fstr; + } + break; + case Za_type::Za_raw: + res += '"'; + if (val.bval) { + size_t blen = val.bval->len(); + + char hex[2*blen+1]; + ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex)); + res += hex; + } + res += '"'; + break; + case Za_type::Za_str: + if (val_str_raw) { + if (val.sval) { res += val.sval; } + } else { + res += '"'; + if (val.sval) { + res += EscapeJSONString(val.sval); + } + res += '"'; + } + break; + case Za_type::Za_obj: + res += '{'; + if (val.objval) { + res += val.objval->toString(); + } + res += '}'; + break; + case Za_type::Za_arr: + if (val.arrval) { + res += val.arrval->toString(); + } else { + res += "[]"; + } + break; + } + + return res; +} + + +void Z_attribute::copyVal(const Z_attribute & rhs) { + freeVal(); + + val.uval32 = 0x00000000; + type = rhs.type; + if (rhs.isNum()) { + val.uval32 = rhs.val.uval32; + } else if (rhs.type == Za_type::Za_raw) { + if (rhs.val.bval) { + val.bval = new SBuffer(rhs.val.bval->len()); + val.bval->addBuffer(*(rhs.val.bval)); + } + } else if (rhs.type == Za_type::Za_str) { + if (rhs.val.sval) { + size_t s_len = strlen_P(rhs.val.sval); + val.sval = new char[s_len+1]; + strcpy_P(val.sval, rhs.val.sval); + } + } + val_str_raw = rhs.val_str_raw; +} + + +void Z_attribute::freeVal(void) { + switch (type) { + case Za_type::Za_raw: + if (val.bval) { delete val.bval; val.bval = nullptr; } + break; + case Za_type::Za_str: + if (val.sval) { delete[] val.sval; val.sval = nullptr; } + break; + case Za_type::Za_obj: + if (val.objval) { delete val.objval; val.objval = nullptr; } + break; + case Za_type::Za_arr: + if (val.arrval) { delete val.arrval; val.arrval = nullptr; } + break; + } +} + +void Z_attribute::deepCopy(const Z_attribute & rhs) { + + if (!rhs.key_is_str) { + key.id.cluster = rhs.key.id.cluster; + key.id.attr_id = rhs.key.id.attr_id; + } else { + if (rhs.key_is_pmem) { + key.key = rhs.key.key; + } else { + key.key = nullptr; + if (rhs.key.key) { + size_t key_len = strlen_P(rhs.key.key); + if (key_len) { + key.key = new char[key_len+1]; + strcpy_P(key.key, rhs.key.key); + } + } + } + } + key_is_str = rhs.key_is_str; + key_is_pmem = rhs.key_is_pmem; + key_suffix = rhs.key_suffix; + attr_type = rhs.attr_type; + attr_multiplier = rhs.attr_multiplier; + + copyVal(rhs); + +} + + + + + + + +Z_attribute & Z_attribute_list::addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) { + Z_attribute & attr = addToLast(); + attr.key.id.cluster = cluster; + attr.key.id.attr_id = attr_id; + attr.key_is_str = false; + if (!suffix) { attr.key_suffix = countAttribute(attr.key.id.cluster, attr.key.id.attr_id); } + else { attr.key_suffix = suffix; } + return attr; +} + + +Z_attribute & Z_attribute_list::addAttribute(const char * name, bool pmem, uint8_t suffix) { + Z_attribute & attr = addToLast(); + attr.setKeyName(name, pmem); + if (!suffix) { attr.key_suffix = countAttribute(attr.key.key); } + else { attr.key_suffix = suffix; } + return attr; +} + +Z_attribute & Z_attribute_list::addAttribute(const char * name, const char * name2, uint8_t suffix) { + Z_attribute & attr = addToLast(); + attr.setKeyName(name, name2); + if (!suffix) { attr.key_suffix = countAttribute(attr.key.key); } + else { attr.key_suffix = suffix; } + return attr; +} + +String Z_attribute_list::toString(bool enclose_brackets) const { + String res = ""; + if (enclose_brackets) { res += '{'; } + bool prefix_comma = false; + for (const auto & attr : *this) { + res += attr.toString(prefix_comma); + prefix_comma = true; + } + + if (0xFF != src_ep) { + if (prefix_comma) { res += ','; } + prefix_comma = true; + res += F("\"" D_CMND_ZIGBEE_ENDPOINT "\":"); + res += src_ep; + } + + if (0xFFFF != group_id) { + if (prefix_comma) { res += ','; } + prefix_comma = true; + res += F("\"" D_CMND_ZIGBEE_GROUP "\":"); + res += group_id; + } + + if (0xFF != lqi) { + if (prefix_comma) { res += ','; } + prefix_comma = true; + res += F("\"" D_CMND_ZIGBEE_LINKQUALITY "\":"); + res += lqi; + } + if (enclose_brackets) { res += '}'; } + + return res; +} + + +const Z_attribute * Z_attribute_list::findAttribute(const Z_attribute &attr) const { + uint8_t suffix = attr.key_suffix; + if (attr.key_is_str) { + return findAttribute(attr.key.key, suffix); + } else { + return findAttribute(attr.key.id.cluster, attr.key.id.attr_id, suffix); + } +} + +const Z_attribute * Z_attribute_list::findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) const { + for (const auto & attr : *this) { + if (attr.equalsKey(cluster, attr_id, suffix)) { return &attr; } + } + return nullptr; +} +size_t Z_attribute_list::countAttribute(uint16_t cluster, uint16_t attr_id) const { + size_t count = 0; + for (const auto & attr : *this) { + if (attr.equalsKey(cluster, attr_id, 0)) { count++; } + } + return count; +} + + +Z_attribute & Z_attribute_list::findOrCreateAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) { + Z_attribute * found = findAttribute(cluster, attr_id, suffix); + return found ? *found : addAttribute(cluster, attr_id, suffix); +} + +const Z_attribute * Z_attribute_list::findAttribute(const char * name, uint8_t suffix) const { + for (const auto & attr : *this) { + if (attr.equalsKey(name, suffix)) { return &attr; } + } + return nullptr; +} +size_t Z_attribute_list::countAttribute(const char * name) const { + size_t count = 0; + for (const auto & attr : *this) { + if (attr.equalsKey(name, 0)) { count++; } + } + return count; +} + +Z_attribute & Z_attribute_list::findOrCreateAttribute(const char * name, uint8_t suffix) { + Z_attribute * found = findAttribute(name, suffix); + return found ? *found : addAttribute(name, suffix); +} + + +Z_attribute & Z_attribute_list::findOrCreateAttribute(const Z_attribute &attr) { + if (attr.key_is_str) { + return findOrCreateAttribute(attr.key.key, attr.key_suffix); + } else { + return findOrCreateAttribute(attr.key.id.cluster, attr.key.id.attr_id, attr.key_suffix); + } +} + +Z_attribute & Z_attribute_list::replaceOrCreate(const Z_attribute &attr) { + Z_attribute &new_attr = findOrCreateAttribute(attr); + new_attr.copyVal(attr); + return new_attr; +} + + +bool Z_attribute_list::mergeList(const Z_attribute_list &attr_list) { + + if (0xFF == src_ep) { + src_ep = attr_list.src_ep; + } else if (0xFF != attr_list.src_ep) { + if (src_ep != attr_list.src_ep) { return false; } + } + if (0xFF != attr_list.lqi) { + lqi = attr_list.lqi; + } + for (auto & attr : attr_list) { + replaceOrCreate(attr); + } + return true; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +#ifdef USE_ZIGBEE + +#ifndef ZIGBEE_SAVE_DELAY_SECONDS +#define ZIGBEE_SAVE_DELAY_SECONDS 2 +#endif +const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; + +enum class Z_Data_Type : uint8_t { + Z_Unknown = 0x00, + Z_Light = 1, + Z_Plug = 2, + Z_PIR = 3, + Z_Alarm = 4, + Z_Thermo = 5, + Z_OnOff = 6, + Z_Ext = 0xF, + Z_Device = 0xFF +}; + +class Z_Data_Set; + + + +class Z_Data { +public: + Z_Data(Z_Data_Type type = Z_Data_Type::Z_Unknown, uint8_t endpoint = 0) : _type(type), _endpoint(endpoint), _config(-1), _power(0) {} + inline Z_Data_Type getType(void) const { return _type; } + inline int8_t getConfig(void) const { return _config; } + inline void setConfig(int8_t config) { _config = config; } + + inline uint8_t getEndpoint(void) const { return _endpoint; } + + void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const; + + static const Z_Data_Type type = Z_Data_Type::Z_Unknown; + + friend class Z_Data_Set; +protected: + Z_Data_Type _type; + uint8_t _endpoint; + int8_t _config; + uint8_t _power; +}; + + + + + + + +class Z_Data_OnOff : public Z_Data { +public: + Z_Data_OnOff(uint8_t endpoint = 0) : + Z_Data(Z_Data_Type::Z_OnOff, endpoint) + { + _config = 1; + } + + inline bool validPower(uint32_t relay = 0) const { return (_config > relay); } + inline bool getPower(uint32_t relay = 0) const { return bitRead(_power, relay); } + void setPower(bool val, uint32_t relay = 0); + + void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const; + + static const Z_Data_Type type = Z_Data_Type::Z_OnOff; +}; + + +void Z_Data_OnOff::setPower(bool val, uint32_t relay) { + if (relay < 8) { + if (_config < relay) { _config = relay; } + bitWrite(_power, relay, val); + } +} + + + + +class Z_Data_Plug : public Z_Data { +public: + Z_Data_Plug(uint8_t endpoint = 0) : + Z_Data(Z_Data_Type::Z_Plug, endpoint), + mains_voltage(0xFFFF), + mains_power(-0x8000) + {} + + inline bool validMainsVoltage(void) const { return 0xFFFF != mains_voltage; } + inline bool validMainsPower(void) const { return -0x8000 != mains_power; } + + inline uint16_t getMainsVoltage(void) const { return mains_voltage; } + inline int16_t getMainsPower(void) const { return mains_power; } + + inline void setMainsVoltage(uint16_t _mains_voltage) { mains_voltage = _mains_voltage; } + inline void setMainsPower(int16_t _mains_power) { mains_power = _mains_power; } + + static const Z_Data_Type type = Z_Data_Type::Z_Plug; + + uint16_t mains_voltage; + int16_t mains_power; +}; + + + + +class Z_Data_Light : public Z_Data { +public: + Z_Data_Light(uint8_t endpoint = 0) : + Z_Data(Z_Data_Type::Z_Light, endpoint), + colormode(0xFF), + dimmer(0xFF), + sat(0xFF), + hue(0xFF), + ct(0xFFFF), + x(0xFFFF), + y(0xFFFF) + {} + + inline bool validColormode(void) const { return 0xFF != colormode; } + inline bool validDimmer(void) const { return 0xFF != dimmer; } + inline bool validSat(void) const { return 0xFF != sat; } + inline bool validHue(void) const { return 0xFFFF != hue; } + inline bool validCT(void) const { return 0xFFFF != ct; } + inline bool validX(void) const { return 0xFFFF != x; } + inline bool validY(void) const { return 0xFFFF != y; } + + inline uint8_t getColorMode(void) const { return colormode; } + inline uint8_t getDimmer(void) const { return dimmer; } + inline uint8_t getSat(void) const { return sat; } + inline uint16_t getHue(void) const { return changeUIntScale(hue, 0, 254, 0, 360); } + inline uint16_t getCT(void) const { return ct; } + inline uint16_t getX(void) const { return x; } + inline uint16_t getY(void) const { return y; } + + inline void setColorMode(uint8_t _colormode) { colormode = _colormode; } + inline void setDimmer(uint8_t _dimmer) { dimmer = _dimmer; } + inline void setSat(uint8_t _sat) { sat = _sat; } + inline void setHue(uint16_t _hue) { hue = changeUIntScale(_hue, 0, 360, 0, 254);; } + inline void setCT(uint16_t _ct) { ct = _ct; } + inline void setX(uint16_t _x) { x = _x; } + inline void setY(uint16_t _y) { y = _y; } + + void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const; + + static const Z_Data_Type type = Z_Data_Type::Z_Light; + + uint8_t colormode; + uint8_t dimmer; + uint8_t sat; + uint8_t hue; + uint16_t ct; + uint16_t x, y; +}; + + + + +class Z_Data_Thermo : public Z_Data { +public: + Z_Data_Thermo(uint8_t endpoint = 0) : + Z_Data(Z_Data_Type::Z_Thermo, endpoint), + temperature(-0x8000), + pressure(-0x8000), + humidity(0xFFFF), + th_setpoint(0xFF), + temperature_target(-0x8000) + {} + + inline bool validTemperature(void) const { return -0x8000 != temperature; } + inline bool validPressure(void) const { return -0x8000 != pressure; } + inline bool validHumidity(void) const { return 0xFFFF != humidity; } + inline bool validThSetpoint(void) const { return 0xFF != th_setpoint; } + inline bool validTempTarget(void) const { return -0x8000 != temperature_target; } + + inline int16_t getTemperature(void) const { return temperature; } + inline int16_t getPressure(void) const { return pressure; } + inline uint16_t getHumidity(void) const { return humidity; } + inline uint8_t getThSetpoint(void) const { return th_setpoint; } + inline int16_t getTempTarget(void) const { return temperature_target; } + + inline void setTemperature(int16_t _temperature) { temperature = _temperature; } + inline void setPressure(int16_t _pressure) { pressure = _pressure; } + inline void setHumidity(uint16_t _humidity) { humidity = _humidity; } + inline void setThSetpoint(uint8_t _th_setpoint) { th_setpoint = _th_setpoint; } + inline void setTempTarget(int16_t _temperature_target){ temperature_target = _temperature_target; } + + static const Z_Data_Type type = Z_Data_Type::Z_Thermo; + + + int16_t temperature; + int16_t pressure; + uint16_t humidity; + + uint8_t th_setpoint; + int16_t temperature_target; +}; + + + + +class Z_Data_Alarm : public Z_Data { +public: + Z_Data_Alarm(uint8_t endpoint = 0) : + Z_Data(Z_Data_Type::Z_Alarm, endpoint), + zone_type(0xFFFF) + {} + + static const Z_Data_Type type = Z_Data_Type::Z_Alarm; + + inline bool validZoneType(void) const { return 0xFFFF != zone_type; } + + inline uint16_t getZoneType(void) const { return zone_type; } + + inline void setZoneType(uint16_t _zone_type) { zone_type = _zone_type; } + + + uint16_t zone_type; +# 250 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +}; + + + + + +class Z_Data_Set : public LList { +public: + + Z_Data & getByType(Z_Data_Type type, uint8_t ep = 0); + const Z_Data & find(Z_Data_Type type, uint8_t ep = 0) const; + + + + + + template + M & get(uint8_t ep = 0); + + template + const M & find(uint8_t ep = 0) const; + + + template + M & addIfNull(M & cur, uint8_t ep = 0); +}; + +Z_Data & Z_Data_Set::getByType(Z_Data_Type type, uint8_t ep) { + switch (type) { + case Z_Data_Type::Z_Light: + return get(ep); + case Z_Data_Type::Z_Plug: + return get(ep); + case Z_Data_Type::Z_Alarm: + return get(ep); + case Z_Data_Type::Z_Thermo: + return get(ep); + case Z_Data_Type::Z_OnOff: + return get(ep); + default: + return *(Z_Data*)nullptr; + } +} + +template +M & Z_Data_Set::get(uint8_t ep) { + M & m = (M&) find(M::type, ep); + return addIfNull(m, ep); +} + +template +const M & Z_Data_Set::find(uint8_t ep) const { + return (M&) find(M::type, ep); +} + + + +template +M & Z_Data_Set::addIfNull(M & cur, uint8_t ep) { + if (nullptr == &cur) { + LList_elt * elt = new LList_elt(); + elt->val()._endpoint = ep; + this->addToLast((LList_elt*)elt); + return elt->val(); + } else { + if (cur._endpoint == 0) { cur._endpoint = ep; } + return cur; + } +} + +const Z_Data & Z_Data_Set::find(Z_Data_Type type, uint8_t ep) const { + for (auto & elt : *this) { + if (elt._type == type) { + + + if ((ep == 0) || (elt._endpoint == 0) || (ep == elt._endpoint)) { + return elt; + } + } + } + return *(Z_Data*)nullptr; +} + + + + +void Z_Data_Light::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const { + attr_list.addAttribute(PSTR(D_JSON_ZIGBEE_LIGHT)).setInt(getConfig()); + Z_Data::toAttributes(attr_list, type); +} + +void Z_Data_OnOff::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const { + if (validPower()) { attr_list.addAttribute(PSTR("Power")).setUInt(getPower() ? 1 : 0); } +} + + + + + +const size_t endpoints_max = 8; + +class Z_Device { +public: + + uint64_t longaddr; + char * manufacturerId; + char * modelId; + char * friendlyName; + + + uint32_t defer_last_message_sent; + + uint8_t endpoints[endpoints_max]; + + Z_attribute_list attr_list; + + uint16_t shortaddr; + uint8_t seqNumber; + bool hidden; + bool reachable; + + + + Z_Data_Set data; + + uint8_t lqi; + uint8_t batterypercent; + + uint32_t last_seen; + + + Z_Device(uint16_t _shortaddr = BAD_SHORTADDR, uint64_t _longaddr = 0x00): + longaddr(_longaddr), + manufacturerId(nullptr), + modelId(nullptr), + friendlyName(nullptr), + defer_last_message_sent(0), + endpoints{ 0, 0, 0, 0, 0, 0, 0, 0 }, + attr_list(), + shortaddr(_shortaddr), + seqNumber(0), + hidden(false), + reachable(false), + + lqi(0xFF), + batterypercent(0xFF), + last_seen(0) + { }; + + inline bool valid(void) const { return BAD_SHORTADDR != shortaddr; } + + inline bool validLongaddr(void) const { return 0x0000 != longaddr; } + inline bool validManufacturerId(void) const { return nullptr != manufacturerId; } + inline bool validModelId(void) const { return nullptr != modelId; } + inline bool validFriendlyName(void) const { return nullptr != friendlyName; } + + inline bool validPower(uint8_t ep =0) const; + + inline bool validLqi(void) const { return 0xFF != lqi; } + inline bool validBatteryPercent(void) const { return 0xFF != batterypercent; } + inline bool validLastSeen(void) const { return 0x0 != last_seen; } + + inline void setReachable(bool _reachable) { reachable = _reachable; } + inline bool getReachable(void) const { return reachable; } + inline bool getPower(uint8_t ep =0) const; + + + void toAttributes(Z_attribute_list & attr_list) const; + + + void setPower(bool power_on, uint8_t ep = 0); + + + int8_t getLightChannels(void) const { + const Z_Data_Light & light = data.find(0); + if (&light != nullptr) { + return light.getConfig(); + } else { + return -1; + } + } + + + bool setLightChannels(int8_t channels) { + bool dirty = false; + if (channels >= 0) { + + Z_Data_Light & light = data.get(0); + if (channels != light.getConfig()) { + light.setConfig(channels); + dirty = true; + } + } else { + + for (auto & data_elt : data) { + if (data_elt.getType() == Z_Data_Type::Z_Light) { + + data.remove(&data_elt); + dirty = true; + } + } + } + return dirty; + } +}; + + + + + +typedef void (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); + + +typedef enum Z_Def_Category { + Z_CAT_ALWAYS = 0, + + Z_CLEAR_DEVICE = 0x01, + Z_CAT_READ_ATTR, + Z_CAT_VIRTUAL_OCCUPANCY, + Z_CAT_REACHABILITY, + Z_CAT_PERMIT_JOIN, + + Z_CLEAR_DEVICE_CLUSTER, + Z_CAT_READ_CLUSTER, + + Z_CLEAR_DEVICE_CLUSTER_ENDPOINT, + Z_CAT_EP_DESC, + Z_CAT_BIND, + Z_CAT_CONFIG_ATTR, + Z_CAT_READ_ATTRIBUTE, +} Z_Def_Category; + +const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 2000; + +typedef struct Z_Deferred { + + uint32_t timer; + uint16_t shortaddr; + uint16_t groupaddr; + uint16_t cluster; + uint8_t endpoint; + uint8_t category; + uint32_t value; + Z_DeviceTimer func; +} Z_Deferred; +# 505 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +class Z_Devices { +public: + Z_Devices() : _deferred() {}; + + + + + + + uint16_t isKnownLongAddr(uint64_t longaddr) const; + uint16_t isKnownIndex(uint32_t index) const; + uint16_t isKnownFriendlyName(const char * name) const; + + Z_Device & findShortAddr(uint16_t shortaddr); + const Z_Device & findShortAddr(uint16_t shortaddr) const; + Z_Device & findLongAddr(uint64_t longaddr); + const Z_Device & findLongAddr(uint64_t longaddr) const; + Z_Device & getShortAddr(uint16_t shortaddr); + Z_Device & getLongAddr(uint64_t longaddr); + + inline bool foundDevice(const Z_Device & device) const { + return (&device != &device_unk); + } + + int32_t findFriendlyName(const char * name) const; + uint64_t getDeviceLongAddr(uint16_t shortaddr) const; + + uint8_t findFirstEndpoint(uint16_t shortaddr) const; + + + + void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); + + + void addEndpoint(uint16_t shortaddr, uint8_t endpoint); + void clearEndpoints(uint16_t shortaddr); + uint32_t countEndpoints(uint16_t shortaddr) const; + + void setManufId(uint16_t shortaddr, const char * str); + void setModelId(uint16_t shortaddr, const char * str); + void setFriendlyName(uint16_t shortaddr, const char * str); + inline const char * getFriendlyName(uint16_t shortaddr) const { + return findShortAddr(shortaddr).friendlyName; + } + inline const char * getModelId(uint16_t shortaddr) const { + return findShortAddr(shortaddr).modelId; + } + inline const char * getManufacturerId(uint16_t shortaddr) const{ + return findShortAddr(shortaddr).manufacturerId; + } + + void setReachable(uint16_t shortaddr, bool reachable); + void setLQI(uint16_t shortaddr, uint8_t lqi); + void setLastSeenNow(uint16_t shortaddr); + + void setBatteryPercent(uint16_t shortaddr, uint8_t bp); + uint8_t getBatteryPercent(uint16_t shortaddr) const; + + + uint8_t getNextSeqNumber(uint16_t shortaddr); + + + static void addLightState(Z_attribute_list & attr_list, const Z_Data_Light & light); + String dumpLightState(uint16_t shortaddr) const; + String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; + int32_t deviceRestore(JsonParserObject json); + + + void setLightProfile(uint16_t shortaddr, uint8_t light_profile); + uint8_t getLightProfile(uint16_t shortaddr) const ; + + + int8_t getHueBulbtype(uint16_t shortaddr) const ; + void hideHueBulb(uint16_t shortaddr, bool hidden); + bool isHueBulbHidden(uint16_t shortaddr) const ; + Z_Data_Light & getLight(uint16_t shortaddr); + + + void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF); + void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); + void queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); + void runTimer(void); + + + void jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list); + void jsonPublishFlush(uint16_t shortaddr); + bool jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const; + void jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list); + + + size_t devicesSize(void) const { + return _devices.length(); + } + const Z_Device & devicesAt(size_t i) const { + const Z_Device * devp = _devices.at(i); + if (devp) { + return *devp; + } else { + return device_unk; + } + } + + + bool removeDevice(uint16_t shortaddr); + + + void dirty(void); + void clean(void); + void shrinkToFit(uint16_t shortaddr); + + + uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; + +private: + LList _devices; + LList _deferred; + uint32_t _saveTimer = 0; + uint8_t _seqNumber = 0; + + + + const Z_Device device_unk = Z_Device(BAD_SHORTADDR); + + + + Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); + void freeDeviceEntry(Z_Device *device); + + void setStringAttribute(char*& attr, const char * str); +}; + + + + +Z_Devices zigbee_devices = Z_Devices(); + + +uint64_t localIEEEAddr = 0; +uint16_t localShortAddr = 0; + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +#ifdef USE_ZIGBEE +# 30 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { + if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return (Z_Device&) device_unk; } + Z_Device device(shortaddr, longaddr); + + dirty(); + return _devices.addHead(device); +} + +void Z_Devices::freeDeviceEntry(Z_Device *device) { + if (device->manufacturerId) { free(device->manufacturerId); } + if (device->modelId) { free(device->modelId); } + if (device->friendlyName) { free(device->friendlyName); } + free(device); +} +# 53 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) { + for (auto & elem : _devices) { + if (elem.shortaddr == shortaddr) { return elem; } + } + return (Z_Device&) device_unk; +} +const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const { + for (const auto & elem : _devices) { + if (elem.shortaddr == shortaddr) { return elem; } + } + return device_unk; +} +# 73 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) { + if (!longaddr) { return (Z_Device&) device_unk; } + for (auto &elem : _devices) { + if (elem.longaddr == longaddr) { return elem; } + } + return (Z_Device&) device_unk; +} +const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const { + if (!longaddr) { return device_unk; } + for (const auto &elem : _devices) { + if (elem.longaddr == longaddr) { return elem; } + } + return device_unk; +} +# 95 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +int32_t Z_Devices::findFriendlyName(const char * name) const { + if (!name) { return -1; } + size_t name_len = strlen(name); + int32_t found = 0; + if (name_len) { + for (auto &elem : _devices) { + if (elem.friendlyName) { + if (strcasecmp(elem.friendlyName, name) == 0) { return found; } + } + found++; + } + } + return -1; +} + +uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { + const Z_Device & device = findLongAddr(longaddr); + if (foundDevice(device)) { + return device.shortaddr; + } else { + return BAD_SHORTADDR; + } +} + +uint16_t Z_Devices::isKnownIndex(uint32_t index) const { + if (index < devicesSize()) { + const Z_Device & device = devicesAt(index); + return device.shortaddr; + } else { + return BAD_SHORTADDR; + } +} + +uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { + if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } + int32_t found = findFriendlyName(name); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.shortaddr; + } else { + return BAD_SHORTADDR; + } +} + +uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const { + return findShortAddr(shortaddr).longaddr; +} + + + + +Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { + if (BAD_SHORTADDR == shortaddr) { return (Z_Device&) device_unk; } + Z_Device & device = findShortAddr(shortaddr); + if (foundDevice(device)) { + return device; + } + return createDeviceEntry(shortaddr, 0); +} + + +Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { + if (!longaddr) { return (Z_Device&) device_unk; } + Z_Device & device = findLongAddr(longaddr); + if (foundDevice(device)) { + return device; + } + return createDeviceEntry(0, longaddr); +} + + +bool Z_Devices::removeDevice(uint16_t shortaddr) { + Z_Device & device = findShortAddr(shortaddr); + if (foundDevice(device)) { + _devices.remove(&device); + dirty(); + return true; + } + return false; +} + + + + + + +void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { + Z_Device * s_found = &findShortAddr(shortaddr); + Z_Device * l_found = &findLongAddr(longaddr); + + if (foundDevice(*s_found) && foundDevice(*l_found)) { + if (s_found == l_found) { + } else { + + l_found->shortaddr = shortaddr; + + freeDeviceEntry(s_found); + _devices.remove(s_found); + dirty(); + } + } else if (foundDevice(*s_found)) { + + + s_found->longaddr = longaddr; + dirty(); + } else if (foundDevice(*l_found)) { + + l_found->shortaddr = shortaddr; + dirty(); + } else { + + if ((BAD_SHORTADDR != shortaddr) || longaddr) { + createDeviceEntry(shortaddr, longaddr); + } + } +} + + + + +void Z_Devices::clearEndpoints(uint16_t shortaddr) { + Z_Device &device = getShortAddr(shortaddr); + for (uint32_t i = 0; i < endpoints_max; i++) { + device.endpoints[i] = 0; + + } +} + + + + +void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) { + if (0x00 == endpoint) { return; } + Z_Device &device = getShortAddr(shortaddr); + + for (uint32_t i = 0; i < endpoints_max; i++) { + if (endpoint == device.endpoints[i]) { + return; + } + if (0 == device.endpoints[i]) { + device.endpoints[i] = endpoint; + dirty(); + return; + } + } +} + + + + +uint32_t Z_Devices::countEndpoints(uint16_t shortaddr) const { + uint32_t count_ep = 0; + const Z_Device & device =findShortAddr(shortaddr); + if (!foundDevice(device)) return 0; + + for (uint32_t i = 0; i < endpoints_max; i++) { + if (0 != device.endpoints[i]) { + count_ep++; + } + } + return count_ep; +} + + +uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const { + + if (0x0000 == shortaddr) { return 1; } + return findShortAddr(shortaddr).endpoints[0]; +} + +void Z_Devices::setStringAttribute(char*& attr, const char * str) { + if (nullptr == str) { return; } + size_t str_len = strlen(str); + + if ((nullptr == attr) && (0 == str_len)) { return; } + if (attr) { + + if (strcmp(attr, str) != 0) { + + free(attr); + attr = nullptr; + } else { + return; + } + } + if (str_len) { + attr = (char*) malloc(str_len + 1); + strlcpy(attr, str, str_len + 1); + } + dirty(); +} +# 296 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { + setStringAttribute(getShortAddr(shortaddr).manufacturerId, str); +} + +void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { + setStringAttribute(getShortAddr(shortaddr).modelId, str); +} + +void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { + setStringAttribute(getShortAddr(shortaddr).friendlyName, str); +} + + +void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) { + getShortAddr(shortaddr).setReachable(reachable); +} + +void Z_Devices::setLQI(uint16_t shortaddr, uint8_t lqi) { + if (shortaddr == localShortAddr) { return; } + getShortAddr(shortaddr).lqi = lqi; +} + +void Z_Devices::setLastSeenNow(uint16_t shortaddr) { + if (shortaddr == localShortAddr) { return; } + + + + + if (Rtc.utc_time < 1577836800) { return; } + getShortAddr(shortaddr).last_seen = Rtc.utc_time; +} + + +void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) { + getShortAddr(shortaddr).batterypercent = bp; +} + + +uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { + Z_Device & device = findShortAddr(shortaddr); + if (foundDevice(device)) { + device.seqNumber += 1; + return device.seqNumber; + } else { + _seqNumber += 1; + return _seqNumber; + } +} + + +void Z_Devices::setLightProfile(uint16_t shortaddr, uint8_t light_profile) { + Z_Device &device = getShortAddr(shortaddr); + if (device.setLightChannels(light_profile)) { + dirty(); + } +} + + +uint8_t Z_Devices::getLightProfile(uint16_t shortaddr) const { + const Z_Device &device = findShortAddr(shortaddr); + return device.getLightChannels(); +} + +int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const { + int8_t light_profile = getLightProfile(shortaddr); + if (0x00 == (light_profile & 0xF0)) { + return (light_profile & 0x07); + } else { + + return -1; + } +} + +void Z_Devices::hideHueBulb(uint16_t shortaddr, bool hidden) { + Z_Device &device = getShortAddr(shortaddr); + if (device.hidden != hidden) { + device.hidden = hidden; + dirty(); + } +} + +bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const { + const Z_Device & device = findShortAddr(shortaddr); + if (foundDevice(device)) { + return device.hidden; + } + return true; +} + + + + +void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster, uint8_t endpoint) { + + for (auto & defer : _deferred) { + if ((defer.shortaddr == shortaddr) && (defer.groupaddr == groupaddr)) { + if ((0xFF == category) || (defer.category == category)) { + if ((0xFFFF == cluster) || (defer.cluster == cluster)) { + if ((0xFF == endpoint) || (defer.endpoint == endpoint)) { + _deferred.remove(&defer); + } + } + } + } + } +} + + +void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { + + if (category >= Z_CLEAR_DEVICE) { + resetTimersForDevice(shortaddr, groupaddr, category, category >= Z_CLEAR_DEVICE_CLUSTER ? cluster : 0xFFFF, category >= Z_CLEAR_DEVICE_CLUSTER_ENDPOINT ? endpoint : 0xFF); + } + + + Z_Deferred & deferred = _deferred.addHead(); + deferred = { wait_ms + millis(), + shortaddr, + groupaddr, + cluster, + endpoint, + category, + value, + func }; +} + + + +void Z_Devices::queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { + Z_Device & device = getShortAddr(shortaddr); + uint32_t now_millis = millis(); + if (TimeReached(device.defer_last_message_sent)) { + device.defer_last_message_sent = now_millis; + } + + device.defer_last_message_sent += wait_ms; + + + setTimer(shortaddr, groupaddr, (device.defer_last_message_sent - now_millis), cluster, endpoint, Z_CAT_ALWAYS, value, func); +} + + + +void Z_Devices::runTimer(void) { + + for (auto & defer : _deferred) { + uint32_t timer = defer.timer; + if (TimeReached(timer)) { + (*defer.func)(defer.shortaddr, defer.groupaddr, defer.cluster, defer.endpoint, defer.value); + _deferred.remove(&defer); + } + } + + + if ((_saveTimer) && TimeReached(_saveTimer)) { + saveZigbeeDevices(); + _saveTimer = 0; + } +} + + + + +bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const { + const Z_Device & device = findShortAddr(shortaddr); + + if (!foundDevice(device)) { return false; } + if (attr_list.isEmpty()) { + return false; + } + + + if (device.attr_list.isValidGroupId() && attr_list.isValidGroupId()) { + if (device.attr_list.group_id != attr_list.group_id) { return true; } + } + + + if (device.attr_list.isValidSrcEp() && attr_list.isValidSrcEp()) { + if (device.attr_list.src_ep != attr_list.src_ep) { return true; } + } + + + + + for (const auto & attr : attr_list) { + const Z_attribute * curr_attr = device.attr_list.findAttribute(attr); + if (nullptr != curr_attr) { + if (!curr_attr->equalsVal(attr)) { + return true; + } + } + } + return false; +} + +void Z_Devices::jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list) { + Z_Device & device = getShortAddr(shortaddr); + device.attr_list.mergeList(attr_list); +} + +void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (!device.valid()) { return; } + Z_attribute_list &attr_list = device.attr_list; + + if (!attr_list.isEmpty()) { + const char * fname = zigbee_devices.getFriendlyName(shortaddr); + bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); + + + gZbLastMessage.device = shortaddr; + gZbLastMessage.groupaddr = attr_list.group_id; + gZbLastMessage.endpoint = attr_list.src_ep; + + mqtt_data[0] = 0; + + if (!Settings.flag4.remove_zbreceived) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":")); + } + + if (use_fname) { + Response_P(PSTR("%s{\"%s\":{"), mqtt_data, fname); + } else { + Response_P(PSTR("%s{\"0x%04X\":{"), mqtt_data, shortaddr); + } + + Response_P(PSTR("%s\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\","), mqtt_data, shortaddr); + + if (fname) { + Response_P(PSTR("%s\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), mqtt_data, EscapeJSONString(fname).c_str()); + } + + Response_P(PSTR("%s%s}}"), mqtt_data, attr_list.toString().c_str()); + + if (!Settings.flag4.remove_zbreceived) { + Response_P(PSTR("%s}"), mqtt_data); + } + attr_list.reset(); + + if (Settings.flag4.zigbee_distinct_topics) { + if (Settings.flag4.zb_topic_fname && fname) { + + char stemp[TOPSZ]; + strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp)); + MakeValidMqtt(0, stemp); + + char frtopic[TOPSZ]; + snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), stemp); + MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain); + } else { + char subtopic[16]; + snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr); + MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); + } + } else { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + } + XdrvRulesProcess(); + } +} + +void Z_Devices::jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list) { + jsonPublishFlush(shortaddr); + jsonAppend(shortaddr, attr_list); + jsonPublishFlush(shortaddr); +} + +void Z_Devices::dirty(void) { + _saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis(); +} +void Z_Devices::clean(void) { + _saveTimer = 0; +} + + + + + + +uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { + if (nullptr == param) { return BAD_SHORTADDR; } + size_t param_len = strlen(param); + char dataBuf[param_len + 1]; + strcpy(dataBuf, param); + RemoveSpace(dataBuf); + uint16_t shortaddr = BAD_SHORTADDR; + + if (strlen(dataBuf) < 4) { + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { + shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); + } + } else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) { + + if (strlen(dataBuf) < 18) { + + shortaddr = strtoull(dataBuf, nullptr, 0); + if (short_must_be_known) { + shortaddr = zigbee_devices.findShortAddr(shortaddr).shortaddr; + } + + } else { + + uint64_t longaddr = strtoull(dataBuf, nullptr, 0); + shortaddr = zigbee_devices.isKnownLongAddr(longaddr); + } + } else { + + shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); + } + + return shortaddr; +} + + +String Z_Devices::dumpLightState(uint16_t shortaddr) const { + Z_attribute_list attr_list; + char hex[8]; + + const Z_Device & device = findShortAddr(shortaddr); + const char * fname = getFriendlyName(shortaddr); + bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + + attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex); + if (fname) { + attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname); + } + + if (foundDevice(device)) { + + attr_list.addAttribute(F("Reachable")).setBool(device.getReachable()); + if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); } + const Z_Data_Light & light = device.data.find(0); + if (&light != nullptr) { + light.toAttributes(attr_list, Z_Data_Light::type); + + if (light.validHue()) { + attr_list.findOrCreateAttribute(PSTR("Hue")).setUInt(light.getHue()); + } + } + + } + + Z_attribute_list attr_list_root; + Z_attribute * attr_root; + if (use_fname) { + attr_root = &attr_list_root.addAttribute(fname); + } else { + attr_root = &attr_list_root.addAttribute(hex); + } + attr_root->setStrRaw(attr_list.toString(true).c_str()); + return attr_list_root.toString(true); +} + + + + +String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { + Z_json_array json_arr; + + for (const auto & device : _devices) { + uint16_t shortaddr = device.shortaddr; + char hex[22]; + + + if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } + + Z_attribute_list attr_list; + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex); + + if (device.friendlyName > 0) { + attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName); + } + + if (2 <= dump_mode) { + hex[0] = '0'; + hex[1] = 'x'; + Uint64toHex(device.longaddr, &hex[2], 64); + attr_list.addAttribute(F("IEEEAddr")).setStr(hex); + if (device.modelId) { + attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId); + } + int8_t bulbtype = getHueBulbtype(shortaddr); + if (bulbtype >= 0) { + attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); + } + if (device.manufacturerId) { + attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId); + } + Z_json_array arr_ep; + for (uint32_t i = 0; i < endpoints_max; i++) { + uint8_t endpoint = device.endpoints[i]; + if (0x00 == endpoint) { break; } + arr_ep.add(endpoint); + } + attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str()); + } + json_arr.addStrRaw(attr_list.toString(true).c_str()); + } + return json_arr.toString(); +} +# 710 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_2a_devices_impl.ino" +int32_t Z_Devices::deviceRestore(JsonParserObject json) { + + + uint16_t device = 0x0000; + uint64_t ieeeaddr = 0x0000000000000000LL; + const char * modelid = nullptr; + const char * manufid = nullptr; + const char * friendlyname = nullptr; + int8_t bulbtype = -1; + size_t endpoints_len = 0; + + + JsonParserToken val_device = json[PSTR("Device")]; + if (val_device) { + device = (uint32_t) val_device.getUInt(device); + } else { + return -1; + } + + ieeeaddr = json.getULong(PSTR("IEEEAddr"), ieeeaddr); + friendlyname = json.getStr(PSTR("Name"), nullptr); + modelid = json.getStr(PSTR("ModelId"), nullptr); + manufid = json.getStr(PSTR("Manufacturer"), nullptr); + JsonParserToken tok_bulbtype = json[PSTR(D_JSON_ZIGBEE_LIGHT)]; + + + updateDevice(device, ieeeaddr); + if (modelid) { setModelId(device, modelid); } + if (manufid) { setManufId(device, manufid); } + if (friendlyname) { setFriendlyName(device, friendlyname); } + if (tok_bulbtype) { setLightProfile(device, tok_bulbtype.getInt()); } + + + JsonParserToken val_endpoints = json[PSTR("Endpoints")]; + if (val_endpoints.isArray()) { + JsonParserArray arr_ep = JsonParserArray(val_endpoints); + clearEndpoints(device); + for (auto ep_elt : arr_ep) { + uint8_t ep = ep_elt.getUInt(); + if (ep) { addEndpoint(device, ep); } + } + } + + return 0; +} + +Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr) { + return getShortAddr(shortaddr).data.get(); +} + + + + +void Z_Device::toAttributes(Z_attribute_list & attr_list) const { + if (validLqi()) { attr_list.addAttribute(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); } + if (validBatteryPercent()) { attr_list.addAttribute(PSTR("BatteryPercentage")).setUInt(batterypercent); } + if (validLastSeen()) { attr_list.addAttribute(PSTR("LastSeen")).setUInt(last_seen); } +} + + + + +void Z_Device::setPower(bool power_on, uint8_t ep) { + data.get(ep).setPower(power_on); +} + +bool Z_Device::validPower(uint8_t ep) const { + const Z_Data_OnOff & onoff = data.find(ep); + return (&onoff != nullptr); +} + +bool Z_Device::getPower(uint8_t ep) const { + const Z_Data_OnOff & onoff = data.find(ep); + if (&onoff != nullptr) return onoff.getPower(); + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" +#ifdef USE_ZIGBEE +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + + + + +void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response) { + static const char HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE[] PROGMEM = + "%s\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":%s}"; + + bool power = false; + bool reachable = false; + uint8_t colormode = 0xFF; + uint8_t bri = 0xFF; + uint8_t sat = 0xFF; + uint16_t ct = 0xFFFF; + uint16_t hue = 0xFFFF; + uint16_t x = 0xFFFF, y = 0xFFFF; + String light_status = ""; + uint32_t echo_gen = findEchoGeneration(); + + const Z_Device & device = zigbee_devices.findShortAddr(shortaddr); + const Z_Data_Light & light = device.data.find(); + if (&light != nullptr) { + bri = light.getDimmer(); + colormode = light.getColorMode(); + sat = light.getSat(); + ct = light.getCT(); + hue = light.getHue(); + x = light.getX(); + y = light.getY(); + } + power = device.getPower(); + reachable = device.getReachable(); + + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + if (sat > 254) sat = 254; + uint16_t hue16 = changeUIntScale(hue, 0, 360, 0, 65535); + + const size_t buf_size = 256; + char * buf = (char*) malloc(buf_size); + + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false"); + + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { + snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct"); + } + if (LST_RGB <= local_light_subtype) { + if (prev_x_str[0] && prev_y_str[0]) { + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); + } else { + float x_f = x / 65536.0f; + float y_f = y / 65536.0f; + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); + } + snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue16, sat); + } + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false"); + + *response += buf; + free(buf); +} + +void HueLightStatus2Zigbee(uint16_t shortaddr, String *response) +{ + const size_t buf_size = 300; + char * buf = (char*) malloc(buf_size); + + const Z_Device & device = zigbee_devices.findShortAddr(shortaddr); + const char * friendlyName = device.friendlyName; + const char * modelId = device.modelId; + const char * manufacturerId = device.manufacturerId; + char shortaddrname[8]; + snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr); + + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, + (friendlyName) ? EscapeJSONString(friendlyName).c_str() : shortaddrname, + (modelId) ? EscapeJSONString(modelId).c_str() : PSTR("Unknown"), + (manufacturerId) ? EscapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"), + GetHueDeviceId(shortaddr).c_str()); + + *response += buf; + free(buf); +} + +void ZigbeeHueStatus(String * response, uint16_t shortaddr) { + *response += F("{\"state\":"); + HueLightStatus1Zigbee(shortaddr, zigbee_devices.getHueBulbtype(shortaddr), response); + HueLightStatus2Zigbee(shortaddr, response); +} + +void ZigbeeCheckHue(String * response, bool &appending) { + uint32_t zigbee_num = zigbee_devices.devicesSize(); + for (uint32_t i = 0; i < zigbee_num; i++) { + uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr; + int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr); + + if (bulbtype >= 0) { + + if (appending) { *response += ","; } + *response += "\""; + *response += EncodeLightId(0, shortaddr); + *response += F("\":{\"state\":"); + HueLightStatus1Zigbee(shortaddr, bulbtype, response); + HueLightStatus2Zigbee(shortaddr, response); + appending = true; + } + } +} + +void ZigbeeHueGroups(String * lights) { + uint32_t zigbee_num = zigbee_devices.devicesSize(); + for (uint32_t i = 0; i < zigbee_num; i++) { + uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr; + int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr); + + if (bulbtype >= 0) { + *lights += ",\""; + *lights += EncodeLightId(i); + *lights += "\""; + } + } +} + + + +void ZigbeeHuePower(uint16_t shortaddr, bool power) { + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, ""); + zigbee_devices.getShortAddr(shortaddr).setPower(power, 0); +} + + +void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { + if (dimmer > 0xFE) { dimmer = 0xFE; } + char param[8]; + snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param); + zigbee_devices.getLight(shortaddr).setDimmer(dimmer); +} + + +void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { + if (ct > 0xFEFF) { ct = 0xFEFF; } + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct); + char param[12]; + snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); + uint8_t colormode = 2; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param); + Z_Data_Light & light = zigbee_devices.getLight(shortaddr); + light.setColorMode(colormode); + light.setCT(ct); +} + + +void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { + char param[16]; + if (x > 0xFEFF) { x = 0xFEFF; } + if (y > 0xFEFF) { y = 0xFEFF; } + snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); + uint8_t colormode = 1; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param); + Z_Data_Light & light = zigbee_devices.getLight(shortaddr); + light.setColorMode(colormode); + light.setX(x); + light.setY(y); +} + + +void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { + char param[16]; + uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); + if (sat > 0xFE) { sat = 0xFE; } + snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); + uint8_t colormode = 0; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param); + Z_Data_Light & light = zigbee_devices.getLight(shortaddr); + light.setColorMode(colormode); + light.setSat(sat); + light.setHue(hue); +} + +void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { + uint8_t power, colormode, bri, sat; + uint16_t ct, hue; + float x, y; + int code = 200; + + bool resp = false; + bool on = false; + + uint8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr); + + const size_t buf_size = 100; + char * buf = (char*) malloc(buf_size); + + if (Webserver->args()) { + response = "["; + + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParserObject root = parser.getRootObject(); + + JsonParserToken hue_on = root[PSTR("on")]; + if (hue_on) { + on = hue_on.getBool(); + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), + device_id, on ? "true" : "false"); + + if (on) { + ZigbeeHuePower(shortaddr, 0x01); + } else { + ZigbeeHuePower(shortaddr, 0x00); + } + response += buf; + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_bri = root[PSTR("bri")]; + if (hue_bri) { + bri = hue_bri.getUInt(); + prev_bri = bri; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "bri", bri); + response += buf; + if (LST_SINGLE <= bulbtype) { + + if (254 <= bri) { bri = 255; } + ZigbeeHueDimmer(shortaddr, bri); + } + resp = true; + } + + + parser.setCurrent(); + JsonParserToken hue_xy = root[PSTR("xy")]; + if (hue_xy) { + JsonParserArray arr_xy = JsonParserArray(hue_xy); + JsonParserToken tok_x = arr_xy[0]; + JsonParserToken tok_y = arr_xy[1]; + float x = tok_x.getFloat(); + float y = tok_y.getFloat(); + strlcpy(prev_x_str, tok_x.getStr(), sizeof(prev_x_str)); + strlcpy(prev_y_str, tok_y.getStr(), sizeof(prev_y_str)); + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), + device_id, prev_x_str, prev_y_str); + response += buf; + resp = true; + uint16_t xi = x * 65536.0f; + uint16_t yi = y * 65536.0f; + ZigbeeHueXY(shortaddr, xi, yi); + } + bool huesat_changed = false; + + parser.setCurrent(); + JsonParserToken hue_hue = root[PSTR("hue")]; + if (hue_hue) { + hue = hue_hue.getUInt(); + prev_hue = hue; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "hue", hue); + response += buf; + if (LST_RGB <= bulbtype) { + + hue = changeUIntScale(hue, 0, 65535, 0, 360); + huesat_changed = true; + } + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_sat = root[PSTR("sat")]; + if (hue_sat) { + sat = hue_sat.getUInt(); + prev_sat = sat; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "sat", sat); + response += buf; + if (LST_RGB <= bulbtype) { + + if (254 <= sat) { sat = 255; } + huesat_changed = true; + } + if (huesat_changed) { + ZigbeeHueHS(shortaddr, hue, sat); + } + resp = true; + } + + parser.setCurrent(); + JsonParserToken hue_ct = root[PSTR("ct")]; + if (hue_ct) { + ct = hue_ct.getUInt(); + prev_ct = ct; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "ct", ct); + response += buf; + if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) { + ZigbeeHueCT(shortaddr, ct); + } + resp = true; + } + + response += "]"; + if (2 == response.length()) { + response = FPSTR(HUE_ERROR_JSON); + } + } + else { + response = FPSTR(HUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + + free(buf); +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +#ifdef USE_ZIGBEE +# 52 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +#ifdef ESP8266 +const static uint16_t z_spi_start_sector = 0xFF; +const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; +const static uint8_t* z_dev_start = z_spi_start + 0x0800; +const static size_t z_spi_len = 0x1000; +const static size_t z_block_offset = 0x0800; +const static size_t z_block_len = 0x0800; +#else +uint8_t* z_dev_start; +const static size_t z_spi_len = 0x1000; +const static size_t z_block_offset = 0x0000; +const static size_t z_block_len = 0x1000; +#endif + +class z_flashdata_t { +public: + uint32_t name; + uint16_t len; + uint16_t reserved; +}; + +const static uint32_t ZIGB_NAME = 0x3167697A; +const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); + + +class SBuffer hibernateDevice(const struct Z_Device &device) { + SBuffer buf(128); + + buf.add8(0x00); + buf.add16(device.shortaddr); + buf.add64(device.longaddr); + + uint32_t endpoints_count = 0; + for (endpoints_count = 0; endpoints_count < endpoints_max; endpoints_count++) { + if (0x00 == device.endpoints[endpoints_count]) { break; } + } + + buf.add8(endpoints_count); + + for (uint32_t i = 0; i < endpoints_max; i++) { + uint8_t endpoint = device.endpoints[i]; + if (0x00 == endpoint) { break; } + + buf.add8(endpoint); + buf.add16(0x0000); + + + buf.add8(0xFF); + + + buf.add8(0xFF); + } + + + if (device.modelId) { + size_t model_len = strlen(device.modelId); + if (model_len > 32) { model_len = 32; } + buf.addBuffer(device.modelId, model_len); + } + buf.add8(0x00); + + + if (device.manufacturerId) { + size_t manuf_len = strlen(device.manufacturerId); + if (manuf_len > 32) { manuf_len = 32; } + buf.addBuffer(device.manufacturerId, manuf_len); + } + buf.add8(0x00); + + + if (device.friendlyName) { + size_t frname_len = strlen(device.friendlyName); + if (frname_len > 32) {frname_len = 32; } + buf.addBuffer(device.friendlyName, frname_len); + } + buf.add8(0x00); + + + buf.add8(device.getLightChannels()); + + + buf.set8(0, buf.len()); + + return buf; +} + +class SBuffer hibernateDevices(void) { + SBuffer buf(2048); + + size_t devices_size = zigbee_devices.devicesSize(); + if (devices_size > 32) { devices_size = 32; } + buf.add8(devices_size); + + for (uint32_t i = 0; i < devices_size; i++) { + const Z_Device & device = zigbee_devices.devicesAt(i); + const SBuffer buf_device = hibernateDevice(device); + buf.addBuffer(buf_device); + } + + size_t buf_len = buf.len(); + if (buf_len > 2040) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len); + } + + + char *hex_char = (char*) malloc((buf_len * 2) + 2); + if (hex_char) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"), + ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2)); + free(hex_char); + } + + return buf; +} + +void hydrateDevices(const SBuffer &buf) { + uint32_t buf_len = buf.len(); + if (buf_len <= 10) { return; } + + uint32_t k = 0; + uint32_t num_devices = buf.get8(k++); + for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { + uint32_t dev_record_len = buf.get8(k); + + SBuffer buf_d = buf.subBuffer(k, dev_record_len); + + uint32_t d = 1; + uint16_t shortaddr = buf_d.get16(d); d += 2; + uint64_t longaddr = buf_d.get64(d); d += 8; + zigbee_devices.updateDevice(shortaddr, longaddr); + + uint32_t endpoints = buf_d.get8(d++); + for (uint32_t j = 0; j < endpoints; j++) { + uint8_t ep = buf_d.get8(d++); + uint16_t ep_profile = buf_d.get16(d); d += 2; + zigbee_devices.addEndpoint(shortaddr, ep); + + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + + } + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + + } + } + + + char empty[] = ""; + + + uint32_t s_len = buf_d.strlen_s(d); + char *ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setModelId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setManufId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setFriendlyName(shortaddr, ptr); + d += s_len + 1; + + + if (d < dev_record_len) { + zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d)); + d++; + } + + + k += dev_record_len; + } +} + +void loadZigbeeDevices(void) { +#ifdef ESP32 + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + ZigbeeRead(&spi_buffer, z_spi_len); + z_dev_start = spi_buffer; +#endif + z_flashdata_t flashdata; + memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); + + + if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) { + uint16_t buf_len = flashdata.len; + + SBuffer buf(buf_len); + buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len); + hydrateDevices(buf); + zigbee_devices.clean(); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); + } + +#ifdef ESP32 + free(spi_buffer); +#endif +} + +void saveZigbeeDevices(void) { + SBuffer buf = hibernateDevices(); + size_t buf_len = buf.len(); + if (buf_len > Z_MAX_FLASH) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); + return; + } + + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + +#ifdef ESP8266 + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); +#else + ZigbeeRead(&spi_buffer, z_spi_len); +#endif + + z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset); + flashdata->name = ZIGB_NAME; + flashdata->len = buf_len; + flashdata->reserved = 0; + + memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len); + + +#ifdef ESP8266 + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); +#else + ZigbeeWrite(&spi_buffer, z_spi_len); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved (%d bytes)"), buf_len); +#endif + free(spi_buffer); +} + + +void eraseZigbeeDevices(void) { + zigbee_devices.clean(); +#ifdef ESP8266 + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + + memset(spi_buffer + z_block_offset, 0xFF, z_block_len); + + + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len); +#else + ZigbeeErase(); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (%d bytes)"), z_block_len); +#endif +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5__constants.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5__constants.ino" +#ifdef USE_ZIGBEE +# 76 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5__constants.ino" +const char Z_strings[] PROGMEM = + "\x00" + "ZCLVersion" "\x00" + "AppVersion" "\x00" + "StackVersion" "\x00" + "HWVersion" "\x00" + "Manufacturer" "\x00" + "ModelId" "\x00" + "DateCode" "\x00" + "PowerSource" "\x00" + "GenericDeviceClass" "\x00" + "GenericDeviceType" "\x00" + "ProductCode" "\x00" + "ProductURL" "\x00" + "SWBuildID" "\x00" + "MainsVoltage" "\x00" + "MainsFrequency" "\x00" + "BatteryVoltage" "\x00" + "BatteryPercentage" "\x00" + "CurrentTemperature" "\x00" + "MinTempExperienced" "\x00" + "MaxTempExperienced" "\x00" + "OverTempTotalDwell" "\x00" + "IdentifyTime" "\x00" + "GroupNameSupport" "\x00" + "SceneCount" "\x00" + "CurrentScene" "\x00" + "CurrentGroup" "\x00" + "SceneValid" "\x00" + "Power" "\x00" + "StartUpOnOff" "\x00" + "SwitchType" "\x00" + "Dimmer" "\x00" + "DimmerOptions" "\x00" + "DimmerRemainingTime" "\x00" + "OnOffTransitionTime" "\x00" + "AlarmCount" "\x00" + "Time" "\x00" + "TimeStatus" "\x00" + "TimeZone" "\x00" + "DstStart" "\x00" + "DstEnd" "\x00" + "DstShift" "\x00" + "StandardTime" "\x00" + "LocalTime" "\x00" + "LastSetTime" "\x00" + "ValidUntilTime" "\x00" + "TimeEpoch" "\x00" + "LocationType" "\x00" + "LocationMethod" "\x00" + "LocationAge" "\x00" + "QualityMeasure" "\x00" + "NumberOfDevices" "\x00" + "AnalogInDescription" "\x00" + "AnalogInMaxValue" "\x00" + "AnalogInMinValue" "\x00" + "AnalogInOutOfService" "\x00" + "AqaraRotate" "\x00" + "AnalogInReliability" "\x00" + "AnalogInResolution" "\x00" + "AnalogInStatusFlags" "\x00" + "AnalogInEngineeringUnits" "\x00" + "AnalogInApplicationType" "\x00" + "Aqara_FF05" "\x00" + "AnalogOutDescription" "\x00" + "AnalogOutMaxValue" "\x00" + "AnalogOutMinValue" "\x00" + "AnalogOutOutOfService" "\x00" + "AnalogOutValue" "\x00" + "AnalogOutReliability" "\x00" + "AnalogOutRelinquishDefault" "\x00" + "AnalogOutResolution" "\x00" + "AnalogOutStatusFlags" "\x00" + "AnalogOutEngineeringUnits" "\x00" + "AnalogOutApplicationType" "\x00" + "AnalogDescription" "\x00" + "AnalogOutOfService" "\x00" + "AnalogValue" "\x00" + "AnalogPriorityArray" "\x00" + "AnalogReliability" "\x00" + "AnalogRelinquishDefault" "\x00" + "AnalogStatusFlags" "\x00" + "AnalogEngineeringUnits" "\x00" + "AnalogApplicationType" "\x00" + "BinaryInActiveText" "\x00" + "BinaryInDescription" "\x00" + "BinaryInInactiveText" "\x00" + "BinaryInOutOfService" "\x00" + "BinaryInPolarity" "\x00" + "BinaryInValue" "\x00" + "BinaryInReliability" "\x00" + "BinaryInStatusFlags" "\x00" + "BinaryInApplicationType" "\x00" + "BinaryOutActiveText" "\x00" + "BinaryOutDescription" "\x00" + "BinaryOutInactiveText" "\x00" + "BinaryOutMinimumOffTime" "\x00" + "BinaryOutMinimumOnTime" "\x00" + "BinaryOutOutOfService" "\x00" + "BinaryOutPolarity" "\x00" + "BinaryOutValue" "\x00" + "BinaryOutReliability" "\x00" + "BinaryOutRelinquishDefault" "\x00" + "BinaryOutStatusFlags" "\x00" + "BinaryOutApplicationType" "\x00" + "BinaryActiveText" "\x00" + "BinaryDescription" "\x00" + "BinaryInactiveText" "\x00" + "BinaryMinimumOffTime" "\x00" + "BinaryMinimumOnTime" "\x00" + "BinaryOutOfService" "\x00" + "BinaryValue" "\x00" + "BinaryReliability" "\x00" + "BinaryRelinquishDefault" "\x00" + "BinaryStatusFlags" "\x00" + "BinaryApplicationType" "\x00" + "MultiInDescription" "\x00" + "MultiInNumberOfStates" "\x00" + "MultiInOutOfService" "\x00" + "MultiInValue" "\x00" + "MultiInReliability" "\x00" + "MultiInStatusFlags" "\x00" + "MultiInApplicationType" "\x00" + "MultiOutDescription" "\x00" + "MultiOutNumberOfStates" "\x00" + "MultiOutOutOfService" "\x00" + "MultiOutValue" "\x00" + "MultiOutReliability" "\x00" + "MultiOutRelinquishDefault" "\x00" + "MultiOutStatusFlags" "\x00" + "MultiOutApplicationType" "\x00" + "MultiDescription" "\x00" + "MultiNumberOfStates" "\x00" + "MultiOutOfService" "\x00" + "MultiValue" "\x00" + "MultiReliability" "\x00" + "MultiRelinquishDefault" "\x00" + "MultiStatusFlags" "\x00" + "MultiApplicationType" "\x00" + "TotalProfileNum" "\x00" + "MultipleScheduling" "\x00" + "EnergyFormatting" "\x00" + "EnergyRemote" "\x00" + "ScheduleMode" "\x00" + "CheckinInterval" "\x00" + "LongPollInterval" "\x00" + "ShortPollInterval" "\x00" + "FastPollTimeout" "\x00" + "CheckinIntervalMin" "\x00" + "LongPollIntervalMin" "\x00" + "FastPollTimeoutMax" "\x00" + "PhysicalClosedLimit" "\x00" + "MotorStepSize" "\x00" + "Status" "\x00" + "ClosedLimit" "\x00" + "Mode" "\x00" + "LockState" "\x00" + "LockType" "\x00" + "ActuatorEnabled" "\x00" + "DoorState" "\x00" + "DoorOpenEvents" "\x00" + "DoorClosedEvents" "\x00" + "OpenPeriod" "\x00" + "AqaraVibrationMode" "\x00" + "AqaraVibrationsOrAngle" "\x00" + "AqaraVibration505" "\x00" + "AqaraAccelerometer" "\x00" + "WindowCoveringType" "\x00" + "PhysicalClosedLimitLift" "\x00" + "PhysicalClosedLimitTilt" "\x00" + "CurrentPositionLift" "\x00" + "CurrentPositionTilt" "\x00" + "NumberofActuationsLift" "\x00" + "NumberofActuationsTilt" "\x00" + "ConfigStatus" "\x00" + "CurrentPositionLiftPercentage" "\x00" + "CurrentPositionTiltPercentage" "\x00" + "InstalledOpenLimitLift" "\x00" + "InstalledClosedLimitLift" "\x00" + "InstalledOpenLimitTilt" "\x00" + "InstalledClosedLimitTilt" "\x00" + "VelocityLift" "\x00" + "AccelerationTimeLift" "\x00" + "DecelerationTimeLift" "\x00" + "IntermediateSetpointsLift" "\x00" + "IntermediateSetpointsTilt" "\x00" + "LocalTemperature" "\x00" + "OutdoorTemperature" "\x00" + "PICoolingDemand" "\x00" + "PIHeatingDemand" "\x00" + "LocalTemperatureCalibration" "\x00" + "OccupiedCoolingSetpoint" "\x00" + "OccupiedHeatingSetpoint" "\x00" + "UnoccupiedCoolingSetpoint" "\x00" + "UnoccupiedHeatingSetpoint" "\x00" + "RemoteSensing" "\x00" + "ControlSequenceOfOperation" "\x00" + "SystemMode" "\x00" + "TRVMode" "\x00" + "ValvePosition" "\x00" + "EurotronicErrors" "\x00" + "CurrentTemperatureSetPoint" "\x00" + "ThSetpoint" "\x00" + "TempTarget" "\x00" + "Hue" "\x00" + "Sat" "\x00" + "RemainingTime" "\x00" + "X" "\x00" + "Y" "\x00" + "DriftCompensation" "\x00" + "CompensationText" "\x00" + "CT" "\x00" + "ColorMode" "\x00" + "NumberOfPrimaries" "\x00" + "Primary1X" "\x00" + "Primary1Y" "\x00" + "Primary1Intensity" "\x00" + "Primary2X" "\x00" + "Primary2Y" "\x00" + "Primary2Intensity" "\x00" + "Primary3X" "\x00" + "Primary3Y" "\x00" + "Primary3Intensity" "\x00" + "WhitePointX" "\x00" + "WhitePointY" "\x00" + "ColorPointRX" "\x00" + "ColorPointRY" "\x00" + "ColorPointRIntensity" "\x00" + "ColorPointGX" "\x00" + "ColorPointGY" "\x00" + "ColorPointGIntensity" "\x00" + "ColorPointBX" "\x00" + "ColorPointBY" "\x00" + "ColorPointBIntensity" "\x00" + "Illuminance" "\x00" + "IlluminanceMinMeasuredValue" "\x00" + "IlluminanceMaxMeasuredValue" "\x00" + "IlluminanceTolerance" "\x00" + "IlluminanceLightSensorType" "\x00" + "IlluminanceLevelStatus" "\x00" + "IlluminanceTargetLevel" "\x00" + "Temperature" "\x00" + "TemperatureMinMeasuredValue" "\x00" + "TemperatureMaxMeasuredValue" "\x00" + "TemperatureTolerance" "\x00" + "Pressure" "\x00" + "PressureMinMeasuredValue" "\x00" + "PressureMaxMeasuredValue" "\x00" + "PressureTolerance" "\x00" + "PressureScaledValue" "\x00" + "PressureMinScaledValue" "\x00" + "PressureMaxScaledValue" "\x00" + "PressureScaledTolerance" "\x00" + "PressureScale" "\x00" + "SeaPressure" "\x00" + "FlowRate" "\x00" + "FlowMinMeasuredValue" "\x00" + "FlowMaxMeasuredValue" "\x00" + "FlowTolerance" "\x00" + "Humidity" "\x00" + "HumidityMinMeasuredValue" "\x00" + "HumidityMaxMeasuredValue" "\x00" + "HumidityTolerance" "\x00" + "Occupancy" "\x00" + "OccupancySensorType" "\x00" + "ZoneState" "\x00" + "ZoneType" "\x00" + "ZoneStatus" "\x00" + "CurrentSummDelivered" "\x00" + "CompanyName" "\x00" + "MeterTypeID" "\x00" + "DataQualityID" "\x00" + "CustomerName" "\x00" + "Model" "\x00" + "PartNumber" "\x00" + "ProductRevision" "\x00" + "SoftwareRevision" "\x00" + "UtilityName" "\x00" + "POD" "\x00" + "AvailablePower" "\x00" + "PowerThreshold" "\x00" + "RMSVoltage" "\x00" + "RMSCurrent" "\x00" + "ActivePower" "\x00" + "NumberOfResets" "\x00" + "PersistentMemoryWrites" "\x00" + "LastMessageLQI" "\x00" + "LastMessageRSSI" "\x00" + "TuyaScheduleWorkdays" "\x00" + "TuyaScheduleHolidays" "\x00" + "TuyaChildLock" "\x00" + "TuyaWindowDetection" "\x00" + "TuyaValveDetection" "\x00" + "TuyaAutoLock" "\x00" + "TuyaTempTarget" "\x00" + "TuyaBattery" "\x00" + "TuyaMinTemp" "\x00" + "TuyaMaxTemp" "\x00" + "TuyaBoostTime" "\x00" + "TuyaComfortTemp" "\x00" + "TuyaEcoTemp" "\x00" + "TuyaValvePosition" "\x00" + "TuyaAwayTemp" "\x00" + "TuyaAwayDays" "\x00" + "TuyaPreset" "\x00" + "TuyaFanMode" "\x00" + "TuyaForceMode" "\x00" + "TuyaWeekSelect" "\x00" + "TerncyRotate" "\x00" + "TerncyDuration" "\x00" + "Identify" "\x00" + "xxxx" "\x00" + "IdentifyQuery" "\x00" + "AddGroup" "\x00" + "xxxx00" "\x00" + "ViewGroup" "\x00" + "GetGroup" "\x00" + "01xxxx" "\x00" + "GetAllGroups" "\x00" + "00" "\x00" + "RemoveGroup" "\x00" + "RemoveAllGroups" "\x00" + "ViewScene" "\x00" + "xxxxyy" "\x00" + "RemoveScene" "\x00" + "RemoveAllScenes" "\x00" + "RecallScene" "\x00" + "GetSceneMembership" "\x00" + "PowerOffEffect" "\x00" + "xxyy" "\x00" + "PowerOnRecall" "\x00" + "PowerOnTimer" "\x00" + "xxyyyyzzzz" "\x00" + "xx0A00" "\x00" + "DimmerUp" "\x00" + "00190200" "\x00" + "DimmerDown" "\x00" + "01190200" "\x00" + "DimmerStop" "\x00" + "ResetAlarm" "\x00" + "xxyyyy" "\x00" + "ResetAllAlarms" "\x00" + "xx000A00" "\x00" + "HueSat" "\x00" + "xxyy0A00" "\x00" + "Color" "\x00" + "xxxxyyyy0A00" "\x00" + "xxxx0A00" "\x00" + "ShutterOpen" "\x00" + "ShutterClose" "\x00" + "ShutterStop" "\x00" + "ShutterLift" "\x00" + "xx" "\x00" + "ShutterTilt" "\x00" + "Shutter" "\x00" + "DimmerMove" "\x00" + "xx0A" "\x00" + "DimmerStepUp" "\x00" + "00xx0A00" "\x00" + "DimmerStepDown" "\x00" + "01xx0A00" "\x00" + "DimmerStep" "\x00" + "xx190A00" "\x00" + "01" "\x00" + "HueMove" "\x00" + "xx19" "\x00" + "HueStepUp" "\x00" + "HueStepDown" "\x00" + "03xx0A00" "\x00" + "HueStep" "\x00" + "SatMove" "\x00" + "SatStep" "\x00" + "xx190A" "\x00" + "ColorMove" "\x00" + "xxxxyyyy" "\x00" + "ColorStep" "\x00" + "ColorTempMoveUp" "\x00" + "01xxxx000000000000" "\x00" + "ColorTempMoveDown" "\x00" + "03xxxx000000000000" "\x00" + "ColorTempMoveStop" "\x00" + "00xxxx000000000000" "\x00" + "ColorTempMove" "\x00" + "xxyyyy000000000000" "\x00" + "ColorTempStepUp" "\x00" + "01xxxx0A0000000000" "\x00" + "ColorTempStepDown" "\x00" + "03xxxx0A0000000000" "\x00" + "ColorTempStep" "\x00" + "xxyyyy0A0000000000" "\x00" + "ArrowClick" "\x00" + "ArrowHold" "\x00" + "ArrowRelease" "\x00" + "ZoneStatusChange" "\x00" + "xxxxyyzz" "\x00" + "xxyyzzzz" "\x00" + "AddScene" "\x00" + "xxyyyyzz" "\x00" + "StoreScene" "\x00" + ; + +enum Z_offsets { + Zo_ = 0, + Zo_ZCLVersion = 1, + Zo_AppVersion = 12, + Zo_StackVersion = 23, + Zo_HWVersion = 36, + Zo_Manufacturer = 46, + Zo_ModelId = 59, + Zo_DateCode = 67, + Zo_PowerSource = 76, + Zo_GenericDeviceClass = 88, + Zo_GenericDeviceType = 107, + Zo_ProductCode = 125, + Zo_ProductURL = 137, + Zo_SWBuildID = 148, + Zo_MainsVoltage = 158, + Zo_MainsFrequency = 171, + Zo_BatteryVoltage = 186, + Zo_BatteryPercentage = 201, + Zo_CurrentTemperature = 219, + Zo_MinTempExperienced = 238, + Zo_MaxTempExperienced = 257, + Zo_OverTempTotalDwell = 276, + Zo_IdentifyTime = 295, + Zo_GroupNameSupport = 308, + Zo_SceneCount = 325, + Zo_CurrentScene = 336, + Zo_CurrentGroup = 349, + Zo_SceneValid = 362, + Zo_Power = 373, + Zo_StartUpOnOff = 379, + Zo_SwitchType = 392, + Zo_Dimmer = 403, + Zo_DimmerOptions = 410, + Zo_DimmerRemainingTime = 424, + Zo_OnOffTransitionTime = 444, + Zo_AlarmCount = 464, + Zo_Time = 475, + Zo_TimeStatus = 480, + Zo_TimeZone = 491, + Zo_DstStart = 500, + Zo_DstEnd = 509, + Zo_DstShift = 516, + Zo_StandardTime = 525, + Zo_LocalTime = 538, + Zo_LastSetTime = 548, + Zo_ValidUntilTime = 560, + Zo_TimeEpoch = 575, + Zo_LocationType = 585, + Zo_LocationMethod = 598, + Zo_LocationAge = 613, + Zo_QualityMeasure = 625, + Zo_NumberOfDevices = 640, + Zo_AnalogInDescription = 656, + Zo_AnalogInMaxValue = 676, + Zo_AnalogInMinValue = 693, + Zo_AnalogInOutOfService = 710, + Zo_AqaraRotate = 731, + Zo_AnalogInReliability = 743, + Zo_AnalogInResolution = 763, + Zo_AnalogInStatusFlags = 782, + Zo_AnalogInEngineeringUnits = 802, + Zo_AnalogInApplicationType = 827, + Zo_Aqara_FF05 = 851, + Zo_AnalogOutDescription = 862, + Zo_AnalogOutMaxValue = 883, + Zo_AnalogOutMinValue = 901, + Zo_AnalogOutOutOfService = 919, + Zo_AnalogOutValue = 941, + Zo_AnalogOutReliability = 956, + Zo_AnalogOutRelinquishDefault = 977, + Zo_AnalogOutResolution = 1004, + Zo_AnalogOutStatusFlags = 1024, + Zo_AnalogOutEngineeringUnits = 1045, + Zo_AnalogOutApplicationType = 1071, + Zo_AnalogDescription = 1096, + Zo_AnalogOutOfService = 1114, + Zo_AnalogValue = 1133, + Zo_AnalogPriorityArray = 1145, + Zo_AnalogReliability = 1165, + Zo_AnalogRelinquishDefault = 1183, + Zo_AnalogStatusFlags = 1207, + Zo_AnalogEngineeringUnits = 1225, + Zo_AnalogApplicationType = 1248, + Zo_BinaryInActiveText = 1270, + Zo_BinaryInDescription = 1289, + Zo_BinaryInInactiveText = 1309, + Zo_BinaryInOutOfService = 1330, + Zo_BinaryInPolarity = 1351, + Zo_BinaryInValue = 1368, + Zo_BinaryInReliability = 1382, + Zo_BinaryInStatusFlags = 1402, + Zo_BinaryInApplicationType = 1422, + Zo_BinaryOutActiveText = 1446, + Zo_BinaryOutDescription = 1466, + Zo_BinaryOutInactiveText = 1487, + Zo_BinaryOutMinimumOffTime = 1509, + Zo_BinaryOutMinimumOnTime = 1533, + Zo_BinaryOutOutOfService = 1556, + Zo_BinaryOutPolarity = 1578, + Zo_BinaryOutValue = 1596, + Zo_BinaryOutReliability = 1611, + Zo_BinaryOutRelinquishDefault = 1632, + Zo_BinaryOutStatusFlags = 1659, + Zo_BinaryOutApplicationType = 1680, + Zo_BinaryActiveText = 1705, + Zo_BinaryDescription = 1722, + Zo_BinaryInactiveText = 1740, + Zo_BinaryMinimumOffTime = 1759, + Zo_BinaryMinimumOnTime = 1780, + Zo_BinaryOutOfService = 1800, + Zo_BinaryValue = 1819, + Zo_BinaryReliability = 1831, + Zo_BinaryRelinquishDefault = 1849, + Zo_BinaryStatusFlags = 1873, + Zo_BinaryApplicationType = 1891, + Zo_MultiInDescription = 1913, + Zo_MultiInNumberOfStates = 1932, + Zo_MultiInOutOfService = 1954, + Zo_MultiInValue = 1974, + Zo_MultiInReliability = 1987, + Zo_MultiInStatusFlags = 2006, + Zo_MultiInApplicationType = 2025, + Zo_MultiOutDescription = 2048, + Zo_MultiOutNumberOfStates = 2068, + Zo_MultiOutOutOfService = 2091, + Zo_MultiOutValue = 2112, + Zo_MultiOutReliability = 2126, + Zo_MultiOutRelinquishDefault = 2146, + Zo_MultiOutStatusFlags = 2172, + Zo_MultiOutApplicationType = 2192, + Zo_MultiDescription = 2216, + Zo_MultiNumberOfStates = 2233, + Zo_MultiOutOfService = 2253, + Zo_MultiValue = 2271, + Zo_MultiReliability = 2282, + Zo_MultiRelinquishDefault = 2299, + Zo_MultiStatusFlags = 2322, + Zo_MultiApplicationType = 2339, + Zo_TotalProfileNum = 2360, + Zo_MultipleScheduling = 2376, + Zo_EnergyFormatting = 2395, + Zo_EnergyRemote = 2412, + Zo_ScheduleMode = 2425, + Zo_CheckinInterval = 2438, + Zo_LongPollInterval = 2454, + Zo_ShortPollInterval = 2471, + Zo_FastPollTimeout = 2489, + Zo_CheckinIntervalMin = 2505, + Zo_LongPollIntervalMin = 2524, + Zo_FastPollTimeoutMax = 2544, + Zo_PhysicalClosedLimit = 2563, + Zo_MotorStepSize = 2583, + Zo_Status = 2597, + Zo_ClosedLimit = 2604, + Zo_Mode = 2616, + Zo_LockState = 2621, + Zo_LockType = 2631, + Zo_ActuatorEnabled = 2640, + Zo_DoorState = 2656, + Zo_DoorOpenEvents = 2666, + Zo_DoorClosedEvents = 2681, + Zo_OpenPeriod = 2698, + Zo_AqaraVibrationMode = 2709, + Zo_AqaraVibrationsOrAngle = 2728, + Zo_AqaraVibration505 = 2751, + Zo_AqaraAccelerometer = 2769, + Zo_WindowCoveringType = 2788, + Zo_PhysicalClosedLimitLift = 2807, + Zo_PhysicalClosedLimitTilt = 2831, + Zo_CurrentPositionLift = 2855, + Zo_CurrentPositionTilt = 2875, + Zo_NumberofActuationsLift = 2895, + Zo_NumberofActuationsTilt = 2918, + Zo_ConfigStatus = 2941, + Zo_CurrentPositionLiftPercentage = 2954, + Zo_CurrentPositionTiltPercentage = 2984, + Zo_InstalledOpenLimitLift = 3014, + Zo_InstalledClosedLimitLift = 3037, + Zo_InstalledOpenLimitTilt = 3062, + Zo_InstalledClosedLimitTilt = 3085, + Zo_VelocityLift = 3110, + Zo_AccelerationTimeLift = 3123, + Zo_DecelerationTimeLift = 3144, + Zo_IntermediateSetpointsLift = 3165, + Zo_IntermediateSetpointsTilt = 3191, + Zo_LocalTemperature = 3217, + Zo_OutdoorTemperature = 3234, + Zo_PICoolingDemand = 3253, + Zo_PIHeatingDemand = 3269, + Zo_LocalTemperatureCalibration = 3285, + Zo_OccupiedCoolingSetpoint = 3313, + Zo_OccupiedHeatingSetpoint = 3337, + Zo_UnoccupiedCoolingSetpoint = 3361, + Zo_UnoccupiedHeatingSetpoint = 3387, + Zo_RemoteSensing = 3413, + Zo_ControlSequenceOfOperation = 3427, + Zo_SystemMode = 3454, + Zo_TRVMode = 3465, + Zo_ValvePosition = 3473, + Zo_EurotronicErrors = 3487, + Zo_CurrentTemperatureSetPoint = 3504, + Zo_ThSetpoint = 3531, + Zo_TempTarget = 3542, + Zo_Hue = 3553, + Zo_Sat = 3557, + Zo_RemainingTime = 3561, + Zo_X = 3575, + Zo_Y = 3577, + Zo_DriftCompensation = 3579, + Zo_CompensationText = 3597, + Zo_CT = 3614, + Zo_ColorMode = 3617, + Zo_NumberOfPrimaries = 3627, + Zo_Primary1X = 3645, + Zo_Primary1Y = 3655, + Zo_Primary1Intensity = 3665, + Zo_Primary2X = 3683, + Zo_Primary2Y = 3693, + Zo_Primary2Intensity = 3703, + Zo_Primary3X = 3721, + Zo_Primary3Y = 3731, + Zo_Primary3Intensity = 3741, + Zo_WhitePointX = 3759, + Zo_WhitePointY = 3771, + Zo_ColorPointRX = 3783, + Zo_ColorPointRY = 3796, + Zo_ColorPointRIntensity = 3809, + Zo_ColorPointGX = 3830, + Zo_ColorPointGY = 3843, + Zo_ColorPointGIntensity = 3856, + Zo_ColorPointBX = 3877, + Zo_ColorPointBY = 3890, + Zo_ColorPointBIntensity = 3903, + Zo_Illuminance = 3924, + Zo_IlluminanceMinMeasuredValue = 3936, + Zo_IlluminanceMaxMeasuredValue = 3964, + Zo_IlluminanceTolerance = 3992, + Zo_IlluminanceLightSensorType = 4013, + Zo_IlluminanceLevelStatus = 4040, + Zo_IlluminanceTargetLevel = 4063, + Zo_Temperature = 4086, + Zo_TemperatureMinMeasuredValue = 4098, + Zo_TemperatureMaxMeasuredValue = 4126, + Zo_TemperatureTolerance = 4154, + Zo_Pressure = 4175, + Zo_PressureMinMeasuredValue = 4184, + Zo_PressureMaxMeasuredValue = 4209, + Zo_PressureTolerance = 4234, + Zo_PressureScaledValue = 4252, + Zo_PressureMinScaledValue = 4272, + Zo_PressureMaxScaledValue = 4295, + Zo_PressureScaledTolerance = 4318, + Zo_PressureScale = 4342, + Zo_SeaPressure = 4356, + Zo_FlowRate = 4368, + Zo_FlowMinMeasuredValue = 4377, + Zo_FlowMaxMeasuredValue = 4398, + Zo_FlowTolerance = 4419, + Zo_Humidity = 4433, + Zo_HumidityMinMeasuredValue = 4442, + Zo_HumidityMaxMeasuredValue = 4467, + Zo_HumidityTolerance = 4492, + Zo_Occupancy = 4510, + Zo_OccupancySensorType = 4520, + Zo_ZoneState = 4540, + Zo_ZoneType = 4550, + Zo_ZoneStatus = 4559, + Zo_CurrentSummDelivered = 4570, + Zo_CompanyName = 4591, + Zo_MeterTypeID = 4603, + Zo_DataQualityID = 4615, + Zo_CustomerName = 4629, + Zo_Model = 4642, + Zo_PartNumber = 4648, + Zo_ProductRevision = 4659, + Zo_SoftwareRevision = 4675, + Zo_UtilityName = 4692, + Zo_POD = 4704, + Zo_AvailablePower = 4708, + Zo_PowerThreshold = 4723, + Zo_RMSVoltage = 4738, + Zo_RMSCurrent = 4749, + Zo_ActivePower = 4760, + Zo_NumberOfResets = 4772, + Zo_PersistentMemoryWrites = 4787, + Zo_LastMessageLQI = 4810, + Zo_LastMessageRSSI = 4825, + Zo_TuyaScheduleWorkdays = 4841, + Zo_TuyaScheduleHolidays = 4862, + Zo_TuyaChildLock = 4883, + Zo_TuyaWindowDetection = 4897, + Zo_TuyaValveDetection = 4917, + Zo_TuyaAutoLock = 4936, + Zo_TuyaTempTarget = 4949, + Zo_TuyaBattery = 4964, + Zo_TuyaMinTemp = 4976, + Zo_TuyaMaxTemp = 4988, + Zo_TuyaBoostTime = 5000, + Zo_TuyaComfortTemp = 5014, + Zo_TuyaEcoTemp = 5030, + Zo_TuyaValvePosition = 5042, + Zo_TuyaAwayTemp = 5060, + Zo_TuyaAwayDays = 5073, + Zo_TuyaPreset = 5086, + Zo_TuyaFanMode = 5097, + Zo_TuyaForceMode = 5109, + Zo_TuyaWeekSelect = 5123, + Zo_TerncyRotate = 5138, + Zo_TerncyDuration = 5151, + Zo_Identify = 5166, + Zo_xxxx = 5175, + Zo_IdentifyQuery = 5180, + Zo_AddGroup = 5194, + Zo_xxxx00 = 5203, + Zo_ViewGroup = 5210, + Zo_GetGroup = 5220, + Zo_01xxxx = 5229, + Zo_GetAllGroups = 5236, + Zo_00 = 5249, + Zo_RemoveGroup = 5252, + Zo_RemoveAllGroups = 5264, + Zo_ViewScene = 5280, + Zo_xxxxyy = 5290, + Zo_RemoveScene = 5297, + Zo_RemoveAllScenes = 5309, + Zo_RecallScene = 5325, + Zo_GetSceneMembership = 5337, + Zo_PowerOffEffect = 5356, + Zo_xxyy = 5371, + Zo_PowerOnRecall = 5376, + Zo_PowerOnTimer = 5390, + Zo_xxyyyyzzzz = 5403, + Zo_xx0A00 = 5414, + Zo_DimmerUp = 5421, + Zo_00190200 = 5430, + Zo_DimmerDown = 5439, + Zo_01190200 = 5450, + Zo_DimmerStop = 5459, + Zo_ResetAlarm = 5470, + Zo_xxyyyy = 5481, + Zo_ResetAllAlarms = 5488, + Zo_xx000A00 = 5503, + Zo_HueSat = 5512, + Zo_xxyy0A00 = 5519, + Zo_Color = 5528, + Zo_xxxxyyyy0A00 = 5534, + Zo_xxxx0A00 = 5547, + Zo_ShutterOpen = 5556, + Zo_ShutterClose = 5568, + Zo_ShutterStop = 5581, + Zo_ShutterLift = 5593, + Zo_xx = 5605, + Zo_ShutterTilt = 5608, + Zo_Shutter = 5620, + Zo_DimmerMove = 5628, + Zo_xx0A = 5639, + Zo_DimmerStepUp = 5644, + Zo_00xx0A00 = 5657, + Zo_DimmerStepDown = 5666, + Zo_01xx0A00 = 5681, + Zo_DimmerStep = 5690, + Zo_xx190A00 = 5701, + Zo_01 = 5710, + Zo_HueMove = 5713, + Zo_xx19 = 5721, + Zo_HueStepUp = 5726, + Zo_HueStepDown = 5736, + Zo_03xx0A00 = 5748, + Zo_HueStep = 5757, + Zo_SatMove = 5765, + Zo_SatStep = 5773, + Zo_xx190A = 5781, + Zo_ColorMove = 5788, + Zo_xxxxyyyy = 5798, + Zo_ColorStep = 5807, + Zo_ColorTempMoveUp = 5817, + Zo_01xxxx000000000000 = 5833, + Zo_ColorTempMoveDown = 5852, + Zo_03xxxx000000000000 = 5870, + Zo_ColorTempMoveStop = 5889, + Zo_00xxxx000000000000 = 5907, + Zo_ColorTempMove = 5926, + Zo_xxyyyy000000000000 = 5940, + Zo_ColorTempStepUp = 5959, + Zo_01xxxx0A0000000000 = 5975, + Zo_ColorTempStepDown = 5994, + Zo_03xxxx0A0000000000 = 6012, + Zo_ColorTempStep = 6031, + Zo_xxyyyy0A0000000000 = 6045, + Zo_ArrowClick = 6064, + Zo_ArrowHold = 6075, + Zo_ArrowRelease = 6085, + Zo_ZoneStatusChange = 6098, + Zo_xxxxyyzz = 6115, + Zo_xxyyzzzz = 6124, + Zo_AddScene = 6133, + Zo_xxyyyyzz = 6142, + Zo_StoreScene = 6151, +}; + + + + + + + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +#ifdef USE_ZIGBEE + + + + + + +enum Z_DataTypes { + Znodata = 0x00, + Zdata8 = 0x08, Zdata16, Zdata24, Zdata32, Zdata40, Zdata48, Zdata56, Zdata64, + Zbool = 0x10, + Zmap8 = 0x18, Zmap16, Zmap24, Zmap32, Zmap40, Zmap48, Zmap56, Zmap64, + Zuint8 = 0x20, Zuint16, Zuint24, Zuint32, Zuint40, Zuint48, Zuint56, Zuint64, + Zint8 = 0x28, Zint16, Zint24, Zint32, Zint40, Zint48, Zint56, Zint64, + Zenum8 = 0x30, Zenum16 = 0x31, + Zsemi = 0x38, Zsingle = 0x39, Zdouble = 0x3A, + Zoctstr = 0x41, Zstring = 0x42, Zoctstr16 = 0x43, Zstring16 = 0x44, + Arrray = 0x48, + Zstruct = 0x4C, + Zset = 0x50, Zbag = 0x51, + ZToD = 0xE0, Zdate = 0xE1, ZUTC = 0xE2, + ZclusterId = 0xE8, ZattribId = 0xE9, ZbacOID = 0xEA, + ZEUI64 = 0xF0, Zkey128 = 0xF1, + Zunk = 0xFF, + + Ztuya1 = 0x80, + Ztuya2 = 0x81, + Ztuya4 = 0x82, +}; + + + + + + +uint8_t Z_getDatatypeLen(uint8_t t) { + if ( ((t >= 0x08) && (t <= 0x0F)) || + ((t >= 0x18) && (t <= 0x2F)) ) { + return (t & 0x07) + 1; + } + switch (t) { + case Zbool: + case Zenum8: + return 1; + case Zenum16: + case Zsemi: + case ZclusterId: + case ZattribId: + case Ztuya1: + return 2; + case Ztuya2: + return 3; + case Zsingle: + case ZToD: + case Zdate: + case ZUTC: + case ZbacOID: + return 4; + case Ztuya4: + return 5; + case Zdouble: + case ZEUI64: + return 8; + case Zkey128: + return 16; + case Znodata: + default: + return 0; + } +} + + +bool Z_isDiscreteDataType(uint8_t t) { + if ( ((t >= 0x20) && (t <= 0x2F)) || + ((t >= 0x38) && (t <= 0x3A)) || + ((t >= 0xE0) && (t <= 0xE2)) ) { + return false; + } else { + return true; + } +} + +typedef struct Z_AttributeConverter { + uint8_t type; + uint8_t cluster_short; + uint16_t attribute; + uint16_t name_offset; + uint8_t multiplier_idx; + + + uint8_t mapping; + +} Z_AttributeConverter; + + +#define Z_OFFSET(c,a) (offsetof(class c, a) - sizeof(Z_Data)) +#define Z_CLASS(c) c +#define Z_MAPPING(c,a) (((((uint8_t)Z_CLASS(c)::type) & 0x0F) << 4) | Z_OFFSET(c,a)) + + + +const uint8_t Z_EXPORT_DATA = 0x80; + + + + +enum Cx_cluster_short { + Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007, + Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F, + Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100, + Cx0101, Cx0102, Cx0201, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403, + Cx0404, Cx0405, Cx0406, Cx0500, Cx0702, Cx0B01, Cx0B04, Cx0B05, + CxEF00, CxFCCC, +}; + +const uint16_t Cx_cluster[] PROGMEM = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100, + 0x0101, 0x0102, 0x0201, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403, + 0x0404, 0x0405, 0x0406, 0x0500, 0x0702, 0x0B01, 0x0B04, 0x0B05, + 0xEF00, 0xFCCC, +}; + +uint16_t CxToCluster(uint8_t cx) { + if (cx < ARRAY_SIZE(Cx_cluster)) { + return pgm_read_word(&Cx_cluster[cx]); + } + return 0xFFFF; +} + +uint8_t ClusterToCx(uint16_t cluster) { + for (uint32_t i=0; iname_offset)) { continue; } + if (0 == strcasecmp_P(command, Z_strings + pgm_read_word(&converter->name_offset))) { + if (cluster) { *cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); } + if (attribute) { *attribute = pgm_read_word(&converter->attribute); } + if (multiplier) { *multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); } + if (zigbee_type) { *zigbee_type = pgm_read_byte(&converter->type); } + uint8_t conv_mapping = pgm_read_byte(&converter->mapping); + if (data_type) { *data_type = (Z_Data_Type) ((conv_mapping & 0xF0)>>4); } + if (map_offset) { *map_offset = (conv_mapping & 0x0F); } + return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset)); + } + } + return nullptr; +} + + + + +const __FlashStringHelper* zigbeeFindAttributeById(uint16_t cluster, uint16_t attr_id, + uint8_t *attr_type, int8_t *multiplier) { + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint16_t conv_attr_id = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && (conv_attr_id == attr_id)) { + if (multiplier) { *multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); } + if (attr_type) { *attr_type = pgm_read_byte(&converter->type); } + return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset)); + } + } + return nullptr; +} + +class ZCLFrame { +public: + + ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, + const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber): + _manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), + _payload(buf_len ? buf_len : 250), + _cluster_id(clusterid), _groupaddr(groupaddr), + _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), + _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber) + { + _frame_control.d8 = frame_control; + _payload.addBuffer(buf, buf_len); + }; + + + void log(void) { + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" + "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," + "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," + "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," + "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), + _groupaddr, _cluster_id, _srcaddr, + _srcendpoint, _dstendpoint, _wasbroadcast, + _linkquality, _securityuse, _seqnumber, + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR)); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + } + + static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber) { + uint32_t i = offset; + ZCLHeaderFrameControl_t frame_control; + uint16_t manuf_code = 0; + uint8_t transact_seq; + uint8_t cmd_id; + + frame_control.d8 = buf.get8(i++); + if (frame_control.b.manuf_specific) { + manuf_code = buf.get16(i); + i += 2; + } + transact_seq = buf.get8(i++); + cmd_id = buf.get8(i++); + ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, + (const char *)(buf.buf() + i), len + offset - i, + clusterid, groupid, + srcaddr, srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber); + return zcl_frame; + } + + bool isClusterSpecificCommand(void) { + return _frame_control.b.frame_type & 1; + } + + void parseReportAttributes(Z_attribute_list& attr_list); + void generateSyntheticAttributes(Z_attribute_list& attr_list); + void computeSyntheticAttributes(Z_attribute_list& attr_list); + void generateCallBacks(Z_attribute_list& attr_list); + void parseReadAttributes(Z_attribute_list& attr_list); + void parseReadAttributesResponse(Z_attribute_list& attr_list); + void parseReadConfigAttributes(Z_attribute_list& attr_list); + void parseConfigAttributes(Z_attribute_list& attr_list); + void parseResponse(void); + void parseResponseOld(void); + void parseClusterSpecificCommand(Z_attribute_list& attr_list); + void postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_list); + + + void syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribute &attr); + void syntheticAqaraSensor2(Z_attribute_list &attr_list, class Z_attribute &attr); + void syntheticAqaraCubeOrButton(Z_attribute_list &attr_list, class Z_attribute &attr); + void syntheticAqaraVibration(Z_attribute_list &attr_list, class Z_attribute &attr); + + + void autoResponder(const uint16_t *attr_list_ids, size_t attr_len); + + inline void setGroupId(uint16_t groupid) { + _groupaddr = groupid; + } + + inline void setClusterId(uint16_t clusterid) { + _cluster_id = clusterid; + } + + inline uint16_t getSrcAddr(void) const { return _srcaddr; } + inline uint16_t getGroupAddr(void) const { return _groupaddr; } + inline uint16_t getClusterId(void) const { return _cluster_id; } + inline uint8_t getLinkQuality(void) const { return _linkquality; } + inline uint8_t getCmdId(void) const { return _cmd_id; } + inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; } + + const SBuffer &getPayload(void) const { + return _payload; + } + + uint16_t getManufCode(void) const { + return _manuf_code; + } + + +private: + ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; + uint16_t _manuf_code = 0; + uint8_t _transact_seq = 0; + uint8_t _cmd_id = 0; + SBuffer _payload; + uint16_t _cluster_id = 0; + uint16_t _groupaddr = 0; + + uint16_t _srcaddr; + uint8_t _srcendpoint; + uint8_t _dstendpoint; + uint8_t _wasbroadcast; + uint8_t _linkquality; + uint8_t _securityuse; + uint8_t _seqnumber; +}; + + + + + + +uint8_t toPercentageCR2032(uint32_t voltage) { + uint32_t percentage; + if (voltage < 2100) { + percentage = 0; + } else if (voltage < 2440) { + percentage = 6 - ((2440 - voltage) * 6) / 340; + } else if (voltage < 2740) { + percentage = 18 - ((2740 - voltage) * 12) / 300; + } else if (voltage < 2900) { + percentage = 42 - ((2900 - voltage) * 24) / 160; + } else if (voltage < 3000) { + percentage = 100 - ((3000 - voltage) * 58) / 100; + } else if (voltage >= 3000) { + percentage = 100; + } + return percentage; +} + + + + + + +int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype) { + uint32_t len = Z_getDatatypeLen(attrtype); + uint32_t u32 = val_d; + int32_t i32 = val_d; + float f32 = val_d; + + switch (attrtype) { + + case Zbool: + case Zuint8: + case Zenum8: + case Zdata8: + case Zmap8: + buf.add8(u32); + break; + case Ztuya1: + buf.add8(1); + buf.add8(u32); + break; + + case Zuint16: + case Zenum16: + case Zdata16: + case Zmap16: + buf.add16(u32); + break; + case Ztuya2: + buf.add8(2); + buf.add16BigEndian(u32); + + case Zuint32: + case Zdata32: + case Zmap32: + case ZUTC: + buf.add32(u32); + break; + + + case Zint8: + buf.add8(i32); + break; + case Zint16: + buf.add16(i32); + break; + case Zint32: + buf.add32(i32); + break; + case Ztuya4: + buf.add8(4); + buf.add32BigEndian(i32); + break; + + case Zsingle: + uint32_t *f_ptr; + buf.add32( *((uint32_t*)&f32) ); + break; + + case Zstring: + case Zstring16: + { + if (nullptr == val_str) { return -2; } + size_t val_len = strlen(val_str); + if (val_len > 32) { val_len = 32; } + len = val_len + 1; + buf.add8(val_len); + if (Zstring16 == attrtype) { + buf.add8(0); + len++; + } + for (uint32_t i = 0; i < val_len; i++) { + buf.add8(val_str[i]); + } + } + break; + + default: + return -1; + } + return len; +} +# 924 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf, + uint32_t offset, int32_t attrtype = -1) { + + uint32_t i = offset; + if (attrtype < 0) { + attrtype = buf.get8(i++); + } + + + attr.setNone(); + + uint32_t len = Z_getDatatypeLen(attrtype); + + + switch (attrtype) { + + + + case Zbool: + case Zuint8: + case Zenum8: + { + uint8_t uint8_val = buf.get8(i); + + if (0xFF != uint8_val) { + attr.setUInt(uint8_val); + } + } + break; + case Zuint16: + case Zenum16: + { + uint16_t uint16_val = buf.get16(i); + + if (0xFFFF != uint16_val) { + attr.setUInt(uint16_val); + } + } + break; + case Zuint32: + case ZUTC: + { + uint32_t uint32_val = buf.get32(i); + + if (0xFFFFFFFF != uint32_val) { + attr.setUInt(uint32_val); + } + } + break; + case Ztuya1: + attr.setUInt(buf.get8(i+1)); + break; + case Ztuya2: + attr.setUInt(buf.get16BigEndian(i+1)); + break; + case Ztuya4: + attr.setInt(buf.get32IBigEndian(i+1)); + break; + + + case Zuint40: + case Zuint48: + case Zuint56: + case Zuint64: + case Zint40: + case Zint48: + case Zint56: + case Zint64: + { + + + char hex[2*len+3]; + snprintf_P(hex, sizeof(hex), PSTR("0x")); + for (uint32_t j=0; j buf.len()) { + len = buf.len() - i; + } + + + if ((Zoctstr == attrtype) || (Zoctstr16 == attrtype)) { parse_as_string = false; } + + if (parse_as_string) { + char str[len+1]; + strncpy(str, buf.charptr(i), len); + str[len] = 0x00; + attr.setStr(str); + } else { + attr.setBuf(buf, i, len); + } + + + + } + + break; + + case Zstruct: + { + uint16_t struct_size = buf.get16(i); + len = 2; + if (0xFFFF != struct_size) { + if (struct_size > 16) { struct_size = 16; } + + for (uint32_t j = 0; j < struct_size; j++) { + uint8_t attr_type = buf.get8(i+len); + len += Z_getDatatypeLen(attr_type) + 1; + } + attr.setBuf(buf, i, len); + } + } + break; + + case Zdata8: + case Zmap8: + { + uint8_t uint8_val = buf.get8(i); + + attr.setUInt(uint8_val); + } + break; + case Zdata16: + case Zmap16: + { + uint16_t uint16_val = buf.get16(i); + + attr.setUInt(uint16_val); + } + break; + case Zdata32: + case Zmap32: + { + uint32_t uint32_val = buf.get32(i); + + attr.setUInt(uint32_val); + } + break; + + case Zsingle: + { + uint32_t uint32_val = buf.get32(i); + float * float_val = (float*) &uint32_val; + + attr.setFloat(*float_val); + } + break; + + + case ZToD: + case Zdate: + case ZclusterId: + case ZattribId: + case ZbacOID: + case ZEUI64: + case Zkey128: + case Zsemi: + break; + + + case Zdata24: + case Zdata40: + case Zdata48: + case Zdata56: + case Zdata64: + break; + + case Zmap24: + case Zmap40: + case Zmap48: + case Zmap56: + case Zmap64: + break; + case Zdouble: + { + uint64_t uint64_val = buf.get64(i); + double * double_val = (double*) &uint64_val; + + attr.setFloat((float) *double_val); + } + break; + } + i += len; + + return i - offset; +} + + +void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) { + uint32_t i = 0; + uint32_t len = _payload.len(); + + while (len >= i + 3) { + uint16_t attrid = _payload.get16(i); + i += 2; + + + if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { + if (0x42 == _payload.get8(i)) { + _payload.set8(i, 0x41); + } + } + + + Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); + + i += parseSingleAttribute(attr, _payload, i); + } + + + + if (0 == _frame_control.b.disable_def_resp) { + + SBuffer buf(2); + buf.add8(_cmd_id); + buf.add8(0x00); + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + _srcaddr, + 0x0000, + _cluster_id, + _srcendpoint, + ZCL_DEFAULT_RESPONSE, + _manuf_code, + false , + false , + true , + _transact_seq, + buf.getBuffer(), buf.len() + })); + } +} + + + + +void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) { + + for (auto &attr : attr_list) { + if (attr.key_is_str) { continue; } + uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; + + switch (ccccaaaa) { + case 0x0000FF01: + syntheticAqaraSensor(attr_list, attr); + break; + case 0x0000FF02: + syntheticAqaraSensor2(attr_list, attr); + break; + case 0x00120055: + syntheticAqaraCubeOrButton(attr_list, attr); + break; + case 0x01010055: + case 0x01010508: + syntheticAqaraVibration(attr_list, attr); + break; + } + } +} + + + + + +void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { + + for (auto &attr : attr_list) { + if (attr.key_is_str) { continue; } + uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; + + switch (ccccaaaa) { + case 0x00010020: + if (attr_list.countAttribute(0x0001,0x0021) == 0) { + uint32_t mv = attr.getUInt()*100; + attr_list.addAttribute(0x0001, 0x0021).setUInt(toPercentageCR2032(mv) * 2); + } + break; + case 0x02010008: + { + const char * manufacturer_c = zigbee_devices.getManufacturerId(_srcaddr); + String manufacturerId((char*) manufacturer_c); + if (manufacturerId.equals(F("Eurotronic"))) { + + uint8_t valve = attr.getUInt(); + if (attr.isNone()) { valve = 255; } + uint8_t valve_100 = changeUIntScale(valve, 0, 255, 0, 100); + attr.setUInt(valve_100); + } + } + break; + case 0x04030000: + { + int16_t pressure = attr.getInt(); + int16_t pressure_sealevel = (pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0f), 5.255f)) - 21.6f; + attr_list.addAttribute(0x0403, 0xFFF0).setInt(pressure_sealevel); + + } + break; + } + } +} + + + +void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) { + static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; + + for (auto &attr : attr_list) { + if (attr.key_is_str) { continue; } + uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; + + switch (ccccaaaa) { + case 0x04060000: + uint32_t occupancy = attr.getUInt(); + if (occupancy) { + zigbee_devices.setTimer(_srcaddr, 0 , OCCUPANCY_TIMEOUT, _cluster_id, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); + } else { + zigbee_devices.resetTimersForDevice(_srcaddr, 0 , Z_CAT_VIRTUAL_OCCUPANCY); + } + break; + } + } +} + + + + + +void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint = 0) { + uint32_t wait_ms = 0xFFFF; + + switch (cluster) { + case 0x0006: + wait_ms = 200; + break; + case 0x0008: + wait_ms = 1050; + break; + case 0x0102: + wait_ms = 10000; + break; + case 0x0300: + wait_ms = 1050; + break; + default: + break; + } + if (0xFFFF != wait_ms) { + if ((BAD_SHORTADDR != shortaddr) && (0 == endpoint)) { + endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + } + if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { + zigbee_devices.queueTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_READ_CLUSTER, 0 , &Z_ReadAttrCallback); + if (BAD_SHORTADDR != shortaddr) { + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); + } + + } + } +} + + +void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { + uint32_t i = 0; + uint32_t len = _payload.len(); + + uint16_t read_attr_ids[len/2]; + + attr_list.addAttribute(F(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id); + + Z_json_array attr_numbers; + Z_attribute_list attr_names; + while (len >= 2 + i) { + uint16_t attrid = _payload.get16(i); + attr_numbers.add(attrid); + read_attr_ids[i/2] = attrid; + + + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { + attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true); + break; + } + } + i += 2; + } + attr_list.addAttribute(F("Read")).setStrRaw(attr_numbers.toString().c_str()); + attr_list.addAttribute(F("ReadNames")).setStrRaw(attr_names.toString(true).c_str()); + + + autoResponder(read_attr_ids, len/2); +} + + +void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { + uint32_t len = _payload.len(); + + Z_attribute_list attr_config_list; + for (uint32_t i=0; len >= i+4; i+=4) { + uint8_t status = _payload.get8(i); + uint16_t attr_id = _payload.get8(i+2); + + Z_attribute_list attr_config_response; + attr_config_response.addAttribute(F("Status")).setUInt(status); + attr_config_response.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); + + const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr); + if (attr_name) { + attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str()); + } else { + attr_config_list.addAttribute(_cluster_id, attr_id).setStrRaw(attr_config_response.toString(true).c_str()); + } + } + + Z_attribute &attr_1 = attr_list.addAttribute(F("ConfigResponse")); + attr_1.setStrRaw(attr_config_list.toString(true).c_str()); +} + + +void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { + uint32_t i = 0; + uint32_t len = _payload.len(); + + Z_attribute &attr_root = attr_list.addAttribute(F("ReadConfig")); + Z_attribute_list attr_1; + + while (len >= i + 4) { + uint8_t status = _payload.get8(i); + uint8_t direction = _payload.get8(i+1); + uint16_t attrid = _payload.get16(i+2); + + Z_attribute_list attr_2; + if (direction) { + attr_2.addAttribute(F("DirectionReceived")).setBool(true); + } + + + int8_t multiplier = 1; + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { + const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset); + attr_2.addAttribute(attr_name, true).setBool(true); + multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + break; + } + } + i += 4; + if (0 != status) { + attr_2.addAttribute(F("Status")).setUInt(status); + attr_2.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); + } else { + + if (direction) { + + uint16_t attr_timeout = _payload.get16(i); + i += 2; + attr_2.addAttribute(F("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout); + } else { + + uint8_t attr_type = _payload.get8(i); + bool attr_discrete = Z_isDiscreteDataType(attr_type); + uint16_t attr_min_interval = _payload.get16(i+1); + uint16_t attr_max_interval = _payload.get16(i+3); + i += 5; + attr_2.addAttribute(F("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval); + attr_2.addAttribute(F("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval); + if (!attr_discrete) { + + Z_attribute &attr_change = attr_2.addAttribute(F("ReportableChange")); + i += parseSingleAttribute(attr_change, _payload, i, attr_type); + if ((1 != multiplier) && (0 != multiplier)) { + float fval = attr_change.getFloat(); + if (multiplier > 0) { fval = fval * multiplier; } + else { fval = fval / (-multiplier); } + attr_change.setFloat(fval); + } + } + } + } + attr_1.addAttribute(_cluster_id, attrid).setStrRaw(attr_2.toString(true).c_str()); + } + attr_root.setStrRaw(attr_1.toString(true).c_str()); +} + + +void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) { + uint32_t i = 0; + uint32_t len = _payload.len(); + + while (len >= i + 4) { + uint16_t attrid = _payload.get16(i); + i += 2; + uint8_t status = _payload.get8(i++); + + if (0 == status) { + Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); + i += parseSingleAttribute(attr, _payload, i); + } + } +} + + +void ZCLFrame::parseResponse(void) { + if (_payload.len() < 2) { return; } + uint8_t cmd = _payload.get8(0); + uint8_t status = _payload.get8(1); + + Z_attribute_list attr_list; + + + char s[12]; + snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); + attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(s); + + const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); + if (friendlyName) { + attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(friendlyName); + } + + snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd); + attr_list.addAttribute(F(D_JSON_ZIGBEE_CMD)).setStr(s); + + attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS)).setUInt(status); + + attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS_MSG)).setStr(getZigbeeStatusMessage(status).c_str()); + + attr_list.addAttribute(F(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint); + + if (_groupaddr) { + attr_list.group_id = _groupaddr; + } + + attr_list.lqi = _linkquality; + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), attr_list.toString(true).c_str()); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); +} + + +void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { + convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); +#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES + if (!_frame_control.b.direction) { + if (_wasbroadcast) { + sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); + } + } +#endif +} + + + +void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribute &attr) { + const SBuffer * buf = attr.getRaw(); + if (buf) { + const SBuffer & buf2 = *buf; + uint32_t i = 0; + uint32_t len = buf2.len(); + + const char * modelId_c = zigbee_devices.getModelId(_srcaddr); + String modelId((char*) modelId_c); + + while (len >= 2 + i) { + uint8_t attrid = buf2.get8(i++); + + Z_attribute attr; + i += parseSingleAttribute(attr, buf2, i); + int32_t ival32 = attr.getInt(); + uint32_t uval32 = attr.getUInt(); + bool translated = false; + if (0x01 == attrid) { + float batteryvoltage = attr.getFloat() / 100; + attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage); + uint8_t batterypercentage = toPercentageCR2032(uval32); + attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2); + } else if ((nullptr != modelId) && (0 == getManufCode())) { + translated = true; + if (modelId.startsWith(F("lumi.sensor_ht")) || + modelId.equals(F("lumi.sens")) || + modelId.startsWith(F("lumi.weather"))) { + + + if (0x64 == attrid) { + attr_list.addAttribute(0x0402, 0x0000).setInt(ival32); + } else if (0x65 == attrid) { + attr_list.addAttribute(0x0405, 0x0000).setUInt(uval32); + } else if (0x66 == attrid) { + attr_list.addAttribute(0x0403, 0x0000).setUInt((ival32 + 50) / 100); + } + } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { + if (0x64 == attrid) { + attr_list.addAttribute(F("SmokeDensity")).copyVal(attr); + } + } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { + if (0x64 == attrid) { + attr_list.addAttribute(F("GasDensity")).copyVal(attr); + } + } else { + translated = false; + } + + } + if (!translated) { + if (attrid >= 100) { + char attr_name[12]; + snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid); + attr_list.addAttribute(attr_name).copyVal(attr); + } + } + } + } +} + +void ZCLFrame::syntheticAqaraSensor2(class Z_attribute_list &attr_list, class Z_attribute &attr) { + const SBuffer * buf = attr.getRaw(); + if (buf) { + const SBuffer & buf2 = *buf; + uint32_t len = buf2.len(); + + + uint16_t struct_size = buf2.get16(0); + size_t struct_len = 2; + if (0xFFFF != struct_size) { + if (struct_size > 16) { struct_size = 16; } + for (uint32_t j = 0; (j < struct_size) && (struct_len < len); j++) { + uint8_t attr_type = buf2.get8(struct_len); + if (0x21 == attr_type) { + uint16_t val = buf2.get16(struct_len+1); + float batteryvoltage = (float)val / 100; + attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage); + uint8_t batterypercentage = toPercentageCR2032(val); + attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2); + break; + } + struct_len += Z_getDatatypeLen(attr_type) + 1; + } + } + } + attr_list.removeAttribute(&attr); +} + + +void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) { + const char * modelId_c = zigbee_devices.findShortAddr(_srcaddr).modelId; + String modelId((char*) modelId_c); + + if (modelId.startsWith(F("lumi.sensor_cube"))) { + int32_t val = attr.getInt(); + const __FlashStringHelper *aqara_cube = F("AqaraCube"); + const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide"); + const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide"); + + switch (val) { + case 0: + attr_list.addAttribute(aqara_cube).setStr(PSTR("shake")); + break; + case 2: + attr_list.addAttribute(aqara_cube).setStr(PSTR("wakeup")); + break; + case 3: + attr_list.addAttribute(aqara_cube).setStr(PSTR("fall")); + break; + case 64 ... 127: + attr_list.addAttribute(aqara_cube).setStr(PSTR("flip90")); + attr_list.addAttribute(aqara_cube_side).setInt(val % 8); + attr_list.addAttribute(aqara_cube_from_side).setInt((val - 64) / 8); + break; + case 128 ... 132: + attr_list.addAttribute(aqara_cube).setStr(PSTR("flip180")); + attr_list.addAttribute(aqara_cube_side).setInt(val - 128); + break; + case 256 ... 261: + attr_list.addAttribute(aqara_cube).setStr(PSTR("slide")); + attr_list.addAttribute(aqara_cube_side).setInt(val - 256); + break; + case 512 ... 517: + attr_list.addAttribute(aqara_cube).setStr(PSTR("tap")); + attr_list.addAttribute(aqara_cube_side).setInt(val - 512); + break; + } + attr_list.removeAttribute(&attr); +# 1672 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + } else if (modelId.startsWith(F("lumi.remote")) || modelId.startsWith(F("lumi.sensor_switch"))) { + int32_t val = attr.getInt(); + const __FlashStringHelper *aqara_click = F("click"); + const __FlashStringHelper *aqara_action = F("action"); + + switch (val) { + case 0: + attr_list.addAttribute(aqara_action).setStr(PSTR("hold")); + break; + case 1: + attr_list.addAttribute(aqara_click).setStr(PSTR("single")); + break; + case 2: + attr_list.addAttribute(aqara_click).setStr(PSTR("double")); + break; + case 16: + attr_list.addAttribute(aqara_action).setStr(PSTR("hold")); + break; + case 17: + attr_list.addAttribute(aqara_action).setStr(PSTR("release")); + break; + case 18: + attr_list.addAttribute(aqara_action).setStr(PSTR("shake")); + break; + case 255: + attr_list.addAttribute(aqara_action).setStr(PSTR("release")); + break; + default: + attr_list.addAttribute(aqara_click).setUInt(val); + break; + } + } +} + + +void ZCLFrame::syntheticAqaraVibration(class Z_attribute_list &attr_list, class Z_attribute &attr) { + switch (attr.key.id.attr_id) { + case 0x0055: + { + int32_t ivalue = attr.getInt(); + const __FlashStringHelper * svalue; + switch (ivalue) { + case 1: svalue = F("vibrate"); break; + case 2: svalue = F("tilt"); break; + case 3: svalue = F("drop"); break; + default: svalue = F("unknown"); break; + } + attr.setStr((const char*)svalue); + } + break; + case 0x0503: + break; + case 0x0505: + break; + case 0x0508: + { + + + const SBuffer * buf = attr.getRaw(); + if (buf) { + const SBuffer & buf2 = *buf; + int16_t x, y, z; + z = buf2.get16(0); + y = buf2.get16(2); + x = buf2.get16(4); + char temp[32]; + snprintf_P(temp, sizeof(temp), "[%i,%i,%i]", x, y, z); + attr.setStrRaw(temp); + + float X = x; + float Y = y; + float Z = z; + int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; + int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; + int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; + snprintf_P(temp, sizeof(temp), "[%i,%i,%i]", Angle_X, Angle_Y, Angle_Z); + attr_list.addAttribute(F("AqaraAngles")).setStrRaw(temp); + } + } + break; + } +} + + +void Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + Z_attribute_list attr_list; + attr_list.addAttribute(F(OCCUPANCY)).setUInt(0); + zigbee_devices.jsonPublishNow(shortaddr, attr_list); +} + + +void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_list) { + + uint8_t src_ep = _srcendpoint; + + for (auto &attr : attr_list) { + + if (!attr.key_is_str) { + uint16_t cluster = attr.key.id.cluster; + uint16_t attribute = attr.key.id.attr_id; + uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id; + Z_Device & device = zigbee_devices.getShortAddr(shortaddr); + + + bool found = false; + const char * conv_name; + Z_Data_Type map_type; + uint8_t map_offset; + uint8_t zigbee_type; + int8_t conv_multiplier; + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && + ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { + conv_multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + zigbee_type = pgm_read_byte(&converter->type); + uint8_t mapping = pgm_read_byte(&converter->mapping); + map_type = (Z_Data_Type) ((mapping & 0xF0)>>4); + map_offset = (mapping & 0x0F); + conv_name = Z_strings + pgm_read_word(&converter->name_offset); + found = true; + break; + } + } + + float fval = attr.getFloat(); + if (found && (map_type != Z_Data_Type::Z_Unknown)) { + + + + + + Z_Data & data = device.data.getByType(map_type, src_ep); + uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + map_offset; + uint32_t uval32 = attr.getUInt(); + int32_t ival32 = attr.getInt(); + + switch (zigbee_type) { + case Zenum8: + case Zuint8: *(uint8_t*)attr_address = uval32; break; + case Zenum16: + case Zuint16: *(uint16_t*)attr_address = uval32; break; + case Zuint32: *(uint32_t*)attr_address = uval32; break; + case Zint8: *(int8_t*)attr_address = ival32; break; + case Zint16: *(int16_t*)attr_address = ival32; break; + case Zint32: *(int32_t*)attr_address = ival32; break; + } + } + + uint16_t uval16 = attr.getUInt(); + int16_t ival16 = attr.getInt(); + Z_Data_Set & data = device.data; + + switch (ccccaaaa) { + case 0x00000004: zigbee_devices.setManufId(shortaddr, attr.getStr()); break; + case 0x00000005: zigbee_devices.setModelId(shortaddr, attr.getStr()); break; + case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16 / 2); break; + case 0x00060000: + case 0x00068000: device.setPower(attr.getBool(), src_ep); break; + } + + + if (found) { + if (0 == conv_multiplier) { attr_list.removeAttribute(&attr); continue; } + if (1 != conv_multiplier) { + if (conv_multiplier > 0) { fval = fval * conv_multiplier; } + else { fval = fval / (-conv_multiplier); } + attr.setFloat(fval); + } + } + + + if (found) { + if (0x00 != pgm_read_byte(conv_name)) { + attr.setKeyName(conv_name, true); + } + } + } + } +} +# 1872 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +bool Z_parseAttributeKey(class Z_attribute & attr) { + + + if (attr.key_is_str) { + const char * key = attr.key.key; + char * delimiter = strchr(key, '/'); + char * delimiter2 = strchr(key, '%'); + if (delimiter) { + uint16_t attr_id = 0xFFFF; + uint16_t cluster_id = 0xFFFF; + uint8_t type_id = Zunk; + + cluster_id = strtoul(key, &delimiter, 16); + if (!delimiter2) { + attr_id = strtoul(delimiter+1, nullptr, 16); + } else { + attr_id = strtoul(delimiter+1, &delimiter2, 16); + type_id = strtoul(delimiter2+1, nullptr, 16); + } + attr.setKeyId(cluster_id, attr_id); + attr.attr_type = type_id; + } + } + + + + if (Zunk == attr.attr_type) { + + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + bool match = false; + uint16_t local_attr_id = pgm_read_word(&converter->attribute); + uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint8_t local_type_id = pgm_read_byte(&converter->type); + int8_t local_multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + + + if (!attr.key_is_str) { + if ((attr.key.id.cluster == local_cluster_id) && (attr.key.id.attr_id == local_attr_id)) { + attr.attr_type = local_type_id; + break; + } + } else if (pgm_read_word(&converter->name_offset)) { + const char * key = attr.key.key; + + if (0 == strcasecmp_P(key, Z_strings + pgm_read_word(&converter->name_offset))) { + + attr.setKeyId(local_cluster_id, local_attr_id); + attr.attr_type = local_type_id; + attr.attr_multiplier = local_multiplier; + break; + } + } + } + } + return (Zunk != attr.attr_type) ? true : false; +} + + + + + + +void Z_Data::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const { + + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint8_t conv_export = pgm_read_byte(&converter->multiplier_idx) & Z_EXPORT_DATA; + uint8_t conv_mapping = pgm_read_byte(&converter->mapping); + Z_Data_Type map_type = (Z_Data_Type) ((conv_mapping & 0xF0)>>4); + uint8_t map_offset = (conv_mapping & 0x0F); + + if ((conv_export != 0) && (map_type == type)) { + + const char * conv_name = Z_strings + pgm_read_word(&converter->name_offset); + uint8_t zigbee_type = pgm_read_byte(&converter->type); + uint8_t *attr_address = ((uint8_t*)this) + sizeof(Z_Data) + map_offset; + + int32_t data_size = 0; + int32_t ival32; + uint32_t uval32; + switch (zigbee_type) { + case Zenum8: + case Zuint8: uval32 = *(uint8_t*)attr_address; if (uval32 != 0xFF) data_size = 8; break; + case Zenum16: + case Zuint16: uval32 = *(uint16_t*)attr_address; if (uval32 != 0xFFFF) data_size = 16; break; + case Zuint32: uval32 = *(uint32_t*)attr_address; if (uval32 != 0xFFFFFFFF) data_size = 32; break; + case Zint8: ival32 = *(int8_t*)attr_address; if (ival32 != -0x80) data_size = -8; break; + case Zint16: ival32 = *(int16_t*)attr_address; if (ival32 != -0x8000) data_size = -16; break; + case Zint32: ival32 = *(int32_t*)attr_address; if (ival32 != -0x80000000) data_size = -32; break; + } + if (data_size != 0) { + Z_attribute & attr = attr_list.addAttribute(conv_name); + + if (data_size > 0) { attr.setUInt(uval32); } + else { attr.setInt(ival32); } + } + } + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +#ifdef USE_ZIGBEE + + + + + +typedef struct Z_CommandConverter { + uint16_t tasmota_cmd_offset; + uint16_t cluster; + uint8_t cmd; + uint8_t direction; + uint16_t param_offset; +} Z_CommandConverter; + +typedef struct Z_XYZ_Var { + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + uint8_t x_type = 0; + uint8_t y_type = 0; + uint8_t z_type = 0; +} Z_XYZ_Var; +# 50 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +const Z_CommandConverter Z_Commands[] PROGMEM = { + + { Z_(Identify), 0x0003, 0x00, 0x01, Z_(xxxx) }, + { Z_(IdentifyQuery), 0x0003, 0x01, 0x01, Z_() }, + + { Z_(AddGroup), 0x0004, 0x00, 0x01, Z_(xxxx00) }, + { Z_(ViewGroup), 0x0004, 0x01, 0x01, Z_(xxxx) }, + { Z_(GetGroup), 0x0004, 0x02, 0x01, Z_(01xxxx) }, + { Z_(GetAllGroups), 0x0004, 0x02, 0x01, Z_(00) }, + { Z_(RemoveGroup), 0x0004, 0x03, 0x01, Z_(xxxx) }, + { Z_(RemoveAllGroups),0x0004, 0x04, 0x01, Z_() }, + + + { Z_(ViewScene), 0x0005, 0x01, 0x01, Z_(xxxxyy) }, + { Z_(RemoveScene), 0x0005, 0x02, 0x01, Z_(xxxxyy) }, + { Z_(RemoveAllScenes),0x0005, 0x03, 0x01, Z_(xxxx) }, + { Z_(RecallScene), 0x0005, 0x05, 0x01, Z_(xxxxyy) }, + { Z_(GetSceneMembership),0x0005, 0x06, 0x01, Z_(xxxx) }, + + { Z_(PowerOffEffect), 0x0006, 0x40, 0x81, Z_(xxyy) }, + { Z_(PowerOnRecall), 0x0006, 0x41, 0x81, Z_() }, + { Z_(PowerOnTimer), 0x0006, 0x42, 0x81, Z_(xxyyyyzzzz) }, + { Z_(Power), 0x0006, 0xFF, 0x01, Z_() }, + { Z_(Dimmer), 0x0008, 0x04, 0x01, Z_(xx0A00) }, + { Z_(DimmerUp), 0x0008, 0x06, 0x01, Z_(00190200) }, + { Z_(DimmerDown), 0x0008, 0x06, 0x01, Z_(01190200) }, + { Z_(DimmerStop), 0x0008, 0x03, 0x01, Z_() }, + { Z_(ResetAlarm), 0x0009, 0x00, 0x01, Z_(xxyyyy) }, + { Z_(ResetAllAlarms), 0x0009, 0x01, 0x01, Z_() }, + { Z_(Hue), 0x0300, 0x00, 0x01, Z_(xx000A00) }, + { Z_(Sat), 0x0300, 0x03, 0x01, Z_(xx0A00) }, + { Z_(HueSat), 0x0300, 0x06, 0x01, Z_(xxyy0A00) }, + { Z_(Color), 0x0300, 0x07, 0x01, Z_(xxxxyyyy0A00) }, + { Z_(CT), 0x0300, 0x0A, 0x01, Z_(xxxx0A00) }, + { Z_(ShutterOpen), 0x0102, 0x00, 0x01, Z_() }, + { Z_(ShutterClose), 0x0102, 0x01, 0x01, Z_() }, + { Z_(ShutterStop), 0x0102, 0x02, 0x01, Z_() }, + { Z_(ShutterLift), 0x0102, 0x05, 0x01, Z_(xx) }, + { Z_(ShutterTilt), 0x0102, 0x08, 0x01, Z_(xx) }, + { Z_(Shutter), 0x0102, 0xFF, 0x01, Z_() }, + + { Z_(Occupancy), 0xEF00, 0x01, 0x82, Z_()}, + + { Z_(Dimmer), 0x0008, 0x00, 0x01, Z_(xx) }, + { Z_(DimmerMove), 0x0008, 0x01, 0x01, Z_(xx0A) }, + { Z_(DimmerStepUp), 0x0008, 0x02, 0x01, Z_(00xx0A00) }, + { Z_(DimmerStepDown), 0x0008, 0x02, 0x01, Z_(01xx0A00) }, + { Z_(DimmerStep), 0x0008, 0x02, 0x01, Z_(xx190A00) }, + { Z_(DimmerMove), 0x0008, 0x05, 0x01, Z_(xx0A) }, + { Z_(DimmerUp), 0x0008, 0x06, 0x01, Z_(00) }, + { Z_(DimmerDown), 0x0008, 0x06, 0x01, Z_(01) }, + { Z_(DimmerStop), 0x0008, 0x07, 0x01, Z_() }, + { Z_(HueMove), 0x0300, 0x01, 0x01, Z_(xx19) }, + { Z_(HueStepUp), 0x0300, 0x02, 0x01, Z_(01xx0A00) }, + { Z_(HueStepDown), 0x0300, 0x02, 0x01, Z_(03xx0A00) }, + { Z_(HueStep), 0x0300, 0x02, 0x01, Z_(xx190A00) }, + { Z_(SatMove), 0x0300, 0x04, 0x01, Z_(xx19) }, + { Z_(SatStep), 0x0300, 0x05, 0x01, Z_(xx190A) }, + { Z_(ColorMove), 0x0300, 0x08, 0x01, Z_(xxxxyyyy) }, + { Z_(ColorStep), 0x0300, 0x09, 0x01, Z_(xxxxyyyy0A00) }, + { Z_(ColorTempMoveUp), 0x0300, 0x4B, 0x01, Z_(01xxxx000000000000) }, + { Z_(ColorTempMoveDown),0x0300, 0x4B, 0x01, Z_(03xxxx000000000000) }, + { Z_(ColorTempMoveStop),0x0300, 0x4B, 0x01, Z_(00xxxx000000000000) }, + { Z_(ColorTempMove), 0x0300, 0x4B, 0x01, Z_(xxyyyy000000000000) }, + { Z_(ColorTempStepUp), 0x0300, 0x4C, 0x01, Z_(01xxxx0A0000000000) }, + { Z_(ColorTempStepDown),0x0300, 0x4C, 0x01, Z_(03xxxx0A0000000000) }, + { Z_(ColorTempStep), 0x0300, 0x4C, 0x01, Z_(xxyyyy0A0000000000) }, + + { Z_(ArrowClick), 0x0005, 0x07, 0x01, Z_(xx) }, + { Z_(ArrowHold), 0x0005, 0x08, 0x01, Z_(xx) }, + { Z_(ArrowRelease), 0x0005, 0x09, 0x01, Z_() }, + + { Z_(IdentifyQuery), 0x0003, 0x00, 0x02, Z_(xxxx) }, + + { Z_(ZoneStatusChange),0x0500, 0x00, 0x82, Z_(xxxxyyzz) }, + + { Z_(AddGroup), 0x0004, 0x00, 0x82, Z_(xxyyyy) }, + { Z_(ViewGroup), 0x0004, 0x01, 0x82, Z_(xxyyyy) }, + { Z_(GetGroup), 0x0004, 0x02, 0x82, Z_(xxyyzzzz) }, + { Z_(RemoveGroup), 0x0004, 0x03, 0x82, Z_(xxyyyy) }, + + { Z_(AddScene), 0x0005, 0x00, 0x82, Z_(xxyyyyzz) }, + { Z_(ViewScene), 0x0005, 0x01, 0x82, Z_(xxyyyyzz) }, + { Z_(RemoveScene), 0x0005, 0x02, 0x82, Z_(xxyyyyzz) }, + { Z_(RemoveAllScenes),0x0005, 0x03, 0x82, Z_(xxyyyy) }, + { Z_(StoreScene), 0x0005, 0x04, 0x82, Z_(xxyyyyzz) }, + { Z_(GetSceneMembership),0x0005, 0x06, 0x82,Z_(xxyyzzzz) }, + + { Z_(), 0xEF00, 0xFF, 0x83, Z_() }, + + { Z_(), 0xFCCC, 0x00, 0x82, Z_(xxyy) }, +}; + + + + +#define ZLE(x) ((x) & 0xFF), ((x) >> 8) + + +const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007), ZLE(0x0008) }; + + +void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + size_t attrs_len = 0; + const uint8_t* attrs = nullptr; + + switch (cluster) { + case 0x0006: + attrs = CLUSTER_0006; + attrs_len = sizeof(CLUSTER_0006); + break; + case 0x0008: + attrs = CLUSTER_0008; + attrs_len = sizeof(CLUSTER_0008); + break; + case 0x0009: + attrs = CLUSTER_0009; + attrs_len = sizeof(CLUSTER_0009); + break; + case 0x0300: + attrs = CLUSTER_0300; + attrs_len = sizeof(CLUSTER_0300); + break; + } + if (attrs) { + if (groupaddr) { + shortaddr = BAD_SHORTADDR; + } + uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + groupaddr, + cluster , + endpoint, + ZCL_READ_ATTRIBUTES, + 0, + false , + true , + false , + seq, + attrs, attrs_len + })); + } +} + + + +void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + if (BAD_SHORTADDR != shortaddr) { + zigbee_devices.setReachable(shortaddr, false); + } +} + + +inline bool isXYZ(char c) { + return (c >= 'x') && (c <= 'z'); +} + + + + +inline int8_t hexValue(char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + return -1; +} + + +uint32_t parseHex_P(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(pgm_read_byte(*data)); + if (v < 0) { break; } + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + + + + + +void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) { + const char *p = model; + uint32_t v = 0; + char c = pgm_read_byte(p); + while (c) { + char c1 = pgm_read_byte(p+1); + if (!c1) { break; } + if (isXYZ(c) && (c == c1) && (v < payload.len())) { + uint8_t val = payload.get8(v); + switch (c) { + case 'x': + xyz->x = xyz->x | (val << (xyz->x_type * 8)); + xyz->x_type++; + break; + case 'y': + xyz->y = xyz->y | (val << (xyz->y_type * 8)); + xyz->y_type++; + break; + case 'z': + xyz->z = xyz->z | (val << (xyz->z_type * 8)); + xyz->z_type++; + break; + } + } + p += 2; + v++; + c = pgm_read_byte(p); + } +} + + + +void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) { + const char * command_name = nullptr; + uint8_t conv_direction; + Z_XYZ_Var xyz; + + + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if (conv_cluster == cluster) { + + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + conv_direction = pgm_read_byte(&conv->direction); + if ((0xFF == conv_cmd) || (cmd == conv_cmd)) { + + if ((direction && (conv_direction & 0x02)) || (!direction && (conv_direction & 0x01))) { + + + + + const char * p = Z_strings + pgm_read_word(&conv->param_offset); + + bool match = true; + for (uint8_t i = 0; i < payload.len(); i++) { + const char c1 = pgm_read_byte(p); + const char c2 = pgm_read_byte(p+1); + + if ((0x00 == c1) || isXYZ(c1)) { + break; + } + const char * p2 = p; + uint32_t nextbyte = parseHex_P(&p2, 2); + + if (nextbyte != payload.get8(i)) { + match = false; + break; + } + p += 2; + } + if (match) { + command_name = Z_strings + pgm_read_word(&conv->tasmota_cmd_offset); + parseXYZ(Z_strings + pgm_read_word(&conv->param_offset), payload, &xyz); + if (0xFF == conv_cmd) { + + xyz.z = xyz.y; + xyz.z_type = xyz.y_type; + xyz.y = xyz.x; + xyz.y_type = xyz.x_type; + xyz.x = cmd; + xyz.x_type = 1; + } + break; + } + } + } + } + } + + + + + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '<' : '!', cmd); + Z_attribute & attr_raw = attr_list.addAttribute(attrid_str); + attr_raw.setBuf(payload, 0, payload.len()); + + if (command_name) { + + + if (conv_direction & 0x80) { + uint32_t cccc00mm = (cluster << 16) | cmd; + + switch (cccc00mm) { + case 0x05000000: + attr_list.addAttribute(command_name, true).setUInt(xyz.x); + if (0 != xyz.y) { + attr_list.addAttribute(command_name, PSTR("Ext")).setUInt(xyz.y); + } + if ((0 != xyz.z) && (0xFF != xyz.z)) { + attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z); + } + + + + attr_list.addAttribute(0x0406, 0x0000).setUInt((xyz.x) & 0x01 ? 1 : 0); + break; + case 0x00040000: + case 0x00040001: + case 0x00040003: + attr_list.addAttribute(command_name, true).setUInt(xyz.y); + attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); + attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); + break; + case 0x00040002: + attr_list.addAttribute(command_name, PSTR("Capacity")).setUInt(xyz.x); + attr_list.addAttribute(command_name, PSTR("Count")).setUInt(xyz.y); + { + + Z_json_array group_list; + for (uint32_t i = 0; i < xyz.y; i++) { + group_list.add(payload.get16(2 + 2*i)); + } + attr_list.addAttribute(command_name, true).setStrRaw(group_list.toString().c_str()); + } + break; + case 0x00050000: + case 0x00050001: + case 0x00050002: + case 0x00050004: + attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); + attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); + attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y); + attr_list.addAttribute(PSTR("SceneId"), true).setUInt(xyz.z); + if (0x00050001 == cccc00mm) { + attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); + } + break; + case 0x00050003: + attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); + attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); + attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y); + break; + case 0x00050006: + attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); + attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); + attr_list.addAttribute(PSTR("Capacity"), true).setUInt(xyz.y); + attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.z); + attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); + break; + case 0x00060040: + attr_list.addAttribute(PSTR("Power"), true).setUInt(0); + attr_list.addAttribute(PSTR("PowerEffect"), true).setUInt(xyz.x); + attr_list.addAttribute(PSTR("PowerEffectVariant"), true).setUInt(xyz.y); + break; + case 0x00060041: + attr_list.addAttribute(PSTR("Power"), true).setUInt(1); + attr_list.addAttribute(PSTR("PowerRecallGlobalScene"), true).setBool(true); + break; + case 0x00060042: + attr_list.addAttribute(PSTR("Power"), true).setUInt(1); + attr_list.addAttribute(PSTR("PowerOnlyWhenOn"), true).setUInt(xyz.x); + attr_list.addAttribute(PSTR("PowerOnTime"), true).setFloat(xyz.y / 10.0f); + attr_list.addAttribute(PSTR("PowerOffWait"), true).setFloat(xyz.z / 10.0f); + break; + case 0xEF000000 ... 0xEF0000FF: + if (convertTuyaSpecificCluster(attr_list, cluster, cmd, direction, shortaddr, srcendpoint, payload)) { + attr_list.removeAttribute(&attr_raw); + } + break; + case 0xFCCC0000: + attr_list.addAttribute(PSTR("TerncyPress"), true).setUInt(xyz.y); + attr_list.addAttribute(PSTR("TerncyCount"), true).setUInt(xyz.x); + break; + } + } else { + + char command_suffix[4] = { 0x00 }; + + if (Settings.flag4.zb_index_ep) { + if (zigbee_devices.countEndpoints(shortaddr) > 0) { + snprintf_P(command_suffix, sizeof(command_suffix), PSTR("%d"), srcendpoint); + } + } + if (0 == xyz.x_type) { + attr_list.addAttribute(command_name, command_suffix).setBool(true); + } else if (0 == xyz.y_type) { + attr_list.addAttribute(command_name, command_suffix).setUInt(xyz.x); + } else { + + Z_json_array arr; + arr.add(xyz.x); + arr.add(xyz.y); + if (xyz.z_type) { + arr.add(xyz.z); + } + attr_list.addAttribute(command_name, command_suffix).setStrRaw(arr.toString().c_str()); + } + } + } +} + + + + + + +bool convertTuyaSpecificCluster(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &buf) { + + + uint16_t dp = buf.get16(2); + + uint8_t len = buf.get8(5); + + if ((1 == cmd) || (2 == cmd)) { + + Z_attribute & attr = attr_list.addAttribute(cluster, dp); + uint8_t attr_type; + switch (len) { + case 1: attr_type = Ztuya1; break; + case 2: attr_type = Ztuya2; break; + case 4: attr_type = Ztuya4; break; + default: attr_type = Zoctstr; break; + } + parseSingleAttribute(attr, buf, 5, attr_type); + return true; + } + return false; +} + + + + + +const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) { + if (nullptr == command) { return nullptr; } + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + uint8_t conv_direction = pgm_read_byte(&conv->direction); + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if ((conv_direction & 0x01) && (0 == strcasecmp_P(command, Z_strings + pgm_read_word(&conv->tasmota_cmd_offset)))) { + *cluster = conv_cluster; + *cmd = conv_cmd; + return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&conv->param_offset)); + } + } + + return nullptr; +} + + +inline char hexDigit(uint32_t h) { + uint32_t nybble = h & 0x0F; + return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; +} + + +String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { + size_t len = strlen_P(zcl_cmd_P); + char zcl_cmd[len+1]; + strcpy_P(zcl_cmd, zcl_cmd_P); + + char *p = zcl_cmd; + while (*p) { + if (isXYZ(*p) && (*p == *(p+1))) { + uint8_t val; + switch (*p) { + case 'x': + val = x & 0xFF; + x = x >> 8; + break; + case 'y': + val = y & 0xFF; + y = y >> 8; + break; + case 'z': + val = z & 0xFF; + z = z >> 8; + break; + } + *p = hexDigit(val >> 4); + *(p+1) = hexDigit(val); + p++; + } + p++; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); + + return String(zcl_cmd); +} + +const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" + "ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" + "TOGGLE|" D_TOGGLE "|" + "ALL" ; + +const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, + 1,1,1,1,1, + 2,2, + 255 }; + + +uint32_t ZigbeeAliasOrNumber(const char *state_text) { + char command[16]; + int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); + if (state_number >= 0) { + + return pgm_read_byte(kZ_Numbers + state_number); + } else { + + return strtoul(state_text, nullptr, 0); + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +#ifdef USE_ZIGBEE + + + +const uint8_t ZIGBEE_STATUS_OK = 0; +const uint8_t ZIGBEE_STATUS_BOOT = 1; +const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; +const uint8_t ZIGBEE_STATUS_STARTING = 3; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_ERROR = 23; +const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; +const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; +const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; +const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; +const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; +const uint8_t ZIGBEE_STATUS_SCANNING = 40; +const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; +const uint8_t ZIGBEE_STATUS_CC_INFO = 51; +const uint8_t ZIGBEE_STATUS_EZ_VERSION = 55; +const uint8_t ZIGBEE_STATUS_EZ_INFO = 56; +const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; +const uint8_t ZIGBEE_STATUS_ABORT = 99; + +typedef union Zigbee_Instruction { + struct { + uint8_t i; + uint8_t d8; + uint16_t d16; + } i; + const void *p; +} Zigbee_Instruction; + +typedef struct Zigbee_Instruction_Type { + uint8_t instr; + uint8_t data; +} Zigbee_Instruction_Type; + +enum Zigbee_StateMachine_Instruction_Set { + + ZGB_INSTR_4_BYTES = 0, + ZGB_INSTR_NOOP = 0, + ZGB_INSTR_LABEL, + ZGB_INSTR_GOTO, + ZGB_INSTR_ON_ERROR_GOTO, + ZGB_INSTR_ON_TIMEOUT_GOTO, + ZGB_INSTR_WAIT, + ZGB_INSTR_WAIT_FOREVER, + ZGB_INSTR_STOP, + + + ZGB_INSTR_8_BYTES = 0x80, + ZGB_INSTR_CALL = 0x80, + ZGB_INSTR_LOG, + ZGB_INSTR_MQTT_STATE, + ZGB_INSTR_SEND, + ZGB_INSTR_WAIT_UNTIL, + ZGB_INSTR_WAIT_RECV, + ZGB_ON_RECV_UNEXPECTED, + + + ZGB_INSTR_12_BYTES = 0xF0, + ZGB_INSTR_WAIT_UNTIL_CALL, + ZGB_INSTR_WAIT_RECV_CALL, +}; + +#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, +#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, +#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, +#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, +#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, +#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, +#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, +#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, + +#define ZI_CALL(f,x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, +#define ZI_LOG(x,m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_MQTT_STATE(x,m) { .i = { ZGB_INSTR_MQTT_STATE, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, +#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV(x,m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_UNTIL(x,m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_UNTIL_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_UNTIL_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, +#define ZI_WAIT_RECV_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, + + + + + +#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) +#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) +#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) +#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) +#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) +#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) +#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) +#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) + +#define ZBM(n,x...) const uint8_t n[] PROGMEM = { x }; + +#define ZBR(n,x...) uint8_t n[] = { x }; +#define ZBW(n,x...) { const uint8_t n ##t[] = { x }; memcpy(n, n ##t, sizeof(n)); } + +#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) + +const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration"; +const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator"; +const char kConfiguredRouter[] PROGMEM = "Configured, starting router"; +const char kConfiguredDevice[] PROGMEM = "Configured, starting device"; +const char kStarted[] PROGMEM = "Started"; +const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started"; +const char kResetting[] PROGMEM = "Resetting configuration"; +const char kResettingDevice[] PROGMEM = D_LOG_ZIGBEE "Resetting EZSP device"; +const char kReconfiguringDevice[] PROGMEM = D_LOG_ZIGBEE "Factory reset EZSP device"; +const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported"; +const char kEZ8[] PROGMEM = "Only EZSP protocol v8 is currently supported"; +const char kAbort[] PROGMEM = "Abort"; +const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; +const char kZigbeeGroup0[] PROGMEM = D_LOG_ZIGBEE "Subscribe to group 0 'ZbListen0 0'"; + +#ifdef USE_ZIGBEE_ZNP + + + +ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) +ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) + +ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) +ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) + + +ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 ) +ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x01 , 0x55) + + +ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) +ZBR(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , + 0x00, 0x00 ) + +ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) +ZBR(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, + 0x08 , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ) + +ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) +ZBR(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, + 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) +ZBR(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, + 0x10 , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + ) + +ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) +ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEYS_ENABLE, + 0x01 , 0x00 ) + +ZBM(ZBS_LOGTYPE, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_LOGICAL_TYPE ) +ZBM(ZBS_LOGTYPE_COORD, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE, + 0x01 , 0x00 ) +ZBM(ZBS_LOGTYPE_ROUTER, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE, + 0x01 , 0x01 ) +ZBM(ZBS_LOGTYPE_DEVICE, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE, + 0x01 , 0x02 ) + + + + +ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) +ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) + + +ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x03 ) + +ZBR(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , 0x00, 0x00 ) + +ZBR(ZBS_W_ALL_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(0xFFFF), Z_B1(0xFFFF) ) + +ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ) + +ZBR(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +const uint32_t ZB_ALL_CHANNELS = 0x07FFF800; +ZBR(ZBS_W_ALL_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(ZB_ALL_CHANNELS), Z_B1(ZB_ALL_CHANNELS), Z_B2(ZB_ALL_CHANNELS), Z_B3(ZB_ALL_CHANNELS), + ) + +ZBM(ZBS_W_LOGTYP_COORD, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x00 ) + +ZBM(ZBS_W_LOGTYP_ROUTER, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x01 ) + +ZBM(ZBS_W_LOGTYP_DEVICE, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x02 ) + +ZBR(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ) + +ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 , 0x00 ) + +ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), + 0x00 , 0x20 , + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, + 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 , 0x01 ) + +ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, + 0x01, 0x00 , 0x01 , 0x00 ) + + +ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) + + +ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), + 0x00 , 0x01 , 0x55 ) + +ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 ) +ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) +ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND ) +ZBM(AREQ_STARTUPFROMAPP_COORD, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) +ZBM(AREQ_STARTUPFROMAPP_ROUTER, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ROUTER ) +ZBM(AREQ_STARTUPFROMAPP_DEVICE, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_END_DEVICE ) + +ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) +ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_SUCCESS ) +# 270 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 , 0x00, 0x00 ) +ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_SUCCESS ) + +ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) +# 288 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) +ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_SUCCESS) +ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, + 0x00, 0x00 , 0x00 ) +ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, + 0x00, 0x00 , 0x02 , 0x0B, 0x01 ) + + +ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) +ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_SUCCESS) +ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) + +ZBM(ZBS_AF_REGISTER_ALL, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x0E , + 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, + 0x07,0x00, 0x08,0x00, 0x0A,0x00, 0x02,0x01, + 0x00,0x03, 0x00,0x04, 0x02,0x04, 0x03,0x04, + 0x05,0x04, 0x06,0x04, + 0x00 ) + + +ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 , + 0x00, 0x00 , 0x00 , 0x00 ) +ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_SUCCESS) +ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 , Z_SUCCESS ) + + +void ZNP_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h) { + uint32_t zb_channel_mask = (1 << zb_channel); + + ZBW(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , + Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) + + ZBW(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, + 0x08 , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid), + ) + + ZBW(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, + 0x04 , + Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), + ) + + ZBW(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, + 0x10 , + Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), + Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), + Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), + Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), + + ) + + ZBW(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) + + ZBW(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid) + ) + + ZBW(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), + ) + + ZBW(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 , + Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), + Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), + Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), + Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), + ) +} + +static const Zigbee_Instruction zb_prog[] PROGMEM = { + ZI_LABEL(0) + ZI_NOOP() + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_RECV_UNEXPECTED(&ZNP_Recv_Default) + ZI_WAIT(10500) + + + ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "rebooting CC2530 device") + + ZI_CALL(&ZNP_Reset_Device, 0) + ZI_WAIT(100) + ZI_CALL(&ZNP_Reset_Device, 1) + ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &ZNP_Reboot) + ZI_WAIT(100) + ZI_LOG(LOG_LEVEL_DEBUG, kCheckingDeviceConfiguration) + ZI_SEND(ZBS_VERSION) + ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &ZNP_ReceiveCheckVersion) + + + ZI_CALL(&Z_SwitchDeviceType, 0) + + + + + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_COORD) + ZI_SEND(ZBS_ZNPHC) + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + + ZI_SEND(ZBS_LOGTYPE) + ZI_WAIT_RECV(1000, ZBS_LOGTYPE_COORD) + ZI_SEND(ZBS_PAN) + ZI_WAIT_RECV(1000, ZBR_PAN) + ZI_SEND(ZBS_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_EXTPAN) + ZI_SEND(ZBS_CHANN) + ZI_WAIT_RECV(1000, ZBR_CHANN) + ZI_SEND(ZBS_PFGK) + ZI_WAIT_RECV(1000, ZBR_PFGK) + ZI_SEND(ZBS_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_PFGKEN) + + + + ZI_LABEL(ZIGBEE_LABEL_START_COORD) + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + + + ZI_SEND(ZBS_STARTUPFROMAPP) + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) + ZI_WAIT_UNTIL_FUNC(10000, AREQ_STARTUPFROMAPP, &ZNP_ReceiveStateChange) + ZI_SEND(ZBS_GETDEVICEINFO) + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &ZNP_ReceiveDeviceInfo) + + ZI_SEND(ZBS_ZDO_NODEDESCREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) + ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) + ZI_SEND(ZBS_AF_REGISTER01) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_AF_REGISTER0B) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) + + + + + ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) + ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) + ZI_CALL(&Z_State_Ready, 1) + ZI_CALL(&Z_Load_Devices, 0) + ZI_CALL(&Z_Query_Bulbs, 0) + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) + ZI_WAIT_FOREVER() + ZI_GOTO(ZIGBEE_LABEL_READY) + + ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_COORD) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_PAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_CHANN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_LOGTYP_COORD) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGK) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_WNV_SECMODE) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + ZI_SEND(ZBS_W_ZDODCB) + ZI_WAIT_RECV(1000, ZBR_W_OK) + + ZI_SEND(ZBS_WNV_INITZNPHC) + ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &ZNP_CheckNVWrite) + ZI_SEND(ZBS_WNV_ZNPHC) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + + + ZI_GOTO(ZIGBEE_LABEL_START_COORD) + + + + + ZI_LABEL(ZIGBEE_LABEL_INIT_ROUTER) + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER) + ZI_SEND(ZBS_ZNPHC) + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + ZI_SEND(ZBS_LOGTYPE) + ZI_WAIT_RECV(1000, ZBS_LOGTYPE_ROUTER) + + + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredRouter) + ZI_LABEL(ZIGBEE_LABEL_START_ROUTER_DEVICE) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_AF_REGISTER_ALL) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_STARTUPFROMAPP) + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) + ZI_WAIT_UNTIL_FUNC(0xFFFF, AREQ_STARTUPFROMAPP, &ZNP_ReceiveStateChange) + ZI_SEND(ZBS_GETDEVICEINFO) + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &ZNP_ReceiveDeviceInfo) + ZI_GOTO(ZIGBEE_LABEL_READY) + + ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_ROUTER) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_LOGTYP_ROUTER) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST) + ZI_SEND(ZBS_W_ALL_PAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_ALL_CHANN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + + + ZI_SEND(ZBS_WNV_INITZNPHC) + ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &ZNP_CheckNVWrite) + ZI_SEND(ZBS_WNV_ZNPHC) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + + ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE) + + + + + ZI_LABEL(ZIGBEE_LABEL_INIT_DEVICE) + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_DEVICE) + ZI_SEND(ZBS_ZNPHC) + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + ZI_SEND(ZBS_LOGTYPE) + ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) + + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredDevice) + ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE) + + ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_LOGTYP_DEVICE) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST) + + + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12) + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + + ZI_LABEL(ZIGBEE_LABEL_ABORT) + ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort) + ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort) + ZI_STOP(ZIGBEE_LABEL_ABORT) +}; + +#endif + +#ifdef USE_ZIGBEE_EZSP + + + + +ZBM(ZBR_RSTACK, Z_B0(EZSP_rstAck), Z_B1(EZSP_rstAck)) + + +ZBM(ZBS_VERSION, EZSP_version, 0x00, 0x08) +ZBM(ZBR_VERSION, EZSP_version, 0x00, 0x08, 0x02) + + + +ZBM(ZBS_SET_ADDR_TABLE, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_KEY_TABLE_SIZE, 0x02, 0x00) +ZBM(ZBS_SET_MCAST_TABLE, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_MULTICAST_TABLE_SIZE, 0x10, 0x00) +ZBM(ZBS_SET_STK_PROF, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_STACK_PROFILE, 0x02, 0x00) +ZBM(ZBS_SET_SEC_LEVEL, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_SECURITY_LEVEL, 0x05, 0x00) +ZBM(ZBS_SET_MAX_DEVICES, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_MAX_END_DEVICE_CHILDREN, 0x20, 0x00) +ZBM(ZBS_SET_INDIRECT_TMO, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 0x00, 0x1E) +ZBM(ZBS_SET_TC_CACHE, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 0x02, 0x00) +ZBM(ZBS_SET_ROUTE_TBL, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_SOURCE_ROUTE_TABLE_SIZE, 0x10, 0x00) +ZBM(ZBS_SET_KEY_TBL, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_KEY_TABLE_SIZE, 0x04, 0x00) +ZBM(ZBS_SET_PANID_CNFLCT, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 0x02, 0x00) +ZBM(ZBS_SET_ZDO_REQ, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_APPLICATION_ZDO_FLAGS, EMBER_APP_RECEIVES_SUPPORTED_ZDO_REQUESTS | EMBER_APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS, 0x00) +ZBM(ZBS_SET_NETWORKS, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_SUPPORTED_NETWORKS, 0x01, 0x00) +ZBM(ZBS_SET_PACKET_BUF, EZSP_setConfigurationValue, 0x00 , EZSP_CONFIG_PACKET_BUFFER_COUNT, 0xFF, 0x00) + +ZBM(ZBR_SET_OK, EZSP_setConfigurationValue, 0x00 , 0x00 ) + + +ZBR(ZBR_SET_OK2, EZSP_setConfigurationValue, 0x00 , 0x00 ) + + + + + + +ZBM(ZBS_ADD_ENDPOINT1, EZSP_addEndpoint, 0x00 , 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , + 0x00 , + 0X00 , + + + + + ) +ZBM(ZBS_ADD_ENDPOINTB, EZSP_addEndpoint, 0x00 , 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , + 0x00 , + 0X00 , + + + + + ) +ZBM(ZBR_ADD_ENDPOINT, EZSP_addEndpoint, 0x00 , 0x00 ) + + +ZBM(ZBS_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 , 0x00 , 0xF9,0xFF , + 0x58,0x02 , 0x08,0x07 , 0x02 , 0x05 , 0x00 ) +ZBM(ZBR_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 , 0x00 ) + + +#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY +ZBR(ZBS_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 , + Z_B0(EZ_SECURITY_MODE), Z_B1(EZ_SECURITY_MODE), + + 0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0x30, 0x39, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + ) +ZBM(ZBR_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 , 0x00 ) + + +ZBM(ZBS_SET_POLICY_00, EZSP_setPolicy, 0x00 , EZSP_TRUST_CENTER_POLICY, + EZSP_DECISION_ALLOW_JOINS | EZSP_DECISION_ALLOW_UNSECURED_REJOINS) +ZBM(ZBS_SET_POLICY_02, EZSP_setPolicy, 0x00 , EZSP_UNICAST_REPLIES_POLICY, + EZSP_HOST_WILL_NOT_SUPPLY_REPLY) +ZBM(ZBS_SET_POLICY_03, EZSP_setPolicy, 0x00 , EZSP_POLL_HANDLER_POLICY, + EZSP_POLL_HANDLER_IGNORE) +ZBM(ZBS_SET_POLICY_04, EZSP_setPolicy, 0x00 , EZSP_MESSAGE_CONTENTS_IN_CALLBACK_POLICY, + EZSP_MESSAGE_TAG_ONLY_IN_CALLBACK) +ZBM(ZBS_SET_POLICY_05, EZSP_setPolicy, 0x00 , EZSP_TC_KEY_REQUEST_POLICY, + EZSP_ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY) +ZBM(ZBS_SET_POLICY_06, EZSP_setPolicy, 0x00 , EZSP_APP_KEY_REQUEST_POLICY, + EZSP_DENY_APP_KEY_REQUESTS) +ZBM(ZBR_SET_POLICY_XX, EZSP_setPolicy, 0x00 , 0x00 ) + + +ZBM(ZBS_NETWORK_INIT, EZSP_networkInit, 0x00 , 0x00, 0x00) +ZBM(ZBR_NETWORK_INIT, EZSP_networkInit, 0x00 , 0x00 ) + + +ZBR(ZBS_FORM_NETWORK, EZSP_formNetwork, 0x00 , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + USE_ZIGBEE_TXRADIO_DBM , + USE_ZIGBEE_CHANNEL , + EMBER_USE_MAC_ASSOCIATION, + 0xFF,0xFF, + 0x00, + 0x00,0x00,0x00,0x00, + ) +ZBM(ZBR_FORM_NETWORK, EZSP_formNetwork, 0x00 , 0x00 ) +ZBM(ZBR_NETWORK_UP, EZSP_stackStatusHandler, 0x00 , EMBER_NETWORK_UP) + + +ZBR(ZBS_LEAVE_NETWORK, EZSP_leaveNetwork, 0x00 ) +ZBM(ZBR_LEAVE_NETWORK, EZSP_leaveNetwork, 0x00 ) + + +ZBM(ZBS_GET_NETW_PARM, EZSP_getNetworkParameters, 0x00 ) +ZBM(ZBR_GET_NETW_PARM, EZSP_getNetworkParameters, 0x00 , 0x00 ) +ZBR(ZBR_CHECK_NETW_PARM, EZSP_getNetworkParameters, 0x00 , + 0x00 , + EMBER_COORDINATOR , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + USE_ZIGBEE_TXRADIO_DBM , + USE_ZIGBEE_CHANNEL , + ) + +ZBM(ZBS_GET_EUI64, EZSP_getEui64, 0x00 ) +ZBM(ZBR_GET_EUI64, EZSP_getEui64, 0x00 ) +ZBM(ZBS_GET_NODEID, EZSP_getNodeId, 0x00 ) +ZBM(ZBR_GET_NODEID, EZSP_getNodeId, 0x00 ) + + +ZBM(ZBS_SET_MCAST_ENTRY, EZSP_setMulticastTableEntry, 0x00 , + 0x00 , 0x00,0x00 , 0x01 , 0x00 ) +ZBM(ZBR_SET_MCAST_ENTRY, EZSP_setMulticastTableEntry, 0x00 , 0x00 ) + + + + +ZBM(ZBS_GET_KEY_NWK, EZSP_getKey, 0x00 , EMBER_CURRENT_NETWORK_KEY) +ZBM(ZBR_GET_KEY_NWK, EZSP_getKey, 0x00 , 0x00 ) + + + + + +uint64_t ezsp_key_low, ezsp_key_high; + +void EZ_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h, int8_t zb_txradio_dbm) { + int8_t txradio = zb_txradio_dbm; + + if (txradio < 0) { txradio = USE_ZIGBEE_TXRADIO_DBM; } + if (txradio > 20) { txradio = USE_ZIGBEE_TXRADIO_DBM; } + ezsp_key_low = zb_precfgkey_l; + ezsp_key_high = zb_precfgkey_h; + + ZBW(ZBS_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 , + Z_B0(EZ_SECURITY_MODE), Z_B1(EZ_SECURITY_MODE), + + 0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0x30, 0x39, + + Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), + Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), + Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), + Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), + 0x00 , + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + ) + + ZBW(ZBS_FORM_NETWORK, EZSP_formNetwork, 0x00 , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid), + Z_B0(zb_pan_id), Z_B1(zb_pan_id), + (uint8_t)txradio , + zb_channel , + EMBER_USE_MAC_ASSOCIATION, + 0xFF,0xFF, + 0x00, + 0x00,0x00,0x00,0x00, + ) + + ZBW(ZBR_CHECK_NETW_PARM, EZSP_getNetworkParameters, 0x00 , + 0x00 , + EMBER_COORDINATOR , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid), + Z_B0(zb_pan_id), Z_B1(zb_pan_id), + (uint8_t)txradio , + zb_channel , + ) +} + +static const Zigbee_Instruction zb_prog[] PROGMEM = { + ZI_LABEL(0) + ZI_CALL(&EZ_Reset_Device, 0) + ZI_LOG(LOG_LEVEL_INFO, kResettingDevice) + + ZI_LABEL(ZIGBEE_LABEL_RESTART) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_RECV_UNEXPECTED(&EZ_Recv_Default) + ZI_WAIT(10500) + + + ZI_LOG(LOG_LEVEL_INFO, kResettingDevice) + ZI_CALL(&EZ_Reset_Device, 0) + ZI_WAIT(100) + ZI_CALL(&EZ_Reset_Device, 1) + + + ZI_WAIT_UNTIL(5000, ZBR_RSTACK) + + + ZI_SEND(ZBS_VERSION) ZI_WAIT_RECV_FUNC(1000, ZBR_VERSION, &EZ_ReceiveCheckVersion) + + + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord) + ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2) + + + + + + + ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) + ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) + + + ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR) + + + ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + + ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + + + ZI_CALL(&EZ_GotoIfResetConfig, ZIGBEE_LABEL_CONFIGURE_EZSP) + + + + + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_BAD_CONFIG) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_BAD_CONFIG) + ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(500, ZBR_NETWORK_INIT) + ZI_WAIT_RECV(1500, ZBR_NETWORK_UP) + + ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK) + ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64) + ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters) + + + ZI_GOTO(ZIGBEE_LABEL_NETWORK_CONFIGURED) + + ZI_LABEL(ZIGBEE_LABEL_BAD_CONFIG) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) + ZI_CALL(EZ_Set_ResetConfig, 1) + ZI_GOTO(ZIGBEE_LABEL_RESTART) + + ZI_LABEL(ZIGBEE_LABEL_CONFIGURE_EZSP) + + ZI_LOG(LOG_LEVEL_INFO, kReconfiguringDevice) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(500, ZBR_SET_SECURITY) + + ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(500, ZBR_FORM_NETWORK) + ZI_WAIT_RECV(5000, ZBR_NETWORK_UP) + + ZI_LABEL(ZIGBEE_LABEL_NETWORK_CONFIGURED) + + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64) + ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NODEID, &EZ_GetNodeId) + + ZI_LOG(LOG_LEVEL_INFO, kZigbeeGroup0) + ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(500, ZBR_SET_MCAST_ENTRY) + + + ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) + ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) + ZI_CALL(&Z_State_Ready, 1) + ZI_CALL(&Z_Load_Devices, 0) +#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES + ZI_CALL(&Z_Query_Bulbs, 0) +#endif + + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) + ZI_WAIT_FOREVER() + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kEZ8) + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + + ZI_LABEL(ZIGBEE_LABEL_ABORT) + ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort) + ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort) + ZI_STOP(ZIGBEE_LABEL_ABORT) +}; + +#endif + +uint8_t ZigbeeGetInstructionSize(uint8_t instr) { + if (instr >= ZGB_INSTR_12_BYTES) { + return 3; + } else if (instr >= ZGB_INSTR_8_BYTES) { + return 2; + } else { + return 1; + } +} + +void ZigbeeGotoLabel(uint8_t label) { + + uint16_t goto_pc = 0xFFFF; + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint8_t cur_instr_len = 1; + + for (uint32_t i = 0; i < ARRAY_SIZE(zb_prog); i += cur_instr_len) { + const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + + + if (ZGB_INSTR_LABEL == cur_instr) { + + if (label == cur_d8) { + + zigbee.pc = i; + zigbee.state_machine = true; + zigbee.state_waiting = false; + return; + } + } + + cur_instr_len = ZigbeeGetInstructionSize(cur_instr); + } + + + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc); + if (ZIGBEE_LABEL_ABORT != label) { + + ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); + zigbee.state_machine = false; + zigbee.active = false; + } +} + +void ZigbeeStateMachine_Run(void) { + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint16_t cur_d16 = 0; + const void* cur_ptr1 = nullptr; + const void* cur_ptr2 = nullptr; + uint32_t now = millis(); + + if (zigbee.state_waiting) { + + if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { + if (!zigbee.state_no_timeout) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); + ZigbeeGotoLabel(zigbee.on_timeout_goto); + } else { + zigbee.state_waiting = false; + } + } + } + + while ((zigbee.state_machine) && (!zigbee.state_waiting)) { + + zigbee.recv_filter = nullptr; + zigbee.recv_func = nullptr; + zigbee.recv_until = false; + zigbee.state_no_timeout = false; + + if (zigbee.pc > ARRAY_SIZE(zb_prog)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); + zigbee.pc = -1; + } + if (zigbee.pc < 0) { + zigbee.state_machine = false; + return; + } + + + const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + cur_d16 = pgm_read_word(&cur_instr_line->i.d16); + if (cur_instr >= ZGB_INSTR_8_BYTES) { + cur_instr_line++; + cur_ptr1 = cur_instr_line->p; + } + if (cur_instr >= ZGB_INSTR_12_BYTES) { + cur_instr_line++; + cur_ptr2 = cur_instr_line->p; + } + + zigbee.pc += ZigbeeGetInstructionSize(cur_instr); + + switch (cur_instr) { + case ZGB_INSTR_NOOP: + case ZGB_INSTR_LABEL: + break; + case ZGB_INSTR_GOTO: + ZigbeeGotoLabel(cur_d8); + break; + case ZGB_INSTR_ON_ERROR_GOTO: + zigbee.on_error_goto = cur_d8; + break; + case ZGB_INSTR_ON_TIMEOUT_GOTO: + zigbee.on_timeout_goto = cur_d8; + break; + case ZGB_INSTR_WAIT: + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + zigbee.state_no_timeout = true; + break; + case ZGB_INSTR_WAIT_FOREVER: + zigbee.next_timeout = 0; + zigbee.state_waiting = true; + break; + case ZGB_INSTR_STOP: + zigbee.state_machine = false; + if (cur_d8) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8); + } + break; + case ZGB_INSTR_CALL: + if (cur_ptr1) { + uint32_t res; + res = (*((ZB_Func)cur_ptr1))(cur_d8); + if (res > 0) { + ZigbeeGotoLabel(res); + continue; + } else if (res == 0) { + + } else if (res == -1) { + + } else { + ZigbeeGotoLabel(zigbee.on_error_goto); + continue; + } + } + break; + case ZGB_INSTR_LOG: + AddLog_P(cur_d8, (char*) cur_ptr1); + break; + case ZGB_INSTR_MQTT_STATE: + { + const char *f_msg = (const char*) cur_ptr1; + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), + cur_d8, f_msg); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + } + break; + case ZGB_INSTR_SEND: +#ifdef USE_ZIGBEE_ZNP + ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 ); +#endif +#ifdef USE_ZIGBEE_EZSP + ZigbeeEZSPSendCmd((uint8_t*) cur_ptr1, cur_d8 ); +#endif + break; + case ZGB_INSTR_WAIT_UNTIL: + zigbee.recv_until = true; + case ZGB_INSTR_WAIT_RECV: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + if (0xFFFF == cur_d16) { + zigbee.next_timeout = 0; + } else { + zigbee.next_timeout = now + cur_d16; + } + zigbee.state_waiting = true; + break; + case ZGB_ON_RECV_UNEXPECTED: + zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; + break; + case ZGB_INSTR_WAIT_UNTIL_CALL: + zigbee.recv_until = true; + case ZGB_INSTR_WAIT_RECV_CALL: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; + if (0xFFFF == cur_d16) { + zigbee.next_timeout = 0; + } else { + zigbee.next_timeout = now + cur_d16; + } + zigbee.state_waiting = true; + break; + } + } +} + + + + +int32_t ZigbeeProcessInput(class SBuffer &buf) { + if (!zigbee.state_machine) { return -1; } + + + bool recv_filter_match = true; + bool recv_prefix_match = false; + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (zigbee.recv_filter_len >= 2) { + recv_prefix_match = false; + if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && + (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { + recv_prefix_match = true; + } + } + + for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { + if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { + recv_filter_match = false; + break; + } + } + } + + + int32_t res = -1; + + + + + + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (!recv_prefix_match) { + res = -1; + } else { + if (recv_filter_match) { + res = 0; + } else { + if (zigbee.recv_until) { + res = -1; + } else { + res = -2; + } + } + } + } else { + res = -1; + } + + if (recv_prefix_match) { + if (zigbee.recv_func) { + res = (*zigbee.recv_func)(res, buf); + } + } + if (-1 == res) { + + if (zigbee.recv_unexpected) { + res = (*zigbee.recv_unexpected)(res, buf); + } + } + + + if (0 == res) { + + zigbee.state_waiting = false; + } else if (res > 0) { + ZigbeeGotoLabel(res); + } else if (-1 == res) { + + + } else { + + ZigbeeGotoLabel(zigbee.on_error_goto); + } + return 0; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +#ifdef USE_ZIGBEE + +#ifdef USE_ZIGBEE_EZSP + + + + +uint8_t ZNP_RSSI2Lqi(int8_t rssi) { + if (rssi < -87) { rssi = -87; } + if (rssi > 10) { rssi = 10; } + return changeUIntScale(rssi + 87, 0, 87+10, 0, 254); +} + + + + + + +int32_t EZ_RSTACK(uint8_t reset_code) { + const char *reason_str; + + switch (reset_code) { + case 0x01: reason_str = PSTR("External"); break; + case 0x02: reason_str = PSTR("Power-on"); break; + case 0x03: reason_str = PSTR("Watchdog"); break; + case 0x06: reason_str = PSTR("Assert"); break; + case 0x09: reason_str = PSTR("Bootloader"); break; + case 0x0B: reason_str = PSTR("Software"); break; + case 0x00: + default: reason_str = PSTR("Unknown"); break; + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"EFR32 booted\",\"RestartReason\":\"%s\"" + ",\"Code\":%d}}"), + ZIGBEE_STATUS_BOOT, reason_str, reset_code); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); +} + + +int32_t EZ_ERROR(uint8_t error_code) { + const char *reason_str; + + switch (error_code) { + case 0x51: reason_str = PSTR("ACK timeout"); break; + default: reason_str = PSTR("Unknown"); break; + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"Failed state\",\"Error\":\"%s\"" + ",\"Code\":%d}}"), + ZIGBEE_STATUS_ABORT, reason_str, error_code); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); +} + +int32_t EZ_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) { + + + uint16_t value = buf.get16(2); + return res; +} +# 89 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t EZ_GetEUI64(int32_t res, class SBuffer &buf) { + localIEEEAddr = buf.get64(2); + return res; +} + + + + +int32_t EZ_GetNodeId(int32_t res, class SBuffer &buf) { + localShortAddr = buf.get8(2); + return res; +} + + + + +int32_t EZ_NetworkParameters(int32_t res, class SBuffer &buf) { + uint8_t node_type = buf.get8(3); +# 116 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" + char hex[20]; + Uint64toHex(localIEEEAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d}}"), + ZIGBEE_STATUS_EZ_INFO, hex, localShortAddr, node_type); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + return res; +} + + + + +int32_t EZ_CheckKeyNWK(int32_t res, class SBuffer &buf) { + uint8_t status = buf.get8(2); + uint16_t bitmask = buf.get16(3); + uint8_t key_type = buf.get8(5); + uint64_t key_low = buf.get64(6); + uint64_t key_high = buf.get64(14); + + if ( (key_type == EMBER_CURRENT_NETWORK_KEY) && + (key_low == ezsp_key_low) && + (key_high == ezsp_key_high) ) { + return 0; + } else { + return -2; + } +} + + + + +int32_t EZ_RouteError(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + uint16_t shortaddr = buf.get16(3); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_ROUTE_ERROR "\":{" + "\"ShortAddr\":\"0x%04X\",\"" D_JSON_ZIGBEE_STATUS "\":%d,\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"}}"), + shortaddr, status, getEmberStatus(status).c_str()); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + return -1; +} + + + + +int32_t EZ_PermitJoinRsp(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + + if (status) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":23,\"Message\":\"Pairing mode error 0x%02X\"}}"), status); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + } + return -1; +} + + + + +void Z_PermitJoinDisable(void) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":20,\"Message\":\"Pairing mode disabled\"}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); +} + + + + + + + +int32_t EZ_MessageSent(int32_t res, const class SBuffer &buf) { + uint8_t message_type = buf.get8(2); + uint16_t dst_addr = buf.get16(3); + uint16_t group_addr = buf.get16(13); + + if ((EMBER_OUTGOING_MULTICAST == message_type) && (0xFFFD == dst_addr)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Sniffing group 0x%04X"), group_addr); + } + return -1; +} + +#endif +# 210 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t Z_EZSPGetEUI64(int32_t res, class SBuffer &buf) { + localIEEEAddr = buf.get64(2); + return res; +} + + + + +int32_t Z_EZSPGetNodeId(int32_t res, class SBuffer &buf) { + localShortAddr = buf.get8(2); + return res; +} + + + + +int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf) { + uint8_t node_type = buf.get8(3); +# 237 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" + char hex[20]; + Uint64toHex(localIEEEAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d}}"), + ZIGBEE_STATUS_EZ_INFO, hex, localShortAddr, node_type); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + return res; +} +# 256 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t ZNP_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { + + + + + + + + Z_IEEEAddress long_adr = buf.get64(3); + Z_ShortAddress short_adr = buf.get16(11); + uint8_t device_type = buf.get8(13); + uint8_t device_state = buf.get8(14); + uint8_t device_associated = buf.get8(15); + + + localIEEEAddr = long_adr; + localShortAddr = short_adr; + + char hex[20]; + Uint64toHex(long_adr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d,\"DeviceState\":%d" + ",\"NumAssocDevices\":%d"), + ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, + device_associated); + + if (device_associated > 0) { + uint idx = 16; + ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); + for (uint32_t i = 0; i < device_associated; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); + idx += 2; + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + return res; +} + +int32_t ZNP_CheckNVWrite(int32_t res, class SBuffer &buf) { + + + + uint8_t status = buf.get8(2); + if ((0x00 == status) || (0x09 == status)) { + return 0; + } else { + return -2; + } +} + +int32_t ZNP_Reboot(int32_t res, class SBuffer &buf) { + + + + uint8_t reason = buf.get8(2); + uint8_t transport_rev = buf.get8(3); + uint8_t product_id = buf.get8(4); + uint8_t major_rel = buf.get8(5); + uint8_t minor_rel = buf.get8(6); + uint8_t hw_rev = buf.get8(7); + const char *reason_str; + + switch (reason) { + case 0: reason_str = PSTR("Power-up"); break; + case 1: reason_str = PSTR("External"); break; + case 2: reason_str = PSTR("Watchdog"); break; + default: reason_str = PSTR("Unknown"); break; + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"CC2530 booted\",\"RestartReason\":\"%s\"" + ",\"MajorRel\":%d,\"MinorRel\":%d}}"), + ZIGBEE_STATUS_BOOT, reason_str, + major_rel, minor_rel); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + +#ifdef USE_ZIGBEE_ZNP +int32_t ZNP_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { +# 357 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" + uint8_t major_rel = buf.get8(4); + uint8_t minor_rel = buf.get8(5); + uint8_t maint_rel = buf.get8(6); + uint32_t revision = buf.get32(7); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" + ",\"MaintRel\":%d,\"Revision\":%d}}"), + ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, + maint_rel, revision); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} +#endif + +#ifdef USE_ZIGBEE_EZSP +int32_t EZ_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { + uint8_t protocol_version = buf.get8(2); + uint8_t stack_type = buf.get8(3); + uint16_t stack_version = buf.get16(4); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Version\":\"%d.%d.%d.%d\",\"Protocol\":%d" + ",\"Stack\":%d}}"), + ZIGBEE_STATUS_EZ_VERSION, + (stack_version & 0xF000) >> 12, + (stack_version & 0x0F00) >> 8, + (stack_version & 0x00F0) >> 4, + stack_version & 0x000F, + protocol_version, + stack_type + ); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + + if (0x08 == protocol_version) { + if ((stack_version & 0xFF00) == 0x6700) { + + ZBW(ZBR_SET_OK2, 0x00, 0x00 , 0x00 ) + } + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + +bool EZ_reset_config = false; + + +int32_t EZ_Set_ResetConfig(uint8_t value) { + EZ_reset_config = value ? true : false; + return 0; +} + + + +int32_t EZ_GotoIfResetConfig(uint8_t value) { + if (EZ_reset_config) { return ZIGBEE_LABEL_CONFIGURE_EZSP; } + else { return 0; } +} + +#endif + + + + + +int32_t Z_SwitchDeviceType(int32_t res, class SBuffer &buf) { + switch (Settings.zb_pan_id) { + case 0xFFFF: return ZIGBEE_LABEL_INIT_ROUTER; + case 0xFFFE: return ZIGBEE_LABEL_INIT_DEVICE; + default: return 0; + } +} + + + + +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { + if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && + (pgm_read_byte(&match[1]) == buf.get8(1)) ) { + return true; + } else { + return false; + } +} + + + + +int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { + + uint8_t duration = buf.get8(2); + uint8_t status_code; + const char* message; + + if (0xFF == duration) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; + message = PSTR("Enable Pairing mode until next boot"); + } else if (duration > 0) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; + message = PSTR("Enable Pairing mode for %d seconds"); + } else { + status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; + message = PSTR("Disable Pairing mode"); + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\""), + status_code); + ResponseAppend_P(message, duration); + ResponseAppend_P(PSTR("\"}}")); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + return -1; +} + + + + +int32_t ZNP_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t logicalType = buf.get8(7); + uint8_t apsFlags = buf.get8(8); + uint8_t MACCapabilityFlags = buf.get8(9); + uint16_t manufacturerCapabilities = buf.get16(10); + uint8_t maxBufferSize = buf.get8(12); + uint16_t maxInTransferSize = buf.get16(13); + uint16_t serverMask = buf.get16(15); + uint16_t maxOutTransferSize = buf.get16(17); + uint8_t descriptorCapabilities = buf.get8(19); + + + if (0 == status) { + uint8_t deviceType = logicalType & 0x7; + const char * deviceTypeStr; + switch (deviceType) { + case 0: deviceTypeStr = PSTR("Coordinator"); break; + case 1: deviceTypeStr = PSTR("Router"); break; + case 2: deviceTypeStr = PSTR("Device"); break; + default: deviceTypeStr = PSTR("Unknown"); break; + } + bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), + ZIGBEE_STATUS_NODE_DESC, deviceTypeStr, + complexDescriptorAvailable ? PSTR("true") : PSTR("false") + ); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + } + + return -1; +} + + + + +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { + +#ifdef USE_ZIGBEE_ZNP + + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t activeEpCount = buf.get8(7); + uint8_t* activeEpList = (uint8_t*) buf.charptr(8); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t status = buf.get8(0); + Z_ShortAddress nwkAddr = buf.get16(1); + uint8_t activeEpCount = buf.get8(3); + uint8_t* activeEpList = (uint8_t*) buf.charptr(4); +#endif + + for (uint32_t i = 0; i < activeEpCount; i++) { + uint8_t ep = activeEpList[i]; + zigbee_devices.addEndpoint(nwkAddr, ep); + if ((i < 4) && (ep < 0x10)) { + zigbee_devices.queueTimer(nwkAddr, 0 , 1500, ep , ep, Z_CAT_EP_DESC, 0 , &Z_SendSimpleDescReq); + } + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"ActiveEndpoints\":["), + ZIGBEE_STATUS_ACTIVE_EP); + for (uint32_t i = 0; i < activeEpCount; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + + Z_SendDeviceInfoRequest(nwkAddr); + + return -1; +} + + +const uint8_t Z_bindings[] PROGMEM = { + Cx0001, Cx0006, Cx0008, Cx0201, Cx0300, + Cx0400, Cx0402, Cx0403, Cx0405, Cx0406, + Cx0500, +}; + +int32_t Z_ClusterToCxBinding(uint16_t cluster) { + uint8_t cx = ClusterToCx(cluster); + for (uint32_t i=0; i= 0) { + bitSet(cluster_map, found_cx); + bitSet(cluster_in_map, found_cx); + } + } + + for (uint32_t i=0; i= 0) { + bitSet(cluster_map, found_cx); + } + } + + + if (bitRead(cluster_map, Z_ClusterToCxBinding(0x0500))) { + + zigbee_devices.queueTimer(shortaddr, 0 , 2000, 0x0500, endpoint, Z_CAT_READ_ATTRIBUTE, 0x0001, &Z_SendSingleAttributeRead); + } + + + for (uint32_t i=0; i 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numInIndex + i*2)); + } + ResponseAppend_P(PSTR("],\"OutClusters\":[")); + for (uint32_t i = 0; i < numOutCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numOutIndex + i*2)); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + + return -1; +} + + + + + +int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + uint8_t status = buf.get8(2); + Z_IEEEAddress ieeeAddr = buf.get64(3); + Z_ShortAddress nwkAddr = buf.get16(11); + + +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t status = buf.get8(0); + Z_IEEEAddress ieeeAddr = buf.get64(1); + Z_ShortAddress nwkAddr = buf.get16(9); + + +#endif + + if (0 == status) { + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""), nwkAddr, hex); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR("\"}}")); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + } + return -1; +} + + + + +int32_t ZNP_DataConfirm(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + uint8_t endpoint = buf.get8(3); + + + if (status) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), endpoint, status, getZigbeeStatusMessage(status).c_str()); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + } + + return -1; +} +# 761 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t ZNP_ReceiveStateChange(int32_t res, const class SBuffer &buf) { + uint8_t state = buf.get8(2); + const char * msg = nullptr; + + switch (state) { + case ZDO_DEV_NWK_DISC: + msg = PSTR("Scanning Zigbee network"); + break; + case ZDO_DEV_NWK_JOINING: + case ZDO_DEV_NWK_REJOIN: + msg = PSTR("Joining a PAN"); + break; + case ZDO_DEV_END_DEVICE_UNAUTH: + msg = PSTR("Joined, not yet authenticated"); + break; + case ZDO_DEV_END_DEVICE: + msg = PSTR("Started as device"); + break; + case ZDO_DEV_ROUTER: + msg = PSTR("Started as router"); + break; + case ZDO_DEV_ZB_COORD: + msg = PSTR("Started as coordinator"); + break; + case ZDO_DEV_NWK_ORPHAN: + msg = PSTR("Device has lost its parent"); + break; + }; + + if (msg) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"NewState\":%d,\"Message\":\"%s\"}}"), + ZIGBEE_STATUS_SCANNING, state, msg + ); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + } + + if ((ZDO_DEV_END_DEVICE == state) || (ZDO_DEV_ROUTER == state) || (ZDO_DEV_ZB_COORD == state)) { + return 0; + } else { + return -1; + } +} + + + + + + +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + + Z_ShortAddress nwkAddr = buf.get16(4); + Z_IEEEAddress ieeeAddr = buf.get64(6); + uint8_t capabilities = buf.get8(14); +#endif +#ifdef USE_ZIGBEE_EZSP + + Z_ShortAddress nwkAddr = buf.get16(0); + Z_IEEEAddress ieeeAddr = buf.get64(2); + uint8_t capabilities = buf.get8(10); +#endif + + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), + ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, + (capabilities & 0x04) ? PSTR("true") : PSTR("false"), + (capabilities & 0x08) ? PSTR("true") : PSTR("false"), + (capabilities & 0x40) ? PSTR("true") : PSTR("false") + ); + + uint32_t wait_ms = 2000; + Z_Query_Bulb(nwkAddr, wait_ms); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + Z_SendActiveEpReq(nwkAddr); + return -1; +} + + + + + +int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_IEEEAddress ieeeAddr = buf.get64(4); + Z_ShortAddress parentNw = buf.get16(12); + + zigbee_devices.updateDevice(srcAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"ParentNetwork\":\"0x%04X\"}}"), + ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw + ); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + return -1; +} + + + + +int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + String msg = getZigbeeStatusMessage(status); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t status = buf.get8(0); + Z_ShortAddress nwkAddr = buf.get16(buf.len()-2); + String msg = getZDPStatusMessage(status); +#endif + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), nwkAddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), status, msg.c_str()); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + + return -1; +} + + + + +int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + String msg = getZigbeeStatusMessage(status); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t status = buf.get8(0); + Z_ShortAddress nwkAddr = buf.get16(buf.len()-2); + String msg = getZDPStatusMessage(status); +#endif + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), nwkAddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), status, msg.c_str()); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + + return -1; +} + + + +int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + uint16_t shortaddr = buf.get16(2); + uint8_t status = buf.get8(4); + uint8_t bind_total = buf.get8(5); + uint8_t bind_start = buf.get8(6); + uint8_t bind_len = buf.get8(7); + const size_t prefix_len = 8; +#endif +#ifdef USE_ZIGBEE_EZSP + uint16_t shortaddr = buf.get16(buf.len()-2); + uint8_t status = buf.get8(0); + uint8_t bind_total = buf.get8(1); + uint8_t bind_start = buf.get8(2); + uint8_t bind_len = buf.get8(3); + const size_t prefix_len = 4; +#endif + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND_STATE "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + ",\"BindingsTotal\":%d" + ",\"BindingsStart\":%d" + ",\"Bindings\":[" + ), status, getZigbeeStatusMessage(status).c_str(), bind_total, bind_start + 1); + + uint32_t idx = prefix_len; + for (uint32_t i = 0; i < bind_len; i++) { + if (idx + 14 > buf.len()) { break; } + + + uint8_t srcep = buf.get8(idx + 8); + uint16_t cluster = buf.get16(idx + 9); + uint8_t addrmode = buf.get8(idx + 11); + uint16_t group = 0x0000; + uint64_t dstaddr = 0; + uint8_t dstep = 0x00; + if (Z_Addr_Group == addrmode) { + group = buf.get16(idx + 12); + idx += 14; + } else if (Z_Addr_IEEEAddress == addrmode) { + dstaddr = buf.get64(idx + 12); + dstep = buf.get8(idx + 20); + idx += 21; + } else { + + break; + } + + if (i > 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"Cluster\":\"0x%04X\",\"Endpoint\":%d,"), cluster, srcep); + if (Z_Addr_Group == addrmode) { + ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group); + } else if (Z_Addr_IEEEAddress == addrmode) { + char hex[20]; + Uint64toHex(dstaddr, hex, 64); + ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep); + } + } + + ResponseAppend_P(PSTR("]}}")); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE)); + + return -1; +} + +#ifdef USE_ZIGBEE_EZSP + + + + +int32_t EZ_ParentAnnceRsp(int32_t res, const class SBuffer &buf, bool rsp) { + size_t prefix_len; + uint8_t status; + uint8_t num_children; + uint16_t shortaddr = buf.get16(buf.len()-2); + if (rsp) { + status = buf.get8(0); + num_children = buf.get8(1); + prefix_len = 2; + } else { + status = 0; + num_children = buf.get8(0); + prefix_len = 1; + } + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PARENT "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + if (rsp) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + ), status, getZigbeeStatusMessage(status).c_str()); + } + ResponseAppend_P(PSTR(",\"Children\":%d" + ",\"ChildInfo\":[" + ), num_children); + + uint32_t idx = prefix_len; + for (uint32_t i = 0; i < num_children; i++) { + if (idx + 8 > buf.len()) { break; } + + uint64_t child_ieee = buf.get64(idx); + idx += 8; + + if (i > 0) { + ResponseAppend_P(PSTR(",")); + } + char hex[20]; + Uint64toHex(child_ieee, hex, 64); + ResponseAppend_P(PSTR("\"0x%s\""), hex); + } + + ResponseAppend_P(PSTR("]}}")); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE)); + + return -1; +} +#endif +# 1070 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +void Z_SendIEEEAddrReq(uint16_t shortaddr) { +#ifdef USE_ZIGBEE_ZNP + uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; + + ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t IEEEAddrReq[] = { Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; + EZ_SendZDO(shortaddr, ZDO_IEEE_addr_req, IEEEAddrReq, sizeof(IEEEAddrReq)); +#endif +} + + + + +void Z_SendActiveEpReq(uint16_t shortaddr) { +#ifdef USE_ZIGBEE_ZNP + uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t ActiveEpReq[] = { Z_B0(shortaddr), Z_B1(shortaddr) }; + EZ_SendZDO(shortaddr, ZDO_Active_EP_req, ActiveEpReq, sizeof(ActiveEpReq)); +#endif +} + + + + + +void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { +#ifdef USE_ZIGBEE_ZNP + uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr), + endpoint }; + ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq)); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t SimpleDescReq[] = { Z_B0(shortaddr), Z_B1(shortaddr), endpoint }; + EZ_SendZDO(shortaddr, ZDO_SIMPLE_DESC_REQ, SimpleDescReq, sizeof(SimpleDescReq)); +#endif +} +# 1121 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +void Z_SendDeviceInfoRequest(uint16_t shortaddr) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + if (0x00 == endpoint) { endpoint = 0x01; } + uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); + + uint8_t InfoReq[] = { 0x04, 0x00, 0x05, 0x00 }; + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, + 0x0000 , + endpoint, + ZCL_READ_ATTRIBUTES, + 0x0000, + false , + true , + false , + transacid, + InfoReq, sizeof(InfoReq) + })); +} + + + + +void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); + uint8_t InfoReq[2] = { Z_B0(value), Z_B1(value) }; + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, + cluster , + endpoint, + ZCL_READ_ATTRIBUTES, + 0x0000, + false , + true , + false , + transacid, + InfoReq, sizeof(InfoReq) + })); +} + + + + +void Z_AutoBind(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(shortaddr); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `ZbBind {\"Device\":\"0x%04X\",\"Endpoint\":%d,\"Cluster\":\"0x%04X\"}`"), + shortaddr, endpoint, cluster); +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_BIND_REQ); + buf.add16(shortaddr); + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + buf.add8(Z_Addr_IEEEAddress); + buf.add64(localIEEEAddr); + buf.add8(0x01); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif + +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(24); + + + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + buf.add8(Z_Addr_IEEEAddress); + buf.add64(localIEEEAddr); + buf.add8(0x01); + + EZ_SendZDO(shortaddr, ZDO_BIND_REQ, buf.buf(), buf.len()); +#endif +} + + + + + + +typedef struct Z_autoAttributeReporting_t { + uint16_t cluster; + uint16_t attr_id; + uint16_t min_interval; + uint16_t max_interval; + float report_change; +} Z_autoAttributeReporting_t; + + +const Z_autoAttributeReporting_t Z_autoAttributeReporting[] PROGMEM = { + { 0x0001, 0x0020, 15*60, 15*60, 0.1 }, + { 0x0001, 0x0021, 15*60, 15*60, 1 }, + { 0x0006, 0x0000, 1, 60*60, 0 }, + { 0x0201, 0x0000, 60, 60*10, 0.5 }, + { 0x0201, 0x0008, 60, 60*10, 10 }, + { 0x0201, 0x0012, 60, 60*10, 0.5 }, + { 0x0008, 0x0000, 1, 60*60, 5 }, + { 0x0300, 0x0000, 1, 60*60, 5 }, + { 0x0300, 0x0001, 1, 60*60, 5 }, + { 0x0300, 0x0003, 1, 60*60, 100 }, + { 0x0300, 0x0004, 1, 60*60, 100 }, + { 0x0300, 0x0007, 1, 60*60, 5 }, + { 0x0300, 0x0008, 1, 60*60, 0 }, + { 0x0400, 0x0000, 10, 60*60, 5 }, + { 0x0402, 0x0000, 30, 60*60, 0.2 }, + { 0x0403, 0x0000, 30, 60*60, 1 }, + { 0x0405, 0x0000, 30, 60*60, 1.0 }, + { 0x0406, 0x0000, 10, 60*60, 0 }, + { 0x0500, 0x0002, 1, 60*60, 0 }, +}; + + + + + + +void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + + SBuffer buf(12*6); + + + Response_P(PSTR("ZbSend {\"Device\":\"0x%04X\",\"Config\":{"), shortaddr); + + boolean comma = false; + for (uint32_t i=0; i 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), mqtt_data); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, + cluster , + endpoint, + ZCL_CONFIGURE_REPORTING, + 0x0000, + false , + false , + false , + zigbee_devices.getNextSeqNumber(shortaddr), + buf.buf(), buf.len() + })); + } +} + + + + + +#ifdef USE_ZIGBEE_EZSP +int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf) { + uint16_t srcAddr = buf.get16(2); + uint64_t ieeeAddr = buf.get64(4); + uint8_t status = buf.get8(12); + uint8_t decision = buf.get8(13); + uint16_t parentNw = buf.get16(14); + + if (EMBER_DEVICE_LEFT != status) { + zigbee_devices.updateDevice(srcAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"ParentNetwork\":\"0x%04X\"" + ",\"Status\":%d,\"Decision\":%d" + "}}"), + ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw, + status, decision + ); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + } + return -1; +} +#endif + + + + + +void Z_IncomingMessage(class ZCLFrame &zcl_received) { + uint16_t srcaddr = zcl_received.getSrcAddr(); + uint16_t groupid = zcl_received.getGroupAddr(); + uint16_t clusterid = zcl_received.getClusterId(); + uint8_t linkquality = zcl_received.getLinkQuality(); + uint8_t srcendpoint = zcl_received.getSrcEndpoint(); + linkquality = linkquality != 0xFF ? linkquality : 0xFE; + + bool defer_attributes = false; + + + zcl_received.log(); + + zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); + zigbee_devices.setLastSeenNow(srcaddr); + + char shortaddr[8]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); + + Z_attribute_list attr_list; + attr_list.lqi = linkquality; + attr_list.src_ep = srcendpoint; + if (groupid) { + attr_list.group_id = groupid; + } + + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseResponse(); + } else { + + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseReportAttributes(attr_list); + if (clusterid) { defer_attributes = true; } + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadAttributesResponse(attr_list); + if (clusterid) { defer_attributes = true; } + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseReadAttributes(attr_list); + + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_REPORTING_CONFIGURATION_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadConfigAttributes(attr_list); + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_CONFIGURE_REPORTING_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseConfigAttributes(attr_list); + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(attr_list); + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str()); + + + if (srcaddr == localShortAddr) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "loopback message, ignoring")); + return; + } + + zcl_received.generateSyntheticAttributes(attr_list); + zcl_received.computeSyntheticAttributes(attr_list); + zcl_received.generateCallBacks(attr_list); + zcl_received.postProcessAttributes(srcaddr, attr_list); + + + zigbee_devices.resetTimersForDevice(srcaddr, 0 , Z_CAT_REACHABILITY); + zigbee_devices.setReachable(srcaddr, true); + + if (defer_attributes) { + + if (zigbee_devices.jsonIsConflict(srcaddr, attr_list)) { + + zigbee_devices.jsonPublishFlush(srcaddr); + } + zigbee_devices.jsonAppend(srcaddr, attr_list); + zigbee_devices.setTimer(srcaddr, 0 , USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, Z_CAT_READ_ATTR, 0, &Z_PublishAttributes); + } else { + + zigbee_devices.jsonPublishNow(srcaddr, attr_list); + } + } +} + + +#ifdef USE_ZIGBEE_EZSP + + + + + +void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload, size_t payload_len) { + SBuffer buf(payload_len + 22); + uint8_t seq = zigbee_devices.getNextSeqNumber(0x0000); + + if (shortaddr < 0xFFFC) { + + buf.add16(EZSP_sendUnicast); + + buf.add8(EMBER_OUTGOING_DIRECT); + buf.add16(shortaddr); + + buf.add16(0x0000); + buf.add16(cmd); + buf.add8(0); + buf.add8(0); + buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); + buf.add16(0x0000); + buf.add8(seq); + + buf.add8(0x01); + buf.add8(payload_len + 1); + buf.add8(seq); + buf.addBuffer(payload, payload_len); + } else { + + buf.add16(EZSP_sendBroadcast); + buf.add16(shortaddr); + + buf.add16(0x0000); + buf.add16(cmd); + buf.add8(0); + buf.add8(0); + buf.add16(0x00); + buf.add16(0x0000); + buf.add8(seq); + + buf.add8(0x1E); + buf.add8(0x01); + buf.add8(payload_len + 1); + buf.add8(seq); + buf.addBuffer(payload, payload_len); + } + + ZigbeeEZSPSendCmd(buf.buf(), buf.len()); +} + + + + + +int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { + uint8_t msgtype = buf.get8(2); + bool wasbroadcast = (msgtype >= EMBER_INCOMING_MULTICAST) && (msgtype <= EMBER_INCOMING_BROADCAST_LOOPBACK); + uint16_t profileid = buf.get16(3); + uint16_t clusterid = buf.get16(5); + uint8_t srcendpoint = buf.get8(7); + uint8_t dstendpoint = buf.get8(8); + uint16_t apsoptions = buf.get16(9); + bool securityuse = (apsoptions & EMBER_APS_OPTION_ENCRYPTION) ? true : false; + uint16_t groupid = buf.get16(11); + uint8_t seqnumber = buf.get8(13); + int8_t linkrssi = buf.get8(15); + uint8_t linkquality = ZNP_RSSI2Lqi(linkrssi); + uint16_t srcaddr = buf.get16(16); + + + + + + if ((0x0000 == profileid) && (0x00 == srcendpoint)) { + + + zigbee_devices.setLQI(srcaddr, linkquality); + zigbee_devices.setLastSeenNow(srcaddr); + + + SBuffer zdo_buf(buf.get8(20) - 1 + 2); + zdo_buf.addBuffer(buf.buf(22), buf.get8(20) - 1); + zdo_buf.add16(srcaddr); + switch (clusterid) { + case ZDO_Device_annce: + return Z_ReceiveEndDeviceAnnonce(res, zdo_buf); + case ZDO_Active_EP_rsp: + return Z_ReceiveActiveEp(res, zdo_buf); + case ZDO_IEEE_addr_rsp: + return Z_ReceiveIEEEAddr(res, zdo_buf); + case ZDO_Simple_Desc_rsp: + return Z_ReceiveSimpleDesc(res, zdo_buf); + case ZDO_Bind_rsp: + return Z_BindRsp(res, zdo_buf); + case ZDO_Unbind_rsp: + return Z_UnbindRsp(res, zdo_buf); + case ZDO_Mgmt_Bind_rsp: + return Z_MgmtBindRsp(res, zdo_buf); + case ZDO_Parent_annce: + return EZ_ParentAnnceRsp(res, zdo_buf, false); + case ZDO_Parent_annce_rsp: + return EZ_ParentAnnceRsp(res, zdo_buf, true); + default: + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Internal ZDO message 0x%04X sent from 0x%04X %s"), clusterid, srcaddr, wasbroadcast ? PSTR("(broadcast)") : ""); + break; + } + } else { + bool defer_attributes = false; + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 21, buf.get8(20), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber); + + Z_IncomingMessage(zcl_received); + } + return -1; +} + + + + + + +int32_t EZ_Reset_Device(uint8_t value) { + + + + + + if (PinUsed(GPIO_ZIGBEE_RST)) { + digitalWrite(Pin(GPIO_ZIGBEE_RST), value); + } else { + + if (value) { + uint8_t ezsp_reset[1] = { 0xC0 }; + ZigbeeEZSPSendRaw(ezsp_reset, sizeof(ezsp_reset), true); + } + } + return 0; +} + + + + + +int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf) { + + if (zigbee.init_phase) { + + return -1; + } else { + uint16_t ezsp_command_index = buf.get16(0); + + switch (ezsp_command_index) { + case EZSP_incomingMessageHandler: + return EZ_IncomingMessage(res, buf); + break; + case EZSP_trustCenterJoinHandler: + return EZ_ReceiveTCJoinHandler(res, buf); + break; + case EZSP_incomingRouteErrorHandler: + return EZ_RouteError(res, buf); + break; + case EZSP_permitJoining: + return EZ_PermitJoinRsp(res, buf); + break; + case EZSP_messageSentHandler: + return EZ_MessageSent(res, buf); + break; + } + return -1; + } +} + +#endif + + + + + + +void Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + zigbee_devices.jsonPublishFlush(shortaddr); +} + + + + + +#ifdef USE_ZIGBEE_ZNP + + + + + + +int32_t ZNP_Reset_Device(uint8_t value) { + + + + + + if (PinUsed(GPIO_ZIGBEE_RST)) { + digitalWrite(Pin(GPIO_ZIGBEE_RST), value); + } else { + + if (value) { + + ZigbeeZNPFlush(); + ZigbeeZNPSend(ZBS_RESET, sizeof(ZBS_RESET)); + } + } + return 0; +} + +int32_t ZNP_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + uint16_t srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + + uint8_t seqnumber = buf.get8(17); + + bool defer_attributes = false; + + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber); + + Z_IncomingMessage(zcl_received); + + return -1; +} + +#endif + + + + + + +#ifdef USE_ZIGBEE_ZNP + + +typedef struct Z_Dispatcher { + uint8_t match[2]; + ZB_RecvMsgFunc func; +} Z_Dispatcher; + + +const Z_Dispatcher Z_DispatchTable[] PROGMEM = { + { { Z_AREQ | Z_AF, AF_DATA_CONFIRM }, &ZNP_DataConfirm }, + { { Z_AREQ | Z_AF, AF_INCOMING_MSG }, &ZNP_ReceiveAfIncomingMessage }, + + { { Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND }, &Z_ReceiveEndDeviceAnnonce }, + { { Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND }, &ZNP_ReceiveTCDevInd }, + { { Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND }, &ZNP_ReceivePermitJoinStatus }, + { { Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP }, &ZNP_ReceiveNodeDesc }, + { { Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP }, &Z_ReceiveActiveEp }, + { { Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP}, &Z_ReceiveSimpleDesc}, + { { Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP }, &Z_ReceiveIEEEAddr }, + { { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &Z_BindRsp }, + { { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &Z_UnbindRsp }, + { { Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP }, &Z_MgmtBindRsp }, +}; + + + + + +int32_t ZNP_Recv_Default(int32_t res, const class SBuffer &buf) { + + if (zigbee.init_phase) { + + return -1; + } else { + for (uint32_t i = 0; i < ARRAY_SIZE(Z_DispatchTable); i++) { + if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { + (*Z_DispatchTable[i].func)(res, buf); + } + } + return -1; + } +} + +#endif +# 1731 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t Z_Load_Devices(uint8_t value) { + + loadZigbeeDevices(); + return 0; +} + + + + +void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { + const uint32_t inter_message_ms = 100; + + if (0 <= zigbee_devices.getHueBulbtype(shortaddr)) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + + if (endpoint) { + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0006, endpoint, Z_CAT_READ_CLUSTER, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0008, endpoint, Z_CAT_READ_CLUSTER, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0300, endpoint, Z_CAT_READ_CLUSTER, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); + wait_ms += 1000; + } + } +} + + + + +int32_t Z_Query_Bulbs(uint8_t value) { + + uint32_t wait_ms = 1000; + for (uint32_t i = 0; i < zigbee_devices.devicesSize(); i++) { + const Z_Device &device = zigbee_devices.devicesAt(i); + Z_Query_Bulb(device.shortaddr, wait_ms); + } + return 0; +} + + + + +int32_t Z_State_Ready(uint8_t value) { + zigbee.init_phase = false; + return 0; +} + + + + + + +void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) { + Z_attribute_list attr_list; + + for (uint32_t i=0; i 0xFEFF) ? uxy[i] : 0xFEFF; + } + if (0x0000 == attr_id) { attr.setUInt(changeUIntScale(hue, 0, 360, 0, 254)); } + if (0x0001 == attr_id) { attr.setUInt(changeUIntScale(sat, 0, 255, 0, 254)); } + if (0x0003 == attr_id) { attr.setUInt(uxy[0]); } + if (0x0004 == attr_id) { attr.setUInt(uxy[1]); } + if (0x0007 == attr_id) { attr.setUInt(LightGetColorTemp()); } + } + break; +#endif + case 0x000A0000: + attr.setUInt((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time); + break; + case 0x000AFF00: + attr.setUInt(Rtc.utc_time); + break; + case 0x000A0001: + attr.setUInt((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00); + break; + case 0x000A0002: + attr.setUInt(Settings.toffset[0] * 60); + break; + case 0x000A0007: + attr.setUInt(Settings.toffset[0] * 60 + ((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time)); + break; + } + if (!attr.isNone()) { + Z_parseAttributeKey(attr); + attr_list.addAttribute(_cluster_id, attr_id) = attr; + } + } + + SBuffer buf(200); + for (const auto & attr : attr_list) { + if (!ZbAppendWriteBuf(buf, attr, true)) { + return; + } + } + + if (buf.len() > 0) { + + + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Auto-responder: ZbSend {\"Device\":\"0x%04X\"" + ",\"Cluster\":\"0x%04X\"" + ",\"Endpoint\":%d" + ",\"Response\":%s}" + ), + _srcaddr, _cluster_id, _srcendpoint, + attr_list.toString().c_str()); + + + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + _srcaddr, + 0x0000, + _cluster_id , + _srcendpoint, + ZCL_READ_ATTRIBUTES_RESPONSE, + 0x0000, + false , + false , + true , + _transact_seq, + buf.getBuffer(), buf.len() + })); + } +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9_serial.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9_serial.ino" +#ifdef USE_ZIGBEE + +#ifdef USE_ZIGBEE_ZNP +const uint32_t ZIGBEE_BUFFER_SIZE = 256; +const uint8_t ZIGBEE_SOF = 0xFE; +const uint8_t ZIGBEE_SOF_ALT = 0xFF; +#endif + +#ifdef USE_ZIGBEE_EZSP +const uint32_t ZIGBEE_BUFFER_SIZE = 256; +const uint8_t ZIGBEE_EZSP_CANCEL = 0x1A; +const uint8_t ZIGBEE_EZSP_EOF = 0x7E; +const uint8_t ZIGBEE_EZSP_ESCAPE = 0x7D; + +const uint32_t ZIGBEE_LED_RECEIVE = 0; +const uint32_t ZIGBEE_LED_SEND = 0; + +class EZSP_Serial_t { +public: + uint8_t to_send = 0; + uint8_t to_end = 0; + uint8_t to_ack = 0; + uint8_t from_ack = 0; + uint8_t ezsp_seq = 0; + SBuffer *to_packets[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +}; + + +EZSP_Serial_t EZSP_Serial; + + + + +const uint32_t Z_LED_STATUS_ON_MILLIS = 50; +bool Z_LedStatusSet(bool onoff) { + static bool led_status_on = false; + static uint32_t led_on_time = 0; + + if (onoff) { + SetLedPowerIdx(ZIGBEE_LED_RECEIVE, 1); + led_status_on = true; + led_on_time = millis(); + } else if ((led_status_on) && (TimePassedSince(led_on_time) >= Z_LED_STATUS_ON_MILLIS)) { + SetLedPowerIdx(ZIGBEE_LED_RECEIVE, 0); + led_status_on = false; + } + return led_status_on; +} + +#endif + +#include +TasmotaSerial *ZigbeeSerial = nullptr; + + + + + +void ZigbeeInputLoop(void) { + +#ifdef USE_ZIGBEE_ZNP + static uint32_t zigbee_polling_window = 0; + static uint8_t fcs = ZIGBEE_SOF; + static uint32_t zigbee_frame_len = 5; +# 92 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9_serial.ino" + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + + + if (0 == zigbee_buffer->len()) { + zigbee_frame_len = 5; + fcs = ZIGBEE_SOF; + + + + if (ZIGBEE_SOF_ALT == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); + zigbee_in_byte = ZIGBEE_SOF; + } + } + + if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); + continue; + } + + if (zigbee_buffer->len() < zigbee_frame_len) { + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); + fcs ^= zigbee_in_byte; + } + + if (zigbee_buffer->len() >= zigbee_frame_len) { + zigbee_polling_window = 0; + break; + } + + + if (02 == zigbee_buffer->len()) { + + uint8_t len_byte = zigbee_buffer->get8(1); + if (len_byte > 250) len_byte = 250; + + zigbee_frame_len = len_byte + 5; + } + } + + if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + char hex_char[(zigbee_buffer->len() * 2) + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + + + if (zigbee_buffer->len() != zigbee_frame_len) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); + } else if (0x00 != fcs) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); + } else { + + + + SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); + + ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR)); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + + ZigbeeProcessInput(znp_buffer); + } + zigbee_buffer->setLen(0); + } +#endif + +#ifdef USE_ZIGBEE_EZSP + static uint32_t zigbee_polling_window = 0; + static bool escape = false; + bool frame_complete = false; + + + + + + Z_LedStatusSet(false); + + while (ZigbeeSerial->available()) { + Z_LedStatusSet(true); + + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + + + + + + + + if ((0x11 == zigbee_in_byte) || (0x13 == zigbee_in_byte)) { + continue; + } + + if (ZIGBEE_EZSP_ESCAPE == zigbee_in_byte) { + + escape = true; + continue; + } + + if (ZIGBEE_EZSP_CANCEL == zigbee_in_byte) { + + zigbee_buffer->setLen(0); + escape = false; + frame_complete = false; + continue; + } + + if (ZIGBEE_EZSP_EOF == zigbee_in_byte) { + + frame_complete = true; + break; + } + + if (zigbee_buffer->len() < ZIGBEE_BUFFER_SIZE) { + if (escape) { + + zigbee_in_byte ^= 0x20; + escape = false; + } + + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); + } + } + + uint32_t frame_len = zigbee_buffer->len(); + if (frame_complete || (frame_len && (millis() > (zigbee_polling_window + ZIGBEE_POLLING)))) { + char hex_char[frame_len * 2 + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + + if ((frame_complete) && (frame_len >= 3)) { + + + uint16_t crc = 0xFFFF; + + for (uint32_t i=0; iget8(i) << 8); + for (uint32_t i=0; i<8; i++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } + } + + uint16_t crc_received = zigbee_buffer->get8(frame_len - 2) << 8 | zigbee_buffer->get8(frame_len - 1); + + + if (crc_received != crc) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %s"), crc_received, crc, hex_char); + } else { + + SBuffer ezsp_buffer = zigbee_buffer->subBuffer(0, frame_len - 2); + + + if (0 == (ezsp_buffer.get8(0) & 0x80)) { + + uint8_t rand = 0x42; + for (uint32_t i=1; i> 1) ^ 0xB8; } + else { rand = (rand >> 1); } + } + } + + ToHex_P((unsigned char*)ezsp_buffer.getBuffer(), ezsp_buffer.len(), hex_char, sizeof(hex_char)); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "2\":\"%s\"}"), hex_char); + + ZigbeeProcessInputRaw(ezsp_buffer); + } + } else { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %d"), hex_char); + } + zigbee_buffer->setLen(0); + escape = false; + frame_complete = false; + } + +#endif + +} + + + + +void ZigbeeInitSerial(void) +{ + + zigbee.active = false; + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX)); + + ZigbeeSerial = new TasmotaSerial(Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX), seriallog_level ? 1 : 2, 0, 256); + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); + } else { + + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); + + } + + if (PinUsed(GPIO_ZIGBEE_RST)) { + pinMode(Pin(GPIO_ZIGBEE_RST), OUTPUT); + digitalWrite(Pin(GPIO_ZIGBEE_RST), 1); + } + + zigbee.active = true; + zigbee.init_phase = true; + zigbee.state_machine = true; + ZigbeeSerial->flush(); + } + +} + +#ifdef USE_ZIGBEE_ZNP + + +void ZigbeeZNPFlush(void) { + if (ZigbeeSerial) { + for (uint32_t i = 0; i < 256; i++) { + ZigbeeSerial->write(0xFF); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " 0xFF x 255")); + } +} + +void ZigbeeZNPSend(const uint8_t *msg, size_t len) { + if ((len < 2) || (len > 252)) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; + + if (ZigbeeSerial) { + uint8_t fcs = data_len; + + ZigbeeSerial->write(ZIGBEE_SOF); + + ZigbeeSerial->write(data_len); + + for (uint32_t i = 0; i < len; i++) { + uint8_t b = pgm_read_byte(msg + i); + ZigbeeSerial->write(b); + fcs ^= b; + + } + ZigbeeSerial->write(fcs); + + } + + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} + + + + + +void CmndZbZNPSendOrReceive(bool send) +{ + if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + } else { + + ZigbeeProcessInput(buf); + } + } + ResponseCmndDone(); +} + + +void CmndZbZNPReceive(void) +{ + CmndZbZNPSendOrReceive(false); +} + +void CmndZbZNPSend(void) +{ + CmndZbZNPSendOrReceive(true); +} + +#endif + +#ifdef USE_ZIGBEE_EZSP + + +void ZigbeeEZSPSend_Out(uint8_t out_byte) { + switch (out_byte) { + case 0x7E: + case 0x11: + case 0x13: + case 0x18: + case 0x1A: + case 0x7D: + + ZigbeeSerial->write(ZIGBEE_EZSP_ESCAPE); + ZigbeeSerial->write(out_byte ^ 0x20); + break; + default: + ZigbeeSerial->write(out_byte); + break; + } +} +# 443 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9_serial.ino" +void ZigbeeEZSPSendRaw(const uint8_t *msg, size_t len, bool send_cancel) { + if ((len < 1) || (len > 252)) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEE_EZSP_SENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; + + + Z_LedStatusSet(true); + + if (ZigbeeSerial) { + if (send_cancel) { + ZigbeeSerial->write(ZIGBEE_EZSP_CANCEL); + } + + bool data_frame = (0 == (msg[0] & 0x80)); + uint8_t rand = 0x42; + uint16_t crc = 0xFFFF; + + for (uint32_t i=0; i 0)) { + out_byte ^= rand; + if (rand & 1) { rand = (rand >> 1) ^ 0xB8; } + else { rand = (rand >> 1); } + } + + + crc = crc ^ ((uint16_t)out_byte << 8); + for (uint32_t i=0; i<8; i++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } + + + ZigbeeEZSPSend_Out(out_byte); + } + + ZigbeeEZSPSend_Out(crc >> 8); + ZigbeeEZSPSend_Out(crc & 0xFF); + + + ZigbeeSerial->write(ZIGBEE_EZSP_EOF); + } + + + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT_RAW " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} + + + +void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len) { + char hex_char[len*2 + 2]; + ToHex_P(msg, len, hex_char, sizeof(hex_char)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbEZSPSend %s"), hex_char); + + SBuffer cmd(len+3); + + cmd.add8(EZSP_Serial.ezsp_seq++); + cmd.add8(0x00); + cmd.add8(0x01); + cmd.addBuffer(msg, len); + + + ZigbeeEZSPSendDATA(cmd.getBuffer(), cmd.len()); +} + + +void ZigbeeEZSPSendDATA_frm(bool send_cancel, uint8_t to_frm, uint8_t from_ack) { + SBuffer *buf = EZSP_Serial.to_packets[to_frm]; + if (!buf) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Buffer for packet %d is not allocated"), EZSP_Serial.to_send); + return; + } + + uint8_t control_byte = ((to_frm & 0x07) << 4) + (from_ack & 0x07); + buf->set8(0, control_byte); + + ZigbeeEZSPSendRaw(buf->getBuffer(), buf->len(), send_cancel); +} + + +void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len) { + + SBuffer *buf = new SBuffer(len+1); + buf->add8(0x00); + buf->addBuffer(msg, len); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: adding packet to_send, to_ack:%d, to_send:%d, to_end:%d"), + EZSP_Serial.to_ack, EZSP_Serial.to_send, EZSP_Serial.to_end); + uint8_t to_frm = EZSP_Serial.to_end; + if (EZSP_Serial.to_packets[to_frm]) { + delete EZSP_Serial.to_packets[to_frm]; + EZSP_Serial.to_packets[to_frm] = nullptr; + } + EZSP_Serial.to_packets[to_frm] = buf; + EZSP_Serial.to_end = (to_frm + 1) & 0x07; + + + + + + +} + + +int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) { + + + + uint16_t frame_control = buf.get16(1); + bool truncated = frame_control & 0x02; + bool overflow = frame_control & 0x01; + bool callbackPending = frame_control & 0x04; + bool security_enabled = frame_control & 0x8000; + if (truncated || overflow || security_enabled) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: specific frame_control 0x%04X"), frame_control); + } + + + for (uint32_t i=0; i> 4) + 1) & 0x07; + uint8_t ack_byte = 0x80 | EZSP_Serial.from_ack; + ZigbeeEZSPSendRaw(&ack_byte, 1, false); + + + + for (uint8_t i=0; i 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + + if (2 == XdrvMailbox.index) { ZigbeeEZSPSendDATA(buf.getBuffer(), buf.len()); } + else if (3 == XdrvMailbox.index) { ZigbeeEZSPSendRaw(buf.getBuffer(), buf.len(), true); } + else { ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); } + + } else { + + if (2 == XdrvMailbox.index) { ZigbeeProcessInput(buf); } + else if (3 == XdrvMailbox.index) { ZigbeeProcessInputRaw(buf); } + else { ZigbeeProcessInputEZSP(buf); } + } + } + ResponseCmndDone(); +} + + +void CmndZbEZSPReceive(void) +{ + CmndZbEZSPSendOrReceive(false); +} + +void CmndZbEZSPSend(void) +{ + CmndZbEZSPSendOrReceive(true); +} +#endif +# 773 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9_serial.ino" +void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { + +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(32+zcl.len); + buf.add8(Z_SREQ | Z_AF); + buf.add8(AF_DATA_REQUEST_EXT); + if (BAD_SHORTADDR == zcl.shortaddr) { + buf.add8(Z_Addr_Group); + buf.add64(zcl.groupaddr); + buf.add8(0xFF); + } else { + buf.add8(Z_Addr_ShortAddress); + buf.add64(zcl.shortaddr); + buf.add8(zcl.endpoint); + } + buf.add16(0x0000); + buf.add8(0x01); + buf.add16(zcl.cluster); + buf.add8(zcl.transacId); + buf.add8(0x30); + buf.add8(0x1E); + + buf.add16(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); + if (zcl.manuf) { + buf.add16(zcl.manuf); + } + buf.add8(zcl.transacId); + buf.add8(zcl.cmd); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif + +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(32+zcl.len); + + if (BAD_SHORTADDR != zcl.shortaddr) { + + buf.add16(EZSP_sendUnicast); + buf.add8(EMBER_OUTGOING_DIRECT); + buf.add16(zcl.shortaddr); + + buf.add16(Z_PROF_HA); + buf.add16(zcl.cluster); + buf.add8(0x01); + buf.add8(zcl.endpoint); + if (zcl.direct) { + buf.add16(0x0000); + } else { + buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); + } + buf.add16(zcl.groupaddr); + buf.add8(zcl.transacId); + + buf.add8(0x01); + + buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); + if (zcl.manuf) { + buf.add16(zcl.manuf); + } + buf.add8(zcl.transacId); + buf.add8(zcl.cmd); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); + } + } else { + + buf.add16(EZSP_sendMulticast); + + buf.add16(Z_PROF_HA); + buf.add16(zcl.cluster); + buf.add8(0x01); + buf.add8(zcl.endpoint); + if (zcl.direct) { + buf.add16(0x0000); + } else { + buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); + } + buf.add16(zcl.groupaddr); + buf.add8(zcl.transacId); + + buf.add8(0); + buf.add8(7); + buf.add8(0x01); + + buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); + if (zcl.manuf) { + buf.add16(zcl.manuf); + } + buf.add8(zcl.transacId); + buf.add8(zcl.cmd); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); + } + } + + ZigbeeEZSPSendCmd(buf.buf(), buf.len()); +#endif +} + + + + + +void ZigbeeOutputLoop(void) { +#ifdef USE_ZIGBEE_EZSP + + if (EZSP_Serial.to_send != EZSP_Serial.to_end) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Something to_send, to_ack:%d, to_send:%d, to_end:%d"), + EZSP_Serial.to_ack, EZSP_Serial.to_send, EZSP_Serial.to_end); + + ZigbeeEZSPSendDATA_frm(true, EZSP_Serial.to_send, EZSP_Serial.from_ack); + + EZSP_Serial.to_send = (EZSP_Serial.to_send + 1) & 0x07; + } +#endif +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9a_upload.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9a_upload.ino" +#ifdef USE_ZIGBEE + +#ifdef USE_ZIGBEE_EZSP +# 33 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9a_upload.ino" +#define XM_SOH 0x01 +#define XM_EOT 0x04 +#define XM_ACK 0x06 +#define XM_CR 0x0d +#define XM_NAK 0x15 +#define XM_CAN 0x18 +#define XM_SUB 0x1a + +enum ZbUploadSteps { ZBU_IDLE, ZBU_INIT, + ZBU_SOFTWARE_RESET, ZBU_SOFTWARE_SEND, ZBU_HARDWARE_RESET, ZBU_PROMPT, + ZBU_SYNC, ZBU_UPLOAD, ZBU_EOT, ZBU_COMPLETE, ZBU_DONE, ZBU_ERROR, ZBU_FINISH }; + +const uint8_t PIN_ZIGBEE_BOOTLOADER = 5; + +struct ZBUPLOAD { + uint32_t ota_size = 0; + uint32_t sector_cursor = 0; + uint32_t sector_counter = 0; + uint32_t byte_counter = 0; + char *buffer; + uint8_t ota_step = ZBU_IDLE; + uint8_t bootloader = 0; + uint8_t state = ZBU_IDLE; +} ZbUpload; + + + + + +uint32_t ZigbeeUploadFlashStart(void) { + return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; +} + +uint32_t ZigbeeUploadAvailable(void) { + int available = ZbUpload.ota_size - ZbUpload.byte_counter; + if (available < 0) { available = 0; } + return available; +} + +char ZigbeeUploadFlashRead(void) { + if (0 == ZbUpload.byte_counter) { + if (!(ZbUpload.buffer = (char *)malloc(SPI_FLASH_SEC_SIZE))) { + return (-1); + } + ZbUpload.sector_counter = ZigbeeUploadFlashStart(); + } + + uint32_t index = ZbUpload.byte_counter % SPI_FLASH_SEC_SIZE; + if (0 == index) { + ESP.flashRead(ZbUpload.sector_counter * SPI_FLASH_SEC_SIZE, (uint32_t*)ZbUpload.buffer, SPI_FLASH_SEC_SIZE); + ZbUpload.sector_counter++; + } + + char data = ZbUpload.buffer[index]; + ZbUpload.byte_counter++; + + if (ZbUpload.byte_counter > ZbUpload.ota_size) { + + + data = XM_SUB; + + } + return data; +} + + + + + + +const uint32_t XMODEM_FLUSH_DELAY = 1000; + +const uint8_t XMODEM_SYNC_TIMEOUT = 30; + +const uint8_t XMODEM_MAX_RETRY = 30; + +const uint8_t XMODEM_PACKET_SIZE = 128; + +struct XMODEM { + uint32_t timeout = 0; + uint32_t delay = 0; + uint32_t flush_delay = 0xFFFFFFFF; + uint32_t filepos = 0; + int crcBuf = 0; + uint8_t packetNo = 1; + uint8_t checksumBuf = 0; + bool oldChecksum; +} XModem; + + +void XModemOutputByte(uint8_t out_char) { + XModem.checksumBuf += out_char; + + XModem.crcBuf = XModem.crcBuf ^ (int) out_char << 8; + for (uint32_t i = 0; i < 8; i++) { + if (XModem.crcBuf & 0x8000) { + XModem.crcBuf = XModem.crcBuf << 1 ^ 0x1021; + } else { + XModem.crcBuf = XModem.crcBuf << 1; + } + } + + ZigbeeSerial->write(out_char); +} + + + +char XModemWaitACK(void) +{ + char in_char; + do { + uint8_t i = 0; + while (!ZigbeeSerial->available()) { + delayMicroseconds(100); + i++; + if (i > 200) { return -1; } + } + in_char = ZigbeeSerial->read(); + if (XM_CAN == in_char) { return XM_CAN; } + } while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C')); + return in_char; +} + +bool XModemSendPacket(uint32_t packet_no) { + XModem.filepos = ZbUpload.byte_counter; + + + uint32_t retries = 0; + char in_char; + do { + + + ZbUpload.byte_counter = XModem.filepos; + + + XModem.checksumBuf = 0x00; + XModem.crcBuf = 0x00; + + + ZigbeeSerial->write(XM_SOH); + ZigbeeSerial->write(packet_no); + ZigbeeSerial->write(~packet_no); + for (uint32_t i = 0; i < XMODEM_PACKET_SIZE; i++) { + in_char = ZigbeeUploadFlashRead(); + XModemOutputByte(in_char); + } + + + if (XModem.oldChecksum) { + ZigbeeSerial->write((char)XModem.checksumBuf); + } else { + ZigbeeSerial->write((char)(XModem.crcBuf >> 8)); + ZigbeeSerial->write((char)(XModem.crcBuf & 0xFF)); + } + in_char = XModemWaitACK(); + if (XM_CAN == in_char) { return false; } + retries++; + if (retries > XMODEM_MAX_RETRY) { return false; } + } while (in_char != XM_ACK); + return true; +} + + + + + + + +void ZigbeeUploadSetSoftwareBootloader() { + + SBuffer buf(4); + buf.add16(EZSP_launchStandaloneBootloader); + buf.add8(0x01); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); +} + +void ZigbeeUploadSetBootloader(uint8_t state) { + pinMode(PIN_ZIGBEE_BOOTLOADER, OUTPUT); + digitalWrite(PIN_ZIGBEE_BOOTLOADER, state); + digitalWrite(Pin(GPIO_ZIGBEE_RST), 0); + delay(100); + digitalWrite(Pin(GPIO_ZIGBEE_RST), 1); +} + +bool ZigbeeUploadBootloaderPrompt(void) { + + + + + uint8_t serial_buffer[255]; + uint32_t buf_len = 0; + + while (ZigbeeSerial->available()) { + yield(); + char bootloader_byte = ZigbeeSerial->read(); + + if (((uint8_t)bootloader_byte >=0) && (buf_len < sizeof(serial_buffer) -2)) { + serial_buffer[buf_len++] = bootloader_byte; + } + + if (ZbUpload.byte_counter != 4) { + switch (ZbUpload.byte_counter) { + case 0: + if ('B' == bootloader_byte) { ZbUpload.byte_counter++; } break; + case 1: + if ('L' == bootloader_byte) { ZbUpload.byte_counter++; } break; + case 2: + if (' ' == bootloader_byte) { ZbUpload.byte_counter++; } break; + case 3: + if ('>' == bootloader_byte) { + ZbUpload.byte_counter++; + XModem.flush_delay = millis() + XMODEM_FLUSH_DELAY; + XModem.delay = XModem.flush_delay + XMODEM_FLUSH_DELAY; + } + } + } + } + + if (buf_len) { + char hex_char[256]; + ToHex_P(serial_buffer, buf_len, hex_char, 256); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd %s"), hex_char); + } + + return ((4 == ZbUpload.byte_counter) && (millis() > XModem.flush_delay)); +} + +bool ZigbeeUploadXmodem(void) { + switch (ZbUpload.ota_step) { + case ZBU_IDLE: { + return false; + } +#ifdef ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST + case ZBU_INIT: { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader")); + ZbUpload.ota_step = ZBU_SOFTWARE_RESET; + return false; + } + case ZBU_SOFTWARE_RESET: { + SBuffer buf(4); + buf.add16(EZSP_launchStandaloneBootloader); + buf.add8(0x01); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); + XModem.timeout = millis() + (10 * 1000); + ZbUpload.ota_step = ZBU_SOFTWARE_SEND; + return false; + } + case ZBU_SOFTWARE_SEND: { + if (millis() > XModem.timeout) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset send timeout")); + ZbUpload.ota_step = ZBU_HARDWARE_RESET; + return true; + } + if (EZSP_Serial.to_send == EZSP_Serial.to_end) { + ZbUpload.bootloader = ZBU_SOFTWARE_RESET; + XModem.timeout = millis() + (10 * 1000); + XModem.delay = millis() + (2 * XMODEM_FLUSH_DELAY); + ZbUpload.byte_counter = 0; + ZbUpload.ota_step = ZBU_PROMPT; + } + break; + } + case ZBU_HARDWARE_RESET: { + ZbUpload.bootloader = ZBU_HARDWARE_RESET; + ZigbeeUploadSetBootloader(0); + XModem.timeout = millis() + (30 * 1000); + XModem.delay = millis() + (2 * XMODEM_FLUSH_DELAY); + ZbUpload.byte_counter = 0; + ZbUpload.ota_step = ZBU_PROMPT; + break; + } + case ZBU_PROMPT: { + if (millis() > XModem.timeout) { + if (ZBU_SOFTWARE_RESET == ZbUpload.bootloader) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset timeout")); + ZbUpload.ota_step = ZBU_HARDWARE_RESET; + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader hardware reset timeout")); + ZbUpload.ota_step = ZBU_ERROR; + } + return true; + } +#else + case ZBU_INIT: { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader")); + ZigbeeUploadSetBootloader(0); + XModem.timeout = millis() + (30 * 1000); + XModem.delay = millis() + (2 * XMODEM_FLUSH_DELAY); + ZbUpload.byte_counter = 0; + ZbUpload.ota_step = ZBU_PROMPT; + break; + } + case ZBU_PROMPT: { + if (millis() > XModem.timeout) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } +#endif + else if (!ZigbeeSerial->available() && (millis() < XModem.flush_delay)) { + + + if (millis() > XModem.delay) { + ZigbeeSerial->write(XM_CR); + XModem.delay = millis() + (2 * XMODEM_FLUSH_DELAY); + } + } else { + + + + + + + if (ZigbeeUploadBootloaderPrompt()) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync")); + ZigbeeSerial->flush(); + ZigbeeSerial->write('1'); + if (ssleep > 0) { + ssleep = 1; + } + XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000); + ZbUpload.ota_step = ZBU_SYNC; + } + } + break; + } + case ZBU_SYNC: { + if (millis() > XModem.timeout) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: SYNC timeout")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } + + if (ZigbeeSerial->available()) { + char xmodem_sync = ZigbeeSerial->read(); + if (('C' == xmodem_sync) || (XM_NAK == xmodem_sync)) { + + XModem.oldChecksum = (xmodem_sync == XM_NAK); + XModem.packetNo = 1; + ZbUpload.byte_counter = 0; + ZbUpload.ota_step = ZBU_UPLOAD; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Init packet send")); + } + } + break; + } + case ZBU_UPLOAD: { + if (ZigbeeUploadAvailable()) { + if (!XModemSendPacket(XModem.packetNo)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } + XModem.packetNo++; + } else { + + + + ZigbeeSerial->write(XM_EOT); + XModem.timeout = millis() + (30 * 1000); + ZbUpload.ota_step = ZBU_EOT; + } + break; + } + case ZBU_EOT: { + + + + + + if (millis() > XModem.timeout) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: EOT ACK timeout")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } + if (ZigbeeSerial->available()) { + char xmodem_ack = XModemWaitACK(); + if (XM_ACK == xmodem_ack) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL)); + XModem.timeout = millis() + (30 * 1000); + ZbUpload.byte_counter = 0; + ZbUpload.ota_step = ZBU_COMPLETE; + } + } + break; + } + case ZBU_COMPLETE: { + if (millis() > XModem.timeout) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } else { +# 433 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_9a_upload.ino" + if (ZigbeeUploadBootloaderPrompt()) { + ZbUpload.state = ZBU_COMPLETE; + ZbUpload.ota_step = ZBU_DONE; + } + } + break; + } + case ZBU_ERROR: + ZbUpload.state = ZBU_ERROR; + case ZBU_DONE: { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING)); + ZigbeeUploadSetBootloader(1); + if (1 == ssleep) { + ssleep = Settings.sleep; + } + + ZbUpload.ota_step = ZBU_FINISH; + break; + } + case ZBU_FINISH: { + + break; + } + } + return true; +} + + + + + +bool ZigbeeUploadOtaReady(void) { + return (ZBU_INIT == ZbUpload.ota_step); +} + +bool ZigbeeUploadFinish(void) { + return (ZBU_FINISH == ZbUpload.ota_step); +} + +uint8_t ZigbeeUploadInit(void) { + if (!PinUsed(GPIO_ZIGBEE_RST) && (ZigbeeSerial == nullptr)) { return 1; } + + ZbUpload.sector_counter = ZigbeeUploadFlashStart(); + ZbUpload.sector_cursor = 0; + ZbUpload.ota_size = 0; + ZbUpload.ota_step = ZBU_IDLE; + ZbUpload.state = ZBU_IDLE; + return 0; +} + +bool ZigbeeUploadWriteBuffer(uint8_t *buf, size_t size) { + + + if (0 == ZbUpload.sector_cursor) { + ESP.flashEraseSector(ZbUpload.sector_counter); + } + ZbUpload.sector_cursor++; + ESP.flashWrite((ZbUpload.sector_counter * SPI_FLASH_SEC_SIZE) + ((ZbUpload.sector_cursor-1) * 2048), (uint32_t*)buf, size); + ZbUpload.ota_size += size; + if (2 == ZbUpload.sector_cursor) { + ZbUpload.sector_cursor = 0; + ZbUpload.sector_counter++; + if (ZbUpload.sector_counter > (SPIFFS_END -2)) { + return false; + } + } + return true; +} + +void ZigbeeUploadDone(void) { + ZbUpload.ota_step = ZBU_INIT; + ZbUpload.state = ZBU_UPLOAD; +} + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_ZIGBEE_XFER "zx" + +const char HTTP_SCRIPT_XFER_STATE[] PROGMEM = + "function z9(){" + "if(x!=null){x.abort();}" + "x=new XMLHttpRequest();" + "x.onreadystatechange=function(){" + "if(x.readyState==4&&x.status==200){" + "var s=x.responseText;" + "if(s!=7){" + "location.href='/u3';" + "}" + "}" + "};" + "x.open('GET','" WEB_HANDLE_ZIGBEE_XFER "?z=1',true);" + "x.send();" + "lt=setTimeout(z9,950);" + "}" + "wl(z9);"; + +void HandleZigbeeXfer(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("z")) { + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d"), ZbUpload.state); + WSContentEnd(); + if (ZBU_ERROR == ZbUpload.state) { + Web.upload_error = 7; + } + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_UPLOAD_TRANSFER)); + + WSContentStart_P(S_INFORMATION); + WSContentSend_P(HTTP_SCRIPT_XFER_STATE); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD_TRANSFER " ...
")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +#endif + +#endif + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" +#ifdef USE_ZIGBEE + +#define XDRV_23 23 + +const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" +#ifdef USE_ZIGBEE_ZNP + D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|" +#endif +#ifdef USE_ZIGBEE_EZSP + D_CMND_ZIGBEE_EZSP_SEND "|" D_CMND_ZIGBEE_EZSP_RECEIVE "|" D_CMND_ZIGBEE_EZSP_LISTEN "|" +#endif + D_CMND_ZIGBEE_PERMITJOIN "|" + D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|" + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" + D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" + D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" + D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA + ; + +void (* const ZigbeeCommand[])(void) PROGMEM = { +#ifdef USE_ZIGBEE_ZNP + &CmndZbZNPSend, &CmndZbZNPReceive, +#endif +#ifdef USE_ZIGBEE_EZSP + &CmndZbEZSPSend, &CmndZbEZSPReceive, &CmndZbEZSPListen, +#endif + &CmndZbPermitJoin, + &CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe, + &CmndZbForget, &CmndZbSave, &CmndZbName, + &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, + &CmndZbLight, &CmndZbRestore, &CmndZbBindState, + &CmndZbConfig, CmndZbData, + }; + + + + +void ZigbeeInit(void) +{ + + + + + + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { + if (0 == Settings.zb_channel) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Randomizing Zigbee parameters, please check with 'ZbConfig'")); + uint64_t mac64 = 0; + WiFi.macAddress((uint8_t*) &mac64); + uint32_t esp_id = ESP_getChipId(); +#ifdef ESP8266 + uint32_t flash_id = ESP.getFlashChipId(); +#else + uint32_t flash_id = 0; +#endif + + uint16_t pan_id = (mac64 & 0x3FFF); + if (0x0000 == pan_id) { pan_id = 0x0001; } + if (0x3FFF == pan_id) { pan_id = 0x3FFE; } + Settings.zb_pan_id = pan_id; + + Settings.zb_ext_panid = 0xCCCCCCCC00000000L | (mac64 & 0x00000000FFFFFFFFL); + Settings.zb_precfgkey_l = (mac64 << 32) | (esp_id << 16) | flash_id; + Settings.zb_precfgkey_h = (mac64 << 32) | (esp_id << 16) | flash_id; + Settings.zb_channel = USE_ZIGBEE_CHANNEL; + Settings.zb_txradio_dbm = USE_ZIGBEE_TXRADIO_DBM; + } + + if (Settings.zb_txradio_dbm < 0) { + Settings.zb_txradio_dbm = -Settings.zb_txradio_dbm; +#ifdef USE_ZIGBEE_EZSP + EZ_reset_config = true; +#endif + SettingsSave(2); + } + } + + +#ifdef USE_ZIGBEE_ZNP + ZNP_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h); +#endif +#ifdef USE_ZIGBEE_EZSP + EZ_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h, Settings.zb_txradio_dbm); +#endif + + ZigbeeInitSerial(); +} + + + + + +#ifdef USE_ZIGBEE_ZNP + +const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = + { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x01 }; + +#endif + +void CmndZbReset(void) { + if (ZigbeeSerial) { + switch (XdrvMailbox.payload) { + case 1: +#ifdef USE_ZIGBEE_ZNP + ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); +#endif + eraseZigbeeDevices(); + case 2: + Settings.zb_txradio_dbm = - abs(Settings.zb_txradio_dbm); + restart_flag = 2; +#ifdef USE_ZIGBEE_ZNP + ResponseCmndChar_P(PSTR(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING)); +#endif +#ifdef USE_ZIGBEE_EZSP + ResponseCmndChar_P(PSTR(D_JSON_ZIGBEE_EZSP " " D_JSON_RESET_AND_RESTARTING)); +#endif + break; + default: + ResponseCmndChar_P(PSTR("1 or 2 to reset")); + } + } +} +# 158 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" +void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, + uint16_t cluster, uint8_t cmd, const char *param) { + size_t size = param ? strlen(param) : 0; + SBuffer buf((size+2)/2); + + if (param) { + while (*param) { + uint8_t code = parseHex_P(¶m, 2); + buf.add8(code); + } + } + + if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) { + + endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), + shortaddr, groupaddr, cluster, endpoint, cmd, param); + + if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); + return; + } + + + + uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + groupaddr, + cluster , + endpoint, + cmd, + manuf, + clusterSpecific , + true , + false , + seq, + buf.getBuffer(), buf.len() + })); + + if (clusterSpecific) { +#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES + sendHueUpdate(shortaddr, groupaddr, cluster, endpoint); +#endif + } +} + + + + + + +void ZbApplyMultiplier(double &val_d, int8_t multiplier) { + if ((0 != multiplier) && (1 != multiplier)) { + if (multiplier > 0) { + val_d = val_d / multiplier; + } else { + val_d = val_d * (-multiplier); + } + } +} + + + + +bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr, uint8_t transid) { + double val_d = attr.getFloat(); + const char * val_str = attr.getStr(); + + if (attr.key_is_str) { return false; } + if (attr.isNum() && (1 != attr.attr_multiplier)) { + ZbApplyMultiplier(val_d, attr.attr_multiplier); + } + buf.add8(0); + buf.add8(transid); + buf.add16(attr.key.id.attr_id); + buf.add8(0); + int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type); + if (res < 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Error for Tuya attribute type %04X/%04X '0x%02X'"), attr.key.id.cluster, attr.key.id.attr_id, attr.attr_type); + return false; + } + return true; +} + + + + +bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok) { + double val_d = attr.getFloat(); + const char * val_str = attr.getStr(); + + if (attr.key_is_str) { return false; } + if (attr.isNum() && (1 != attr.attr_multiplier)) { + ZbApplyMultiplier(val_d, attr.attr_multiplier); + } + + + buf.add16(attr.key.id.attr_id); + if (prepend_status_ok) { + buf.add8(Z_SUCCESS); + } + buf.add8(attr.attr_type); + int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type); + if (res < 0) { + + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Unsupported attribute type %04X/%04X '0x%02X'"), attr.key.id.cluster, attr.key.id.attr_id, attr.attr_type); + return false; + } + return true; +} + + + + + +void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZigbeeZCLSendMessage & packet) { + SBuffer buf(200); + + if (nullptr == XdrvMailbox.command) { + XdrvMailbox.command = (char*) ""; + } + + + for (auto key : val_pubwrite.getObject()) { + JsonParserToken value = key.getValue(); + + Z_attribute attr; + attr.setKeyName(key.getStr()); + if (Z_parseAttributeKey(attr)) { + + + + if (0xFFFF == packet.cluster) { + packet.cluster = attr.key.id.cluster; + } else if (packet.cluster != attr.key.id.cluster) { + ResponseCmndChar_P(PSTR("No more than one cluster id per command")); + return; + } + + } else { + if (attr.key_is_str) { + Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute "), key); + return; + } + if (Zunk == attr.attr_type) { + Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute type for attribute "), key); + return; + } + } + + + if (value.isStr()) { + attr.setStr(value.getStr()); + } else if (value.isNum()) { + attr.setFloat(value.getFloat()); + } + + double val_d = 0; + const char* val_str = ""; + + + if (packet.cmd != ZCL_CONFIGURE_REPORTING) { + if ((packet.cluster == 0XEF00) && (packet.cmd == ZCL_WRITE_ATTRIBUTES)) { + + if (buf.len() > 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Only 1 attribute allowed for Tuya")); + return; + } + packet.clusterSpecific = true; + packet.cmd = 0x00; + if (!ZbTuyaWrite(buf, attr, zigbee_devices.getNextSeqNumber(packet.shortaddr))) { + return; + } + } else if (!ZbAppendWriteBuf(buf, attr, packet.cmd == ZCL_READ_ATTRIBUTES_RESPONSE)) { + return; + } + } else { + + + if (!value.isObject()) { + ResponseCmndChar_P(PSTR("Config requires JSON objects")); + return; + } + JsonParserObject attr_config = value.getObject(); + bool attr_direction = false; + + uint32_t dir = attr_config.getUInt(PSTR("DirectionReceived"), 0); + if (dir) { attr_direction = true; } + + + uint16_t attr_min_interval = attr_config.getUInt(PSTR("MinInterval"), 0xFFFF); + uint16_t attr_max_interval = attr_config.getUInt(PSTR("MaxInterval"), 0xFFFF); + + + JsonParserToken val_attr_rc = attr_config[PSTR("ReportableChange")]; + if (val_attr_rc) { + val_d = val_attr_rc.getFloat(); + val_str = val_attr_rc.getStr(); + ZbApplyMultiplier(val_d, attr.attr_multiplier); + } + + + uint16_t attr_timeout = attr_config.getUInt(PSTR("TimeoutPeriod"), 0x0000); + + bool attr_discrete = Z_isDiscreteDataType(attr.attr_type); + + + + buf.add8(attr_direction ? 0x01 : 0x00); + buf.add16(attr.key.id.attr_id); + if (attr_direction) { + buf.add16(attr_timeout); + } else { + buf.add8(attr.attr_type); + buf.add16(attr_min_interval); + buf.add16(attr_max_interval); + if (!attr_discrete) { + int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type); + if (res < 0) { + Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, attr.attr_type); + return; + } + } + } + } + } + + + if (0 == buf.len()) { + ResponseCmndChar_P(PSTR("No attribute in list")); + return; + } + + + packet.transacId = zigbee_devices.getNextSeqNumber(packet.shortaddr); + packet.msg = buf.getBuffer(); + packet.len = buf.len(); + ZigbeeZCLSend_Raw(packet); + ResponseCmndDone(); +} + + +void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) { + uint8_t cmd = 0; + String cmd_str = ""; + const char *cmd_s = ""; + bool clusterSpecific = true; + + static char delim[] = ", "; + + + + if (val_cmd.isObject()) { + + JsonParserObject cmd_obj = val_cmd.getObject(); + int32_t cmd_size = cmd_obj.size(); + if (cmd_size > 1) { + Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); + return; + } else if (1 == cmd_size) { + + JsonParserKey key = cmd_obj.getFirstElement(); + JsonParserToken value = key.getValue(); + uint32_t x = 0, y = 0, z = 0; + uint16_t cmd_var; + uint16_t local_cluster_id; + + const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.getStr(), &local_cluster_id, &cmd_var); + if (tasmota_cmd) { + cmd_str = tasmota_cmd; + } else { + Response_P(PSTR("Unrecognized zigbee command: %s"), key.getStr()); + return; + } + + if (0xFFFF == cluster) { + cluster = local_cluster_id; + } else if (cluster != local_cluster_id) { + ResponseCmndChar_P(PSTR("No more than one cluster id per command")); + return; + } + + + if (value.isNum()) { + x = value.getUInt(); + + + + + + } else { + + const char *s_const = value.getStr(nullptr); + + if (s_const != nullptr) { + char s[strlen(s_const)+1]; + strcpy(s, s_const); + if ((nullptr != s) && (0x00 != *s)) { + char *sval = strtok(s, delim); + if (sval) { + x = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + y = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + z = ZigbeeAliasOrNumber(sval); + } + } + } + } + } + } + + + if (0xFF == cmd_var) { + cmd = x; + x = y; + y = z; + } else { + cmd = cmd_var; + } + cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); + + cmd_s = cmd_str.c_str(); + } else { + + } + } else if (val_cmd.isStr()) { + + + + + + const char * data = val_cmd.getStr(); + uint16_t local_cluster_id = parseHex(&data, 4); + + + if (0xFFFF == cluster) { + cluster = local_cluster_id; + } else if (cluster != local_cluster_id) { + ResponseCmndChar_P(PSTR("No more than one cluster id per command")); + return; + } + + + if (('_' == *data) || ('!' == *data)) { + if ('_' == *data) { clusterSpecific = false; } + data++; + } else { + ResponseCmndChar_P(PSTR("Wrong delimiter for payload")); + return; + } + + cmd = parseHex(&data, 2); + + + + if ('/' == *data) { data++; } + + cmd_s = data; + } else { + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""), + device, groupaddr, endpoint, cluster, cmd, cmd_s); + zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s); + ResponseCmndDone(); +} + + + +void ZbSendRead(JsonParserToken val_attr, ZigbeeZCLSendMessage & packet) { +# 546 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + size_t attrs_len = 0; + uint8_t* attrs = nullptr; + size_t attr_item_len = 2; + size_t attr_item_offset = 0; + if (ZCL_READ_REPORTING_CONFIGURATION == packet.cmd) { + attr_item_len = 3; + attr_item_offset = 1; + } + + if (val_attr.isArray()) { + + JsonParserArray attr_arr = val_attr.getArray(); + attrs_len = attr_arr.size() * attr_item_len; + attrs = (uint8_t*) calloc(attrs_len, 1); + + uint32_t i = 0; + for (auto value : attr_arr) { + uint16_t val = value.getUInt(); + i += attr_item_offset; + attrs[i++] = val & 0xFF; + attrs[i++] = val >> 8; + i += attr_item_len - 2 - attr_item_offset; + } + } else if (val_attr.isObject()) { + + JsonParserObject attr_obj = val_attr.getObject(); + attrs_len = attr_obj.size() * attr_item_len; + attrs = (uint8_t*) calloc(attrs_len, 1); + uint32_t actual_attr_len = 0; + + + for (auto key : attr_obj) { + JsonParserToken value = key.getValue(); + + bool found = false; + + for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + bool match = false; + uint16_t local_attr_id = pgm_read_word(&converter->attribute); + uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short)); + + + if ((pgm_read_word(&converter->name_offset)) && (0 == strcasecmp_P(key.getStr(), Z_strings + pgm_read_word(&converter->name_offset)))) { + + + + if (!(value.getBool()) && attr_item_offset) { + + attrs[actual_attr_len] = 0x01; + } + actual_attr_len += attr_item_offset; + attrs[actual_attr_len++] = local_attr_id & 0xFF; + attrs[actual_attr_len++] = local_attr_id >> 8; + actual_attr_len += attr_item_len - 2 - attr_item_offset; + found = true; + + if (0xFFFF == packet.cluster) { + packet.cluster = local_cluster_id; + } else if (packet.cluster != local_cluster_id) { + ResponseCmndChar_P(PSTR("No more than one cluster id per command")); + if (attrs) { free(attrs); } + return; + } + break; + } + } + if (!found) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Unknown attribute name (ignored): %s"), key); + } + } + + attrs_len = actual_attr_len; + } else { + + if (0xFFFF != packet.cluster) { + uint16_t val = val_attr.getUInt(); + attrs_len = attr_item_len; + attrs = (uint8_t*) calloc(attrs_len, 1); + attrs[0 + attr_item_offset] = val & 0xFF; + attrs[1 + attr_item_offset] = val >> 8; + } + } + + if (attrs_len > 0) { + + packet.transacId = zigbee_devices.getNextSeqNumber(packet.shortaddr); + packet.msg = attrs; + packet.len = attrs_len; + ZigbeeZCLSend_Raw(packet); + ResponseCmndDone(); + } else { + ResponseCmndChar_P(PSTR("Missing parameters")); + } + + if (attrs) { free(attrs); } +} +# 656 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" +void CmndZbSend(void) { +# 668 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + uint16_t device = BAD_SHORTADDR; + uint16_t groupaddr = 0x0000; + uint16_t cluster = 0xFFFF; + uint8_t endpoint = 0x00; + uint16_t manuf = 0x0000; + + + + JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)]; + if (val_device) { + device = zigbee_devices.parseDeviceParam(val_device.getStr()); + if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (BAD_SHORTADDR == device) { + JsonParserToken val_group = root[PSTR(D_CMND_ZIGBEE_GROUP)]; + if (val_group) { + groupaddr = val_group.getUInt(); + } else { + ResponseCmndChar_P(PSTR("Unknown device")); + return; + } + } + + + + + cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), cluster); + endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), endpoint); + manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), manuf); + + + if (BAD_SHORTADDR == device) { + endpoint = 0xFF; + } else if (0 == endpoint) { + endpoint = zigbee_devices.findFirstEndpoint(device); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), endpoint); + } + if (0 == endpoint) { + ResponseCmndChar_P(PSTR("Missing endpoint")); + return; + } + + + + JsonParserToken val_cmd = root[PSTR(D_CMND_ZIGBEE_SEND)]; + JsonParserToken val_read = root[PSTR(D_CMND_ZIGBEE_READ)]; + JsonParserToken val_write = root[PSTR(D_CMND_ZIGBEE_WRITE)]; + JsonParserToken val_publish = root[PSTR(D_CMND_ZIGBEE_REPORT)]; + JsonParserToken val_response = root[PSTR(D_CMND_ZIGBEE_RESPONSE)]; + JsonParserToken val_read_config = root[PSTR(D_CMND_ZIGBEE_READ_CONFIG)]; + JsonParserToken val_config = root[PSTR(D_CMND_ZIGBEE_CONFIG)]; + uint32_t multi_cmd = ((bool)val_cmd) + ((bool)val_read) + ((bool)val_write) + ((bool)val_publish) + + ((bool)val_response) + ((bool)val_read_config) + ((bool)val_config); + if (multi_cmd > 1) { + ResponseCmndChar_P(PSTR("Can only have one of: 'Send', 'Read', 'Write', 'Report', 'Reponse', 'ReadConfig' or 'Config'")); + return; + } + + + + ZigbeeZCLSendMessage packet({ + device, + groupaddr, + cluster , + endpoint, + ZCL_READ_ATTRIBUTES, + manuf, + false , + false , + false , + 0, + nullptr, 0 + }); + + if (val_cmd) { + + + ZbSendSend(val_cmd, device, groupaddr, cluster, endpoint, manuf); + } else if (val_read) { + + + packet.cmd = ZCL_READ_ATTRIBUTES; + ZbSendRead(val_read, packet); + } else if (val_write) { + + if (!val_write.isObject()) { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + + packet.cmd = ZCL_WRITE_ATTRIBUTES; + ZbSendReportWrite(val_write, packet); + } else if (val_publish) { + + + if (!val_publish.isObject()) { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + packet.cmd = ZCL_REPORT_ATTRIBUTES; + ZbSendReportWrite(val_publish, packet); + } else if (val_response) { + + + if (!val_response.isObject()) { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + packet.cmd = ZCL_READ_ATTRIBUTES_RESPONSE; + ZbSendReportWrite(val_response, packet); + } else if (val_read_config) { + + + packet.cmd = ZCL_READ_REPORTING_CONFIGURATION; + ZbSendRead(val_read_config, packet); + } else if (val_config) { + + + if (!val_config.isObject()) { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + packet.cmd = ZCL_CONFIGURE_REPORTING; + ZbSendReportWrite(val_config, packet); + } else { + Response_P(PSTR("Missing zigbee 'Send', 'Write', 'Report' or 'Response'")); + return; + } +} + + + + +void ZbBindUnbind(bool unbind) { + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + uint16_t srcDevice = BAD_SHORTADDR; + uint16_t dstDevice = BAD_SHORTADDR; + uint64_t dstLongAddr = 0; + uint8_t endpoint = 0x00; + uint8_t toendpoint = 0x01; + uint16_t toGroup = 0x0000; + uint16_t cluster = 0; + uint32_t group = 0xFFFFFFFF; + + + + srcDevice = zigbee_devices.parseDeviceParam(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)); + if (BAD_SHORTADDR == srcDevice) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } + + uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); + if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; } + + endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), endpoint); + if (0 == endpoint) { endpoint = zigbee_devices.findFirstEndpoint(srcDevice); } + + JsonParserToken val_cluster = root[PSTR(D_CMND_ZIGBEE_CLUSTER)]; + if (val_cluster) { + cluster = val_cluster.getUInt(cluster); + if (0 == cluster) { + zigbeeFindAttributeByName(val_cluster.getStr(), &cluster, nullptr, nullptr); + } + } + + + JsonParserToken to_group = root[PSTR("ToGroup")]; + if (to_group) { toGroup = to_group.getUInt(toGroup); } + + + + + + JsonParserToken dst_device = root[PSTR("ToDevice")]; + + + if ((!to_group) && (!dst_device)) { + dstDevice = 0x0000; + } + + if ((dst_device) || (BAD_SHORTADDR != dstDevice)) { + if (BAD_SHORTADDR == dstDevice) { + dstDevice = zigbee_devices.parseDeviceParam(dst_device.getStr(nullptr)); + if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (0x0000 == dstDevice) { + dstLongAddr = localIEEEAddr; + } else { + dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice); + } + if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; } + + toendpoint = root.getUInt(PSTR("ToEndpoint"), toendpoint); + } + + + if (to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; } + if (!to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; } + +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + if (unbind) { + buf.add8(ZDO_UNBIND_REQ); + } else { + buf.add8(ZDO_BIND_REQ); + } + buf.add16(srcDevice); + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + if (dstLongAddr) { + buf.add8(Z_Addr_IEEEAddress); + buf.add64(dstLongAddr); + buf.add8(toendpoint); + } else { + buf.add8(Z_Addr_Group); + buf.add16(toGroup); + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif + +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(24); + + + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + if (dstLongAddr) { + buf.add8(Z_Addr_IEEEAddress); + buf.add64(dstLongAddr); + buf.add8(toendpoint); + } else { + buf.add8(Z_Addr_Group); + buf.add16(toGroup); + } + + EZ_SendZDO(srcDevice, unbind ? ZDO_UNBIND_REQ : ZDO_BIND_REQ, buf.buf(), buf.len()); +#endif + + ResponseCmndDone(); +} + + + + +void CmndZbBind(void) { + ZbBindUnbind(false); +} + + + + +void CmndZbUnbind(void) { + ZbBindUnbind(true); +} + + + + + +void CmndZbBindState(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + uint8_t index = XdrvMailbox.index - 1; + +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(10); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_MGMT_BIND_REQ); + buf.add16(shortaddr); + buf.add8(index); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif + + +#ifdef USE_ZIGBEE_EZSP + + uint8_t buf[] = { index }; + + EZ_SendZDO(shortaddr, ZDO_Mgmt_Bind_req, buf, sizeof(buf)); +#endif + + ResponseCmndDone(); +} + + +void CmndZbProbe(void) { + CmndZbProbeOrPing(true); +} + + + + +void CmndZbProbeOrPing(boolean probe) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + + + Z_SendIEEEAddrReq(shortaddr); + if (probe) { + Z_SendActiveEpReq(shortaddr); + } + ResponseCmndDone(); +} + + +void CmndZbPing(void) { + CmndZbProbeOrPing(false); +} + + + + + +void CmndZbName(void) { + + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + + if (p == nullptr) { + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : ""); + } else { + if (strlen(p) > 32) { p[32] = 0x00; } + zigbee_devices.setFriendlyName(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); + } +} + + + + + +void CmndZbModelId(void) { + + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + + if (p == nullptr) { + const char * modelId = zigbee_devices.getModelId(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : ""); + } else { + zigbee_devices.setModelId(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); + } +} + + + + +void CmndZbLight(void) { + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + + if (p) { + int8_t bulbtype = strtol(p, nullptr, 10); + if (bulbtype > 5) { bulbtype = 5; } + if (bulbtype < -1) { bulbtype = -1; } + zigbee_devices.setLightProfile(shortaddr, bulbtype); + } + String dump = zigbee_devices.dumpLightState(shortaddr); + Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str()); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT)); + ResponseCmndDone(); +} + + + + + +void CmndZbForget(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + + + if (zigbee_devices.removeDevice(shortaddr)) { + ResponseCmndDone(); + } else { + ResponseCmndChar_P(PSTR("Unknown device")); + } +} + + + + + +void CmndZbSave(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + saveZigbeeDevices(); + ResponseCmndDone(); +} +# 1128 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" +void CmndZbRestore(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + JsonParser parser(XdrvMailbox.data); + JsonParserToken root = parser.getRoot(); + + if (!parser || !(root.isObject() || root.isArray())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + JsonParserToken zbstatus = root.getObject().findStartsWith(PSTR("ZbStatus")); + if (zbstatus) { + root = zbstatus; + } + + + if (root.isArray()) { + JsonParserArray arr = JsonParserArray(root); + for (const auto elt : arr) { + + if (elt.isObject()) { + int32_t res = zigbee_devices.deviceRestore(JsonParserObject(elt)); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + } + } + } else if (root.isObject()) { + int32_t res = zigbee_devices.deviceRestore(JsonParserObject(root)); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + + } else { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + ResponseCmndDone(); +} + + + + + +void CmndZbPermitJoin(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + uint32_t payload = XdrvMailbox.payload; + uint8_t duration = 60; + + if (payload <= 0) { + duration = 0; + } + + +#ifdef USE_ZIGBEE_ZNP + if (99 == payload) { + duration = 0xFF; + } + + uint16_t dstAddr = 0xFFFC; + + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); + buf.add8(0x0F); + buf.add16(0xFFFC); + buf.add8(duration); + buf.add8(0x00); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + +#endif + + +#ifdef USE_ZIGBEE_EZSP + if (99 == payload) { + ResponseCmndChar_P(PSTR("Unlimited time not supported")); return; + } + + SBuffer buf(3); + buf.add16(EZSP_permitJoining); + buf.add8(duration); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); + + + buf.setLen(0); + buf.add8(duration); + buf.add8(0x01); + EZ_SendZDO(0xFFFC, ZDO_Mgmt_Permit_Joining_req, buf.buf(), buf.len()); + + + if (duration > 0) { + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":21,\"Message\":\"Pairing mode enabled\"}}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + zigbee.permit_end_time = millis() + duration * 1000; + AddLog_P2(LOG_LEVEL_INFO, "zigbee.permit_end_time = %d", zigbee.permit_end_time); + } else { + zigbee.permit_end_time = millis(); + } +#endif + + ResponseCmndDone(); +} + +#ifdef USE_ZIGBEE_EZSP + + + + + +void CmndZbEZSPListen(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + int32_t index = XdrvMailbox.index; + int32_t group = XdrvMailbox.payload; + + if (group <= 0) { + group = 0; + } else if (group > 0xFFFF) { + group = 0xFFFF; + } + + SBuffer buf(8); + buf.add16(EZSP_setMulticastTableEntry); + buf.add8(index); + buf.add16(group); + buf.add8(0x01); + buf.add8(0x00); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + +void ZigbeeGlowPermitJoinLight(void) { + static const uint16_t cycle_time = 1000; + static const uint16_t half_cycle_time = cycle_time / 2; + if (zigbee.permit_end_time) { + uint16_t led_power = 0; + + if (TimeReached(zigbee.permit_end_time)) { + zigbee.permit_end_time = 0; + Z_PermitJoinDisable(); + } else { + uint32_t millis_to_go = millis() - zigbee.permit_end_time; + uint32_t sub_second = millis_to_go % cycle_time; + if (sub_second <= half_cycle_time) { + led_power = changeUIntScale(sub_second, 0, half_cycle_time, 0, 1023); + } else { + led_power = changeUIntScale(sub_second, half_cycle_time, cycle_time, 1023, 0); + } + led_power = ledGamma10_10(led_power); + } + + + uint32_t led_pin = Pin(GPIO_LEDLNK); + if (led_pin < 99) { + analogWrite(led_pin, ledlnk_inverted ? 1023 - led_power : led_power); + } + } +} +#endif + + + + +void CmndZbStatus(void) { + if (ZigbeeSerial) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (XdrvMailbox.data_len > 0) { + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + } + + String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); + Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); + } +} + + + + + +bool parseDeviceInnerData(class Z_Device & device, JsonParserObject root) { + for (auto data_elt : root) { + + const char * data_type_str = data_elt.getStr(); + Z_Data_Type data_type; + + switch (data_type_str[0]) { + case 'P': data_type = Z_Data_Type::Z_Plug; break; + case 'L': data_type = Z_Data_Type::Z_Light; break; + case 'O': data_type = Z_Data_Type::Z_OnOff; break; + case 'T': data_type = Z_Data_Type::Z_Thermo; break; + case 'A': data_type = Z_Data_Type::Z_Alarm; break; + case '_': data_type = Z_Data_Type::Z_Device; break; + default: data_type = Z_Data_Type::Z_Unknown; break; + } + + if (data_type == Z_Data_Type::Z_Unknown) { + Response_P(PSTR("{\"%s\":\"%s \"%s\"\"}"), XdrvMailbox.command, PSTR("Invalid Parameters"), data_type_str); + return false; + } + + JsonParserObject data_values = data_elt.getValue().getObject(); + if (!data_values) { return false; } + + + uint8_t endpoint = strtoul(&data_type_str[1], nullptr, 16); + JsonParserToken val; + + + Z_Data & data = device.data.getByType(data_type, endpoint); + + + for (auto attr : data_values) { + JsonParserToken attr_value = attr.getValue(); + uint8_t conv_zigbee_type; + Z_Data_Type conv_data_type; + uint8_t conv_map_offset; + if (zigbeeFindAttributeByName(attr.getStr(), nullptr, nullptr, nullptr, &conv_zigbee_type, &conv_data_type, &conv_map_offset) != nullptr) { + + if (conv_data_type == data_type) { + + + uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + conv_map_offset; + uint32_t uval32 = attr_value.getUInt(); + int32_t ival32 = attr_value.getInt(); + switch (conv_zigbee_type) { + case Zenum8: + case Zuint8: *(uint8_t*)attr_address = uval32; break; + case Zenum16: + case Zuint16: *(uint16_t*)attr_address = uval32; break; + case Zuint32: *(uint32_t*)attr_address = uval32; break; + case Zint8: *(int8_t*)attr_address = ival32; break; + case Zint16: *(int16_t*)attr_address = ival32; break; + case Zint32: *(int32_t*)attr_address = ival32; break; + } + } else if (conv_data_type != Z_Data_Type::Z_Unknown) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "attribute %s is wrong type %d (expected %d)"), attr.getStr(), (uint8_t)data_type, (uint8_t)conv_data_type); + } + } + } + + + switch (data_type) { +# 1385 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + case Z_Data_Type::Z_OnOff: + { + Z_Data_OnOff & onoff = (Z_Data_OnOff&) data; + + if (val = data_values[PSTR("Power")]) { onoff.setPower(val.getUInt() ? true : false); } + } + break; +# 1402 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + case Z_Data_Type::Z_Device: + { + if (val = data_values[PSTR(D_CMND_ZIGBEE_LINKQUALITY)]) { device.lqi = val.getUInt(); } + if (val = data_values[PSTR("BatteryPercentage")]) { device.batterypercent = val.getUInt(); } + if (val = data_values[PSTR("LastSeen")]) { device.last_seen = val.getUInt(); } + } + break; + } + } + return true; +} + + + + +void CmndZbData(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + RemoveSpace(XdrvMailbox.data); + if (XdrvMailbox.data[0] == '{') { + + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + JsonParserToken zbdata = root.getObject().findStartsWith(PSTR("ZbData")); + if (zbdata) { + root = zbdata; + } + + for (auto device_name : root) { + uint16_t shortaddr = zigbee_devices.parseDeviceParam(device_name.getStr()); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + Z_Device & device = zigbee_devices.getShortAddr(shortaddr); + JsonParserObject inner_data = device_name.getValue().getObject(); + if (inner_data) { + if (!parseDeviceInnerData(device, inner_data)) { + return; + } + } + } + ResponseCmndDone(); + } else { + + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + const Z_Device & device = zigbee_devices.findShortAddr(shortaddr); + + Z_attribute_list attr_data; + + { + Z_attribute_list device_attr; + device.toAttributes(device_attr); + attr_data.addAttribute(F("_00")).setStrRaw(device_attr.toString(true).c_str()); + } + + + for (auto & data_elt : device.data) { + Z_attribute_list inner_attr; + char key[4]; + snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint()); +# 1473 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + Z_Data_Type data_type = data_elt.getType(); + switch (data_type) { + case Z_Data_Type::Z_Plug: + { + key[0] = 'P'; + ((Z_Data_Plug&)data_elt).toAttributes(inner_attr, data_type); + } + break; + case Z_Data_Type::Z_Light: + { + key[0] = 'L'; + ((Z_Data_Light&)data_elt).toAttributes(inner_attr, data_type); + } + break; + case Z_Data_Type::Z_OnOff: + { + key[0] = 'O'; + ((Z_Data_OnOff&)data_elt).toAttributes(inner_attr, data_type); + } + break; + case Z_Data_Type::Z_Thermo: + { + key[0] = 'T'; + ((Z_Data_Thermo&)data_elt).toAttributes(inner_attr, data_type); + } + break; + case Z_Data_Type::Z_Alarm: + { + key[0] = 'A'; + ((Z_Data_Alarm&)data_elt).toAttributes(inner_attr, data_type); + } + break; + } + if (key[0] != '?') { + attr_data.addAttribute(key).setStrRaw(inner_attr.toString(true).c_str()); + } + } + + char hex[8]; + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + Response_P(PSTR("{\"%s\":{\"%s\":%s}}"), XdrvMailbox.command, hex, attr_data.toString(true).c_str()); + } +} + + + + +void CmndZbConfig(void) { + + + uint8_t zb_channel = Settings.zb_channel; + uint16_t zb_pan_id = Settings.zb_pan_id; + uint64_t zb_ext_panid = Settings.zb_ext_panid; + uint64_t zb_precfgkey_l = Settings.zb_precfgkey_l; + uint64_t zb_precfgkey_h = Settings.zb_precfgkey_h; + int8_t zb_txradio_dbm = Settings.zb_txradio_dbm; + + + RemoveSpace(XdrvMailbox.data); + if (strlen(XdrvMailbox.data) > 0) { + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + zb_channel = root.getUInt(PSTR("Channel"), zb_channel); + zb_pan_id = root.getUInt(PSTR("PanID"), zb_pan_id); + zb_ext_panid = root.getULong(PSTR("ExtPanID"), zb_ext_panid); + zb_precfgkey_l = root.getULong(PSTR("KeyL"), zb_precfgkey_l); + zb_precfgkey_h = root.getULong(PSTR("KeyH"), zb_precfgkey_h); + zb_txradio_dbm = root.getInt(PSTR("TxRadio"), zb_txradio_dbm); + + if (zb_channel < 11) { zb_channel = 11; } + if (zb_channel > 26) { zb_channel = 26; } + + if ((0 == zb_precfgkey_l) && (0 == zb_precfgkey_h)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "generating random Zigbee network key")); + zb_precfgkey_l = (uint64_t)HwRandom() << 32 | HwRandom(); + zb_precfgkey_h = (uint64_t)HwRandom() << 32 | HwRandom(); + } + + + if ( (zb_channel != Settings.zb_channel) || + (zb_pan_id != Settings.zb_pan_id) || + (zb_ext_panid != Settings.zb_ext_panid) || + (zb_precfgkey_l != Settings.zb_precfgkey_l) || + (zb_precfgkey_h != Settings.zb_precfgkey_h) || + (zb_txradio_dbm != Settings.zb_txradio_dbm) ) { + Settings.zb_channel = zb_channel; + Settings.zb_pan_id = zb_pan_id; + Settings.zb_ext_panid = zb_ext_panid; + Settings.zb_precfgkey_l = zb_precfgkey_l; + Settings.zb_precfgkey_h = zb_precfgkey_h; + Settings.zb_txradio_dbm = zb_txradio_dbm; + restart_flag = 2; + } + } + + + char hex_ext_panid[20] = "0x"; + Uint64toHex(zb_ext_panid, &hex_ext_panid[2], 64); + char hex_precfgkey_l[20] = "0x"; + Uint64toHex(zb_precfgkey_l, &hex_precfgkey_l[2], 64); + char hex_precfgkey_h[20] = "0x"; + Uint64toHex(zb_precfgkey_h, &hex_precfgkey_h[2], 64); + + + Response_P(PSTR("{\"" D_PRFX_ZB D_JSON_ZIGBEE_CONFIG "\":{" + "\"Channel\":%d" + ",\"PanID\":\"0x%04X\"" + ",\"ExtPanID\":\"%s\"" + ",\"KeyL\":\"%s\"" + ",\"KeyH\":\"%s\"" + ",\"TxRadio\":%d" + "}}"), + zb_channel, zb_pan_id, + hex_ext_panid, + hex_precfgkey_l, hex_precfgkey_h, + zb_txradio_dbm); +} + + + + + +extern "C" { + int device_cmp(const void * a, const void * b) { + const Z_Device &dev_a = zigbee_devices.devicesAt(*(uint8_t*)a); + const Z_Device &dev_b = zigbee_devices.devicesAt(*(uint8_t*)b); + const char * fn_a = dev_a.friendlyName; + const char * fn_b = dev_b.friendlyName; + + if (fn_a && fn_b) { + return strcasecmp(fn_a, fn_b); + } else if (!fn_a && !fn_b) { + return (int32_t)dev_a.shortaddr - (int32_t)dev_b.shortaddr; + } else { + if (fn_a) return -1; + return 1; + } + } +# 1627 "/workspace/Tasmota/tasmota/xdrv_23_zigbee_A_impl.ino" + int convert_seconds_to_dhm(uint32_t n, char *result, size_t result_len){ + char fmtstr[] = "%02dmhd"; + uint32_t conversions[3] = {24 * 3600, 3600, 60}; + uint32_t value; + for(int i = 0; i < 3; ++i) { + value = n / conversions[i]; + if(value > 0) { + fmtstr[4] = fmtstr[6-i]; + break; + } + n = n % conversions[i]; + } + + + fmtstr[5] = '\0'; + return snprintf(result, result_len, fmtstr, value); + } +} +void ZigbeeShow(bool json) +{ + if (json) { + return; +#ifdef USE_WEBSERVER + } else { + uint32_t zigbee_num = zigbee_devices.devicesSize(); + if (!zigbee_num) { return; } + if (zigbee_num > 255) { zigbee_num = 255; } + + WSContentSend_P(PSTR("{t}")); + WSContentSend_P(PSTR( + "" + )); + + + uint8_t sorted_idx[zigbee_num]; + for (uint32_t i = 0; i < zigbee_num; i++) { + sorted_idx[i] = i; + } + qsort(sorted_idx, zigbee_num, sizeof(sorted_idx[0]), device_cmp); + + uint32_t now = Rtc.utc_time; + + for (uint32_t i = 0; i < zigbee_num; i++) { + const Z_Device &device = zigbee_devices.devicesAt(sorted_idx[i]); + uint16_t shortaddr = device.shortaddr; + char *name = (char*) device.friendlyName; + + char sdevice[33]; + if (nullptr == name) { + snprintf_P(sdevice, sizeof(sdevice), PSTR(D_DEVICE " 0x%04X"), shortaddr); + name = sdevice; + } + + char sbatt[64]; + snprintf_P(sbatt, sizeof(sbatt), PSTR(" ")); + if (device.validBatteryPercent()) { + snprintf_P(sbatt, sizeof(sbatt), + PSTR(""), + device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14) + ); + } + + uint32_t num_bars = 0; + + char slqi[4]; + slqi[0] = '-'; + slqi[1] = '\0'; + if (device.validLqi()){ + num_bars = changeUIntScale(device.lqi, 0, 254, 0, 4); + snprintf_P(slqi, sizeof(slqi), PSTR("%d"), device.lqi); + } + + WSContentSend_PD(PSTR( + "" + "%s" + "%s" + "
" + ), name, sbatt, slqi); + + if(device.validLqi()) { + for(uint32_t j = 0; j < 4; ++j) { + WSContentSend_PD(PSTR(""), j, (num_bars < j) ? PSTR(" o30") : PSTR("")); + } + } + char dhm[16]; + snprintf_P(dhm, sizeof(dhm), PSTR(" ")); + if(device.validLastSeen()){ + snprintf_P(dhm, sizeof(dhm), PSTR("🕗")); + convert_seconds_to_dhm(now - device.last_seen, &dhm[9], 7); + } + + WSContentSend_PD(PSTR( + "
" + "%s{e}" + ), dhm ); + + + const Z_Data_Thermo & thermo = device.data.find(); + + if (&thermo != nullptr) { + WSContentSend_P(PSTR("┆")); + if (thermo.validTemperature()) { + char buf[12]; + dtostrf(thermo.getTemperature() / 100.0f, 3, 1, buf); + WSContentSend_PD(PSTR(" ☀️ %s°C"), buf); + } + if (thermo.validTempTarget()) { + char buf[12]; + dtostrf(thermo.getTempTarget() / 100.0f, 3, 1, buf); + WSContentSend_PD(PSTR(" 🎯 %s°C"), buf); + } + if (thermo.validThSetpoint()) { + WSContentSend_PD(PSTR(" ⚙️ %d%%"), thermo.getThSetpoint()); + } + if (thermo.validHumidity()) { + WSContentSend_P(PSTR(" 💧 %d%%"), (uint16_t)(thermo.getHumidity() / 100.0f + 0.5f)); + } + if (thermo.validPressure()) { + WSContentSend_P(PSTR(" ⛅ %d hPa"), thermo.getPressure()); + } + + WSContentSend_P(PSTR("{e}")); + } + + + const Z_Data_OnOff & onoff = device.data.find(); + const Z_Data_Light & light = device.data.find(); + bool light_display = (&light != nullptr) ? light.validDimmer() : false; + const Z_Data_Plug & plug = device.data.find(); + if ((&onoff != nullptr) || light_display || (&plug != nullptr)) { + int8_t channels = device.getLightChannels(); + if (channels < 0) { channels = 5; } + WSContentSend_P(PSTR("┆")); + if (&onoff != nullptr) { + WSContentSend_P(PSTR(" %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF)); + } + if (&light != nullptr) { + if (light.validDimmer() && (channels >= 1)) { + WSContentSend_P(PSTR(" 🔅 %d%%"), changeUIntScale(light.getDimmer(),0,254,0,100)); + } + if (light.validCT() && ((channels == 2) || (channels == 5))) { + uint32_t ct_k = (((1000000 / light.getCT()) + 25) / 50) * 50; + WSContentSend_P(PSTR(" %dK"), light.getCT(), ct_k); + } + if (light.validHue() && light.validSat() && (channels >= 3)) { + uint8_t r,g,b; + uint8_t sat = changeUIntScale(light.getSat(), 0, 254, 0, 255); + LightStateClass::HsToRgb(light.getHue(), sat, &r, &g, &b); + WSContentSend_P(PSTR(" #%02X%02X%02X"), r,g,b,r,g,b); + } else if (light.validX() && light.validY() && (channels >= 3)) { + uint8_t r,g,b; + LightStateClass::XyToRgb(light.getX() / 65535.0f, light.getY() / 65535.0f, &r, &g, &b); + WSContentSend_P(PSTR(" #%02X%02X%02X"), r,g,b,r,g,b); + } + } + if (&plug != nullptr) { + WSContentSend_P(PSTR(" ⚡ ")); + if (plug.validMainsVoltage()) { + WSContentSend_P(PSTR(" %dV"), plug.getMainsVoltage()); + } + if (plug.validMainsPower()) { + WSContentSend_P(PSTR(" %dW"), plug.getMainsPower()); + } + } + WSContentSend_P(PSTR("{e}")); + } + } + + WSContentSend_P(PSTR("{t}")); +#endif + } +} + + + + + +bool Xdrv23(uint8_t function) +{ + bool result = false; + + if (zigbee.active) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (!zigbee.init_phase) { + zigbee_devices.runTimer(); + } + break; + case FUNC_LOOP: +#ifdef USE_ZIGBEE_EZSP + if (ZigbeeUploadXmodem()) { + return false; + } +#endif + if (ZigbeeSerial) { + ZigbeeInputLoop(); + ZigbeeOutputLoop(); +#ifdef USE_ZIGBEE_EZSP + ZigbeeGlowPermitJoinLight(); +#endif + } + if (zigbee.state_machine) { + ZigbeeStateMachine_Run(); + } + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ZigbeeShow(false); + break; +#ifdef USE_ZIGBEE_EZSP + + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_ZIGBEE_XFER), HandleZigbeeXfer); + break; +#endif +#endif + case FUNC_PRE_INIT: + ZigbeeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kZbCommands, ZigbeeCommand); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_24_buzzer.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_24_buzzer.ino" +#ifdef USE_BUZZER + + + + +#define XDRV_24 24 + +struct BUZZER { + uint32_t tune = 0; + uint32_t tune_reload = 0; + bool active = true; + bool enable = false; + uint8_t inverted = 0; + uint8_t count = 0; + uint8_t mode = 0; + uint8_t freq_mode = 0; + uint8_t set[2]; + uint8_t duration; + uint8_t state = 0; +} Buzzer; + + + +void BuzzerSet(uint8_t state) +{ + if (Buzzer.inverted) { + state = !state; + } + + if (Buzzer.freq_mode == 1) { + static uint8_t last_state = 0; + if (last_state != state) { + if (state) { + analogWrite(Pin(GPIO_BUZZER, 0), Settings.pwm_range / 2); + } + else { + analogWrite(Pin(GPIO_BUZZER, 0), 0); + } + last_state = state; + } + } + else { + DigitalWrite(GPIO_BUZZER, 0, state); + } + +} + + +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) +{ + Buzzer.set[0] = off; + Buzzer.set[1] = on; + Buzzer.duration = 1; + Buzzer.tune_reload = 0; + Buzzer.mode = mode; + + if (tune) { + uint32_t tune1 = tune; + uint32_t tune2 = tune; + for (uint32_t i = 0; i < 32; i++) { + if (!(tune2 & 0x80000000)) { + tune2 <<= 1; + } else { + Buzzer.tune_reload <<= 1; + Buzzer.tune_reload |= tune1 & 1; + tune1 >>= 1; + } + } + Buzzer.tune = Buzzer.tune_reload; + } + Buzzer.count = count * 2; + + + if (Settings.flag4.buzzer_freq_mode) { + Buzzer.freq_mode = 1; + } + else { + Buzzer.freq_mode = 0; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X),%d"), count, Buzzer.count, on, off, tune, Buzzer.tune, Buzzer.freq_mode); + + Buzzer.enable = (Buzzer.count > 0); + if (Buzzer.enable) { + if (Settings.sleep > PWM_MAX_SLEEP) { + ssleep = PWM_MAX_SLEEP; + } else { + ssleep = Settings.sleep; + } + } + else { + ssleep = Settings.sleep; + BuzzerSet(0); + } +} + +void BuzzerSetStateToLed(uint32_t state) +{ + if (Buzzer.enable && (2 == Buzzer.mode)) { + Buzzer.state = (state != 0); + BuzzerSet(Buzzer.state); + } +} + +void BuzzerBeep(uint32_t count) +{ + BuzzerBeep(count, 1, 1, 0, 0); +} + +void BuzzerEnabledBeep(uint32_t count, uint32_t duration) +{ + if (Settings.flag3.buzzer_enable) { + BuzzerBeep(count, duration, 1, 0, 0); + } +} + + + +bool BuzzerPinState(void) +{ + if (XdrvMailbox.index == AGPIO(GPIO_BUZZER_INV)) { + Buzzer.inverted = 1; + XdrvMailbox.index -= (AGPIO(GPIO_BUZZER_INV) - AGPIO(GPIO_BUZZER)); + return true; + } + return false; +} + +void BuzzerInit(void) +{ + if (PinUsed(GPIO_BUZZER)) { + pinMode(Pin(GPIO_BUZZER), OUTPUT); + BuzzerSet(0); + } else { + Buzzer.active = false; + } +} + +void BuzzerEvery100mSec(void) +{ + if (Buzzer.enable && (Buzzer.mode != 2)) { + if (Buzzer.count) { + if (Buzzer.duration) { + Buzzer.duration--; + if (!Buzzer.duration) { + if (Buzzer.tune) { + Buzzer.state = Buzzer.tune & 1; + Buzzer.tune >>= 1; + } else { + Buzzer.tune = Buzzer.tune_reload; + Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1; + Buzzer.state = Buzzer.count & 1; + if (Buzzer.mode) { + Buzzer.count |= 2; + } + } + Buzzer.duration = Buzzer.set[Buzzer.state]; + } + } + BuzzerSet(Buzzer.state); + } else { + Buzzer.enable = false; + } + } +} + + + + + +const char kBuzzerCommands[] PROGMEM = "|" + "Buzzer" ; + +void (* const BuzzerCommand[])(void) PROGMEM = { + &CmndBuzzer }; + +void CmndBuzzer(void) +{ +# 210 "/workspace/Tasmota/tasmota/xdrv_24_buzzer.ino" + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload != 0) { + uint32_t parm[4] = { 0 }; + uint32_t mode = 0; + ParseParameters(4, parm); + if (XdrvMailbox.payload <= 0) { + parm[0] = 1; + mode = -XdrvMailbox.payload; + } + for (uint32_t i = 1; i < 3; i++) { + if (parm[i] < 1) { parm[i] = 1; } + } + BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode); + } else { + BuzzerBeep(0); + } + } else { + BuzzerBeep(1); + } + ResponseCmndDone(); +} + + + + + +bool Xdrv24(uint8_t function) +{ + bool result = false; + + if (Buzzer.active) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + BuzzerEvery100mSec(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kBuzzerCommands, BuzzerCommand); + break; + case FUNC_PRE_INIT: + BuzzerInit(); + break; + case FUNC_PIN_STATE: + result = BuzzerPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +# 21 "/workspace/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +#ifdef USE_A4988_STEPPER + + + + +#define XDRV_25 25 + +#include + +short A4988_dir_pin = 0; +short A4988_stp_pin = 0; +short A4988_ms1_pin = 0; +short A4988_ms2_pin = 0; +short A4988_ms3_pin = 0; +short A4988_ena_pin = 0; +int A4988_spr = 0; +float A4988_rpm = 0; +short A4988_mis = 0; + +A4988_Stepper* myA4988 = nullptr; + +void A4988Init(void) +{ + A4988_dir_pin = Pin(GPIO_A4988_DIR); + A4988_stp_pin = Pin(GPIO_A4988_STP); + A4988_ena_pin = Pin(GPIO_A4988_ENA); + A4988_ms1_pin = Pin(GPIO_A4988_MS1); + A4988_ms2_pin = Pin(GPIO_A4988_MS1, 1); + A4988_ms3_pin = Pin(GPIO_A4988_MS1, 2); + A4988_spr = 200; + A4988_rpm = 30; + A4988_mis = 1; + + myA4988 = new A4988_Stepper( A4988_spr + , A4988_rpm + , A4988_mis + , A4988_dir_pin + , A4988_stp_pin + , A4988_ena_pin + , A4988_ms1_pin + , A4988_ms2_pin + , A4988_ms3_pin ); +} + +const char kA4988Commands[] PROGMEM = "Motor|" + "Move|Rotate|Turn|MIS|SPR|RPM"; + +void (* const A4988Command[])(void) PROGMEM = { + &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; + +void CmndDoMove(void) { + if (XdrvMailbox.data_len > 0) { + long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doMove(stepsPlease); + ResponseCmndDone(); + } +} + +void CmndDoRotate(void) { + if (XdrvMailbox.data_len > 0) { + long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doRotate(degrsPlease); + ResponseCmndDone(); + } +} + +void CmndDoTurn(void) { + if (XdrvMailbox.data_len > 0) { + float turnsPlease = strtod(XdrvMailbox.data,nullptr); + myA4988->doTurn(turnsPlease); + ResponseCmndDone(); + } +} + +void CmndSetMIS(void) { + if (PinUsed(GPIO_A4988_MS1) && PinUsed(GPIO_A4988_MS1, 1) && PinUsed(GPIO_A4988_MS1, 2) && (XdrvMailbox.data_len > 0)) { + short newMIS = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setMIS(newMIS); + ResponseCmndDone(); + } +} + +void CmndSetSPR(void) { + if (XdrvMailbox.data_len > 0) { + int newSPR = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setSPR(newSPR); + ResponseCmndDone(); + } +} + +void CmndSetRPM(void) { + if (XdrvMailbox.data_len > 0) { + short newRPM = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setRPM(newRPM); + ResponseCmndDone(); + } +} + + + + +bool Xdrv25(uint8_t function) +{ + bool result = false; + if (PinUsed(GPIO_A4988_DIR) && PinUsed(GPIO_A4988_STP)) { + switch (function) { + case FUNC_INIT: + A4988Init(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kA4988Commands, A4988Command); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +#ifdef USE_LIGHT +#ifdef USE_ARILUX_RF + + + + +#define XDRV_26 26 + +const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; + +const uint8_t ARILUX_RF_MAX_CHANGES = 51; +const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; +const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; + +struct ARILUX { + int rf_timings[ARILUX_RF_MAX_CHANGES]; + + unsigned long rf_received_value = 0; + unsigned long rf_last_received_value = 0; + unsigned long rf_last_time = 0; + unsigned long rf_lasttime = 0; + + unsigned int rf_change_count = 0; + unsigned int rf_repeat_count = 0; + + uint8_t rf_toggle = 0; +} Arilux; + +#ifndef USE_WS2812_DMA +void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; +#endif + +void AriluxRfInterrupt(void) +{ + unsigned long time = micros(); + int duration = time - Arilux.rf_lasttime; + + if (duration > ARILUX_RF_SEPARATION_LIMIT) { + if (abs(duration - Arilux.rf_timings[0]) < 200) { + Arilux.rf_repeat_count++; + if (Arilux.rf_repeat_count == 2) { + unsigned long code = 0; + const int delay = Arilux.rf_timings[0] / 31; + const int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; + for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { + code <<= 1; + if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { + code |= 1; + } + } + if (Arilux.rf_change_count > 49) { + Arilux.rf_received_value = code; + } + Arilux.rf_repeat_count = 0; + } + } + Arilux.rf_change_count = 0; + } + if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { + Arilux.rf_change_count = 0; + Arilux.rf_repeat_count = 0; + } + Arilux.rf_timings[Arilux.rf_change_count++] = duration; + Arilux.rf_lasttime = time; +} + +void AriluxRfHandler(void) +{ + unsigned long now = millis(); + if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { + Arilux.rf_last_received_value = Arilux.rf_received_value; + Arilux.rf_last_time = now; + + uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; + if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { + Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; + Settings.rf_code[1][7] = hostcode & 0xFF; + } + uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; + + DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); + + if (hostcode == stored_hostcode) { + char command[33]; + char value = '-'; + command[0] = '\0'; + uint8_t keycode = Arilux.rf_received_value & 0xFF; + switch (keycode) { + case 1: + case 3: + snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); + break; + case 2: + Arilux.rf_toggle++; + Arilux.rf_toggle &= 0x3; + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); + break; + case 4: + value = '+'; + case 7: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); + break; + case 5: + value = '+'; + case 8: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); + break; + case 6: + value = '+'; + case 9: + snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); + break; + default: { + if ((keycode >= 10) && (keycode <= 21)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); + } + } + } + if (strlen(command)) { + ExecuteCommand(command, SRC_LIGHT); + } + } + } + Arilux.rf_received_value = 0; +} + +void AriluxRfInit(void) +{ + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { + if (Settings.last_module != Settings.module) { + Settings.rf_code[1][6] = 0; + Settings.rf_code[1][7] = 0; + Settings.last_module = Settings.module; + } + Arilux.rf_received_value = 0; + + digitalWrite(Pin(GPIO_ARIRFSEL), 0); + attachInterrupt(Pin(GPIO_ARIRFRCV), AriluxRfInterrupt, CHANGE); + } +} + +void AriluxRfDisable(void) +{ + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { + detachInterrupt(Pin(GPIO_ARIRFRCV)); + digitalWrite(Pin(GPIO_ARIRFSEL), 1); + } +} + + + + + +bool Xdrv26(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (PinUsed(GPIO_ARIRFRCV)) { AriluxRfHandler(); } + break; + case FUNC_EVERY_SECOND: + if (10 == uptime) { AriluxRfInit(); } + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_27_shutter.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_27_shutter.ino" +#ifdef USE_SHUTTER + + + + +#define XDRV_27 27 +#ifndef SHUTTER_STEPPER + #define SHUTTER_STEPPER +#endif + +#define D_SHUTTER "SHUTTER" + +const uint16_t MOTOR_STOP_TIME = 500; +const uint16_t RESOLUTION = 1000; +const uint8_t STEPS_PER_SECOND = 20; +const uint16_t pwm_max = 500; +const uint16_t pwm_min = 90; + +uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; +uint16_t messwerte[5] = {30,50,70,90,100}; + +int32_t velocity_max = 0; +int32_t velocity_change_per_step_max = 0; +int32_t min_runtime_ms = 0; +int32_t current_stop_way = 0; +int32_t next_possible_stop_position = 0; +int32_t toBeAcc = 0; + + +const uint8_t MAX_MODES = 7; +enum Shutterposition_mode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; +enum Shutterswitch_mode {SHT_SWITCH, SHT_PULSE,}; +enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; + +const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" + D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" D_CMND_SHUTTER_PWMRANGE "|" + D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" + D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" + D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC; + +void (* const ShutterCommand[])(void) PROGMEM = { + &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, &CmndShutterPwmRange, + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, + &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, + &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec}; + + const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d}"; + const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; + +#include + +Ticker TickerShutter; + +struct SHUTTER { + uint32_t time; + int32_t open_max; + int32_t target_position; + int32_t start_position; + int32_t real_position; + uint16_t open_time; + uint16_t close_time; + uint16_t close_velocity; + int8_t direction; + int8_t lastdirection; + uint8_t switch_mode; + int16_t motordelay; + int16_t pwm_velocity; + uint16_t pwm_value; + uint16_t close_velocity_max; + int32_t accelerator; +} Shutter[MAX_SHUTTERS]; + +struct SHUTTERGLOBAL { + power_t RelayShutterMask = 0; + power_t RelayOldMask = 0; + power_t RelayCurrentMask = 0; + uint8_t position_mode = 0; + uint8_t skip_relay_change; + uint8_t start_reported = 0; + uint16_t open_velocity_max = 1000; +} ShutterGlobal; + +#define SHT_DIV_ROUND(__A,__B) (((__A) + (__B)/2) / (__B)) + +void ShutterLogPos(uint32_t i) +{ + char stemp2[10]; + dtostrfd((float)Shutter[i].time / STEPS_PER_SECOND, 2, stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"), + i+1, Shutter[i].real_position, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction, Shutter[i].motordelay, stemp2, Shutter[i].pwm_velocity, Shutter[i].pwm_value); +} + +void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source) +{ + + if (device <= devices_present) ExecuteCommandPower(device,state,source); +} + +void ShutterUpdateVelocity(uint8_t i) +{ + + + Shutter[i].pwm_velocity += Shutter[i].accelerator; + Shutter[i].pwm_velocity = tmax(0,tmin(Shutter[i].direction==1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max,Shutter[i].pwm_velocity)); +} + +void ShutterRtc50mS(void) +{ + + for (uint8_t i = 0; i < shutters_present; i++) { + if (Shutter[i].direction) { + + Shutter[i].real_position = ShutterCalculatePosition(i); + Shutter[i].time++; + ShutterCalculateAccelerator(i); + switch (ShutterGlobal.position_mode) { + case SHT_PWM_VALUE: + ShutterUpdateVelocity(i); + Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0); + Shutter[i].pwm_value = SHT_DIV_ROUND((Settings.shutter_pwmrange[1][i]-Settings.shutter_pwmrange[0][i]) * Shutter[i].real_position , Shutter[i].open_max)+Settings.shutter_pwmrange[0][i]; + analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value); + break; + + case SHT_COUNTER: + if (Shutter[i].accelerator) { + + ShutterUpdateVelocity(i); + analogWriteFreq(Shutter[i].pwm_velocity); + analogWrite(Pin(GPIO_PWM1, i), 50); + } + break; + } + } + } +} + +int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent*10 : (Settings.shuttercoeff[1][index] * percent + (Settings.shuttercoeff[0][index]*10))*10; + } else { + uint32_t realpos; + + for (uint32_t j = 0; j < 5; j++) { + if (0 == Settings.shuttercoeff[j][index]) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); + for (uint32_t k = 0; k < 5; k++) { + Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]); + } + } + } + for (uint32_t k = 0; k < 5; k++) { + if ((percent * 10) >= Settings.shuttercoeff[k][index]) { + realpos = SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[k+1], 100); + + } else { + if (0 == k) { + realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter[index].open_max * calibrate_pos[k+1], Settings.shuttercoeff[k][index]), 10); + } else { + + + realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]), Settings.shuttercoeff[k][index] - Settings.shuttercoeff[k-1][index]), 100); + } + break; + } + } + return realpos; + } +} + +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return (Settings.shuttercoeff[2][index] * 5 > realpos/10) ? SHT_DIV_ROUND(realpos/10, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos/10-Settings.shuttercoeff[0][index]*10, Settings.shuttercoeff[1][index]); + } else { + uint16_t realpercent; + + for (uint32_t j = 0; j < 5; j++) { + if (realpos >= Shutter[index].open_max * calibrate_pos[j+1] / 100) { + realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[j][index], 10); + + } else { + if (0 == j) { + realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * Settings.shuttercoeff[j][index], calibrate_pos[j+1]), Shutter[index].open_max); + } else { + + + + realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * (Settings.shuttercoeff[j][index] - Settings.shuttercoeff[j-1][index]), (calibrate_pos[j+1] - calibrate_pos[j])), Shutter[index].open_max) ; + } + break; + } + } + return (int16_t)realpercent < 0 ? 0 : realpercent; + } +} + +void ShutterInit(void) +{ + shutters_present = 0; + ShutterGlobal.RelayShutterMask = 0; + + ShutterGlobal.RelayOldMask = power; + + + + if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) { + ShutterGlobal.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : ShutterGlobal.open_velocity_max; + } + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + + Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); + if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { + shutters_present++; + + + ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ; + + + switch (Settings.pulse_timer[i]) { + case 0: + Shutter[i].switch_mode = SHT_SWITCH; + break; + default: + Shutter[i].switch_mode = SHT_PULSE; + break; + } + + if (Settings.shutter_mode == SHT_UNDEF) { + bool relay_in_interlock = false; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate...")); + + for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { + + if (Settings.interlock[j] && (Settings.interlock[j] & ShutterGlobal.RelayShutterMask)) { + + relay_in_interlock = true; + } + } + + if (relay_in_interlock) { + ShutterGlobal.position_mode = SHT_TIME; + } else { + ShutterGlobal.position_mode = SHT_TIME_UP_DOWN; + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { + ShutterGlobal.position_mode = SHT_COUNTER; + } + } + + } else { + ShutterGlobal.position_mode = Settings.shutter_mode; + } + + + TickerShutter.attach_ms(50, ShutterRtc50mS ); + + + Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; + + + Shutter[i].open_time = Settings.shutter_opentime[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; + Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + + + Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10; + Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ; + + + if (Settings.shutter_set50percent[i] != 50) { + Settings.shuttercoeff[1][i] = Shutter[i].open_max/10 * (100 - Settings.shutter_set50percent[i] ) / 5000 ; + Settings.shuttercoeff[0][i] = Shutter[i].open_max/100 - (Settings.shuttercoeff[1][i] * 10); + Settings.shuttercoeff[2][i] = (int32_t)(Settings.shuttercoeff[0][i]*10 + 5 * Settings.shuttercoeff[1][i]) / 5; + + } + ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1); + + Shutter[i].real_position = ShutterPercentToRealPosition(Settings.shutter_position[i], i); + + Shutter[i].start_position = Shutter[i].target_position = Shutter[i].real_position; + Shutter[i].motordelay = Settings.shutter_motordelay[i]; + Shutter[i].lastdirection = (50 < Settings.shutter_position[i]) ? 1 : -1; + + switch (ShutterGlobal.position_mode) { + case SHT_PWM_VALUE: + ShutterGlobal.open_velocity_max = RESOLUTION; + + Settings.shutter_pwmrange[0][i] = Settings.shutter_pwmrange[0][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_min; + Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[1][i] : pwm_max; + break; + } + Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time; + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"), + i+1, Shutter[i].real_position, + (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0); + + } else { + + break; + } + ShutterLimitRealAndTargetPositions(i); + Settings.shutter_accuracy = 1; + } +} + +void ShutterReportPosition(bool always, uint32_t index) +{ + Response_P(PSTR("{")); + rules_flag.shutter_moving = 0; + uint32_t i = 0; + uint32_t n = shutters_present; + if( index != MAX_SHUTTERS) { + i = index; + n = index+1; + } + for (i; i < n; i++) { + + uint32_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i); + if (Shutter[i].direction != 0) { + rules_flag.shutter_moving = 1; + ShutterLogPos(i); + } + if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); } + uint32_t target = ShutterRealToPercentPosition(Shutter[i].target_position, i); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter[i].direction,(Settings.shutter_options[i] & 1) ? 100-target : target ); + } + ResponseJsonEnd(); + if (always || (rules_flag.shutter_moving)) { + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); + } + +} + +void ShutterLimitRealAndTargetPositions(uint32_t i) { + if (Shutter[i].real_position<0) Shutter[i].real_position = 0; + if (Shutter[i].real_position>Shutter[i].open_max) Shutter[i].real_position = Shutter[i].open_max; + if (Shutter[i].target_position<0) Shutter[i].target_position = 0; + if (Shutter[i].target_position>Shutter[i].open_max) Shutter[i].target_position = Shutter[i].open_max; +} + +void ShutterCalculateAccelerator(uint8_t i) +{ + + if (Shutter[i].direction != 0) { + switch (ShutterGlobal.position_mode) { + case SHT_COUNTER: + case SHT_PWM_VALUE: + + velocity_max = Shutter[i].direction == 1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max; + + velocity_change_per_step_max = velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1); + + min_runtime_ms = Shutter[i].pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max; + + current_stop_way = (min_runtime_ms * (Shutter[i].pwm_velocity+velocity_change_per_step_max)/100 - Shutter[i].pwm_velocity)*RESOLUTION/ShutterGlobal.open_velocity_max * Shutter[i].direction ; + next_possible_stop_position = Shutter[i].real_position + current_stop_way ; + toBeAcc = 0; + + if (Shutter[i].accelerator < 0 || (next_possible_stop_position * Shutter[i].direction) +RESOLUTION*Shutter[i].pwm_velocity/ShutterGlobal.open_velocity_max>= Shutter[i].target_position * Shutter[i].direction ) { + + toBeAcc = 100+(Shutter[i].direction*(next_possible_stop_position-Shutter[i].target_position)*velocity_max/Shutter[i].pwm_velocity*10/RESOLUTION); + Shutter[i].accelerator = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10)); + } else if ( Shutter[i].accelerator > 0 && Shutter[i].pwm_velocity == velocity_max) { + Shutter[i].accelerator = 0; + } + break; + } + } +} + +void ShutterDecellerateForStop(uint8_t i) +{ + switch (ShutterGlobal.position_mode) { + case SHT_PWM_VALUE: + case SHT_COUNTER: + int16_t missing_steps; + Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>4 ? (Shutter[i].motordelay*11)/10 : 4) ); + while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator ) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator ); + + + delay(50); + } + if (ShutterGlobal.position_mode == SHT_COUNTER){ + missing_steps = ((Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter[i].pwm_velocity); + Shutter[i].accelerator = 0; + Shutter[i].pwm_velocity = Shutter[i].pwm_velocity > 250 ? 250 : Shutter[i].pwm_velocity; + analogWriteFreq(Shutter[i].pwm_velocity); + analogWrite(Pin(GPIO_PWM1, i), 50); + Shutter[i].pwm_velocity = 0; + analogWriteFreq(Shutter[i].pwm_velocity); + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) { + delay(1); + } + analogWrite(Pin(GPIO_PWM1, i), 0); + Shutter[i].real_position = ShutterCalculatePosition(i); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], Shutter[i].start_position); + + } + Shutter[i].direction = 0; + Shutter[i].pwm_velocity = 0; + break; + } +} + +void ShutterPowerOff(uint8_t i) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d. Switchmode %d"), i,Shutter[i].switch_mode); + ShutterDecellerateForStop(i); + if (Shutter[i].direction !=0) { + Shutter[i].direction = 0; + delay(MOTOR_STOP_TIME); + } + switch (Shutter[i].switch_mode) { + case SHT_SWITCH: + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + } + if ((1 << (Settings.shutter_startrelay[i])) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_PULSE: + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter[i].direction == 1 ? 0 : (uint8_t)(ShutterGlobal.position_mode == SHT_TIME)) ; + + if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) { + ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER); + + if ((1 << (Settings.shutter_startrelay[i])) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + } else { + last_source = SRC_SHUTTER; + } + break; + } + + switch (ShutterGlobal.position_mode) { + case SHT_PWM_VALUE: + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter[i].pwm_value); + ExecuteCommand(scmnd, SRC_BUTTON); + break; + } +} + +void ShutterUpdatePosition(void) +{ + + char scommand[CMDSZ]; + char stopic[TOPSZ]; + for (uint32_t i = 0; i < shutters_present; i++) { + if (Shutter[i].direction != 0) { + if (!ShutterGlobal.start_reported) { + ShutterReportPosition(true, i); + XdrvRulesProcess(); + ShutterGlobal.start_reported = 1; + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_cur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change %d, dir: %d"),Shutter[i].time,toBeAcc,current_stop_way, + Shutter[i].pwm_velocity,velocity_max, Shutter[i].accelerator,min_runtime_ms,Shutter[i].real_position, next_possible_stop_position,Shutter[i].target_position,velocity_change_per_step_max,Shutter[i].direction); + + + if ( Shutter[i].real_position * Shutter[i].direction >= Shutter[i].target_position * Shutter[i].direction || Shutter[i].pwm_velocity=<%s>, max10s?"),i+i, rules_vars[i]); + rules_flag.shutter_moving = 1; + XdrvRulesProcess(); + uptime_Local = uptime; + while (uptime_Local+10 > uptime && (String)rules_vars[i] == "99") { + loop(); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. Done")); +#endif +} + +void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) +{ + + if ( ( (1 == direction) && ((Shutter[i].open_max - Shutter[i].real_position) / 100 <= 2) ) + || ( (-1 == direction) && (Shutter[i].real_position / Shutter[i].close_velocity <= 2)) ) { + ShutterGlobal.skip_relay_change = 1; + } else { + Shutter[i].pwm_velocity = 0; + switch (ShutterGlobal.position_mode) { +#ifdef SHUTTER_STEPPER + case SHT_COUNTER: + analogWriteFreq(Shutter[i].pwm_velocity); + analogWrite(Pin(GPIO_PWM1, i), 0); + RtcSettings.pulse_counter[i] = 0; + break; +#endif + } + Shutter[i].accelerator = ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1); + Shutter[i].target_position = target_pos; + Shutter[i].start_position = Shutter[i].real_position; + rules_flag.shutter_moving = 1; + ShutterAllowPreStartProcedure(i); + Shutter[i].time = 0; + Shutter[i].direction = direction; + ShutterGlobal.skip_relay_change = 0; + rules_flag.shutter_moved = 0; + ShutterGlobal.start_reported = 0; + + } + +} + +int32_t ShutterCalculatePosition(uint32_t i) +{ + + if (Shutter[i].direction != 0) { + switch (ShutterGlobal.position_mode) { + case SHT_COUNTER: + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND / ShutterGlobal.open_velocity_max * RESOLUTION)+Shutter[i].start_position; + break; + case SHT_TIME: + case SHT_TIME_UP_DOWN: + case SHT_TIME_GARAGE: + return Shutter[i].start_position + ( (Shutter[i].time - Shutter[i].motordelay) * (Shutter[i].direction > 0 ? RESOLUTION : -Shutter[i].close_velocity)); + break; + case SHT_PWM_TIME: + break; + case SHT_PWM_VALUE: + return Shutter[i].real_position; + break; + default: + break; + } + } else { + return Shutter[i].real_position; + } +} + +void ShutterRelayChanged(void) +{ + + + + + char stemp1[10]; + + for (uint32_t i = 0; i < shutters_present; i++) { + power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; + + + uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed); + if (manual_relays_changed) { + + ShutterLimitRealAndTargetPositions(i); + switch (Shutter[i].switch_mode ) { + case SHT_PULSE: + if (Shutter[i].direction != 0 && powerstate_local) { + Shutter[i].target_position = Shutter[i].real_position; + powerstate_local = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, Shutter[i].target_position, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed); + } + break; + default: + last_source = SRC_SHUTTER; + if (Shutter[i].direction != 0 ) ShutterPowerOff(i); + } + switch (ShutterGlobal.position_mode) { + + case SHT_TIME_UP_DOWN: + case SHT_COUNTER: + case SHT_PWM_VALUE: + case SHT_PWM_TIME: + ShutterPowerOff(i); + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter[i].open_max); + break; + case 3: + ShutterStartInit(i, -1, 0); + break; + default: + + Shutter[i].target_position = Shutter[i].real_position; + } + break; + case SHT_TIME: + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter[i].open_max); + break; + case 2: + ShutterStartInit(i, -1, 0); + break; + default: + + Shutter[i].target_position = Shutter[i].real_position; + } + break; + case SHT_TIME_GARAGE: + switch (powerstate_local) { + case 1: + ShutterStartInit(i, Shutter[i].lastdirection*-1 , Shutter[i].lastdirection == 1 ? 0 : Shutter[i].open_max); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter[i].target_position); + break; + default: + Shutter[i].target_position = Shutter[i].real_position; + } + + + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter[i].target_position, powerstate_local); + } + } +} + +bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { + + uint32 min_shutterbutton_hold_timer = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer)) + min_shutterbutton_hold_timer = Button.hold_timer[i]; + } + return ((-1 != min_shutterbutton_hold_timer) && (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1))); +} + +void ShutterButtonHandler(void) +{ + uint8_t buttonState = SHT_NOT_PRESSED; + uint8_t button = XdrvMailbox.payload; + uint8_t press_index; + uint32_t button_index = XdrvMailbox.index; + uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; + uint16_t loops_per_second = 1000 / Settings.button_debounce; + + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + if (Settings.flag.button_single) { + buttonState = SHT_PRESSED_MULTI; + press_index = 1; + } else { + if ((Shutter[shutter_index].direction) && (Button.press_counter[button_index]==0)) { + buttonState = SHT_PRESSED_IMMEDIATE; + press_index = 1; + Button.press_counter[button_index] = 99; + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + + Button.window_timer[button_index] = (loops_per_second >> 2) * 3; + } + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (!Settings.flag.button_single) { + if (Settings.param[P_HOLD_IGNORE] > 0) { + if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; + Button.press_counter[button_index] = 0; + } + } + if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) + Button.press_counter[i] = 99; + press_index = 0; + buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS; + } + if (Button.press_counter[button_index]<99) { + press_index = 0; + buttonState = SHT_PRESSED_HOLD; + } + Button.press_counter[button_index] = 0; + } + if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { + press_index = -1; + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS; + } else { + buttonState = SHT_PRESSED_EXT_HOLD; + } + } + } + } + + if (!Settings.flag.button_single) { + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) { + if (Button.press_counter[button_index]<99) { + + uint32 min_shutterbutton_press_counter = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d, i %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter, i); + if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (i != button_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { + min_shutterbutton_press_counter = Button.press_counter[i]; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: min_shutterbutton_press_counter %d"), min_shutterbutton_press_counter); + } + } + if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: simultanous presss deteced")); + press_index = Button.press_counter[button_index]; + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index)) + Button.press_counter[i] = 99; + buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; + } + if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) { + + press_index = Button.press_counter[button_index]; + buttonState = SHT_PRESSED_MULTI; + } + } + Button.press_counter[button_index] = 0; + } + } + } + + if (buttonState != SHT_NOT_PRESSED) { + if ((!Settings.flag.button_restrict) && (((press_index>=5) && (press_index<=7)) || (buttonState == SHT_PRESSED_EXT_HOLD) || (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS))){ + + uint8_t shutter_index_num_buttons = 0; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) { + shutter_index_num_buttons++; + } + } + if ((buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_MULTI))){ + + + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } else if ((buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_EXT_HOLD))){ + + + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + } + if (buttonState <= SHT_PRESSED_IMMEDIATE) { + if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { + uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); + if (pos_press_index>3) pos_press_index=3; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1); + XdrvMailbox.index = shutter_index +1; + last_source = SRC_BUTTON; + XdrvMailbox.data_len = 0; + char databuf[1] = ""; + XdrvMailbox.data = databuf; + XdrvMailbox.command = NULL; + if (buttonState == SHT_PRESSED_IMMEDIATE) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } else { + uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; + if (position) { + if (Shutter[shutter_index].direction) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } else { + XdrvMailbox.payload = position = (position-1)<<1; + + if (102 == position) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterToggle(); + } else { + CmndShutterPosition(); + } + if (Settings.shutter_button[button_index] & ((0x01<<26)< 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (dir) { + XdrvMailbox.payload = (Shutter[index].lastdirection > 0) ? 0 : 100; + } + else { + XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter[index].real_position, index)) ? 0 : 100; + } + XdrvMailbox.data_len = 0; + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } +} + + + + + +void CmndShutterOpen(void) +{ + + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + XdrvMailbox.payload = 100; + last_source = SRC_WEBGUI; + CmndShutterPosition(); +} + +void CmndShutterStopOpen(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter[index].direction) { + CmndShutterStop(); + } else { + CmndShutterOpen(); + } + } +} + +void CmndShutterClose(void) +{ + + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + XdrvMailbox.payload = 0; + XdrvMailbox.data_len = 0; + last_source = SRC_WEBGUI; + CmndShutterPosition(); +} + +void CmndShutterStopClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter[index].direction) { + CmndShutterStop(); + } else { + CmndShutterClose(); + } + } +} + +void CmndShutterToggle(void) +{ + ShutterToggle(false); +} + +void CmndShutterToggleDir(void) +{ + ShutterToggle(true); +} + +void CmndShutterStopToggle(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter[index].direction) { + CmndShutterStop(); + } else { + CmndShutterToggle(); + } + } +} + +void CmndShutterStopToggleDir(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter[index].direction) { + CmndShutterStop(); + } else { + CmndShutterToggleDir(); + } + } +} + +void CmndShutterStop(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + uint32_t i = XdrvMailbox.index -1; + if (Shutter[i].direction != 0) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction); + + int32_t temp_realpos = ShutterCalculatePosition(i); + XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } else { + if (XdrvMailbox.command) + ResponseCmndDone(); + } + } else { + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterIncDec(void) +{ + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + XdrvMailbox.payload = ShutterRealToPercentPosition(Shutter[XdrvMailbox.index-1].target_position, XdrvMailbox.index-1)+XdrvMailbox.payload; + + XdrvMailbox.payload = XdrvMailbox.payload < 0 ? 0 : (XdrvMailbox.payload > 100 ? 100 : XdrvMailbox.payload); + CmndShutterPosition(); + } + } +} + + +void CmndShutterPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + uint32_t index = XdrvMailbox.index-1; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); + + + + if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { + + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) { + CmndShutterOpen(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) { + CmndShutterClose(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLE)) { + CmndShutterToggle(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDIR)) { + CmndShutterToggleDir(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter[index].direction) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) { + XdrvMailbox.payload = -99; + CmndShutterStop(); + return; + } + } + + int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter[index].real_position, index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); + + target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; + if (XdrvMailbox.payload != -99) { + + Shutter[index].target_position = ShutterPercentToRealPosition(target_pos_percent, index); + + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter[index].real_position ,Shutter[index].target_position,target_pos_percent); + } + if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter[index].target_position - Shutter[index].real_position ) / Shutter[index].close_velocity > 2) { + if (Settings.shutter_options[index] & 4) { + if (0 == target_pos_percent) Shutter[index].target_position -= 1 * RESOLUTION * STEPS_PER_SECOND; + if (100 == target_pos_percent) Shutter[index].target_position += 1 * RESOLUTION * STEPS_PER_SECOND; + } + int8_t new_shutterdirection = Shutter[index].real_position < Shutter[index].target_position ? 1 : -1; + if (Shutter[index].direction == -new_shutterdirection) { + ShutterPowerOff(index); + } + if (Shutter[index].direction != new_shutterdirection) { + ShutterStartInit(index, new_shutterdirection, Shutter[index].target_position); + switch (ShutterGlobal.position_mode) { + case SHT_COUNTER: + case SHT_PWM_TIME: + case SHT_PWM_VALUE: + case SHT_TIME_UP_DOWN: + if (!ShutterGlobal.skip_relay_change) { + + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); + + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } + if (ShutterGlobal.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); + break; + case SHT_TIME: + if (!ShutterGlobal.skip_relay_change) { + if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter[index].switch_mode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); + } + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + } + break; + case SHT_TIME_GARAGE: + if (!ShutterGlobal.skip_relay_change) { + if (new_shutterdirection == Shutter[index].lastdirection) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter[index].switch_mode == SHT_PULSE); + for (uint8_t k=0 ; k <= (uint8_t)(Shutter[index].switch_mode == SHT_PULSE) ; k++) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + delay(500); + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + delay(500); + } + + Shutter[index].time = 0; + } + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } + break; + } + ShutterGlobal.RelayCurrentMask = 0; + } + } else { + target_pos_percent = ShutterRealToPercentPosition(Shutter[index].real_position, index); + ShutterReportPosition(true, index); + } + XdrvMailbox.index = index +1; + if (XdrvMailbox.command) + ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); + } else { + ShutterReportPosition(true, MAX_SHUTTERS); + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterStopPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter[index].direction) { + XdrvMailbox.payload = -99; + CmndShutterStop(); + } else { + CmndShutterPosition(); + } + } +} +void CmndShutterOpenTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterCloseTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterMotorDelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(STEPS_PER_SECOND * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / STEPS_PER_SECOND, 2, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) { + ShutterGlobal.position_mode = XdrvMailbox.payload; + Settings.shutter_mode = XdrvMailbox.payload; + ShutterInit(); + } + ResponseCmndNumber(ShutterGlobal.position_mode); +} + +void CmndShutterRelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + if (XdrvMailbox.payload > 0) { + ShutterGlobal.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1); + } else { + ShutterGlobal.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + } + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + ShutterInit(); + + } + ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); + } +} + +void CmndShutterButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + uint32_t setting = 0; +# 1218 "/workspace/Tasmota/tasmota/xdrv_27_shutter.ino" + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + uint32_t button_index = 0; + bool done = false; + bool isShortCommand = false; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field; + switch (str[0]) { + case '-': + field = -1; + break; + case 't': + field = 102; + break; + default: + field = atoi(str); + break; + } + switch (i) { + case 0: + if ((field >= -1) && (field<=4)) { + button_index = (field<=0)?(-1):field; + done = (button_index==-1); + } else + done = true; + break; + case 1: + if (!strcmp_P(str, PSTR("up"))) { + setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("down"))) { + setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("updown"))) { + setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("toggle"))) { + setting |= (((102>>1)+1)<<2) | (((50>>1)+1)<<8); + isShortCommand = true; + break; + } + case 2: + if (isShortCommand) { + if ((field==1) && (setting & (0x3F<<(2+6*3)))) + + setting |= (0x3<<29); + done = true; + break; + } + case 3: + case 4: + if ((field >= -1) && (field<=102)) + setting |= (((field>>1)+1)<<(i*6 + (2-6))); + break; + case 5: + case 6: + case 7: + case 8: + case 9: + if (field==1) + setting |= (1<<(i + (26-5))); + break; + } + if (done) break; + } + + if (button_index) { + if (button_index==-1) { + + for (uint32_t i=0 ; i < MAX_KEYS ; i++) + if ((Settings.shutter_button[i]&0x3) == (XdrvMailbox.index-1)) + Settings.shutter_button[i] = 0; + } else { + if (setting) { + + setting |= (1<<31); + setting |= (XdrvMailbox.index-1) & 0x3; + } + Settings.shutter_button[button_index-1] = setting; + } + } + } + char setting_chr[30*MAX_KEYS] = "-", *setting_chr_ptr = setting_chr; + for (uint32_t i=0 ; i < MAX_KEYS ; i++) { + setting = Settings.shutter_button[i]; + if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) { + if (*setting_chr_ptr == 0) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|")); + setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1); + + for (uint32_t j=0 ; j < 4 ; j++) { + int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1; + if (0 <= pos) + if (102 == pos) { + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" t")); + } else { + setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos); + } + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + for (uint32_t j=0 ; j < 5 ; j++) { + bool mqtt = ((setting>>(26+j))&(0x01)!=0); + if (mqtt) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1")); + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + } + } + ResponseCmndIdxChar(setting_chr); + } +} + +void CmndShutterSetHalfway(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.shutter_set50percent[XdrvMailbox.index -1] = (Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; + ShutterInit(); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); + } +} + +void CmndShutterFrequency(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { + ShutterGlobal.open_velocity_max = XdrvMailbox.payload; + if (shutters_present < 4) { + Settings.shuttercoeff[4][3] = ShutterGlobal.open_velocity_max; + } + ShutterInit(); + } + ResponseCmndNumber(ShutterGlobal.open_velocity_max); +} + +void CmndShutterSetClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter[XdrvMailbox.index -1].real_position = 0; + ShutterStartInit(XdrvMailbox.index -1, 0, 0); + Settings.shutter_position[XdrvMailbox.index -1] = 0; + ResponseCmndIdxChar(D_CONFIGURATION_RESET); + } +} + +void CmndShutterSetOpen(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter[XdrvMailbox.index -1].real_position = Shutter[XdrvMailbox.index -1].open_max; + ShutterStartInit(XdrvMailbox.index -1, 0, Shutter[XdrvMailbox.index -1].open_max); + Settings.shutter_position[XdrvMailbox.index -1] = 100; + ResponseCmndIdxChar(D_CONFIGURATION_RESET); + } +} + +void CmndShutterPwmRange(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + uint8_t i = 0; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 2; str = strtok_r(nullptr, " ", &str_ptr), i++) { + uint16_t field = atoi(str); + + + if ((field <= 0) || (field > 1023)) { + break; + } + Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init1. pwmmin %d, pwmmax %d"), XdrvMailbox.index , Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]); + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } else { + char setting_chr[30] = "0"; + snprintf_P(setting_chr, sizeof(setting_chr), PSTR("Shutter %d: min:%d max:%d"), XdrvMailbox.index, Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]); + ResponseCmndIdxChar(setting_chr); + } + } +} + +void CmndShutterCalibration(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + uint8_t i = 0; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field = atoi(str); + + + if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) { + break; + } + messwerte[i] = field; + } + for (i = 0; i < 5; i++) { + Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shuttercoeff: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]); + } + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } else { + char setting_chr[30] = "0"; + snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings.shuttercoeff[0][XdrvMailbox.index -1], Settings.shuttercoeff[1][XdrvMailbox.index -1], Settings.shuttercoeff[2][XdrvMailbox.index -1], Settings.shuttercoeff[3][XdrvMailbox.index -1], Settings.shuttercoeff[4][XdrvMailbox.index -1]); + ResponseCmndIdxChar(setting_chr); + } + } +} + +void ShutterOptionsSetHelper(uint16_t option){ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(option); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (option); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & option) ? 1 : 0); + } +} + +void CmndShutterInvert(void) { + ShutterOptionsSetHelper(1); +} + +void CmndShutterLock(void) { + ShutterOptionsSetHelper(2); +} + +void CmndShutterEnableEndStopTime(void) { + ShutterOptionsSetHelper(4); +} + +void CmndShutterInvertWebButtons(void) { + ShutterOptionsSetHelper(8); +} + + + + + +bool Xdrv27(uint8_t function) +{ + bool result = false; + + if (Settings.flag3.shutter_mode) { + switch (function) { + case FUNC_PRE_INIT: + ShutterInit(); + break; + case FUNC_EVERY_50_MSECOND: + ShutterUpdatePosition(); + break; + case FUNC_EVERY_SECOND: + + ShutterReportPosition(false, MAX_SHUTTERS); + break; + + case FUNC_COMMAND: + result = DecodeCommand(kShutterCommands, ShutterCommand); + break; + case FUNC_JSON_APPEND: + for (uint8_t i = 0; i < shutters_present; i++) { + uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i] : Settings.shutter_position[i]; + uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter[i].target_position, i) : ShutterRealToPercentPosition(Shutter[i].target_position, i); + + ResponseAppend_P(","); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter[i].direction,target); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_SHUTTER, position); + } +#endif + } + break; + case FUNC_SET_POWER: + char stemp1[10]; + + ShutterGlobal.RelayCurrentMask = XdrvMailbox.index ^ ShutterGlobal.RelayOldMask; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + ShutterRelayChanged(); + ShutterGlobal.RelayOldMask = XdrvMailbox.index; + break; + case FUNC_SET_DEVICE_POWER: + if (ShutterGlobal.skip_relay_change ) { + uint8_t i; + for (i = 0; i < devices_present; i++) { + if (ShutterGlobal.RelayCurrentMask &1) { + break; + } + ShutterGlobal.RelayCurrentMask >>= 1; + } + + result = true; + ShutterGlobal.skip_relay_change = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); + ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER); + } + break; + case FUNC_BUTTON_PRESSED: + if (Settings.shutter_button[XdrvMailbox.index] & (1<<31)) { + ShutterButtonHandler(); + result = true; + } + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_28_pcf8574.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_28_pcf8574.ino" +#ifdef USE_I2C +#ifdef USE_PCF8574 + + + + + + + +#define XDRV_28 28 +#define XI2C_02 2 + +#define PCF8574_ADDR1 0x20 +#define PCF8574_ADDR2 0x38 + +struct PCF8574 { + int error; + uint8_t pin[64]; + uint8_t address[MAX_PCF8574]; + uint8_t pin_mask[MAX_PCF8574] = { 0 }; + uint8_t max_connected_ports = 0; + uint8_t max_devices = 0; + char stype[9]; + bool type = false; +} Pcf8574; + +void Pcf8574SwitchRelay(void) +{ + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t relay_state = bitRead(XdrvMailbox.index, i); + + + + if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { + uint8_t board = Pcf8574.pin[i]>>3; + uint8_t oldpinmask = Pcf8574.pin_mask[board]; + uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; + + + + if (_val) { + Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); + } else { + Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); + } + if (oldpinmask != Pcf8574.pin_mask[board]) { + Wire.beginTransmission(Pcf8574.address[board]); + Wire.write(Pcf8574.pin_mask[board]); + Pcf8574.error = Wire.endTransmission(); + } + + } + } +} + +void Pcf8574Init(void) +{ + uint8_t pcf8574_address = PCF8574_ADDR1; + while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { + +#ifdef USE_MCP230xx_ADDR + if (USE_MCP230xx_ADDR == pcf8574_address) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address); + pcf8574_address++; + if ((PCF8574_ADDR1 +7) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2 +1; + } + } +#endif + + + + if (I2cSetDevice(pcf8574_address)) { + Pcf8574.type = true; + + Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; + Pcf8574.max_devices++; + + strcpy(Pcf8574.stype, "PCF8574"); + if (pcf8574_address >= PCF8574_ADDR2) { + strcpy(Pcf8574.stype, "PCF8574A"); + } + I2cSetActiveFound(pcf8574_address, Pcf8574.stype); + } + + pcf8574_address++; + if ((PCF8574_ADDR1 +7) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2 +1; + } + } + if (Pcf8574.type) { + for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { + Pcf8574.pin[i] = 99; + } + devices_present = devices_present - Pcf8574.max_connected_ports; + Pcf8574.max_connected_ports = 0; + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); + + for (uint32_t i = 0; i < 8; i++) { + uint8_t _result = Settings.pcf8574_config[idx] >> i &1; + + if (_result > 0) { + Pcf8574.pin[devices_present] = i + 8 * idx; + bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); + devices_present++; + Pcf8574.max_connected_ports++; + } + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); + } +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_PCF8574 "pcf" + +const char HTTP_BTN_MENU_PCF8574[] PROGMEM = + "

"; + +const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = + "
 " D_PCF8574_PARAMETERS " " + "
" + "


"; + +const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = + "" D_DEVICE " %d " D_PORT " %d"; + +void HandlePcf8574(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); + + if (Webserver->hasArg("save")) { + Pcf8574SaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(D_CONFIGURE_PCF8574); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + for (uint32_t idx2 = 0; idx2 < 8; idx2++) { + uint8_t helper = 1 << idx2; + WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, + idx +1, idx2, + idx2 + 8*idx, + idx2 + 8*idx, + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " + ); + } + } + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void Pcf8574SaveSettings(void) +{ + char stemp[7]; + char tmp[100]; + + + + Settings.flag3.pcf8574_ports_inverted = Webserver->hasArg("b1"); + for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { + byte count=0; + byte n = Settings.pcf8574_config[idx]; + while(n!=0) { + n = n&(n-1); + count++; + } + if (count <= devices_present) { + devices_present = devices_present - count; + } + for (byte i = 0; i < 8; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); + WebGetArg(stemp, tmp, sizeof(tmp)); + byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); + if (_value) { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; + devices_present++; + Pcf8574.max_connected_ports++; + } else { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); + } + } + + + + } +} +#endif + + + + + +bool Xdrv28(uint8_t function) +{ + if (!I2cEnabled(XI2C_02)) { return false; } + + bool result = false; + + if (FUNC_PRE_INIT == function) { + Pcf8574Init(); + } + else if (Pcf8574.type) { + switch (function) { + case FUNC_SET_POWER: + Pcf8574SwitchRelay(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_PCF8574); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_PCF8574), HandlePcf8574); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_29_deepsleep.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#ifdef USE_DEEPSLEEP +# 31 "/workspace/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#define XDRV_29 29 + +#define D_PRFX_DEEPSLEEP "DeepSleep" +#define D_CMND_DEEPSLEEP_TIME "Time" + +const uint32_t DEEPSLEEP_MAX = 10 * 366 * 24 * 60 * 60; +const uint32_t DEEPSLEEP_MAX_CYCLE = 60 * 60; +const uint32_t DEEPSLEEP_MIN_TIME = 5; +const uint32_t DEEPSLEEP_START_COUNTDOWN = 4; + +const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" + D_CMND_DEEPSLEEP_TIME ; + +void (* const DeepsleepCommand[])(void) PROGMEM = { + &CmndDeepsleepTime }; + +uint32_t deepsleep_sleeptime = 0; +uint8_t deepsleep_flag = 0; + +bool DeepSleepEnabled(void) +{ + if ((Settings.deepsleep < 10) || (Settings.deepsleep > DEEPSLEEP_MAX)) { + Settings.deepsleep = 0; + return false; + } + + if (PinUsed(GPIO_DEEPSLEEP)) { + pinMode(Pin(GPIO_DEEPSLEEP), INPUT_PULLUP); + return (digitalRead(Pin(GPIO_DEEPSLEEP))); + } + + return true; +} + +void DeepSleepReInit(void) +{ + if ((ResetReason() == REASON_DEEP_SLEEP_AWAKE) && DeepSleepEnabled()) { + if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { + + RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE; + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); + RtcSettingsSave(); + RtcRebootReset(); +#ifdef ESP8266 + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); +#else + esp_sleep_enable_timer_wakeup(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep)); + esp_deep_sleep_start(); +#endif + yield(); + + } + } + + RtcSettings.ultradeepsleep = 0; +} + +void DeepSleepPrepare(void) +{ + + + + + if ((RtcSettings.nextwakeup == 0) || + (RtcSettings.deepsleep_slip < 9000) || + (RtcSettings.deepsleep_slip > 11000) || + (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup = 0; + RtcSettings.deepsleep_slip = 10000; + } + + + + int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; + + + + timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; + if (timeslip) { + RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / tmax((Settings.deepsleep - (millis() / 1000)),5); + + RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); + RtcSettings.nextwakeup += Settings.deepsleep; + } + + + + if (RtcSettings.nextwakeup <= (UtcTime() - DEEPSLEEP_MIN_TIME)) { + + RtcSettings.nextwakeup += (((UtcTime() + DEEPSLEEP_MIN_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + } + + String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); + + + deepsleep_sleeptime = tmin((uint32_t)DEEPSLEEP_MAX_CYCLE ,RtcSettings.nextwakeup - UtcTime()); + + + Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); + + + +} + +void DeepSleepStart(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); + + WifiShutdown(); + RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); + RtcSettingsSave(); +#ifdef ESP8266 + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); +#else + esp_sleep_enable_timer_wakeup(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); + esp_deep_sleep_start(); +#endif + yield(); +} + +void DeepSleepEverySecond(void) +{ + if (!deepsleep_flag) { return; } + + if (DeepSleepEnabled()) { + if (DEEPSLEEP_START_COUNTDOWN == deepsleep_flag) { + SettingsSaveAll(); + DeepSleepPrepare(); + } + deepsleep_flag--; + if (deepsleep_flag <= 0) { + DeepSleepStart(); + } + } else { + deepsleep_flag = 0; + } +} + + + + + +void CmndDeepsleepTime(void) +{ + if ((0 == XdrvMailbox.payload) || + ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < DEEPSLEEP_MAX))) { + Settings.deepsleep = XdrvMailbox.payload; + RtcSettings.nextwakeup = 0; + deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; + if (deepsleep_flag) { + if (!Settings.tele_period) { + Settings.tele_period = TELE_PERIOD; + } + } + } + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); +} + + + + + +bool Xdrv29(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + DeepSleepEverySecond(); + break; + case FUNC_AFTER_TELEPERIOD: + if (DeepSleepEnabled() && !deepsleep_flag && (Settings.tele_period == 10 || Settings.tele_period == 300 || UpTime() > Settings.tele_period)) { + deepsleep_flag = DEEPSLEEP_START_COUNTDOWN; + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); + break; + case FUNC_PRE_INIT: + DeepSleepReInit(); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_EXS_DIMMER + + + + + + + +#define XDRV_30 30 + +#define EXS_GATE_1_ON 0x20 +#define EXS_GATE_1_OFF 0x21 +#define EXS_DIMM_1_ON 0x22 +#define EXS_DIMM_1_OFF 0x23 +#define EXS_DIMM_1_TBL 0x24 +#define EXS_DIMM_1_VAL 0x25 +#define EXS_GATE_2_ON 0x30 +#define EXS_GATE_2_OFF 0x31 +#define EXS_DIMM_2_ON 0x32 +#define EXS_DIMM_2_OFF 0x33 +#define EXS_DIMM_2_TBL 0x34 +#define EXS_DIMM_2_VAL 0x35 +#define EXS_GATES_ON 0x40 +#define EXS_GATES_OFF 0x41 +#define EXS_DIMMS_ON 0x50 +#define EXS_DIMMS_OFF 0x51 +#define EXS_CH_LOCK 0x60 +#define EXS_GET_VALUES 0xFA +#define EXS_WRITE_EE 0xFC +#define EXS_READ_EE 0xFD +#define EXS_GET_VERSION 0xFE +#define EXS_RESET 0xFF + +#define EXS_BUFFER_SIZE 256 +#define EXS_ACK_TIMEOUT 200 + +#include + +TasmotaSerial *ExsSerial = nullptr; + +typedef struct +{ + uint8_t on = 0; + uint8_t bright_tbl = 0; + uint8_t dimm = 0; + uint8_t impuls_start = 0; + uint32_t impuls_len = 0; +} CHANNEL; + +typedef struct +{ + uint8_t version_major = 0; + uint8_t version_minor = 0; + CHANNEL channel[2]; + uint8_t gate_lock = 0; +} DIMMER; + +struct EXS +{ + uint8_t *buffer = nullptr; + int byte_counter = 0; + int cmd_status = 0; + uint8_t power = 0; + uint8_t dimm[2] = {0, 0}; + DIMMER dimmer; +} Exs; + + + + + +uint8_t crc8(const uint8_t *p, uint8_t len) +{ + const uint8_t table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; + + const uint8_t table_rev[] = { + 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, + 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; + + uint8_t offset; + uint8_t temp, crc8_temp; + uint8_t crc8 = 0; + + for (int i = 0; i < len; i++) + { + temp = *(p + i); + offset = temp ^ crc8; + offset >>= 4; + crc8_temp = crc8 & 0x0f; + crc8 = crc8_temp ^ table_rev[offset]; + offset = crc8 ^ temp; + offset &= 0x0f; + crc8_temp = crc8 & 0xf0; + crc8 = crc8_temp ^ table[offset]; + } + return crc8 ^ 0x55; +} + +void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) +{ + int retries = 3; + char rc; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); + for (uint32_t i = 0; i < len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + while (retries) + { + retries--; + + ExsSerial->write(data, len); + ExsSerial->flush(); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + + if (!ExsSerial->available()) + { + +#ifdef EXS_DEBUG + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); +#endif + continue; + } + + rc = ExsSerial->read(); + if (rc == 0xFF) + break; + } +} + +void ExsSendCmd(uint8_t cmd, uint8_t value) +{ + uint8_t buffer[8]; + uint16_t len; + + buffer[0] = 0x7b; + buffer[3] = cmd; + + switch (cmd) + { + case EXS_GATE_1_ON: + case EXS_GATE_1_OFF: + case EXS_DIMM_1_ON: + case EXS_DIMM_1_OFF: + case EXS_GATE_2_ON: + case EXS_GATE_2_OFF: + case EXS_DIMM_2_ON: + case EXS_DIMM_2_OFF: + case EXS_GATES_ON: + case EXS_GATES_OFF: + case EXS_DIMMS_ON: + case EXS_DIMMS_OFF: + case EXS_GET_VALUES: + case EXS_GET_VERSION: + case EXS_RESET: + buffer[2] = 1; + len = 4; + break; + + case EXS_CH_LOCK: + case EXS_DIMM_1_TBL: + case EXS_DIMM_1_VAL: + case EXS_DIMM_2_TBL: + case EXS_DIMM_2_VAL: + buffer[2] = 2; + buffer[4] = value; + len = 5; + break; + } + buffer[1] = crc8(&buffer[3], buffer[2]); + + ExsSerialSend(buffer, len); +} + +void ExsSetPower(uint8_t device, uint8_t power) +{ + Exs.dimmer.channel[device].dimm = power; + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); +} + +void ExsSetBri(uint8_t device, uint8_t bri) +{ + Exs.dimmer.channel[device].bright_tbl = bri; + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); +} + +void ExsSyncState(uint8_t device) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), + device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), + device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); +#endif + + if (bitRead(Exs.power, device) && + Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { + ExsSetBri(device, Exs.dimm[device]); + } + + if (!Exs.dimm[device]) { + Exs.dimmer.channel[device].dimm = 0; + } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { + ExsSetPower(device, bitRead(Exs.power, device)); + } +} + +bool ExsSyncState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); +#endif + + if (!ExsSerial || Exs.cmd_status != 0) + return false; + + ExsSyncState(0); + ExsSyncState(1); + return true; +} + +void ExsDebugState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), + Exs.dimmer.version_major, Exs.dimmer.version_minor, + Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, + Exs.dimmer.channel[0].bright_tbl, + changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, + Exs.dimmer.channel[1].bright_tbl, + changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.gate_lock); +#endif +} + +void ExsPacketProcess(void) +{ + uint8_t len = Exs.buffer[1]; + uint8_t cmd = Exs.buffer[2]; + + switch (cmd) + { + case EXS_GET_VALUES: +# 295 "/workspace/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + if (len > 9) + { + Exs.dimmer.version_major = Exs.buffer[3]; + Exs.dimmer.version_minor = Exs.buffer[4]; + + + Exs.dimmer.channel[0].on = Exs.buffer[6]; + Exs.dimmer.channel[0].dimm = Exs.buffer[6]; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; + + + Exs.dimmer.channel[1].on = Exs.buffer[9]; + Exs.dimmer.channel[1].dimm = Exs.buffer[9]; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; + + Exs.dimmer.gate_lock = Exs.buffer[11]; + } + else +# 328 "/workspace/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + { + Exs.dimmer.version_major = 1; + Exs.dimmer.version_minor = 0; + + + Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; + + + Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; + + Exs.dimmer.gate_lock = Exs.buffer[9] - 48; + } + + ExsDebugState(); + ExsSyncState(); + ExsDebugState(); + break; + default: + break; + } +} + + + +bool ExsModuleSelected(void) +{ + Settings.light_correction = 0; + Settings.flag.mqtt_serial = 0; + Settings.flag3.pwm_multi_channels = 1; + SetSeriallog(LOG_LEVEL_NONE); + + devices_present = +2; + light_type = LT_SERIAL2; + return true; +} + +bool ExsSetChannels(void) +{ +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); + for (int i = 0; i < XdrvMailbox.data_len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; + Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; + return ExsSyncState(); +} + +bool ExsSetPower(void) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), + active_device, XdrvMailbox.index); + + Exs.power = XdrvMailbox.index; + return ExsSyncState(); +} + +void EsxMcuStart(void) +{ + int retries = 3; + +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), Pin(GPIO_EXS_ENABLE)); +#endif + + pinMode(Pin(GPIO_EXS_ENABLE), OUTPUT); + digitalWrite(Pin(GPIO_EXS_ENABLE), LOW); + + delay(1); + + while (ExsSerial->available()) + { + + ExsSerial->read(); + } +} + +void ExsInit(void) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD)); +#endif + + Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); + if (Exs.buffer != nullptr) + { + ExsSerial = new TasmotaSerial(Pin(GPIO_RXD), Pin(GPIO_TXD), 2); + if (ExsSerial->begin(9600)) + { + if (ExsSerial->hardwareSerial()) + { + ClaimSerial(); + } + ExsSerial->flush(); + EsxMcuStart(); + ExsSendCmd(EXS_CH_LOCK, 0); + ExsSendCmd(EXS_GET_VALUES, 0); + } + } +} + +void ExsSerialInput(void) +{ + while (ExsSerial->available()) + { + yield(); + uint8_t serial_in_byte = ExsSerial->read(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); + + if (Exs.cmd_status == 0 && + serial_in_byte == 0x7B) + { + Exs.cmd_status = 1; + Exs.byte_counter = 0; + } + else if (Exs.byte_counter >= EXS_BUFFER_SIZE) + { + Exs.cmd_status = 0; + } + else if (Exs.cmd_status == 1) + { + Exs.buffer[Exs.byte_counter++] = serial_in_byte; + + if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) + { + uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); + + + Exs.cmd_status = 0; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); + for (uint32_t i = 0; i < Exs.byte_counter; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + if (Exs.buffer[0] == crc) + { + ExsSerial->write(0xFF); + ExsPacketProcess(); + } + else + { + ExsSerial->write(0x00); + } + + } + } + } +} + + + + + +#ifdef EXS_MCU_CMNDS + +#define D_PRFX_EXS "Exs" +#define D_CMND_EXS_DIMM "Dimm" +#define D_CMND_EXS_DIMM_TBL "DimmTbl" +#define D_CMND_EXS_DIMM_VAL "DimmVal" +#define D_CMND_EXS_DIMMS "Dimms" +#define D_CMND_EXS_CH_LOCK "ChLock" +#define D_CMND_EXS_STATE "State" + +const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" + D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" + D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" + D_CMND_EXS_STATE; + +void (* const ExsCommand[])(void) PROGMEM = { + &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, + &CmndExsDimms, &CmndExsChLock, + &CmndExsState }; + +void CmndExsDimm(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsDimmTbl(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimmVal(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimms(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsChLock(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsState(void) +{ + ExsSendCmd(EXS_GET_VALUES, 0); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + ExsSerialInput(); + + Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); + ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," + "\"Channels\":["), + Exs.dimmer.version_major, Exs.dimmer.version_minor); + + for (uint32_t i = 0; i < 2; i++) { + if (i != 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"On\":\"%d\"," + "\"BrightProz\":\"%d\"," + "\"BrightTab\":\"%d\"," + "\"Dimm\":\"%d\"}"), + Exs.dimmer.channel[i].on, + changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[i].bright_tbl, + Exs.dimmer.channel[i].dimm); + } + ResponseAppend_P(PSTR("],")); + ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); + ResponseJsonEndEnd(); +} + +#endif + + + + + +bool Xdrv30(uint8_t function) +{ + bool result = false; + + if (EXS_DIMMER == my_module_type) + { + switch (function) + { + case FUNC_LOOP: + if (ExsSerial) + ExsSerialInput(); + break; + case FUNC_MODULE_INIT: + result = ExsModuleSelected(); + break; + case FUNC_INIT: + ExsInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = ExsSetPower(); + break; + case FUNC_SET_CHANNELS: + result = ExsSetChannels(); + break; +#ifdef EXS_MCU_CMNDS + case FUNC_COMMAND: + result = DecodeCommand(kExsCommands, ExsCommand); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_31_tasmota_client.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_31_tasmota_client.ino" +#ifdef USE_TASMOTA_CLIENT + + + + +#define XDRV_31 31 + +#define CONST_STK_CRC_EOP 0x20 + +#define CMND_STK_GET_SYNC 0x30 +#define CMND_STK_SET_DEVICE 0x42 +#define CMND_STK_SET_DEVICE_EXT 0x45 +#define CMND_STK_ENTER_PROGMODE 0x50 +#define CMND_STK_LEAVE_PROGMODE 0x51 +#define CMND_STK_LOAD_ADDRESS 0x55 +#define CMND_STK_PROG_PAGE 0x64 + + + + + +#define CMND_START 0xFC +#define CMND_END 0xFD + +#define CMND_FEATURES 0x01 +#define CMND_JSON 0x02 +#define CMND_FUNC_EVERY_SECOND 0x03 +#define CMND_FUNC_EVERY_100_MSECOND 0x04 +#define CMND_CLIENT_SEND 0x05 +#define CMND_PUBLISH_TELE 0x06 +#define CMND_EXECUTE_CMND 0x07 + +#define PARAM_DATA_START 0xFE +#define PARAM_DATA_END 0xFF + +#include + + + + + +class SimpleHexParse { + public: + SimpleHexParse(void); + uint8_t parseLine(char *hexline); + uint8_t ptr_l = 0; + uint8_t ptr_h = 0; + bool PageIsReady = false; + bool firstrun = true; + bool EndOfFile = false; + uint8_t FlashPage[128]; + uint8_t FlashPageIdx = 0; + uint8_t layoverBuffer[16]; + uint8_t layoverIdx = 0; + uint8_t getByte(char *hexline, uint8_t idx); +}; + +SimpleHexParse::SimpleHexParse(void) { + +} + +uint8_t SimpleHexParse::parseLine(char *hexline) { + if (layoverIdx) { + memcpy(&FlashPage[0], &layoverBuffer[0], layoverIdx); + FlashPageIdx = layoverIdx; + layoverIdx = 0; + } + uint8_t len = getByte(hexline, 1); + uint8_t addr_h = getByte(hexline, 2); + uint8_t addr_l = getByte(hexline, 3); + uint8_t rectype = getByte(hexline, 4); + for (uint8_t idx = 0; idx < len; idx++) { + if (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = getByte(hexline, idx+5); + FlashPageIdx++; + } else { + layoverBuffer[layoverIdx] = getByte(hexline, idx+5); + layoverIdx++; + } + } + if (1 == rectype) { + EndOfFile = true; + while (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = 0xFF; + FlashPageIdx++; + } + } + if (FlashPageIdx == 128) { + if (firstrun) { + firstrun = false; + } else { + ptr_l += 0x40; + if (ptr_l == 0) { + ptr_l = 0; + ptr_h++; + } + } + firstrun = false; + PageIsReady = true; + } + return 0; +} + +uint8_t SimpleHexParse::getByte(char* hexline, uint8_t idx) { + char buff[3]; + buff[3] = '\0'; + memcpy(&buff, &hexline[(idx*2)-1], 2); + return strtol(buff, 0, 16); +} + + + + + +struct TCLIENT { + uint32_t spi_hex_size = 0; + uint32_t spi_sector_counter = 0; + uint8_t spi_sector_cursor = 0; + uint8_t inverted = LOW; + bool type = false; + bool flashing = false; + bool SerialEnabled = false; + uint8_t waitstate = 0; + bool unsupported = false; +} TClient; + +typedef union { + uint32_t data; + struct { + uint32_t func_json_append : 1; + uint32_t func_every_second : 1; + uint32_t func_every_100_msecond : 1; + uint32_t func_client_send : 1; + uint32_t spare4 : 1; + uint32_t spare5 : 1; + uint32_t spare6 : 1; + uint32_t spare7 : 1; + uint32_t spare8 : 1; + uint32_t spare9 : 1; + uint32_t spare10 : 1; + uint32_t spare11 : 1; + uint32_t spare12 : 1; + uint32_t spare13 : 1; + uint32_t spare14 : 1; + uint32_t spare15 : 1; + uint32_t spare16 : 1; + uint32_t spare17 : 1; + uint32_t spare18 : 1; + uint32_t spare19 : 1; + uint32_t spare20 : 1; + uint32_t spare21 : 1; + uint32_t spare22 : 1; + uint32_t spare23 : 1; + uint32_t spare24 : 1; + uint32_t spare25 : 1; + uint32_t spare26 : 1; + uint32_t spare27 : 1; + uint32_t spare28 : 1; + uint32_t spare29 : 1; + uint32_t spare30 : 1; + uint32_t spare31 : 1; + }; +} TClientFeatureCfg; + + + + + + +struct TCLIENT_FEATURES { + uint32_t features_version; + TClientFeatureCfg features; +} TClientSettings; + +struct TCLIENT_COMMAND { + uint8_t command; + uint8_t parameter; + uint8_t unused2; + uint8_t unused3; +} TClientCommand; + +TasmotaSerial *TasmotaClient_Serial; + +uint32_t TasmotaClient_FlashStart(void) { + return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; +} + +uint8_t TasmotaClient_UpdateInit(void) { + TClient.spi_hex_size = 0; + TClient.spi_sector_counter = TasmotaClient_FlashStart(); + TClient.spi_sector_cursor = 0; + return 0; +} + +void TasmotaClient_Reset(void) { + if (TClient.SerialEnabled) { + digitalWrite(Pin(GPIO_TASMOTACLIENT_RST), !TClient.inverted); + delay(1); + digitalWrite(Pin(GPIO_TASMOTACLIENT_RST), TClient.inverted); + delay(1); + digitalWrite(Pin(GPIO_TASMOTACLIENT_RST), !TClient.inverted); + delay(5); + } +} + +uint8_t TasmotaClient_waitForSerialData(int dataCount, int timeout) { + int timer = 0; + while (timer < timeout) { + if (TasmotaClient_Serial->available() >= dataCount) { + return 1; + } + delay(1); + timer++; + } + return 0; +} + +uint8_t TasmotaClient_sendBytes(uint8_t* bytes, int count) { + TasmotaClient_Serial->write(bytes, count); + TasmotaClient_waitForSerialData(2, 250); + uint8_t sync = TasmotaClient_Serial->read(); + uint8_t ok = TasmotaClient_Serial->read(); + if ((sync == 0x14) && (ok == 0x10)) { + return 1; + } + return 0; +} + +uint8_t TasmotaClient_execCmd(uint8_t cmd) { + uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; + return TasmotaClient_sendBytes(bytes, 2); +} + +uint8_t TasmotaClient_execParam(uint8_t cmd, uint8_t* params, int count) { + uint8_t bytes[32]; + bytes[0] = cmd; + int i = 0; + while (i < count) { + bytes[i + 1] = params[i]; + i++; + } + bytes[i + 1] = CONST_STK_CRC_EOP; + return TasmotaClient_sendBytes(bytes, i + 2); +} + +uint8_t TasmotaClient_exitProgMode(void) { + return TasmotaClient_execCmd(CMND_STK_LEAVE_PROGMODE); +} + +uint8_t TasmotaClient_SetupFlash(void) { + uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; + uint8_t ExtProgParams[] = {0x05, 0x04, 0xd7, 0xc2, 0x00}; + TasmotaClient_Serial->begin(USE_TASMOTA_CLIENT_FLASH_SPEED); + if (TasmotaClient_Serial->hardwareSerial()) { + ClaimSerial(); + } + + TasmotaClient_Reset(); + + uint8_t timeout = 0; + uint8_t no_error = 0; + while (50 > timeout) { + if (TasmotaClient_execCmd(CMND_STK_GET_SYNC)) { + timeout = 200; + no_error = 1; + } + timeout++; + delay(1); + } + if (no_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Found bootloader")); + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Bootloader could not be found")); + } + if (no_error) { + if (TasmotaClient_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Could not configure device for programming (1)")); + } + } + if (no_error) { + if (TasmotaClient_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Could not configure device for programming (2)")); + } + } + if (no_error) { + if (TasmotaClient_execCmd(CMND_STK_ENTER_PROGMODE)) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Failed to put bootloader into programming mode")); + } + } + return no_error; +} + +uint8_t TasmotaClient_loadAddress(uint8_t adrHi, uint8_t adrLo) { + uint8_t params[] = { adrLo, adrHi }; + return TasmotaClient_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); +} + +void TasmotaClient_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data) { + uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; + TasmotaClient_loadAddress(addr_h, addr_l); + TasmotaClient_Serial->write(Header, 4); + for (int i = 0; i < 128; i++) { + TasmotaClient_Serial->write(data[i]); + } + TasmotaClient_Serial->write(CONST_STK_CRC_EOP); + TasmotaClient_waitForSerialData(2, 250); + TasmotaClient_Serial->read(); + TasmotaClient_Serial->read(); +} + +void TasmotaClient_Flash(void) { + bool reading = true; + uint32_t read = 0; + uint32_t processed = 0; + char thishexline[50]; + uint8_t position = 0; + char* flash_buffer; + + SimpleHexParse hexParse = SimpleHexParse(); + + if (!TasmotaClient_SetupFlash()) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Flashing aborted!")); + TClient.flashing = false; + restart_flag = 2; + return; + } + + flash_buffer = new char[SPI_FLASH_SEC_SIZE]; + uint32_t flash_start = TasmotaClient_FlashStart() * SPI_FLASH_SEC_SIZE; + while (reading) { + ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); + read = read + SPI_FLASH_SEC_SIZE; + if (read >= TClient.spi_hex_size) { + reading = false; + } + for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { + processed++; + if ((processed <= TClient.spi_hex_size) && (!hexParse.EndOfFile)) { + if (':' == flash_buffer[ca]) { + position = 0; + } + if (0x0D == flash_buffer[ca]) { + thishexline[position] = 0; + hexParse.parseLine(thishexline); + if (hexParse.PageIsReady) { + TasmotaClient_FlashPage(hexParse.ptr_h, hexParse.ptr_l, hexParse.FlashPage); + hexParse.PageIsReady = false; + hexParse.FlashPageIdx = 0; + } + } else { + if (0x0A != flash_buffer[ca]) { + thishexline[position] = flash_buffer[ca]; + position++; + } + } + } + } + } + TasmotaClient_exitProgMode(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Flash done!")); + TClient.flashing = false; + restart_flag = 2; +} + +void TasmotaClient_SetFlagFlashing(bool value) { + TClient.flashing = value; +} + +bool TasmotaClient_GetFlagFlashing(void) { + return TClient.flashing; +} + +void TasmotaClient_WriteBuffer(uint8_t *buf, size_t size) { + if (0 == TClient.spi_sector_cursor) { + ESP.flashEraseSector(TClient.spi_sector_counter); + } + TClient.spi_sector_cursor++; + ESP.flashWrite((TClient.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((TClient.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); + TClient.spi_hex_size = TClient.spi_hex_size + size; + if (2 == TClient.spi_sector_cursor) { + TClient.spi_sector_cursor = 0; + TClient.spi_sector_counter++; + } +} + +void TasmotaClient_Init(void) { + if (TClient.type) { + return; + } + if (10 > TClient.waitstate) { + TClient.waitstate++; + return; + } + if (!TClient.SerialEnabled) { + if (PinUsed(GPIO_TASMOTACLIENT_RXD) && PinUsed(GPIO_TASMOTACLIENT_TXD) && + (PinUsed(GPIO_TASMOTACLIENT_RST) || PinUsed(GPIO_TASMOTACLIENT_RST_INV))) { + TasmotaClient_Serial = new TasmotaSerial(Pin(GPIO_TASMOTACLIENT_RXD), Pin(GPIO_TASMOTACLIENT_TXD), 1, 0, 200); + if (TasmotaClient_Serial->begin(USE_TASMOTA_CLIENT_SERIAL_SPEED)) { + if (TasmotaClient_Serial->hardwareSerial()) { + ClaimSerial(); + } + TasmotaClient_Serial->setTimeout(100); + if (PinUsed(GPIO_TASMOTACLIENT_RST_INV)) { + SetPin(Pin(GPIO_TASMOTACLIENT_RST_INV), AGPIO(GPIO_TASMOTACLIENT_RST)); + TClient.inverted = HIGH; + } + pinMode(Pin(GPIO_TASMOTACLIENT_RST), OUTPUT); + TClient.SerialEnabled = true; + TasmotaClient_Reset(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Enabled")); + } + } + } + if (TClient.SerialEnabled) { + TasmotaClient_sendCmnd(CMND_FEATURES, 0); + char buffer[32] = { 0 }; + TasmotaClient_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); + uint8_t len = TasmotaClient_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); + + if (len) { AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)buffer, len); } + + memcpy(&TClientSettings, &buffer, sizeof(TClientSettings)); + if (20191129 == TClientSettings.features_version) { + TClient.type = true; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Version %u"), TClientSettings.features_version); + } else { + if ((!TClient.unsupported) && (TClientSettings.features_version > 0)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TCL: Version %u not supported!"), TClientSettings.features_version); + TClient.unsupported = true; + } + } + } +} + +bool TasmotaClient_Available(void) { + return TClient.SerialEnabled; +} + +void TasmotaClient_Show(void) { + if ((TClient.type) && (TClientSettings.features.func_json_append)) { + char buffer[100]; + TasmotaClient_sendCmnd(CMND_JSON, 0); + TasmotaClient_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)-1); + uint8_t len = TasmotaClient_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)-1); + buffer[len] = '\0'; + ResponseAppend_P(PSTR(",\"TasmotaClient\":%s"), buffer); + } +} + +void TasmotaClient_sendCmnd(uint8_t cmnd, uint8_t param) { + TClientCommand.command = cmnd; + TClientCommand.parameter = param; + char buffer[sizeof(TClientCommand)+2]; + buffer[0] = CMND_START; + memcpy(&buffer[1], &TClientCommand, sizeof(TClientCommand)); + buffer[sizeof(TClientCommand)+1] = CMND_END; + + TasmotaClient_Serial->flush(); + + for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { + TasmotaClient_Serial->write(buffer[ca]); + } +} + +#define D_PRFX_CLIENT "Client" +#define D_CMND_CLIENT_RESET "Reset" +#define D_CMND_CLIENT_SEND "Send" + +const char kTasmotaClientCommands[] PROGMEM = D_PRFX_CLIENT "|" + D_CMND_CLIENT_RESET "|" D_CMND_CLIENT_SEND; + +void (* const TasmotaClientCommand[])(void) PROGMEM = { + &CmndClientReset, &CmndClientSend }; + +void CmndClientReset(void) { + TasmotaClient_Reset(); + TClient.type = false; + TClient.waitstate = 7; + TClient.unsupported = false; + ResponseCmndDone(); +} + +void CmndClientSend(void) { + if (TClient.SerialEnabled) { + if (0 < XdrvMailbox.data_len) { + TasmotaClient_sendCmnd(CMND_CLIENT_SEND, XdrvMailbox.data_len); + TasmotaClient_Serial->write(char(PARAM_DATA_START)); + for (uint8_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + TasmotaClient_Serial->write(XdrvMailbox.data[idx]); + } + TasmotaClient_Serial->write(char(PARAM_DATA_END)); + } + ResponseCmndDone(); + } +} + +void TasmotaClient_ProcessIn(void) { + uint8_t cmnd = TasmotaClient_Serial->read(); + if (CMND_START == cmnd) { + TasmotaClient_waitForSerialData(sizeof(TClientCommand),50); + uint8_t buffer[sizeof(TClientCommand)]; + for (uint8_t idx = 0; idx < sizeof(TClientCommand); idx++) { + buffer[idx] = TasmotaClient_Serial->read(); + } + TasmotaClient_Serial->read(); + memcpy(&TClientCommand, &buffer, sizeof(TClientCommand)); + char inbuf[TClientCommand.parameter+1]; + TasmotaClient_waitForSerialData(TClientCommand.parameter, 50); + TasmotaClient_Serial->read(); + for (uint8_t idx = 0; idx < TClientCommand.parameter; idx++) { + inbuf[idx] = TasmotaClient_Serial->read(); + } + TasmotaClient_Serial->read(); + inbuf[TClientCommand.parameter] = '\0'; + + if (CMND_PUBLISH_TELE == TClientCommand.command) { + Response_P(PSTR("{\"TasmotaClient\":")); + ResponseAppend_P("%s", inbuf); + ResponseJsonEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, mqtt_data); + } + if (CMND_EXECUTE_CMND == TClientCommand.command) { + ExecuteCommand(inbuf, SRC_IGNORE); + } + } +} + + + + + +bool Xdrv31(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if (TClient.type) { + if (TasmotaClient_Serial->available()) { + TasmotaClient_ProcessIn(); + } + if (TClientSettings.features.func_every_100_msecond) { + TasmotaClient_sendCmnd(CMND_FUNC_EVERY_100_MSECOND, 0); + } + } + break; + case FUNC_EVERY_SECOND: + if ((TClient.type) && (TClientSettings.features.func_every_second)) { + TasmotaClient_sendCmnd(CMND_FUNC_EVERY_SECOND, 0); + } + TasmotaClient_Init(); + break; + case FUNC_JSON_APPEND: + if ((TClient.type) && (TClientSettings.features.func_json_append)) { + TasmotaClient_Show(); + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kTasmotaClientCommands, TasmotaClientCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_32_hotplug.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_32_hotplug.ino" +#ifdef USE_HOTPLUG + + + + + + + +#define XDRV_32 32 + +const uint32_t HOTPLUG_MAX = 254; + +const char kHotPlugCommands[] PROGMEM = "|" + D_CMND_HOTPLUG; + +void (* const HotPlugCommand[])(void) PROGMEM = { + &CmndHotPlugTime }; + +struct { + + bool enabled = false; + uint8_t timeout = 0; +} Hotplug; + +void HotPlugInit(void) +{ + + if (Settings.hotplug_scan == 0xFF) { Settings.hotplug_scan = 0; } + if (Settings.hotplug_scan != 0) { + Hotplug.enabled = true; + Hotplug.timeout = 1; + } else + Hotplug.enabled = false; +} + +void HotPlugEverySecond(void) +{ + if (Hotplug.enabled) { + if (Hotplug.timeout == 0) { + XsnsCall(FUNC_HOTPLUG_SCAN); + Hotplug.timeout = Settings.hotplug_scan; + } + Hotplug.timeout--; + } +} + + + + + +void CmndHotPlugTime(void) +{ + if (XdrvMailbox.payload <= HOTPLUG_MAX) { + Settings.hotplug_scan = XdrvMailbox.payload; + HotPlugInit(); + } + ResponseCmndNumber(Settings.hotplug_scan); +} + + + + + +bool Xdrv32(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + HotPlugEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kHotPlugCommands, HotPlugCommand); + break; + case FUNC_PRE_INIT: + HotPlugInit(); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +# 31 "/workspace/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 + + + + + + + +#define XDRV_33 33 + +#include + +const char NRF24type[] PROGMEM = "NRF24"; + +struct { + uint8_t chipType = 0; +} NRF24; + + + +RF24 NRF24radio; + +bool NRF24initRadio() +{ + NRF24radio.begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC)); + NRF24radio.powerUp(); + + if(NRF24radio.isChipConnected()){ + DEBUG_DRIVER_LOG(PSTR("NRF24 chip connected")); + return true; + } + DEBUG_DRIVER_LOG(PSTR("NRF24 chip NOT !!!! connected")); + return false; +} + +bool NRF24Detect(void) +{ + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_DC)) { + if(NRF24initRadio()){ + NRF24.chipType = 32; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01 initialized")); + if(NRF24radio.isPVariant()){ + NRF24.chipType = 43; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01+ detected")); + } + return true; + } + } + return false; +} + + + + + +bool Xdrv33(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + result = NRF24Detect(); + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +#ifdef USE_I2C +#ifdef USE_WEMOS_MOTOR_V1 +# 45 "/workspace/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +#define XDRV_34 34 +#define XI2C_44 44 + +#ifndef WEMOS_MOTOR_V1_ADDR +#define WEMOS_MOTOR_V1_ADDR 0x30 +#endif +#ifndef WEMOS_MOTOR_V1_FREQ +#define WEMOS_MOTOR_V1_FREQ 1000 +#endif + +#define MOTOR_A 0 +#define MOTOR_B 1 + +#define SHORT_BRAKE 0 +#define CCW 1 +#define CW 2 +#define STOP 3 +#define STANDBY 4 + +struct WMOTORV1 { + bool detected = false; + uint8_t motor; +} WMotorV1; + +void WMotorV1Detect(void) +{ + if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) { + WMotorV1.detected = true; + I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); + WMotorV1Reset(); + } +} + +void WMotorV1Reset(void) +{ + + WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ); +} + +void WMotorV1SetFrequency(uint32_t freq) +{ + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); + Wire.write(((byte)(freq >> 16)) & (byte)0x0f); + Wire.write((byte)(freq >> 16)); + Wire.write((byte)(freq >> 8)); + Wire.write((byte)freq); + Wire.endTransmission(); + +} + +void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val) +{ + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); + Wire.write(motor | (byte)0x10); + Wire.write(dir); + + uint16_t _pwm_val = uint16_t(pwm_val * 100); + if (_pwm_val > 10000) { + _pwm_val = 10000; + } + + Wire.write((byte)(_pwm_val >> 8)); + Wire.write((byte)_pwm_val); + Wire.endTransmission(); + +} + +bool WMotorV1Command(void) +{ + uint8_t args_count = 0; + + if (XdrvMailbox.data_len > 0) { + args_count = 1; + } else { + return false; + } + + for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + if (' ' == XdrvMailbox.data[idx]) { + XdrvMailbox.data[idx] = ','; + } + if (',' == XdrvMailbox.data[idx]) { + args_count++; + } + } + UpperCase(XdrvMailbox.data, XdrvMailbox.data); + + char *command = strtok(XdrvMailbox.data, ","); + + if (strcmp(command, "RESET") == 0) { + WMotorV1Reset(); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); + return true; + } + + if (strcmp(command, "SETMOTOR") == 0) { + if (args_count >= 3) { + + int motor = atoi(strtok(NULL, ",")); + int dir = atoi(strtok(NULL, ",")); + int duty = 100; + if (args_count == 4) { + duty = atoi(strtok(NULL, ",")); + } + + WMotorV1SetMotor(motor, dir, duty); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); + return true; + } + } + return false; +} + + + + + +bool Xdrv34(uint8_t function) +{ + if (!I2cEnabled(XI2C_44)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + WMotorV1Detect(); + } + else if (WMotorV1.detected) { + switch (function) { + case FUNC_COMMAND_DRIVER: + if (XI2C_44 == XdrvMailbox.index) { + result = WMotorV1Command(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +#ifdef USE_PWM_DIMMER +# 33 "/workspace/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +#define XDRV_35 35 +#define MAX_PWM_DIMMER_KEYS 3 + +const char kPWMDimmerCommands[] PROGMEM = "|" + D_CMND_BRI_PRESET +#ifdef USE_DEVICE_GROUPS + "|" D_CMND_PWM_DIMMER_PWMS +#endif + ; + +void (* const PWMDimmerCommand[])(void) PROGMEM = { + &CmndBriPreset, +#ifdef USE_DEVICE_GROUPS + &CmndPWMDimmerPWMs, +#endif + }; + +#ifdef USE_PWM_DIMMER_REMOTE +struct remote_pwm_dimmer { + bool power_on; + uint8_t bri_power_on; + uint8_t bri_preset_low; + uint8_t bri_preset_high; + uint8_t fixed_color_index; + uint8_t bri; + bool power_button_increases_bri; +}; +#endif + +uint32_t ignore_any_key_time = 0; +uint32_t button_hold_time[3]; +uint8_t led_timeout_seconds = 0; +uint8_t restore_powered_off_led_counter = 0; +uint8_t power_button_index = 0; +uint8_t down_button_index = 1; +uint8_t buttons_pressed = 0; +uint8_t local_fixed_color_index = 128; +bool button_tapped = false; +bool down_button_tapped = false; +bool ignore_power_button = false; +bool multibutton_in_progress = false; +bool power_button_increases_bri = true; +bool tap_handled = false; +bool invert_power_button_bri_direction = false; +bool button_pressed[3] = { false, false, false }; +bool button_held[3]; +#ifdef USE_PWM_DIMMER_REMOTE +struct remote_pwm_dimmer remote_pwm_dimmers[MAX_PWM_DIMMER_KEYS]; +struct remote_pwm_dimmer * active_remote_pwm_dimmer; +#endif + +void PWMModulePreInit(void) +{ + Settings.seriallog_level = 0; + Settings.flag.mqtt_serial = 0; + Settings.ledstate = 0; + + + if (Settings.last_module != Settings.module) { + Settings.flag.pwm_control = true; + Settings.bri_power_on = Settings.bri_preset_low = Settings.bri_preset_high = 0; + Settings.last_module = Settings.module; + + + + + + if (Settings.param[P_HOLD_TIME] == 5) Settings.param[P_HOLD_TIME] = 40; + } + + + if (!Settings.bri_power_on) Settings.bri_power_on = 128; + if (!Settings.bri_preset_low) Settings.bri_preset_low = 10; + if (Settings.bri_preset_high < Settings.bri_preset_low) Settings.bri_preset_high = 255; + + PWMDimmerSetPoweredOffLed(); + + + + +#ifdef USE_PWM_DIMMER_REMOTE + + + if (Settings.flag4.multiple_device_groups) { + Settings.flag4.device_groups_enabled = true; + + device_group_count = 0; + for (uint32_t button_index = 0; button_index < MAX_PWM_DIMMER_KEYS; button_index++) { + if (PinUsed(GPIO_KEY1, button_index)) device_group_count++; + } + + + if (!PinUsed(GPIO_REL1) && !PinUsed(GPIO_PWM1)) { + first_device_group_is_local = false; + + + devices_present--; + light_type = 0; + } + + for (uint8_t i = 0; i < MAX_PWM_DIMMER_KEYS; i++) { + active_remote_pwm_dimmer = &remote_pwm_dimmers[i]; + active_remote_pwm_dimmer->bri_power_on = 128; + active_remote_pwm_dimmer->bri_preset_low = 10; + active_remote_pwm_dimmer->bri_preset_high = 255; + active_remote_pwm_dimmer->fixed_color_index = 128; + } + } +#endif +} + + +void PWMDimmerSetBrightnessLeds(int32_t bri) +{ + + uint32_t leds = 0; + uint32_t mask = 1; + int32_t led; + for (led = 0; led < leds_present; led++) { + if (Settings.ledmask & mask) leds++; + mask <<= 1; + } + + + + if (leds) { + led_timeout_seconds = 5; + if (bri < 0) { + bri = ((bri == -2 && Settings.flag4.led_timeout) || !Light.power ? 0 : light_state.getBri()); + if (!bri || !Settings.flag4.led_timeout) led_timeout_seconds = 0; + } + uint32_t step = 256 / (leds + 1); + + + uint32_t level = 0; + led = -1; + mask = 0; + for (uint32_t count = 0; count < leds; count++) { + level += step; + for (;;) { + led++; + mask <<= 1; + if (!mask) mask = 1; + if (Settings.ledmask & mask) break; + } + SetLedPowerIdx(led, bri >= level); + } + } +} + +void PWMDimmerSetPoweredOffLed(void) +{ + + if (PinUsed(GPIO_LEDLNK)) { + bool power_off_led_on = !power && Settings.flag4.powered_off_led; + if (ledlnk_inverted) power_off_led_on ^= 1; + digitalWrite(Pin(GPIO_LEDLNK), power_off_led_on); + } +} + +void PWMDimmerSetPower(void) +{ + DigitalWrite(GPIO_REL1, 0, bitRead(rel_inverted, 0) ? !power : power); + PWMDimmerSetBrightnessLeds(-1); + PWMDimmerSetPoweredOffLed(); +} + +#ifdef USE_DEVICE_GROUPS +void PWMDimmerHandleDevGroupItem(void) +{ +#ifdef USE_PWM_DIMMER_REMOTE + uint8_t device_group_index = *(uint8_t *)XdrvMailbox.topic; + bool is_local = ((XdrvMailbox.index & DGR_FLAG_LOCAL) != 0); + if (device_group_index > MAX_PWM_DIMMER_KEYS) return; + struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index]; +#else + if (!(XdrvMailbox.index & DGR_FLAG_LOCAL)) return; +#endif + uint32_t value = XdrvMailbox.payload; + + switch (XdrvMailbox.command_code) { +#ifdef USE_PWM_DIMMER_REMOTE + case DGR_ITEM_LIGHT_BRI: + remote_pwm_dimmer->bri = value; + break; + case DGR_ITEM_POWER: + remote_pwm_dimmer->power_on = value & 1; + remote_pwm_dimmer->power_button_increases_bri = (remote_pwm_dimmer->bri < 128); + break; +#endif + case DGR_ITEM_LIGHT_FIXED_COLOR: +#ifdef USE_PWM_DIMMER_REMOTE + remote_pwm_dimmer->fixed_color_index = value; + if (is_local) +#endif + local_fixed_color_index = value; + break; + case DGR_ITEM_BRI_POWER_ON: +#ifdef USE_PWM_DIMMER_REMOTE + remote_pwm_dimmer->bri_power_on = value; + if (is_local) +#endif + Settings.bri_power_on = value; + break; + case DGR_ITEM_BRI_PRESET_LOW: +#ifdef USE_PWM_DIMMER_REMOTE + remote_pwm_dimmer->bri_preset_low = value; + if (is_local) +#endif + Settings.bri_preset_low = value; + break; + case DGR_ITEM_BRI_PRESET_HIGH: +#ifdef USE_PWM_DIMMER_REMOTE + remote_pwm_dimmer->bri_preset_high = value; + if (is_local) +#endif + Settings.bri_preset_high = value; + break; + case DGR_ITEM_STATUS: +#ifdef USE_PWM_DIMMER_REMOTE + if (is_local) +#endif + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on, + DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); + break; + } +} +#endif + +void PWMDimmerHandleButton(uint32_t button_index, bool pressed) +{ + bool handle_tap = false; + bool state_updated = false; + int32_t bri_offset = 0; + uint8_t power_on_bri = 0; + uint8_t dgr_item = 0; + uint8_t dgr_value = 0; + uint8_t dgr_more_to_come = true; + uint8_t mqtt_trigger = 0; + + +#ifdef USE_PWM_DIMMER_REMOTE + bool power_is_on = (active_remote_pwm_dimmer ? active_remote_pwm_dimmer->power_on : power); + bool is_power_button = (button_index == power_button_index); + bool is_down_button = (button_index == down_button_index); +#else + bool power_is_on = power; + bool is_power_button = !button_index; + bool is_down_button = (button_index == (power_button_index ? 0 : 1)); +#endif + + + if (pressed) { + uint32_t now = millis(); + + + if (is_power_button) { + + + if (!ignore_power_button && buttons_pressed == 1) { + + + + + if (power_is_on) { +#ifdef USE_PWM_DIMMER_REMOTE + bri_offset = (active_remote_pwm_dimmer ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); +#else + bri_offset = (power_button_increases_bri ? 1 : -1); +#endif + invert_power_button_bri_direction = true; + } + + + + else { +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; + else +#endif + power_on_bri = Settings.bri_preset_low; + button_hold_time[button_index] = now + 500; + } + } + } + + + else { + + + if (button_pressed[power_button_index]) { + + + + if (button_tapped) { + handle_tap = true; + button_hold_time[button_index] = now + 500; + } + + + + else if (power_is_on && Settings.flag4.multiple_device_groups) { + bri_offset = (is_down_button ? -1 : 1); + } + + + else { + mqtt_trigger = (is_down_button ? 1 : 2); + button_hold_time[button_index] = now + 60000; + ignore_power_button = true; + } + } + + + + else if (power_is_on && !button_tapped) { + bri_offset = (is_down_button ? -1 : 1); + } + } + } + + + else { + bool button_was_held = button_held[button_index]; + + + if (is_power_button) { + + + + + if (button_tapped) { + if (!tap_handled) { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_remote_pwm_dimmer) { +#endif + + + if (down_button_tapped) { + Settings.flag4.led_timeout ^= 1; + if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? 0 : -1); + } + + + else { + Settings.flag4.powered_off_led ^= 1; + PWMDimmerSetPoweredOffLed(); + } +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } + tap_handled = false; + } + + + else if (button_was_held) { + + + + if (invert_power_button_bri_direction) { + invert_power_button_bri_direction = false; +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + active_remote_pwm_dimmer->power_button_increases_bri ^= 1; + else +#endif + power_button_increases_bri ^= 1; +#ifdef USE_PWM_DIMMER_REMOTE + dgr_item = DGR_ITEM_FLAGS; + state_updated = true; +#endif + } + } + + + + else if (!ignore_power_button) { +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + power_on_bri = active_remote_pwm_dimmer->bri_power_on; + else +#endif + power_on_bri = Settings.bri_power_on; + } + } + + + else { + + + + if (button_pressed[power_button_index]) { + ignore_power_button = true; + + + if (button_tapped) { + handle_tap = true; + } + + + else if (!button_was_held) { + button_tapped = true; + down_button_tapped = is_down_button; + } + } + + + else { + + + if (power_is_on) { + + + + if (!button_was_held) { + bri_offset = (is_down_button ? -5 : 5); + dgr_more_to_come = false; + state_updated = true; + } + + + + else { + dgr_item = DGR_ITEM_FLAGS; + state_updated = true; + } + } + + + + else { +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + power_on_bri = active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); + else +#endif + power_on_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); + } + } + } + } + + + if (bri_offset) { + int32_t bri; +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + bri = active_remote_pwm_dimmer->bri; + else +#endif + bri = light_state.getBri(); + int32_t new_bri = bri + bri_offset * ((dgr_item ? 16 : Settings.light_correction ? 4 : bri / 16 + 1)); + + if (bri_offset > 0) { + if (new_bri > 255) new_bri = 255; + } + else { + if (new_bri < 1) new_bri = 1; + } + if (new_bri != bri) { +#ifdef USE_DEVICE_GROUPS + SendDeviceGroupMessage(power_button_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_BRI, new_bri); +#endif +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) { + active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; + PWMDimmerSetBrightnessLeds(new_bri); + } + else { +#endif + skip_light_fade = true; +#ifdef USE_DEVICE_GROUPS + ignore_dgr_sends = true; +#endif + light_state.setBri(new_bri); + LightAnimate(); + skip_light_fade = false; +#ifdef USE_DEVICE_GROUPS + ignore_dgr_sends = false; +#endif + Settings.bri_power_on = new_bri; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } +#ifdef USE_PWM_DIMMER_REMOTE + else if (active_remote_pwm_dimmer) + PWMDimmerSetBrightnessLeds(new_bri); +#endif + else + PWMDimmerSetBrightnessLeds(-1); + } + + + else if (power_on_bri) { + power_t new_power; +#ifdef USE_DEVICE_GROUPS +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) { + active_remote_pwm_dimmer->power_on ^= 1; + new_power = active_remote_pwm_dimmer->power_on; + PWMDimmerSetBrightnessLeds(new_power ? -power_on_bri : 0); + } + else { +#endif + new_power = power ^ 1; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + if (new_power) + SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, power_on_bri, DGR_ITEM_POWER, new_power); + else + SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, new_power); +#endif +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) + active_remote_pwm_dimmer->power_button_increases_bri = (power_on_bri < 128); + else { +#endif + light_state.setBri(power_on_bri); +#ifdef USE_DEVICE_GROUPS + Light.devgrp_no_channels_out = true; +#endif + ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); +#ifdef USE_DEVICE_GROUPS + Light.devgrp_no_channels_out = false; +#endif +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } + + + + if (handle_tap) { + ignore_power_button = tap_handled = true; + + + + if (down_button_tapped) { +#ifdef USE_DEVICE_GROUPS + int8_t add_value = (is_down_button ? -1 : 1); +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) { + active_remote_pwm_dimmer->fixed_color_index += add_value; + dgr_value = active_remote_pwm_dimmer->fixed_color_index; + } + else { +#endif + local_fixed_color_index += add_value; + dgr_value = local_fixed_color_index; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; +#endif + ; + } + + else { + mqtt_trigger = (is_down_button ? 3 : 4); + } + } + + + if (mqtt_trigger) { + char topic[TOPSZ]; + sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); +#ifdef USE_PWM_DIMMER_REMOTE + if (active_remote_pwm_dimmer) { + snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/EVENT"), device_groups[power_button_index].group_name); + MqttPublish(topic); + } + else +#endif + MqttPublishPrefixTopic_P(CMND, PSTR("EVENT")); + } + + + if (dgr_item) { +#ifdef USE_DEVICE_GROUPS + DevGroupMessageType message_type = DGR_MSGTYP_UPDATE_DIRECT; +#ifdef USE_PWM_DIMMER_REMOTE + if (handle_tap && !active_remote_pwm_dimmer) +#else + if (handle_tap) +#endif + message_type = (DevGroupMessageType)(message_type + DGR_MSGTYPFLAG_WITH_LOCAL); + SendDeviceGroupMessage(power_button_index, message_type, dgr_item, dgr_value); +#endif +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_remote_pwm_dimmer) +#endif + light_controller.saveSettings(); + } + + if (state_updated && Settings.flag3.hass_tele_on_power) { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_remote_pwm_dimmer) +#endif + MqttPublishTeleState(); + } +} + + + + + +void CmndBriPreset(void) +{ + if (XdrvMailbox.data_len > 0) { + bool valid = true; + uint32_t value; + uint8_t parm[2]; + parm[0] = Settings.bri_preset_low; + parm[1] = Settings.bri_preset_high; + char * ptr = XdrvMailbox.data; + for (uint32_t i = 0; i < 2; i++) { + while (*ptr == ' ') ptr++; + if (*ptr == '+') { + if (parm[i] < 255) parm[i]++; + } + else if (*ptr == '-') { + if (parm[i] > 1) parm[i]--; + } + else { + value = strtoul(ptr, &ptr, 0); + if (value < 1 || value > 255) { + valid = false; + break; + } + parm[i] = value; + if (*ptr != ',') break; + } + ptr++; + } + if (valid && !*ptr) { + if (parm[0] < parm[1]) { + Settings.bri_preset_low = parm[0]; + Settings.bri_preset_high = parm[1]; + } else + { + Settings.bri_preset_low = parm[1]; + Settings.bri_preset_high = parm[0]; + } +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); +#endif + } + } + Response_P(PSTR("{\"" D_CMND_BRI_PRESET "\":{\"Low\":%d,\"High\":%d}}"), Settings.bri_preset_low, Settings.bri_preset_high); +} + +#ifdef USE_DEVICE_GROUPS +void CmndPWMDimmerPWMs(void) +{ + if (XdrvMailbox.data_len > 0 && XdrvMailbox.payload <= 5) { + Settings.pwm_dimmer_cfg.pwm_count = XdrvMailbox.payload - 1; + restart_flag = 2; + } + Response_P(PSTR("{\"" D_CMND_PWM_DIMMER_PWMS "\":%u}"), Settings.pwm_dimmer_cfg.pwm_count + 1); +} +#endif + + + + + +bool Xdrv35(uint8_t function) +{ + bool result = false; + + if (PWM_DIMMER != my_module_type) return result; + + switch (function) { + case FUNC_EVERY_SECOND: + + if (led_timeout_seconds && !--led_timeout_seconds) { + PWMDimmerSetBrightnessLeds(-2); + } + + + + + + if (global_state.data) + restore_powered_off_led_counter = 5; + else if (restore_powered_off_led_counter) { + PWMDimmerSetPoweredOffLed(); + restore_powered_off_led_counter--; + } + break; + + case FUNC_BUTTON_PRESSED: + + if (!XdrvMailbox.payload || button_pressed[XdrvMailbox.index]) { + uint32_t button_index = XdrvMailbox.index; + uint32_t now = millis(); + + + if (!XdrvMailbox.payload) { + + + + if (!button_pressed[button_index]) { + button_pressed[button_index] = true; + button_hold_time[button_index] = now + (button_index == power_button_index ? 500 : 250); + buttons_pressed++; + if (buttons_pressed > 1) multibutton_in_progress = true; + +#ifdef USE_PWM_DIMMER_REMOTE +# 759 "/workspace/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" + if (buttons_pressed == 1 && Settings.flag4.multiple_device_groups) { + power_button_index = button_index; + down_button_index = (Pin(GPIO_KEY1, power_button_index) == 15 ? gpio_pin[1] : gpio_pin[15]) - 32; + active_remote_pwm_dimmer = nullptr; + if (power_button_index || !first_device_group_is_local) + active_remote_pwm_dimmer = &remote_pwm_dimmers[power_button_index]; + } +#endif + } + + + else if (button_hold_time[button_index] <= now) { + PWMDimmerHandleButton(button_index, true); + button_held[button_index] = true; + } + } + + + + else { + button_pressed[button_index] = false; + buttons_pressed--; + + + if (multibutton_in_progress || button_held[button_index]) { + PWMDimmerHandleButton(button_index, false); + + + + ignore_any_key_time = now + 500; + } + + + if (button_index == power_button_index) { + if (ignore_power_button) ignore_any_key_time = now + 500; + ignore_power_button = false; + button_tapped = false; + } + button_held[button_index] = false; + } + + + if (multibutton_in_progress) { + result = true; + if (buttons_pressed == 0) multibutton_in_progress = false; + } + } + break; + + case FUNC_ANY_KEY: + { + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; + if ((state == 2 || state == 10) && ignore_any_key_time < millis()) { + PWMDimmerHandleButton((XdrvMailbox.payload & 0xFF) - 1, false); + } + } + break; + +#ifdef USE_DEVICE_GROUPS + case FUNC_DEVICE_GROUP_ITEM: + PWMDimmerHandleDevGroupItem(); + break; +#endif + + case FUNC_COMMAND: + result = DecodeCommand(kPWMDimmerCommands, PWMDimmerCommand); + break; + + case FUNC_SET_DEVICE_POWER: + + + if (XdrvMailbox.index) { + PWMDimmerSetPower(); + + + power_button_increases_bri = (light_state.getBri() < 128); + } + + + + else + result = true; + break; + + case FUNC_PRE_INIT: + PWMModulePreInit(); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_36_keeloq.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_36_keeloq.ino" +#ifdef USE_KEELOQ +# 30 "/workspace/Tasmota/tasmota/xdrv_36_keeloq.ino" +#define XDRV_36 36 + +#include "cc1101.h" +#include + +#define SYNC_WORD 199 + +#define Lowpulse 400 +#define Highpulse 800 + +const char kJaroliftCommands[] PROGMEM = "Keeloq|" + "SendRaw|SendButton|Set"; + +void (* const jaroliftCommand[])(void) PROGMEM = { + &CmndSendRaw, &CmdSendButton, &CmdSet}; + +CC1101 cc1101; + +struct JAROLIFT_DEVICE { + int device_key_msb = 0x0; + int device_key_lsb = 0x0; + uint64_t button = 0x0; + int disc = 0x0100; + uint32_t enc = 0x0; + uint64_t pack = 0; + int count = 0; + uint32_t serial = 0x0; + uint8_t port_tx; + uint8_t port_rx; +} jaroliftDevice; + +void CmdSet(void) +{ + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload > 0) { + char *p; + uint32_t i = 0; + uint32_t param[4] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) { + param[i] = strtoul(str, nullptr, 0); + i++; + } + for (uint32_t i = 0; i < 3; i++) { + if (param[i] < 1) { param[i] = 1; } + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); + Settings.keeloq_master_msb = param[0]; + Settings.keeloq_master_lsb = param[1]; + Settings.keeloq_serial = param[2]; + Settings.keeloq_count = param[3]; + + jaroliftDevice.serial = param[2]; + jaroliftDevice.count = param[3]; + + GenerateDeviceCryptKey(); + ResponseCmndDone(); + } else { + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no payload")); + } + } else { + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no param")); + } +} + +void GenerateDeviceCryptKey() +{ + Keeloq k(Settings.keeloq_master_msb, Settings.keeloq_master_lsb); + jaroliftDevice.device_key_msb = k.decrypt(jaroliftDevice.serial | 0x60000000L); + jaroliftDevice.device_key_lsb = k.decrypt(jaroliftDevice.serial | 0x20000000L); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); +} + +void CmdSendButton(void) +{ + noInterrupts(); + entertx(); + + if (XdrvMailbox.data_len > 0) + { + if (XdrvMailbox.payload > 0) + { + jaroliftDevice.button = strtoul(XdrvMailbox.data, nullptr, 0); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("msb: %08x"), jaroliftDevice.device_key_msb); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("serial: %08x"), jaroliftDevice.serial); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("disc: %08x"), jaroliftDevice.disc); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count); + + CreateKeeloqPacket(); + jaroliftDevice.count++; + Settings.keeloq_count = jaroliftDevice.count; + + for(int repeat = 0; repeat <= 1; repeat++) + { + uint64_t bitsToSend = jaroliftDevice.pack; + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(1150); + SendSyncPreamble(13); + delayMicroseconds(3500); + for(int i=72; i>0; i--) + { + SendBit(bitsToSend & 0x0000000000000001); + bitsToSend >>= 1; + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + + delay(16); + } + } + } + + interrupts(); + enterrx(); + + ResponseCmndDone(); +} + +void SendBit(byte bitToSend) +{ + if (bitToSend==1) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(Lowpulse); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(Highpulse); + } + else + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(Highpulse); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(Lowpulse); + } +} + +void CmndSendRaw(void) +{ + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cmd send called at %d"), micros()); + noInterrupts(); + entertx(); + for(int repeat = 0; repeat <= 1; repeat++) + { + if (XdrvMailbox.data_len > 0) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(1150); + SendSyncPreamble(13); + delayMicroseconds(3500); + + for(int i=XdrvMailbox.data_len-1; i>=0; i--) + { + SendBit(XdrvMailbox.data[i] == '1'); + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + + delay(16); + } + interrupts(); + } + enterrx(); + ResponseCmndDone(); +} + +void enterrx() { + unsigned char marcState = 0; + cc1101.setRxState(); + delay(2); + unsigned long rx_time = micros(); + while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x0D ) + { + if (micros() - rx_time > 50000) break; + } +} + +void entertx() { + unsigned char marcState = 0; + cc1101.setTxState(); + delay(2); + unsigned long rx_time = micros(); + while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x13 && 0x14 && 0x15) + { + if (micros() - rx_time > 50000) break; + } +} + +void SendSyncPreamble(int l) +{ + for (int i = 0; i < l; ++i) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(400); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(380); + } +} + +void CreateKeeloqPacket() +{ + Keeloq k(jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); + unsigned int result = (jaroliftDevice.disc << 16) | jaroliftDevice.count; + jaroliftDevice.pack = (uint64_t)0; + jaroliftDevice.pack |= jaroliftDevice.serial & 0xfffffffL; + jaroliftDevice.pack |= (jaroliftDevice.button & 0xfL) << 28; + jaroliftDevice.pack <<= 32; + + jaroliftDevice.enc = k.encrypt(result); + jaroliftDevice.pack |= jaroliftDevice.enc; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack); +} + +void KeeloqInit() +{ + jaroliftDevice.port_tx = Pin(GPIO_CC1101_GDO2); + jaroliftDevice.port_rx = Pin(GPIO_CC1101_GDO0); + + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()")); + delay(100); + cc1101.init(); + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done.")); + cc1101.setSyncWord(SYNC_WORD, false); + cc1101.setCarrierFreq(CFREQ_433); + cc1101.disableAddressCheck(); + + pinMode(jaroliftDevice.port_tx, OUTPUT); + pinMode(jaroliftDevice.port_rx, INPUT_PULLUP); + + jaroliftDevice.serial = Settings.keeloq_serial; + jaroliftDevice.count = Settings.keeloq_count; + GenerateDeviceCryptKey(); +} + + + + +bool Xdrv36(uint8_t function) +{ + if (!PinUsed(GPIO_CC1101_GDO0) || !PinUsed(GPIO_CC1101_GDO2)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_COMMAND: + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("calling command")); + result = DecodeCommand(kJaroliftCommands, jaroliftCommand); + break; + case FUNC_INIT: + KeeloqInit(); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("init done.")); + break; + } + + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +#ifdef USE_LIGHT +#ifdef USE_SONOFF_D1 +# 35 "/workspace/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +#define XDRV_37 37 + +struct SONOFFD1 { + uint8_t receive_len = 0; + uint8_t power = 255; + uint8_t dimmer = 255; +} SnfD1; + + + +void SonoffD1Received(void) +{ + if (serial_in_byte_counter < 8) { return; } + + uint8_t action = serial_in_buffer[6] & 1; + if (action != SnfD1.power) { + SnfD1.power = action; + + + + ExecuteCommandPower(1, action, SRC_SWITCH); + } + + uint8_t dimmer = serial_in_buffer[7]; + if (dimmer != SnfD1.dimmer) { + SnfD1.dimmer = dimmer; + + + + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), SnfD1.dimmer); + ExecuteCommand(scmnd, SRC_SWITCH); + } +# 79 "/workspace/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +} + +bool SonoffD1SerialInput(void) +{ + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + SnfD1.receive_len = 7; + } + if (SnfD1.receive_len) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (6 == serial_in_byte_counter) { + SnfD1.receive_len += serial_in_byte; + } + if (serial_in_byte_counter == SnfD1.receive_len) { +# 102 "/workspace/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < SnfD1.receive_len -1; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[SnfD1.receive_len -1]) { + SonoffD1Received(); + SnfD1.receive_len = 0; + return true; + } + } + serial_in_byte = 0; + } + return false; +} + + + +void SonoffD1Send() +{ + + uint8_t buffer[17] = { 0xAA,0x55,0x01,0x04,0x00,0x0A,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00 }; + + buffer[6] = SnfD1.power; + buffer[7] = SnfD1.dimmer; + + for (uint32_t i = 0; i < sizeof(buffer); i++) { + if ((i > 1) && (i < sizeof(buffer) -1)) { buffer[16] += buffer[i]; } + Serial.write(buffer[i]); + } +} + +bool SonoffD1SendPower(void) +{ + uint8_t action = XdrvMailbox.index &1; + if (action != SnfD1.power) { + SnfD1.power = action; + + + + SonoffD1Send(); + } + return true; +} + +bool SonoffD1SendDimmer(void) +{ + uint8_t dimmer = LightGetDimmer(1); + dimmer = (dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : dimmer; + dimmer = (dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : dimmer; + if (dimmer != SnfD1.dimmer) { + SnfD1.dimmer = dimmer; + + + + SonoffD1Send(); + } + return true; +} + +bool SonoffD1ModuleSelected(void) +{ + SetSerial(9600, TS_SERIAL_8N1); + + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + + + + + +bool Xdrv37(uint8_t function) +{ + bool result = false; + + if (SONOFF_D1 == my_module_type) { + switch (function) { + case FUNC_SERIAL: + result = SonoffD1SerialInput(); + break; + case FUNC_SET_DEVICE_POWER: + result = SonoffD1SendPower(); + break; + case FUNC_SET_CHANNELS: + result = SonoffD1SendDimmer(); + break; + case FUNC_MODULE_INIT: + result = SonoffD1ModuleSelected(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_38_ping.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_38_ping.ino" +#ifdef USE_PING + +#define XDRV_38 38 + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/raw.h" +#include "lwip/timeouts.h" + +const char kPingCommands[] PROGMEM = "|" + D_CMND_PING + ; + +void (* const PingCommand[])(void) PROGMEM = { + &CmndPing, + }; + +extern "C" { + + extern uint32 system_relative_time(uint32 time); + extern void ets_bzero(void *s, size_t n); + + const uint16_t Ping_ID = 0xAFAF; + const size_t Ping_data_size = 32; + const uint32_t Ping_timeout_ms = 1000; + const uint32_t Ping_coarse = 1000; + + typedef struct Ping_t { + uint32 ip; + Ping_t *next; + uint16_t seq_num; + uint16_t seqno; + uint8_t success_count; + uint8_t timeout_count; + uint8_t to_send_count; + uint32_t ping_time_sent; + uint32_t min_time; + uint32_t max_time; + uint32_t sum_time; + bool done; + bool fast; + String hostname; + } Ping_t; + + + Ping_t *ping_head = nullptr; + struct raw_pcb *t_ping_pcb = nullptr; + + + + + + + + Ping_t ICACHE_FLASH_ATTR * t_ping_find(uint32_t ip) { + Ping_t *ping = ping_head; + while (ping != nullptr) { + if (ping->ip == ip) { + return ping; + } + ping = ping->next; + } + return nullptr; + } +# 92 "/workspace/Tasmota/tasmota/xdrv_38_ping.ino" + void ICACHE_FLASH_ATTR t_ping_timeout(void* arg) { + Ping_t *ping = (Ping_t*) arg; + ping->timeout_count++; + } + + + + + + + void ICACHE_FLASH_ATTR t_ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len, Ping_t *ping) { + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = Ping_ID; + ping->seq_num++; + if (ping->seq_num == 0x7fff) { ping->seq_num = 0; } + + iecho->seqno = htons(ping->seq_num); + + + for (uint32_t i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); + } + + + + void ICACHE_FLASH_ATTR t_ping_send(struct raw_pcb *raw, Ping_t *ping) { + struct pbuf *p; + uint16_t ping_size = sizeof(struct icmp_echo_hdr) + Ping_data_size; + + ping->ping_time_sent = system_get_time(); + p = pbuf_alloc(PBUF_IP, ping_size, PBUF_RAM); + if (!p) { return; } + if ((p->len == p->tot_len) && (p->next == nullptr)) { + ip_addr_t ping_target; + struct icmp_echo_hdr *iecho; + + ping_target.addr = ping->ip; + iecho = (struct icmp_echo_hdr *) p->payload; + + t_ping_prepare_echo(iecho, ping_size, ping); + raw_sendto(raw, p, &ping_target); + } + pbuf_free(p); + } + + + + + + static void ICACHE_FLASH_ATTR t_ping_coarse_tmr(void *arg) { + Ping_t *ping = (Ping_t*) arg; + if (ping->to_send_count > 0) { + ping->to_send_count--; + + t_ping_send(t_ping_pcb, ping); + + sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); + sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); + } else { + sys_untimeout(t_ping_coarse_tmr, ping); + ping->done = true; + } + } + + + + + + + + static uint8_t ICACHE_FLASH_ATTR t_ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) { + Ping_t *ping = t_ping_find(addr->addr); + + if (nullptr == ping) { + return 0; + } + + if (pbuf_header( p, -PBUF_IP_HLEN)==0) { + struct icmp_echo_hdr *iecho; + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == Ping_ID) && (iecho->seqno == htons(ping->seq_num)) && iecho->type == ICMP_ER) { + + if (iecho->seqno != ping->seqno){ + + sys_untimeout(t_ping_timeout, ping); + uint32_t delay = system_relative_time(ping->ping_time_sent); + delay /= 1000; + + ping->sum_time += delay; + if (delay < ping->min_time) { ping->min_time = delay; } + if (delay > ping->max_time) { ping->max_time = delay; } + + ping->success_count++; + ping->seqno = iecho->seqno; + if (ping->fast) { + sys_untimeout(t_ping_coarse_tmr, ping); + ping->done = true; + ping->to_send_count = 0; + } + } + + pbuf_free(p); + return 1; + } + } + + return 0; + } + + + + + + void t_ping_register_pcb(void) { + if (nullptr == t_ping_pcb) { + t_ping_pcb = raw_new(IP_PROTO_ICMP); + + raw_recv(t_ping_pcb, t_ping_recv, nullptr); + raw_bind(t_ping_pcb, IP_ADDR_ANY); + } + } + + + void t_ping_deregister_pcb(void) { + if (nullptr == ping_head) { + raw_remove(t_ping_pcb); + t_ping_pcb = nullptr; + } + } +# 237 "/workspace/Tasmota/tasmota/xdrv_38_ping.ino" + int32_t t_ping_start(const char *hostname, uint32_t count) { + IPAddress ipfull; + if (!WiFi.hostByName(hostname, ipfull)) { + return -2; + } + + uint32_t ip = ipfull; + if (0xFFFFFFFF == ip) { return -2; } + + + if (t_ping_find(ip)) { + return -1; + } + + Ping_t *ping = new Ping_t(); + if (0 == count) { + count = 4; + ping->fast = true; + } + ping->min_time = UINT32_MAX; + ping->ip = ip; + ping->to_send_count = count - 1; + ping->hostname = hostname; + + + ping->next = ping_head; + ping_head = ping; + + t_ping_register_pcb(); + t_ping_send(t_ping_pcb, ping); + + + sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); + sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); + + return 0; + } + +} + + +void PingResponsePoll(void) { + Ping_t *ping = ping_head; + Ping_t **prev_link = &ping_head; + + while (ping != nullptr) { + if (ping->done) { + uint32_t success = ping->success_count; + uint32_t ip = ping->ip; + + Response_P(PSTR("{\"" D_JSON_PING "\":{\"%s\":{" + "\"Reachable\":%s" + ",\"IP\":\"%d.%d.%d.%d\"" + ",\"Success\":%d" + ",\"Timeout\":%d" + ",\"MinTime\":%d" + ",\"MaxTime\":%d" + ",\"AvgTime\":%d" + "}}}"), + ping->hostname.c_str(), + success ? "true" : "false", + ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24, + success, + ping->timeout_count, + success ? ping->min_time : 0, + ping->max_time, + success ? ping->sum_time / success : 0 + ); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_PING)); + + + *prev_link = ping->next; + + Ping_t *ping_to_delete = ping; + ping = ping->next; + delete ping_to_delete; + } else { + prev_link = &ping->next; + ping = ping->next; + } + } +} + + + + + +void CmndPing(void) { + uint32_t count = XdrvMailbox.index; + + RemoveSpace(XdrvMailbox.data); + if (count > 10) { count = 8; } + + int32_t res = t_ping_start(XdrvMailbox.data, count); + if (0 == res) { + ResponseCmndDone(); + } else if (-1 == res) { + ResponseCmndChar_P(PSTR("Ping already ongoing for this IP")); + } else { + Response_P(PSTR("{\"" D_JSON_PING "\":{\"%s\":{" + "\"Reachable\":false" + ",\"IP\":\"\"" + ",\"Success\":false" + "}}}"), + XdrvMailbox.data + ); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_PING)); + ResponseCmndChar_P(PSTR("Unable to resolve IP address")); + } +} + + + + + + +bool Xdrv38(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + PingResponsePoll(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kPingCommands, PingCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_39_thermostat.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_39_thermostat.ino" +#ifdef USE_THERMOSTAT + +#define XDRV_39 39 +# 31 "/workspace/Tasmota/tasmota/xdrv_39_thermostat.ino" +#ifdef DEBUG_THERMOSTAT +#define DOMOTICZ_MAX_IDX 4 +#define DOMOTICZ_IDX1 791 +#define DOMOTICZ_IDX2 792 +#define DOMOTICZ_IDX3 799 +#define DOMOTICZ_IDX4 800 +#define DOMOTICZ_IDX5 801 +#endif + + +#define D_CMND_THERMOSTATMODESET "ThermostatModeSet" +#define D_CMND_CLIMATEMODESET "ClimateModeSet" +#define D_CMND_TEMPFROSTPROTECTSET "TempFrostProtectSet" +#define D_CMND_CONTROLLERMODESET "ControllerModeSet" +#define D_CMND_INPUTSWITCHSET "InputSwitchSet" +#define D_CMND_INPUTSWITCHUSE "InputSwitchUse" +#define D_CMND_OUTPUTRELAYSET "OutputRelaySet" +#define D_CMND_TIMEALLOWRAMPUPSET "TimeAllowRampupSet" +#define D_CMND_TEMPFORMATSET "TempFormatSet" +#define D_CMND_TEMPMEASUREDSET "TempMeasuredSet" +#define D_CMND_TEMPTARGETSET "TempTargetSet" +#define D_CMND_TEMPMEASUREDGRDREAD "TempMeasuredGrdRead" +#define D_CMND_TEMPSENSNUMBERSET "TempSensNumberSet" +#define D_CMND_SENSORINPUTSET "SensorInputSet" +#define D_CMND_STATEEMERGENCYSET "StateEmergencySet" +#define D_CMND_TIMEMANUALTOAUTOSET "TimeManualToAutoSet" +#define D_CMND_TIMEONLIMITSET "TimeOnLimitSet" +#define D_CMND_PROPBANDSET "PropBandSet" +#define D_CMND_TIMERESETSET "TimeResetSet" +#define D_CMND_TIMEPICYCLESET "TimePiCycleSet" +#define D_CMND_TEMPANTIWINDUPRESETSET "TempAntiWindupResetSet" +#define D_CMND_TEMPHYSTSET "TempHystSet" +#ifdef USE_PI_AUTOTUNING +#define D_CMND_PERFLEVELAUTOTUNE "PerfLevelAutotune" +#endif +#define D_CMND_TIMEMAXACTIONSET "TimeMaxActionSet" +#define D_CMND_TIMEMINACTIONSET "TimeMinActionSet" +#define D_CMND_TIMEMINTURNOFFACTIONSET "TimeMinTurnoffActionSet" +#define D_CMND_TEMPRUPDELTINSET "TempRupDeltInSet" +#define D_CMND_TEMPRUPDELTOUTSET "TempRupDeltOutSet" +#define D_CMND_TIMERAMPUPMAXSET "TimeRampupMaxSet" +#define D_CMND_TIMERAMPUPCYCLESET "TimeRampupCycleSet" +#define D_CMND_TEMPRAMPUPPIACCERRSET "TempRampupPiAccErrSet" +#define D_CMND_TIMEPIPROPORTREAD "TimePiProportRead" +#define D_CMND_TIMEPIINTEGRREAD "TimePiIntegrRead" +#define D_CMND_TIMESENSLOSTSET "TimeSensLostSet" +#define D_CMND_DIAGNOSTICMODESET "DiagnosticModeSet" +#define D_CMND_CTRDUTYCYCLEREAD "CtrDutyCycleRead" +#define D_CMND_ENABLEOUTPUTSET "EnableOutputSet" + +enum ThermostatModes { THERMOSTAT_OFF, THERMOSTAT_AUTOMATIC_OP, THERMOSTAT_MANUAL_OP, THERMOSTAT_MODES_MAX }; +#ifdef USE_PI_AUTOTUNING +enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_PI_AUTOTUNE, CTR_MODES_MAX }; +enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI, CTR_HYBRID_PI_AUTOTUNE }; +enum AutotuneStates { AUTOTUNE_OFF, AUTOTUNE_ON, AUTOTUNE_MAX }; +enum AutotunePerformanceParam { AUTOTUNE_PERF_FAST, AUTOTUNE_PERF_NORMAL, AUTOTUNE_PERF_SLOW, AUTOTUNE_PERF_MAX }; +#else +enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_MODES_MAX }; +enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI }; +#endif +enum ClimateModes { CLIMATE_HEATING, CLIMATE_COOLING, CLIMATE_MODES_MAX }; +enum InterfaceStates { IFACE_OFF, IFACE_ON }; +enum InputUsage { INPUT_NOT_USED, INPUT_USED }; +enum CtrCycleStates { CYCLE_OFF, CYCLE_ON }; +enum EmergencyStates { EMERGENCY_OFF, EMERGENCY_ON }; +enum SensorType { SENSOR_MQTT, SENSOR_LOCAL, SENSOR_MAX }; +enum TempFormat { TEMP_CELSIUS, TEMP_FAHRENHEIT }; +enum TempConvType { TEMP_CONV_ABSOLUTE, TEMP_CONV_RELATIVE }; +enum DiagnosticModes { DIAGNOSTIC_OFF, DIAGNOSTIC_ON }; +enum ThermostatSupportedInputSwitches { + THERMOSTAT_INPUT_NONE, + THERMOSTAT_INPUT_SWT1 = 1, + THERMOSTAT_INPUT_SWT2, + THERMOSTAT_INPUT_SWT3, + THERMOSTAT_INPUT_SWT4, + THERMOSTAT_INPUT_SWT5, + THERMOSTAT_INPUT_SWT6, + THERMOSTAT_INPUT_SWT7, + THERMOSTAT_INPUT_SWT8 +}; +enum ThermostatSupportedOutputRelays { + THERMOSTAT_OUTPUT_NONE, + THERMOSTAT_OUTPUT_REL1 = 1, + THERMOSTAT_OUTPUT_REL2, + THERMOSTAT_OUTPUT_REL3, + THERMOSTAT_OUTPUT_REL4, + THERMOSTAT_OUTPUT_REL5, + THERMOSTAT_OUTPUT_REL6, + THERMOSTAT_OUTPUT_REL7, + THERMOSTAT_OUTPUT_REL8 +}; + +typedef union { + uint32_t data; + struct { + uint32_t thermostat_mode : 2; + uint32_t controller_mode : 2; + uint32_t climate_mode : 1; + uint32_t sensor_alive : 1; + uint32_t sensor_type : 1; + uint32_t temp_format : 1; + uint32_t command_output : 1; + uint32_t status_output : 1; + uint32_t status_input : 1; + uint32_t use_input : 1; + uint32_t phase_hybrid_ctr : 2; + uint32_t status_cycle_active : 1; + uint32_t counter_seconds : 6; + uint32_t output_relay_number : 4; + uint32_t input_switch_number : 3; + uint32_t enable_output : 1; +#ifdef USE_PI_AUTOTUNING + uint32_t autotune_flag : 1; + uint32_t autotune_perf_mode : 2; +#else + uint32_t free : 3; +#endif + }; +} ThermostatStateBitfield; + +typedef union { + uint8_t data; + struct { + uint8_t state_emergency : 1; + uint8_t diagnostic_mode : 1; + uint8_t output_inconsist_ctr : 2; + }; +} ThermostatDiagBitfield; + +#ifdef DEBUG_THERMOSTAT +const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}"; +uint16_t Domoticz_Virtual_Switches[DOMOTICZ_MAX_IDX] = { DOMOTICZ_IDX1, DOMOTICZ_IDX3, DOMOTICZ_IDX4, DOMOTICZ_IDX5 }; +#endif + +const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_CLIMATEMODESET "|" + D_CMND_TEMPFROSTPROTECTSET "|" D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_INPUTSWITCHUSE "|" + D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|" + D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|" + D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" +#ifdef USE_PI_AUTOTUNING + D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_PERFLEVELAUTOTUNE "|" D_CMND_TIMEMAXACTIONSET "|" +#else + D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" +#endif + D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" + D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" + D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET "|" D_CMND_CTRDUTYCYCLEREAD "|" + D_CMND_ENABLEOUTPUTSET; + +void (* const ThermostatCommand[])(void) PROGMEM = { + &CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, + &CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet, + &CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet, + &CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, +#ifdef USE_PI_AUTOTUNING + &CmndPerfLevelAutotune, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, +#else + &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, +#endif + &CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet, + &CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet, &CmndCtrDutyCycleRead, + &CmndEnableOutputSet }; + +struct THERMOSTAT { + ThermostatStateBitfield status; + uint32_t timestamp_temp_measured_update = 0; + uint32_t timestamp_temp_meas_change_update = 0; + uint32_t timestamp_output_off = 0; + uint32_t timestamp_input_on = 0; + uint32_t time_thermostat_total = 0; + uint32_t time_ctr_checkpoint = 0; + uint32_t time_ctr_changepoint = 0; + int32_t temp_measured_gradient = 0; + int16_t temp_target_level = THERMOSTAT_TEMP_INIT; + int16_t temp_target_level_ctr = THERMOSTAT_TEMP_INIT; + int16_t temp_pi_accum_error = 0; + int16_t temp_pi_error = 0; + int32_t time_proportional_pi; + int32_t time_integral_pi; + int32_t time_total_pi; + uint16_t kP_pi = 0; + uint16_t kI_pi = 0; + int32_t temp_rampup_meas_gradient = 0; + uint32_t timestamp_rampup_start = 0; + uint32_t time_rampup_deadtime = 0; + uint32_t time_rampup_nextcycle = 0; + int16_t temp_measured = 0; + int16_t temp_rampup_output_off = 0; + uint8_t time_output_delay = THERMOSTAT_TIME_OUTPUT_DELAY; + uint8_t counter_rampup_cycles = 0; + uint8_t temp_rampup_pi_acc_error = THERMOSTAT_TEMP_PI_RAMPUP_ACC_E; + uint8_t temp_rampup_delta_out = THERMOSTAT_TEMP_RAMPUP_DELTA_OUT; + uint8_t temp_rampup_delta_in = THERMOSTAT_TEMP_RAMPUP_DELTA_IN; + uint8_t val_prop_band = THERMOSTAT_PROP_BAND; + int16_t temp_rampup_start = 0; + int16_t temp_rampup_cycle = 0; + uint16_t time_rampup_max = THERMOSTAT_TIME_RAMPUP_MAX; + uint16_t time_rampup_cycle = THERMOSTAT_TIME_RAMPUP_CYCLE; + uint16_t time_allow_rampup = THERMOSTAT_TIME_ALLOW_RAMPUP; + uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; + uint16_t time_manual_to_auto = THERMOSTAT_TIME_MANUAL_TO_AUTO; + uint32_t time_reset = THERMOSTAT_TIME_RESET; + uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; + uint16_t time_max_action = THERMOSTAT_TIME_MAX_ACTION; + uint16_t time_min_action = THERMOSTAT_TIME_MIN_ACTION; + uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; + uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; + int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; + uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; + ThermostatDiagBitfield diag; +#ifdef USE_PI_AUTOTUNING + uint8_t dutycycle_step_autotune = THERMOSTAT_DUTYCYCLE_AUTOTUNE; + uint8_t peak_ctr = 0; + uint8_t temp_band_no_peak_det = THERMOSTAT_TEMP_BAND_NO_PEAK_DET; + uint8_t val_prop_band_atune = 0; + uint32_t time_reset_atune = 0; + uint16_t pU_pi_atune = 0; + uint16_t kU_pi_atune = 0; + uint16_t kP_pi_atune = 0; + uint16_t kI_pi_atune = 0; + int16_t temp_peaks_atune[THERMOSTAT_PEAKNUMBER_AUTOTUNE]; + int16_t temp_abs_max_atune; + int16_t temp_abs_min_atune; + uint16_t time_peak_timestamps_atune[THERMOSTAT_PEAKNUMBER_AUTOTUNE]; + uint16_t time_std_dev_peak_det_ok = THERMOSTAT_TIME_STD_DEV_PEAK_DET_OK; +#endif +} Thermostat[THERMOSTAT_CONTROLLER_OUTPUTS]; + + + +void ThermostatInit(uint8_t ctr_output) +{ + + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF; + Thermostat[ctr_output].status.controller_mode = CTR_HYBRID; + Thermostat[ctr_output].status.climate_mode = CLIMATE_HEATING; + Thermostat[ctr_output].status.sensor_alive = IFACE_OFF; + Thermostat[ctr_output].status.sensor_type = SENSOR_MQTT; + Thermostat[ctr_output].status.temp_format = TEMP_CELSIUS; + Thermostat[ctr_output].status.command_output = IFACE_OFF; + Thermostat[ctr_output].status.status_output = IFACE_OFF; + Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI; + Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF; + Thermostat[ctr_output].diag.state_emergency = EMERGENCY_OFF; + Thermostat[ctr_output].status.counter_seconds = 0; + Thermostat[ctr_output].status.output_relay_number = (THERMOSTAT_RELAY_NUMBER + ctr_output); + Thermostat[ctr_output].status.input_switch_number = (THERMOSTAT_SWITCH_NUMBER + ctr_output); + Thermostat[ctr_output].status.use_input = INPUT_NOT_USED; + Thermostat[ctr_output].status.enable_output = IFACE_ON; + Thermostat[ctr_output].diag.output_inconsist_ctr = 0; + Thermostat[ctr_output].diag.diagnostic_mode = DIAGNOSTIC_ON; +#ifdef USE_PI_AUTOTUNING + Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF; + Thermostat[ctr_output].status.autotune_perf_mode = AUTOTUNE_PERF_FAST; +#endif + + if (Thermostat[ctr_output].status.enable_output == IFACE_ON) { + ExecuteCommandPower(Thermostat[ctr_output].status.output_relay_number, POWER_OFF, SRC_THERMOSTAT); + } +} + +bool ThermostatMinuteCounter(uint8_t ctr_output) +{ + bool result = false; + Thermostat[ctr_output].status.counter_seconds++; + + if ((Thermostat[ctr_output].status.counter_seconds % 60) == 0) { + result = true; + Thermostat[ctr_output].status.counter_seconds = 0; + } + return result; +} + +inline bool ThermostatSwitchIdValid(uint8_t switchId) +{ + return (switchId >= THERMOSTAT_INPUT_SWT1 && switchId <= THERMOSTAT_INPUT_SWT8); +} + +inline bool ThermostatRelayIdValid(uint8_t relayId) +{ + return (relayId >= THERMOSTAT_OUTPUT_REL1 && relayId <= THERMOSTAT_OUTPUT_REL8); +} + +uint8_t ThermostatInputStatus(uint8_t input_switch) +{ + bool ifId = ThermostatSwitchIdValid(input_switch); + uint8_t value = 0; + if(ifId) { + value = SwitchGetVirtual(ifId - THERMOSTAT_INPUT_SWT1); + } + return value; +} + +uint8_t ThermostatOutputStatus(uint8_t output_switch) +{ + return (uint8_t)bitRead(power, (output_switch - 1)); +} + +int16_t ThermostatCelsiusToFahrenheit(const int32_t deg, uint8_t conv_type) { + int32_t value; + value = (int32_t)(((int32_t)deg * (int32_t)90) / (int32_t)50); + if (conv_type == TEMP_CONV_ABSOLUTE) { + value += (int32_t)320; + } + + + if (value <= (int32_t)(INT16_MIN)) { + value = (int32_t)(INT16_MIN); + } + else if (value >= (int32_t)INT16_MAX) { + value = (int32_t)INT16_MAX; + } + + return (int16_t)value; +} + +int16_t ThermostatFahrenheitToCelsius(const int32_t deg, uint8_t conv_type) { + int16_t offset = 0; + int32_t value; + if (conv_type == TEMP_CONV_ABSOLUTE) { + offset = 320; + } + + value = (int32_t)(((deg - (int32_t)offset) * (int32_t)50) / (int32_t)90); + + + if (value <= (int32_t)(INT16_MIN)) { + value = (int32_t)(INT16_MIN); + } + else if (value >= (int32_t)INT16_MAX) { + value = (int32_t)INT16_MAX; + } + + return (int16_t)value; +} + +void ThermostatSignalPreProcessingSlow(uint8_t ctr_output) +{ + + if ((uptime - Thermostat[ctr_output].timestamp_temp_measured_update) > ((uint32_t)Thermostat[ctr_output].time_sens_lost * 60)) { + Thermostat[ctr_output].status.sensor_alive = IFACE_OFF; + Thermostat[ctr_output].temp_measured_gradient = 0; + Thermostat[ctr_output].temp_measured = 0; + } +} + +void ThermostatSignalPostProcessingSlow(uint8_t ctr_output) +{ + + if ((Thermostat[ctr_output].status.status_output != Thermostat[ctr_output].status.command_output) + &&(Thermostat[ctr_output].status.enable_output == IFACE_ON)) { + Thermostat[ctr_output].diag.output_inconsist_ctr++; + } + else { + Thermostat[ctr_output].diag.output_inconsist_ctr = 0; + } +} + +void ThermostatSignalProcessingFast(uint8_t ctr_output) +{ + + Thermostat[ctr_output].status.status_input = (uint32_t)ThermostatInputStatus(Thermostat[ctr_output].status.input_switch_number); + + if (Thermostat[ctr_output].status.status_input == IFACE_ON) { + Thermostat[ctr_output].timestamp_input_on = uptime; + } + + Thermostat[ctr_output].status.status_output = (uint32_t)ThermostatOutputStatus(Thermostat[ctr_output].status.output_relay_number); +} + +void ThermostatCtrState(uint8_t ctr_output) +{ +#ifdef USE_PI_AUTOTUNING + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); +#endif + + switch (Thermostat[ctr_output].status.controller_mode) { + + case CTR_HYBRID: + ThermostatHybridCtrPhase(ctr_output); + break; + + case CTR_PI: +#ifdef USE_PI_AUTOTUNING + + + + + if ((Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_ON) + &&(Thermostat[ctr_output].temp_measured == Thermostat[ctr_output].temp_target_level) + && ((flag_heating && (Thermostat[ctr_output].temp_measured_gradient < 0)) + ||(!flag_heating && (Thermostat[ctr_output].temp_measured_gradient > 0)))) + { + Thermostat[ctr_output].status.controller_mode = CTR_PI_AUTOTUNE; + ThermostatPeakDetectorInit(ctr_output); + } +#endif + break; + + case CTR_RAMP_UP: + break; +#ifdef USE_PI_AUTOTUNING + + case CTR_PI_AUTOTUNE: + + + if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF) + { + Thermostat[ctr_output].status.controller_mode = CTR_PI; + } + break; +#endif + } +} + +void ThermostatHybridCtrPhase(uint8_t ctr_output) +{ + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + if (Thermostat[ctr_output].status.controller_mode == CTR_HYBRID) { + switch (Thermostat[ctr_output].status.phase_hybrid_ctr) { + + case CTR_HYBRID_RAMP_UP: + + + if((Thermostat[ctr_output].time_ctr_checkpoint != 0) + && (uptime >= Thermostat[ctr_output].time_ctr_checkpoint)) { + + Thermostat[ctr_output].time_ctr_checkpoint = 0; + + Thermostat[ctr_output].time_ctr_changepoint = 0; + + Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI; + } + break; + + case CTR_HYBRID_PI: + + + + + if (((uptime - Thermostat[ctr_output].timestamp_output_off) > (60 * (uint32_t)Thermostat[ctr_output].time_allow_rampup)) + && (Thermostat[ctr_output].temp_target_level != Thermostat[ctr_output].temp_target_level_ctr) + && ( ( (Thermostat[ctr_output].temp_target_level - Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_rampup_delta_in) + && (flag_heating)) + || ( (Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_target_level > Thermostat[ctr_output].temp_rampup_delta_in) + && (!flag_heating)))) { + Thermostat[ctr_output].timestamp_rampup_start = uptime; + Thermostat[ctr_output].temp_rampup_start = Thermostat[ctr_output].temp_measured; + Thermostat[ctr_output].temp_rampup_meas_gradient = 0; + Thermostat[ctr_output].time_rampup_deadtime = 0; + Thermostat[ctr_output].counter_rampup_cycles = 1; + Thermostat[ctr_output].time_ctr_changepoint = 0; + Thermostat[ctr_output].time_ctr_checkpoint = 0; + Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_RAMP_UP; + } +#ifdef USE_PI_AUTOTUNING + + + + + if ((Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_ON) + &&(Thermostat[ctr_output].temp_measured == Thermostat[ctr_output].temp_target_level) + && ((flag_heating && (Thermostat[ctr_output].temp_measured_gradient < 0)) + ||(!flag_heating && (Thermostat[ctr_output].temp_measured_gradient > 0)))) + { + Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI_AUTOTUNE; + ThermostatPeakDetectorInit(ctr_output); + } +#endif + break; +#ifdef USE_PI_AUTOTUNING + + case CTR_HYBRID_PI_AUTOTUNE: + + + if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF) + { + Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI; + } + break; +#endif + } + } +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitchCtrState(ctr_output); +#endif +} + +bool ThermostatStateAutoToManual(uint8_t ctr_output) +{ + bool change_state = false; + + + + + if ((Thermostat[ctr_output].status.use_input == INPUT_USED) + &&((Thermostat[ctr_output].status.status_input == IFACE_ON) + || (Thermostat[ctr_output].status.sensor_alive == IFACE_OFF))) { + change_state = true; + } + return change_state; +} + +bool ThermostatStateManualToAuto(uint8_t ctr_output) +{ + bool change_state = false; + + + + + + if ((Thermostat[ctr_output].status.status_input == IFACE_OFF) + &&(Thermostat[ctr_output].status.sensor_alive == IFACE_ON) + && ((uptime - Thermostat[ctr_output].timestamp_input_on) > ((uint32_t)Thermostat[ctr_output].time_manual_to_auto * 60))) { + change_state = true; + } + return change_state; +} + +void ThermostatEmergencyShutdown(uint8_t ctr_output) +{ + + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF; + Thermostat[ctr_output].status.command_output = IFACE_OFF; + if (Thermostat[ctr_output].status.enable_output == IFACE_ON) { + ThermostatOutputRelay(ctr_output, Thermostat[ctr_output].status.command_output); + } +} + +void ThermostatState(uint8_t ctr_output) +{ + switch (Thermostat[ctr_output].status.thermostat_mode) { + + case THERMOSTAT_OFF: + + break; + + case THERMOSTAT_AUTOMATIC_OP: + if (ThermostatStateAutoToManual(ctr_output)) { + + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_MANUAL_OP; + } + ThermostatCtrState(ctr_output); + break; + + case THERMOSTAT_MANUAL_OP: + if (ThermostatStateManualToAuto(ctr_output)) { + + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; + } + break; + } +} + +void ThermostatOutputRelay(uint8_t ctr_output, uint32_t command) +{ + + + + if ((command == IFACE_ON) + && (Thermostat[ctr_output].status.status_output == IFACE_OFF)) { + + if (Thermostat[ctr_output].status.enable_output == IFACE_ON) { + ExecuteCommandPower(Thermostat[ctr_output].status.output_relay_number, POWER_ON, SRC_THERMOSTAT); + } + + Thermostat[ctr_output].status.status_output = IFACE_ON; +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitch(ctr_output); +#endif + } + + + + else if ((command == IFACE_OFF) && (Thermostat[ctr_output].status.status_output == IFACE_ON)) { + + if (Thermostat[ctr_output].status.enable_output == IFACE_ON) { + ExecuteCommandPower(Thermostat[ctr_output].status.output_relay_number, POWER_OFF, SRC_THERMOSTAT); + } + + Thermostat[ctr_output].timestamp_output_off = uptime; + Thermostat[ctr_output].status.status_output = IFACE_OFF; +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitch(ctr_output); +#endif + } +} + +void ThermostatCalculatePI(uint8_t ctr_output) +{ + + + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + int32_t aux_temp_error; + + + aux_temp_error = (int32_t)(Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_measured) * 10; + + + if (Thermostat[ctr_output].status.climate_mode == CLIMATE_COOLING) { + aux_temp_error *= -1; + } + + + if (aux_temp_error <= (int32_t)(INT16_MIN)) { + Thermostat[ctr_output].temp_pi_error = (int16_t)(INT16_MIN); + } + else if (aux_temp_error >= (int32_t)INT16_MAX) { + Thermostat[ctr_output].temp_pi_error = (int16_t)INT16_MAX; + } + else { + Thermostat[ctr_output].temp_pi_error = (int16_t)aux_temp_error; + } + + + Thermostat[ctr_output].kP_pi = 100 / (uint16_t)(Thermostat[ctr_output].val_prop_band); + + Thermostat[ctr_output].time_proportional_pi = ((int32_t)(Thermostat[ctr_output].temp_pi_error * (int16_t)Thermostat[ctr_output].kP_pi) * ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) / 10000; + + + + + + if ((Thermostat[ctr_output].time_proportional_pi < abs(((int32_t)Thermostat[ctr_output].time_min_action * 60))) + && (Thermostat[ctr_output].time_proportional_pi > 0)) { + Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_min_action * 60); + } + + if (Thermostat[ctr_output].time_proportional_pi < 0) { + Thermostat[ctr_output].time_proportional_pi = 0; + } + else if (Thermostat[ctr_output].time_proportional_pi > ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { + Thermostat[ctr_output].time_proportional_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); + } + + + Thermostat[ctr_output].kI_pi = (uint16_t)((((uint32_t)Thermostat[ctr_output].kP_pi * (uint32_t)Thermostat[ctr_output].time_pi_cycle * 6000)) / (uint32_t)Thermostat[ctr_output].time_reset); + + + + + if (abs((Thermostat[ctr_output].temp_pi_error) / 10) > Thermostat[ctr_output].temp_reset_anti_windup) { + Thermostat[ctr_output].time_integral_pi = 0; + Thermostat[ctr_output].temp_pi_accum_error = 0; + } + + + + else { +# 688 "/workspace/Tasmota/tasmota/xdrv_39_thermostat.ino" + aux_temp_error = (int32_t)Thermostat[ctr_output].temp_pi_accum_error + (int32_t)Thermostat[ctr_output].temp_pi_error; + + + if (aux_temp_error <= (int32_t)INT16_MIN) { + Thermostat[ctr_output].temp_pi_accum_error = INT16_MIN; + } + else if (aux_temp_error >= (int32_t)INT16_MAX) { + Thermostat[ctr_output].temp_pi_accum_error = INT16_MAX; + } + else { + Thermostat[ctr_output].temp_pi_accum_error = (int16_t)aux_temp_error; + } + + + + + if ( (Thermostat[ctr_output].temp_pi_error >= 0) + && (abs((Thermostat[ctr_output].temp_pi_error) / 10) <= (int16_t)Thermostat[ctr_output].temp_hysteresis) + && ( ((Thermostat[ctr_output].temp_measured_gradient > 0) + && (flag_heating)) + || ( (Thermostat[ctr_output].temp_measured_gradient < 0) + && (!flag_heating)))) { + + Thermostat[ctr_output].temp_pi_accum_error *= 0.8; + } + + + else if ((Thermostat[ctr_output].temp_pi_error < 0) + && ( ((Thermostat[ctr_output].temp_measured_gradient > 0) + && (flag_heating)) + || ( (Thermostat[ctr_output].temp_measured_gradient < 0) + && (!flag_heating)))) { + + Thermostat[ctr_output].temp_pi_accum_error *= 0.8; + } + + + if (Thermostat[ctr_output].temp_pi_accum_error < 0) { + Thermostat[ctr_output].temp_pi_accum_error = 0; + } + + + Thermostat[ctr_output].time_integral_pi = (((int32_t)Thermostat[ctr_output].temp_pi_accum_error * (int32_t)Thermostat[ctr_output].kI_pi) * (int32_t)((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60)) / 1000000; + + + + + if (Thermostat[ctr_output].time_integral_pi > ((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { + Thermostat[ctr_output].time_integral_pi = ((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60); + } + } + + + Thermostat[ctr_output].time_total_pi = Thermostat[ctr_output].time_proportional_pi + Thermostat[ctr_output].time_integral_pi; + + + + + if (Thermostat[ctr_output].time_total_pi >= ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { + + Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); + } + else if (Thermostat[ctr_output].time_total_pi < 0) { + Thermostat[ctr_output].time_total_pi = 0; + } + + + + if (Thermostat[ctr_output].temp_pi_error <= 0) { + + if ((abs((Thermostat[ctr_output].temp_pi_error) / 10) > Thermostat[ctr_output].temp_hysteresis) + || ( ((Thermostat[ctr_output].temp_measured_gradient >= 0) + && (flag_heating)) + || ( (Thermostat[ctr_output].temp_measured_gradient <= 0) + && (!flag_heating)))){ + Thermostat[ctr_output].time_total_pi = 0; + } + } + + + + + else if ((Thermostat[ctr_output].temp_pi_error > 0) + && (abs((Thermostat[ctr_output].temp_pi_error) / 10) <= Thermostat[ctr_output].temp_hysteresis) + && (((Thermostat[ctr_output].temp_measured_gradient > 0) + && (flag_heating)) + || ( (Thermostat[ctr_output].temp_measured_gradient < 0) + && (!flag_heating)))) { + Thermostat[ctr_output].time_total_pi = 0; + } + + + + if ((Thermostat[ctr_output].time_total_pi <= abs(((uint32_t)Thermostat[ctr_output].time_min_action * 60))) + && (Thermostat[ctr_output].time_total_pi != 0)) { + Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_min_action * 60); + } + + + else if (Thermostat[ctr_output].time_total_pi > abs(((int32_t)Thermostat[ctr_output].time_max_action * 60))) { + Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_max_action * 60); + } + + else if (Thermostat[ctr_output].time_total_pi > (((int32_t)Thermostat[ctr_output].time_pi_cycle * 60) - ((int32_t)Thermostat[ctr_output].time_min_turnoff_action * 60))) { + Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); + } + + + Thermostat[ctr_output].time_ctr_changepoint = uptime + (uint32_t)Thermostat[ctr_output].time_total_pi; + + Thermostat[ctr_output].time_ctr_checkpoint = uptime + ((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60); +} + +void ThermostatWorkAutomaticPI(uint8_t ctr_output) +{ + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + if ( (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) + || (Thermostat[ctr_output].temp_target_level != Thermostat[ctr_output].temp_target_level_ctr) + || ( (( (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_target_level) + && (Thermostat[ctr_output].temp_measured_gradient < 0) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_target_level) + && (Thermostat[ctr_output].temp_measured_gradient > 0) + && (!flag_heating))) + && (Thermostat[ctr_output].status.status_cycle_active == CYCLE_OFF))) { + Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level; + ThermostatCalculatePI(ctr_output); + + Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF; + } + if (uptime < Thermostat[ctr_output].time_ctr_changepoint) { + Thermostat[ctr_output].status.status_cycle_active = CYCLE_ON; + Thermostat[ctr_output].status.command_output = IFACE_ON; + } + else { + Thermostat[ctr_output].status.command_output = IFACE_OFF; + } +} + +void ThermostatWorkAutomaticRampUp(uint8_t ctr_output) +{ + uint32_t time_in_rampup; + int16_t aux_temp_delta; + int16_t temp_delta_rampup; + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + + + + if ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_rampup_start) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_rampup_start) + && (!flag_heating))) + { + Thermostat[ctr_output].temp_rampup_start = Thermostat[ctr_output].temp_measured; + } + + + time_in_rampup = uptime - Thermostat[ctr_output].timestamp_rampup_start; + temp_delta_rampup = Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_rampup_start; + + Thermostat[ctr_output].status.command_output = IFACE_ON; + + Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level; + + + + if ((time_in_rampup <= (60 * (uint32_t)Thermostat[ctr_output].time_rampup_max)) + && ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_target_level) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_target_level) + && (!flag_heating)))){ + + + + if ( (abs(temp_delta_rampup) >= Thermostat[ctr_output].temp_rampup_delta_out) + && (Thermostat[ctr_output].time_rampup_deadtime == 0)) { + + + int32_t time_aux; + time_aux = ((time_in_rampup / 2) - Thermostat[ctr_output].time_output_delay); + if (time_aux >= Thermostat[ctr_output].time_output_delay) { + Thermostat[ctr_output].time_rampup_deadtime = (uint32_t)time_aux; + } + else { + Thermostat[ctr_output].time_rampup_deadtime = Thermostat[ctr_output].time_output_delay; + } + + Thermostat[ctr_output].temp_rampup_meas_gradient = (int32_t)((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_in_rampup); + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); + + Thermostat[ctr_output].temp_rampup_cycle = Thermostat[ctr_output].temp_measured; + Thermostat[ctr_output].time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat[ctr_output].time_rampup_max); + Thermostat[ctr_output].temp_rampup_output_off = Thermostat[ctr_output].temp_target_level_ctr; + } + + else if ((Thermostat[ctr_output].time_rampup_deadtime > 0) && (uptime >= Thermostat[ctr_output].time_rampup_nextcycle)) { + + + temp_delta_rampup = Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_rampup_cycle; + uint32_t time_total_rampup = (uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60 * Thermostat[ctr_output].counter_rampup_cycles; + + Thermostat[ctr_output].temp_rampup_meas_gradient = int32_t((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_total_rampup); + if ( ((Thermostat[ctr_output].temp_rampup_meas_gradient > 0) + && ((flag_heating))) + || ((Thermostat[ctr_output].temp_rampup_meas_gradient < 0) + && ((!flag_heating)))) { + + + + + aux_temp_delta =Thermostat[ctr_output].temp_target_level_ctr - Thermostat[ctr_output].temp_rampup_cycle; + Thermostat[ctr_output].time_ctr_changepoint = (uint32_t)(uint32_t)(((uint32_t)(aux_temp_delta) * (uint32_t)(time_total_rampup)) / (uint32_t)temp_delta_rampup) + (uint32_t)Thermostat[ctr_output].time_rampup_nextcycle - (uint32_t)time_total_rampup - (uint32_t)Thermostat[ctr_output].time_rampup_deadtime; + + + + Thermostat[ctr_output].temp_rampup_output_off = (int16_t)(((int32_t)temp_delta_rampup * (int32_t)(Thermostat[ctr_output].time_ctr_changepoint - (uptime - (time_total_rampup)))) / (int32_t)(time_total_rampup * Thermostat[ctr_output].counter_rampup_cycles)) + Thermostat[ctr_output].temp_rampup_cycle; + + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); + Thermostat[ctr_output].temp_rampup_cycle = Thermostat[ctr_output].temp_measured; + + Thermostat[ctr_output].counter_rampup_cycles = 1; + } + else { + + Thermostat[ctr_output].counter_rampup_cycles++; + + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); + + Thermostat[ctr_output].time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat[ctr_output].time_rampup_max) - time_in_rampup; + Thermostat[ctr_output].temp_rampup_output_off = Thermostat[ctr_output].temp_target_level_ctr; + } + + Thermostat[ctr_output].time_ctr_checkpoint = Thermostat[ctr_output].time_ctr_changepoint + Thermostat[ctr_output].time_rampup_deadtime; + } + + + + + + + if ((Thermostat[ctr_output].time_rampup_deadtime == 0) + || (Thermostat[ctr_output].time_ctr_checkpoint == 0) + || (uptime < Thermostat[ctr_output].time_ctr_changepoint) + || ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_rampup_output_off) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_rampup_output_off) + && (!flag_heating))) + || ( ((Thermostat[ctr_output].temp_rampup_meas_gradient <= 0) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_rampup_meas_gradient >= 0) + && (!flag_heating)))) { + Thermostat[ctr_output].status.command_output = IFACE_ON; + } + else { + Thermostat[ctr_output].status.command_output = IFACE_OFF; + } + } + else { + + if ( ((Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_target_level_ctr) + && (flag_heating)) + || ((Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_target_level_ctr) + && (!flag_heating))) { + Thermostat[ctr_output].temp_pi_accum_error = Thermostat[ctr_output].temp_rampup_pi_acc_error; + } + + Thermostat[ctr_output].time_ctr_checkpoint = uptime; + + Thermostat[ctr_output].status.command_output = IFACE_OFF; + } +} + +#ifdef USE_PI_AUTOTUNING + +void ThermostatPeakDetectorInit(uint8_t ctr_output) +{ + for (uint8_t i = 0; i < THERMOSTAT_PEAKNUMBER_AUTOTUNE; i++) { + Thermostat[ctr_output].temp_peaks_atune[i] = 0; + } + Thermostat[ctr_output].pU_pi_atune = 0; + Thermostat[ctr_output].kP_pi_atune = 0; + Thermostat[ctr_output].kI_pi_atune = 0; + Thermostat[ctr_output].kU_pi_atune = 0; + Thermostat[ctr_output].peak_ctr = 0; + Thermostat[ctr_output].temp_abs_max_atune = 0; + Thermostat[ctr_output].temp_abs_min_atune = 100; + Thermostat[ctr_output].time_ctr_checkpoint = uptime + THERMOSTAT_TIME_MAX_AUTOTUNE; +} + +void ThermostatPeakDetector(uint8_t ctr_output) +{ + uint8_t peak_num = Thermostat[ctr_output].peak_ctr; + int16_t peak_avg = 0; + bool peak_transition = false; + + if (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_abs_max_atune) { + Thermostat[ctr_output].temp_abs_max_atune = Thermostat[ctr_output].temp_measured; + } + if (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_abs_min_atune) { + Thermostat[ctr_output].temp_abs_min_atune = Thermostat[ctr_output].temp_measured; + } + + + if (peak_num < THERMOSTAT_PEAKNUMBER_AUTOTUNE) { + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + bool cond_peak_1 = ( (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_peaks_atune[peak_num]) + && (flag_heating) + || (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_peaks_atune[peak_num]) + && (!flag_heating)); + bool cond_peak_2 = ( (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_peaks_atune[peak_num]) + && (flag_heating) + || (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_peaks_atune[peak_num]) + && (!flag_heating)); + bool cond_gradient_1 = ( (Thermostat[ctr_output].temp_measured_gradient > 0) + && (flag_heating) + || (Thermostat[ctr_output].temp_measured_gradient < 0) + && (!flag_heating)); + bool cond_gradient_2 = ( (Thermostat[ctr_output].temp_measured_gradient < 0) + && (flag_heating) + || (Thermostat[ctr_output].temp_measured_gradient > 0) + && (!flag_heating)); + + if ((peak_num % 2) == 0) { + + + + if (cond_peak_1 && cond_gradient_1) { + Thermostat[ctr_output].temp_peaks_atune[peak_num] = Thermostat[ctr_output].temp_measured; + } + + + + if ( (cond_peak_2) + && (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) { + + Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60); + Thermostat[ctr_output].peak_ctr++; + peak_transition = true; + } + } + + else { + + + + if (cond_peak_2 && cond_gradient_2) { + Thermostat[ctr_output].temp_peaks_atune[peak_num] = Thermostat[ctr_output].temp_measured; + } + + + + if ( (cond_peak_1) + && (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) { + + + Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60); + Thermostat[ctr_output].peak_ctr++; + peak_transition = true; + } + } + } + else { + + ThermostatAutotuneParamCalc(ctr_output); + Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF; + } + + + if ((Thermostat[ctr_output].peak_ctr > 2) && (peak_transition)) { + + peak_num = Thermostat[ctr_output].peak_ctr; + + peak_avg = (abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1] + - Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]) + + abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 2] + - Thermostat[ctr_output].temp_peaks_atune[peak_num - 3])) / 2; + + if ((20 * (int32_t)peak_avg) < (int32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune)) { + + for (uint8_t i = 0; i < peak_num; i++) { + peak_avg += Thermostat[ctr_output].temp_peaks_atune[i]; + } + peak_avg /= peak_num; + + if (10 * abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1] - Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]) < (Thermostat[ctr_output].temp_abs_max_atune - peak_avg)) { + + ThermostatAutotuneParamCalc(ctr_output); + Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF; + } + } + } + peak_transition = false; +} + +void ThermostatAutotuneParamCalc(uint8_t ctr_output) +{ + uint8_t peak_num = Thermostat[ctr_output].peak_ctr; + + + + Thermostat[ctr_output].kU_pi_atune = (uint16_t)(100 * ((uint32_t)400000 * (uint32_t)(Thermostat[ctr_output].dutycycle_step_autotune)) / ((uint32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune) * (uint32_t)314159)); + Thermostat[ctr_output].pU_pi_atune = (Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 1] - Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 2]); + + switch (Thermostat[ctr_output].status.autotune_perf_mode) { + case AUTOTUNE_PERF_FAST: + + Thermostat[ctr_output].kP_pi_atune = (4 * Thermostat[ctr_output].kU_pi_atune) / 10; + break; + case AUTOTUNE_PERF_NORMAL: + + Thermostat[ctr_output].kP_pi_atune = (18 * Thermostat[ctr_output].kU_pi_atune) / 100; + break; + case AUTOTUNE_PERF_SLOW: + + Thermostat[ctr_output].kP_pi_atune = (13 * Thermostat[ctr_output].kU_pi_atune) / 100; + break; + } + + + Thermostat[ctr_output].kI_pi_atune = (12 * (6000 * Thermostat[ctr_output].kU_pi_atune / Thermostat[ctr_output].pU_pi_atune)) / 10; + + + Thermostat[ctr_output].val_prop_band_atune = 100 / Thermostat[ctr_output].kP_pi_atune; + + Thermostat[ctr_output].time_reset_atune = (uint32_t)((((uint32_t)Thermostat[ctr_output].kP_pi_atune * (uint32_t)Thermostat[ctr_output].time_pi_cycle * 6000)) / (uint32_t)Thermostat[ctr_output].kI_pi_atune); +} + +void ThermostatWorkAutomaticPIAutotune(uint8_t ctr_output) +{ + bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING); + + + if ((uptime < Thermostat[ctr_output].time_ctr_checkpoint) + &&(Thermostat[ctr_output].temp_target_level_ctr == Thermostat[ctr_output].temp_target_level)) { + if (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) { + Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level; + + Thermostat[ctr_output].time_ctr_changepoint = uptime + (((uint32_t)Thermostat[ctr_output].time_pi_cycle * (uint32_t)Thermostat[ctr_output].dutycycle_step_autotune) / (uint32_t)100); + + Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF; + } + + if (uptime < Thermostat[ctr_output].time_ctr_changepoint) { + Thermostat[ctr_output].status.status_cycle_active = CYCLE_ON; + Thermostat[ctr_output].status.command_output = IFACE_ON; + } + else { + Thermostat[ctr_output].status.command_output = IFACE_OFF; + } + + ThermostatPeakDetector(ctr_output); + } + else { + + Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF; + } + + if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF) { + + Thermostat[ctr_output].status.command_output = IFACE_OFF; + } +} +#endif + +void ThermostatCtrWork(uint8_t ctr_output) +{ + switch (Thermostat[ctr_output].status.controller_mode) { + + case CTR_HYBRID: + switch (Thermostat[ctr_output].status.phase_hybrid_ctr) { + case CTR_HYBRID_RAMP_UP: + ThermostatWorkAutomaticRampUp(ctr_output); + break; + case CTR_HYBRID_PI: + ThermostatWorkAutomaticPI(ctr_output); + break; +#ifdef USE_PI_AUTOTUNING + + case CTR_HYBRID_PI_AUTOTUNE: + ThermostatWorkAutomaticPIAutotune(ctr_output); + break; +#endif + } + break; + + case CTR_PI: + ThermostatWorkAutomaticPI(ctr_output); + break; + + case CTR_RAMP_UP: + ThermostatWorkAutomaticRampUp(ctr_output); + break; +#ifdef USE_PI_AUTOTUNING + + case CTR_PI_AUTOTUNE: + ThermostatWorkAutomaticPIAutotune(ctr_output); + break; +#endif + } +} + +void ThermostatWork(uint8_t ctr_output) +{ + switch (Thermostat[ctr_output].status.thermostat_mode) { + + case THERMOSTAT_OFF: + Thermostat[ctr_output].status.command_output = IFACE_OFF; + break; + + case THERMOSTAT_AUTOMATIC_OP: + ThermostatCtrWork(ctr_output); + + break; + + case THERMOSTAT_MANUAL_OP: + Thermostat[ctr_output].time_ctr_checkpoint = 0; + Thermostat[ctr_output].status.command_output = Thermostat[ctr_output].status.status_input; + break; + } + ThermostatOutputRelay(ctr_output, Thermostat[ctr_output].status.command_output); +} + +void ThermostatDiagnostics(uint8_t ctr_output) +{ + + if ((Thermostat[ctr_output].diag.diagnostic_mode == DIAGNOSTIC_ON) + &&(Thermostat[ctr_output].diag.output_inconsist_ctr >= THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST)) { + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF; + Thermostat[ctr_output].diag.state_emergency = EMERGENCY_ON; + } + + + + + + if (Thermostat[ctr_output].diag.state_emergency == EMERGENCY_ON) { + ThermostatEmergencyShutdown(ctr_output); + } +} + +void ThermostatController(uint8_t ctr_output) +{ + ThermostatState(ctr_output); + ThermostatWork(ctr_output); +} + +bool ThermostatTimerArm(uint8_t ctr_output, int16_t tempVal) +{ + bool result = false; + + if ((tempVal >= -1000) + && (tempVal <= 1000) + && (tempVal >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) { + Thermostat[ctr_output].temp_target_level = tempVal; + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; + result = true; + } + + return result; +} + +void ThermostatTimerDisarm(uint8_t ctr_output) +{ + Thermostat[ctr_output].temp_target_level = THERMOSTAT_TEMP_INIT; + Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF; +} + +#ifdef DEBUG_THERMOSTAT +void ThermostatVirtualSwitch(uint8_t ctr_output) +{ + char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + if (ctr_output < DOMOTICZ_MAX_IDX) { + Response_P(DOMOTICZ_MES, Domoticz_Virtual_Switches[ctr_output], (0 == Thermostat[ctr_output].status.command_output) ? 0 : 1, ""); + MqttPublish(domoticz_in_topic); + } +} + +void ThermostatVirtualSwitchCtrState(uint8_t ctr_output) +{ + char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + Response_P(DOMOTICZ_MES, DOMOTICZ_IDX2, (0 == Thermostat[0].status.phase_hybrid_ctr) ? 0 : 1, ""); + MqttPublish(domoticz_in_topic); +} + +void ThermostatDebug(uint8_t ctr_output) +{ + char result_chr[FLOATSZ]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat Start ------")); + dtostrfd(Thermostat[ctr_output].status.counter_seconds, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.counter_seconds: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.thermostat_mode, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.thermostat_mode: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].diag.state_emergency, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.state_emergency: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].diag.output_inconsist_ctr, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.output_inconsist_ctr: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.controller_mode, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.controller_mode: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.command_output, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.command_output: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.status_output, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_output: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.status_input, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_input: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.phase_hybrid_ctr, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.phase_hybrid_ctr: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.sensor_alive, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.sensor_alive: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].status.status_cycle_active, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_cycle_active: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].temp_pi_error, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_error: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].temp_pi_accum_error, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_accum_error: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_proportional_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_proportional_pi: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_integral_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_integral_pi: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_total_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_total_pi: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].temp_measured_gradient, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_measured_gradient: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_rampup_deadtime, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_rampup_deadtime: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].temp_rampup_meas_gradient, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_meas_gradient: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_ctr_changepoint, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_changepoint: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].temp_rampup_output_off, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_output_off: %s"), result_chr); + dtostrfd(Thermostat[ctr_output].time_ctr_checkpoint, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_checkpoint: %s"), result_chr); + dtostrfd(uptime, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("uptime: %s"), result_chr); + dtostrfd(power, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("power: %s"), result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------")); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); +} +#endif + +void ThermostatGetLocalSensor(uint8_t ctr_output) { + String buf = mqtt_data; + JsonParser parser((char*)buf.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { + JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")]; + if (value_token.isNum()) { + int16_t value = value_token.getFloat() * 10; + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatFahrenheitToCelsius(value, TEMP_CONV_ABSOLUTE); + } + if ( (value >= -1000) + && (value <= 1000) + && (Thermostat[ctr_output].status.sensor_type == SENSOR_LOCAL)) { + uint32_t timestamp = uptime; + + if (value != Thermostat[ctr_output].temp_measured) { + int32_t temp_delta = (value - Thermostat[ctr_output].temp_measured); + uint32_t time_delta = (timestamp - Thermostat[ctr_output].timestamp_temp_meas_change_update); + Thermostat[ctr_output].temp_measured_gradient = (int32_t)((360000 * temp_delta) / ((int32_t)time_delta)); + Thermostat[ctr_output].temp_measured = value; + Thermostat[ctr_output].timestamp_temp_meas_change_update = timestamp; + } + Thermostat[ctr_output].timestamp_temp_measured_update = timestamp; + Thermostat[ctr_output].status.sensor_alive = IFACE_ON; + } + } + } +} + + + + + +void CmndThermostatModeSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); + if ((value >= THERMOSTAT_OFF) && (value < THERMOSTAT_MODES_MAX)) { + Thermostat[ctr_output].status.thermostat_mode = value; + Thermostat[ctr_output].timestamp_input_on = 0; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.thermostat_mode); + } +} + +void CmndClimateModeSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); + if ((value >= CLIMATE_HEATING) && (value < CLIMATE_MODES_MAX)) { + Thermostat[ctr_output].status.climate_mode = value; + + Thermostat[ctr_output].time_ctr_checkpoint = uptime; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.climate_mode); + } +} + +void CmndTempFrostProtectSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + int16_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (int16_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_ABSOLUTE); + } + else { + value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= 0) + && (value <= 127)) { + Thermostat[ctr_output].temp_frost_protect = (uint8_t)value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_frost_protect, TEMP_CONV_ABSOLUTE); + } + else { + value = (int16_t)Thermostat[ctr_output].temp_frost_protect; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndControllerModeSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= CTR_HYBRID) && (value < CTR_MODES_MAX)) { + Thermostat[ctr_output].status.controller_mode = value; + + Thermostat[ctr_output].timestamp_rampup_start = uptime; + Thermostat[ctr_output].temp_rampup_start = Thermostat[ctr_output].temp_measured; + Thermostat[ctr_output].temp_rampup_meas_gradient = 0; + Thermostat[ctr_output].time_rampup_deadtime = 0; + Thermostat[ctr_output].counter_rampup_cycles = 1; + Thermostat[ctr_output].time_ctr_changepoint = 0; + Thermostat[ctr_output].time_ctr_checkpoint = 0; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.controller_mode); + } +} + +void CmndInputSwitchSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if (ThermostatSwitchIdValid(value)) { + Thermostat[ctr_output].status.input_switch_number = value; + Thermostat[ctr_output].timestamp_input_on = uptime; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.input_switch_number); + } +} + +void CmndInputSwitchUse(void) +{ + if ((XdrvMailbox.index >= INPUT_NOT_USED) && (XdrvMailbox.index <= INPUT_USED)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + Thermostat[ctr_output].status.use_input = (uint32_t)(XdrvMailbox.payload); + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.use_input); + } +} + +void CmndSensorInputSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= SENSOR_MQTT) && (value < SENSOR_MAX)) { + Thermostat[ctr_output].status.sensor_type = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.sensor_type); + } +} + +void CmndOutputRelaySet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if (ThermostatRelayIdValid(value)) { + Thermostat[ctr_output].status.output_relay_number = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.output_relay_number); + } +} + +void CmndTimeAllowRampupSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value < 1440)) { + Thermostat[ctr_output].time_allow_rampup = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_allow_rampup)); + } +} + +void CmndTempFormatSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= TEMP_FAHRENHEIT)) { + Thermostat[ctr_output].status.temp_format = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.temp_format); + } +} + +void CmndTempMeasuredSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + int16_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_ABSOLUTE); + } + else { + value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= -1000) + && (value <= 1000) + && (Thermostat[ctr_output].status.sensor_type == SENSOR_MQTT)) { + uint32_t timestamp = uptime; + + if (value != Thermostat[ctr_output].temp_measured) { + int32_t temp_delta = (value - Thermostat[ctr_output].temp_measured); + uint32_t time_delta = (timestamp - Thermostat[ctr_output].timestamp_temp_meas_change_update); + Thermostat[ctr_output].temp_measured_gradient = (int32_t)((360000 * temp_delta) / ((int32_t)time_delta)); + Thermostat[ctr_output].temp_measured = value; + Thermostat[ctr_output].timestamp_temp_meas_change_update = timestamp; + } + Thermostat[ctr_output].timestamp_temp_measured_update = timestamp; + Thermostat[ctr_output].status.sensor_alive = IFACE_ON; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_measured, TEMP_CONV_ABSOLUTE); + } + else { + value = Thermostat[ctr_output].temp_measured; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndTempTargetSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + int16_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_ABSOLUTE); + } + else { + value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= -1000) + && (value <= 1000) + && (value >= (int16_t)Thermostat[ctr_output].temp_frost_protect)) { + Thermostat[ctr_output].temp_target_level = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_target_level, TEMP_CONV_ABSOLUTE); + } + else { + value = Thermostat[ctr_output].temp_target_level; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndTempMeasuredGrdRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + int16_t value; + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_measured_gradient, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_measured_gradient; + } + ResponseCmndFloat(((float)value) / 1000, 1); + } +} + +void CmndStateEmergencySet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1)) { + Thermostat[ctr_output].diag.state_emergency = (uint16_t)value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].diag.state_emergency); + } +} + +void CmndTimeManualToAutoSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_manual_to_auto = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_manual_to_auto)); + } +} + +void CmndPropBandSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 20)) { + Thermostat[ctr_output].val_prop_band = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].val_prop_band); + } +} + +void CmndTimeResetSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat[ctr_output].time_reset = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].time_reset); + } +} + +void CmndTimePiProportRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + ResponseCmndNumber((int)Thermostat[ctr_output].time_proportional_pi); + } +} + +void CmndTimePiIntegrRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + ResponseCmndNumber((int)Thermostat[ctr_output].time_integral_pi); + } +} + +void CmndTimePiCycleSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_pi_cycle = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_pi_cycle)); + } +} + +void CmndTempAntiWindupResetSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + uint8_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (uint8_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_RELATIVE); + } + else { + value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= 0) + && (value <= 100)) { + Thermostat[ctr_output].temp_reset_anti_windup = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_reset_anti_windup, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_reset_anti_windup; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndTempHystSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + int8_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (int8_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_RELATIVE); + } + else { + value = (int8_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= -100) + && (value <= 100)) { + Thermostat[ctr_output].temp_hysteresis = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_hysteresis, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_hysteresis; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +#ifdef USE_PI_AUTOTUNING +void CmndPerfLevelAutotune(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= AUTOTUNE_PERF_MAX)) { + Thermostat[ctr_output].status.autotune_perf_mode = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.autotune_perf_mode); + } +} +#endif + +void CmndTimeMaxActionSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_max_action = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_max_action)); + } +} + +void CmndTimeMinActionSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_min_action = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_action)); + } +} + +void CmndTimeSensLostSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_sens_lost = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_sens_lost)); + } +} + +void CmndTimeMinTurnoffActionSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_min_turnoff_action = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_turnoff_action)); + } +} + +void CmndTempRupDeltInSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + uint8_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (uint8_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_RELATIVE); + } + else { + value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= 0) + && (value <= 100)) { + Thermostat[ctr_output].temp_rampup_delta_in = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_rampup_delta_in, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_rampup_delta_in; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndTempRupDeltOutSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + uint8_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (uint8_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 10), TEMP_CONV_RELATIVE); + } + else { + value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + } + if ( (value >= 0) + && (value <= 100)) { + Thermostat[ctr_output].temp_rampup_delta_out = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_rampup_delta_out, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_rampup_delta_out; + } + ResponseCmndFloat((float)value / 10, 1); + } +} + +void CmndTimeRampupMaxSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_rampup_max = (uint16_t)value; + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_rampup_max)); + } +} + +void CmndTimeRampupCycleSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_rampup_cycle = (uint16_t)value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].time_rampup_cycle); + } +} + +void CmndTempRampupPiAccErrSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + uint16_t value; + if (XdrvMailbox.data_len > 0) { + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = (uint16_t)ThermostatFahrenheitToCelsius((int32_t)(CharToFloat(XdrvMailbox.data) * 100), TEMP_CONV_RELATIVE); + } + else { + value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 100); + } + if ( (value >= 0) + && (value <= 2500)) { + Thermostat[ctr_output].temp_rampup_pi_acc_error = value; + } + } + if (Thermostat[ctr_output].status.temp_format == TEMP_FAHRENHEIT) { + value = ThermostatCelsiusToFahrenheit((int32_t)Thermostat[ctr_output].temp_rampup_pi_acc_error, TEMP_CONV_RELATIVE); + } + else { + value = Thermostat[ctr_output].temp_rampup_pi_acc_error; + } + ResponseCmndFloat((float)value / 100, 1); + } +} + +void CmndDiagnosticModeSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); + if ((value >= DIAGNOSTIC_OFF) && (value <= DIAGNOSTIC_ON)) { + Thermostat[ctr_output].diag.diagnostic_mode = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].diag.diagnostic_mode); + } +} + +void CmndCtrDutyCycleRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + uint8_t value = 0; + if ( (Thermostat[ctr_output].status.controller_mode == CTR_PI) + || ((Thermostat[ctr_output].status.controller_mode == CTR_HYBRID) + &&(Thermostat[ctr_output].status.phase_hybrid_ctr == CTR_HYBRID_PI))) { + value = Thermostat[ctr_output].time_total_pi / Thermostat[ctr_output].time_pi_cycle; + } + else if ( (Thermostat[ctr_output].status.controller_mode == CTR_RAMP_UP) + || ((Thermostat[ctr_output].status.controller_mode == CTR_HYBRID) + &&(Thermostat[ctr_output].status.phase_hybrid_ctr == CTR_HYBRID_RAMP_UP))) { + if (Thermostat[ctr_output].status.status_output == IFACE_ON) { + value = 100; + } + else { + value = 0; + } + } + ResponseCmndNumber((int)value); + } +} + +void CmndEnableOutputSet(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); + if ((value >= IFACE_OFF) && (value <= IFACE_ON)) { + Thermostat[ctr_output].status.enable_output = value; + } + } + ResponseCmndNumber((int)Thermostat[ctr_output].status.enable_output); + } +} + + + + + +bool Xdrv39(uint8_t function) +{ + bool result = false; + uint8_t ctr_output; + + switch (function) { + case FUNC_INIT: + for (ctr_output = 0; ctr_output < THERMOSTAT_CONTROLLER_OUTPUTS; ctr_output++) { + ThermostatInit(ctr_output); + } + break; + case FUNC_LOOP: + for (ctr_output = 0; ctr_output < THERMOSTAT_CONTROLLER_OUTPUTS; ctr_output++) { + if (Thermostat[ctr_output].status.thermostat_mode != THERMOSTAT_OFF) { + ThermostatSignalProcessingFast(ctr_output); + ThermostatDiagnostics(ctr_output); + } + } + break; + case FUNC_SERIAL: + break; + case FUNC_EVERY_SECOND: + for (ctr_output = 0; ctr_output < THERMOSTAT_CONTROLLER_OUTPUTS; ctr_output++) { + if ((ThermostatMinuteCounter(ctr_output)) + && (Thermostat[ctr_output].status.thermostat_mode != THERMOSTAT_OFF)) { + ThermostatSignalPreProcessingSlow(ctr_output); + ThermostatController(ctr_output); + ThermostatSignalPostProcessingSlow(ctr_output); +#ifdef DEBUG_THERMOSTAT + ThermostatDebug(ctr_output); +#endif + } + } + break; + case FUNC_SHOW_SENSOR: + for (ctr_output = 0; ctr_output < THERMOSTAT_CONTROLLER_OUTPUTS; ctr_output++) { + if (Thermostat[ctr_output].status.thermostat_mode != THERMOSTAT_OFF) { + ThermostatGetLocalSensor(ctr_output); + } + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kThermostatCommands, ThermostatCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_40_telegram.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_40_telegram.ino" +#ifdef USE_TELEGRAM +# 41 "/workspace/Tasmota/tasmota/xdrv_40_telegram.ino" +#define XDRV_40 40 + +#ifndef TELEGRAM_LOOP_WAIT +#define TELEGRAM_LOOP_WAIT 10 +#endif + +#define TELEGRAM_SEND_RETRY 4 +#define TELEGRAM_MAX_MESSAGES 2 + +#ifdef USE_MQTT_TLS_CA_CERT + static const uint32_t tls_rx_size = 2048; + static const uint32_t tls_tx_size = 1024; +#else + static const uint32_t tls_rx_size = 1024; + static const uint32_t tls_tx_size = 1024; +#endif + +#include "WiFiClientSecureLightBearSSL.h" +BearSSL::WiFiClientSecure_light *telegramClient = nullptr; + +static const uint8_t Telegram_Fingerprint[] PROGMEM = USE_TELEGRAM_FINGERPRINT; + +typedef struct { + String text; + + + + uint32_t update_id = 0; + uint32_t chat_id = 0; +} TelegramMessage; + +struct { + TelegramMessage message[TELEGRAM_MAX_MESSAGES]; + uint32_t next_update_id = 0; + uint8_t message_count = 0; + uint8_t state = 0; + uint8_t index = 0; + uint8_t retry = 0; + uint8_t poll = TELEGRAM_LOOP_WAIT; + uint8_t wait = 0; + bool send_enable = false; + bool recv_enable = false; + bool echo_enable = false; + bool recv_busy = false; + bool skip = true; +} Telegram; + +bool TelegramInit(void) { + bool init_done = false; + if (strlen(SettingsText(SET_TELEGRAM_TOKEN))) { + if (!telegramClient) { + telegramClient = new BearSSL::WiFiClientSecure_light(tls_rx_size, tls_tx_size); +#ifdef USE_MQTT_TLS_CA_CERT + telegramClient->setTrustAnchor(&GoDaddyCAG2_TA, 1); +#else + telegramClient->setPubKeyFingerprint(Telegram_Fingerprint, Telegram_Fingerprint, false); +#endif + Telegram.message_count = 0; + Telegram.next_update_id = 0; + Telegram.message[0].text = ""; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TGM: Started")); + } + init_done = true; + } + return init_done; +} + +String TelegramConnectToTelegram(String command) { + + + if (!TelegramInit()) { return ""; } + + String response = ""; + uint32_t tls_connect_time = millis(); + if (telegramClient->connect("api.telegram.org", 443)) { + + + + telegramClient->println("GET /"+command); + + char c; + int ch_count=0; + uint32_t now = millis(); + bool avail = false; + while (millis() -now < 1500) { + while (telegramClient->available()) { + char c = telegramClient->read(); + if (ch_count < 700) { + response = response + c; + ch_count++; + } + avail = true; + } + if (avail) { + break; + } + } + + telegramClient->stop(); + } + + return response; +} + +void TelegramGetUpdates(uint32_t offset) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getUpdates")); + + if (!TelegramInit()) { return; } + + String _token = SettingsText(SET_TELEGRAM_TOKEN); + String command = "bot" + _token + "/getUpdates?offset=" + String(offset); + String response = TelegramConnectToTelegram(command); +# 207 "/workspace/Tasmota/tasmota/xdrv_40_telegram.ino" + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str()); + + JsonParser parser((char*)response.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { + + + + JsonParserArray arr = root[PSTR("result")]; + uint32_t max_updates = arr.size(); + + if (max_updates > 1) { max_updates = 1; } + Telegram.message_count = 0; + if (max_updates) { + for (uint32_t i = 0; i < max_updates; i++) { + Telegram.message[i].text = ""; + JsonParserObject result = arr[i].getObject(); + if (result) { + + + + + + + Telegram.message_count++; + Telegram.message[i].update_id = result["update_id"].getUInt(); + + + + Telegram.message[i].chat_id = result["message"].getObject()["chat"].getObject()["id"].getUInt(); + Telegram.message[i].text = result["message"].getObject()["text"].getStr(); + } + Telegram.next_update_id = Telegram.message[i].update_id +1; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Parsed update_id %d, chat_id %d, text \"%s\""), Telegram.message[i].update_id, Telegram.message[i].chat_id, Telegram.message[i].text.c_str()); + } + } else { + + } + } else { + + } +} + +bool TelegramSendMessage(uint32_t chat_id, String text) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: sendMessage")); + + if (!TelegramInit()) { return false; } + + bool sent = false; + if (text != "") { + String _token = SettingsText(SET_TELEGRAM_TOKEN); + String command = "bot" + _token + "/sendMessage?chat_id=" + String(chat_id) + "&text=" + text; + String response = TelegramConnectToTelegram(command); + + + + if (response.startsWith("{\"ok\":true")) { + + sent = true; + } + } + + return sent; +} +# 289 "/workspace/Tasmota/tasmota/xdrv_40_telegram.ino" +String TelegramExecuteCommand(const char *svalue) { + String response = ""; + + uint32_t curridx = web_log_index; + ExecuteCommand(svalue, SRC_CHAT); + if (web_log_index != curridx) { + uint32_t counter = curridx; + response = F("{"); + bool cflg = false; + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { + size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + if (cflg) { response += F(","); } + response += stemp; + cflg = true; + } + } + counter++; + counter &= 0xFF; + if (!counter) counter++; + } while (counter != web_log_index); + response += F("}"); + } else { + response = F("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}"); + } + + return response; +} + +void TelegramLoop(void) { + if (!global_state.network_down && (Telegram.recv_enable || Telegram.echo_enable)) { + switch (Telegram.state) { + case 0: + TelegramInit(); + Telegram.state++; + break; + case 1: + TelegramGetUpdates(Telegram.next_update_id); + Telegram.index = 0; + Telegram.retry = TELEGRAM_SEND_RETRY; + Telegram.state++; + break; + case 2: + if (Telegram.echo_enable) { + if (Telegram.retry && (Telegram.index < Telegram.message_count)) { + if (TelegramSendMessage(Telegram.message[Telegram.index].chat_id, Telegram.message[Telegram.index].text)) { + Telegram.index++; + Telegram.retry = TELEGRAM_SEND_RETRY; + } else { + Telegram.retry--; + } + } else { + Telegram.message_count = 0; + Telegram.wait = Telegram.poll; + Telegram.state++; + } + } else { + if (Telegram.skip) { + Telegram.skip = false; + } else { + if (Telegram.message_count && (Telegram.message[Telegram.index].text.length() > 0)) { + String logging = TelegramExecuteCommand(Telegram.message[Telegram.index].text.c_str()); + if (logging.length() > 0) { + TelegramSendMessage(Telegram.message[Telegram.index].chat_id, logging); + } + } + } + Telegram.message_count = 0; + Telegram.wait = Telegram.poll; + Telegram.state++; + } + break; + case 3: + if (Telegram.wait) { + Telegram.wait--; + } else { + Telegram.state = 1; + } + } + } +} + + + + + +#define D_CMND_TMSTATE "State" +#define D_CMND_TMPOLL "Poll" +#define D_CMND_TMSEND "Send" +#define D_CMND_TMTOKEN "Token" +#define D_CMND_TMCHATID "ChatId" + +const char kTelegramCommands[] PROGMEM = "Tm|" + D_CMND_TMSTATE "|" D_CMND_TMPOLL "|" D_CMND_TMTOKEN "|" D_CMND_TMCHATID "|" D_CMND_TMSEND; + +void (* const TelegramCommand[])(void) PROGMEM = { + &CmndTmState, &CmndTmPoll, &CmndTmToken, &CmndTmChatId, &CmndTmSend }; + +void CmndTmState(void) { + if (XdrvMailbox.data_len > 0) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + Telegram.send_enable = XdrvMailbox.payload &1; + break; + case 2: + case 3: + Telegram.recv_enable = XdrvMailbox.payload &1; + break; + case 4: + case 5: + Telegram.echo_enable = XdrvMailbox.payload &1; + break; + } + } + } + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"Send\":\"%s\",\"Receive\":\"%s\",\"Echo\":\"%s\"}}"), + XdrvMailbox.command, GetStateText(Telegram.send_enable), GetStateText(Telegram.recv_enable), GetStateText(Telegram.echo_enable)); +} + +void CmndTmPoll(void) { + if ((XdrvMailbox.payload >= 4) && (XdrvMailbox.payload <= 300)) { + Telegram.poll = XdrvMailbox.payload; + if (Telegram.poll < Telegram.wait) { + Telegram.wait = Telegram.poll; + } + } + ResponseCmndNumber(Telegram.poll); +} + +void CmndTmToken(void) { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_TELEGRAM_TOKEN, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_TELEGRAM_TOKEN)); +} + +void CmndTmChatId(void) { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_TELEGRAM_CHATID, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_TELEGRAM_CHATID)); +} + +void CmndTmSend(void) { + if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) { + ResponseCmndChar(D_JSON_FAILED); + return; + } + if (XdrvMailbox.data_len > 0) { + String message = XdrvMailbox.data; + String chat_id = SettingsText(SET_TELEGRAM_CHATID); + if (!TelegramSendMessage(chat_id.toInt(), message)) { + ResponseCmndChar(D_JSON_FAILED); + return; + } + } + ResponseCmndDone(); +} + + + + + +bool Xdrv40(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + TelegramLoop(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTelegramCommands, TelegramCommand); + break; + } + return result; +} +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_41_tcp_bridge.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_41_tcp_bridge.ino" +#ifdef USE_TCP_BRIDGE + +#define XDRV_41 41 + +#ifndef TCP_BRIDGE_CONNECTIONS +#define TCP_BRIDGE_CONNECTIONS 2 +#endif + +#ifndef TCP_BRIDGE_BUF_SIZE +#define TCP_BRIDGE_BUF_SIZE 255 +#endif + + +WiFiServer *server_tcp = nullptr; + +WiFiClient client_tcp[TCP_BRIDGE_CONNECTIONS]; +uint8_t client_next = 0; +uint8_t *tcp_buf = nullptr; + +#include +TasmotaSerial *TCPSerial = nullptr; + +const char kTCPCommands[] PROGMEM = "TCP" "|" + "Start" "|" "Baudrate" + ; + +void (* const TCPCommand[])(void) PROGMEM = { + &CmndTCPStart, &CmndTCPBaudrate + }; + + + + +void TCPLoop(void) +{ + uint8_t c; + bool busy; + int32_t buf_len; + + if (!TCPSerial) return; + + + if ((server_tcp) && (server_tcp->hasClient())) { + + uint32_t i; + for (i=0; iavailable(); + break; + } + } + if (i >= ARRAY_SIZE(client_tcp)) { + i = client_next++ % ARRAY_SIZE(client_tcp); + WiFiClient &client = client_tcp[i]; + client.stop(); + client = server_tcp->available(); + } + } + + do { + busy = false; + + + buf_len = 0; + while ((buf_len < TCP_BRIDGE_BUF_SIZE) && (TCPSerial->available())) { + c = TCPSerial->read(); + if (c >= 0) { + tcp_buf[buf_len++] = c; + busy = true; + } + } + if (buf_len > 0) { + char hex_char[TCP_BRIDGE_BUF_SIZE+1]; + ToHex_P(tcp_buf, buf_len, hex_char, 256); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "from MCU: %s"), hex_char); + + for (uint32_t i=0; i= 0) { + tcp_buf[buf_len++] = c; + busy = true; + } + } + if (buf_len > 0) { + char hex_char[TCP_BRIDGE_BUF_SIZE+1]; + ToHex_P(tcp_buf, buf_len, hex_char, 256); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "to MCU/%d: %s"), i+1, hex_char); + TCPSerial->write(tcp_buf, buf_len); + } + } + + yield(); + } while (busy); +} + + +void TCPInit(void) { + if (PinUsed(GPIO_TCP_RX) && PinUsed(GPIO_TCP_TX)) { + tcp_buf = (uint8_t*) malloc(TCP_BRIDGE_BUF_SIZE); + if (!tcp_buf) { AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_TCP "could not allocate buffer")); return; } + + if (!Settings.tcp_baudrate) { Settings.tcp_baudrate = 115200 / 1200; } + TCPSerial = new TasmotaSerial(Pin(GPIO_TCP_RX), Pin(GPIO_TCP_TX), seriallog_level ? 1 : 2, 0, TCP_BRIDGE_BUF_SIZE); + TCPSerial->begin(Settings.tcp_baudrate * 1200); + if (TCPSerial->hardwareSerial()) { + ClaimSerial(); + } + } +} +# 148 "/workspace/Tasmota/tasmota/xdrv_41_tcp_bridge.ino" +void CmndTCPStart(void) { + + if (!TCPSerial) { return; } + int32_t tcp_port = XdrvMailbox.payload; + + if (server_tcp) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Stopping TCP server")); + server_tcp->stop(); + delete server_tcp; + server_tcp = nullptr; + + for (uint32_t i=0; i 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Starting TCP server on port %d"), tcp_port); + server_tcp = new WiFiServer(tcp_port); + server_tcp->begin(); + server_tcp->setNoDelay(true); + } + + ResponseCmndDone(); +} + +void CmndTCPBaudrate(void) { + if ((XdrvMailbox.payload >= 1200) && (XdrvMailbox.payload <= 115200)) { + XdrvMailbox.payload /= 1200; + Settings.tcp_baudrate = XdrvMailbox.payload; + TCPSerial->begin(Settings.tcp_baudrate * 1200); + } + ResponseCmndNumber(Settings.tcp_baudrate * 1200); +} + + + + + +bool Xdrv41(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + TCPLoop(); + break; + case FUNC_PRE_INIT: + TCPInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTCPCommands, TCPCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_42_i2s_audio.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_42_i2s_audio.ino" +#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH)) +#include "AudioFileSourcePROGMEM.h" +#include "AudioFileSourceID3.h" +#include "AudioGeneratorMP3.h" +#include "AudioOutputI2S.h" +#include +#include "AudioFileSourceFS.h" +#ifdef SAY_TIME +#include "AudioGeneratorTalkie.h" +#endif +#include "AudioFileSourceICYStream.h" +#include "AudioFileSourceBuffer.h" +#include "AudioGeneratorAAC.h" + +#ifdef USE_TTGO_WATCH +#undef TTGO_PWR_ON +#undef TTGO_PWR_OFF +#define TTGO_PWR_ON TTGO_audio_power(true); +#define TTGO_PWR_OFF TTGO_audio_power(false); +#else +#undef TTGO_PWR_ON +#undef TTGO_PWR_OFF +#define TTGO_PWR_ON +#define TTGO_PWR_OFF +#endif + +#define EXTERNAL_DAC_PLAY 1 + +#define XDRV_42 42 + +AudioGeneratorMP3 *mp3 = nullptr; +AudioFileSourceFS *file; +AudioOutputI2S *out; +AudioFileSourceID3 *id3; +AudioGeneratorMP3 *decoder = NULL; +void *mp3ram = NULL; + +#define I2SAUDIO_TASK_STACK_SIZE 8192 + + +#ifdef ESP8266 +const int preallocateBufferSize = 5*1024; +const int preallocateCodecSize = 29192; +#else +const int preallocateBufferSize = 16*1024; +const int preallocateCodecSize = 29192; + +#endif + +#ifdef USE_WEBRADIO +AudioFileSourceICYStream *ifile = NULL; +AudioFileSourceBuffer *buff = NULL; +char wr_title[64]; + + +void *preallocateBuffer = NULL; +void *preallocateCodec = NULL; +uint32_t retryms = 0; +#endif + +#ifdef SAY_TIME +AudioGeneratorTalkie *talkie = nullptr; +#endif + + +#ifdef ESP32 +#undef TWATCH_DAC_IIS_BCK +#undef TWATCH_DAC_IIS_WS +#undef TWATCH_DAC_IIS_DOUT +#define TWATCH_DAC_IIS_BCK 26 +#define TWATCH_DAC_IIS_WS 25 +#define TWATCH_DAC_IIS_DOUT 33 +#else +#undef TWATCH_DAC_IIS_BCK +#undef TWATCH_DAC_IIS_WS +#undef TWATCH_DAC_IIS_DOUT +#define TWATCH_DAC_IIS_BCK 15 +#define TWATCH_DAC_IIS_WS 2 +#define TWATCH_DAC_IIS_DOUT 3 +#endif + + +#ifdef SAY_TIME +long timezone = 2; +byte daysavetime = 1; + +uint8_t spTHE[] PROGMEM = {0x08,0xE8,0x3E,0x55,0x01,0xC3,0x86,0x27,0xAF,0x72,0x0D,0x4D,0x97,0xD5,0xBC,0x64,0x3C,0xF2,0x5C,0x51,0xF1,0x93,0x36,0x8F,0x4F,0x59,0x2A,0x42,0x7A,0x32,0xC3,0x64,0xFF,0x3F}; +uint8_t spTIME[] PROGMEM = {0x0E,0x28,0xAC,0x2D,0x01,0x5D,0xB6,0x0D,0x33,0xF3,0x54,0xB3,0x60,0xBA,0x8C,0x54,0x5C,0xCD,0x2D,0xD4,0x32,0x73,0x0F,0x8E,0x34,0x33,0xCB,0x4A,0x25,0xD4,0x25,0x83,0x2C,0x2B,0xD5,0x50,0x97,0x08,0x32,0xEC,0xD4,0xDC,0x4C,0x33,0xC8,0x70,0x73,0x0F,0x33,0xCD,0x20,0xC3,0xCB,0x43,0xDD,0x3C,0xCD,0x8C,0x20,0x77,0x89,0xF4,0x94,0xB2,0xE2,0xE2,0x35,0x22,0x5D,0xD6,0x4A,0x8A,0x96,0xCC,0x36,0x25,0x2D,0xC9,0x9A,0x7B,0xC2,0x18,0x87,0x24,0x4B,0x1C,0xC9,0x50,0x19,0x92,0x2C,0x71,0x34,0x4B,0x45,0x8A,0x8B,0xC4,0x96,0xB6,0x5A,0x29,0x2A,0x92,0x5A,0xCA,0x53,0x96,0x20,0x05,0x09,0xF5,0x92,0x5D,0xBC,0xE8,0x58,0x4A,0xDD,0xAE,0x73,0xBD,0x65,0x4B,0x8D,0x78,0xCA,0x2B,0x4E,0xD8,0xD9,0xED,0x22,0x20,0x06,0x75,0x00,0x00,0x80,0xFF,0x07}; +uint8_t spIS[] PROGMEM = {0x21,0x18,0x96,0x38,0xB7,0x14,0x8D,0x60,0x3A,0xA6,0xE8,0x51,0xB4,0xDC,0x2E,0x48,0x7B,0x5A,0xF1,0x70,0x1B,0xA3,0xEC,0x09,0xC6,0xCB,0xEB,0x92,0x3D,0xA7,0x69,0x1F,0xAF,0x71,0x89,0x9C,0xA2,0xB3,0xFC,0xCA,0x35,0x72,0x9A,0xD1,0xF0,0xAB,0x12,0xB3,0x2B,0xC6,0xCD,0x4F,0xCC,0x32,0x26,0x19,0x07,0xDF,0x0B,0x8F,0xB8,0xA4,0xED,0x7C,0xCF,0x23,0x62,0x8B,0x8E,0xF1,0x23,0x0A,0x8B,0x6E,0xCB,0xCE,0xEF,0x54,0x44,0x3C,0xDC,0x08,0x60,0x0B,0x37,0x01,0x1C,0x53,0x26,0x80,0x15,0x4E,0x14,0xB0,0x54,0x2B,0x02,0xA4,0x69,0xFF,0x7F}; +uint8_t spA_M_[] PROGMEM = {0xCD,0xEF,0x86,0xAB,0x57,0x6D,0x0F,0xAF,0x71,0xAD,0x49,0x55,0x3C,0xFC,0x2E,0xC5,0xB7,0x5C,0xF1,0xF2,0x87,0x66,0xDD,0x4E,0xC5,0xC3,0xEF,0x92,0xE2,0x3A,0x65,0xB7,0xA0,0x09,0xAA,0x1B,0x97,0x54,0x82,0x2E,0x28,0x77,0x5C,0x52,0x09,0x1A,0xA3,0xB8,0x76,0x49,0x25,0x68,0x8C,0x73,0xDB,0x24,0x95,0xA0,0x32,0xA9,0x6B,0xA7,0xD9,0x82,0x26,0xA9,0x76,0x42,0xD6,0x08,0xBA,0xE1,0xE8,0x0E,0x5A,0x2B,0xEA,0x9E,0x3D,0x27,0x18,0xAD,0xA8,0x07,0xF1,0x98,0x90,0x35,0xA2,0x96,0x44,0xA3,0x5D,0x66,0x8B,0x6B,0x12,0xCD,0x32,0x85,0x25,0xC9,0x81,0x2D,0xC3,0x64,0x85,0x34,0x58,0x89,0x94,0x52,0x1C,0x52,0x2F,0x35,0xDA,0xC7,0x51,0x48,0x23,0x97,0xCC,0x2C,0x97,0x2E,0xF3,0x5C,0xF3,0xA2,0x14,0xBA,0x2C,0x48,0xCE,0xCA,0x76,0xE8,0x32,0x2F,0x34,0xB2,0xDB,0x85,0xC9,0x83,0x90,0xA8,0x2C,0x57,0x26,0x8F,0x9C,0xBD,0xA2,0x53,0xD9,0xC2,0x54,0x59,0x28,0x99,0x4B,0x2C,0x5D,0xFF,0x3F}; +uint8_t spP_M_[] PROGMEM = {0x0E,0x98,0x41,0x54,0x00,0x43,0xA0,0x05,0xAB,0x42,0x8E,0x1D,0xA3,0x15,0xEC,0x4E,0x58,0xF7,0x92,0x66,0x70,0x1B,0x66,0xDB,0x73,0x99,0xC1,0xEB,0x98,0xED,0xD6,0x25,0x25,0x6F,0x70,0x92,0xDD,0x64,0xD8,0xFC,0x61,0xD0,0x66,0x83,0xD6,0x0A,0x86,0x23,0xAB,0x69,0xDA,0x2B,0x18,0x9E,0x3D,0x37,0x69,0x9D,0xA8,0x07,0x71,0x9F,0xA0,0xBD,0xA2,0x16,0xD5,0x7C,0x54,0xF6,0x88,0x6B,0x54,0x8B,0x34,0x49,0x2D,0x29,0x49,0x3C,0x34,0x64,0xA5,0x24,0x1B,0x36,0xD7,0x72,0x13,0x92,0xA4,0xC4,0x2D,0xC3,0xB3,0x4B,0xA3,0x62,0x0F,0x2B,0x37,0x6E,0x8B,0x5A,0xD4,0x3D,0xDD,0x9A,0x2D,0x50,0x93,0xF6,0x4C,0xAA,0xB6,0xC4,0x85,0x3B,0xB2,0xB1,0xD8,0x93,0x20,0x4D,0x8F,0x24,0xFF,0x0F}; +uint8_t spOH[] PROGMEM = {0xC6,0xC9,0x71,0x5A,0xA2,0x92,0x14,0x2F,0x6E,0x97,0x9C,0x46,0x9D,0xDC,0xB0,0x4D,0x62,0x1B,0x55,0x70,0xDD,0x55,0xBE,0x0E,0x36,0xC1,0x33,0x37,0xA9,0xA7,0x51,0x1B,0xCF,0x3C,0xA5,0x9E,0x44,0xAC,0x3C,0x7D,0x98,0x7B,0x52,0x96,0x72,0x65,0x4B,0xF6,0x1A,0xD9,0xCA,0xF5,0x91,0x2D,0xA2,0x2A,0x4B,0xF7,0xFF,0x01}; +uint8_t spOCLOCK[] PROGMEM = {0x21,0x4E,0x3D,0xB8,0x2B,0x19,0xBB,0x24,0x0E,0xE5,0xEC,0x60,0xE4,0xF2,0x90,0x13,0xD4,0x2A,0x11,0x80,0x00,0x42,0x69,0x26,0x40,0xD0,0x2B,0x04,0x68,0xE0,0x4D,0x00,0x3A,0x35,0x35,0x33,0xB6,0x51,0xD9,0x64,0x34,0x82,0xB4,0x9A,0x63,0x92,0x55,0x89,0x52,0x5B,0xCA,0x2E,0x34,0x25,0x4E,0x63,0x28,0x3A,0x50,0x95,0x26,0x8D,0xE6,0xAA,0x64,0x58,0xEA,0x92,0xCE,0xC2,0x46,0x15,0x9B,0x86,0xCD,0x2A,0x2E,0x37,0x00,0x00,0x00,0x0C,0xC8,0xDD,0x05,0x01,0xB9,0x33,0x21,0xA0,0x74,0xD7,0xFF,0x07}; +uint8_t spONE[] PROGMEM = {0xCC,0x67,0x75,0x42,0x59,0x5D,0x3A,0x4F,0x9D,0x36,0x63,0xB7,0x59,0xDC,0x30,0x5B,0x5C,0x23,0x61,0xF3,0xE2,0x1C,0xF1,0xF0,0x98,0xC3,0x4B,0x7D,0x39,0xCA,0x1D,0x2C,0x2F,0xB7,0x15,0xEF,0x70,0x79,0xBC,0xD2,0x46,0x7C,0x52,0xE5,0xF1,0x4A,0x6A,0xB3,0x71,0x47,0xC3,0x2D,0x39,0x34,0x4B,0x23,0x35,0xB7,0x7A,0x55,0x33,0x8F,0x59,0xDC,0xA2,0x44,0xB5,0xBC,0x66,0x72,0x8B,0x64,0xF5,0xF6,0x98,0xC1,0x4D,0x42,0xD4,0x27,0x62,0x38,0x2F,0x4A,0xB6,0x9C,0x88,0x68,0xBC,0xA6,0x95,0xF8,0x5C,0xA1,0x09,0x86,0x77,0x91,0x11,0x5B,0xFF,0x0F}; +uint8_t spTWO[] PROGMEM = {0x0E,0x38,0x6E,0x25,0x00,0xA3,0x0D,0x3A,0xA0,0x37,0xC5,0xA0,0x05,0x9E,0x56,0x35,0x86,0xAA,0x5E,0x8C,0xA4,0x82,0xB2,0xD7,0x74,0x31,0x22,0x69,0xAD,0x1C,0xD3,0xC1,0xD0,0xFA,0x28,0x2B,0x2D,0x47,0xC3,0x1B,0xC2,0xC4,0xAE,0xC6,0xCD,0x9C,0x48,0x53,0x9A,0xFF,0x0F}; +uint8_t spTHREE[] PROGMEM = {0x02,0xD8,0x2E,0x9C,0x01,0xDB,0xA6,0x33,0x60,0xFB,0x30,0x01,0xEC,0x20,0x12,0x8C,0xE4,0xD8,0xCA,0x32,0x96,0x73,0x63,0x41,0x39,0x89,0x98,0xC1,0x4D,0x0D,0xED,0xB0,0x2A,0x05,0x37,0x0F,0xB4,0xA5,0xAE,0x5C,0xDC,0x36,0xD0,0x83,0x2F,0x4A,0x71,0x7B,0x03,0xF7,0x38,0x59,0xCD,0xED,0x1E,0xB4,0x6B,0x14,0x35,0xB7,0x6B,0x94,0x99,0x91,0xD5,0xDC,0x26,0x48,0x77,0x4B,0x66,0x71,0x1B,0x21,0xDB,0x2D,0x8A,0xC9,0x6D,0x88,0xFC,0x26,0x28,0x3A,0xB7,0x21,0xF4,0x1F,0xA3,0x65,0xBC,0x02,0x38,0xBB,0x3D,0x8E,0xF0,0x2B,0xE2,0x08,0xB7,0x34,0xFF,0x0F}; +uint8_t spFOUR[] PROGMEM = {0x0C,0x18,0xB6,0x9A,0x01,0xC3,0x75,0x09,0x60,0xD8,0x0E,0x09,0x30,0xA0,0x9B,0xB6,0xA0,0xBB,0xB0,0xAA,0x16,0x4E,0x82,0xEB,0xEA,0xA9,0xFA,0x59,0x49,0x9E,0x59,0x23,0x9A,0x27,0x3B,0x78,0x66,0xAE,0x4A,0x9C,0x9C,0xE0,0x99,0xD3,0x2A,0xBD,0x72,0x92,0xEF,0xE6,0x88,0xE4,0x45,0x4D,0x7E,0x98,0x2D,0x62,0x67,0x37,0xF9,0xA1,0x37,0xA7,0x6C,0x94,0xE4,0xC7,0x1E,0xDC,0x3C,0xA5,0x83,0x1F,0x8B,0xEB,0x52,0x0E,0x0E,0x7E,0x2E,0x4E,0xC7,0x31,0xD2,0x79,0xA5,0x3A,0x0D,0xD9,0xC4,0xFF,0x07}; +uint8_t spFIVE[] PROGMEM = {0x02,0xE8,0x3E,0x8C,0x01,0xDD,0x65,0x08,0x60,0x98,0x4C,0x06,0x34,0x93,0xCE,0x80,0xE6,0xDA,0x9A,0x14,0x6B,0xAA,0x47,0xD1,0x5E,0x56,0xAA,0x6D,0x56,0xCD,0x78,0xD9,0xA9,0x1C,0x67,0x05,0x83,0xE1,0xA4,0xBA,0x38,0xEE,0x16,0x86,0x9B,0xFA,0x60,0x87,0x5B,0x18,0x6E,0xEE,0x8B,0x1D,0x6E,0x61,0xB9,0x69,0x36,0x65,0xBA,0x8D,0xE5,0xE5,0x3E,0x1C,0xE9,0x0E,0x96,0x9B,0x5B,0xAB,0x95,0x2B,0x58,0x6E,0xCE,0xE5,0x3A,0x6A,0xF3,0xB8,0x35,0x84,0x7B,0x05,0xA3,0xE3,0x36,0xEF,0x92,0x19,0xB4,0x86,0xDB,0xB4,0x69,0xB4,0xD1,0x2A,0x4E,0x65,0x9A,0x99,0xCE,0x28,0xD9,0x85,0x71,0x4C,0x18,0x6D,0x67,0x47,0xC6,0x5E,0x53,0x4A,0x9C,0xB5,0xE2,0x85,0x45,0x26,0xFE,0x7F}; +uint8_t spSIX[] PROGMEM = {0x0E,0xD8,0xAE,0xDD,0x03,0x0E,0x38,0xA6,0xD2,0x01,0xD3,0xB4,0x2C,0xAD,0x6A,0x35,0x9D,0xB1,0x7D,0xDC,0xEE,0xC4,0x65,0xD7,0xF1,0x72,0x47,0x24,0xB3,0x19,0xD9,0xD9,0x05,0x70,0x40,0x49,0xEA,0x02,0x98,0xBE,0x42,0x01,0xDF,0xA4,0x69,0x40,0x00,0xDF,0x95,0xFC,0x3F}; +uint8_t spSEVEN[] PROGMEM = {0x02,0xB8,0x3A,0x8C,0x01,0xDF,0xA4,0x73,0x40,0x01,0x47,0xB9,0x2F,0x33,0x3B,0x73,0x5F,0x53,0x7C,0xEC,0x9A,0xC5,0x63,0xD5,0xD1,0x75,0xAE,0x5B,0xFC,0x64,0x5C,0x35,0x87,0x91,0xF1,0x83,0x36,0xB5,0x68,0x55,0xC5,0x6F,0xDA,0x45,0x2D,0x1C,0x2D,0xB7,0x38,0x37,0x9F,0x60,0x3C,0xBC,0x9A,0x85,0xA3,0x25,0x66,0xF7,0x8A,0x57,0x1C,0xA9,0x67,0x56,0xCA,0x5E,0xF0,0xB2,0x16,0xB2,0xF1,0x89,0xCE,0x8B,0x92,0x25,0xC7,0x2B,0x33,0xCF,0x48,0xB1,0x99,0xB4,0xF3,0xFF}; +uint8_t spEIGHT[] PROGMEM = {0xC3,0x6C,0x86,0xB3,0x27,0x6D,0x0F,0xA7,0x48,0x99,0x4E,0x55,0x3C,0xBC,0x22,0x65,0x36,0x4D,0xD1,0xF0,0x32,0xD3,0xBE,0x34,0xDA,0xC3,0xEB,0x82,0xE2,0xDA,0x65,0x35,0xAF,0x31,0xF2,0x6B,0x97,0x95,0xBC,0x86,0xD8,0x6F,0x82,0xA6,0x73,0x0B,0xC6,0x9E,0x72,0x99,0xCC,0xCB,0x02,0xAD,0x3C,0x9A,0x10,0x60,0xAB,0x62,0x05,0x2C,0x37,0x84,0x00,0xA9,0x73,0x00,0x00,0xFE,0x1F}; +uint8_t spNINE[] PROGMEM = {0xCC,0xA1,0x26,0xBB,0x83,0x93,0x18,0xCF,0x4A,0xAD,0x2E,0x31,0xED,0x3C,0xA7,0x24,0x26,0xC3,0x54,0xF1,0x92,0x64,0x8B,0x8A,0x98,0xCB,0x2B,0x2E,0x34,0x53,0x2D,0x0E,0x2F,0x57,0xB3,0x0C,0x0D,0x3C,0xBC,0x3C,0x4C,0x4B,0xCA,0xF4,0xF0,0x72,0x0F,0x6E,0x49,0x53,0xCD,0xCB,0x53,0x2D,0x35,0x4D,0x0F,0x2F,0x0F,0xD7,0x0C,0x0D,0x3D,0xBC,0xDC,0x4D,0xD3,0xDD,0xC2,0xF0,0x72,0x52,0x4F,0x57,0x9B,0xC3,0xAB,0x89,0xBD,0x42,0x2D,0x0F,0xAF,0x5A,0xD1,0x71,0x91,0x55,0xBC,0x2C,0xC5,0x3B,0xD8,0x65,0xF2,0x82,0x94,0x18,0x4E,0x3B,0xC1,0x73,0x42,0x32,0x33,0x15,0x45,0x4F,0x79,0x52,0x6A,0x55,0xA6,0xA3,0xFF,0x07}; +uint8_t spTEN[] PROGMEM = {0x0E,0xD8,0xB1,0xDD,0x01,0x3D,0xA8,0x24,0x7B,0x04,0x27,0x76,0x77,0xDC,0xEC,0xC2,0xC5,0x23,0x84,0xCD,0x72,0x9A,0x51,0xF7,0x62,0x45,0xC7,0xEB,0x4E,0x35,0x4A,0x14,0x2D,0xBF,0x45,0xB6,0x0A,0x75,0xB8,0xFC,0x16,0xD9,0x2A,0xD9,0xD6,0x0A,0x5A,0x10,0xCD,0xA2,0x48,0x23,0xA8,0x81,0x35,0x4B,0x2C,0xA7,0x20,0x69,0x0A,0xAF,0xB6,0x15,0x82,0xA4,0x29,0x3C,0xC7,0x52,0x08,0xA2,0x22,0xCF,0x68,0x4B,0x2E,0xF0,0x8A,0xBD,0xA3,0x2C,0xAB,0x40,0x1B,0xCE,0xAA,0xB2,0x6C,0x82,0x40,0x4D,0x7D,0xC2,0x89,0x88,0x8A,0x61,0xCC,0x74,0xD5,0xFF,0x0F}; +uint8_t spELEVEN[] PROGMEM = {0xC3,0xCD,0x76,0x5C,0xAE,0x14,0x0F,0x37,0x9B,0x71,0xDE,0x92,0x55,0xBC,0x2C,0x27,0x70,0xD3,0x76,0xF0,0x83,0x5E,0xA3,0x5E,0x5A,0xC1,0xF7,0x61,0x58,0xA7,0x19,0x35,0x3F,0x99,0x31,0xDE,0x52,0x74,0xFC,0xA2,0x26,0x64,0x4B,0xD1,0xF1,0xAB,0xAE,0xD0,0x2D,0xC5,0xC7,0x2F,0x36,0xDD,0x27,0x15,0x0F,0x3F,0xD9,0x08,0x9F,0x62,0xE4,0xC2,0x2C,0xD4,0xD8,0xD3,0x89,0x0B,0x1B,0x57,0x11,0x0B,0x3B,0xC5,0xCF,0xD6,0xCC,0xC6,0x64,0x35,0xAF,0x18,0x73,0x1F,0xA1,0x5D,0xBC,0x62,0x45,0xB3,0x45,0x51,0xF0,0xA2,0x62,0xAB,0x4A,0x5B,0xC9,0x4B,0x8A,0x2D,0xB3,0x6C,0x06,0x2F,0x29,0xB2,0xAC,0x8A,0x18,0xBC,0x28,0xD9,0xAA,0xD2,0x92,0xF1,0xBC,0xE0,0x98,0x8C,0x48,0xCC,0x17,0x52,0xA3,0x27,0x6D,0x93,0xD0,0x4B,0x8E,0x0E,0x77,0x02,0x00,0xFF,0x0F}; +uint8_t spTWELVE[] PROGMEM = {0x06,0x28,0x46,0xD3,0x01,0x25,0x06,0x13,0x20,0xBA,0x70,0x70,0xB6,0x79,0xCA,0x36,0xAE,0x28,0x38,0xE1,0x29,0xC5,0x35,0xA3,0xE6,0xC4,0x16,0x6A,0x53,0x8C,0x97,0x9B,0x72,0x86,0x4F,0x28,0x1A,0x6E,0x0A,0x59,0x36,0xAE,0x68,0xF8,0x29,0x67,0xFA,0x06,0xA3,0x16,0xC4,0x96,0xE6,0x53,0xAC,0x5A,0x9C,0x56,0x72,0x77,0x31,0x4E,0x49,0x5C,0x8D,0x5B,0x29,0x3B,0x24,0x61,0x1E,0x6C,0x9B,0x6C,0x97,0xF8,0xA7,0x34,0x19,0x92,0x4C,0x62,0x9E,0x72,0x65,0x58,0x12,0xB1,0x7E,0x09,0xD5,0x2E,0x53,0xC5,0xBA,0x36,0x6B,0xB9,0x2D,0x17,0x05,0xEE,0x9A,0x6E,0x8E,0x05,0x50,0x6C,0x19,0x07,0x18,0x50,0xBD,0x3B,0x01,0x92,0x08,0x41,0x40,0x10,0xA6,0xFF,0x0F}; +uint8_t spTHIRTEEN[] PROGMEM = {0x08,0xE8,0x2C,0x15,0x01,0x43,0x07,0x13,0xE0,0x98,0xB4,0xA6,0x35,0xA9,0x1E,0xDE,0x56,0x8E,0x53,0x9C,0x7A,0xE7,0xCA,0x5E,0x76,0x8D,0x94,0xE5,0x2B,0xAB,0xD9,0xB5,0x62,0xA4,0x9C,0xE4,0xE6,0xB4,0x41,0x1E,0x7C,0xB6,0x93,0xD7,0x16,0x99,0x5A,0xCD,0x61,0x76,0x55,0xC2,0x91,0x61,0x1B,0xC0,0x01,0x5D,0x85,0x05,0xE0,0x68,0x51,0x07,0x1C,0xA9,0x64,0x80,0x1D,0x4C,0x9C,0x95,0x88,0xD4,0x04,0x3B,0x4D,0x4E,0x21,0x5C,0x93,0xA8,0x26,0xB9,0x05,0x4B,0x6E,0xA0,0xE2,0xE4,0x57,0xC2,0xB9,0xC1,0xB2,0x93,0x5F,0x09,0xD7,0x24,0xCB,0x4E,0x41,0x25,0x54,0x1D,0x62,0x3B,0x05,0x8D,0x52,0x57,0xAA,0xAD,0x10,0x24,0x26,0xE3,0xE1,0x36,0x5D,0x10,0x85,0xB4,0x97,0x85,0x72,0x41,0x14,0x52,0x5E,0x1A,0xCA,0xF9,0x91,0x6B,0x7A,0x5B,0xC4,0xE0,0x17,0x2D,0x54,0x1D,0x92,0x8C,0x1F,0x25,0x4B,0x8F,0xB2,0x16,0x41,0xA1,0x4A,0x3E,0xE6,0xFA,0xFF,0x01}; +uint8_t spFOURTEEN[] PROGMEM = {0x0C,0x58,0xAE,0x5C,0x01,0xD9,0x87,0x07,0x51,0xB7,0x25,0xB3,0x8A,0x15,0x2C,0xF7,0x1C,0x35,0x87,0x4D,0xB2,0xDD,0x53,0xCE,0x28,0x2B,0xC9,0x0E,0x97,0x2D,0xBD,0x2A,0x17,0x27,0x76,0x8E,0xD2,0x9A,0x6C,0x80,0x94,0x71,0x00,0x00,0x02,0xB0,0x58,0x58,0x00,0x9E,0x0B,0x0A,0xC0,0xB2,0xCE,0xC1,0xC8,0x98,0x7A,0x52,0x95,0x24,0x2B,0x11,0xED,0x36,0xD4,0x92,0xDC,0x4C,0xB5,0xC7,0xC8,0x53,0xF1,0x2A,0xE5,0x1A,0x17,0x55,0xC5,0xAF,0x94,0xBB,0xCD,0x1C,0x26,0xBF,0x52,0x9A,0x72,0x53,0x98,0xFC,0xC2,0x68,0xD2,0x4D,0x61,0xF0,0xA3,0x90,0xB6,0xD6,0x50,0xC1,0x8F,0x42,0xDA,0x4A,0x43,0x39,0x3F,0x48,0x2D,0x6B,0x33,0xF9,0xFF}; +uint8_t spFIFTEEN[] PROGMEM = {0x08,0xE8,0x2A,0x0D,0x01,0xDD,0xBA,0x31,0x60,0x6A,0xF7,0xA0,0xAE,0x54,0xAA,0x5A,0x76,0x97,0xD9,0x34,0x69,0xEF,0x32,0x1E,0x66,0xE1,0xE2,0xB3,0x43,0xA9,0x18,0x55,0x92,0x4E,0x37,0x2D,0x67,0x6F,0xDF,0xA2,0x5A,0xB6,0x04,0x30,0x55,0xA8,0x00,0x86,0x09,0xE7,0x00,0x01,0x16,0x17,0x05,0x70,0x40,0x57,0xE5,0x01,0xF8,0x21,0x34,0x00,0xD3,0x19,0x33,0x80,0x89,0x9A,0x62,0x34,0x4C,0xD5,0x49,0xAE,0x8B,0x53,0x09,0xF7,0x26,0xD9,0x6A,0x7E,0x23,0x5C,0x13,0x12,0xB3,0x04,0x9D,0x50,0x4F,0xB1,0xAD,0x14,0x15,0xC2,0xD3,0xA1,0xB6,0x42,0x94,0xA8,0x8C,0x87,0xDB,0x74,0xB1,0x70,0x59,0xE1,0x2E,0xC9,0xC5,0x81,0x5B,0x55,0xA4,0x4C,0x17,0x47,0xC1,0x6D,0xE3,0x81,0x53,0x9C,0x84,0x6A,0x46,0xD9,0x4C,0x51,0x31,0x42,0xD9,0x66,0xC9,0x44,0x85,0x29,0x6A,0x9B,0xAD,0xFF,0x07}; +uint8_t spSIXTEEN[] PROGMEM = {0x0A,0x58,0x5A,0x5D,0x00,0x93,0x97,0x0B,0x60,0xA9,0x48,0x05,0x0C,0x15,0xAE,0x80,0xAD,0x3D,0x14,0x30,0x7D,0xD9,0x50,0x92,0x92,0xAC,0x0D,0xC5,0xCD,0x2A,0x82,0xAA,0x3B,0x98,0x04,0xB3,0x4A,0xC8,0x9A,0x90,0x05,0x09,0x68,0x51,0xD4,0x01,0x23,0x9F,0x1A,0x60,0xA9,0x12,0x03,0xDC,0x50,0x81,0x80,0x22,0xDC,0x20,0x00,0xCB,0x06,0x3A,0x60,0x16,0xE3,0x64,0x64,0x42,0xDD,0xCD,0x6A,0x8A,0x5D,0x28,0x75,0x07,0xA9,0x2A,0x5E,0x65,0x34,0xED,0x64,0xBB,0xF8,0x85,0xF2,0x94,0x8B,0xAD,0xE4,0x37,0x4A,0x5B,0x21,0xB6,0x52,0x50,0x19,0xAD,0xA7,0xD8,0x4A,0x41,0x14,0xDA,0x5E,0x12,0x3A,0x04,0x91,0x4B,0x7B,0x69,0xA8,0x10,0x24,0x2E,0xE5,0xA3,0x81,0x52,0x90,0x94,0x5A,0x55,0x98,0x32,0x41,0x50,0xCC,0x93,0x2E,0x47,0x85,0x89,0x1B,0x5B,0x5A,0x62,0x04,0x44,0xE3,0x02,0x80,0x80,0x64,0xDD,0xFF,0x1F}; +uint8_t spSEVENTEEN[] PROGMEM = {0x02,0x98,0x3A,0x42,0x00,0x5B,0xA6,0x09,0x60,0xDB,0x52,0x06,0x1C,0x93,0x29,0x80,0xA9,0x52,0x87,0x9A,0xB5,0x99,0x4F,0xC8,0x3E,0x46,0xD6,0x5E,0x7E,0x66,0xFB,0x98,0xC5,0x5A,0xC6,0x9A,0x9C,0x63,0x15,0x6B,0x11,0x13,0x8A,0x9C,0x97,0xB9,0x9A,0x5A,0x39,0x71,0xEE,0xD2,0x29,0xC2,0xA6,0xB8,0x58,0x59,0x99,0x56,0x14,0xA3,0xE1,0x26,0x19,0x19,0xE3,0x8C,0x93,0x17,0xB4,0x46,0xB5,0x88,0x71,0x9E,0x97,0x9E,0xB1,0x2C,0xC5,0xF8,0x56,0xC4,0x58,0xA3,0x1C,0xE1,0x33,0x9D,0x13,0x41,0x8A,0x43,0x58,0xAD,0x95,0xA9,0xDB,0x36,0xC0,0xD1,0xC9,0x0E,0x58,0x4E,0x45,0x01,0x23,0xA9,0x04,0x37,0x13,0xAE,0x4D,0x65,0x52,0x82,0xCA,0xA9,0x37,0x99,0x4D,0x89,0xBA,0xC0,0xBC,0x14,0x36,0x25,0xEA,0x1C,0x73,0x52,0x1D,0x97,0xB8,0x33,0xAC,0x0E,0x75,0x9C,0xE2,0xCE,0xB0,0xDA,0xC3,0x51,0x4A,0x1A,0xA5,0xCA,0x70,0x5B,0x21,0xCE,0x4C,0x26,0xD2,0x6C,0xBA,0x38,0x71,0x2E,0x1F,0x2D,0xED,0xE2,0x24,0xB8,0xBC,0x3D,0x52,0x88,0xAB,0x50,0x8E,0xA8,0x48,0x22,0x4E,0x42,0xA0,0x26,0x55,0xFD,0x3F}; +uint8_t spEIGHTEEN[] PROGMEM = {0x2E,0x9C,0xD1,0x4D,0x54,0xEC,0x2C,0xBF,0x1B,0x8A,0x99,0x70,0x7C,0xFC,0x2E,0x29,0x6F,0x52,0xF6,0xF1,0xBA,0x20,0xBF,0x36,0xD9,0xCD,0xED,0x0C,0xF3,0x27,0x64,0x17,0x73,0x2B,0xA2,0x99,0x90,0x65,0xEC,0xED,0x40,0x73,0x32,0x12,0xB1,0xAF,0x30,0x35,0x0B,0xC7,0x00,0xE0,0x80,0xAE,0xDD,0x1C,0x70,0x43,0xAA,0x03,0x86,0x51,0x36,0xC0,0x30,0x64,0xCE,0x4C,0x98,0xFB,0x5C,0x65,0x07,0xAF,0x10,0xEA,0x0B,0x66,0x1B,0xFC,0x46,0xA8,0x3E,0x09,0x4D,0x08,0x2A,0xA6,0x3E,0x67,0x36,0x21,0x2A,0x98,0x67,0x9D,0x15,0xA7,0xA8,0x60,0xEE,0xB6,0x94,0x99,0xA2,0x4A,0x78,0x22,0xC2,0xA6,0x8B,0x8C,0x8E,0xCC,0x4C,0x8A,0x2E,0x8A,0x4C,0xD3,0x57,0x03,0x87,0x28,0x71,0x09,0x1F,0x2B,0xE4,0xA2,0xC4,0xC5,0x6D,0xAD,0x54,0x88,0xB2,0x63,0xC9,0xF2,0x50,0x2E,0x8A,0x4A,0x38,0x4A,0xEC,0x88,0x28,0x08,0xE3,0x28,0x49,0xF3,0xFF}; +uint8_t spNINETEEN[] PROGMEM = {0xC2,0xEA,0x8A,0x95,0x2B,0x6A,0x05,0x3F,0x71,0x71,0x5F,0x0D,0x12,0xFC,0x28,0x25,0x62,0x35,0xF0,0xF0,0xB3,0x48,0x1E,0x0F,0xC9,0xCB,0x2F,0x45,0x7C,0x2C,0x25,0x1F,0xBF,0x14,0xB3,0x2C,0xB5,0x75,0xFC,0x5A,0x5C,0xA3,0x5D,0xE1,0xF1,0x7A,0x76,0xB3,0x4E,0x45,0xC7,0xED,0x96,0x23,0x3B,0x18,0x37,0x7B,0x18,0xCC,0x09,0x51,0x13,0x4C,0xAB,0x6C,0x4C,0x4B,0x96,0xD2,0x49,0xAA,0x36,0x0B,0xC5,0xC2,0x20,0x26,0x27,0x35,0x63,0x09,0x3D,0x30,0x8B,0xF0,0x48,0x5C,0xCA,0x61,0xDD,0xCB,0xCD,0x91,0x03,0x8E,0x4B,0x76,0xC0,0xCC,0x4D,0x06,0x98,0x31,0x31,0x98,0x99,0x70,0x6D,0x2A,0xA3,0xE4,0x16,0xCA,0xBD,0xCE,0x5C,0x92,0x57,0x28,0xCF,0x09,0x69,0x2E,0x7E,0xA5,0x3C,0x63,0xA2,0x30,0x05,0x95,0xD2,0x74,0x98,0xCD,0x14,0x54,0xCA,0x53,0xA9,0x96,0x52,0x50,0x28,0x6F,0xBA,0xCB,0x0C,0x41,0x50,0xDE,0x65,0x2E,0xD3,0x05,0x89,0x4B,0x7B,0x6B,0x20,0x17,0x44,0xAE,0xED,0x23,0x81,0x52,0x90,0x85,0x73,0x57,0xD0,0x72,0x41,0xB1,0x02,0xDE,0x2E,0xDB,0x04,0x89,0x05,0x79,0xBB,0x62,0xE5,0x76,0x11,0xCA,0x61,0x0E,0xFF,0x1F}; +uint8_t spTWENTY[] PROGMEM = {0x01,0x98,0xD1,0xC2,0x00,0xCD,0xA4,0x32,0x20,0x79,0x13,0x04,0x28,0xE7,0x92,0xDC,0x70,0xCC,0x5D,0xDB,0x76,0xF3,0xD2,0x32,0x0B,0x0B,0x5B,0xC3,0x2B,0xCD,0xD4,0xDD,0x23,0x35,0xAF,0x44,0xE1,0xF0,0xB0,0x6D,0x3C,0xA9,0xAD,0x3D,0x35,0x0E,0xF1,0x0C,0x8B,0x28,0xF7,0x34,0x01,0x68,0x22,0xCD,0x00,0xC7,0xA4,0x04,0xBB,0x32,0xD6,0xAC,0x56,0x9C,0xDC,0xCA,0x28,0x66,0x53,0x51,0x70,0x2B,0xA5,0xBC,0x0D,0x9A,0xC1,0xEB,0x14,0x73,0x37,0x29,0x19,0xAF,0x33,0x8C,0x3B,0xA7,0x24,0xBC,0x42,0xB0,0xB7,0x59,0x09,0x09,0x3C,0x96,0xE9,0xF4,0x58,0xFF,0x0F}; +uint8_t spTHIRTY[] PROGMEM = {0x08,0x98,0xD6,0x15,0x01,0x43,0xBB,0x0A,0x20,0x1B,0x8B,0xE5,0x16,0xA3,0x1E,0xB6,0xB6,0x96,0x97,0x3C,0x57,0xD4,0x2A,0x5E,0x7E,0x4E,0xD8,0xE1,0x6B,0x7B,0xF8,0x39,0x63,0x0D,0x9F,0x95,0xE1,0xE7,0x4C,0x76,0xBC,0x91,0x5B,0x90,0x13,0xC6,0x68,0x57,0x4E,0x41,0x8B,0x10,0x5E,0x1D,0xA9,0x44,0xD3,0xBA,0x47,0xB8,0xDD,0xE4,0x35,0x86,0x11,0x93,0x94,0x92,0x5F,0x29,0xC7,0x4C,0x30,0x0C,0x41,0xC5,0x1C,0x3B,0x2E,0xD3,0x05,0x15,0x53,0x6C,0x07,0x4D,0x15,0x14,0x8C,0xB5,0xC9,0x6A,0x44,0x90,0x10,0x4E,0x9A,0xB6,0x21,0x81,0x23,0x3A,0x91,0x91,0xE8,0xFF,0x01}; +uint8_t spFOURTY[] PROGMEM = {0x04,0x18,0xB6,0x4C,0x00,0xC3,0x56,0x30,0xA0,0xE8,0xF4,0xA0,0x98,0x99,0x62,0x91,0xAE,0x83,0x6B,0x77,0x89,0x78,0x3B,0x09,0xAE,0xBD,0xA6,0x1E,0x63,0x3B,0x79,0x7E,0x71,0x5A,0x8F,0x95,0xE6,0xA5,0x4A,0x69,0xB9,0x4E,0x8A,0x5F,0x12,0x56,0xE4,0x58,0x69,0xE1,0x36,0xA1,0x69,0x2E,0x2B,0xF9,0x95,0x93,0x55,0x17,0xED,0xE4,0x37,0xC6,0xBA,0x93,0xB2,0x92,0xDF,0x19,0xD9,0x6E,0xC8,0x0A,0xFE,0x60,0xE8,0x37,0x21,0xC9,0xF9,0x8D,0x61,0x5F,0x32,0x13,0xE7,0x17,0x4C,0xD3,0xC6,0xB1,0x94,0x97,0x10,0x8F,0x8B,0xAD,0x11,0x7E,0xA1,0x9A,0x26,0x92,0xF6,0xFF,0x01}; +uint8_t spFIFTY[] PROGMEM = {0x08,0xE8,0x2E,0x84,0x00,0x23,0x84,0x13,0x60,0x38,0x95,0xA5,0x0F,0xCF,0xE2,0x79,0x8A,0x8F,0x37,0x02,0xB3,0xD5,0x2A,0x6E,0x5E,0x93,0x94,0x79,0x45,0xD9,0x05,0x5D,0x0A,0xB9,0x97,0x63,0x02,0x74,0xA7,0x82,0x80,0xEE,0xC3,0x10,0xD0,0x7D,0x28,0x03,0x6E,0x14,0x06,0x70,0xE6,0x0A,0xC9,0x9A,0x4E,0x37,0xD9,0x95,0x51,0xCE,0xBA,0xA2,0x14,0x0C,0x81,0x36,0x1B,0xB2,0x5C,0x30,0x38,0xFA,0x9C,0xC9,0x32,0x41,0xA7,0x18,0x3B,0xA2,0x48,0x04,0x05,0x51,0x4F,0x91,0x6D,0x12,0x04,0x20,0x9B,0x61,0x89,0xFF,0x1F}; +uint8_t spGOOD[] PROGMEM = {0x0A,0x28,0xCD,0x34,0x20,0xD9,0x1A,0x45,0x74,0xE4,0x66,0x24,0xAD,0xBA,0xB1,0x8C,0x9B,0x91,0xA5,0x64,0xE6,0x98,0x21,0x16,0x0B,0x96,0x9B,0x4C,0xE5,0xFF,0x01}; +uint8_t spMORNING[] PROGMEM = {0xCE,0x08,0x52,0x2A,0x35,0x5D,0x39,0x53,0x29,0x5B,0xB7,0x0A,0x15,0x0C,0xEE,0x2A,0x42,0x56,0x66,0xD2,0x55,0x2E,0x37,0x2F,0xD9,0x45,0xB3,0xD3,0xC5,0xCA,0x6D,0x27,0xD5,0xEE,0x50,0xF5,0x50,0x94,0x14,0x77,0x2D,0xD8,0x5D,0x49,0x92,0xFD,0xB1,0x64,0x2F,0xA9,0x49,0x0C,0x93,0x4B,0xAD,0x19,0x17,0x3E,0x66,0x1E,0xF1,0xA2,0x5B,0x84,0xE2,0x29,0x8F,0x8B,0x72,0x10,0xB5,0xB1,0x2E,0x4B,0xD4,0x45,0x89,0x4A,0xEC,0x5C,0x95,0x14,0x2B,0x8A,0x9C,0x34,0x52,0x5D,0xBC,0xCC,0xB5,0x3B,0x49,0x69,0x89,0x87,0xC1,0x98,0x56,0x3A,0x21,0x2B,0x82,0x67,0xCC,0x5C,0x85,0xB5,0x4A,0x8A,0xF6,0x64,0xA9,0x96,0xC4,0x69,0x3C,0x52,0x81,0x58,0x1C,0x97,0xF6,0x0E,0x1B,0xCC,0x0D,0x42,0x32,0xAA,0x65,0x12,0x67,0xD4,0x6A,0x61,0x52,0xFC,0xFF}; +uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x9D,0x28,0x71,0xB4,0xAC,0x9C,0x74,0x5A,0x42,0x55,0x33,0xB2,0x93,0x0A,0x09,0xD4,0xC5,0x9A,0xD6,0x44,0x45,0xE3,0x38,0x60,0x9A,0x32,0x05,0xF4,0x18,0x01,0x09,0xD8,0xA9,0xC2,0x00,0x5E,0xCA,0x24,0xD5,0x5B,0x9D,0x4A,0x95,0xEA,0x34,0xEE,0x63,0x92,0x5C,0x4D,0xD0,0xA4,0xEE,0x58,0x0C,0xB9,0x4D,0xCD,0x42,0xA2,0x3A,0x24,0x37,0x25,0x8A,0xA8,0x8E,0xA0,0x53,0xE4,0x28,0x23,0x26,0x13,0x72,0x91,0xA2,0x76,0xBB,0x72,0x38,0x45,0x0A,0x46,0x63,0xCA,0x69,0x27,0x39,0x58,0xB1,0x8D,0x60,0x1C,0x34,0x1B,0x34,0xC3,0x55,0x8E,0x73,0x45,0x2D,0x4F,0x4A,0x3A,0x26,0x10,0xA1,0xCA,0x2D,0xE9,0x98,0x24,0x0A,0x1E,0x6D,0x97,0x29,0xD2,0xCC,0x71,0xA2,0xDC,0x86,0xC8,0x12,0xA7,0x8E,0x08,0x85,0x22,0x8D,0x9C,0x43,0xA7,0x12,0xB2,0x2E,0x50,0x09,0xEF,0x51,0xC5,0xBA,0x28,0x58,0xAD,0xDB,0xE1,0xFF,0x03}; +uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; +uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; + +void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) ; + +void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) { + TTGO_PWR_ON + talkie = new AudioGeneratorTalkie(); + talkie->begin(nullptr, out); + + bool pm = (hour >= 12); + uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE, spTEN, spELEVEN }; + size_t spHourLen[] = { sizeof(spTWELVE), sizeof(spONE), sizeof(spTWO), + sizeof(spTHREE), sizeof(spFOUR), sizeof(spFIVE), + sizeof(spSIX), sizeof(spSEVEN), sizeof(spEIGHT), + sizeof(spNINE), sizeof(spTEN), sizeof(spELEVEN) }; + uint8_t *spMinDec[] = { spOH, spTEN, spTWENTY, spTHIRTY, spFOURTY, spFIFTY }; + size_t spMinDecLen[] = { sizeof(spOH), sizeof(spTEN), sizeof(spTWENTY), + sizeof(spTHIRTY), sizeof(spFOURTY), sizeof(spFIFTY) }; + uint8_t *spMinSpecial[] = { spELEVEN, spTWELVE, spTHIRTEEN, spFOURTEEN, + spFIFTEEN, spSIXTEEN, spSEVENTEEN, spEIGHTEEN, + spNINETEEN }; + size_t spMinSpecialLen[] = { sizeof(spELEVEN), sizeof(spTWELVE), + sizeof(spTHIRTEEN), sizeof(spFOURTEEN), + sizeof(spFIFTEEN), sizeof(spSIXTEEN), + sizeof(spSEVENTEEN), sizeof(spEIGHTEEN), + sizeof(spNINETEEN) }; + uint8_t *spMinLow[] = { spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE }; + size_t spMinLowLen[] = { sizeof(spONE), sizeof(spTWO), sizeof(spTHREE), + sizeof(spFOUR), sizeof(spFIVE), sizeof(spSIX), + sizeof(spSEVEN), sizeof(spEIGHT), sizeof(spNINE) }; + + talkie->say(spTHE, sizeof(spTHE)); + talkie->say(spTIME, sizeof(spTIME)); + talkie->say(spIS, sizeof(spIS)); + + hour = hour % 12; + talkie->say(spHour[hour], spHourLen[hour]); + if (minutes==0) { + talkie->say(spOCLOCK, sizeof(spOCLOCK)); + } else if (minutes<=10 || minutes >=20) { + talkie->say(spMinDec[minutes / 10], spMinDecLen[minutes /10]); + if (minutes % 10) { + talkie->say(spMinLow[(minutes % 10) - 1], spMinLowLen[(minutes % 10) - 1]); + } + } else { + talkie->say(spMinSpecial[minutes - 11], spMinSpecialLen[minutes - 11]); + } + if (pm) { + talkie->say(spP_M_, sizeof(spP_M_)); + } else { + talkie->say(spA_M_, sizeof(spA_M_)); + } + out->stop(); + delete talkie; + TTGO_PWR_OFF +} +#endif + + +uint8_t is2_volume; + +void I2S_Init(void) { + +#if EXTERNAL_DAC_PLAY + out = new AudioOutputI2S(); +#ifdef ESP32 + out->SetPinout(TWATCH_DAC_IIS_BCK, TWATCH_DAC_IIS_WS, TWATCH_DAC_IIS_DOUT); +#endif +#else + out = new AudioOutputI2S(0, 1); +#endif + + is2_volume=10; + out->SetGain(((float)is2_volume/100.0)*4.0); + out->stop(); + mp3ram = nullptr; + +#ifdef ESP32 + if (psramFound()) { + mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + +#ifdef USE_WEBRADIO + if (psramFound()) { + preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + preallocateBuffer = malloc(preallocateBufferSize); + preallocateCodec = malloc(preallocateCodecSize); + } + if (!preallocateBuffer || !preallocateCodec) { + + } +#endif +#endif +} + +#ifdef ESP32 +TaskHandle_t mp3_task_h; + +void mp3_task(void *arg) { + while (1) { + while (mp3->isRunning()) { + if (!mp3->loop()) { + mp3->stop(); + mp3_delete(); + if (mp3_task_h) { + vTaskDelete(mp3_task_h); + mp3_task_h = 0; + } + + } + delay(1); + } + } +} +#endif + +#ifdef USE_WEBRADIO +void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { + const char *ptr = reinterpret_cast(cbData); + (void) isUnicode; + (void) ptr; + if (strstr_P(type, PSTR("Title"))) { + strncpy(wr_title, str, sizeof(wr_title)); + wr_title[sizeof(wr_title)-1] = 0; + + } else { + + } +} + +void StatusCallback(void *cbData, int code, const char *string) { + const char *ptr = reinterpret_cast(cbData); + (void) code; + (void) ptr; + + +} + +void Webradio(const char *url) { + if (decoder || mp3) return; + TTGO_PWR_ON + ifile = new AudioFileSourceICYStream(url); + ifile->RegisterMetadataCB(MDCallback, NULL); + buff = new AudioFileSourceBuffer(ifile, preallocateBuffer, preallocateBufferSize); + buff->RegisterStatusCB(StatusCallback, NULL); + decoder = new AudioGeneratorMP3(preallocateCodec, preallocateCodecSize); + decoder->RegisterStatusCB(StatusCallback, NULL); + decoder->begin(buff, out); + if (!decoder->isRunning()) { + + StopPlaying(); + + retryms = millis() + 2000; + } + + xTaskCreatePinnedToCore(mp3_task2, "MP3-2", I2SAUDIO_TASK_STACK_SIZE, NULL, 3, &mp3_task_h, 1); +} + +void mp3_task2(void *arg){ + while (1) { + if (decoder && decoder->isRunning()) { + if (!decoder->loop()) { + StopPlaying(); + + } + delay(1); + } + } +} + +void StopPlaying() { + + if (mp3_task_h) { + vTaskDelete(mp3_task_h); + mp3_task_h = nullptr; + } + + if (decoder) { + decoder->stop(); + delete decoder; + decoder = NULL; + } + if (buff) { + buff->close(); + delete buff; + buff = NULL; + } + if (ifile) { + ifile->close(); + delete ifile; + ifile = NULL; + } + TTGO_PWR_OFF +} + +void Cmd_WebRadio(void) { + if (decoder) { + StopPlaying(); + } + if (XdrvMailbox.data_len > 0) { + Webradio(XdrvMailbox.data); + ResponseCmndChar(XdrvMailbox.data); + } else { + ResponseCmndChar_P(PSTR("Stopped")); + } + +} + +const char HTTP_WEBRADIO[] PROGMEM = + "{s}" "I2S_WR-Title" "{m}%s{e}"; + +void I2S_WR_Show(void) { + if (decoder) { + WSContentSend_PD(HTTP_WEBRADIO,wr_title); + } +} + +#endif + +#ifdef ESP32 +void Play_mp3(const char *path) { +#if defined(USE_SCRIPT) && defined(USE_SCRIPT_FATFS) + if (decoder || mp3) return; + + bool I2S_Task; + + TTGO_PWR_ON + if (*path=='+') { + I2S_Task = true; + path++; + } else { + I2S_Task = false; + } + + file = new AudioFileSourceFS(*fsp,path); + if (file->isOpen()) { + id3 = new AudioFileSourceID3(file); + + if (mp3ram) { + mp3 = new AudioGeneratorMP3(mp3ram, preallocateCodecSize); + } else { + mp3 = new AudioGeneratorMP3(); + } + mp3->begin(id3, out); + + if (I2S_Task) { + xTaskCreatePinnedToCore(mp3_task, "MP3", I2SAUDIO_TASK_STACK_SIZE, NULL, 3, &mp3_task_h, 1); + } else { + while (mp3->isRunning()) { + if (!mp3->loop()) { + mp3->stop(); + mp3_delete(); + break; + } + OsWatchLoop(); + } + } + } + +#endif +} + +void mp3_delete(void) { + delete file; + delete id3; + delete mp3; + mp3=nullptr; + TTGO_PWR_OFF +} +#endif + +void Say(char *text) { + + TTGO_PWR_ON + + out->begin(); + ESP8266SAM *sam = new ESP8266SAM; + sam->Say(out, text); + delete sam; + out->stop(); + + TTGO_PWR_OFF +} + + +const char kI2SAudio_Commands[] PROGMEM = "I2S|" + "Say|Gain|Time" +#ifdef ESP32 + "|Play" +#ifdef USE_WEBRADIO + "|WR" +#endif +#endif + ; + +void (* const I2SAudio_Command[])(void) PROGMEM = { + &Cmd_Say, &Cmd_Gain, &Cmd_Time +#ifdef ESP32 + ,&Cmd_Play +#ifdef USE_WEBRADIO + ,&Cmd_WebRadio +#endif +#endif +}; + + + +void Cmd_Play(void) { + if (XdrvMailbox.data_len > 0) { + Play_mp3(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void Cmd_Gain(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (out) { + is2_volume=XdrvMailbox.payload; + out->SetGain(((float)(is2_volume-2)/100.0)*4.0); + } + } + ResponseCmndNumber(is2_volume); +} + +void Cmd_Say(void) { + if (XdrvMailbox.data_len > 0) { + Say(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void Cmd_Time(void) { +#ifdef SAY_TIME + sayTime(RtcTime.hour, RtcTime.minute, talkie); +#endif + ResponseCmndDone(); +} + + + + + +bool Xdrv42(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); + break; + case FUNC_INIT: + I2S_Init(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_WEBRADIO + case FUNC_WEB_SENSOR: + I2S_WR_Show(); + break; +#endif +#endif + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_43_mlx90640.ino" +# 26 "/workspace/Tasmota/tasmota/xdrv_43_mlx90640.ino" +#ifdef USE_I2C +#ifdef USE_MLX90640 + +#define MLX90640_ADDRESS 0x33 +#define MLX90640_POI_NUM 6 + + + + + +#define XDRV_43 43 +#define XI2C_53 53 +#include + +const char MLX90640type[] PROGMEM = "MLX90640"; + +#ifdef USE_WEBSERVER +#define WEB_HANDLE_MLX90640 "mlx" +const char HTTP_BTN_MENU_MLX90640[] PROGMEM = "

"; +#endif + +struct { + uint32_t type:1; + uint32_t ready:1; + uint32_t dumpedEE:1; + uint32_t extractedParams:1; + paramsMLX90640 *params; + float Ta; + uint16_t Frame[834]; + float To[768]; + uint8_t pois[2*MLX90640_POI_NUM] = {2,1, 30,1, 10,12, 22,12, 2,23, 30,23}; +} MLX90640; + + + + + +#define D_CMND_MLX90640 "MLX" + +const char S_JSON_MLX90640_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\":%d}"; +const char S_JSON_MLX90640_COMMAND[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\"}"; +const char kMLX90640_Commands[] PROGMEM = "POI"; + +enum MLX90640_Commands { + CMND_MLX90640_POI + }; + + + + +#ifdef USE_WEBSERVER + +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_MLX90640_1_SNS_SIZE = 389; +const char HTTP_MLX90640_1_SNS_COMPRESSED[] PROGMEM = "\x3D\x3C\x1F\xF4\x65\x2A\x2B\x32\x18\xCF\x87\xDD\x33\x65\x1D\x86\xBB\x33\xB0\x41" + "\xA4\x7D\x9F\x81\xE7\x7A\x90\xDB\x18\x7C\x3B\xA6\x76\x10\xB6\x75\x1B\x0E\x43\xA8" + "\x8C\x8E\x43\xA8\x8D\x87\x28\xEA\x23\x23\x94\x77\x8F\x87\xE1\x02\x0D\x13\xAC\xD8" + "\x72\x1D\xE3\xD6\x77\x48\xC8\xE5\x1D\x64\x6C\x39\x47\x78\xEC\x3B\xA4\x64\x72\x1D" + "\x64\x6C\x39\x0E\xF1\xDB\x23\x61\xCA\x3C\x10\x20\xE3\x3A\x36\xC7\x9A\x3E\x2E\x63" + "\xE8\xB4\x6D\x8F\x33\xC1\x9D\xFD\x07\x7C\x67\x7E\x3A\x83\xA3\x61\xD4\x3D\xF1\x0F" + "\x06\x77\xF4\x3C\x43\x0D\x87\x50\xCC\xD3\xE1\xEF\x1E\xF9\xE0\xCE\xFE\xBE\x56\x7C" + "\x3D\xE3\xDF\x3C\x18\x17\xC1\xD6\xE7\x21\xE7\x44\x37\x05\xF9\x90\xCC\xF1\xDD\x04" + "\x2C\x65\x33\x3A\x3B\xC8\xF6\x82\x0E\x87\xF6\x1D\x23\xE0\x21\x66\x87\x41\xE7\x44" + "\x3B\x05\xF0\x9B\xC3\xC4\x18\x5A\xFA\x8B\xEC\x3A\x3B\xA7\x78\xF0\x67\x7F\x46\xC4" + "\x7C\x4C\xCE\x8E\x81\x85\xAF\xA8\x8D\x87\x5F\xD8\x74\x74\x09\x98\xA3\xC6\x98\x3B" + "\xA6\xC3\xF0\xE5\xD3\x3B\xC7\xB4\x8D\x87\xC3\x97\x11\xE0\xF7\x17\xDD\x0B\xFF\x23" + "\xDA\x6C\x3C\xD1\x0D\xBA\x14\x74\x30\x16\x67\xCE\xE8\xDB\x18\x77\x4D\x87\x51\xC6" + "\x75\x5D\x33\xA9\x9D\x57\x0E\x88\xEF\x1D\xE3\xA8\x8C\x81\x32\xF9\xDD\x04\x5D\x04" + "\x8C\x91\xD6\xBE\xC3\xA3\xA5\x60\xC3\xBC\x75\x1C\x67\x55\x63\x3A\x99\xD5\x56\x74" + "\x47\x78\xEF\x1E\xE3\xC1\xEE"; +#define HTTP_MLX90640_1_SNS Decompress(HTTP_MLX90640_1_SNS_COMPRESSED,HTTP_MLX90640_1_SNS_SIZE).c_str() +#else +const char HTTP_MLX90640_1_SNS[] PROGMEM = + "" +; +#endif +#ifdef USE_UNISHOX_COMPRESSION +const size_t HTTP_MLX90640_4b_SNS_SIZE = 418; +const char HTTP_MLX90640_4b_SNS_COMPRESSED[] PROGMEM = "\x3D\x07\x60\x86\x4B\x38\x2C\xB1\x0F\x87\xDF\x9D\x0B\x18\x77\x4E\xF1\xE0\xFB\x3F" + "\x0F\x40\xEF\x8C\xEF\xCB\x44\x3E\x1F\x63\x42\x36\x1F\x68\x7F\x44\xA1\x47\xC3\xEC" + "\xE5\xE3\x3E\xCE\xE1\x0A\x7A\x3C\x2A\x2B\x8F\x87\xD9\xCA\xC6\x7D\x9F\x87\xA1\xD8" + "\x40\x83\x83\x9F\x87\xA0\x9A\x66\x7E\x1E\x87\x60\x9A\x66\x7E\x1E\x9E\x61\x30\xE9" + "\x68\x87\xC3\xEC\x66\x69\x04\x7D\xAC\xE0\xC5\x5F\x0F\x33\xE1\xF6\x37\x3C\x77\x4E" + "\xF1\xF6\x7E\x1E\x98\x32\xB7\x39\x19\xD8\x42\xD9\xF0\xFB\x38\xCF\xB3\xF0\x88\x61" + "\x61\x69\xD6\x72\x1E\x87\x61\x02\x0D\x40\x4B\xB8\x72\x10\x20\xDC\x39\x44\x0A\x77" + "\x0E\x51\x02\x0D\xC3\x96\x40\xA7\x70\xE5\x90\x20\xDC\x39\x84\x0A\x77\x0E\x61\x02" + "\x0D\xC3\x9A\x40\xA7\x70\xE6\x90\x20\xDC\x39\xC4\x08\xB7\x0E\xC0\x41\xE1\x2A\x01" + "\xFC\x3D\x04\xD3\x30\x41\xE2\x0C\xE4\x3E\xC8\x10\xF8\x5B\x13\x4C\xCF\xC2\x18\x58" + "\x5A\x75\x9C\x67\x99\xDC\x3D\x0B\xC3\x2F\x96\x88\x7C\x3E\xEC\xE4\x3E\xCF\xC3\xD0" + "\xEC\x2F\x0C\xBE\x3F\x26\x3B\x32\xF2\x0D\x1D\xDF\x3E\xF6\x7C\xEF\x02\x2E\x1E\x08" + "\x39\x11\xCA\x20\x44\xC8\x8E\xC1\xD8\x21\x91\xF8"; +#define HTTP_MLX90640_4b_SNS Decompress(HTTP_MLX90640_4b_SNS_COMPRESSED,HTTP_MLX90640_4b_SNS_SIZE).c_str() +#else +const char HTTP_MLX90640_4b_SNS[] PROGMEM = + "" + "" + "
" + "" + "
" + "
POI-0: °C (sensor)
" + "
" + "" + ; +#endif +void MLX90640UpdateGUI(void){ + WSContentStart_P("mlx"); + WSContentSendStyle(); + WSContentSend_P(HTTP_MLX90640_1_SNS); + WSContentSend_P(HTTP_MLX90640_2a_SNS); + WSContentSend_P(HTTP_MLX90640_2b_SNS); + WSContentSend_P(HTTP_MLX90640_3a_SNS); + WSContentSend_P(HTTP_MLX90640_3b_SNS); + WSContentSend_P(HTTP_MLX90640_4a_SNS); + WSContentSend_P(HTTP_MLX90640_4b_SNS); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +void MLX90640HandleWebGuiResponse(void){ + char tmp[(MLX90640_POI_NUM*2)+4]; + WebGetArg("ul", tmp, sizeof(tmp)); + if (strlen(tmp)) { + uint8_t _line = atoi(tmp); + + float _buf[65]; + if(_line==0){_buf[0]=1000+MLX90640.Ta;} + else{_buf[0]=(float)_line;} + memcpy((char*)&_buf[1],(char*)&MLX90640.To[_line*64],64*4); + Webserver->send(200,PSTR("application/octet-stream"),(const char*)&_buf,65*4); + return; + } + WebGetArg("up", tmp, sizeof(tmp)); + if (strlen(tmp)==1) { + Webserver->send(200,PSTR("application/octet-stream"),(const char*)&MLX90640.pois,MLX90640_POI_NUM*2); + return; + } + else if (strlen(tmp)>2) { + uint32_t _poi = atoi(tmp); + uint32_t _poiNum = (_poi-(_poi%10000))/10000; + MLX90640.pois[_poiNum*2] = (_poi%10000)/100; + MLX90640.pois[(_poiNum*2)+1] = _poi%100; + + for(int i = 0;i(MLX90640_POI_NUM-1)&&XdrvMailbox.index<1) return false; + _idx = (XdrvMailbox.index-1)*2; + if (XdrvMailbox.data_len > 0) { + uint32_t _coord = TextToInt(XdrvMailbox.data); + MLX90640.pois[_idx] = (_coord%10000)/100; + if(MLX90640.pois[_idx]>31) MLX90640.pois[_idx]=31; + MLX90640.pois[_idx+1] = _coord%100; + if(MLX90640.pois[_idx+1]>23) MLX90640.pois[_idx+1]=23; + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("POI-%u = x:%u,y:%u"),XdrvMailbox.index,MLX90640.pois[_idx],MLX90640.pois[_idx+1]); + Response_P(S_JSON_MLX90640_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + +void MLX90640init() +{ + if (MLX90640.type || !I2cSetDevice(MLX90640_ADDRESS)) { return; } + + Wire.setClock(400000); + int status = -1; + if(!MLX90640.dumpedEE){ + status = MLX90640_DumpEE(MLX90640_ADDRESS, MLX90640.Frame); + if (status != 0){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("Failed to load system parameters")); + } + else { + AddLog_P2(LOG_LEVEL_INFO, PSTR("MLX90640: started")); + MLX90640.type = true; + } + MLX90640.params = new paramsMLX90640; + } +} + + + + +void MLX90640every100msec(){ + static uint32_t _job = 0; + int status; + uint32_t _time; + + if(!MLX90640.extractedParams){ + static uint32_t _chunk = 0; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: will read chunk: %u"), _chunk); + _time = millis(); + status = MLX90640_ExtractParameters(MLX90640.Frame, MLX90640.params, _chunk); + if (status == 0){ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: parameter received after: %u msec, status: %u"), TimePassedSince(_time), status); + } + if (_chunk == 5) MLX90640.extractedParams = true; + _chunk++; + return; + } + + switch(_job){ + case 0: + if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){ + _job=-1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: frame not ready")); + break; + } + + status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame); + + break; + case 1: + MLX90640.Ta = MLX90640_GetTa(MLX90640.Frame, MLX90640.params); + break; + case 2: + + MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 0); + + break; + case 5: + if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){ + _job=4; + break; + } + + status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame); + + break; + case 7: + + MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 1); + + break; + default: + break; + } + _job++; + if(_job>10) _job=0; +} + + + + + + +void MLX90640Show(uint8_t json) +{ + char amb_tstr[FLOATSZ]; + dtostrfd(MLX90640.Ta, Settings.flag2.temperature_resolution, amb_tstr); + if (json) { + ResponseAppend_P(PSTR(",\"MLX90640\":{\"" D_JSON_TEMPERATURE "\":[%s"), amb_tstr); + for(int i = 0;i + + +struct miel_hvac_header { + uint8_t start; +#define MIEL_HVAC_H_START 0xfc + uint8_t type; +#define MIEL_HVAC_H_TYPE_UPDATED 0x61 +#define MIEL_HVAC_H_TYPE_DATA 0x62 +#define MIEL_HVAC_H_TYPE_CONNECTED 0x7a + uint8_t middle1; +#define MIEL_HVAC_H_MIDDLE1 0x01 + uint8_t middle2; +#define MIEL_HVAC_H_MIDDLE2 0x30 + uint8_t len; +}; + +struct miel_hvac_data_settings { + uint8_t _pad1[2]; + uint8_t power; + uint8_t mode; +#define MIEL_HVAC_SETTINGS_MODE_MASK 0x7f + uint8_t temp; + uint8_t fan; + uint8_t vane; + uint8_t _pad2[2]; + uint8_t widevane; +#define MIEL_HVAC_SETTTINGS_WIDEVANE_MASK \ + 0x0f +}; + +struct miel_hvac_data_roomtemp { + uint8_t _pad1[2]; + uint8_t temp; +}; + +struct miel_hvac_data_status { + uint8_t _pad1[2]; + uint8_t compressor; + uint8_t operation; +}; + +struct miel_hvac_data { + uint8_t type; +#define MIEL_HVAC_DATA_T_SETTINGS 0x02 +#define MIEL_HVAC_DATA_T_ROOMTEMP 0x03 +#define MIEL_HVAC_DATA_T_TIMER 0x05 +#define MIEL_HVAC_DATA_T_STATUS 0x06 +#define MIEL_HVAC_DATA_T_STAGE 0x09 + + union { + struct miel_hvac_data_settings + settings; + struct miel_hvac_data_roomtemp + roomtemp; + struct miel_hvac_data_status + status; + + uint8_t bytes[15]; + } data; +}; + +CTASSERT(sizeof(struct miel_hvac_data) == 16); + +CTASSERT(offsetof(struct miel_hvac_data, data.settings.power) == 3); +CTASSERT(offsetof(struct miel_hvac_data, data.settings.mode) == 4); +CTASSERT(offsetof(struct miel_hvac_data, data.settings.temp) == 5); +CTASSERT(offsetof(struct miel_hvac_data, data.settings.fan) == 6); +CTASSERT(offsetof(struct miel_hvac_data, data.settings.vane) == 7); +CTASSERT(offsetof(struct miel_hvac_data, data.settings.widevane) == 10); + +CTASSERT(offsetof(struct miel_hvac_data, data.roomtemp.temp) == 3); + + + +#define MIEL_HVAC_H_TYPE_CONNECT 0x5a +static const uint8_t miel_hvac_msg_connect[] = { 0xca, 0x01 }; + +#define MIEL_HVAC_H_TYPE_REQUEST 0x42 + +struct miel_hvac_msg_request { + uint8_t type; +#define MIEL_HVAC_REQUEST_SETTINGS 0x02 +#define MIEL_HVAC_REQUEST_ROOMTEMP 0x03 +#define MIEL_HVAC_REQUEST_TIMERS 0x05 +#define MIEL_HVAC_REQUEST_STATUS 0x06 +#define MIEL_HVAC_REQUEST_STAGE 0x09 + uint8_t zero[15]; +}; + +#define MIEL_HVAC_H_TYPE_UPDATE 0x41 + +struct miel_hvac_msg_update { + uint8_t one; + uint16_t flags; +#define MIEL_HVAC_UPDATE_F_WIDEVANE (1 << 0) +#define MIEL_HVAC_UPDATE_F_POWER (1 << 8) +#define MIEL_HVAC_UPDATE_F_MODE (1 << 9) +#define MIEL_HVAC_UPDATE_F_TEMP (1 << 10) +#define MIEL_HVAC_UPDATE_F_FAN (1 << 11) +#define MIEL_HVAC_UPDATE_F_VANE (1 << 12) + uint8_t power; +#define MIEL_HVAC_UPDATE_POWER_OFF 0x00 +#define MIEL_HVAC_UPDATE_POWER_ON 0x01 + uint8_t mode; +#define MIEL_HVAC_UPDATE_MODE_HEAT 0x01 +#define MIEL_HVAC_UPDATE_MODE_DRY 0x02 +#define MIEL_HVAC_UPDATE_MODE_COOL 0x03 +#define MIEL_HVAC_UPDATE_MODE_FAN 0x07 +#define MIEL_HVAC_UPDATE_MODE_AUTO 0x08 + uint8_t temp; +#define MIEL_HVAC_UPDATE_TEMP_MIN 16 +#define MIEL_HVAC_UPDATE_TEMP_MAX 31 + uint8_t fan; +#define MIEL_HVAC_UPDATE_FAN_AUTO 0x00 +#define MIEL_HVAC_UPDATE_FAN_QUIET 0x01 +#define MIEL_HVAC_UPDATE_FAN_1 0x02 +#define MIEL_HVAC_UPDATE_FAN_2 0x03 +#define MIEL_HVAC_UPDATE_FAN_3 0x05 +#define MIEL_HVAC_UPDATE_FAN_4 0x06 + uint8_t vane; +#define MIEL_HVAC_UPDATE_VANE_AUTO 0x00 +#define MIEL_HVAC_UPDATE_VANE_1 0x01 +#define MIEL_HVAC_UPDATE_VANE_2 0x02 +#define MIEL_HVAC_UPDATE_VANE_3 0x03 +#define MIEL_HVAC_UPDATE_VANE_4 0x04 +#define MIEL_HVAC_UPDATE_VANE_5 0x05 +#define MIEL_HVAC_UPDATE_VANE_SWING 0x07 + uint8_t _pad1[5]; + uint8_t widevane; +#define MIEL_HVAC_UPDATE_WIDEVANE_MASK 0x0f +#define MIEL_HVAC_UPDATE_WIDEVANE_LL 0x01 +#define MIEL_HVAC_UPDATE_WIDEVANE_L 0x02 +#define MIEL_HVAC_UPDATE_WIDEVANE_LL 0x01 +#define MIEL_HVAC_UPDATE_WIDEVANE_L 0x02 +#define MIEL_HVAC_UPDATE_WIDEVANE_C 0x03 +#define MIEL_HVAC_UPDATE_WIDEVANE_R 0x04 +#define MIEL_HVAC_UPDATE_WIDEVANE_RR 0x05 +#define MIEL_HVAC_UPDATE_WIDEVANE_LR 0x08 +#define MIEL_HVAC_UPDATE_WIDEVANE_SWING 0x0c +#define MIEL_HVAC_UPDATE_WIDEVANE_ADJ 0x80 + uint8_t _pad2[2]; +} __packed; + +CTASSERT(sizeof(struct miel_hvac_msg_update) == 16); +#define MIEL_HVAC_OFFS(_v) ((_v) - sizeof(struct miel_hvac_header)) +CTASSERT(offsetof(struct miel_hvac_msg_update, flags) == MIEL_HVAC_OFFS(6)); +CTASSERT(offsetof(struct miel_hvac_msg_update, power) == MIEL_HVAC_OFFS(8)); +CTASSERT(offsetof(struct miel_hvac_msg_update, mode) == MIEL_HVAC_OFFS(9)); +CTASSERT(offsetof(struct miel_hvac_msg_update, temp) == MIEL_HVAC_OFFS(10)); +CTASSERT(offsetof(struct miel_hvac_msg_update, fan) == MIEL_HVAC_OFFS(11)); +CTASSERT(offsetof(struct miel_hvac_msg_update, vane) == MIEL_HVAC_OFFS(12)); +CTASSERT(offsetof(struct miel_hvac_msg_update, widevane) == MIEL_HVAC_OFFS(18)); + +static inline uint8_t +miel_hvac_deg2temp(uint8_t deg) +{ + return (31 - deg); +} + +static inline uint8_t +miel_hvac_temp2deg(uint8_t temp) +{ + return (31 - temp); +} + +static inline unsigned int +miel_hvac_roomtemp2deg(uint8_t roomtemp) +{ + return ((unsigned int)roomtemp + 10); +} + +struct miel_hvac_msg_remotetemp { + uint8_t seven; + uint8_t control; +#define MIEL_HVAC_REMOTETEMP_CLR 0x00 +#define MIEL_HVAC_REMOTETEMP_SET 0x01 + + uint8_t temp_old; +#define MIEL_HVAC_REMOTETEMP_OLD_MIN 8 +#define MIEL_HVAC_REMOTETEMP_OLD_MAX 38 +#define MIEL_HVAC_REMOTETEMP_OLD_FACTOR 2 + + uint8_t temp; +#define MIEL_HVAC_REMOTETEMP_MIN -63 +#define MIEL_HVAC_REMOTETEMP_MAX 63 +#define MIEL_HVAC_REMOTETEMP_OFFSET 64 +#define MIEL_HVAC_REMOTETEMP_FACTOR 2 + uint8_t _pad2[12]; +}; + +CTASSERT(sizeof(struct miel_hvac_msg_remotetemp) == 16); + +static inline uint8_t +miel_hvac_cksum_fini(uint8_t sum) +{ + return (0xfc - sum); +} + +struct miel_hvac_map { + uint8_t byte; + const char *name; +}; + +static const struct miel_hvac_map miel_hvac_mode_map[] = { + { MIEL_HVAC_UPDATE_MODE_HEAT, "heat" }, + { MIEL_HVAC_UPDATE_MODE_DRY, "dry" }, + { MIEL_HVAC_UPDATE_MODE_COOL, "cool" }, + { MIEL_HVAC_UPDATE_MODE_FAN, "fan_only" }, + { MIEL_HVAC_UPDATE_MODE_AUTO, "auto" }, +}; + +static const struct miel_hvac_map miel_hvac_fan_map[] = { + { MIEL_HVAC_UPDATE_FAN_AUTO, "auto" }, + { MIEL_HVAC_UPDATE_FAN_QUIET, "quiet" }, + { MIEL_HVAC_UPDATE_FAN_1, "1" }, + { MIEL_HVAC_UPDATE_FAN_2, "2" }, + { MIEL_HVAC_UPDATE_FAN_3, "3" }, + { MIEL_HVAC_UPDATE_FAN_4, "4" }, +}; + +static const struct miel_hvac_map miel_hvac_vane_map[] = { + { MIEL_HVAC_UPDATE_VANE_AUTO, "auto" }, + { MIEL_HVAC_UPDATE_VANE_1, "1" }, + { MIEL_HVAC_UPDATE_VANE_2, "2" }, + { MIEL_HVAC_UPDATE_VANE_3, "3" }, + { MIEL_HVAC_UPDATE_VANE_4, "4" }, + { MIEL_HVAC_UPDATE_VANE_5, "5" }, + { MIEL_HVAC_UPDATE_VANE_SWING, "swing" }, +}; + +static const struct miel_hvac_map miel_hvac_widevane_map[] = { + { MIEL_HVAC_UPDATE_WIDEVANE_LL, "LL" }, + { MIEL_HVAC_UPDATE_WIDEVANE_L, "L" }, + { MIEL_HVAC_UPDATE_WIDEVANE_C, "C" }, + { MIEL_HVAC_UPDATE_WIDEVANE_R, "R" }, + { MIEL_HVAC_UPDATE_WIDEVANE_RR, "RR" }, + { MIEL_HVAC_UPDATE_WIDEVANE_LR, "split" }, + { MIEL_HVAC_UPDATE_WIDEVANE_SWING, "swing" }, +}; + +enum miel_hvac_parser_state { + MIEL_HVAC_P_START, + MIEL_HVAC_P_TYPE, + MIEL_HVAC_P_MIDDLE1, + MIEL_HVAC_P_MIDDLE2, + MIEL_HVAC_P_LEN, + MIEL_HVAC_P_DATA, + MIEL_HVAC_P_CKSUM, + + MIEL_HVAC_P_SKIP, + MIEL_HVAC_P_SKIP_CKSUM, +}; + +#define MIEL_HVAC_DATABUFLEN 64 + +struct miel_hvac_parser { + enum miel_hvac_parser_state + p_state; + uint8_t p_type; + uint8_t p_sum; + uint8_t p_len; + uint8_t p_off; + uint8_t p_data[MIEL_HVAC_DATABUFLEN]; +}; + +struct miel_hvac_softc { + TasmotaSerial *sc_serial; + struct miel_hvac_parser sc_parser; + + unsigned int sc_device; + unsigned int sc_tick; + bool sc_settings_set; + bool sc_connected; + + struct miel_hvac_data sc_settings; + struct miel_hvac_data sc_temp; + struct miel_hvac_data sc_status; + struct miel_hvac_data sc_stage; + + struct miel_hvac_msg_update + sc_update; + struct miel_hvac_msg_remotetemp + sc_remotetemp; +}; + +static inline bool +miel_hvac_update_pending(struct miel_hvac_softc *sc) +{ + struct miel_hvac_msg_update *update = &sc->sc_update; + + return (update->flags != htons(0)); +} + +static struct miel_hvac_softc *miel_hvac_sc = nullptr; + +static void miel_hvac_input_connected(struct miel_hvac_softc *, + const void *, size_t); +static void miel_hvac_input_data(struct miel_hvac_softc *, + const void *, size_t); +static void miel_hvac_input_updated(struct miel_hvac_softc *, + const void *, size_t); + +static enum miel_hvac_parser_state +miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte) +{ + struct miel_hvac_parser *p = &sc->sc_parser; + enum miel_hvac_parser_state nstate = p->p_state; + + switch (p->p_state) { + case MIEL_HVAC_P_START: + if (byte != MIEL_HVAC_H_START) + return (MIEL_HVAC_P_START); + + + p->p_sum = 0; + + nstate = MIEL_HVAC_P_TYPE; + break; + + case MIEL_HVAC_P_TYPE: + p->p_type = byte; + nstate = MIEL_HVAC_P_MIDDLE1; + break; + + case MIEL_HVAC_P_MIDDLE1: + if (byte != MIEL_HVAC_H_MIDDLE1) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": parse state MIDDLE1 expected %02x got %02x" + ", restarting"), MIEL_HVAC_H_MIDDLE1, byte); + return (MIEL_HVAC_P_START); + } + + nstate = MIEL_HVAC_P_MIDDLE2; + break; + + case MIEL_HVAC_P_MIDDLE2: + if (byte != MIEL_HVAC_H_MIDDLE2) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": parse state MIDDLE2 expected %02x got %02x" + ", restarting"), MIEL_HVAC_H_MIDDLE2, byte); + return (MIEL_HVAC_P_START); + } + + nstate = MIEL_HVAC_P_LEN; + break; + + case MIEL_HVAC_P_LEN: + if (byte == 0) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": skipping 0 byte message type 0x%02x"), + p->p_type); + return (MIEL_HVAC_P_SKIP_CKSUM); + } + + p->p_len = byte; + p->p_off = 0; + + switch (p->p_type) { + case MIEL_HVAC_H_TYPE_CONNECTED: + case MIEL_HVAC_H_TYPE_DATA: + case MIEL_HVAC_H_TYPE_UPDATED: + break; + default: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": skipping unknown message type 0x%02x"), + p->p_type); + return (MIEL_HVAC_P_SKIP); + } + + if (byte > sizeof(p->p_data)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": skipping %u data bytes of message type 0x%02x"), + p->p_len, p->p_type); + return (MIEL_HVAC_P_SKIP); + } + + nstate = MIEL_HVAC_P_DATA; + break; + + case MIEL_HVAC_P_DATA: + p->p_data[p->p_off++] = byte; + if (p->p_off >= p->p_len) + nstate = MIEL_HVAC_P_CKSUM; + break; + + case MIEL_HVAC_P_CKSUM: + if (miel_hvac_cksum_fini(p->p_sum) != byte) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": checksum failed, restarting")); + return (MIEL_HVAC_P_START); + } + + switch (p->p_type) { + case MIEL_HVAC_H_TYPE_CONNECTED: + miel_hvac_input_connected(sc, p->p_data, p->p_len); + break; + case MIEL_HVAC_H_TYPE_DATA: + miel_hvac_input_data(sc, p->p_data, p->p_len); + break; + case MIEL_HVAC_H_TYPE_UPDATED: + miel_hvac_input_updated(sc, p->p_data, p->p_len); + break; + } + + + return (MIEL_HVAC_P_START); + + case MIEL_HVAC_P_SKIP: + if (++p->p_off >= p->p_len) + return (MIEL_HVAC_P_SKIP_CKSUM); + return (nstate); + case MIEL_HVAC_P_SKIP_CKSUM: + return (MIEL_HVAC_P_START); + } + + p->p_sum += byte; + + return (nstate); +} + +static uint8_t +miel_hvac_write(struct miel_hvac_softc *sc, const uint8_t *bytes, size_t len) +{ + TasmotaSerial *serial = sc->sc_serial; + uint8_t cksum = 0; + size_t i; + + for (i = 0; i < len; i++) { + uint8_t b = bytes[i]; + serial->write(b); + cksum += b; + } + + return (cksum); +} + +static void +miel_hvac_send(struct miel_hvac_softc *sc, uint8_t type, + const void *data, size_t len) +{ + TasmotaSerial *serial = sc->sc_serial; + struct miel_hvac_header h = { + MIEL_HVAC_H_START, + type, + MIEL_HVAC_H_MIDDLE1, + MIEL_HVAC_H_MIDDLE2, + (uint8_t)len, + }; + uint8_t cksum = 0; + + cksum += miel_hvac_write(sc, (const uint8_t *)&h, sizeof(h)); + cksum += miel_hvac_write(sc, (const uint8_t *)data, len); + + char hex_h[(sizeof(h) + 1) * 2]; + char hex_d[(len + 1) * 2]; + AddLog_P2(LOG_LEVEL_DEBUG, + PSTR(MIEL_HVAC_LOGNAME ": sending %s %s %02x"), + ToHex_P((uint8_t *)&h, sizeof(h), hex_h, sizeof(hex_h)), + ToHex_P((uint8_t *)data, len, hex_d, sizeof(hex_d)), + miel_hvac_cksum_fini(cksum)); + + serial->write(miel_hvac_cksum_fini(cksum)); + serial->flush(); +} + +#define miel_hvac_send_connect(_sc) \ + miel_hvac_send((_sc), MIEL_HVAC_H_TYPE_CONNECT, \ + miel_hvac_msg_connect, sizeof(miel_hvac_msg_connect)) + +static const struct miel_hvac_map * +miel_hvac_map_byname(const char *name, + const struct miel_hvac_map *m, size_t n) +{ + const struct miel_hvac_map *e; + size_t i; + + for (i = 0; i < n; i++) { + e = &m[i]; + if (strcasecmp(e->name, name) == 0) + return (e); + } + + return (NULL); +} + +static const char * +miel_hvac_map_byval(uint8_t byte, + const struct miel_hvac_map *m, size_t n) +{ + const struct miel_hvac_map *e; + size_t i; + + for (i = 0; i < n; i++) { + e = &m[i]; + if (byte == e->byte) + return (e->name); + } + + return (NULL); +} + +static void +miel_hvac_request(struct miel_hvac_softc *sc, uint8_t type) +{ + struct miel_hvac_msg_request request = { type }; + + miel_hvac_send(sc, MIEL_HVAC_H_TYPE_REQUEST, + &request, sizeof(request)); +} + +static void +miel_hvac_init_update(struct miel_hvac_msg_update *update) +{ + memset(update, 0, sizeof(*update)); + update->one = 1; +} + +static inline void +miel_hvac_send_update(struct miel_hvac_softc *sc, + const struct miel_hvac_msg_update *update) +{ + miel_hvac_send(sc, MIEL_HVAC_H_TYPE_UPDATE, update, sizeof(*update)); +} + +static inline void +miel_hvac_send_remotetemp(struct miel_hvac_softc *sc, + const struct miel_hvac_msg_remotetemp *remotetemp) +{ + miel_hvac_send(sc, MIEL_HVAC_H_TYPE_UPDATE, + remotetemp, sizeof(*remotetemp)); +} + +static bool +miel_hvac_set_power(struct miel_hvac_softc *sc) +{ + struct miel_hvac_msg_update *update = &sc->sc_update; + uint16_t source = XdrvMailbox.payload; + + if (source == SRC_SWITCH) + return (false); + + update->flags |= htons(MIEL_HVAC_UPDATE_F_POWER); + update->power = (XdrvMailbox.index & (1 << sc->sc_device)) ? + MIEL_HVAC_UPDATE_POWER_ON : MIEL_HVAC_UPDATE_POWER_OFF; + + return (true); +} + +static void +miel_hvac_respond_unsupported(void) +{ + ResponseCmndChar_P(PSTR("Unsupported")); +} + +static void +miel_hvac_cmnd_setfanspeed(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + const struct miel_hvac_map *e; + + if (XdrvMailbox.data_len == 0) + return; + + e = miel_hvac_map_byname(XdrvMailbox.data, + miel_hvac_fan_map, nitems(miel_hvac_fan_map)); + if (e == NULL) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_FAN); + update->fan = e->byte; + + ResponseCmndChar_P(e->name); +} + +static void +miel_hvac_cmnd_setmode(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + const struct miel_hvac_map *e; + + if (XdrvMailbox.data_len == 0) + return; + + e = miel_hvac_map_byname(XdrvMailbox.data, + miel_hvac_mode_map, nitems(miel_hvac_mode_map)); + if (e == NULL) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_MODE); + update->mode = e->byte; + + ResponseCmndChar_P(e->name); +} + +static void +miel_hvac_cmnd_sethamode(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + const struct miel_hvac_map *e; + + if (XdrvMailbox.data_len == 0) + return; + + if (strcasecmp(XdrvMailbox.data, "off") == 0) { + update->flags |= htons(MIEL_HVAC_UPDATE_F_POWER); + update->power = MIEL_HVAC_UPDATE_POWER_OFF; + ResponseCmndChar_P(PSTR("off")); + return; + } + + + + + + + e = miel_hvac_map_byname(XdrvMailbox.data, + miel_hvac_mode_map, nitems(miel_hvac_mode_map)); + if (e == NULL) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_POWER) | + htons(MIEL_HVAC_UPDATE_F_MODE); + update->power = MIEL_HVAC_UPDATE_POWER_ON; + update->mode = e->byte; + + ResponseCmndChar_P(e->name); +} + +static void +miel_hvac_cmnd_settemp(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + unsigned long degc; + + if (XdrvMailbox.data_len == 0) + return; + + degc = strtoul(XdrvMailbox.data, nullptr, 0); + if (degc < MIEL_HVAC_UPDATE_TEMP_MIN || + degc > MIEL_HVAC_UPDATE_TEMP_MAX) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_TEMP); + update->temp = miel_hvac_deg2temp(degc); + + ResponseCmndNumber(degc); +} + +static void +miel_hvac_cmnd_setvane(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + const struct miel_hvac_map *e; + + if (XdrvMailbox.data_len == 0) + return; + + e = miel_hvac_map_byname(XdrvMailbox.data, + miel_hvac_vane_map, nitems(miel_hvac_vane_map)); + if (e == NULL) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_VANE); + update->vane = e->byte; + + ResponseCmndChar_P(e->name); +} + +static void +miel_hvac_cmnd_setwidevane(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_update *update = &sc->sc_update; + const struct miel_hvac_map *e; + + if (XdrvMailbox.data_len == 0) + return; + + e = miel_hvac_map_byname(XdrvMailbox.data, + miel_hvac_widevane_map, nitems(miel_hvac_widevane_map)); + if (e == NULL) { + miel_hvac_respond_unsupported(); + return; + } + + update->flags |= htons(MIEL_HVAC_UPDATE_F_WIDEVANE); + update->widevane = e->byte; + + ResponseCmndChar_P(e->name); +} + +static inline uint8_t +miel_hvac_remotetemp_degc2old(long degc) +{ +# 760 "/workspace/Tasmota/tasmota/xdrv_44_miel_hvac.ino" + if (degc < MIEL_HVAC_REMOTETEMP_OLD_MIN) + degc = MIEL_HVAC_REMOTETEMP_OLD_MIN; + else if (degc > MIEL_HVAC_REMOTETEMP_OLD_MAX) + degc = MIEL_HVAC_REMOTETEMP_OLD_MIN; + + return ((degc - MIEL_HVAC_REMOTETEMP_OLD_MIN) * + MIEL_HVAC_REMOTETEMP_OLD_FACTOR); +} + +static void +miel_hvac_cmnd_remotetemp(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + struct miel_hvac_msg_remotetemp *rt = &sc->sc_remotetemp; + uint8_t control = MIEL_HVAC_REMOTETEMP_SET; + long degc; + + if (XdrvMailbox.data_len == 0) + return; + + if (strcasecmp(XdrvMailbox.data, "clear") == 0) { + control = MIEL_HVAC_REMOTETEMP_CLR; + degc = 0; + + ResponseCmndChar_P("clear"); + } else { + degc = strtol(XdrvMailbox.data, nullptr, 0); + + + if (degc < MIEL_HVAC_REMOTETEMP_MIN) + degc = MIEL_HVAC_REMOTETEMP_MIN; + else if (degc > MIEL_HVAC_REMOTETEMP_MAX) + degc = MIEL_HVAC_REMOTETEMP_MAX; + + ResponseCmndNumber(degc); + } + + memset(rt, 0, sizeof(*rt)); + rt->seven = 0x7; + rt->control = control; +# 808 "/workspace/Tasmota/tasmota/xdrv_44_miel_hvac.ino" + rt->temp_old = miel_hvac_remotetemp_degc2old(degc); + rt->temp = (degc + MIEL_HVAC_REMOTETEMP_OFFSET) * + MIEL_HVAC_REMOTETEMP_OLD_FACTOR; +} + +#ifdef MIEL_HVAC_DEBUG +static void +miel_hvac_cmnd_request(void) +{ + struct miel_hvac_softc *sc = miel_hvac_sc; + uint8_t type = MIEL_HVAC_REQUEST_ROOMTEMP; + + if (XdrvMailbox.data_len > 0) + type = strtoul(XdrvMailbox.data, nullptr, 0); + + miel_hvac_request(sc, type); + + ResponseCmndDone(); +} +#endif + + + + + +static void +miel_hvac_log_bytes(struct miel_hvac_softc *sc, const char *name, + const void *buf, size_t len) +{ + char hex[(MIEL_HVAC_DATABUFLEN + 1) * 2]; + const unsigned char *b = (const unsigned char *)buf; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": response %s %s"), name, ToHex_P(b, len, hex, sizeof(hex))); +} + +static void +miel_hvac_input_connected(struct miel_hvac_softc *sc, + const void *buf, size_t len) +{ + AddLog_P2(LOG_LEVEL_INFO, + PSTR(MIEL_HVAC_LOGNAME ": connected to Mitsubishi Electric HVAC")); + sc->sc_connected = 1; +} + +static void +miel_hvac_publish_settings(struct miel_hvac_softc *sc) +{ + const struct miel_hvac_data_settings *set = + &sc->sc_settings.data.settings; + char hex[(sizeof(sc->sc_settings) + 1) * 2]; + char temp[33]; + const char *name; + + Response_P(PSTR("{\"" D_JSON_IRHVAC_POWER "\":\"%s\""), + set->power ? "ON" : "OFF"); + + name = miel_hvac_map_byval( set->mode & + MIEL_HVAC_SETTINGS_MODE_MASK, + miel_hvac_mode_map, nitems(miel_hvac_mode_map)); + if (name != NULL) { + ResponseAppend_P(PSTR(",\"" D_JSON_IRHVAC_MODE "\":\"%s\""), + name); + ResponseAppend_P(PSTR(",\"HA" D_JSON_IRHVAC_MODE "\":\"%s\""), + set->power ? name : "off"); + } + + dtostrfd(ConvertTemp(miel_hvac_temp2deg(set->temp)), + Settings.flag2.temperature_resolution, temp); + ResponseAppend_P(PSTR(",\"" D_JSON_IRHVAC_TEMP "\":%s"), temp); + + name = miel_hvac_map_byval(set->fan, + miel_hvac_fan_map, nitems(miel_hvac_fan_map)); + if (name != NULL) { + ResponseAppend_P(PSTR(",\"" D_JSON_IRHVAC_FANSPEED "\":\"%s\""), + name); + } + + name = miel_hvac_map_byval(set->vane, + miel_hvac_vane_map, nitems(miel_hvac_vane_map)); + if (name != NULL) { + ResponseAppend_P(PSTR(",\"" D_JSON_IRHVAC_SWINGV "\":\"%s\""), + name); + } + + name = miel_hvac_map_byval(set->widevane & + MIEL_HVAC_SETTTINGS_WIDEVANE_MASK, + miel_hvac_widevane_map, nitems(miel_hvac_widevane_map)); + if (name != NULL) { + ResponseAppend_P(PSTR(",\"" D_JSON_IRHVAC_SWINGH "\":\"%s\""), + name); + } + + ResponseAppend_P(PSTR(",\"Bytes\":\"%s\""), + ToHex_P((uint8_t *)&sc->sc_settings, sizeof(sc->sc_settings), + hex, sizeof(hex))); + + ResponseAppend_P(PSTR("}")); + + MqttPublishPrefixTopic_P(TELE, PSTR("HVACSettings")); + + XdrvRulesProcess(); +} + +static void +miel_hvac_input_settings(struct miel_hvac_softc *sc, + const struct miel_hvac_data *d) +{ + const struct miel_hvac_data_settings *set = &d->data.settings; + uint32_t state = set->power ? 1 : 0; + bool publish; + + if (miel_hvac_update_pending(sc)) { + + + + + sc->sc_settings_set = 0; + return; + } + + if (bitRead(power, sc->sc_device) != !!state) + ExecuteCommandPower(sc->sc_device, state, SRC_SWITCH); + + publish = (sc->sc_settings_set == 0) || + (memcmp(d, &sc->sc_settings, sizeof(sc->sc_settings)) != 0); + sc->sc_settings_set = 1; + sc->sc_settings = *d; + + if (publish) + miel_hvac_publish_settings(sc); +} + +static void +miel_hvac_data_response(struct miel_hvac_softc *sc, + const struct miel_hvac_data *d) +{ + char hex[(sizeof(*d) + 1) * 2]; + + Response_P(PSTR("{\"Bytes\":\"%s\"}"), + ToHex_P((uint8_t *)d, sizeof(*d), hex, sizeof(hex))); + + MqttPublishPrefixTopic_P(TELE, PSTR("HVACData")); + XdrvRulesProcess(); +} + +static void +miel_hvac_input_sensor(struct miel_hvac_softc *sc, struct miel_hvac_data *dst, + const struct miel_hvac_data *src) +{ + bool publish; + + publish = (memcmp(dst, src, sizeof(*dst)) != 0); + *dst = *src; + + if (publish) + MqttPublishSensor(); + +} + +static void +miel_hvac_input_data(struct miel_hvac_softc *sc, + const void *buf, size_t len) +{ + const struct miel_hvac_data *d; + + miel_hvac_log_bytes(sc, "data", buf, len); + if (len < sizeof(*d)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME + ": short data response (%zu < %zu)"), len, sizeof(*d)); + return; + } + + d = (const struct miel_hvac_data *)buf; + + switch (d->type) { + case MIEL_HVAC_DATA_T_SETTINGS: + miel_hvac_input_settings(sc, d); + break; + case MIEL_HVAC_DATA_T_ROOMTEMP: + miel_hvac_input_sensor(sc, &sc->sc_temp, d); + break; + case MIEL_HVAC_DATA_T_STATUS: + miel_hvac_input_sensor(sc, &sc->sc_status, d); + break; + case MIEL_HVAC_DATA_T_STAGE: + miel_hvac_input_sensor(sc, &sc->sc_stage, d); + break; + default: + miel_hvac_data_response(sc, d); + break; + } +} + +static void +miel_hvac_input_updated(struct miel_hvac_softc *sc, + const void *buf, size_t len) +{ + miel_hvac_log_bytes(sc, "updated", buf, len); +} + + + + + +static void +miel_hvac_pre_init(void) +{ + struct miel_hvac_softc *sc; + int baudrate = 2400; + + if (!PinUsed(GPIO_MIEL_HVAC_TX) || !PinUsed(GPIO_MIEL_HVAC_RX)) + return; + + sc = (struct miel_hvac_softc *)malloc(sizeof(*sc)); + if (sc == NULL) { + AddLog_P(LOG_LEVEL_ERROR, + PSTR(MIEL_HVAC_LOGNAME ": unable to allocate state")); + return; + } + + memset(sc, 0, sizeof(*sc)); + miel_hvac_init_update(&sc->sc_update); + + sc->sc_serial = new TasmotaSerial(Pin(GPIO_MIEL_HVAC_RX), + Pin(GPIO_MIEL_HVAC_TX), 2); + + if (!sc->sc_serial->begin(baudrate, 2)) { + AddLog_P2(LOG_LEVEL_ERROR, + PSTR(MIEL_HVAC_LOGNAME ": unable to begin serial " + "(baudrate %d)"), baudrate); + goto del; + } + + if (sc->sc_serial->hardwareSerial()) { + ClaimSerial(); + SetSerial(baudrate, TS_SERIAL_8E1); + } + + sc->sc_device = devices_present++; + + miel_hvac_sc = sc; + return; +del: + delete sc->sc_serial; +free: + free(sc); +} + +static void +miel_hvac_loop(struct miel_hvac_softc *sc) +{ + TasmotaSerial *serial = sc->sc_serial; + + while (serial->available()) { + yield(); + + sc->sc_parser.p_state = miel_hvac_parse(sc, serial->read()); + } +} + +static void +miel_hvac_sensor(struct miel_hvac_softc *sc) +{ + char hex[(sizeof(sc->sc_status) + 1) * 2]; + const char *sep = ""; + + ResponseAppend_P(PSTR("," "\"MiElHVAC\":{")); + + if (sc->sc_temp.type != 0) { + const struct miel_hvac_data_roomtemp *rt = + &sc->sc_temp.data.roomtemp; + unsigned int temp = miel_hvac_roomtemp2deg(rt->temp); + char room_temp[33]; + + dtostrfd(ConvertTemp(temp), + Settings.flag2.temperature_resolution, room_temp); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), + room_temp); + + sep = ","; + } + + if (sc->sc_status.type != 0) { + const struct miel_hvac_data_status *s = + &sc->sc_status.data.status; + + ResponseAppend_P(PSTR("%s" "\"Operation\":\"%s\"" "," + "\"Compressor\":\"%s\""), sep, + s->operation ? "ON" : "OFF", + s->compressor ? "ON" : "OFF"); + + sep = ","; + } + + if (sc->sc_temp.type != 0) { + ResponseAppend_P(PSTR("%s" "\"roomtemp\":\"%s\""), sep, + ToHex_P((uint8_t *)&sc->sc_temp, sizeof(sc->sc_temp), + hex, sizeof(hex))); + + sep = ","; + } + + if (sc->sc_status.type != 0) { + ResponseAppend_P(PSTR("%s" "\"status\":\"%s\""), sep, + ToHex_P((uint8_t *)&sc->sc_status, sizeof(sc->sc_status), + hex, sizeof(hex))); + + sep = ","; + } + + if (sc->sc_stage.type != 0) { + ResponseAppend_P(PSTR("%s" "\"stage\":\"%s\""), sep, + ToHex_P((uint8_t *)&sc->sc_stage, sizeof(sc->sc_stage), + hex, sizeof(hex))); + } + + ResponseAppend_P(PSTR("}")); +} +# 1136 "/workspace/Tasmota/tasmota/xdrv_44_miel_hvac.ino" +enum miel_hvac_connect_states { + MIEL_HVAC_CONNECT_S_2400_MSG, + MIEL_HVAC_CONNECT_S_9600, + MIEL_HVAC_CONNECT_S_9600_MSG, + MIEL_HVAC_CONNECT_S_2400, + + MIEL_HVAC_CONNECT_S_COUNT, +}; + +static void +miel_hvac_connect(struct miel_hvac_softc *sc) +{ + TasmotaSerial *serial = sc->sc_serial; + uint32_t baudrate; + unsigned int state; + + state = (sc->sc_tick++ % MIEL_HVAC_CONNECT_S_COUNT); + + switch (state) { + case MIEL_HVAC_CONNECT_S_2400: + baudrate = 2400; + break; + case MIEL_HVAC_CONNECT_S_9600: + baudrate = 9600; + break; + default: + miel_hvac_send_connect(sc); + return; + } + + serial->begin(baudrate, 2); + if (serial->hardwareSerial()) + SetSerial(baudrate, TS_SERIAL_8E1); +} + +static void +miel_hvac_tick(struct miel_hvac_softc *sc) +{ + static const uint8_t updates[] = { + MIEL_HVAC_REQUEST_SETTINGS, + MIEL_HVAC_REQUEST_STATUS, + MIEL_HVAC_REQUEST_SETTINGS, + MIEL_HVAC_REQUEST_ROOMTEMP, + + MIEL_HVAC_REQUEST_SETTINGS, + + MIEL_HVAC_REQUEST_STAGE, + }; + + unsigned int i; + + if (miel_hvac_update_pending(sc)) { + struct miel_hvac_msg_update *update = &sc->sc_update; + + miel_hvac_send_update(sc, update); + + miel_hvac_init_update(update); + + + sc->sc_tick = 0; + return; + } + + if (sc->sc_remotetemp.seven) { + struct miel_hvac_msg_remotetemp *remotetemp = + &sc->sc_remotetemp; + + miel_hvac_send_remotetemp(sc, remotetemp); + memset(remotetemp, 0, sizeof(*remotetemp)); + return; + } + + i = (sc->sc_tick++ % nitems(updates)); + + miel_hvac_request(sc, updates[i]); +} + + + + + +static const char miel_hvac_cmnd_names[] PROGMEM = + + "|" D_CMND_MIEL_HVAC_SETFANSPEED + "|" D_CMND_MIEL_HVAC_SETMODE + "|" D_CMND_MIEL_HVAC_SETHAMODE + "|" D_CMND_MIEL_HVAC_SETTEMP + "|" D_CMND_MIEL_HVAC_SETSWINGV + "|" D_CMND_MIEL_HVAC_SETSWINGH + "|" D_CMND_MIEL_HVAC_REMOTETEMP +#ifdef MIEL_HVAC_DEBUG + "|" "HVACRequest" +#endif + ; + +static void (*const miel_hvac_cmnds[])(void) PROGMEM = { + &miel_hvac_cmnd_setfanspeed, + &miel_hvac_cmnd_setmode, + &miel_hvac_cmnd_sethamode, + &miel_hvac_cmnd_settemp, + &miel_hvac_cmnd_setvane, + &miel_hvac_cmnd_setwidevane, + &miel_hvac_cmnd_remotetemp, +#ifdef MIEL_HVAC_DEBUG + &miel_hvac_cmnd_request, +#endif +}; + +bool Xdrv44(uint8_t function) { + bool result = false; + struct miel_hvac_softc *sc = miel_hvac_sc; + + switch (function) { + case FUNC_PRE_INIT: + miel_hvac_pre_init(); + return (false); + } + + if (sc == NULL) + return (false); + + switch (function) { + case FUNC_LOOP: + miel_hvac_loop(sc); + break; + + case FUNC_SET_DEVICE_POWER: + result = miel_hvac_set_power(sc); + break; + + case FUNC_EVERY_250_MSECOND: + if (sc->sc_connected) + miel_hvac_tick(sc); + else + miel_hvac_connect(sc); + break; + + case FUNC_EVERY_50_MSECOND: + case FUNC_EVERY_100_MSECOND: + case FUNC_EVERY_200_MSECOND: + case FUNC_EVERY_SECOND: + break; + + case FUNC_JSON_APPEND: + miel_hvac_sensor(sc); + break; + case FUNC_AFTER_TELEPERIOD: + if (sc->sc_settings_set) + miel_hvac_publish_settings(sc); + break; + + case FUNC_COMMAND: + result = DecodeCommand(miel_hvac_cmnd_names, miel_hvac_cmnds); + break; + } + + return (result); +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_81_webcam.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_81_webcam.ino" +#ifdef ESP32 +#ifdef USE_WEBCAM +# 61 "/workspace/Tasmota/tasmota/xdrv_81_webcam.ino" +#define XDRV_81 81 + +#include "esp_camera.h" +#include "sensor.h" +#include "fb_gfx.h" +#include "fd_forward.h" +#include "fr_forward.h" + +bool HttpCheckPriviledgedAccess(bool); +extern ESP8266WebServer *Webserver; + +ESP8266WebServer *CamServer; +#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" + +WiFiClient client; + + + +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +struct { + uint8_t up; + uint16_t width; + uint16_t height; + uint8_t stream_active; +#ifdef USE_FACE_DETECT + uint8_t faces; + uint16_t face_detect_time; +#endif +} Wc; + +#ifdef ENABLE_RTSPSERVER +#include +#include +#include +#include +WiFiServer rtspServer(8554); +CStreamer *rtsp_streamer; +CRtspSession *rtsp_session; +WiFiClient rtsp_client; +uint8_t rtsp_start; +OV2640 cam; +#endif + + + +bool WcPinUsed(void) { + bool pin_used = true; + for (uint32_t i = 0; i < MAX_WEBCAM_DATA; i++) { + if (!PinUsed(GPIO_WEBCAM_DATA, i)) { + pin_used = false; + } + + + + + + } + if (!PinUsed(GPIO_WEBCAM_XCLK) || !PinUsed(GPIO_WEBCAM_PCLK) || + !PinUsed(GPIO_WEBCAM_VSYNC) || !PinUsed(GPIO_WEBCAM_HREF) || + !PinUsed(GPIO_WEBCAM_SIOD) || !PinUsed(GPIO_WEBCAM_SIOC)) { + pin_used = false; + } + return pin_used; +} + +uint32_t WcSetup(int32_t fsiz) { + if (fsiz > 10) { fsiz = 10; } + + Wc.stream_active = 0; + + if (fsiz < 0) { + esp_camera_deinit(); + Wc.up = 0; + return 0; + } + + if (Wc.up) { + esp_camera_deinit(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Deinit")); + + } + Wc.up = 0; + + + + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + + + + if (WcPinUsed()) { + config.pin_d0 = Pin(GPIO_WEBCAM_DATA); + config.pin_d1 = Pin(GPIO_WEBCAM_DATA, 1); + config.pin_d2 = Pin(GPIO_WEBCAM_DATA, 2); + config.pin_d3 = Pin(GPIO_WEBCAM_DATA, 3); + config.pin_d4 = Pin(GPIO_WEBCAM_DATA, 4); + config.pin_d5 = Pin(GPIO_WEBCAM_DATA, 5); + config.pin_d6 = Pin(GPIO_WEBCAM_DATA, 6); + config.pin_d7 = Pin(GPIO_WEBCAM_DATA, 7); + config.pin_xclk = Pin(GPIO_WEBCAM_XCLK); + config.pin_pclk = Pin(GPIO_WEBCAM_PCLK); + config.pin_vsync = Pin(GPIO_WEBCAM_VSYNC); + config.pin_href = Pin(GPIO_WEBCAM_HREF); + config.pin_sscb_sda = Pin(GPIO_WEBCAM_SIOD); + config.pin_sscb_scl = Pin(GPIO_WEBCAM_SIOC); + config.pin_pwdn = (PinUsed(GPIO_WEBCAM_PWDN)) ? Pin(GPIO_WEBCAM_PWDN) : -1; + config.pin_reset = (PinUsed(GPIO_WEBCAM_RESET)) ? Pin(GPIO_WEBCAM_RESET) : -1; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: User template")); + } else { + + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Default template")); + } +# 218 "/workspace/Tasmota/tasmota/xdrv_81_webcam.ino" + bool psram = psramFound(); + if (psram) { + config.frame_size = FRAMESIZE_UXGA; + config.jpeg_quality = 10; + config.fb_count = 2; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM found")); + } else { + config.frame_size = FRAMESIZE_VGA; + config.jpeg_quality = 12; + config.fb_count = 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found")); + } + + + + + + + + void *x = 0; + esp_err_t err = esp_camera_init(&config); + if (x) { free(x); } + + if (err != ESP_OK) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: Init failed with error 0x%x"), err); + return 0; + } + + + + sensor_t * wc_s = esp_camera_sensor_get(); + + wc_s->set_vflip(wc_s, Settings.webcam_config.flip); + wc_s->set_hmirror(wc_s, Settings.webcam_config.mirror); + wc_s->set_brightness(wc_s, Settings.webcam_config.brightness -2); + wc_s->set_saturation(wc_s, Settings.webcam_config.saturation -2); + wc_s->set_contrast(wc_s, Settings.webcam_config.contrast -2); + + + wc_s->set_framesize(wc_s, (framesize_t)fsiz); + + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: Init failed to get the frame on time")); + return 0; + } + Wc.width = wc_fb->width; + Wc.height = wc_fb->height; + esp_camera_fb_return(wc_fb); + +#ifdef USE_FACE_DETECT + fd_init(); +#endif + + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: Initialized")); + + Wc.up = 1; + if (psram) { Wc.up = 2; } + + return Wc.up; +} + + + +int32_t WcSetOptions(uint32_t sel, int32_t value) { + int32_t res = 0; + sensor_t *s = esp_camera_sensor_get(); + if (!s) { return -99; } + + switch (sel) { + case 0: + if (value >= 0) { s->set_framesize(s, (framesize_t)value); } + res = s->status.framesize; + break; + case 1: + if (value >= 0) { s->set_special_effect(s, value); } + res = s->status.special_effect; + break; + case 2: + if (value >= 0) { s->set_vflip(s, value); } + res = s->status.vflip; + break; + case 3: + if (value >= 0) { s->set_hmirror(s, value); } + res = s->status.hmirror; + break; + case 4: + if (value >= -4) { s->set_contrast(s, value); } + res = s->status.contrast; + break; + case 5: + if (value >= -4) { s->set_brightness(s, value); } + res = s->status.brightness; + break; + case 6: + if (value >= -4) { s->set_saturation(s,value); } + res = s->status.saturation; + break; + } + + return res; +} + +uint32_t WcGetWidth(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return 0; } + Wc.width = wc_fb->width; + esp_camera_fb_return(wc_fb); + return Wc.width; +} + +uint32_t WcGetHeight(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return 0; } + Wc.height = wc_fb->height; + esp_camera_fb_return(wc_fb); + return Wc.height; +} + + + +uint16_t motion_detect; +uint32_t motion_ltime; +uint32_t motion_trigger; +uint32_t motion_brightness; +uint8_t *last_motion_buffer; + +uint32_t WcSetMotionDetect(int32_t value) { + if (value >= 0) { motion_detect = value; } + if (-1 == value) { + return motion_trigger; + } else { + return motion_brightness; + } +} + + +void WcDetectMotion(void) { + camera_fb_t *wc_fb; + uint8_t *out_buf = 0; + + if ((millis()-motion_ltime) > motion_detect) { + motion_ltime = millis(); + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return; } + + if (!last_motion_buffer) { + last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + if (last_motion_buffer) { + if (PIXFORMAT_JPEG == wc_fb->format) { + out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (out_buf) { + fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf); + uint32_t x, y; + uint8_t *pxi = out_buf; + uint8_t *pxr = last_motion_buffer; + + uint64_t accu = 0; + uint64_t bright = 0; + for (y = 0; y < wc_fb->height; y++) { + for (x = 0; x < wc_fb->width; x++) { + int32_t gray = (pxi[0] + pxi[1] + pxi[2]) / 3; + int32_t lgray = pxr[0]; + pxr[0] = gray; + pxi += 3; + pxr++; + accu += abs(gray - lgray); + bright += gray; + } + } + motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100); + motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100); + free(out_buf); + } + } + } + esp_camera_fb_return(wc_fb); + } +} + + + +#ifdef USE_FACE_DETECT + +static mtmn_config_t mtmn_config = {0}; + +void fd_init(void) { + mtmn_config.type = FAST; + mtmn_config.min_face = 80; + mtmn_config.pyramid = 0.707; + mtmn_config.pyramid_times = 4; + mtmn_config.p_threshold.score = 0.6; + mtmn_config.p_threshold.nms = 0.7; + mtmn_config.p_threshold.candidate_number = 20; + mtmn_config.r_threshold.score = 0.7; + mtmn_config.r_threshold.nms = 0.7; + mtmn_config.r_threshold.candidate_number = 10; + mtmn_config.o_threshold.score = 0.7; + mtmn_config.o_threshold.nms = 0.7; + mtmn_config.o_threshold.candidate_number = 1; +} + +#define FACE_COLOR_WHITE 0x00FFFFFF +#define FACE_COLOR_BLACK 0x00000000 +#define FACE_COLOR_RED 0x000000FF +#define FACE_COLOR_GREEN 0x0000FF00 +#define FACE_COLOR_BLUE 0x00FF0000 +#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN) +#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN) +#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) +void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id); +# 469 "/workspace/Tasmota/tasmota/xdrv_81_webcam.ino" +#define DL_SPIRAM_SUPPORT + +uint32_t WcSetFaceDetect(int32_t value) { + if (value >= 0) { Wc.face_detect_time = value; } + return Wc.faces; +} + +uint32_t face_ltime; + +uint32_t WcDetectFace(void); + +uint32_t WcDetectFace(void) { + dl_matrix3du_t *image_matrix; + size_t out_len, out_width, out_height; + uint8_t * out_buf; + bool s; + bool detected = false; + int face_id = 0; + camera_fb_t *fb; + + if ((millis() - face_ltime) > Wc.face_detect_time) { + face_ltime = millis(); + fb = esp_camera_fb_get(); + if (!fb) { return ESP_FAIL; } + + image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); + if (!image_matrix) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: dl_matrix3du_alloc failed")); + esp_camera_fb_return(fb); + return ESP_FAIL; + } + + out_buf = image_matrix->item; + + + + + s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf); + esp_camera_fb_return(fb); + if (!s){ + dl_matrix3du_free(image_matrix); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: to rgb888 failed")); + return ESP_FAIL; + } + + box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config); + if (net_boxes){ + detected = true; + Wc.faces = net_boxes->len; + + + + + free(net_boxes->score); + free(net_boxes->box); + free(net_boxes->landmark); + free(net_boxes); + } else { + Wc.faces = 0; + } + dl_matrix3du_free(image_matrix); + + + } +} +#endif + + + +#ifndef MAX_PICSTORE +#define MAX_PICSTORE 4 +#endif +struct PICSTORE { + uint8_t *buff; + uint32_t len; +}; + +struct PICSTORE picstore[MAX_PICSTORE]; + +#ifdef COPYFRAME +struct PICSTORE tmp_picstore; +#endif + +uint32_t WcGetPicstore(int32_t num, uint8_t **buff) { + if (num<0) { return MAX_PICSTORE; } + *buff = picstore[num].buff; + return picstore[num].len; +} + +uint32_t WcGetFrame(int32_t bnum) { + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb = 0; + bool jpeg_converted = false; + + if (bnum < 0) { + if (bnum < -MAX_PICSTORE) { bnum=-1; } + bnum = -bnum; + bnum--; + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].len = 0; + return 0; + } + +#ifdef COPYFRAME + if (bnum & 0x10) { + bnum &= 0xf; + _jpg_buf = tmp_picstore.buff; + _jpg_buf_len = tmp_picstore.len; + if (!_jpg_buf_len) { return 0; } + goto pcopy; + } +#endif + + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Can't get frame")); + return 0; + } + if (!bnum) { + Wc.width = wc_fb->width; + Wc.height = wc_fb->height; + esp_camera_fb_return(wc_fb); + return 0; + } + + if (wc_fb->format != PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + +pcopy: + if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; } + bnum--; + if (picstore[bnum].buff) { free(picstore[bnum].buff); } + picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (picstore[bnum].buff) { + memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len); + picstore[bnum].len = _jpg_buf_len; + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Can't allocate picstore")); + picstore[bnum].len = 0; + } + if (wc_fb) { esp_camera_fb_return(wc_fb); } + if (jpeg_converted) { free(_jpg_buf); } + if (!picstore[bnum].buff) { return 0; } + + return _jpg_buf_len; +} + +void HandleImage(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + uint32_t bnum = Webserver->arg(F("p")).toInt(); + if ((bnum < 0) || (bnum > MAX_PICSTORE)) { bnum= 1; } + WiFiClient client = Webserver->client(); + String response = "HTTP/1.1 200 OK\r\n"; + response += "Content-disposition: inline; filename=cap.jpg\r\n"; + response += "Content-type: image/jpeg\r\n\r\n"; + Webserver->sendContent(response); + + if (!bnum) { + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb = 0; + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { return; } + if (wc_fb->format != PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + if (_jpg_buf_len) { + client.write((char *)_jpg_buf, _jpg_buf_len); + } + if (wc_fb) { esp_camera_fb_return(wc_fb); } + } else { + bnum--; + if (!picstore[bnum].len) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: No image #: %d"), bnum); + return; + } + client.write((char *)picstore[bnum].buff, picstore[bnum].len); + } + client.stop(); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Sending image #: %d"), bnum+1); +} + +void HandleImageBasic(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP "Capture image")); + + if (Settings.webcam_config.stream) { + if (!CamServer) { + WcStreamControl(); + } + } + + camera_fb_t *wc_fb; + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Frame buffer could not be acquired")); + return; + } + + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + if (wc_fb->format != PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + + if (_jpg_buf_len) { + Webserver->client().flush(); + WSHeaderSend(); + Webserver->sendHeader(F("Content-disposition"), F("inline; filename=snapshot.jpg")); + Webserver->send_P(200, "image/jpeg", (char *)_jpg_buf, _jpg_buf_len); + Webserver->client().stop(); + } + + esp_camera_fb_return(wc_fb); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Image sent")); +} + +void HandleWebcamMjpeg(void) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Handle camserver")); + + + Wc.stream_active = 1; + client = CamServer->client(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Create client")); + +} + +void HandleWebcamMjpegTask(void) { + camera_fb_t *wc_fb; + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + + + uint32_t tlen; + bool jpeg_converted = false; + + if (!client.connected()) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Client fail")); + Wc.stream_active = 0; + } + if (1 == Wc.stream_active) { + client.flush(); + client.setTimeout(3); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Start stream")); + client.print("HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" + "\r\n"); + Wc.stream_active = 2; + } + if (2 == Wc.stream_active) { + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Frame fail")); + Wc.stream_active = 0; + } + } + if (2 == Wc.stream_active) { + if (wc_fb->format != PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: JPEG compression failed")); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + + client.printf("Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n" + "\r\n", static_cast(_jpg_buf_len)); + tlen = client.write(_jpg_buf, _jpg_buf_len); + + + + + + + client.print("\r\n--" BOUNDARY "\r\n"); + +#ifdef COPYFRAME + if (tmp_picstore.buff) { free(tmp_picstore.buff); } + tmp_picstore.buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (tmp_picstore.buff) { + memcpy(tmp_picstore.buff, _jpg_buf, _jpg_buf_len); + tmp_picstore.len = _jpg_buf_len; + } else { + tmp_picstore.len = 0; + } +#endif + + if (jpeg_converted) { free(_jpg_buf); } + esp_camera_fb_return(wc_fb); + + } + if (0 == Wc.stream_active) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); + client.flush(); + client.stop(); + } +} + +void HandleWebcamRoot(void) { + + CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); + CamServer->send(302, "", ""); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Root called")); +} + + + +uint32_t WcSetStreamserver(uint32_t flag) { + if (global_state.network_down) { return 0; } + + Wc.stream_active = 0; + + if (flag) { + if (!CamServer) { + CamServer = new ESP8266WebServer(81); + CamServer->on("/", HandleWebcamRoot); + CamServer->on("/cam.mjpeg", HandleWebcamMjpeg); + CamServer->on("/cam.jpg", HandleWebcamMjpeg); + CamServer->on("/stream", HandleWebcamMjpeg); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream init")); + CamServer->begin(); + } + } else { + if (CamServer) { + CamServer->stop(); + delete CamServer; + CamServer = NULL; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); + } + } + return 0; +} + +void WcStreamControl() { + WcSetStreamserver(Settings.webcam_config.stream); + WcSetup(Settings.webcam_config.resolution); +} + + +#ifdef ENABLE_RTSPSERVER +static uint32_t rtsp_lastframe_time; +#ifndef RTSP_FRAME_TIME +#define RTSP_FRAME_TIME 100 +#endif +#endif + +void WcLoop(void) { + if (CamServer) { + CamServer->handleClient(); + if (Wc.stream_active) { HandleWebcamMjpegTask(); } + } + if (motion_detect) { WcDetectMotion(); } +#ifdef USE_FACE_DETECT + if (Wc.face_detect_time) { WcDetectFace(); } +#endif + +#ifdef ENABLE_RTSPSERVER + + if (!rtsp_start && !global_state.wifi_down && Wc.up) { + rtspServer.begin(); + rtsp_start = 1; + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP init")); + rtsp_lastframe_time = millis(); + } + + + if (rtsp_session) { + rtsp_session->handleRequests(0); + + + uint32_t now = millis(); + if ((now-rtsp_lastframe_time) > RTSP_FRAME_TIME) { + rtsp_session->broadcastCurrentFrame(now); + rtsp_lastframe_time = now; + + } + + if (rtsp_session->m_stopped) { + delete rtsp_session; + delete rtsp_streamer; + rtsp_session = NULL; + rtsp_streamer = NULL; + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP stopped")); + } + } + else { + rtsp_client = rtspServer.accept(); + if (rtsp_client) { + rtsp_streamer = new OV2640Streamer(&rtsp_client, cam); + rtsp_session = new CRtspSession(&rtsp_client, rtsp_streamer); + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP stream created")); + } + } +#endif +} + +void WcPicSetup(void) { + WebServer_on(PSTR("/wc.jpg"), HandleImage); + WebServer_on(PSTR("/wc.mjpeg"), HandleImage); + WebServer_on(PSTR("/snapshot.jpg"), HandleImage); +} + +void WcShowStream(void) { + if (Settings.webcam_config.stream) { + + if (!CamServer) { + WcStreamControl(); + delay(50); + } + if (CamServer && Wc.up) { + WSContentSend_P(PSTR("

Webcam stream

"), + WiFi.localIP().toString().c_str()); + } + } +} + +void WcInit(void) { + if (!Settings.webcam_config.data) { + Settings.webcam_config.stream = 1; + Settings.webcam_config.resolution = 5; + Settings.webcam_config.flip = 0; + Settings.webcam_config.mirror = 0; + Settings.webcam_config.saturation = 0; + Settings.webcam_config.brightness = 3; + Settings.webcam_config.contrast = 2; + } +} + + + + + +#define D_PRFX_WEBCAM "WC" +#define D_CMND_WC_STREAM "Stream" +#define D_CMND_WC_RESOLUTION "Resolution" +#define D_CMND_WC_MIRROR "Mirror" +#define D_CMND_WC_FLIP "Flip" +#define D_CMND_WC_SATURATION "Saturation" +#define D_CMND_WC_BRIGHTNESS "Brightness" +#define D_CMND_WC_CONTRAST "Contrast" +#define D_CMND_WC_INIT "Init" + +const char kWCCommands[] PROGMEM = D_PRFX_WEBCAM "|" + "|" D_CMND_WC_STREAM "|" D_CMND_WC_RESOLUTION "|" D_CMND_WC_MIRROR "|" D_CMND_WC_FLIP "|" + D_CMND_WC_SATURATION "|" D_CMND_WC_BRIGHTNESS "|" D_CMND_WC_CONTRAST "|" D_CMND_WC_INIT + ; + +void (* const WCCommand[])(void) PROGMEM = { + &CmndWebcam, &CmndWebcamStream, &CmndWebcamResolution, &CmndWebcamMirror, &CmndWebcamFlip, + &CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast, &CmndWebcamInit + }; + +void CmndWebcam(void) { + Response_P(PSTR("{\"" D_PRFX_WEBCAM "\":{\"" D_CMND_WC_STREAM "\":%d,\"" D_CMND_WC_RESOLUTION "\":%d,\"" D_CMND_WC_MIRROR "\":%d,\"" + D_CMND_WC_FLIP "\":%d,\"" + D_CMND_WC_SATURATION "\":%d,\"" D_CMND_WC_BRIGHTNESS "\":%d,\"" D_CMND_WC_CONTRAST "\":%d}}"), + Settings.webcam_config.stream, Settings.webcam_config.resolution, Settings.webcam_config.mirror, + Settings.webcam_config.flip, + Settings.webcam_config.saturation -2, Settings.webcam_config.brightness -2, Settings.webcam_config.contrast -2); +} + +void CmndWebcamStream(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.stream = XdrvMailbox.payload; + if (!Settings.webcam_config.stream) { WcStreamControl(); } + } + ResponseCmndStateText(Settings.webcam_config.stream); +} + +void CmndWebcamResolution(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + Settings.webcam_config.resolution = XdrvMailbox.payload; + WcSetOptions(0, Settings.webcam_config.resolution); + } + ResponseCmndNumber(Settings.webcam_config.resolution); +} + +void CmndWebcamMirror(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.mirror = XdrvMailbox.payload; + WcSetOptions(3, Settings.webcam_config.mirror); + } + ResponseCmndStateText(Settings.webcam_config.mirror); +} + +void CmndWebcamFlip(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.flip = XdrvMailbox.payload; + WcSetOptions(2, Settings.webcam_config.flip); + } + ResponseCmndStateText(Settings.webcam_config.flip); +} + +void CmndWebcamSaturation(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.saturation = XdrvMailbox.payload +2; + WcSetOptions(6, Settings.webcam_config.saturation -2); + } + ResponseCmndNumber(Settings.webcam_config.saturation -2); +} + +void CmndWebcamBrightness(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.brightness = XdrvMailbox.payload +2; + WcSetOptions(5, Settings.webcam_config.brightness -2); + } + ResponseCmndNumber(Settings.webcam_config.brightness -2); +} + +void CmndWebcamContrast(void) { + if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) { + Settings.webcam_config.contrast = XdrvMailbox.payload +2; + WcSetOptions(4, Settings.webcam_config.contrast -2); + } + ResponseCmndNumber(Settings.webcam_config.contrast -2); +} + +void CmndWebcamInit(void) { + WcStreamControl(); + ResponseCmndDone(); +} + + + + + +bool Xdrv81(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + WcLoop(); + break; + case FUNC_WEB_ADD_HANDLER: + WcPicSetup(); + break; + case FUNC_WEB_ADD_MAIN_BUTTON: + WcShowStream(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWCCommands, WCCommand); + break; + case FUNC_PRE_INIT: + WcInit(); + break; + + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_82_ethernet.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_82_ethernet.ino" +#ifdef ESP32 +#ifdef USE_ETHERNET +# 59 "/workspace/Tasmota/tasmota/xdrv_82_ethernet.ino" +#define XDRV_82 82 +# 81 "/workspace/Tasmota/tasmota/xdrv_82_ethernet.ino" +#include + +char eth_hostname[sizeof(my_hostname)]; + +void EthernetEvent(WiFiEvent_t event) { + switch (event) { + case SYSTEM_EVENT_ETH_START: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ETH: " D_ATTEMPTING_CONNECTION)); + ETH.setHostname(eth_hostname); + break; + case SYSTEM_EVENT_ETH_CONNECTED: + AddLog_P2(LOG_LEVEL_INFO, PSTR("ETH: " D_CONNECTED " at %dMbps%s"), + ETH.linkSpeed(), (ETH.fullDuplex()) ? " Full Duplex" : ""); + break; + case SYSTEM_EVENT_ETH_GOT_IP: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %s, Hostname %s"), + ETH.macAddress().c_str(), ETH.localIP().toString().c_str(), eth_hostname); + Settings.ip_address[1] = (uint32_t)ETH.gatewayIP(); + Settings.ip_address[2] = (uint32_t)ETH.subnetMask(); + Settings.ip_address[3] = (uint32_t)ETH.dnsIP(); + global_state.eth_down = 0; + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + AddLog_P2(LOG_LEVEL_INFO, PSTR("ETH: Disconnected")); + global_state.eth_down = 1; + break; + case SYSTEM_EVENT_ETH_STOP: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ETH: Stopped")); + global_state.eth_down = 1; + break; + default: + break; + } +} + +void EthernetInit(void) { + if (!Settings.flag4.network_ethernet) { return; } + if (!PinUsed(GPIO_ETH_PHY_MDC) && !PinUsed(GPIO_ETH_PHY_MDIO)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ETH: No ETH MDC and/or ETH MDIO GPIO defined")); + return; + } + + + strlcpy(eth_hostname, my_hostname, sizeof(eth_hostname) -5); + strcat(eth_hostname, "_eth"); + + WiFi.onEvent(EthernetEvent); + + int eth_power = (PinUsed(GPIO_ETH_PHY_POWER)) ? Pin(GPIO_ETH_PHY_POWER) : -1; + int eth_mdc = Pin(GPIO_ETH_PHY_MDC); + int eth_mdio = Pin(GPIO_ETH_PHY_MDIO); + if (!ETH.begin(Settings.eth_address, eth_power, eth_mdc, eth_mdio, (eth_phy_type_t)Settings.eth_type, (eth_clock_mode_t)Settings.eth_clk_mode)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error")); + }; +} + +IPAddress EthernetLocalIP(void) { + return ETH.localIP(); +} + +char* EthernetHostname(void) { + return eth_hostname; +} + +String EthernetMacAddress(void) { + return ETH.macAddress(); +} + + + + + +#define D_CMND_ETHADDRESS "EthAddress" +#define D_CMND_ETHTYPE "EthType" +#define D_CMND_ETHCLOCKMODE "EthClockMode" + +const char kEthernetCommands[] PROGMEM = "|" + D_CMND_ETHERNET "|" D_CMND_ETHADDRESS "|" D_CMND_ETHTYPE "|" D_CMND_ETHCLOCKMODE; + +void (* const EthernetCommand[])(void) PROGMEM = { + &CmndEthernet, &CmndEthAddress, &CmndEthType, &CmndEthClockMode }; + +void CmndEthernet(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag4.network_ethernet = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndStateText(Settings.flag4.network_ethernet); +} + +void CmndEthAddress(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 31)) { + Settings.eth_address = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.eth_address); +} + +void CmndEthType(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.eth_type = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.eth_type); +} + +void CmndEthClockMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.eth_clk_mode = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.eth_clk_mode); +} + + + + + +bool Xdrv82(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kEthernetCommands, EthernetCommand); + break; + case FUNC_INIT: + EthernetInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_83_esp32watch.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_83_esp32watch.ino" +#ifdef ESP32 +#ifdef USE_TTGO_WATCH + +#include +#include +#include +#include + +#define XDRV_83 83 + +#define AXP202_INT 35 + +struct TTGO_ADC { + float vbus_v; + float vbus_c; + float batt_v; + float batt_c; + float temp; + uint16_t per; +} ttgo_adc; + +enum { + Q_EVENT_WIFI_SCAN_DONE, + Q_EVENT_WIFI_CONNECT, + Q_EVENT_BMA_INT, + Q_EVENT_AXP_INT, +} ; + +#define BMA423_INT1 39 +#define BMA423_INT2 0 + +#define WATCH_FLAG_SLEEP_MODE _BV(1) +#define WATCH_FLAG_SLEEP_EXIT _BV(2) +#define WATCH_FLAG_BMA_IRQ _BV(3) +#define WATCH_FLAG_AXP_IRQ _BV(4) + +struct TTGO_globs { + AXP20X_Class *ttgo_power = nullptr; + I2CBus *i2c = nullptr; + BMA *bma = nullptr; + QueueHandle_t g_event_queue_handle = NULL; + + EventGroupHandle_t isr_group = NULL; + bool lenergy = false; + bool bma_double_click = false; + bool bma_click = false; + bool bma_button = false; + bool power_ok = false; +} ttgo_globs; + + +void TTGO_Init(void) { + ttgo_globs.ttgo_power = new AXP20X_Class(); + ttgo_globs.i2c = new I2CBus(); + initPower(); + +#ifdef USE_BMA423 + ttgo_globs.bma = new BMA(*ttgo_globs.i2c); + if (ttgo_globs.bma->begin()) { + I2cSetActiveFound(BMA4_I2C_ADDR_SECONDARY, "BMA423"); + } else { + return; + } + + ttgo_globs.bma->attachInterrupt(); + pinMode(BMA423_INT1, INPUT); + attachInterrupt(BMA423_INT1, [] { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + EventBits_t bits = xEventGroupGetBitsFromISR(ttgo_globs.isr_group); + if (bits & WATCH_FLAG_SLEEP_MODE) + { + + xEventGroupSetBitsFromISR(ttgo_globs.isr_group, WATCH_FLAG_SLEEP_EXIT | WATCH_FLAG_BMA_IRQ, &xHigherPriorityTaskWoken); + } else + { + + uint8_t data = Q_EVENT_BMA_INT; + xQueueSendFromISR(ttgo_globs.g_event_queue_handle, &data, &xHigherPriorityTaskWoken); + } + + if (xHigherPriorityTaskWoken) + { + portYIELD_FROM_ISR (); + } + }, RISING); + struct bma423_axes_remap remap_data; + + remap_data.x_axis = 0; + remap_data.x_axis_sign = 1; + remap_data.y_axis = 1; + remap_data.y_axis_sign = 0; + remap_data.z_axis = 2; + remap_data.z_axis_sign = 1; + + ttgo_globs.bma->set_remap_axes(&remap_data); + + ttgo_globs.bma->enableWakeupInterrupt(true); + ttgo_globs.bma->enableAnyNoMotionInterrupt(true); + ttgo_globs.bma->enableAccel(); +#endif +} + +void initPower(void) { + int ret = ttgo_globs.ttgo_power->begin(axpReadBytes, axpWriteBytes); + if (ret == AXP_FAIL) { + + + } else { + I2cSetActiveFound(AXP202_SLAVE_ADDRESS, "AXP202"); + ttgo_globs.power_ok = true; + + + + ttgo_globs.ttgo_power->setShutdownTime(AXP_POWER_OFF_TIME_4S); + + ttgo_globs.ttgo_power->setChgLEDMode(AXP20X_LED_OFF); + + ttgo_globs.ttgo_power->setPowerOutPut(AXP202_EXTEN, false); + + ttgo_globs.ttgo_power->setChargeControlCur(300); + + ttgo_globs.ttgo_power->adc1Enable(AXP202_VBUS_VOL_ADC1 | AXP202_VBUS_CUR_ADC1 | AXP202_BATT_CUR_ADC1 | AXP202_BATT_VOL_ADC1, true); + + ttgo_globs.ttgo_power->adc2Enable(AXP202_TEMP_MONITORING_ADC2, true); + + ttgo_globs.ttgo_power->enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ, AXP202_ON); + ttgo_globs.ttgo_power->clearIRQ(); + + ttgo_globs.ttgo_power->setPowerOutPut(AXP202_LDO2, AXP202_ON); + + ttgo_globs.isr_group = xEventGroupCreate(); + ttgo_globs.g_event_queue_handle = xQueueCreate(20, sizeof(uint8_t)); + + + pinMode(AXP202_INT, INPUT); + attachInterrupt(AXP202_INT, [] { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + EventBits_t bits = xEventGroupGetBitsFromISR(ttgo_globs.isr_group); + if (bits & WATCH_FLAG_SLEEP_MODE) { + + xEventGroupSetBitsFromISR(ttgo_globs.isr_group, WATCH_FLAG_SLEEP_EXIT | WATCH_FLAG_AXP_IRQ, &xHigherPriorityTaskWoken); + } else { + + uint8_t data = Q_EVENT_AXP_INT; + xQueueSendFromISR(ttgo_globs.g_event_queue_handle, &data, &xHigherPriorityTaskWoken); + } + if (xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR (); + } + }, FALLING); + + } + +} + +static uint8_t axpWriteBytes(uint8_t devAddress, uint8_t regAddress, uint8_t *data, uint8_t len) { + ttgo_globs.i2c->writeBytes(devAddress, regAddress, data, len); + return 0; +} + +static uint8_t axpReadBytes(uint8_t devAddress, uint8_t regAddress, uint8_t *data, uint8_t len) { + ttgo_globs.i2c->readBytes(devAddress, regAddress, data, len); + return 0; +} + + +void TTGO_GetADC(void) { + ttgo_adc.vbus_v = ttgo_globs.ttgo_power->getVbusVoltage(); + ttgo_adc.vbus_c = ttgo_globs.ttgo_power->getVbusCurrent(); + ttgo_adc.batt_v = ttgo_globs.ttgo_power->getBattVoltage(); + ttgo_adc.per = ttgo_globs.ttgo_power->getBattPercentage(); + ttgo_adc.batt_c = ttgo_globs.ttgo_power->getBattDischargeCurrent(); + ttgo_adc.temp = ttgo_globs.ttgo_power->getTemp(); +} + +#ifdef USE_WEBSERVER +const char HTTP_TTGO[] PROGMEM = + "{s}TTGO " "VBUS Voltage" "{m}%s mV" "{e}" + "{s}TTGO " "VBUS Current" "{m}%s mA" "{e}" + "{s}TTGO " "BATT Voltage" "{m}%s mV" "{e}" + "{s}TTGO " "BATT Current" "{m}%s mA" "{e}" + "{s}TTGO " "BATT Percentage" "{m}%d %%" "{e}" + "{s}TTGO " "Temperature" "{m}%s C" "{e}"; +#ifdef USE_BMA423 +const char HTTP_TTGO_BMA[] PROGMEM = + "{s}TTGO " "BMA x" "{m}%d mg" "{e}" + "{s}TTGO " "BMA y" "{m}%d mg" "{e}" + "{s}TTGO " "BMA z" "{m}%d mg" "{e}"; +#endif +#endif + + +void TTGO_WebShow(uint32_t json) { + + if (ttgo_globs.power_ok == false) return; + + TTGO_GetADC(); + + char vstring[32]; + char cstring[32]; + char bvstring[32]; + char bcstring[32]; + char tstring[32]; + dtostrfd(ttgo_adc.vbus_v,2,vstring); + dtostrfd(ttgo_adc.vbus_c,2,cstring); + dtostrfd(ttgo_adc.batt_v,2,bvstring); + dtostrfd(ttgo_adc.batt_c,2,bcstring); + dtostrfd(ttgo_adc.temp,2,tstring); + +#ifdef USE_BMA423 + Accel acc; + bool res = ttgo_globs.bma->getAccel(acc); +#endif + + if (json) { + ResponseAppend_P(PSTR(",\"TTGO_WATCH\":{\"VBV\":%s,\"VBC\":%s,\"BV\":%s,\"BC\":%s,\"BP\":%d,\"CT\":%s"), + vstring, cstring, bvstring, bcstring, ttgo_adc.per, tstring); +#ifdef USE_BMA423 + ResponseAppend_P(PSTR(",\"BMAX\":%d,\"BMAY\":%d,\"BMAZ\":%d"),acc.x,acc.y,acc.z); +#endif + ResponseJsonEnd(); + } else { + WSContentSend_PD(HTTP_TTGO,vstring,cstring,bvstring,bcstring,ttgo_adc.per,tstring); +#ifdef USE_BMA423 + WSContentSend_PD(HTTP_TTGO_BMA,acc.x,acc.y,acc.z); +#endif + } +} + + +void enableLDO3(bool en = true) { + if (!ttgo_globs.ttgo_power) return; + ttgo_globs.ttgo_power->setLDO3Mode(1); + ttgo_globs.ttgo_power->setPowerOutPut(AXP202_LDO3, en); +} + +void TTGO_audio_power(bool power) { + enableLDO3(power); +} + +const char TTGO_Commands[] PROGMEM = "TTGO|" + "LSLP"; + +void (* const TTTGO_Command[])(void) PROGMEM = { + &TTGO_LightSleep}; + +void TTGO_LightSleep(void) { +int32_t ttgo_sleeptime; + + + if ((abs(XdrvMailbox.payload) >= 10) && (abs(XdrvMailbox.payload) <= 3600*24)) { + ttgo_sleeptime = XdrvMailbox.payload; + } else { + ttgo_sleeptime = 0; + } + + ResponseCmndNumber(ttgo_sleeptime); + + TTGO_Sleep(ttgo_sleeptime); + +} + +void TTGO_Sleep(int32_t stime) { +int32_t ttgo_sleeptime; + + ttgo_sleeptime = stime; + +#ifdef USE_DISPLAY + DisplayOnOff(0); +#endif + if (ttgo_sleeptime>=0) { + + WifiShutdown(); + SettingsSaveAll(); + RtcSettingsSave(); + ttgo_globs.lenergy = true; + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_2M); + xEventGroupSetBits(ttgo_globs.isr_group, WATCH_FLAG_SLEEP_MODE); + gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL); + gpio_wakeup_enable ((gpio_num_t)BMA423_INT1, GPIO_INTR_HIGH_LEVEL); + esp_sleep_enable_gpio_wakeup(); + if (ttgo_sleeptime) { + esp_sleep_enable_timer_wakeup(ttgo_sleeptime * 1000000); + } + esp_light_sleep_start(); + } else { + ttgo_globs.ttgo_power->setPowerOutPut(0xFF, false); + Settings.deepsleep = -ttgo_sleeptime; +#ifdef USE_DEEPSLEEP + RtcSettings.nextwakeup = 0; + deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; + if (deepsleep_flag) { + if (!Settings.tele_period) { + Settings.tele_period = TELE_PERIOD; + } + } +#endif + return; + } + + if (ttgo_sleeptime) { + ttgo_globs.lenergy = false; + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); +#ifdef USE_DISPLAY + DisplayOnOff(1); +#endif + } else { + while (ttgo_globs.lenergy == true) { + TTGO_loop(0); + OsWatchLoop(); + } + } +} + + +void TTGO_loop(uint32_t flg) { +bool rlst; +uint8_t data; + + if (!flg) { + + EventBits_t bits = xEventGroupGetBits(ttgo_globs.isr_group); + if (bits & WATCH_FLAG_SLEEP_EXIT) { + if (ttgo_globs.lenergy) { + ttgo_globs.lenergy = false; + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M); +#ifdef USE_DISPLAY + DisplayOnOff(1); +#endif + } + +#ifdef USE_BMA423 + if (bits & WATCH_FLAG_BMA_IRQ) { + do { + rlst = ttgo_globs.bma->readInterrupt(); + } while (!rlst); + xEventGroupClearBits(ttgo_globs.isr_group, WATCH_FLAG_BMA_IRQ); + } +#endif + + if (bits & WATCH_FLAG_AXP_IRQ) { + ttgo_globs.ttgo_power->readIRQ(); + ttgo_globs.ttgo_power->clearIRQ(); + xEventGroupClearBits(ttgo_globs.isr_group, WATCH_FLAG_AXP_IRQ); + } + xEventGroupClearBits(ttgo_globs.isr_group, WATCH_FLAG_SLEEP_EXIT); + xEventGroupClearBits(ttgo_globs.isr_group, WATCH_FLAG_SLEEP_MODE); + } + } else { + + if (xQueueReceive(ttgo_globs.g_event_queue_handle, &data, 5 / portTICK_RATE_MS) == pdPASS) { + switch (data) { +#ifdef USE_BMA423 + case Q_EVENT_BMA_INT: + + do { + rlst = ttgo_globs.bma->readInterrupt(); + } while (!rlst); + + if (ttgo_globs.bma->isDoubleClick()) { + ttgo_globs.bma_double_click = true; + + } + if (ttgo_globs.bma->isAnyNoMotion()) { + ttgo_globs.bma_click = true; + + } + + + if (ttgo_globs.bma->isStepCounter()) { + + } + break; +#endif + case Q_EVENT_AXP_INT: + + ttgo_globs.ttgo_power->readIRQ(); + if (ttgo_globs.ttgo_power->isVbusPlugInIRQ()) { + + + } + if (ttgo_globs.ttgo_power->isVbusRemoveIRQ()) { + + + } + if (ttgo_globs.ttgo_power->isChargingDoneIRQ()) { + + + } + if (ttgo_globs.ttgo_power->isPEKShortPressIRQ()) { + ttgo_globs.bma_button = true; + + } + ttgo_globs.ttgo_power->clearIRQ(); + break; + default: + break; + } + } + } +} + +bool TTGO_doubleclick(void) { + bool retval = ttgo_globs.bma_double_click; + ttgo_globs.bma_double_click = false; + return retval; +} + +bool TTGO_button(void) { + bool retval = ttgo_globs.bma_button; + ttgo_globs.bma_button = false; + return retval; +} + + + + + +bool Xdrv83(uint8_t function) { + bool result = false; + + switch (function) { + + case FUNC_WEB_SENSOR: +#ifdef USE_WEBSERVER + TTGO_WebShow(0); +#endif + break; + case FUNC_JSON_APPEND: + TTGO_WebShow(1); + break; + case FUNC_COMMAND: + result = DecodeCommand(TTGO_Commands, TTTGO_Command); + break; + case FUNC_INIT: + TTGO_Init(); + break; + case FUNC_LOOP: + TTGO_loop(1); + break; + + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_99_debug.ino" +# 22 "/workspace/Tasmota/tasmota/xdrv_99_debug.ino" +#ifdef DEBUG_THEO +#ifndef USE_DEBUG_DRIVER +#define USE_DEBUG_DRIVER +#endif +#endif + +#ifdef USE_DEBUG_DRIVER + + + + + + +#define XDRV_99 99 + +#ifndef CPU_LOAD_CHECK +#define CPU_LOAD_CHECK 1 +#endif + + + + + +#define D_CMND_CFGDUMP "CfgDump" +#define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGPOKE "CfgPoke" +#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CPUCHECK "CpuChk" +#define D_CMND_EXCEPTION "Exception" +#define D_CMND_FLASHDUMP "FlashDump" +#define D_CMND_FLASHMODE "FlashMode" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_HELP "Help" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_I2CWRITE "I2CWrite" +#define D_CMND_I2CREAD "I2CRead" +#define D_CMND_I2CSTRETCH "I2CStretch" +#define D_CMND_I2CCLOCK "I2CClock" +#define D_CMND_SERBUFF "SerBufSize" + +const char kDebugCommands[] PROGMEM = "|" + D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" +#ifdef USE_WEBSERVER + D_CMND_CFGXOR "|" +#endif + D_CMND_CPUCHECK "|" D_CMND_SERBUFF "|" +#ifdef DEBUG_THEO + D_CMND_EXCEPTION "|" +#endif + D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" +#ifdef USE_I2C + D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK +#endif + ; + +void (* const DebugCommand[])(void) PROGMEM = { + &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, +#ifdef USE_WEBSERVER + &CmndCfgXor, +#endif + &CmndCpuCheck, &CmndSerBufSize, +#ifdef DEBUG_THEO + &CmndException, +#endif + &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, +#ifdef USE_I2C + &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock +#endif + }; + +uint32_t CPU_loops = 0; +uint32_t CPU_last_millis = 0; +uint32_t CPU_last_loop_time = 0; +uint8_t CPU_load_check = 0; +uint8_t CPU_show_freemem = 0; + + + +#ifdef DEBUG_THEO +void ExceptionTest(uint8_t type) +{ +# 145 "/workspace/Tasmota/tasmota/xdrv_99_debug.ino" + if (1 == type) { + char svalue[10]; + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); + } +# 159 "/workspace/Tasmota/tasmota/xdrv_99_debug.ino" + if (2 == type) { + while(1) delay(1000); + } +} + +#endif + + + +void CpuLoadLoop(void) +{ + CPU_last_loop_time = millis(); + if (CPU_load_check && CPU_last_millis) { + CPU_loops ++; + if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { +#if defined(F_CPU) && (F_CPU == 160000000L) + int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *800) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#else + int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *400) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#endif + CPU_last_millis = CPU_last_loop_time; + CPU_loops = 0; + } + } +} + + + +#ifdef ESP8266 + + + + +extern "C" { +#include + extern cont_t* g_pcont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); +} + +#else + +void DebugFreeMem(void) +{ + register uint8_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data); +} + +#endif + + + +void DebugRtcDump(char* parms) +{ +#ifdef ESP8266 + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; +# 239 "/workspace/Tasmota/tasmota/xdrv_99_debug.ino" + uint8_t buffer[768]; + + system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); + + maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + } +#endif +} + + + +void DebugCfgDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + uint8_t *buffer = (uint8_t *) &Settings; + maxrow = ((sizeof(Settings)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + delay(1); + } +} + +void DebugCfgPeek(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(Settings)) address = sizeof(Settings) -4; + address = (address >> 2) << 2; + + uint8_t *buffer = (uint8_t *) &Settings; + uint8_t data8 = buffer[address]; + uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; + + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); + AddLog(LOG_LEVEL_INFO); +} + +void DebugCfgPoke(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(Settings)) address = sizeof(Settings) -4; + address = (address >> 2) << 2; + + uint32_t data = strtol(p, &p, 16); + + uint8_t *buffer = (uint8_t *) &Settings; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + uint8_t *nbuffer = (uint8_t *) &data; + for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } + + uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); +} + +void SetFlashMode(uint8_t mode) +{ +#ifdef ESP8266 + uint8_t *_buffer; + uint32_t address; + + address = 0; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != mode) { + _buffer[2] = mode; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + } + delete[] _buffer; +#endif +} + + + + + +void CmndHelp(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); + ResponseCmndDone(); +} + +void CmndRtcDump(void) +{ + DebugRtcDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgDump(void) +{ + DebugCfgDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPeek(void) +{ + DebugCfgPeek(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPoke(void) +{ + DebugCfgPoke(XdrvMailbox.data); + ResponseCmndDone(); +} + +#ifdef USE_WEBSERVER +void CmndCfgXor(void) +{ + if (XdrvMailbox.data_len > 0) { + Web.config_xor_on_set = XdrvMailbox.payload; + } + char temp[10]; + snprintf_P(temp, sizeof(temp), PSTR("0x%02X"), Web.config_xor_on_set); + ResponseCmndChar(temp); +} +#endif + +#ifdef DEBUG_THEO +void CmndException(void) +{ + if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } + ResponseCmndDone(); +} +#endif + +void CmndCpuCheck(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_load_check = XdrvMailbox.payload; + CPU_last_millis = CPU_last_loop_time; + } + ResponseCmndNumber(CPU_load_check); +} + +void CmndSerBufSize(void) +{ + if (XdrvMailbox.data_len > 0) { + Serial.setRxBufferSize(XdrvMailbox.payload); + } +#ifdef ESP8266 + ResponseCmndNumber(Serial.getRxBufferSize()); +#else + ResponseCmndDone(); +#endif +} + +void CmndFreemem(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + ResponseCmndNumber(CPU_show_freemem); +} + +void CmndSetSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + if (1 == XdrvMailbox.payload) { + restart_flag = 2; + } + } + Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); + } +} + +void CmndFlashMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + SetFlashMode(XdrvMailbox.payload); + } + ResponseCmndNumber(ESP.getFlashChipMode()); +} + +uint32_t DebugSwap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} + +void CmndFlashDump(void) +{ +#ifdef ESP8266 + + + + const uint32_t flash_start = 0x40200000; + const uint8_t bytes_per_cols = 0x20; + const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; + + uint32_t start = flash_start; + uint32_t rows = 8; + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { + start += (XdrvMailbox.payload &0x7FFFFFFC); + + char *p; + uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); + rows = strtol(p, &p, 10); + if (0 == rows) { rows = 8; } + } + uint32_t end = start + (rows * bytes_per_cols); + if ((end - flash_start) > max) { + end = flash_start + max; + } + + for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { + uint32_t* values = (uint32_t*)(pos); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, + DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), + DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); + } + ResponseCmndDone(); +#endif +} + +#ifdef USE_I2C +void CmndI2cWrite(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 1) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + + Wire.beginTransmission(buffer[0]); + for (uint32_t i = 1; i < index; i++) { + Wire.write(buffer[i]); + } + int result = Wire.endTransmission(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); + } + } + ResponseCmndDone(); +} + +void CmndI2cRead(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 0) { + uint8_t size = 1; + if (index > 1) { + size = buffer[1]; + } + Wire.requestFrom(buffer[0], size); + index = 0; + while (Wire.available() && index < sizeof(buffer)) { + buffer[index++] = Wire.read(); + } + if (index > 0) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + } + } + } + ResponseCmndDone(); +} + +void CmndI2cStretch(void) +{ +#ifdef ESP8266 + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClockStretchLimit(XdrvMailbox.payload); + } + ResponseCmndDone(); +#endif +} + +void CmndI2cClock(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClock(XdrvMailbox.payload); + } + ResponseCmndDone(); +} +#endif + + + + + +bool Xdrv99(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + CpuLoadLoop(); + break; + case FUNC_FREE_MEM: + if (CPU_show_freemem) { DebugFreeMem(); } + break; + case FUNC_PRE_INIT: + CPU_last_millis = millis(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kDebugCommands, DebugCommand); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xdrv_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xdrv_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdrv_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDRV_01 + &Xdrv01, +#endif + +#ifdef XDRV_02 + &Xdrv02, +#endif + +#ifdef XDRV_03 + &Xdrv03, +#endif + +#ifdef XDRV_04 + &Xdrv04, +#endif + +#ifdef XDRV_05 + &Xdrv05, +#endif + +#ifdef XDRV_06 + &Xdrv06, +#endif + +#ifdef XDRV_07 + &Xdrv07, +#endif + +#ifdef XDRV_08 + &Xdrv08, +#endif + +#ifdef XDRV_09 + &Xdrv09, +#endif + +#ifdef XDRV_10 + &Xdrv10, +#endif + +#ifdef XDRV_11 + &Xdrv11, +#endif + +#ifdef XDRV_12 + &Xdrv12, +#endif + +#ifdef XDRV_13 + &Xdrv13, +#endif + +#ifdef XDRV_14 + &Xdrv14, +#endif + +#ifdef XDRV_15 + &Xdrv15, +#endif + +#ifdef XDRV_16 + &Xdrv16, +#endif + +#ifdef XDRV_17 + &Xdrv17, +#endif + +#ifdef XDRV_18 + &Xdrv18, +#endif + +#ifdef XDRV_19 + &Xdrv19, +#endif + +#ifdef XDRV_20 + &Xdrv20, +#endif + +#ifdef XDRV_21 + &Xdrv21, +#endif + +#ifdef XDRV_22 + &Xdrv22, +#endif + +#ifdef XDRV_23 + &Xdrv23, +#endif + +#ifdef XDRV_24 + &Xdrv24, +#endif + +#ifdef XDRV_25 + &Xdrv25, +#endif + +#ifdef XDRV_26 + &Xdrv26, +#endif + +#ifdef XDRV_27 + &Xdrv27, +#endif + +#ifdef XDRV_28 + &Xdrv28, +#endif + +#ifdef XDRV_29 + &Xdrv29, +#endif + +#ifdef XDRV_30 + &Xdrv30, +#endif + +#ifdef XDRV_31 + &Xdrv31, +#endif + +#ifdef XDRV_32 + &Xdrv32, +#endif + +#ifdef XDRV_33 + &Xdrv33, +#endif + +#ifdef XDRV_34 + &Xdrv34, +#endif + +#ifdef XDRV_35 + &Xdrv35, +#endif + +#ifdef XDRV_36 + &Xdrv36, +#endif + +#ifdef XDRV_37 + &Xdrv37, +#endif + +#ifdef XDRV_38 + &Xdrv38, +#endif + +#ifdef XDRV_39 + &Xdrv39, +#endif + +#ifdef XDRV_40 + &Xdrv40, +#endif + +#ifdef XDRV_41 + &Xdrv41, +#endif + +#ifdef XDRV_42 + &Xdrv42, +#endif + +#ifdef XDRV_43 + &Xdrv43, +#endif + +#ifdef XDRV_44 + &Xdrv44, +#endif + +#ifdef XDRV_45 + &Xdrv45, +#endif + +#ifdef XDRV_46 + &Xdrv46, +#endif + +#ifdef XDRV_47 + &Xdrv47, +#endif + +#ifdef XDRV_48 + &Xdrv48, +#endif + +#ifdef XDRV_49 + &Xdrv49, +#endif + +#ifdef XDRV_50 + &Xdrv50, +#endif + +#ifdef XDRV_51 + &Xdrv51, +#endif + +#ifdef XDRV_52 + &Xdrv52, +#endif + +#ifdef XDRV_53 + &Xdrv53, +#endif + +#ifdef XDRV_54 + &Xdrv54, +#endif + +#ifdef XDRV_55 + &Xdrv55, +#endif + +#ifdef XDRV_56 + &Xdrv56, +#endif + +#ifdef XDRV_57 + &Xdrv57, +#endif + +#ifdef XDRV_58 + &Xdrv58, +#endif + +#ifdef XDRV_59 + &Xdrv59, +#endif + +#ifdef XDRV_60 + &Xdrv60, +#endif + +#ifdef XDRV_61 + &Xdrv61, +#endif + +#ifdef XDRV_62 + &Xdrv62, +#endif + +#ifdef XDRV_63 + &Xdrv63, +#endif + +#ifdef XDRV_64 + &Xdrv64, +#endif + +#ifdef XDRV_65 + &Xdrv65, +#endif + +#ifdef XDRV_66 + &Xdrv66, +#endif + +#ifdef XDRV_67 + &Xdrv67, +#endif + +#ifdef XDRV_68 + &Xdrv68, +#endif + +#ifdef XDRV_69 + &Xdrv69, +#endif + +#ifdef XDRV_70 + &Xdrv70, +#endif + +#ifdef XDRV_71 + &Xdrv71, +#endif + +#ifdef XDRV_72 + &Xdrv72, +#endif + +#ifdef XDRV_73 + &Xdrv73, +#endif + +#ifdef XDRV_74 + &Xdrv74, +#endif + +#ifdef XDRV_75 + &Xdrv75, +#endif + +#ifdef XDRV_76 + &Xdrv76, +#endif + +#ifdef XDRV_77 + &Xdrv77, +#endif + +#ifdef XDRV_78 + &Xdrv78, +#endif + +#ifdef XDRV_79 + &Xdrv79, +#endif + +#ifdef XDRV_80 + &Xdrv80, +#endif + +#ifdef XDRV_81 + &Xdrv81, +#endif + +#ifdef XDRV_82 + &Xdrv82, +#endif + +#ifdef XDRV_83 + &Xdrv83, +#endif + +#ifdef XDRV_84 + &Xdrv84, +#endif + +#ifdef XDRV_85 + &Xdrv85, +#endif + +#ifdef XDRV_86 + &Xdrv86, +#endif + +#ifdef XDRV_87 + &Xdrv87, +#endif + +#ifdef XDRV_88 + &Xdrv88, +#endif + +#ifdef XDRV_89 + &Xdrv89, +#endif + +#ifdef XDRV_90 + &Xdrv90, +#endif + +#ifdef XDRV_91 + &Xdrv91, +#endif + +#ifdef XDRV_92 + &Xdrv92, +#endif + +#ifdef XDRV_93 + &Xdrv93, +#endif + +#ifdef XDRV_94 + &Xdrv94, +#endif + +#ifdef XDRV_95 + &Xdrv95, +#endif + +#ifdef XDRV_96 + &Xdrv96, +#endif + +#ifdef XDRV_97 + &Xdrv97, +#endif + +#ifdef XDRV_98 + &Xdrv98, +#endif + +#ifdef XDRV_99 + &Xdrv99 +#endif +}; + +const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXdrvList[] PROGMEM = { +#else +const uint8_t kXdrvList[] = { +#endif + +#ifdef XDRV_01 + XDRV_01, +#endif + +#ifdef XDRV_02 + XDRV_02, +#endif + +#ifdef XDRV_03 + XDRV_03, +#endif + +#ifdef XDRV_04 + XDRV_04, +#endif + +#ifdef XDRV_05 + XDRV_05, +#endif + +#ifdef XDRV_06 + XDRV_06, +#endif + +#ifdef XDRV_07 + XDRV_07, +#endif + +#ifdef XDRV_08 + XDRV_08, +#endif + +#ifdef XDRV_09 + XDRV_09, +#endif + +#ifdef XDRV_10 + XDRV_10, +#endif + +#ifdef XDRV_11 + XDRV_11, +#endif + +#ifdef XDRV_12 + XDRV_12, +#endif + +#ifdef XDRV_13 + XDRV_13, +#endif + +#ifdef XDRV_14 + XDRV_14, +#endif + +#ifdef XDRV_15 + XDRV_15, +#endif + +#ifdef XDRV_16 + XDRV_16, +#endif + +#ifdef XDRV_17 + XDRV_17, +#endif + +#ifdef XDRV_18 + XDRV_18, +#endif + +#ifdef XDRV_19 + XDRV_19, +#endif + +#ifdef XDRV_20 + XDRV_20, +#endif + +#ifdef XDRV_21 + XDRV_21, +#endif + +#ifdef XDRV_22 + XDRV_22, +#endif + +#ifdef XDRV_23 + XDRV_23, +#endif + +#ifdef XDRV_24 + XDRV_24, +#endif + +#ifdef XDRV_25 + XDRV_25, +#endif + +#ifdef XDRV_26 + XDRV_26, +#endif + +#ifdef XDRV_27 + XDRV_27, +#endif + +#ifdef XDRV_28 + XDRV_28, +#endif + +#ifdef XDRV_29 + XDRV_29, +#endif + +#ifdef XDRV_30 + XDRV_30, +#endif + +#ifdef XDRV_31 + XDRV_31, +#endif + +#ifdef XDRV_32 + XDRV_32, +#endif + +#ifdef XDRV_33 + XDRV_33, +#endif + +#ifdef XDRV_34 + XDRV_34, +#endif + +#ifdef XDRV_35 + XDRV_35, +#endif + +#ifdef XDRV_36 + XDRV_36, +#endif + +#ifdef XDRV_37 + XDRV_37, +#endif + +#ifdef XDRV_38 + XDRV_38, +#endif + +#ifdef XDRV_39 + XDRV_39, +#endif + +#ifdef XDRV_40 + XDRV_40, +#endif + +#ifdef XDRV_41 + XDRV_41, +#endif + +#ifdef XDRV_42 + XDRV_42, +#endif + +#ifdef XDRV_43 + XDRV_43, +#endif + +#ifdef XDRV_44 + XDRV_44, +#endif + +#ifdef XDRV_45 + XDRV_45, +#endif + +#ifdef XDRV_46 + XDRV_46, +#endif + +#ifdef XDRV_47 + XDRV_47, +#endif + +#ifdef XDRV_48 + XDRV_48, +#endif + +#ifdef XDRV_49 + XDRV_49, +#endif + +#ifdef XDRV_50 + XDRV_50, +#endif + +#ifdef XDRV_51 + XDRV_51, +#endif + +#ifdef XDRV_52 + XDRV_52, +#endif + +#ifdef XDRV_53 + XDRV_53, +#endif + +#ifdef XDRV_54 + XDRV_54, +#endif + +#ifdef XDRV_55 + XDRV_55, +#endif + +#ifdef XDRV_56 + XDRV_56, +#endif + +#ifdef XDRV_57 + XDRV_57, +#endif + +#ifdef XDRV_58 + XDRV_58, +#endif + +#ifdef XDRV_59 + XDRV_59, +#endif + +#ifdef XDRV_60 + XDRV_60, +#endif + +#ifdef XDRV_61 + XDRV_61, +#endif + +#ifdef XDRV_62 + XDRV_62, +#endif + +#ifdef XDRV_63 + XDRV_63, +#endif + +#ifdef XDRV_64 + XDRV_64, +#endif + +#ifdef XDRV_65 + XDRV_65, +#endif + +#ifdef XDRV_66 + XDRV_66, +#endif + +#ifdef XDRV_67 + XDRV_67, +#endif + +#ifdef XDRV_68 + XDRV_68, +#endif + +#ifdef XDRV_69 + XDRV_69, +#endif + +#ifdef XDRV_70 + XDRV_70, +#endif + +#ifdef XDRV_71 + XDRV_71, +#endif + +#ifdef XDRV_72 + XDRV_72, +#endif + +#ifdef XDRV_73 + XDRV_73, +#endif + +#ifdef XDRV_74 + XDRV_74, +#endif + +#ifdef XDRV_75 + XDRV_75, +#endif + +#ifdef XDRV_76 + XDRV_76, +#endif + +#ifdef XDRV_77 + XDRV_77, +#endif + +#ifdef XDRV_78 + XDRV_78, +#endif + +#ifdef XDRV_79 + XDRV_79, +#endif + +#ifdef XDRV_80 + XDRV_80, +#endif + +#ifdef XDRV_81 + XDRV_81, +#endif + +#ifdef XDRV_82 + XDRV_82, +#endif + +#ifdef XDRV_83 + XDRV_83, +#endif + +#ifdef XDRV_84 + XDRV_84, +#endif + +#ifdef XDRV_85 + XDRV_85, +#endif + +#ifdef XDRV_86 + XDRV_86, +#endif + +#ifdef XDRV_87 + XDRV_87, +#endif + +#ifdef XDRV_88 + XDRV_88, +#endif + +#ifdef XDRV_89 + XDRV_89, +#endif + +#ifdef XDRV_90 + XDRV_90, +#endif + +#ifdef XDRV_91 + XDRV_91, +#endif + +#ifdef XDRV_92 + XDRV_92, +#endif + +#ifdef XDRV_93 + XDRV_93, +#endif + +#ifdef XDRV_94 + XDRV_94, +#endif + +#ifdef XDRV_95 + XDRV_95, +#endif + +#ifdef XDRV_96 + XDRV_96, +#endif + +#ifdef XDRV_97 + XDRV_97, +#endif + +#ifdef XDRV_98 + XDRV_98, +#endif + +#ifdef XDRV_99 + XDRV_99 +#endif +}; + + + +void XsnsDriverState(void) +{ + ResponseAppend_P(PSTR(",\"Drivers\":\"")); + for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t driverid = pgm_read_byte(kXdrvList + i); +#else + uint32_t driverid = kXdrvList[i]; +#endif + ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); + } + ResponseAppend_P(PSTR("\"")); +} + + + +bool XdrvRulesProcess(void) +{ + return XdrvCallDriver(10, FUNC_RULES_PROCESS); +} + +#ifdef USE_DEBUG_DRIVER +void ShowFreeMem(const char *where) +{ + char stemp[30]; + snprintf_P(stemp, sizeof(stemp), where); + XdrvMailbox.data = stemp; + XdrvCall(FUNC_FREE_MEM); +} +#endif + + + + + +bool XdrvCallDriver(uint32_t driver, uint8_t Function) +{ + for (uint32_t x = 0; x < xdrv_present; x++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t listed = pgm_read_byte(kXdrvList + x); +#else + uint32_t listed = kXdrvList[x]; +#endif + if (driver == listed) { + return xdrv_func_ptr[x](Function); + } + } + return false; +} + + + + + +bool XdrvCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DRV: %d"), Function); + + for (uint32_t x = 0; x < xdrv_present; x++) { + result = xdrv_func_ptr[x](Function); + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_COMMAND_DRIVER == Function) || + (FUNC_MQTT_DATA == Function) || + (FUNC_RULES_PROCESS == Function) || + (FUNC_BUTTON_PRESSED == Function) || + (FUNC_SERIAL == Function) || + (FUNC_MODULE_INIT == Function) || + (FUNC_SET_CHANNELS == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_SET_DEVICE_POWER == Function) + )) { + break; + } + } + + return result; +} +# 1 "/workspace/Tasmota/tasmota/xdsp_01_lcd.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_01_lcd.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_LCD + +#define XDSP_01 1 +#define XI2C_03 3 + +#define LCD_ADDRESS1 0x27 +#define LCD_ADDRESS2 0x3F + +#include +#include + +LiquidCrystal_I2C *lcd; + + + +void LcdInitMode(void) +{ + lcd->init(); + lcd->clear(); +} + +void LcdInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + LcdInitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayClearScreenBuffer(); +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void LcdInitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(LCD_ADDRESS1)) { + Settings.display_address[0] = LCD_ADDRESS1; + Settings.display_model = XDSP_01; + } + else if (I2cSetDevice(LCD_ADDRESS2)) { + Settings.display_address[0] = LCD_ADDRESS2; + Settings.display_model = XDSP_01; + } + } + + if (XDSP_01 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "LCD"); + + Settings.display_width = Settings.display_cols[0]; + Settings.display_height = Settings.display_rows; + lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); + +#ifdef USE_DISPLAY_MODES1TO5 + DisplayAllocScreenBuffer(); +#endif + + LcdInitMode(); + } +} + +void LcdDrawStringAt(void) +{ + if (dsp_flag) { + dsp_x--; + dsp_y--; + } + lcd->setCursor(dsp_x, dsp_y); + lcd->print(dsp_str); +} + +void LcdDisplayOnOff() +{ + if (disp_power) { + lcd->backlight(); + } else { + lcd->noBacklight(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void LcdCenter(uint8_t row, char* txt) +{ + char line[Settings.display_cols[0] +2]; + + int len = strlen(txt); + int offset = 0; + if (len >= Settings.display_cols[0]) { + len = Settings.display_cols[0]; + } else { + offset = (Settings.display_cols[0] - len) / 2; + } + memset(line, 0x20, Settings.display_cols[0]); + line[Settings.display_cols[0]] = 0; + for (uint32_t i = 0; i < len; i++) { + line[offset +i] = txt[i]; + } + lcd->setCursor(0, row); + lcd->print(line); +} + +bool LcdPrintLog(void) +{ + bool result = false; + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\337'); + if (txt != nullptr) { + uint8_t last_row = Settings.display_rows -1; + + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + lcd->setCursor(0, i); + lcd->print(disp_screen_buffer[i +1]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + lcd->setCursor(0, last_row); + lcd->print(disp_screen_buffer[last_row]); + + result = true; + } + } + return result; +} + +void LcdTime(void) +{ + char line[Settings.display_cols[0] +1]; + + snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + LcdCenter(0, line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + LcdCenter(1, line); +} + +void LcdRefresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + LcdTime(); + break; + case 2: + case 4: + LcdPrintLog(); + break; + case 3: + case 5: { + if (!LcdPrintLog()) { LcdTime(); } + break; + } + } + } +} + +#endif + + + + + +bool Xdsp01(uint8_t function) +{ + if (!I2cEnabled(XI2C_03)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + LcdInitDriver(); + } + else if (XDSP_01 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + LcdInit(dsp_init); + break; + case FUNC_DISPLAY_POWER: + LcdDisplayOnOff(); + break; + case FUNC_DISPLAY_CLEAR: + lcd->clear(); + break; +# 238 "/workspace/Tasmota/tasmota/xdsp_01_lcd.ino" + case FUNC_DISPLAY_DRAW_STRING: + LcdDrawStringAt(); + break; + + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + LcdRefresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_02_ssd1306.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_02_ssd1306.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1306 + +#define XDSP_02 2 +#define XI2C_04 4 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SSD1306 *oled1306; + +extern uint8_t *buffer; + + + +void SSD1306InitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_02; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_02; + } + } + + if (XDSP_02 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SSD1306"); + + if ((Settings.display_width != 64) && (Settings.display_width != 96) && (Settings.display_width != 128)) { + Settings.display_width = 128; + } + if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 48) && (Settings.display_height != 64)) { + Settings.display_height = 64; + } + + uint8_t reset_pin = -1; + if (PinUsed(GPIO_OLED_RESET)) { + reset_pin = Pin(GPIO_OLED_RESET); + } + + + if (buffer) { free(buffer); } + buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); + if (!buffer) { return; } + + + + oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); + oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], reset_pin >= 0); + renderer = oled1306; + renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SSD1306")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + + } +} + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ssd1306PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void Ssd1306Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void Ssd1306Refresh(void) +{ + if (!renderer) return; + + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + Ssd1306Time(); + break; + case 2: + case 3: + case 4: + case 5: + Ssd1306PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp02(byte function) +{ + if (!I2cEnabled(XI2C_04)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1306InitDriver(); + } + else if (XDSP_02 == Settings.display_model) { + switch (function) { +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ssd1306Refresh(); + break; +#endif + case FUNC_DISPLAY_MODEL: + result = true; + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_03_matrix.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_03_matrix.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_MATRIX + +#define XDSP_03 3 +#define XI2C_05 5 + +#define MTX_MAX_SCREEN_BUFFER 80 + +#include +#include +#include + +Adafruit_8x8matrix *matrix[8]; +uint8_t mtx_matrices = 0; +uint8_t mtx_state = 0; +uint8_t mtx_counter = 0; +int16_t mtx_x = 0; +int16_t mtx_y = 0; + + +char *mtx_buffer = nullptr; + +uint8_t mtx_mode = 0; +uint8_t mtx_loop = 0; +uint8_t mtx_done = 0; + + + +void MatrixWrite(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->writeDisplay(); + } +} + +void MatrixClear(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + } + MatrixWrite(); +} + +void MatrixFixed(char* txt) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixCenter(char* txt) +{ + int offset; + + int len = strlen(txt); + offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-(i *8)+offset, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixScrollLeft(char* txt, int loop) +{ + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_x = 8 * mtx_matrices; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); + + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(mtx_x - i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + + mtx_x--; + int16_t len = strlen(txt); + if (mtx_x < -(len *6)) { mtx_state = loop; } + } + break; + } +} + +void MatrixScrollUp(char* txt, int loop) +{ + int wordcounter = 0; + char tmpbuf[200]; + char *words[100]; + + + + char separators[] = " /"; + + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_y = 8; + mtx_counter = 0; + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + strlcpy(tmpbuf, txt, sizeof(tmpbuf)); + char *p = strtok(tmpbuf, separators); + while (p != nullptr && wordcounter < 40) { + words[wordcounter++] = p; + p = strtok(nullptr, separators); + } + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + for (uint32_t j = 0; j < wordcounter; j++) { + matrix[i]->setCursor(-i *8, mtx_y + (j *8)); + matrix[i]->println(words[j]); + } + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + if (((mtx_y %8) == 0) && mtx_counter) { + mtx_counter--; + } else { + mtx_y--; + mtx_counter = STATES * 1; + } + if (mtx_y < -(wordcounter *8)) { mtx_state = loop; } + } + break; + } +} + + + +void MatrixInitMode(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->setRotation(Settings.display_rotate); + matrix[i]->setBrightness(Settings.display_dimmer); + matrix[i]->blinkRate(0); + matrix[i]->setTextWrap(false); + + + matrix[i]->cp437(true); + } + MatrixClear(); +} + +void MatrixInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + MatrixInitMode(); + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void MatrixInitDriver(void) +{ + mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); + if (mtx_buffer != nullptr) { + if (!Settings.display_model) { + if (I2cSetDevice(Settings.display_address[1])) { + Settings.display_model = XDSP_03; + } + } + + if (XDSP_03 == Settings.display_model) { + mtx_state = 1; + for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) { + if (Settings.display_address[mtx_matrices]) { + I2cSetActiveFound(Settings.display_address[mtx_matrices], "8x8Matrix"); + matrix[mtx_matrices] = new Adafruit_8x8matrix(); + matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]); + } else { + break; + } + } + + Settings.display_width = mtx_matrices * 8; + Settings.display_height = 8; + + MatrixInitMode(); + } + } +} + +void MatrixOnOff(void) +{ + if (!disp_power) { MatrixClear(); } +} + +void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); + mtx_mode = x &1; + mtx_loop = y &1; + if (!mtx_state) { mtx_state = 1; } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void MatrixPrintLog(uint8_t direction) +{ + char* txt = (!mtx_done) ? DisplayLogBuffer('\370') : mtx_buffer; + if (txt != nullptr) { + if (!mtx_state) { mtx_state = 1; } + + if (!mtx_done) { + + uint8_t space = 0; + uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER; + mtx_buffer[0] = '\0'; + uint8_t i = 0; + while ((txt[i] != '\0') && (i < max_cols)) { + if (txt[i] == ' ') { + space++; + } else { + space = 0; + } + if (space < 2) { + strncat(mtx_buffer, (const char*)txt +i, (strlen(mtx_buffer) < MTX_MAX_SCREEN_BUFFER -1) ? 1 : 0); + } + i++; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); + + mtx_done = 1; + } + + if (direction) { + MatrixScrollUp(mtx_buffer, 0); + } else { + MatrixScrollLeft(mtx_buffer, 0); + } + if (!mtx_state) { mtx_done = 0; } + } else { + char disp_time[9]; + + snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + MatrixFixed(disp_time); + } +} + +#endif + +void MatrixRefresh(void) +{ + if (disp_power) { + switch (Settings.display_mode) { + case 0: { + switch (mtx_mode) { + case 0: + MatrixScrollLeft(mtx_buffer, mtx_loop); + break; + case 1: + MatrixScrollUp(mtx_buffer, mtx_loop); + break; + } + break; + } +#ifdef USE_DISPLAY_MODES1TO5 + case 2: { + char disp_date[9]; + snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); + MatrixFixed(disp_date); + break; + } + case 3: { + char disp_day[10]; + snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month); + MatrixCenter(disp_day); + break; + } + case 4: + MatrixPrintLog(0); + break; + case 1: + case 5: + MatrixPrintLog(1); + break; +#endif + } + } +} + + + + + +bool Xdsp03(uint8_t function) +{ + if (!I2cEnabled(XI2C_05)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + MatrixInitDriver(); + } + else if (XDSP_03 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + MatrixInit(dsp_init); + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: + MatrixRefresh(); + break; + case FUNC_DISPLAY_POWER: + MatrixOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_04_ili9341.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_04_ili9341.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9341 + +#define XDSP_04 4 + +#define TFT_TOP 16 +#define TFT_BOTTOM 16 +#define TFT_FONT_WIDTH 6 +#define TFT_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_ILI9341 *tft; + +uint16_t tft_top = TFT_TOP; +uint16_t tft_bottom = TFT_BOTTOM; +uint16_t tft_scroll = TFT_TOP; +uint16_t tft_cols = 0; + + + +bool Ili9341Header(void) { + if (Settings.display_cols[0] != tft_cols) { + tft_cols = Settings.display_cols[0]; + if (tft_cols > 17) { + tft_top = TFT_TOP; + tft_bottom = TFT_BOTTOM; + } else { + tft_top = 0; + tft_bottom = 0; + } + tft_scroll = tft_top; + tft->setScrollMargins(tft_top, tft_bottom); + } + return (tft_cols > 17); +} + +void Ili9341InitMode(void) +{ + tft->setRotation(Settings.display_rotate); + tft->invertDisplay(0); + tft->fillScreen(ILI9341_BLACK); + tft->setTextWrap(false); + tft->cp437(true); + if (!Settings.display_mode) { + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK); + tft->setTextSize(1); + } else { + Ili9341Header(); + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); + tft->setTextSize(2); + + + } +} + +void Ili9341Init(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + Ili9341InitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayClearScreenBuffer(); + } +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void Ili9341InitDriver(void) +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_04; + } + + if (XDSP_04 == Settings.display_model) { + if (Settings.display_width != ILI9341_TFTWIDTH) { + Settings.display_width = ILI9341_TFTWIDTH; + } + if (Settings.display_height != ILI9341_TFTHEIGHT) { + Settings.display_height = ILI9341_TFTHEIGHT; + } + + tft = new Adafruit_ILI9341(Pin(GPIO_SPI_CS), Pin(GPIO_SPI_DC)); + tft->begin(); + +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayAllocScreenBuffer(); + } +#endif + + Ili9341InitMode(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("DSP: ILI9341")); + } +} + +void Ili9341Clear(void) +{ + tft->fillScreen(ILI9341_BLACK); + tft->setCursor(0, 0); +} + +void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + uint16_t active_color = ILI9341_WHITE; + + tft->setTextSize(Settings.display_size); + if (!flag) { + tft->setCursor(x, y); + } else { + tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size); + } + if (color) { active_color = color; } + tft->setTextColor(active_color, ILI9341_BLACK); + tft->println(str); +} + +void Ili9341DisplayOnOff() +{ + + + if (PinUsed(GPIO_BACKLIGHT)) { + pinMode(Pin(GPIO_BACKLIGHT), OUTPUT); + digitalWrite(Pin(GPIO_BACKLIGHT), disp_power); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ili9341PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (Settings.display_rotate) { + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + } + + char* txt = DisplayLogBuffer('\370'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * TFT_FONT_HEIGTH; + + tft->setTextSize(size); + tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); + if (!Settings.display_rotate) { + tft->setCursor(0, tft_scroll); + tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); + tft->print(txt); + tft_scroll += theight; + if (tft_scroll >= (tft->height() - tft_bottom)) { + tft_scroll = tft_top; + } + tft->scrollTo(tft_scroll); + } else { + uint8_t last_row = Settings.display_rows -1; + + tft_scroll = (tft_top) ? theight : 0; + tft->setCursor(0, tft_scroll); + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + + tft->print(disp_screen_buffer[i]); + tft_scroll += theight; + tft->setCursor(0, tft_scroll); + delay(1); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + tft->print(disp_screen_buffer[last_row]); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void Ili9341Refresh(void) +{ + if (Settings.display_mode) { + + + + if (Ili9341Header()) { + char tftdt[Settings.display_cols[0] +1]; + char date4[11]; + uint8_t time_size = (Settings.display_cols[0] >= 20) ? 9 : 6; + char spaces[Settings.display_cols[0] - (8 + time_size)]; + char time[time_size]; + + tft->setTextSize(Settings.display_size); + tft->setTextColor(ILI9341_YELLOW, ILI9341_RED); + tft->setCursor(0, 0); + + snprintf_P(date4, sizeof(date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + memset(spaces, 0x20, sizeof(spaces)); + spaces[sizeof(spaces) -1] = '\0'; + snprintf_P(time, sizeof(time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + snprintf_P(tftdt, sizeof(tftdt), PSTR("%s%s%s"), date4, spaces, time); + + tft->print(tftdt); + } else { + tft->setCursor(0, 0); + } + + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + Ili9341PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp04(uint8_t function) +{ + bool result = false; + + if (spi_flg) { + if (FUNC_DISPLAY_INIT_DRIVER == function) { + Ili9341InitDriver(); + } + else if (XDSP_04 == Settings.display_model) { + + if (!dsp_color) { dsp_color = ILI9341_WHITE; } + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + Ili9341Init(dsp_init); + break; + case FUNC_DISPLAY_POWER: + Ili9341DisplayOnOff(); + break; + case FUNC_DISPLAY_CLEAR: + Ili9341Clear(); + break; + case FUNC_DISPLAY_DRAW_HLINE: + tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_VLINE: + tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_LINE: + tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_DRAW_CIRCLE: + tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_FILL_CIRCLE: + tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_DRAW_RECTANGLE: + tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_FILL_RECTANGLE: + tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + + + + case FUNC_DISPLAY_TEXT_SIZE: + tft->setTextSize(Settings.display_size); + break; + case FUNC_DISPLAY_FONT_SIZE: + + break; + case FUNC_DISPLAY_DRAW_STRING: + Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + case FUNC_DISPLAY_ROTATION: + tft->setRotation(Settings.display_rotate); + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ili9341Refresh(); + break; +#endif + } + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_05_epaper_29.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_05_epaper_29.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_29 + +#define XDSP_05 5 + +#define EPD_TOP 12 +#define EPD_FONT_HEIGTH 12 + +#define COLORED 1 +#define UNCOLORED 0 + + + +#define USE_TINY_FONT + +#include +#include + + +extern uint8_t *buffer; +uint16_t epd_scroll; + +Epd *epd; + + + +void EpdInitDriver29() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_05; + } + + if (XDSP_05 == Settings.display_model) { + if (Settings.display_width != EPD_WIDTH) { + Settings.display_width = EPD_WIDTH; + } + if (Settings.display_height != EPD_HEIGHT) { + Settings.display_height = EPD_HEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); + if (!buffer) return; + + + epd = new Epd(EPD_WIDTH,EPD_HEIGHT); + + + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_CLK) && PinUsed(GPIO_SPI_MOSI)) { + epd->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SPI_CS), Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MOSI)); + } + else if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_MOSI)) { + epd->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SSPI_CS), Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI)); + } else { + free(buffer); + return; + } + + renderer = epd; + epd->Init(DISPLAY_INIT_FULL); + epd->Init(DISPLAY_INIT_PARTIAL); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(1); + renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); + renderer->Updateframe(); + delay(1000); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 +#define EPD_FONT_HEIGTH 12 +void EpdPrintLog29(void) +{ + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + + char* txt = DisplayLogBuffer('\040'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * EPD_FONT_HEIGTH; + + renderer->setTextFont(size); + uint8_t last_row = Settings.display_rows -1; + + + epd_scroll = 0; + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); + epd_scroll += theight; + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void EpdRefresh29(void) +{ + if (Settings.display_mode) { + + if (!renderer) return; +# 165 "/workspace/Tasmota/tasmota/xdsp_05_epaper_29.ino" + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + EpdPrintLog29(); + renderer->Updateframe(); + break; + } + + + } +} + +#endif + + + + + +bool Xdsp05(uint8_t function) +{ + bool result = false; + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver29(); + } + else if (XDSP_05 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh29(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_06_epaper_42.ino" +# 21 "/workspace/Tasmota/tasmota/xdsp_06_epaper_42.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_42 + +#define XDSP_06 6 + +#define COLORED42 1 +#define UNCOLORED42 0 + + + +#define USE_TINY_FONT + +#include +#include + +extern uint8_t *buffer; + +Epd42 *epd42; + + + + +void EpdInitDriver42() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_06; + } + + if (XDSP_06 == Settings.display_model) { + + if (Settings.display_width != EPD_WIDTH42) { + Settings.display_width = EPD_WIDTH42; + } + if (Settings.display_height != EPD_HEIGHT42) { + Settings.display_height = EPD_HEIGHT42; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); + if (!buffer) return; + + + epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); + + #ifdef USE_SPI + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { + epd42->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); + } else { + free(buffer); + return; + } + #else + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { + epd42->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); + } else { + free(buffer); + return; + } + #endif + + renderer = epd42; + + epd42->Init(); + + renderer->fillScreen(0); + + + epd42->Init(DISPLAY_INIT_FULL); + + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + epd42->ClearFrame(); + renderer->Updateframe(); + delay(3000); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); + renderer->Updateframe(); + delay(350); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void EpdRefresh42() +{ + if (Settings.display_mode) { + + } +} + +#endif + + + + + + +bool Xdsp06(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver42(); + } + else if (XDSP_06 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh42(); + break; +#endif + } + } + return result; +} + + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_07_sh1106.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_07_sh1106.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SH1106 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +extern uint8_t *buffer; + +#define XDSP_07 7 +#define XI2C_06 6 + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SH1106 *oled1106; + + + + +void SH1106InitDriver() +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_07; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_07; + } + } + + if (XDSP_07 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SH1106"); + + if (Settings.display_width != SH1106_LCDWIDTH) { + Settings.display_width = SH1106_LCDWIDTH; + } + if (Settings.display_height != SH1106_LCDHEIGHT) { + Settings.display_height = SH1106_LCDHEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); + if (!buffer) return; + + + oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); + renderer=oled1106; + renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SH1106")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void SH1106PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SH1106Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SH1106Refresh(void) +{ + if (!renderer) return; + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SH1106Time(); + break; + case 2: + case 3: + case 4: + case 5: + SH1106PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp07(uint8_t function) +{ + if (!I2cEnabled(XI2C_06)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SH1106InitDriver(); + } + else if (XDSP_07 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SH1106Refresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_08_ILI9488.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_08_ILI9488.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9488 + +#define XDSP_08 8 +#define XI2C_38 38 + +#define COLORED 1 +#define UNCOLORED 0 + + + +#define USE_TINY_FONT + + +#include +uint8_t ili9488_ctouch_counter = 0; + + +#define BACKPLANE_PIN 2 + +extern uint8_t *buffer; +extern uint8_t color_type; +ILI9488 *ili9488; +extern const uint16_t picture[]; + + + +void ILI9488_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_08; + } + + if (XDSP_08 == Settings.display_model) { + + if (Settings.display_width != ILI9488_TFTWIDTH) { + Settings.display_width = ILI9488_TFTWIDTH; + } + if (Settings.display_height != ILI9488_TFTHEIGHT) { + Settings.display_height = ILI9488_TFTHEIGHT; + } + + + buffer=NULL; + + + fg_color = ILI9488_WHITE; + bg_color = ILI9488_BLACK; + + uint8_t bppin=BACKPLANE_PIN; + if (PinUsed(GPIO_BACKLIGHT)) { + bppin=Pin(GPIO_BACKLIGHT); + } + +#ifdef ESP32 +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 23 +#undef HW_SPI_MISO +#define HW_SPI_MISO 19 +#undef HW_SPI_CLK +#define HW_SPI_CLK 18 +#else +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 13 +#undef HW_SPI_MISO +#define HW_SPI_MISO 12 +#undef HW_SPI_CLK +#define HW_SPI_CLK 14 +#endif + + + if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SSPI_SCLK)==HW_SPI_CLK)) { + ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin); + } else { + if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK)) { + ili9488 = new ILI9488(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),bppin); + } else { + return; + } + } + + ili9488->begin(); + renderer = ili9488; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); + renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); + delay(1000); + + +#endif + + color_type = COLOR_COLOR; + +#ifdef USE_FT5206 + Touch_Init(Wire); +#endif + } +} + +#ifdef USE_FT5206 +#ifdef USE_TOUCH_BUTTONS + +void ILI9488_RotConvert(int16_t *x, int16_t *y) { +int16_t temp; + if (renderer) { + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + temp=*y; + *y=renderer->height()-*x; + *x=temp; + break; + case 1: + break; + case 2: + break; + case 3: + temp=*y; + *y=*x; + *x=renderer->width()-temp; + break; + } + } +} + + +void ILI9488_CheckTouch(void) { + ili9488_ctouch_counter++; + if (2 == ili9488_ctouch_counter) { + + ili9488_ctouch_counter = 0; + Touch_Check(ILI9488_RotConvert); + } +} +#endif +#endif + + + + + + +bool Xdsp08(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ILI9488_InitDriver(); + } + else if (XDSP_08 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT5206_found) { + ILI9488_CheckTouch(); + } +#endif + break; + } + } + + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_09_SSD1351.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_09_SSD1351.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1351 + +#define XDSP_09 9 + +#define COLORED 1 +#define UNCOLORED 0 + + + + +#define USE_TINY_FONT + +#include + +extern uint8_t *buffer; +extern uint8_t color_type; +SSD1351 *ssd1351; + + + +void SSD1351_InitDriver() { + if (!Settings.display_model) { + Settings.display_model = XDSP_09; + } + + if (XDSP_09 == Settings.display_model) { + + if (Settings.display_width != SSD1351_WIDTH) { + Settings.display_width = SSD1351_WIDTH; + } + if (Settings.display_height != SSD1351_HEIGHT) { + Settings.display_height = SSD1351_HEIGHT; + } + + buffer=0; + + + fg_color = SSD1351_WHITE; + bg_color = SSD1351_BLACK; + + + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)){ + ssd1351 = new SSD1351(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); + } else { + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { + ssd1351 = new SSD1351(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); + } else { + return; + } + } + + delay(100); + ssd1351->begin(); + renderer = ssd1351; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); + renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + } +} + +#ifdef USE_DISPLAY_MODES1TO5 + +void SSD1351PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SSD1351Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(2); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SSD1351Refresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SSD1351Time(); + break; + case 2: + case 3: + case 4: + case 5: + SSD1351PrintLog(); + break; + } + } +} + +#endif + + + + +bool Xdsp09(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1351_InitDriver(); + } + else if (XDSP_09 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SSD1351Refresh(); + break; +#endif + } + } + return result; +} +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_10_RA8876.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_10_RA8876.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_RA8876 + +#define XDSP_10 10 +#define XI2C_39 39 + +#define COLORED 1 +#define UNCOLORED 0 + + + +#define USE_TINY_FONT + +#include + +uint8_t ra8876_ctouch_counter = 0; +extern uint8_t *buffer; +extern uint8_t color_type; +RA8876 *ra8876; + + +void RA8876_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_10; + } + + if (XDSP_10 == Settings.display_model) { + + if (Settings.display_width != RA8876_TFTWIDTH) { + Settings.display_width = RA8876_TFTWIDTH; + } + if (Settings.display_height != RA8876_TFTHEIGHT) { + Settings.display_height = RA8876_TFTHEIGHT; + } + buffer=0; + + + fg_color = RA8876_WHITE; + bg_color = RA8876_BLACK; + +#ifdef ESP32 +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 23 +#undef HW_SPI_MISO +#define HW_SPI_MISO 19 +#undef HW_SPI_CLK +#define HW_SPI_CLK 18 +#else +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 13 +#undef HW_SPI_MISO +#define HW_SPI_MISO 12 +#undef HW_SPI_CLK +#define HW_SPI_CLK 14 +#endif + + + if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SSPI_MISO)==HW_SPI_MISO) && (Pin(GPIO_SSPI_SCLK)==HW_SPI_CLK)) { + ra8876 = new RA8876(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_MISO),Pin(GPIO_SSPI_SCLK),Pin(GPIO_BACKLIGHT)); + } else { + if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_MISO)==HW_SPI_MISO) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK)) { + ra8876 = new RA8876(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_CLK),Pin(GPIO_BACKLIGHT)); + } else { + return; + } + } + + ra8876->begin(); + renderer = ra8876; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); + renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + +#ifdef USE_FT5206 + Touch_Init(Wire); +#endif + + } +} + + +#ifdef USE_FT5206 +#ifdef USE_TOUCH_BUTTONS + + +void RA8876_RotConvert(int16_t *x, int16_t *y) { +int16_t temp; + if (renderer) { + *x=*x*renderer->width()/800; + *y=*y*renderer->height()/480; + + *x = renderer->width() - *x; + *y = renderer->height() - *y; + } +} + + +void RA8876_CheckTouch(void) { + ra8876_ctouch_counter++; + if (2 == ra8876_ctouch_counter) { + + ra8876_ctouch_counter = 0; + Touch_Check(RA8876_RotConvert); + } +} +#endif +#endif +# 324 "/workspace/Tasmota/tasmota/xdsp_10_RA8876.ino" +bool Xdsp10(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + RA8876_InitDriver(); + } + else if (XDSP_10 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_FT5206 + if (FT5206_found) RA8876_CheckTouch(); +#endif + break; + } + } + return result; +} +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_11_sevenseg.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_11_sevenseg.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SEVENSEG + +#define XDSP_11 11 +#define XI2C_47 47 + +#include +#include +#include + +Adafruit_7segment *sevenseg[8]; +uint8_t sevensegs = 0; +uint8_t sevenseg_state = 0; + + + +void SevensegWrite(void) +{ + for (uint32_t i = 0; i < sevensegs; i++) { + sevenseg[i]->writeDisplay(); + } +} + +void SevensegClear(void) +{ + for (uint32_t i = 0; i < sevensegs; i++) { + sevenseg[i]->clear(); + } + SevensegWrite(); +} + + + + +void SevensegInitMode(void) +{ + for (uint32_t i = 0; i < sevensegs; i++) { + sevenseg[i]->setBrightness(Settings.display_dimmer); + sevenseg[i]->blinkRate(0); + } + SevensegClear(); +} + +void SevensegInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + SevensegInitMode(); + break; + } +} + +void SevensegInitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(Settings.display_address[0])) { + Settings.display_model = XDSP_11; + } + } + + if (XDSP_11 == Settings.display_model) { + sevenseg_state = 1; + for (sevensegs = 0; sevensegs < 8; sevensegs++) { + if (Settings.display_address[sevensegs]) { + I2cSetActiveFound(Settings.display_address[sevensegs], "SevenSeg"); + sevenseg[sevensegs] = new Adafruit_7segment(); + sevenseg[sevensegs]->begin(Settings.display_address[sevensegs]); + } else { + break; + } + } + + Settings.display_width = 4; + Settings.display_height = sevensegs; + + SevensegInitMode(); + } +} + +void SevensegOnOff(void) +{ + if (!disp_power) { SevensegClear(); } +} + +void SevensegDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + enum OutNumType {DECIMAL, HEXADECIMAL, FLOAT, SEGMENTS}; + int16_t number = 0; + double numberf = 0; + boolean hasnumber= false; + uint8_t dots= 0; + OutNumType outnumtype= DECIMAL; + uint8 fds = 0; + boolean done= false; + boolean s= false; + uint8_t unit= y; + char *buf; + + if ((unit>=sevensegs) || (unit<0)) { + unit=0; + } + + for (int i=0; (str[i]!='\0') && (!done); i++) { +# 155 "/workspace/Tasmota/tasmota/xdsp_11_sevenseg.ino" + switch (str[i]) { + case 'x': + + outnumtype = HEXADECIMAL; + break; + case 'f': + + outnumtype = FLOAT; + break; + case ':': + dots |= 0x02; + break; + case '^': + dots |= 0x08; + break; + case 'v': + dots |= 0x04; + break; + case '.': + dots |= 0x10; + break; + case 's': + s = true; + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + hasnumber= true; + if (outnumtype == FLOAT) { + + numberf = atof(str+i); + + buf= str+i; + char *cp= strchr(buf, '.'); + if (cp == NULL) { + fds= 0; + } else { + fds= buf+strlen(buf) - 1 - cp; + } + } else { + + number = atoi(str+i); + } + done = true; + break; + case 'z': + hasnumber=false; + dots=0; + s=false; + sevenseg[unit]->clear(); + break; + case 'r': + outnumtype= SEGMENTS; + break; + default: + break; + } + } + + + if (hasnumber) { + if (s) { + + int hour = number/60/60; + int minute = (number/60)%60; + + if (hour) { + + number = hour*100 + minute; + } else { + + number = minute*100 + number%60; + } + } + + if (outnumtype == HEXADECIMAL) { + + sevenseg[unit]->print(number, HEX); + } else if (outnumtype == FLOAT) { + + sevenseg[unit]->printFloat(numberf, fds, 10); + } else if (outnumtype == SEGMENTS) { + + sevenseg[unit]->writeDigitRaw(x, number); + } else { + + sevenseg[unit]->print(number, DEC); + } + } + + if (dots) { + sevenseg[unit]->writeDigitRaw(2, dots); + } + + sevenseg[unit]->writeDisplay(); +} + + + +#ifdef USE_DISPLAY_MODES1TO5 +void SevensegTime(boolean time_24) +{ + + uint hours = RtcTime.hour; + uint minutes = RtcTime.minute; + uint second = RtcTime.second; + uint16_t displayValue = hours * 100 + minutes; + uint16_t dots = 0; + + + if (!time_24) { + + if (hours > 12) { + displayValue -= 1200; + } + + else if (hours == 0) { + displayValue += 1200; + } + } + + + + sevenseg[0]->print(displayValue, DEC); + + + + + if (time_24) { + if (hours == 0) { + + sevenseg[0]->writeDigitNum(1, 0); + + if (minutes < 10) { + sevenseg[0]->writeDigitNum(3, 0); + } + } + if (hours < 10) { + + sevenseg[0]->writeDigitNum(0, 0); + } + } else { + + if (hours >= 12) { + dots |= 0x10; + } + } + + sevenseg[0]->writeDigitRaw(2, dots |= ((second%2) << 1)); + sevenseg[0]->writeDisplay(); +} + +void SevensegRefresh(void) +{ + if (disp_power) { + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SevensegTime(false); + break; + case 2: + SevensegTime(true); + break; + case 4: + case 3: + case 5: { + break; + } + } + } + } +} + +#endif + + + + + +bool Xdsp11(uint8_t function) +{ + if (!I2cEnabled(XI2C_47)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SevensegInitDriver(); + } + else if (XDSP_11 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + SevensegInit(dsp_init); + break; + case FUNC_DISPLAY_CLEAR: + SevensegClear(); + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SevensegRefresh(); + break; +#endif + case FUNC_DISPLAY_POWER: + SevensegOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + SevensegDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_12_ST7789.ino" +# 21 "/workspace/Tasmota/tasmota/xdsp_12_ST7789.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ST7789 + +#define XDSP_12 12 +#define XI2C_38 38 + +#undef COLORED +#define COLORED 1 +#undef UNCOLORED +#define UNCOLORED 0 + + +#undef FT5206_address +#define FT5206_address 0x38 + + + +#undef USE_TINY_FONT +#define USE_TINY_FONT + + +#include +#include + + +#define BACKPLANE_PIN 2 + +extern uint8_t *buffer; +extern uint8_t color_type; +Arduino_ST7789 *st7789; + +#ifdef USE_FT5206 +uint8_t st7789_ctouch_counter = 0; +#endif + + + +void ST7789_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_12; + } + + if (XDSP_12 == Settings.display_model) { + + if (Settings.display_width != ST7789_TFTWIDTH) { + Settings.display_width = ST7789_TFTWIDTH; + } + if (Settings.display_height != ST7789_TFTHEIGHT) { + Settings.display_height = ST7789_TFTHEIGHT; + } + + + buffer=NULL; + + + fg_color = ST7789_WHITE; + bg_color = ST7789_BLACK; + + int8_t bppin=BACKPLANE_PIN; + if (PinUsed(GPIO_BACKLIGHT)) { + bppin=Pin(GPIO_BACKLIGHT); + } + + int8_t reset = -1; + if (PinUsed(GPIO_OLED_RESET)) { + reset=Pin(GPIO_OLED_RESET); + } + + int8_t cs = -1; + if (PinUsed(GPIO_SSPI_CS)) { + cs=Pin(GPIO_SSPI_CS); + } else if (PinUsed(GPIO_SPI_CS)) { + cs=Pin(GPIO_SPI_CS); + } + +#ifdef ESP32 +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 23 +#undef HW_SPI_CLK +#define HW_SPI_CLK 18 +#else +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 13 +#undef HW_SPI_CLK +#define HW_SPI_CLK 14 +#endif + + + + if ((Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK) && PinUsed(GPIO_SPI_DC)) { + st7789 = new Arduino_ST7789(Pin(GPIO_SPI_DC), reset, cs, bppin); + } else { + if ((PinUsed(GPIO_SSPI_CS) || PinUsed(GPIO_OLED_RESET)) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_DC)) { + st7789 = new Arduino_ST7789(Pin(GPIO_SSPI_DC), reset, Pin(GPIO_SSPI_MOSI), Pin(GPIO_SSPI_SCLK), cs, bppin); + } else { + return; + } + } + st7789->init(Settings.display_width,Settings.display_height); + renderer = st7789; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(ST7789_WHITE,ST7789_BLACK); + renderer->DrawStringAt(30, 100, "ST7789 TFT!", ST7789_WHITE,0); + delay(1000); +#endif + + color_type = COLOR_COLOR; + +#ifdef ESP32 +#ifdef USE_FT5206 + + #define SDA_2 23 + #define SCL_2 32 + Wire1.begin(SDA_2, SCL_2, 400000); + Touch_Init(Wire1); +#endif +#endif + + } +} + +#ifdef ESP32 +#ifdef USE_FT5206 +#ifdef USE_TOUCH_BUTTONS + +void ST7789_RotConvert(int16_t *x, int16_t *y) { +int16_t temp; + if (renderer) { + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + break; + case 1: + temp=*y; + *y=renderer->height()-*x; + *x=temp; + break; + case 2: + *x=renderer->width()-*x; + *y=renderer->height()-*y; + break; + case 3: + temp=*y; + *y=*x; + *x=renderer->width()-temp; + break; + } + } +} + + +void ST7789_CheckTouch() { +st7789_ctouch_counter++; + if (2 == st7789_ctouch_counter) { + + st7789_ctouch_counter = 0; + Touch_Check(ST7789_RotConvert); + } +} +#endif +#endif +#endif + + + + + +bool Xdsp12(uint8_t function) +{ + bool result = false; + + + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ST7789_InitDriver(); + } + else if (XDSP_12 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_FT5206 +#ifdef USE_TOUCH_BUTTONS + if (FT5206_found) { + ST7789_CheckTouch(); + } +#endif +#endif + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xdsp_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xdsp_interface.ino" +#if defined(USE_I2C) || defined(USE_SPI) +#ifdef USE_DISPLAY + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdsp_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDSP_01 + &Xdsp01, +#endif + +#ifdef XDSP_02 + &Xdsp02, +#endif + +#ifdef XDSP_03 + &Xdsp03, +#endif + +#ifdef XDSP_04 + &Xdsp04, +#endif + +#ifdef XDSP_05 + &Xdsp05, +#endif + +#ifdef XDSP_06 + &Xdsp06, +#endif + +#ifdef XDSP_07 + &Xdsp07, +#endif + +#ifdef XDSP_08 + &Xdsp08, +#endif + +#ifdef XDSP_09 + &Xdsp09, +#endif + +#ifdef XDSP_10 + &Xdsp10, +#endif + +#ifdef XDSP_11 + &Xdsp11, +#endif + +#ifdef XDSP_12 + &Xdsp12, +#endif + +#ifdef XDSP_13 + &Xdsp13, +#endif + +#ifdef XDSP_14 + &Xdsp14, +#endif + +#ifdef XDSP_15 + &Xdsp15, +#endif + +#ifdef XDSP_16 + &Xdsp16 +#endif +}; + +const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); +# 117 "/workspace/Tasmota/tasmota/xdsp_interface.ino" +uint8_t XdspPresent(void) +{ + return xdsp_present; +} + +bool XdspCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DSP: %d"), Function); + + for (uint32_t x = 0; x < xdsp_present; x++) { + result = xdsp_func_ptr[x](Function); + + if (result && (FUNC_DISPLAY_MODEL == Function)) { + break; + } + } + + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_01_ws2812.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_01_ws2812.ino" +#ifdef USE_LIGHT +#ifdef USE_WS2812 +# 38 "/workspace/Tasmota/tasmota/xlgt_01_ws2812.ino" +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 8; + +const char kWs2812Commands[] PROGMEM = "|" + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; + +#include + +#if (USE_WS2812_CTYPE == NEO_GRB) + typedef NeoGrbFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_BRG) + typedef NeoBrgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RBG) + typedef NeoRbgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RGBW) + typedef NeoRgbwFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_GRBW) + typedef NeoGrbwFeature selectedNeoFeatureType; +#else + typedef NeoRgbFeature selectedNeoFeatureType; +#endif + +#ifdef USE_WS2812_DMA + +#ifdef USE_WS2812_INVERTED + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266DmaInvertedWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266DmaInvertedSk6812Method selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + typedef NeoEsp8266DmaInvertedApa106Method selectedNeoSpeedType; +#else + typedef NeoEsp8266DmaInverted800KbpsMethod selectedNeoSpeedType; +#endif + +#else + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; +#else + typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; +#endif + +#endif + +#else + +#ifdef USE_WS2812_INVERTED + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266BitBangWs2812xInvertedMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266BitBangSk6812InvertedMethod selectedNeoSpeedType; +#else + typedef NeoEsp8266BitBang400KbpsInvertedMethod selectedNeoSpeedType; +#endif + +#else + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; +#else + typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; +#endif + +#endif + +#endif + +NeoPixelBus *strip = nullptr; + +struct WsColor { + uint8_t red, green, blue; +}; + +struct ColorScheme { + WsColor* colors; + uint8_t count; +}; + +WsColor kIncandescent[2] = { 255,140,20, 0,0,0 }; +WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 }; +WsColor kChristmas[2] = { 255,0,0, 0,255,0 }; +WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; +WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; +WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; +WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; +ColorScheme kSchemes[WS2812_SCHEMES -1] = { + kIncandescent, 2, + kRgb, 3, + kChristmas, 2, + kHanukkah, 2, + kwanzaa, 3, + kRainbow, 7, + kFire, 3 }; + +uint8_t kWidth[5] = { + 1, + 2, + 4, + 8, + 255 }; +uint8_t kWsRepeat[5] = { + 8, + 6, + 4, + 2, + 1 }; + +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; + + + +void Ws2812StripShow(void) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; +#else + RgbColor c; +#endif + + if (Settings.light_correction) { + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + c = strip->GetPixelColor(i); + c.R = ledGamma(c.R); + c.G = ledGamma(c.G); + c.B = ledGamma(c.B); +#if (USE_WS2812_CTYPE > NEO_3LED) + c.W = ledGamma(c.W); +#endif + strip->SetPixelColor(i, c); + } + } + strip->Show(); +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor color; +#else + RgbColor color; +#endif + + uint32_t mod_position = mod(position, (int)Settings.light_pixels); + + color = strip->GetPixelColor(mod_position); + float dimmer = 100 / (float)Settings.light_dimmer; + color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255); + color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255); + color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255); + strip->SetPixelColor(mod_position, color); +} + +void Ws2812UpdateHand(int position, uint32_t index) +{ + uint32_t width = Settings.light_width; + if (index < WS_MARKER) { width = Settings.ws_width[index]; } + if (!width) { return; } + + position = (position + Settings.light_rotation) % Settings.light_pixels; + + if (Settings.flag.ws_clock_reverse) { + position = Settings.light_pixels -position; + } + WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] }; + + Ws2812UpdatePixelColor(position, hand_color, 1); + + uint32_t range = ((width -1) / 2) +1; + for (uint32_t h = 1; h < range; h++) { + float offset = (float)(range - h) / (float)range; + Ws2812UpdatePixelColor(position -h, hand_color, offset); + Ws2812UpdatePixelColor(position +h, hand_color, offset); + } +} + +void Ws2812Clock(void) +{ + strip->ClearTo(0); + int clksize = 60000 / (int)Settings.light_pixels; + + Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); + Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); + Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); + if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { + for (uint32_t i = 0; i < 12; i++) { + Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); + } + } + + Ws2812StripShow(); +} + +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) +{ + + + + + ColorScheme scheme = kSchemes[schemenr]; + uint32_t curRange = i / range; + uint32_t rangeIndex = i % range; + uint32_t colorIndex = rangeIndex / gradRange; + uint32_t start = colorIndex; + uint32_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float dimmer = 100 / (float)Settings.light_dimmer; + float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; + float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; + float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; + mColor->red = (uint8_t)fmyRed; + mColor->green = (uint8_t)fmyGrn; + mColor->blue = (uint8_t)fmyBlu; +} + +void Ws2812Gradient(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + if (scheme.count < 2) { return; } + + uint32_t repeat = kWsRepeat[Settings.light_width]; + uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); + uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; + + WsColor oldColor, currentColor; + Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); + currentColor = oldColor; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (kWsRepeat[Settings.light_width] > 1) { + Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset); + } + if (Settings.light_speed > 0) { + + c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); + c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); + c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); + } + else { + + c.R = currentColor.red; + c.G = currentColor.green; + c.B = currentColor.blue; + } + strip->SetPixelColor(i, c); + oldColor = currentColor; + } + Ws2812StripShow(); +} + +void Ws2812Bars(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + + uint32_t maxSize = Settings.light_pixels / scheme.count; + if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } + + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; + + WsColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float dimmer = 100 / (float)Settings.light_dimmer; + for (uint32_t i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].red / dimmer; + float fmyGrn = (float)mcolor[i].green / dimmer; + float fmyBlu = (float)mcolor[i].blue / dimmer; + mcolor[i].red = (uint8_t)fmyRed; + mcolor[i].green = (uint8_t)fmyGrn; + mcolor[i].blue = (uint8_t)fmyBlu; + } + uint32_t colorIndex = offset % scheme.count; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } + c.R = mcolor[colorIndex].red; + c.G = mcolor[colorIndex].green; + c.B = mcolor[colorIndex].blue; + strip->SetPixelColor(i, c); + } + Ws2812StripShow(); +} + +void Ws2812Clear(void) +{ + strip->ClearTo(0); + strip->Show(); + Ws2812.show_next = 1; +} + +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; + lcolor.W = white; +#else + RgbColor lcolor; +#endif + + lcolor.R = red; + lcolor.G = green; + lcolor.B = blue; + if (led) { + strip->SetPixelColor(led -1, lcolor); + } else { + + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + strip->SetPixelColor(i, lcolor); + } + } + + if (!Ws2812.suspend_update) { + strip->Show(); + Ws2812.show_next = 1; + } +} + +char* Ws2812GetColor(uint32_t led, char* scolor) +{ + uint8_t sl_ledcolor[4]; + + #if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor = strip->GetPixelColor(led -1); + sl_ledcolor[3] = lcolor.W; + #else + RgbColor lcolor = strip->GetPixelColor(led -1); + #endif + sl_ledcolor[0] = lcolor.R; + sl_ledcolor[1] = lcolor.G; + sl_ledcolor[2] = lcolor.B; + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); + } else { + snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); + } + } + return scolor; +} + + + + + +void Ws2812ForceSuspend (void) +{ + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + strip->Show(); + Ws2812.show_next = 1; +} + + + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; + + switch (scheme) { + case 0: + if ((1 == state_250mS) || (Ws2812.show_next)) { + Ws2812Clock(); + Ws2812.show_next = 0; + } + break; + default: + if (1 == Settings.light_fade) { + Ws2812Gradient(scheme -1); + } else { + Ws2812Bars(scheme -1); + } + Ws2812.show_next = 1; + break; + } +} + +void Ws2812ModuleSelected(void) +{ + if (PinUsed(GPIO_WS2812)) { + + + strip = new NeoPixelBus(WS2812_MAX_LEDS, Pin(GPIO_WS2812)); + strip->Begin(); + + Ws2812Clear(); + + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#if (USE_WS2812_CTYPE > NEO_3LED) + light_type = LT_RGBW; +#else + light_type = LT_RGB; +#endif + light_flg = XLGT_01; + } +} + + + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings.light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { + Settings.light_pixels = XdrvMailbox.payload; + Settings.light_rotation = 0; + Ws2812Clear(); + Light.update = true; + } + ResponseCmndNumber(Settings.light_pixels); +} + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { + Settings.light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings.light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); + } + } +} + + + + + +bool Xlgt01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_02_my92x1.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_02_my92x1.ino" +#ifdef USE_LIGHT +#ifdef USE_MY92X1 + + + + +#define XLGT_02 2 + +struct MY92X1 { + uint8_t pdi_pin = 0; + uint8_t pdcki_pin = 0; + uint8_t model = 0; +} My92x1; + +extern "C" { + void os_delay_us(unsigned int); +} + +void LightDiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdi_pin, HIGH); + digitalWrite(My92x1.pdi_pin, LOW); + } +} + +void LightDckiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdcki_pin, HIGH); + digitalWrite(My92x1.pdcki_pin, LOW); + } +} + +void LightMy92x1Write(uint8_t data) +{ + for (uint32_t i = 0; i < 4; i++) { + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, HIGH); + data = data << 1; + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, LOW); + data = data << 1; + } +} + +void LightMy92x1Init(void) +{ + uint8_t chips[3] = { 1, 2, 2 }; + + LightDckiPulse(chips[My92x1.model] * 32); + os_delay_us(12); + + + LightDiPulse(12); + os_delay_us(12); + for (uint32_t n = 0; n < chips[My92x1.model]; n++) { + LightMy92x1Write(0x18); + } + os_delay_us(12); + + + LightDiPulse(16); + os_delay_us(12); +} + +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) +{ + uint8_t channels[3] = { 4, 6, 6 }; + + uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, + { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, + { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; + + os_delay_us(12); + for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { + LightMy92x1Write(duty[My92x1.model][channel]); + } + os_delay_us(12); + LightDiPulse(8); + os_delay_us(12); +} + + + +bool My92x1SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + + return true; +} + +void My92x1ModuleSelected(void) +{ + if (PinUsed(GPIO_DCKI) && PinUsed(GPIO_DI)) { + My92x1.pdi_pin = Pin(GPIO_DI); + My92x1.pdcki_pin = Pin(GPIO_DCKI); + + pinMode(My92x1.pdi_pin, OUTPUT); + pinMode(My92x1.pdcki_pin, OUTPUT); + digitalWrite(My92x1.pdi_pin, LOW); + digitalWrite(My92x1.pdcki_pin, LOW); + + My92x1.model = 2; + light_type = LT_RGBW; + if (AILIGHT == my_module_type) { + My92x1.model = 0; + + } + else if (SONOFF_B1 == my_module_type) { + My92x1.model = 1; + light_type = LT_RGBWC; + } + + LightMy92x1Init(); + + light_flg = XLGT_02; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); + } +} + + + + + +bool Xlgt02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = My92x1SetChannels(); + break; + case FUNC_MODULE_INIT: + My92x1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_03_sm16716.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_03_sm16716.ino" +#ifdef USE_LIGHT +#ifdef USE_SM16716 + + + + + + + +#define XLGT_03 3 + +#define D_LOG_SM16716 "SM16716: " + +struct SM16716 { + uint8_t pin_clk = 0; + uint8_t pin_dat = 0; + uint8_t pin_sel = 0; + bool enabled = false; +} Sm16716; + +void SM16716_SendBit(uint8_t v) +{ + + + + + + digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); + + digitalWrite(Sm16716.pin_clk, HIGH); + + digitalWrite(Sm16716.pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (Sm16716.pin_sel < 99) { + bool should_enable = (duty_r | duty_g | duty_b); + if (!Sm16716.enabled && should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); + Sm16716.enabled = true; + digitalWrite(Sm16716.pin_sel, HIGH); + + + delayMicroseconds(1000); + SM16716_Init(); + } + else if (Sm16716.enabled && !should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); + Sm16716.enabled = false; + digitalWrite(Sm16716.pin_sel, LOW); + } + } + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); + + + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + + + + + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} +# 111 "/workspace/Tasmota/tasmota/xlgt_03_sm16716.ino" +void SM16716_Init(void) +{ + for (uint32_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + + + +bool Sm16716SetChannels(void) +{ +# 132 "/workspace/Tasmota/tasmota/xlgt_03_sm16716.ino" + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + + return true; +} + +void Sm16716ModuleSelected(void) +{ + if (PinUsed(GPIO_SM16716_CLK) && PinUsed(GPIO_SM16716_DAT)) { + Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); + Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); + Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); +# 161 "/workspace/Tasmota/tasmota/xlgt_03_sm16716.ino" + pinMode(Sm16716.pin_clk, OUTPUT); + digitalWrite(Sm16716.pin_clk, LOW); + + pinMode(Sm16716.pin_dat, OUTPUT); + digitalWrite(Sm16716.pin_dat, LOW); + + if (Sm16716.pin_sel < 99) { + pinMode(Sm16716.pin_sel, OUTPUT); + digitalWrite(Sm16716.pin_sel, LOW); + + } else { + + SM16716_Init(); + } + + LightPwmOffset(LST_RGB); + light_type += LST_RGB; + light_flg = XLGT_03; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + } +} + + + + + +bool Xlgt03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm16716SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm16716ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_04_sm2135.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_04_sm2135.ino" +#ifdef USE_LIGHT +#ifdef USE_SM2135 +# 31 "/workspace/Tasmota/tasmota/xlgt_04_sm2135.ino" +#define XLGT_04 4 + +#define SM2135_ADDR_MC 0xC0 +#define SM2135_ADDR_CH 0xC1 +#define SM2135_ADDR_R 0xC2 +#define SM2135_ADDR_G 0xC3 +#define SM2135_ADDR_B 0xC4 +#define SM2135_ADDR_C 0xC5 +#define SM2135_ADDR_W 0xC6 + +#define SM2135_RGB 0x00 +#define SM2135_CW 0x80 + +#define SM2135_10MA 0x00 +#define SM2135_15MA 0x01 +#define SM2135_20MA 0x02 +#define SM2135_25MA 0x03 +#define SM2135_30MA 0x04 +#define SM2135_35MA 0x05 +#define SM2135_40MA 0x06 +#define SM2135_45MA 0x07 +#define SM2135_50MA 0x08 +#define SM2135_55MA 0x09 +#define SM2135_60MA 0x0A + +enum Sm2135Color { SM2135_WCGRB, SM2135_WCBGR }; + + +const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; + +struct SM2135 { + uint8_t clk = 0; + uint8_t data = 0; + uint8_t model = SM2135_WCGRB; +} Sm2135; + + + + + +const uint8_t SM2135_DELAY = 4; + +void Sm2135SetLow(uint8_t pin) { + noInterrupts(); + digitalWrite(pin, LOW); + pinMode(pin, OUTPUT); + interrupts(); +} + +void Sm2135SetHigh(uint8_t pin) { + noInterrupts(); + pinMode(pin, INPUT_PULLUP); + interrupts(); +} + +bool Sm2135Init(void) { + digitalWrite(Sm2135.data, LOW); + digitalWrite(Sm2135.clk, LOW); + Sm2135SetHigh(Sm2135.data); + Sm2135SetHigh(Sm2135.clk); + return (!((digitalRead(Sm2135.data) == LOW || digitalRead(Sm2135.clk) == LOW))); +} + +bool Sm2135Write(uint8_t value) { + for (uint8_t curr = 0X80; curr != 0; curr >>= 1) { + if (curr & value) { + Sm2135SetHigh(Sm2135.data); + } else { + Sm2135SetLow(Sm2135.data); + } + Sm2135SetHigh(Sm2135.clk); + delayMicroseconds(SM2135_DELAY); + Sm2135SetLow(Sm2135.clk); + } + + Sm2135SetHigh(Sm2135.data); + Sm2135SetHigh(Sm2135.clk); + delayMicroseconds(SM2135_DELAY / 2); + uint8_t ack = digitalRead(Sm2135.data); + Sm2135SetLow(Sm2135.clk); + delayMicroseconds(SM2135_DELAY / 2); + Sm2135SetLow(Sm2135.data); + return (0 == ack); +} + +bool Sm2135Start(uint8_t addr) { + Sm2135SetLow(Sm2135.data); + delayMicroseconds(SM2135_DELAY); + Sm2135SetLow(Sm2135.clk); + return Sm2135Write(addr); +} + +void Sm2135Stop(void) { + Sm2135SetLow(Sm2135.data); + delayMicroseconds(SM2135_DELAY); + Sm2135SetHigh(Sm2135.clk); + delayMicroseconds(SM2135_DELAY); + Sm2135SetHigh(Sm2135.data); + delayMicroseconds(SM2135_DELAY); +} + + + +bool Sm2135SetChannels(void) { + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + uint8_t data[6]; + + Sm2135Start(SM2135_ADDR_MC); + Sm2135Write(SM2135_CURRENT); + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { + Sm2135Write(SM2135_CW); + Sm2135Stop(); + delay(1); + Sm2135Start(SM2135_ADDR_C); + Sm2135Write(cur_col[4]); + Sm2135Write(cur_col[3]); + } else { + Sm2135Write(SM2135_RGB); + if (SM2135_WCBGR == Sm2135.model) { + Sm2135Write(cur_col[2]); + Sm2135Write(cur_col[1]); + Sm2135Write(cur_col[0]); + } else { + Sm2135Write(cur_col[1]); + Sm2135Write(cur_col[0]); + Sm2135Write(cur_col[2]); + } + } + Sm2135Stop(); + + return true; +} + +void Sm2135ModuleSelected(void) +{ + if (PinUsed(GPIO_SM2135_CLK) && PinUsed(GPIO_SM2135_DAT)) { + Sm2135.clk = Pin(GPIO_SM2135_CLK); + Sm2135.data = Pin(GPIO_SM2135_DAT); + + Sm2135.model = SM2135_WCGRB; + if (PinUsed(GPIO_SWT1)) { + Sm2135.model = SM2135_WCBGR; + pinMode(Pin(GPIO_SWT1), INPUT); + SetPin(Pin(GPIO_SWT1), AGPIO(GPIO_NONE)); + } + + Sm2135Init(); + + light_type = LT_RGBWC; + light_flg = XLGT_04; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 (%s) Found"), (SM2135_WCBGR == Sm2135.model) ? PSTR("BGR") : PSTR("GRB")); + } +} + + + + + +bool Xlgt04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm2135SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm2135ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +#ifdef USE_LIGHT +#ifdef USE_SONOFF_L1 + + + + +#define XLGT_05 5 + +#define SONOFF_L1_BUFFER_SIZE 140 + +#define SONOFF_L1_MODE_COLORFUL 1 +#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 +#define SONOFF_L1_MODE_COLORFUL_BREATH 3 +#define SONOFF_L1_MODE_DIY_GRADIENT 4 +#define SONOFF_L1_MODE_DIY_PULSE 5 +#define SONOFF_L1_MODE_DIY_BREATH 6 +#define SONOFF_L1_MODE_DIY_STROBE 7 +#define SONOFF_L1_MODE_RGB_GRADIENT 8 +#define SONOFF_L1_MODE_RGB_PULSE 9 +#define SONOFF_L1_MODE_RGB_BREATH 10 +#define SONOFF_L1_MODE_RGB_STROBE 11 +#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 + +struct SNFL1 { + uint32_t unlock = 0; + bool receive_ready = true; +} Snfl1; + + + +void SnfL1Send(const char *buffer) +{ + + + Serial.print(buffer); + Serial.write(0x1B); + Serial.flush(); +} + +void SnfL1SerialSendOk(void) +{ + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); + + SnfL1Send(buffer); +} + +bool SnfL1SerialInput(void) +{ + if (serial_in_byte != 0x1B) { + if (serial_in_byte_counter >= 140) { + serial_in_byte_counter = 0; + } + if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } + } else { + serial_in_buffer[serial_in_byte_counter++] = 0x00; + + + + + + + if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { + Snfl1.receive_ready = true; + } + else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { + char cmnd_dimmer[20]; + char cmnd_color[20]; + char *end_str; + char *string = serial_in_buffer +10; + char *token = strtok_r(string, ",", &end_str); + + bool color_updated[3] = { false, false, false }; + uint8_t current_color[3]; + memcpy(current_color, Settings.light_color, 3); + + bool switch_state = false; + bool is_power_change = false; + bool is_color_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"sequence\"", 10)) { + + + + token = nullptr; + } + + else if (!strncmp(token2, "\"switch\"", 8)) { + switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_power_change = (switch_state != Light.power); + } + + else if (!strncmp(token2, "\"color", 6)) { + char color_channel_name = token2[6]; + int color_index; + switch(color_channel_name) + { + case 'R': color_index = 0; + break; + case 'G': color_index = 1; + break; + case 'B': color_index = 2; + break; + } + int color_value = atoi(token3); + current_color[color_index] = color_value; + color_updated[color_index] = true; + + bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; + if (all_color_channels_updated) { + + + + + + is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); + } + snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); + } + + else if (!strncmp(token2, "\"bright\"", 8)) { + uint8_t dimmer = atoi(token3); + + + + is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); + snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); + } + + token = strtok_r(nullptr, ",", &end_str); + } + + if (is_power_change) { + if (Settings.light_scheme > 0) { + if (!switch_state) { + char cmnd_scheme[20]; + snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); + ExecuteCommand(cmnd_scheme, SRC_SWITCH); + } + } else { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (is_brightness_change) { + ExecuteCommand(cmnd_dimmer, SRC_SWITCH); + } + else if (Light.power && is_color_change) { + if (0 == Settings.light_scheme) { + if (Settings.light_fade) { + char cmnd_fade[20]; + snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); + ExecuteCommand(cmnd_fade, SRC_SWITCH); + } + ExecuteCommand(cmnd_color, SRC_SWITCH); + } + } + } + + SnfL1SerialSendOk(); + + return true; + } + serial_in_byte = 0; + return false; +} + + + +bool SnfL1SetChannels(void) +{ + if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { + + uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; + + char buffer[140]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), + LocalTime(), millis()%1000, + Light.power ? "on" : "off", + scale_col[0], scale_col[1], scale_col[2], + light_state.getDimmer(), + SONOFF_L1_MODE_COLORFUL); + + SnfL1Send(buffer); + + Snfl1.unlock = millis() + 500; + Snfl1.receive_ready = false; + } + return true; +} + +void SnfL1ModuleSelected(void) +{ + if (SONOFF_L1 == my_module_type) { + if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) { + SetSerial(19200, TS_SERIAL_8N1); + + light_type = LT_RGB; + light_flg = XLGT_05; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); + } + } +} + + + + + +bool Xlgt05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SERIAL: + result = SnfL1SerialInput(); + break; + case FUNC_SET_CHANNELS: + result = SnfL1SetChannels(); + break; + case FUNC_MODULE_INIT: + SnfL1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +#ifdef USE_LIGHT +#ifdef USE_ELECTRIQ_MOODL +# 31 "/workspace/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +#define XLGT_06 6 + + + +bool ElectriqMoodLSetChannels(void) +{ + uint8_t *col = (uint8_t*)XdrvMailbox.data; + uint8_t checksum = (uint8_t)(0x65 + 0xAA + 0x01 + 0x0A); + + Serial.write(0x65); + Serial.write(0xAA); + Serial.write(0x00); + Serial.write(0x01); + Serial.write(0x0A); + + uint8_t payload[5]; + payload[0] = col[0]; + payload[1] = col[1]; + payload[2] = col[2]; + payload[3] = col[3]; + payload[4] = 0x0; + + + for (uint32_t i = 0; i < 5; i++) { + Serial.write(payload[i]); + checksum += payload[i]; + } + + + for (uint32_t i = 0; i < 5; i++) { + Serial.write(payload[i]); + checksum += payload[i]; + } + + Serial.write(checksum); + Serial.flush(); + + return true; +} + +void ElectriqMoodLModuleSelected(void) +{ + if (PinUsed(GPIO_ELECTRIQ_MOODL_TX)) { + SetSerial(9600, TS_SERIAL_8N1); + light_type = LT_RGBW; + light_flg = XLGT_06; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: ElectriQ Mood Lamp Found")); + } +} + + + + + +bool Xlgt06(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = ElectriqMoodLSetChannels(); + break; + case FUNC_MODULE_INIT: + ElectriqMoodLModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xlgt_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xlgt_interface.ino" +#ifdef USE_LIGHT + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xlgt_func_ptr[])(uint8_t) = { +#endif + +#ifdef XLGT_01 + &Xlgt01, +#endif + +#ifdef XLGT_02 + &Xlgt02, +#endif + +#ifdef XLGT_03 + &Xlgt03, +#endif + +#ifdef XLGT_04 + &Xlgt04, +#endif + +#ifdef XLGT_05 + &Xlgt05, +#endif + +#ifdef XLGT_06 + &Xlgt06, +#endif + +#ifdef XLGT_07 + &Xlgt07, +#endif + +#ifdef XLGT_08 + &Xlgt08, +#endif + +#ifdef XLGT_09 + &Xlgt09, +#endif + +#ifdef XLGT_10 + &Xlgt10, +#endif + +#ifdef XLGT_11 + &Xlgt11, +#endif + +#ifdef XLGT_12 + &Xlgt12, +#endif + +#ifdef XLGT_13 + &Xlgt13, +#endif + +#ifdef XLGT_14 + &Xlgt14, +#endif + +#ifdef XLGT_15 + &Xlgt15, +#endif + +#ifdef XLGT_16 + &Xlgt16 +#endif +}; + +const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); + +uint8_t xlgt_active = 0; + +bool XlgtCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("LGT: %d"), function); + + if (FUNC_MODULE_INIT == function) { + for (uint32_t x = 0; x < xlgt_present; x++) { + xlgt_func_ptr[x](function); + if (light_flg) { + xlgt_active = x; + return true; + } + } + } + else if (light_flg) { + return xlgt_func_ptr[xlgt_active](function); + } + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_01_hlw8012.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_01_hlw8012.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + + + + + + +#define XNRG_01 1 + + +#define HLW_PREF 10000 +#define HLW_UREF 2200 +#define HLW_IREF 4545 + + +#define HJL_PREF 1362 +#define HJL_UREF 822 +#define HJL_IREF 3300 + +#define HLW_POWER_PROBE_TIME 10 +#define HLW_SAMPLE_COUNT 10 + + + +struct HLW { +#ifdef HLW_DEBUG + uint32_t debug[HLW_SAMPLE_COUNT]; +#endif + volatile uint32_t cf_pulse_length = 0; + volatile uint32_t cf_pulse_last_time = 0; + uint32_t cf_power_pulse_length = 0; + + volatile uint32_t cf1_pulse_length = 0; + volatile uint32_t cf1_pulse_last_time = 0; + volatile uint32_t cf1_summed_pulse_length = 0; + volatile uint32_t cf1_pulse_counter = 0; + uint32_t cf1_voltage_pulse_length = 0; + uint32_t cf1_current_pulse_length = 0; + + volatile uint32_t energy_period_counter = 0; + + uint32_t power_ratio = 0; + uint32_t voltage_ratio = 0; + uint32_t current_ratio = 0; + + uint8_t model_type = 0; + volatile uint8_t cf1_timer = 0; + uint8_t power_retry = 0; + bool select_ui_flag = false; + bool ui_flag = true; + volatile bool load_off = true; +} Hlw; + + +#ifndef USE_WS2812_DMA +void HlwCfInterrupt(void) ICACHE_RAM_ATTR; +void HlwCf1Interrupt(void) ICACHE_RAM_ATTR; +#endif + +void HlwCfInterrupt(void) +{ + uint32_t us = micros(); + + if (Hlw.load_off) { + Hlw.cf_pulse_last_time = us; + Hlw.load_off = false; + } else { + Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; + Hlw.cf_pulse_last_time = us; + Hlw.energy_period_counter++; + } + Energy.data_valid[0] = 0; +} + +void HlwCf1Interrupt(void) +{ + uint32_t us = micros(); + + Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; + Hlw.cf1_pulse_last_time = us; + if ((Hlw.cf1_timer > 2) && (Hlw.cf1_timer < 8)) { + Hlw.cf1_summed_pulse_length += Hlw.cf1_pulse_length; +#ifdef HLW_DEBUG + Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; +#endif + Hlw.cf1_pulse_counter++; + if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { + Hlw.cf1_timer = 8; + } + } + Energy.data_valid[0] = 0; +} + + + +void HlwEvery200ms(void) +{ + uint32_t cf1_pulse_length = 0; + uint32_t hlw_w = 0; + uint32_t hlw_u = 0; + uint32_t hlw_i = 0; + + if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { + Hlw.cf_pulse_length = 0; + Hlw.load_off = true; + } + Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; + + if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { + hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; + Energy.active_power[0] = (float)hlw_w / 10; + Hlw.power_retry = 1; + } else { + if (Hlw.power_retry) { + Hlw.power_retry--; + } else { + Energy.active_power[0] = 0; + } + } + + if (PinUsed(GPIO_NRG_CF1)) { + Hlw.cf1_timer++; + if (Hlw.cf1_timer >= 8) { + Hlw.cf1_timer = 0; + Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; + DigitalWrite(GPIO_NRG_SEL, 0, Hlw.select_ui_flag); + + if (Hlw.cf1_pulse_counter) { + cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; + } + +#ifdef HLW_DEBUG + + char stemp[100]; + stemp[0] = '\0'; + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); + } + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { + if (Hlw.debug[i] > Hlw.debug[j]) { + std::swap(Hlw.debug[i], Hlw.debug[j]); + } + } + } + uint32_t median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), + Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); +#endif + + if (Hlw.select_ui_flag == Hlw.ui_flag) { + Hlw.cf1_voltage_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { + hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; + Energy.voltage[0] = (float)hlw_u / 10; + } else { + Energy.voltage[0] = 0; + } + + } else { + Hlw.cf1_current_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { + hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; + Energy.current[0] = (float)hlw_i / 1000; + } else { + Energy.current[0] = 0; + } + + } + Hlw.cf1_summed_pulse_length = 0; + Hlw.cf1_pulse_counter = 0; + } + } +} + +void HlwEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Hlw.cf1_voltage_pulse_length = 0; + Hlw.cf1_current_pulse_length = 0; + Hlw.cf_power_pulse_length = 0; + } else { + uint32_t hlw_len; + + if (Hlw.energy_period_counter) { + hlw_len = 10000 * 100 / Hlw.energy_period_counter; + Hlw.energy_period_counter = 0; + if (hlw_len) { + Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 100) / hlw_len; + EnergyUpdateToday(); + } + } + } +} + +void HlwSnsInit(void) +{ + if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) { + Settings.energy_power_calibration = HLW_PREF_PULSE; + Settings.energy_voltage_calibration = HLW_UREF_PULSE; + Settings.energy_current_calibration = HLW_IREF_PULSE; + } + + if (Hlw.model_type) { + Hlw.power_ratio = HJL_PREF; + Hlw.voltage_ratio = HJL_UREF; + Hlw.current_ratio = HJL_IREF; + } else { + Hlw.power_ratio = HLW_PREF; + Hlw.voltage_ratio = HLW_UREF; + Hlw.current_ratio = HLW_IREF; + } + + if (PinUsed(GPIO_NRG_SEL)) { + pinMode(Pin(GPIO_NRG_SEL), OUTPUT); + digitalWrite(Pin(GPIO_NRG_SEL), Hlw.select_ui_flag); + } + if (PinUsed(GPIO_NRG_CF1)) { + pinMode(Pin(GPIO_NRG_CF1), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_NRG_CF1), HlwCf1Interrupt, FALLING); + } + pinMode(Pin(GPIO_HLW_CF), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_HLW_CF), HlwCfInterrupt, FALLING); +} + +void HlwDrvInit(void) +{ + Hlw.model_type = 0; + if (PinUsed(GPIO_HJL_CF)) { + SetPin(Pin(GPIO_HJL_CF), AGPIO(GPIO_HLW_CF)); + Hlw.model_type = 1; + } + + if (PinUsed(GPIO_HLW_CF)) { + + Hlw.ui_flag = true; + if (PinUsed(GPIO_NRG_SEL_INV)) { + SetPin(Pin(GPIO_NRG_SEL_INV), AGPIO(GPIO_NRG_SEL)); + Hlw.ui_flag = false; + } + + if (PinUsed(GPIO_NRG_CF1)) { + if (!PinUsed(GPIO_NRG_SEL)) { + Energy.current_available = false; + } + } else { + Energy.current_available = false; + Energy.voltage_available = false; + } + + energy_flg = XNRG_01; + } +} + +bool HlwCommand(void) +{ + bool serviced = true; + + if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { + Settings.energy_power_calibration = ((uint32_t)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { + Settings.energy_voltage_calibration = ((uint32_t)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { + Settings.energy_current_calibration = ((uint32_t)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_200_MSECOND: + HlwEvery200ms(); + break; + case FUNC_ENERGY_EVERY_SECOND: + HlwEverySecond(); + break; + case FUNC_COMMAND: + result = HlwCommand(); + break; + case FUNC_INIT: + HlwSnsInit(); + break; + case FUNC_PRE_INIT: + HlwDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_02_cse7766.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_02_cse7766.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_CSE7766 +# 31 "/workspace/Tasmota/tasmota/xnrg_02_cse7766.ino" +#define XNRG_02 2 + +#define CSE_MAX_INVALID_POWER 128 + +#define CSE_NOT_CALIBRATED 0xAA + +#define CSE_PULSES_NOT_INITIALIZED -1 + +#define CSE_PREF 1000 +#define CSE_UREF 100 + +#define CSE_BUFFER_SIZE 25 + +#include + +TasmotaSerial *CseSerial = nullptr; + +struct CSE { + long voltage_cycle = 0; + long current_cycle = 0; + long power_cycle = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + + int byte_counter = 0; + uint8_t *rx_buffer = nullptr; + uint8_t power_invalid = 0; + bool received = false; +} Cse; + +void CseReceived(void) { + + + + + + + uint8_t header = Cse.rx_buffer[0]; + if ((header & 0xFC) == 0xFC) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); + return; + } + + + if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { + long voltage_coefficient = 191200; + if (CSE_NOT_CALIBRATED != header) { + voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4]; + } + Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; + } + if (HLW_IREF_PULSE == Settings.energy_current_calibration) { + long current_coefficient = 16140; + if (CSE_NOT_CALIBRATED != header) { + current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10]; + } + Settings.energy_current_calibration = current_coefficient; + } + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + long power_coefficient = 5364000; + if (CSE_NOT_CALIBRATED != header) { + power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16]; + } + Settings.energy_power_calibration = power_coefficient / CSE_PREF; + } + + uint8_t adjustement = Cse.rx_buffer[20]; + Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7]; + Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13]; + Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19]; + Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22]; + + if (Energy.power_on) { + if (adjustement & 0x40) { + Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; + } + if (adjustement & 0x10) { + Cse.power_invalid = 0; + if ((header & 0xF2) == 0xF2) { + Energy.active_power[0] = 0; + } else { + if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } + if (Cse.power_cycle_first != Cse.power_cycle) { + Cse.power_cycle_first = -1; + Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; + } else { + Energy.active_power[0] = 0; + } + } + } else { + if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { + Cse.power_invalid++; + } else { + Cse.power_cycle_first = 0; + Energy.active_power[0] = 0; + } + } + if (adjustement & 0x20) { + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; + } + } + } else { + Cse.power_cycle_first = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; + } +} + +void CseSerialInput(void) { + while (CseSerial->available()) { + yield(); + uint8_t serial_in_byte = CseSerial->read(); + + if (Cse.received) { + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + if (24 == Cse.byte_counter) { + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24); + + uint8_t checksum = 0; + for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; } + if (checksum == Cse.rx_buffer[23]) { + Energy.data_valid[0] = 0; + CseReceived(); + Cse.received = false; + return; + } else { + do { + memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24); + Cse.byte_counter--; + } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); + if (0x5A != Cse.rx_buffer[1]) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); + Cse.received = false; + Cse.byte_counter = 0; + } + } + } + } else { + if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { + Cse.received = true; + } else { + Cse.byte_counter = 0; + } + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + } + } +} + + + +void CseEverySecond(void) { + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Cse.voltage_cycle = 0; + Cse.current_cycle = 0; + Cse.power_cycle = 0; + } else { + if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + } else { + uint32_t cf_pulses = 0; + if (Cse.cf_pulses < Cse.cf_pulses_last_time) { + cf_pulses = (0x10000 - Cse.cf_pulses_last_time) + Cse.cf_pulses; + } else { + cf_pulses = Cse.cf_pulses - Cse.cf_pulses_last_time; + } + if (cf_pulses && Energy.active_power[0]) { + uint32_t delta = (cf_pulses * Settings.energy_power_calibration) / 36; + + + if (delta <= (4000 * 1000 / 36)) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + Energy.kWhtoday_delta += delta; + } + else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: Overload")); + Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + } + EnergyUpdateToday(); + } + } + } +} + +void CseSnsInit(void) { + + + CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), -1, 1); + if (CseSerial->begin(4800, 2)) { + if (CseSerial->hardwareSerial()) { + SetSerial(4800, TS_SERIAL_8E1); + ClaimSerial(); + } + if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { + Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; + } + Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; + } else { + energy_flg = ENERGY_NONE; + } +} + +void CseDrvInit(void) { + + if (PinUsed(GPIO_CSE7766_RX)) { + Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); + if (Cse.rx_buffer != nullptr) { + energy_flg = XNRG_02; + } + } +} + +bool CseCommand(void) { + bool serviced = true; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.power_cycle) { + Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.voltage_cycle) { + Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.current_cycle) { + Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg02(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (CseSerial) { CseSerialInput(); } + break; + case FUNC_EVERY_SECOND: + CseEverySecond(); + break; + case FUNC_COMMAND: + result = CseCommand(); + break; + case FUNC_INIT: + CseSnsInit(); + break; + case FUNC_PRE_INIT: + CseDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_03_pzem004t.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM004T +# 31 "/workspace/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#define XNRG_03 3 + +const uint32_t PZEM_STABILIZE = 30; + +#include + +TasmotaSerial *PzemSerial = nullptr; + +#define PZEM_VOLTAGE (uint8_t)0xB0 +#define RESP_VOLTAGE (uint8_t)0xA0 + +#define PZEM_CURRENT (uint8_t)0xB1 +#define RESP_CURRENT (uint8_t)0xA1 + +#define PZEM_POWER (uint8_t)0xB2 +#define RESP_POWER (uint8_t)0xA2 + +#define PZEM_ENERGY (uint8_t)0xB3 +#define RESP_ENERGY (uint8_t)0xA3 + +#define PZEM_SET_ADDRESS (uint8_t)0xB4 +#define RESP_SET_ADDRESS (uint8_t)0xA4 + +#define PZEM_POWER_ALARM (uint8_t)0xB5 +#define RESP_POWER_ALARM (uint8_t)0xA5 + +#define PZEM_DEFAULT_READ_TIMEOUT 500 + + + +struct PZEM { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t read_state = 0; + uint8_t phase = 0; + uint8_t address = 0; +} Pzem; + +struct PZEMCommand { + uint8_t command; + uint8_t addr[4]; + uint8_t data; + uint8_t crc; +}; + +uint8_t PzemCrc(uint8_t *data) +{ + uint16_t crc = 0; + for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; + } + return (uint8_t)(crc & 0xFF); +} + +void PzemSend(uint8_t cmd) +{ + PZEMCommand pzem; + + pzem.command = cmd; + pzem.addr[0] = 192; + pzem.addr[1] = 168; + pzem.addr[2] = 1; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; + pzem.data = 0; + + uint8_t *bytes = (uint8_t*)&pzem; + pzem.crc = PzemCrc(bytes); + + PzemSerial->flush(); + PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; +} + +bool PzemReceiveReady(void) +{ + return PzemSerial->available() >= (int)sizeof(PZEMCommand); +} + +bool PzemRecieve(uint8_t resp, float *data) +{ +# 124 "/workspace/Tasmota/tasmota/xnrg_03_pzem004t.ino" + uint8_t buffer[sizeof(PZEMCommand)] = { 0 }; + + unsigned long start = millis(); + uint8_t len = 0; + while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { + if (PzemSerial->available() > 0) { + uint8_t c = (uint8_t)PzemSerial->read(); + if (!len && ((c & 0xF8) != 0xA0)) { + continue; + } + buffer[len++] = c; + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); + + if (len != sizeof(PZEMCommand)) { + + return false; + } + if (buffer[6] != PzemCrc(buffer)) { + + return false; + } + if (buffer[0] != resp) { + + return false; + } + + switch (resp) { + case RESP_VOLTAGE: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); + break; + case RESP_CURRENT: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); + break; + case RESP_POWER: + *data = (float)(buffer[1] << 8) + buffer[2]; + break; + case RESP_ENERGY: + *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; + break; + } + return true; +} + + + +const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; +const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; + +void PzemEvery250ms(void) +{ + bool data_ready = PzemReceiveReady(); + + if (data_ready) { + float value = 0; + if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { + Energy.data_valid[Pzem.phase] = 0; + switch (Pzem.read_state) { + case 1: + Energy.voltage[Pzem.phase] = value; + break; + case 2: + Energy.current[Pzem.phase] = value; + break; + case 3: + Energy.active_power[Pzem.phase] = value; + break; + case 4: + Pzem.energy += value; + if (Pzem.phase == Energy.phase_count -1) { + if (Pzem.energy > Pzem.last_energy) { + if (uptime > PZEM_STABILIZE) { + EnergyUpdateTotal(Pzem.energy, false); + } + Pzem.last_energy = Pzem.energy; + } + Pzem.energy = 0; + } + break; + } + Pzem.read_state++; + if (5 == Pzem.read_state) { + Pzem.read_state = 1; + } + + + } + } + + if (0 == Pzem.send_retry || data_ready) { + if (1 == Pzem.read_state) { + if (0 == Pzem.phase) { + Pzem.phase = Energy.phase_count -1; + } else { + Pzem.phase--; + } + + + } + + if (Pzem.address) { + Pzem.read_state = 0; + } + + Pzem.send_retry = 5; + PzemSend(pzem_commands[Pzem.read_state]); + } + else { + Pzem.send_retry--; + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemSnsInit(void) +{ + + PzemSerial = new TasmotaSerial(Pin(GPIO_PZEM004_RX), Pin(GPIO_PZEM0XX_TX), 1); + if (PzemSerial->begin(9600)) { + if (PzemSerial->hardwareSerial()) { + ClaimSerial(); + } + Energy.phase_count = 3; + Pzem.phase = 0; + Pzem.read_state = 1; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDrvInit(void) +{ + if (PinUsed(GPIO_PZEM004_RX) && PinUsed(GPIO_PZEM0XX_TX)) { + energy_flg = XNRG_03; + } +} + +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Pzem.address = XdrvMailbox.payload; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } + break; + case FUNC_COMMAND: + result = PzemCommand(); + break; + case FUNC_INIT: + PzemSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_MCP39F501 + + + + + + + +#define XNRG_04 4 + +#define MCP_BAUDRATE 4800 +#define MCP_TIMEOUT 4 +#define MCP_CALIBRATION_TIMEOUT 2 + +#define MCP_CALIBRATE_POWER 0x001 +#define MCP_CALIBRATE_VOLTAGE 0x002 +#define MCP_CALIBRATE_CURRENT 0x004 +#define MCP_CALIBRATE_FREQUENCY 0x008 +#define MCP_SINGLE_WIRE_FLAG 0x100 + +#define MCP_START_FRAME 0xA5 +#define MCP_ACK_FRAME 0x06 +#define MCP_ERROR_NAK 0x15 +#define MCP_ERROR_CRC 0x51 + +#define MCP_SINGLE_WIRE 0xAB + +#define MCP_SET_ADDRESS 0x41 + +#define MCP_READ 0x4E +#define MCP_READ_16 0x52 +#define MCP_READ_32 0x44 + +#define MCP_WRITE 0x4D +#define MCP_WRITE_16 0x57 +#define MCP_WRITE_32 0x45 + +#define MCP_SAVE_REGISTERS 0x53 + +#define MCP_CALIBRATION_BASE 0x0028 +#define MCP_CALIBRATION_LEN 52 + +#define MCP_FREQUENCY_REF_BASE 0x0094 +#define MCP_FREQUENCY_GAIN_BASE 0x00AE +#define MCP_FREQUENCY_LEN 4 + +#define MCP_BUFFER_SIZE 60 + +#include +TasmotaSerial *McpSerial = nullptr; + +typedef struct mcp_cal_registers_type { + uint16_t gain_current_rms; + uint16_t gain_voltage_rms; + uint16_t gain_active_power; + uint16_t gain_reactive_power; + sint32_t offset_current_rms; + sint32_t offset_active_power; + sint32_t offset_reactive_power; + sint16_t dc_offset_current; + sint16_t phase_compensation; + uint16_t apparent_power_divisor; + + uint32_t system_configuration; + uint16_t dio_configuration; + uint32_t range; + + uint32_t calibration_current; + uint16_t calibration_voltage; + uint32_t calibration_active_power; + uint32_t calibration_reactive_power; + uint16_t accumulation_interval; +} mcp_cal_registers_type; + +char *mcp_buffer = nullptr; +unsigned long mcp_window = 0; +unsigned long mcp_kWhcounter = 0; +uint32_t mcp_system_configuration = 0x03000000; +uint32_t mcp_active_power; + + +uint32_t mcp_current_rms; +uint16_t mcp_voltage_rms; +uint16_t mcp_line_frequency; + +uint8_t mcp_address = 0; +uint8_t mcp_calibration_active = 0; +uint8_t mcp_init = 0; +uint8_t mcp_timeout = 0; +uint8_t mcp_calibrate = 0; +uint8_t mcp_byte_counter = 0; + + + + + + +uint8_t McpChecksum(uint8_t *data) +{ + uint8_t checksum = 0; + uint8_t offset = 0; + uint8_t len = data[1] -1; + + for (uint32_t i = offset; i < len; i++) { checksum += data[i]; } + return checksum; +} + +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) +{ + unsigned long result = 0; + unsigned long pow = 1; + + for (uint32_t i = 0; i < size; i++) { + result = result + (uint8_t)data[offset + i] * pow; + pow = pow * 256; + } + return result; +} + +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) +{ + for (uint32_t i = 0; i < size; i++) { + data[offset + i] = ((value >> (i * 8)) & 0xFF); + } +} + +void McpSend(uint8_t *data) +{ + if (mcp_timeout) { return; } + mcp_timeout = MCP_TIMEOUT; + + data[0] = MCP_START_FRAME; + data[data[1] -1] = McpChecksum(data); + + + + for (uint32_t i = 0; i < data[1]; i++) { + McpSerial->write(data[i]); + } +} + + + +void McpGetAddress(void) +{ + uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpAddressReceive(void) +{ + + mcp_address = mcp_buffer[3]; +} + + + +void McpGetCalibration(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; + + McpSend(data); +} + +void McpParseCalibration(void) +{ + bool action = false; + mcp_cal_registers_type cal_registers; + + + cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); + cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); + cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); + cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); + cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); + cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); + cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); + cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); + cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); + cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); + + cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); + cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); + cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); + + cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); + cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); + cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); + cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); + cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); + + if (mcp_calibrate & MCP_CALIBRATE_POWER) { + cal_registers.calibration_active_power = Settings.energy_power_calibration; + if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { + cal_registers.calibration_voltage = Settings.energy_voltage_calibration; + if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { + cal_registers.calibration_current = Settings.energy_current_calibration; + if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } + } + mcp_timeout = 0; + if (action) { McpSetCalibration(&cal_registers); } + + mcp_calibrate = 0; + + Settings.energy_power_calibration = cal_registers.calibration_active_power; + Settings.energy_voltage_calibration = cal_registers.calibration_voltage; + Settings.energy_current_calibration = cal_registers.calibration_current; + + mcp_system_configuration = cal_registers.system_configuration; + + if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { + mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; + McpSetSystemConfiguration(2); + } +} + +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) +{ + uint32_t measured; + uint32_t expected; + uint16_t *gain; + uint32_t new_gain; + + if (range_shift == 0) { + measured = mcp_voltage_rms; + expected = cal_registers->calibration_voltage; + gain = &(cal_registers->gain_voltage_rms); + } else if (range_shift == 8) { + measured = mcp_current_rms; + expected = cal_registers->calibration_current; + gain = &(cal_registers->gain_current_rms); + } else if (range_shift == 16) { + measured = mcp_active_power; + expected = cal_registers->calibration_active_power; + gain = &(cal_registers->gain_active_power); + } else { + return false; + } + + if (measured == 0) { + return false; + } + + uint32_t range = (cal_registers->range >> range_shift) & 0xFF; + +calc: + new_gain = (*gain) * expected / measured; + + if (new_gain < 25000) { + range++; + if (measured > 6) { + measured = measured / 2; + goto calc; + } + } + + if (new_gain > 55000) { + range--; + measured = measured * 2; + goto calc; + } + + *gain = new_gain; + uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; + cal_registers->range = cal_registers->range ^ (old_range << range_shift); + cal_registers->range = cal_registers->range | (range << range_shift); + + return true; +} + + + + + + +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) +{ + uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; + + data[5] = MCP_WRITE; + data[6] = MCP_CALIBRATION_LEN; + + McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); + McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); + McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); + McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); + McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); + McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); + McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); + McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); + McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); + McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); + + McpSetInt(cal_registers->system_configuration, data, 26+7, 4); + McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); + McpSetInt(cal_registers->range, data, 32+7, 4); + + McpSetInt(cal_registers->calibration_current, data, 36+7, 4); + McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); + McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); + McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); + McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); + + data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; + data[MCP_CALIBRATION_LEN+8] = mcp_address; + + McpSend(data); +} + + + +void McpSetSystemConfiguration(uint16 interval) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = 0x00; + data[ 4] = 0x42; + data[ 5] = MCP_WRITE_32; + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; + data[10] = MCP_SET_ADDRESS; + data[11] = 0x00; + data[12] = 0x5A; + data[13] = MCP_WRITE_16; + data[14] = (interval >> 8) & 0xFF; + data[15] = (interval >> 0) & 0xFF; + + McpSend(data); +} + + + +void McpGetFrequency(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, + MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpParseFrequency(void) +{ + + uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; + uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; + + if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { + line_frequency_ref = Settings.energy_frequency_calibration; + + if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { + mcp_line_frequency = 50000; + gain_line_frequency = 0x8000; + } + gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; + + mcp_timeout = 0; + McpSetFrequency(line_frequency_ref, gain_line_frequency); + } + + Settings.energy_frequency_calibration = line_frequency_ref; + + mcp_calibrate = 0; +} + +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; + data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; + + data[ 5] = MCP_WRITE_16; + data[ 6] = (line_frequency_ref >> 8) & 0xFF; + data[ 7] = (line_frequency_ref >> 0) & 0xFF; + + data[ 8] = MCP_SET_ADDRESS; + data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; + data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; + + data[11] = MCP_WRITE_16; + data[12] = (gain_line_frequency >> 8) & 0xFF; + data[13] = (gain_line_frequency >> 0) & 0xFF; + + data[14] = MCP_SAVE_REGISTERS; + data[15] = mcp_address; + + McpSend(data); +} + + + +void McpGetData(void) +{ + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; + + McpSend(data); +} + +void McpParseData(void) +{ + + + + + + mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); + mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); + mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); + + + mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); + + if (Energy.power_on) { + Energy.data_valid[0] = 0; + Energy.frequency[0] = (float)mcp_line_frequency / 1000; + Energy.voltage[0] = (float)mcp_voltage_rms / 10; + Energy.active_power[0] = (float)mcp_active_power / 100; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)mcp_current_rms / 10000; + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + } +} + + + +void McpSerialInput(void) +{ + while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { + yield(); + mcp_buffer[mcp_byte_counter++] = McpSerial->read(); + mcp_window = millis(); + } + + + if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); + + if (MCP_BUFFER_SIZE == mcp_byte_counter) { + + } + else if (1 == mcp_byte_counter) { + if (MCP_ERROR_CRC == mcp_buffer[0]) { + + mcp_timeout = 0; + } + else if (MCP_ERROR_NAK == mcp_buffer[0]) { + + mcp_timeout = 0; + } + } + else if (MCP_ACK_FRAME == mcp_buffer[0]) { + if (mcp_byte_counter == mcp_buffer[1]) { + + if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { + if (5 == mcp_buffer[1]) { McpAddressReceive(); } + if (25 == mcp_buffer[1]) { McpParseData(); } + if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } + if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } + } + + } + mcp_timeout = 0; + } + else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { + mcp_timeout = 0; + } + + mcp_byte_counter = 0; + McpSerial->flush(); + } +} + + + +void McpEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + mcp_voltage_rms = 0; + mcp_current_rms = 0; + mcp_active_power = 0; + mcp_line_frequency = 0; + } + + if (mcp_active_power) { + Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); + EnergyUpdateToday(); + } + + if (mcp_timeout) { + mcp_timeout--; + } + else if (mcp_calibration_active) { + mcp_calibration_active--; + } + else if (mcp_init) { + if (2 == mcp_init) { + McpGetCalibration(); + } + else if (1 == mcp_init) { + McpGetFrequency(); + } + mcp_init--; + } + else if (!mcp_address) { + McpGetAddress(); + } + else { + McpGetData(); + } +} + +void McpSnsInit(void) +{ + + McpSerial = new TasmotaSerial(Pin(GPIO_MCP39F5_RX), Pin(GPIO_MCP39F5_TX), 1); + if (McpSerial->begin(MCP_BAUDRATE)) { + if (McpSerial->hardwareSerial()) { + ClaimSerial(); + mcp_buffer = serial_in_buffer; + } else { + mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); + } + DigitalWrite(GPIO_MCP39F5_RST, 0, 1); + } else { + energy_flg = ENERGY_NONE; + } +} + +void McpDrvInit(void) +{ + if (PinUsed(GPIO_MCP39F5_RX) && PinUsed(GPIO_MCP39F5_TX)) { + if (PinUsed(GPIO_MCP39F5_RST)) { + pinMode(Pin(GPIO_MCP39F5_RST), OUTPUT); + digitalWrite(Pin(GPIO_MCP39F5_RST), 0); + } + mcp_calibrate = 0; + mcp_timeout = 2; + mcp_init = 2; + energy_flg = XNRG_04; + } +} + +bool McpCommand(void) +{ + bool serviced = true; + unsigned long value = 0; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_active_power) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_POWER; + McpGetCalibration(); + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_voltage_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 1000) && (value < 2600)) { + Settings.energy_voltage_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; + McpGetCalibration(); + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_current_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 100) && (value < 80000)) { + Settings.energy_current_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_CURRENT; + McpGetCalibration(); + } + } + } + else if (CMND_FREQUENCYSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_line_frequency) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); + if ((value > 45000) && (value < 65000)) { + Settings.energy_frequency_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; + McpGetFrequency(); + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (McpSerial) { McpSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + if (McpSerial) { McpEverySecond(); } + break; + case FUNC_COMMAND: + result = McpCommand(); + break; + case FUNC_INIT: + McpSnsInit(); + break; + case FUNC_PRE_INIT: + McpDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_AC +# 33 "/workspace/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#define XNRG_05 5 + +const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_AC_STABILIZE = 30; + +#include +TasmotaModbus *PzemAcModbus; + +struct PZEMAC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemAc; + +void PzemAcEverySecond(void) +{ + bool data_ready = PzemAcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[30]; + + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); + } else { + Energy.data_valid[PzemAc.phase] = 0; + if (10 == registers) { + + + + + + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; + + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); + if (PzemAc.phase == Energy.phase_count -1) { + if (PzemAc.energy > PzemAc.last_energy) { + if (uptime > PZEM_AC_STABILIZE) { + EnergyUpdateTotal(PzemAc.energy, false); + } + PzemAc.last_energy = PzemAc.energy; + } + PzemAc.energy = 0; + } + + } + } + } + + if (0 == PzemAc.send_retry || data_ready) { + if (0 == PzemAc.phase) { + PzemAc.phase = Energy.phase_count -1; + } else { + PzemAc.phase--; + } + PzemAc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } + } + else { + PzemAc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemAcSnsInit(void) +{ + PzemAcModbus = new TasmotaModbus(Pin(GPIO_PZEM016_RX), Pin(GPIO_PZEM0XX_TX)); + uint8_t result = PzemAcModbus->Begin(9600); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + PzemAc.phase = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemAcDrvInit(void) +{ + if (PinUsed(GPIO_PZEM016_RX) && PinUsed(GPIO_PZEM0XX_TX)) { + energy_flg = XNRG_05; + } +} + +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemAcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; + case FUNC_INIT: + PzemAcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemAcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_DC +# 32 "/workspace/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#define XNRG_06 6 + +const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_DC_STABILIZE = 30; + +#include +TasmotaModbus *PzemDcModbus; + +struct PZEMDC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemDc; + +void PzemDcEverySecond(void) +{ + bool data_ready = PzemDcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[26]; + + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); + } else { + Energy.data_valid[PzemDc.channel] = 0; + if (8 == registers) { + + + + + + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; + + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); + if (PzemDc.channel == Energy.phase_count -1) { + if (PzemDc.energy > PzemDc.last_energy) { + if (uptime > PZEM_DC_STABILIZE) { + EnergyUpdateTotal(PzemDc.energy, false); + } + PzemDc.last_energy = PzemDc.energy; + } + PzemDc.energy = 0; + } + } + } + } + + if (0 == PzemDc.send_retry || data_ready) { + if (0 == PzemDc.channel) { + PzemDc.channel = Energy.phase_count -1; + } else { + PzemDc.channel--; + } + PzemDc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } + } + else { + PzemDc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemDcSnsInit(void) +{ + PzemDcModbus = new TasmotaModbus(Pin(GPIO_PZEM017_RX), Pin(GPIO_PZEM0XX_TX)); + uint8_t result = PzemDcModbus->Begin(9600, 2); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.type_dc = true; + Energy.phase_count = 3; + PzemDc.channel = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDcDrvInit(void) +{ + if (PinUsed(GPIO_PZEM017_RX) && PinUsed(GPIO_PZEM0XX_TX)) { + energy_flg = XNRG_06; + } +} + +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg06(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemDcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; + case FUNC_INIT: + PzemDcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_07_ade7953.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_07_ade7953.ino" +#ifdef USE_I2C +#ifdef USE_ENERGY_SENSOR +#ifdef USE_ADE7953 +# 31 "/workspace/Tasmota/tasmota/xnrg_07_ade7953.ino" +#define XNRG_07 7 +#define XI2C_07 7 + +#define ADE7953_PREF 1540 +#define ADE7953_UREF 26000 +#define ADE7953_IREF 10000 + +#define ADE7953_ADDR 0x38 + +const uint16_t Ade7953Registers[] { + 0x31B, + 0x313, + 0x311, + 0x315, + 0x31A, + 0x312, + 0x310, + 0x314, + 0x31C, + 0x10E +}; + +struct Ade7953 { + uint32_t voltage_rms = 0; + uint32_t period = 0; + uint32_t current_rms[2] = { 0, 0 }; + uint32_t active_power[2] = { 0, 0 }; + uint8_t init_step = 0; +} Ade7953; + +int Ade7953RegSize(uint16_t reg) +{ + int size = 0; + switch ((reg >> 8) & 0x0F) { + case 0x03: + size++; + case 0x02: + size++; + case 0x01: + size++; + case 0x00: + case 0x07: + case 0x08: + size++; + } + return size; +} + +void Ade7953Write(uint16_t reg, uint32_t val) +{ + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + while (size--) { + Wire.write((val >> (8 * size)) & 0xFF); + } + Wire.endTransmission(); + delayMicroseconds(5); + } +} + +int32_t Ade7953Read(uint16_t reg) +{ + uint32_t response = 0; + + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + Wire.endTransmission(0); + Wire.requestFrom(ADE7953_ADDR, size); + if (size <= Wire.available()) { + for (uint32_t i = 0; i < size; i++) { + response = response << 8 | Wire.read(); + } + } + } + return response; +} + +void Ade7953Init(void) +{ + Ade7953Write(0x102, 0x0004); + Ade7953Write(0x0FE, 0x00AD); + Ade7953Write(0x120, 0x0030); +} + +void Ade7953GetData(void) +{ + int32_t reg[2][4]; + for (uint32_t i = 0; i < sizeof(Ade7953Registers)/sizeof(uint16_t); i++) { + int32_t value = Ade7953Read(Ade7953Registers[i]); + if (8 == i) { + Ade7953.voltage_rms = value; + } else if (9 == i) { + Ade7953.period = value; + } else { + reg[i >> 2][i &3] = value; + } + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + Ade7953.voltage_rms, Ade7953.period, + reg[0][0], reg[0][1], reg[0][2], reg[0][3], + reg[1][0], reg[1][1], reg[1][2], reg[1][3]); + + uint32_t apparent_power[2] = { 0, 0 }; + uint32_t reactive_power[2] = { 0, 0 }; + + for (uint32_t channel = 0; channel < 2; channel++) { + Ade7953.current_rms[channel] = reg[channel][0]; + if (Ade7953.current_rms[channel] < 2000) { + Ade7953.current_rms[channel] = 0; + Ade7953.active_power[channel] = 0; + } else { + Ade7953.active_power[channel] = abs(reg[channel][1]); + apparent_power[channel] = abs(reg[channel][2]); + reactive_power[channel] = abs(reg[channel][3]); + } + } + + uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; + uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), + Ade7953.voltage_rms, Ade7953.period, + Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, + Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); + + if (Energy.power_on) { + Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; + Energy.frequency[0] = 223750.0f / ( (float)Ade7953.period + 1); + + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.data_valid[channel] = 0; + Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); + Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); + Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); + if (0 == Energy.active_power[channel]) { + Energy.current[channel] = 0; + } else { + Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); + } + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + Energy.data_valid[1] = ENERGY_WATCHDOG; + } + + if (active_power_sum) { + Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); + EnergyUpdateToday(); + } +} + +void Ade7953EnergyEverySecond(void) +{ + if (Ade7953.init_step) { + if (1 == Ade7953.init_step) { + Ade7953Init(); + } + Ade7953.init_step--; + } else { + Ade7953GetData(); + } +} + +void Ade7953DrvInit(void) +{ + if (PinUsed(GPIO_ADE7953_IRQ)) { + pinMode(Pin(GPIO_ADE7953_IRQ), INPUT); + delay(100); + if (I2cSetDevice(ADE7953_ADDR)) { + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + Settings.energy_power_calibration = ADE7953_PREF; + Settings.energy_voltage_calibration = ADE7953_UREF; + Settings.energy_current_calibration = ADE7953_IREF; + } + I2cSetActiveFound(ADE7953_ADDR, "ADE7953"); + Ade7953.init_step = 2; + Energy.phase_count = 2; + Energy.voltage_common = true; + Energy.frequency_common = true; + energy_flg = XNRG_07; + } + } +} + +bool Ade7953Command(void) +{ + bool serviced = true; + + uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; + uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); + + if (CMND_POWERCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } + + } + else if (CMND_VOLTAGECAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } + + } + else if (CMND_CURRENTCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.voltage_rms) { + if ((value > 10000) && (value < 26000)) { + Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { + if ((value > 2000) && (value < 1000000)) { + Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg07(uint8_t function) +{ + if (!I2cEnabled(XI2C_07)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + Ade7953EnergyEverySecond(); + break; + case FUNC_COMMAND: + result = Ade7953Command(); + break; + case FUNC_PRE_INIT: + Ade7953DrvInit(); + break; + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_08_sdm120.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_08_sdm120.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM120 + + + + + + +#define XNRG_08 8 + + +#ifndef SDM120_SPEED + #define SDM120_SPEED 2400 +#endif + +#ifndef SDM120_ADDR + #define SDM120_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm120Modbus; + +const uint8_t sdm120_table = 8; +const uint8_t sdm220_table = 13; + +const uint16_t sdm120_start_addresses[] { + 0x0000, + 0x0006, + 0x000C, + 0x0012, + 0x0018, + 0x001E, + 0x0046, + 0x0156, + + 0X0048, + 0X004A, + 0X004C, + 0X004E, + 0X0024 +}; + +struct SDM120 { + float total_active = 0; + float import_active = NAN; + float import_reactive = 0; + float export_reactive = 0; + float phase_angle = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = sdm220_table; +} Sdm120; + + + +void SDM120Every250ms(void) +{ + bool data_ready = Sdm120Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm120.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value; + break; + + case 3: + Energy.apparent_power[0] = value; + break; + + case 4: + Energy.reactive_power[0] = value; + break; + + case 5: + Energy.power_factor[0] = value; + break; + + case 6: + Energy.frequency[0] = value; + break; + + case 7: + Sdm120.total_active = value; + break; + + case 8: + Sdm120.import_active = value; + break; + + case 9: + Energy.export_active[0] = value; + break; + + case 10: + Sdm120.import_reactive = value; + break; + + case 11: + Sdm120.export_reactive = value; + break; + + case 12: + Sdm120.phase_angle = value; + break; + } + + Sdm120.read_state++; + if (Sdm120.read_state == Sdm120.start_address_count) { + Sdm120.read_state = 0; + + if (Sdm120.start_address_count > sdm120_table) { + if (!isnan(Sdm120.import_active)) { + Sdm120.total_active = Sdm120.import_active; + } else { + Sdm120.start_address_count = sdm120_table; + } + } + EnergyUpdateTotal(Sdm120.total_active, true); + } + } + } + + if (0 == Sdm120.send_retry || data_ready) { + Sdm120.send_retry = 5; + Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); + } else { + Sdm120.send_retry--; + } +} + +void Sdm120SnsInit(void) +{ + Sdm120Modbus = new TasmotaModbus(Pin(GPIO_SDM120_RX), Pin(GPIO_SDM120_TX)); + uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm120DrvInit(void) +{ + if (PinUsed(GPIO_SDM120_RX) && PinUsed(GPIO_SDM120_TX)) { + energy_flg = XNRG_08; + } +} + +void Sdm220Reset(void) +{ + if (isnan(Sdm120.import_active)) { return; } + + Sdm120.import_active = 0; + Sdm120.import_reactive = 0; + Sdm120.export_reactive = 0; + Sdm120.phase_angle = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SDM220[] PROGMEM = + "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; +#endif + +void Sdm220Show(bool json) +{ + if (isnan(Sdm120.import_active)) { return; } + + char import_active_chr[FLOATSZ]; + dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); + char import_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); + char export_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); + char phase_angle_chr[FLOATSZ]; + dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), + import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#endif + } +} + + + + + +bool Xnrg08(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM120Every250ms(); } + break; + case FUNC_JSON_APPEND: + Sdm220Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sdm220Show(0); + break; +#endif + case FUNC_ENERGY_RESET: + Sdm220Reset(); + break; + case FUNC_INIT: + Sdm120SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm120DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_09_dds2382.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_09_dds2382.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDS2382 + + + + + + +#define XNRG_09 9 + +#ifndef DDS2382_SPEED +#define DDS2382_SPEED 9600 +#endif +#ifndef DDS2382_ADDR +#define DDS2382_ADDR 1 +#endif + +#include +TasmotaModbus *Dds2382Modbus; + +uint8_t Dds2382_send_retry = 0; + +void Dds2382EverySecond(void) +{ + bool data_ready = Dds2382Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[46]; + + uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 67 "/workspace/Tasmota/tasmota/xnrg_09_dds2382.ino" + Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; + Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; + uint8_t offset = 11; + if (Settings.flag3.dds2382_model) { + offset = 19; + } + Energy.export_active[0] = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; + float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; + + EnergyUpdateTotal(import_active, true); + } + } + + if (0 == Dds2382_send_retry || data_ready) { + Dds2382_send_retry = 5; + Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); + } else { + Dds2382_send_retry--; + } +} + +void Dds2382SnsInit(void) +{ + Dds2382Modbus = new TasmotaModbus(Pin(GPIO_DDS2382_RX), Pin(GPIO_DDS2382_TX)); + uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Dds2382DrvInit(void) +{ + if (PinUsed(GPIO_DDS2382_RX) && PinUsed(GPIO_DDS2382_TX)) { + energy_flg = XNRG_09; + } +} + + + + + +bool Xnrg09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { Dds2382EverySecond(); } + break; + case FUNC_INIT: + Dds2382SnsInit(); + break; + case FUNC_PRE_INIT: + Dds2382DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_10_sdm630.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_10_sdm630.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM630 + + + + + + +#define XNRG_10 10 + + +#ifndef SDM630_SPEED + #define SDM630_SPEED 9600 +#endif + +#ifndef SDM630_ADDR + #define SDM630_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm630Modbus; + +const uint16_t sdm630_start_addresses[] { + + 0x0000, + 0x0002, + 0x0004, + 0x0006, + 0x0008, + 0x000A, + 0x000C, + 0x000E, + 0x0010, + 0x0018, + 0x001A, + 0x001C, + 0x001E, + 0x0020, + 0x0022, + 0x0046, + 0x0160, + 0x0162, + 0x0164, +#ifdef SDM630_IMPORT + 0x015A, + 0x015C, + 0x015E, +#endif + 0x0156 +}; + +struct SDM630 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Sdm630; + + + +void SDM630Every250ms(void) +{ + bool data_ready = Sdm630Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + } else { + Energy.data_valid[0] = 0; + Energy.data_valid[1] = 0; + Energy.data_valid[2] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm630.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + Energy.frequency[0] = value; + break; + + case 16: + Energy.export_active[0] = value; + break; + + case 17: + Energy.export_active[1] = value; + break; + + case 18: + Energy.export_active[2] = value; + break; + + case 19: +#ifdef SDM630_IMPORT + Energy.import_active[0] = value; + break; + + case 20: + Energy.import_active[1] = value; + break; + + case 21: + Energy.import_active[2] = value; + break; + + case 22: +#endif + EnergyUpdateTotal(value, true); + break; + } + + Sdm630.read_state++; + if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { + Sdm630.read_state = 0; + } + } + } + + if (0 == Sdm630.send_retry || data_ready) { + Sdm630.send_retry = 5; + Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); + } else { + Sdm630.send_retry--; + } +} + +void Sdm630SnsInit(void) +{ + Sdm630Modbus = new TasmotaModbus(Pin(GPIO_SDM630_RX), Pin(GPIO_SDM630_TX)); + uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + Energy.frequency_common = true; + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm630DrvInit(void) +{ + if (PinUsed(GPIO_SDM630_RX) && PinUsed(GPIO_SDM630_TX)) { + energy_flg = XNRG_10; + } +} + + + + + +bool Xnrg10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM630Every250ms(); } + break; + case FUNC_INIT: + Sdm630SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm630DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_11_ddsu666.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_11_ddsu666.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDSU666 + + + + +#define XNRG_11 11 + + +#ifndef DDSU666_SPEED + #define DDSU666_SPEED 9600 +#endif + +#ifndef DDSU666_ADDR + #define DDSU666_ADDR 1 +#endif + +#include +TasmotaModbus *Ddsu666Modbus; + +const uint16_t Ddsu666_start_addresses[] { + 0x2000, + 0x2002, + 0x2004, + 0x2006, + 0x200A, + 0x200E, + 0X4000, + 0X400A, +}; + +struct DDSU666 { + float import_active = NAN; + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Ddsu666; + + + +void DDSU666Every250ms(void) +{ + bool data_ready = Ddsu666Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Ddsu666.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value * 1000; + break; + + case 3: + Energy.reactive_power[0] = value * 1000; + break; + + case 4: + Energy.power_factor[0] = value; + break; + + case 5: + Energy.frequency[0] = value; + break; + + case 6: + Ddsu666.import_active = value; + break; + + case 7: + Energy.export_active[0] = value; + break; + } + + Ddsu666.read_state++; + + if (Ddsu666.read_state == 8) { + Ddsu666.read_state = 0; + EnergyUpdateTotal(Ddsu666.import_active, true); + } + } + } + + if (0 == Ddsu666.send_retry || data_ready) { + Ddsu666.send_retry = 5; + Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); + } else { + Ddsu666.send_retry--; + } +} + +void Ddsu666SnsInit(void) +{ + Ddsu666Modbus = new TasmotaModbus(Pin(GPIO_DDSU666_RX), Pin(GPIO_DDSU666_TX)); + uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Ddsu666DrvInit(void) +{ + if (PinUsed(GPIO_DDSU666_RX) && PinUsed(GPIO_DDSU666_TX)) { + energy_flg = XNRG_11; + } +} + + + + + +bool Xnrg11(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { DDSU666Every250ms(); } + break; + case FUNC_INIT: + Ddsu666SnsInit(); + break; + case FUNC_PRE_INIT: + Ddsu666DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_12_solaxX1.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_12_solaxX1.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 + + + + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 +#endif + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +enum solaxX1_Error +{ + solaxX1_ERR_NO_ERROR, + solaxX1_ERR_CRC_ERROR +}; + +union { + uint32_t ErrMessage; + struct { + + uint8_t TzProtectFault:1; + uint8_t MainsLostFault:1; + uint8_t GridVoltFault:1; + uint8_t GridFreqFault:1; + uint8_t PLLLostFault:1; + uint8_t BusVoltFault:1; + uint8_t ErrBit06:1; + uint8_t OciFault:1; + + uint8_t Dci_OCP_Fault:1; + uint8_t ResidualCurrentFault:1; + uint8_t PvVoltFault:1; + uint8_t Ac10Mins_Voltage_Fault:1; + uint8_t IsolationFault:1; + uint8_t TemperatureOverFault:1; + uint8_t FanFault:1; + uint8_t ErrBit15:1; + + uint8_t SpiCommsFault:1; + uint8_t SciCommsFault:1; + uint8_t ErrBit18:1; + uint8_t InputConfigFault:1; + uint8_t EepromFault:1; + uint8_t RelayFault:1; + uint8_t SampleConsistenceFault:1; + uint8_t ResidualCurrent_DeviceFault:1; + + uint8_t ErrBit24:1; + uint8_t ErrBit25:1; + uint8_t ErrBit26:1; + uint8_t ErrBit27:1; + uint8_t ErrBit28:1; + uint8_t DCI_DeviceFault:1; + uint8_t OtherDeviceFault:1; + uint8_t ErrBit31:1; + }; +} ErrCode; + +const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; + +const char kSolaxError[] PROGMEM = + D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" + D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; + + + +TasmotaSerial *solaxX1Serial; + +uint8_t solaxX1_Init = 1; + +struct SOLAXX1 { + float temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + float energy_total = 0; + float runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + + uint8_t status = 0; + uint32_t errorCode = 0; +} solaxX1; + +union { + uint8_t status; + struct { + uint8_t freeBit7:1; + uint8_t freeBit6:1; + uint8_t freeBit5:1; + uint8_t queryOffline:1; + uint8_t queryOfflineSend:1; + uint8_t hasAddress:1; + uint8_t inverterAddressSend:1; + uint8_t inverterSnReceived:1; + }; +} protocolStatus; + +uint8_t header[2] = {0xAA, 0x55}; +uint8_t source[2] = {0x00, 0x00}; +uint8_t destination[2] = {0x00, 0x00}; +uint8_t controlCode[1] = {0x00}; +uint8_t functionCode[1] = {0x00}; +uint8_t dataLength[1] = {0x00}; +uint8_t data[16] = {0}; + +uint8_t message[30]; + + + +bool solaxX1_RS485ReceiveReady(void) +{ + return (solaxX1Serial->available() > 1); +} + +void solaxX1_RS485Send(uint16_t msgLen) +{ + memcpy(message, header, 2); + memcpy(message + 2, source, 2); + memcpy(message + 4, destination, 2); + memcpy(message + 6, controlCode, 1); + memcpy(message + 7, functionCode, 1); + memcpy(message + 8, dataLength, 1); + memcpy(message + 9, data, sizeof(data)); + uint16_t crc = solaxX1_calculateCRC(message, msgLen); + + while (solaxX1Serial->available() > 0) + { + solaxX1Serial->read(); + } + + solaxX1Serial->flush(); + solaxX1Serial->write(message, msgLen); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); +} + +uint8_t solaxX1_RS485Receive(uint8_t *value) +{ + uint8_t len = 0; + + while (solaxX1Serial->available() > 0) + { + value[len++] = (uint8_t)solaxX1Serial->read(); + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); + + uint16_t crc = solaxX1_calculateCRC(value, len - 2); + + if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) + { + return solaxX1_ERR_NO_ERROR; + } + else + { + return solaxX1_ERR_CRC_ERROR; + } +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum; + wChkSum = 0; + + for (i = 0; i < bLen; i++) + { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_SendInverterAddress(void) +{ + source[0] = 0x00; + destination[0] = 0x00; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x01; + dataLength[0] = 0x0F; + data[14] = INVERTER_ADDRESS; + solaxX1_RS485Send(24); +} + +void solaxX1_QueryLiveData(void) +{ + source[0] = 0x01; + destination[0] = 0x00; + destination[1] = INVERTER_ADDRESS; + controlCode[0] = 0x11; + functionCode[0] = 0x02; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + ErrCode.ErrMessage = code; + + if (code == 0) return 0; + if (ErrCode.MainsLostFault) return 1; + if (ErrCode.GridVoltFault) return 2; + if (ErrCode.GridFreqFault) return 3; + if (ErrCode.PvVoltFault) return 4; + if (ErrCode.IsolationFault) return 5; + if (ErrCode.TemperatureOverFault) return 6; + if (ErrCode.FanFault) return 7; + if (ErrCode.OtherDeviceFault) return 8; + return 0; +} + + + +uint8_t solaxX1_send_retry = 0; +uint8_t solaxX1_nodata_count = 0; + +void solaxX1250MSecond(void) +{ + uint8_t value[61] = {0}; + bool data_ready = solaxX1_RS485ReceiveReady(); + + if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + } + else + { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + Energy.data_valid[0] = 0; + + solaxX1.temperature = (float)((value[9] << 8) | value[10]); + solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; + solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; + solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; + solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; + solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; + Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; + Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; + Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; + Energy.active_power[0] = (float)((value[27] << 8) | value[28]); + + solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; + solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); + solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); + + + + + + + + solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); + + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + + solaxX1_QueryLiveData(); + EnergyUpdateTotal(solaxX1.energy_total, true); + } + } + + if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { + solaxX1_send_retry = 12; + solaxX1_QueryLiveData(); + } + + + + if (255 == solaxX1_nodata_count) { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + } + } + else + { + if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } + if (solaxX1_nodata_count < 10 * 4) + { + solaxX1_nodata_count++; + } + else if (255 != solaxX1_nodata_count) + { + + solaxX1_nodata_count = 255; + solaxX1_send_retry = 12; + protocolStatus.status = 0b00001000; + Energy.data_valid[0] = ENERGY_WATCHDOG; + + solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; + solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + + } + } + + if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + + if (protocolStatus.inverterAddressSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); + } + else + { + if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); + protocolStatus.status = 0b00100000; + } + } + } + + + if (protocolStatus.queryOfflineSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); + } + else + { + + if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) + { + for (uint8_t i = 9; i <= 22; i++) + { + data[i - 9] = value[i]; + } + solaxX1_SendInverterAddress(); + protocolStatus.status = 0b1100000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); + } + } + } + } + + if (solaxX1_send_retry == 0) + { + if (protocolStatus.queryOfflineSend) + { + protocolStatus.status = 0b00001000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); + } + solaxX1_send_retry = 12; + } + + + if (protocolStatus.queryOffline) + { + + source[0] = 0x01; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x00; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); + protocolStatus.status = 0b00010000; + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); + } + } + + if (!data_ready) + solaxX1_send_retry--; +} + +void solaxX1SnsInit(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX)); + protocolStatus.status = 0b00100000; + + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void solaxX1DrvInit(void) +{ + if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { + energy_flg = XNRG_12; + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; +#ifdef SOLAXX1_PV2 +const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; +#endif +const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; +#endif + +void solaxX1Show(bool json) +{ + char solar_power[33]; + dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); + char pv1_voltage[33]; + dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); + char pv1_current[33]; + dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); + char pv1_power[33]; + dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); +#ifdef SOLAXX1_PV2 + char pv2_voltage[33]; + dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); + char pv2_current[33]; + dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); + char pv2_power[33]; + dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); +#endif + char temperature[33]; + dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); + char runtime[33]; + dtostrfd(solaxX1.runtime_total, 0, runtime); + char status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); + + if (json) + { + ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), + solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + temperature, runtime, status, solaxX1.errorCode); + +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); +#endif + WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); +#endif + } +} + + + + + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { solaxX1250MSecond(); } + break; + case FUNC_JSON_APPEND: + solaxX1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1Show(0); + break; +#endif + case FUNC_INIT: + solaxX1SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_LE01MR +# 71 "/workspace/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#define XNRG_13 13 + + +#ifndef LE01MR_SPEED + #define LE01MR_SPEED 2400 +#endif + +#ifndef LE01MR_ADDR + #define LE01MR_ADDR 1 +#endif + +#include +TasmotaModbus *FifLEModbus; + +const uint8_t le01mr_table_sz = 9; + +const uint16_t le01mr_register_addresses[] { + + 0x0130, + 0x0131, + 0x0158, + 0x0139, + 0x0140, + 0x0148, + 0x0150, + 0xA000, + 0xA01E +}; + +struct LE01MR { + float total_active = 0; + float total_reactive = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = le01mr_table_sz; +} Le01mr; + + + +void FifLEEvery250ms(void) +{ + bool data_ready = FifLEModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + uint8_t reg_count = 2; + if (Le01mr.read_state < 3) { + reg_count=1; + } + + uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 146 "/workspace/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" + uint32_t value_buff = 0; + + if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { + value_buff = ((uint32_t)buffer[3])<<8 | buffer[4]; + } else { + value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6]; + } + + switch(Le01mr.read_state) { + case 0: + Energy.frequency[0] = value_buff * 0.01f; + break; + + case 1: + Energy.voltage[0] = value_buff * 0.01f; + break; + + case 2: + Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; + break; + + case 3: + Energy.current[0] = value_buff * 0.001f; + break; + + case 4: + Energy.active_power[0] = value_buff * 1.0f; + break; + + case 5: + Energy.reactive_power[0] = value_buff * 1.0f; + break; + + case 6: + Energy.apparent_power[0] = value_buff * 1.0f; + break; + + case 7: + Le01mr.total_active = value_buff * 0.01f; + break; + + case 8: + Le01mr.total_reactive = value_buff * 0.01f; + break; + } + + Le01mr.read_state++; + if (Le01mr.read_state == Le01mr.start_address_count) { + Le01mr.read_state = 0; + + EnergyUpdateTotal(Le01mr.total_active, true); + } + } + } + + if (0 == Le01mr.send_retry || data_ready) { + uint8_t reg_count = 2; + + Le01mr.send_retry = 5; + + if (Le01mr.read_state < 3) reg_count=1; + + FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count); + } else { + Le01mr.send_retry--; + } +} + +void FifLESnsInit(void) +{ + FifLEModbus = new TasmotaModbus(Pin(GPIO_LE01MR_RX), Pin(GPIO_LE01MR_TX)); + uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void FifLEDrvInit(void) +{ + if (PinUsed(GPIO_LE01MR_RX) && PinUsed(GPIO_LE01MR_TX)) { + energy_flg = XNRG_13; + } +} + +void FifLEReset(void) +{ + Le01mr.total_active = 0; + Le01mr.total_reactive = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_LE01MR[] PROGMEM = + "{s}" D_TOTAL_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + ; +#endif + +void FifLEShow(bool json) +{ + char total_reactive_chr[FLOATSZ]; + dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr); + char total_active_chr[FLOATSZ]; + dtostrfd(Le01mr.total_active, Settings.flag2.energy_resolution, total_active_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL_ACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s"), + total_active_chr, total_reactive_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_LE01MR, total_active_chr, total_reactive_chr); +#endif + } +} + + + + + +bool Xnrg13(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { + FifLEEvery250ms(); + } + break; + case FUNC_JSON_APPEND: + FifLEShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + FifLEShow(0); + break; +#endif + case FUNC_ENERGY_RESET: + FifLEReset(); + break; + case FUNC_INIT: + FifLESnsInit(); + break; + case FUNC_PRE_INIT: + FifLEDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_14_bl0940.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_14_bl0940.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_BL0940 +# 30 "/workspace/Tasmota/tasmota/xnrg_14_bl0940.ino" +#define XNRG_14 14 + +#define BL0940_PREF 1430 +#define BL0940_UREF 33000 +#define BL0940_IREF 2750 + +#define BL0940_PULSES_NOT_INITIALIZED -1 + +#define BL0940_BUFFER_SIZE 36 + +#define BL0940_WRITE_COMMAND 0xA0 +#define BL0940_REG_I_FAST_RMS_CTRL 0x10 +#define BL0940_REG_MODE 0x18 +#define BL0940_REG_SOFT_RESET 0x19 +#define BL0940_REG_USR_WRPROT 0x1A +#define BL0940_REG_TPS_CTRL 0x1B + +#define BL0940_READ_COMMAND 0x50 +#define BL0940_FULL_PACKET 0xAA + +#define BL0940_PACKET_HEADER 0x55 + +#include + +TasmotaSerial *Bl0940Serial = nullptr; + +struct BL0940 { + long voltage = 0; + long current = 0; + long power = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED; + float temperature; + + int byte_counter = 0; + uint16_t tps1 = 0; + uint8_t *rx_buffer = nullptr; + bool received = false; +} Bl0940; + +const uint8_t bl0940_init[5][6] = { + { BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38 }, + { BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0 }, + { BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37 }, + { BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE }, + { BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B }}; + +void Bl0940Received(void) { + + + + + + + uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; + if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || + (Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) + ) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data")); + return; + } + + Bl0940.tps1 = tps1; + float t = ((170.0f/448.0f)*(((float)Bl0940.tps1/2.0f)-32.0f))-45.0f; + Bl0940.temperature = ConvertTemp(t); + + Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10]; + Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4]; + int32_t power = Bl0940.rx_buffer[18] << 24 | Bl0940.rx_buffer[17] << 16 | Bl0940.rx_buffer[16] << 8; + Bl0940.power = abs(power) >> 8; + int32_t cf_cnt = Bl0940.rx_buffer[24] << 24 | Bl0940.rx_buffer[23] << 16 | Bl0940.rx_buffer[22] << 8; + Bl0940.cf_pulses = abs(cf_cnt) >> 8; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, C %d, T %d"), + Bl0940.voltage, Bl0940.current, Bl0940.power, Bl0940.cf_pulses, Bl0940.tps1); + + if (Energy.power_on) { + Energy.voltage[0] = (float)Bl0940.voltage / Settings.energy_voltage_calibration; + if (power && (Bl0940.power > Settings.energy_power_calibration)) { + Energy.active_power[0] = (float)Bl0940.power / Settings.energy_power_calibration; + Energy.current[0] = (float)Bl0940.current / (Settings.energy_current_calibration * 100); + } else { + Energy.active_power[0] = 0; + Energy.current[0] = 0; + } + } else { + + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; + } +} + +void Bl0940SerialInput(void) { + while (Bl0940Serial->available()) { + yield(); + uint8_t serial_in_byte = Bl0940Serial->read(); + if (!Bl0940.received && (BL0940_PACKET_HEADER == serial_in_byte)) { + Bl0940.received = true; + Bl0940.byte_counter = 0; + } + if (Bl0940.received) { + Bl0940.rx_buffer[Bl0940.byte_counter++] = serial_in_byte; + if (BL0940_BUFFER_SIZE == Bl0940.byte_counter) { + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Bl0940.rx_buffer, BL0940_BUFFER_SIZE -1); + + uint8_t checksum = BL0940_READ_COMMAND; + for (uint32_t i = 0; i < BL0940_BUFFER_SIZE -2; i++) { checksum += Bl0940.rx_buffer[i]; } + checksum ^= 0xFF; + if (checksum == Bl0940.rx_buffer[34]) { + Energy.data_valid[0] = 0; + Bl0940Received(); + Bl0940.received = false; + return; + } else { + do { + memmove(Bl0940.rx_buffer, Bl0940.rx_buffer +1, BL0940_BUFFER_SIZE -1); + Bl0940.byte_counter--; + } while ((Bl0940.byte_counter > 1) && (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0])); + if (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE)); + Bl0940.received = false; + Bl0940.byte_counter = 0; + } + } + } + } + } +} + + + +void Bl0940EverySecond(void) { + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Bl0940.voltage = 0; + Bl0940.current = 0; + Bl0940.power = 0; + } else { +# 178 "/workspace/Tasmota/tasmota/xnrg_14_bl0940.ino" + if (BL0940_PULSES_NOT_INITIALIZED == Bl0940.cf_pulses_last_time) { + Bl0940.cf_pulses_last_time = Bl0940.cf_pulses; + } else { + uint32_t cf_pulses = 0; + if (Bl0940.cf_pulses < Bl0940.cf_pulses_last_time) { + cf_pulses = (0x1000000 - Bl0940.cf_pulses_last_time) + Bl0940.cf_pulses; + } else { + cf_pulses = Bl0940.cf_pulses - Bl0940.cf_pulses_last_time; + } + if (cf_pulses && Energy.active_power[0]) { + uint32_t watt256 = (1638400 * 256) / Settings.energy_power_calibration; + uint32_t delta = (cf_pulses * watt256) / 36; + if (delta <= (4000 * 1000 / 36)) { + Bl0940.cf_pulses_last_time = Bl0940.cf_pulses; + Energy.kWhtoday_delta += delta; + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: Overload")); + Bl0940.cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED; + } + EnergyUpdateToday(); + } + } + + } + + + + Bl0940Serial->flush(); + Bl0940Serial->write(BL0940_READ_COMMAND); + Bl0940Serial->write(BL0940_FULL_PACKET); +} + +void Bl0940SnsInit(void) { + + Bl0940Serial = new TasmotaSerial(Pin(GPIO_BL0940_RX), Pin(GPIO_TXD), 1); + if (Bl0940Serial->begin(4800, 1)) { + if (Bl0940Serial->hardwareSerial()) { + ClaimSerial(); + } + if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { + Settings.energy_voltage_calibration = BL0940_UREF; + Settings.energy_current_calibration = BL0940_IREF; + Settings.energy_power_calibration = BL0940_PREF; + } + + for (uint32_t i = 0; i < 5; i++) { + for (uint32_t j = 0; j < 6; j++) { + Bl0940Serial->write(bl0940_init[i][j]); + + } + delay(1); + } + + } else { + energy_flg = ENERGY_NONE; + } +} + +void Bl0940DrvInit(void) { + if (PinUsed(GPIO_BL0940_RX) && PinUsed(GPIO_TXD)) { + Bl0940.rx_buffer = (uint8_t*)(malloc(BL0940_BUFFER_SIZE)); + if (Bl0940.rx_buffer != nullptr) { + energy_flg = XNRG_14; + } + } +} + +bool Bl0940Command(void) { + bool serviced = true; + + uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Bl0940.power) { + Settings.energy_power_calibration = (Bl0940.power * 100) / value; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Bl0940.voltage) { + Settings.energy_voltage_calibration = (Bl0940.voltage * 100) / value; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Bl0940.current) { + Settings.energy_current_calibration = Bl0940.current / value; + } + } + else serviced = false; + + return serviced; +} + +void Bl0940Show(bool json) { + char temperature[33]; + dtostrfd(Bl0940.temperature, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, "BL0940", temperature); + if (0 == tele_period) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, Bl0940.temperature); +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xnrg14(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (Bl0940Serial) { Bl0940SerialInput(); } + break; + case FUNC_EVERY_SECOND: + Bl0940EverySecond(); + break; + case FUNC_JSON_APPEND: + Bl0940Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Bl0940Show(0); + break; +#endif + case FUNC_COMMAND: + result = Bl0940Command(); + break; + case FUNC_INIT: + Bl0940SnsInit(); + break; + case FUNC_PRE_INIT: + Bl0940DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_TELEINFO +# 34 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +#define XNRG_15 15 + +#include "LibTeleinfo.h" +#include + +#define TINFO_READ_TIMEOUT 400 + + +enum TInfoContrat{ + CONTRAT_BAS = 1, + CONTRAT_HC, + CONTRAT_EJP, + CONTRAT_BBR, + CONTRAT_END +}; + + +const char kContratName[] PROGMEM = + "|Base|Heures Creuses|EJP|Bleu Blanc Rouge" + ; + + +const char kContratValue[] PROGMEM = + "|BASE|HC..|EJP.|BBR" + ; + + +enum TInfoTarif{ + TARIF_TH = 1, + TARIF_HC, TARIF_HP, + TARIF_HN, TARIF_PM, + TARIF_CB, TARIF_CW, TARIF_CR, + TARIF_PB, TARIF_PW, TARIF_PR, + TARIF_END +}; + + + +const char kTarifValue[] PROGMEM = + "|TH..|HC..|HP.." + "|HN..|PM.." + "|HCJB|HCJW|HCJR" + "|HPJB|HPJW|HPJR" + ; + + +const char kTarifName[] PROGMEM = + "|Toutes|Creuses|Pleines" + "|Normales|Pointe Mobile" + "|Creuses Bleu|Creuses Blanc|Creuse Rouges" + "|Pleines Bleu|Pleines Blanc|Pleines Rouges" + ; + + +enum TInfoLabel{ + LABEL_BASE = 1, + LABEL_ADCO, LABEL_ADSC, + LABEL_HCHC, LABEL_HCHP, LABEL_EAST, LABEL_EASF01, LABEL_EASF02, + LABEL_OPTARIF, LABEL_NGTF, LABEL_ISOUSC, LABEL_PREF, LABEL_PTEC, LABEL_LTARF, LABEL_NTARF, + LABEL_PAPP, LABEL_SINSTS, LABEL_IINST, LABEL_IRMS1, LABEL_TENSION, LABEL_URMS1, + LABEL_IMAX, LABEL_PMAX, LABEL_SMAXSN, + LABEL_DEMAIN, + LABEL_END +}; + +const char kLabel[] PROGMEM = + "|BASE|ADCO|ADSC" + "|HCHC|HCHP|EAST|EASF01|EASF02" + "|OPTARIF|NGTF|ISOUSC|PREF|PTEC|LTARF|NTARF" + "|PAPP|SINSTS|IINST|IRMS1|TENSION|URMS1" + "|IMAX|PMAX|SMAXSN" + "|DEMAIN" + ; + +TInfo tinfo; +TasmotaSerial *TInfoSerial = nullptr; +_Mode_e tinfo_mode = TINFO_MODE_HISTORIQUE; +char serialNumber[13] = ""; +bool tinfo_found = false; +int contrat; +int tarif; +int isousc; +# 126 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +char * getValueFromLabelIndex(int labelIndex, char * value) +{ + char labelName[16]; + + GetTextIndexed(labelName, sizeof(labelName), labelIndex, kLabel); + + return tinfo.valueGet(labelName, value) ; +} +# 147 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void ADPSCallback(uint8_t phase) +{ + + if (phase == 0){ + phase = 1; + } + + Response_P(PSTR("{")); + ResponseAppend_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"ADPS\":%i}}"), serialNumber, phase ); + ResponseJsonEnd(); + + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase); +} +# 172 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void DataCallback(struct _ValueList * me, uint8_t flags) +{ + char c = ' '; + int ilabel ; + + + if (flags & (TINFO_FLAGS_ADDED | TINFO_FLAGS_UPDATED) ) { + char labelName[16]; + + for ( ilabel = 1 ; ilabel < LABEL_END ; ilabel++) { + GetTextIndexed(labelName, sizeof(labelName), ilabel, kLabel); + if (!strcmp(labelName, me->name)) { + break; + } + } + + if (flags & TINFO_FLAGS_ADDED) { c = '#'; } + if (flags & TINFO_FLAGS_UPDATED) { c = '*'; } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: [%d]%c %s=%s"), ilabel, c , me->name, me->value); + + if (ilabelvalue)) { + break; + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif changed, now '%s' (%d)"), me->value, tarif); + } + + + else if (ilabel == LABEL_LTARF) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif name changed, now '%s'"), me->value); + } + + else if (ilabel == LABEL_NTARF) + { + tarif = atoi(me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif index changed, now '%d'"), tarif); + } + + + + else if ( ilabel == LABEL_TENSION || ilabel == LABEL_URMS1) + { + Energy.voltage_available = true; + Energy.voltage[0] = (float) atoi(me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Voltage %s, now %d"), me->value, (int) Energy.voltage[0]); + } + + + else if (ilabel == LABEL_IINST || ilabel == LABEL_IRMS1) + { + Energy.current[0] = (float) atoi(me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Current %s, now %d"), me->value, (int) Energy.current[0]); + } + + + else if (ilabel == LABEL_PAPP || ilabel == LABEL_SINSTS) + { + Energy.active_power[0] = (float) atoi(me->value);; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, (int) Energy.active_power[0]); + } + + + else if ( ilabel == LABEL_HCHC || ilabel == LABEL_HCHP || ilabel == LABEL_BASE) + { + char value[32]; + uint32_t hc = 0; + uint32_t hp = 0; + uint32_t total = 0; + + + if (ilabel == LABEL_BASE) { + total = atoi(me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Base:%u"), total); + + } else { + + if (ilabel == LABEL_HCHC) { + hc = atoi(me->value); + if ( getValueFromLabelIndex(LABEL_HCHP, value) ) { + hp = atoi(value); + } + + + } else if (ilabel == LABEL_HCHP) { + hp = atoi(me->value); + if ( getValueFromLabelIndex(LABEL_HCHC, value) ) { + hc = atoi(value); + } + } + total = hc + hp; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total); + } + + if (!Settings.flag4.teleinfo_rawdata) { + EnergyUpdateTotal(total/1000.0f, true); + } + } + + + else if ( ilabel == LABEL_EAST) + { + uint32_t total = atoi(me->value); + if (!Settings.flag4.teleinfo_rawdata) { + EnergyUpdateTotal(total/1000.0f, true); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total); + } + + + else if ( ilabel == LABEL_EASF01) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u"), atoi(me->value)); + } + else if ( ilabel == LABEL_EASF02) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HP:%u"), atoi(me->value)); + } + + + else if (ilabel == LABEL_OPTARIF) + { + char contrat_value[] = " "; + + for (contrat = CONTRAT_BAS ; contrat < CONTRAT_END ; contrat++) { + GetTextIndexed(contrat_value, sizeof(contrat_value), contrat, kContratValue); + if (!strcmp(contrat_value, me->value)) { + break; + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s' (%d)"), me->value, contrat); + } + + else if (ilabel == LABEL_NGTF) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s'"), me->value); + } + + + else if (ilabel == LABEL_ISOUSC || ilabel == LABEL_PREF) + { + isousc = atoi( me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc); + } + + + else if (ilabel == LABEL_ADCO || ilabel == LABEL_ADSC) + { + strcpy(serialNumber, me->value); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: %s set to %s"), me->name, serialNumber); + } + + } + } + + + + +} +# 348 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void ResponseAppendTInfo(char sep) +{ + struct _ValueList * me = tinfo.getList(); + + char * p ; + boolean isNumber ; + + + + while (me->next) { + + me = me->next; + + if (me->name && me->value && *me->name && *me->value) { + isNumber = true; + p = me->value; + + + if (strcmp(me->name, "ADCO")==0 || strcmp(me->name, "ADSC")==0) { + isNumber = false; + } else { + + while (*p && isNumber) { + if ( *p < '0' || *p > '9' ) { + isNumber = false; + } + p++; + } + } + + ResponseAppend_P( PSTR("%c\"%s\":"), sep, me->name ); + + if (!isNumber) { + ResponseAppend_P( PSTR("\"%s\""), me->value ); + } else { + ResponseAppend_P( PSTR("%d"), atoi(me->value)); + } + + + sep =','; + } + } +} +# 399 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void NewFrameCallback(struct _ValueList * me) +{ + + Energy.data_valid[0] = 0; + + + + if (Settings.flag4.teleinfo_rawdata) { + Response_P(PSTR("{")); + ResponseAppendTInfo(' '); + ResponseJsonEnd(); + + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false); + } +} +# 423 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void TInfoDrvInit(void) { + if (PinUsed(GPIO_TELEINFO_RX)) { + energy_flg = XNRG_15; + Energy.voltage_available = false; + } +} +# 437 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void TInfoInit(void) +{ + int baudrate; + + + if (Settings.flag4.teleinfo_baudrate) { + baudrate = 9600; + tinfo_mode = TINFO_MODE_STANDARD; + } else { + baudrate = 1200; + tinfo_mode = TINFO_MODE_HISTORIQUE; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),baudrate); + + if (PinUsed(GPIO_TELEINFO_RX)) { + uint8_t rx_pin = Pin(GPIO_TELEINFO_RX); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d"), rx_pin); + + + if (PinUsed(GPIO_TELEINFO_ENABLE)) { + uint8_t en_pin = Pin(GPIO_TELEINFO_ENABLE); + pinMode(en_pin, OUTPUT); + digitalWrite(en_pin, HIGH); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: Enable with GPIO%d"), en_pin); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: always enabled")); + } + +#if defined (ESP8266) + + TInfoSerial = new TasmotaSerial(rx_pin, -1, 2); + +#else + TInfoSerial = new TasmotaSerial(rx_pin, -1, 1); +#endif + + + + if (TInfoSerial->begin(baudrate)) { + + +#if defined (ESP8266) + if (TInfoSerial->hardwareSerial() ) { + ClaimSerial(); +# 490 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using hardware serial")); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using software serial")); + } + +#elif defined (ESP32) + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); +#endif + + tinfo.init(tinfo_mode); + + tinfo.attachADPS(ADPSCallback); + tinfo.attachData(DataCallback); + tinfo.attachNewFrame(NewFrameCallback); + tinfo_found = true; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: Ready")); + } + } +} +# 518 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +void TInfoEvery250ms(void) +{ + if (!tinfo_found) { + return; + } + + if (TInfoSerial->available()) { + unsigned long start = millis(); + char c; + + + while (TInfoSerial->available()>8 && millis()-start < 100) { + + c = TInfoSerial->read(); + + tinfo.process(c & 0x7F); + } + } +} +# 545 "/workspace/Tasmota/tasmota/xnrg_15_teleinfo.ino" +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_ID_TELEINFO[] PROGMEM = "{s}ID{m}%s{e}" ; +const char HTTP_ENERGY_INDEX_TELEINFO[] PROGMEM = "{s}%s{m}%s " D_UNIT_WATTHOUR "{e}" ; +const char HTTP_ENERGY_PAPP_TELEINFO[] PROGMEM = "{s}" D_POWERUSAGE "{m}%d " D_UNIT_WATT "{e}" ; +const char HTTP_ENERGY_IINST_TELEINFO[] PROGMEM = "{s}" D_CURRENT "{m}%d " D_UNIT_AMPERE "{e}" ; +const char HTTP_ENERGY_TARIF_TELEINFO[] PROGMEM = "{s}" D_CURRENT_TARIFF "{m}Heures %s{e}" ; +const char HTTP_ENERGY_CONTRAT_TELEINFO[] PROGMEM = "{s}" D_CONTRACT "{m}%s %d" D_UNIT_AMPERE "{e}" ; +const char HTTP_ENERGY_LOAD_TELEINFO[] PROGMEM = "{s}" D_POWER_LOAD "{m}%d" D_UNIT_PERCENT "{e}" ; +const char HTTP_ENERGY_IMAX_TELEINFO[] PROGMEM = "{s}" D_MAX_CURRENT "{m}%d" D_UNIT_AMPERE "{e}" ; +const char HTTP_ENERGY_PMAX_TELEINFO[] PROGMEM = "{s}" D_MAX_POWER "{m}%d" D_UNIT_WATT "{e}" ; +#endif + +void TInfoShow(bool json) +{ + + + + if (json) + { + + if (isousc) { + ResponseAppend_P(PSTR(",\"Load\":%d"),(int) ((Energy.current[0]*100.0f) / isousc)); + } + + + if (!Settings.flag4.teleinfo_rawdata) { + ResponseAppendTInfo(','); + } + + +#ifdef USE_WEBSERVER + } + else + { + char name[32]; + char value[32]; + + if (getValueFromLabelIndex(LABEL_BASE, value) ) { + GetTextIndexed(name, sizeof(name), LABEL_BASE, kLabel); + WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, name, value); + } + if (getValueFromLabelIndex(LABEL_HCHC, value) ) { + GetTextIndexed(name, sizeof(name), LABEL_HCHC, kLabel); + WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, name, value); + } + if (getValueFromLabelIndex(LABEL_HCHP, value) ) { + GetTextIndexed(name, sizeof(name), LABEL_HCHP, kLabel); + WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, name, value); + } + if (getValueFromLabelIndex(LABEL_IMAX, value) ) { + WSContentSend_PD(HTTP_ENERGY_IMAX_TELEINFO, atoi(value)); + } + if (getValueFromLabelIndex(LABEL_PMAX, value) ) { + WSContentSend_PD(HTTP_ENERGY_PMAX_TELEINFO, atoi(value)); + } + + if (tinfo_mode==TINFO_MODE_HISTORIQUE ) { + if (tarif) { + GetTextIndexed(name, sizeof(name), tarif-1, kTarifName); + WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); + } + if (contrat && isousc) { + int percent = (int) ((Energy.current[0]*100.0f) / isousc) ; + GetTextIndexed(name, sizeof(name), contrat, kContratName); + WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); + WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); + } + } else if (tinfo_mode==TINFO_MODE_STANDARD ) { + if (getValueFromLabelIndex(LABEL_LTARF, name) ) { + WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); + } + + if (getValueFromLabelIndex(LABEL_NGTF, name) ) { + if (isousc) { + int percent = (int) ((Energy.current[0]*100.0f) / isousc) ; + WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); + WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); + } + } + } + + + if (*serialNumber) { + WSContentSend_PD(HTTP_ENERGY_ID_TELEINFO, serialNumber); + } + +#endif + } +} + + + + +bool Xnrg15(uint8_t function) +{ + switch (function) + { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { TInfoEvery250ms(); } + break; + case FUNC_JSON_APPEND: + TInfoShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + TInfoShow(0); + break; + #endif + case FUNC_INIT: + TInfoInit(); + break; + case FUNC_PRE_INIT: + TInfoDrvInit(); + break; + } + return false; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_16_iem3000.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_16_iem3000.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_IEM3000 + + + + + + +#define XNRG_16 16 + + +#ifndef IEM3000_SPEED + #define IEM3000_SPEED 19200 +#endif + +#ifndef IEM3000_ADDR + #define IEM3000_ADDR 1 +#endif + +#include +TasmotaModbus *Iem3000Modbus; + +const uint16_t Iem3000_start_addresses[] { + + 0x0bb7, + 0x0bb9, + 0x0bbb, + 0x0bd3, + 0x0bd5, + 0x0bd7, + 0x0bed, + 0x0bef, + 0x0bf1, + 0x0c25, + 0xb02b, +}; + +struct IEM3000 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Iem3000; + + + +void IEM3000Every250ms(void) +{ + bool data_ready = Iem3000Modbus->ReceiveReady(); + uint8_t reg_count = 4; + if (Iem3000.read_state < 10) { + reg_count = 2; + } + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Iem3000Modbus->ReceiveBuffer(buffer, reg_count); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Iem3000Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Iem3000 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + int64_t value64; + if(Iem3000.read_state >= 0 && Iem3000.read_state < 10) { + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + } else { + ((uint8_t*)&value64)[7] = buffer[3]; + ((uint8_t*)&value64)[6] = buffer[4]; + ((uint8_t*)&value64)[5] = buffer[5]; + ((uint8_t*)&value64)[4] = buffer[6]; + ((uint8_t*)&value64)[3] = buffer[7]; + ((uint8_t*)&value64)[2] = buffer[8]; + ((uint8_t*)&value64)[1] = buffer[9]; + ((uint8_t*)&value64)[0] = buffer[10]; + } + + switch(Iem3000.read_state) { + case 0: + Energy.current[0] = value; + break; + + case 1: + Energy.current[1] = value; + break; + + case 2: + Energy.current[2] = value; + break; + + case 3: + Energy.voltage[0] = value; + break; + + case 4: + Energy.voltage[1] = value; + break; + + case 5: + Energy.voltage[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.frequency[0] = value; + break; + + case 10: + EnergyUpdateTotal(value64 * 0.001f, true); + break; + } + + Iem3000.read_state++; + if (sizeof(Iem3000_start_addresses)/2 == Iem3000.read_state) { + Iem3000.read_state = 0; + } + } + } + + if (0 == Iem3000.send_retry || data_ready) { + Iem3000.send_retry = 5; + Iem3000Modbus->Send(IEM3000_ADDR, 0x03, Iem3000_start_addresses[Iem3000.read_state], reg_count); + } else { + Iem3000.send_retry--; + } +} + +void Iem3000SnsInit(void) +{ + Iem3000Modbus = new TasmotaModbus(Pin(GPIO_IEM3000_RX), Pin(GPIO_IEM3000_TX)); + uint8_t result = Iem3000Modbus->Begin(IEM3000_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + Energy.frequency_common = true; + } else { + energy_flg = ENERGY_NONE; + } +} + +void Iem3000DrvInit(void) +{ + if (PinUsed(GPIO_IEM3000_RX) && PinUsed(GPIO_IEM3000_TX)) { + energy_flg = XNRG_16; + } +} + + + + + +bool Xnrg16(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { IEM3000Every250ms(); } + break; + case FUNC_INIT: + Iem3000SnsInit(); + break; + case FUNC_PRE_INIT: + Iem3000DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_17_ornowe517.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_17_ornowe517.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_WE517 +# 30 "/workspace/Tasmota/tasmota/xnrg_17_ornowe517.ino" +#define XNRG_17 17 + + +#ifndef WE517_SPEED + #define WE517_SPEED 9600 +#endif + +#ifndef WE517_ADDR + #define WE517_ADDR 1 +#endif + +#define FUNCTION_CODE_READ_HOLDING_REGISTERS (0x03) + +#include +TasmotaModbus *We517Modbus; + +const uint16_t we517_start_addresses[] { + + 0x000E, + 0x0010, + 0x0012, + 0x0016, + 0x0018, + 0x001A, + 0x001E, + 0x0020, + 0x0022, + 0x0026, + 0x0026, + 0x002A, + 0x0036, + 0x0038, + 0x003A, + 0x0014, + 0x0100 +}; + +struct WE517 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} We517; + + + +void WE517Every250ms(void) +{ + bool data_ready = We517Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = We517Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, We517Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 error %d"), error); + } else { + Energy.data_valid[0] = 0; + Energy.data_valid[1] = 0; + Energy.data_valid[2] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(We517.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value * 1000; + break; + + case 7: + Energy.active_power[1] = value * 1000; + break; + + case 8: + Energy.active_power[2] = value * 1000; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + Energy.frequency[0] = value; + break; + + case 16: + EnergyUpdateTotal(value, true); + break; + } + + We517.read_state++; + if (sizeof(we517_start_addresses)/2 == We517.read_state) { + We517.read_state = 0; + } + } + } + + if (0 == We517.send_retry || data_ready) { + We517.send_retry = 5; + We517Modbus->Send(WE517_ADDR, FUNCTION_CODE_READ_HOLDING_REGISTERS, we517_start_addresses[We517.read_state], 2); + } else { + We517.send_retry--; + } +} + +void We517SnsInit(void) +{ + We517Modbus = new TasmotaModbus(Pin(GPIO_WE517_RX), Pin(GPIO_WE517_TX)); + uint8_t result = We517Modbus->Begin(WE517_SPEED); + if (result) { + if (2 == result) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 HW serial init 8E1 at %d baud"), WE517_SPEED); + Serial.begin(WE517_SPEED, SERIAL_8E1); + ClaimSerial(); + } + Energy.phase_count = 3; + Energy.frequency_common = true; + } else { + energy_flg = ENERGY_NONE; + } +} + +void We517DrvInit(void) +{ + if (PinUsed(GPIO_WE517_RX) && PinUsed(GPIO_WE517_TX)) { + energy_flg = XNRG_17; + } +} + + + + + +bool Xnrg17(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { WE517Every250ms(); } + break; + case FUNC_INIT: + We517SnsInit(); + break; + case FUNC_PRE_INIT: + We517DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xnrg_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xnrg_interface.ino" +#ifdef USE_ENERGY_SENSOR + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xnrg_func_ptr[])(uint8_t) = { +#endif + +#ifdef XNRG_01 + &Xnrg01, +#endif + +#ifdef XNRG_02 + &Xnrg02, +#endif + +#ifdef XNRG_03 + &Xnrg03, +#endif + +#ifdef XNRG_04 + &Xnrg04, +#endif + +#ifdef XNRG_05 + &Xnrg05, +#endif + +#ifdef XNRG_06 + &Xnrg06, +#endif + +#ifdef XNRG_07 + &Xnrg07, +#endif + +#ifdef XNRG_08 + &Xnrg08, +#endif + +#ifdef XNRG_09 + &Xnrg09, +#endif + +#ifdef XNRG_10 + &Xnrg10, +#endif + +#ifdef XNRG_11 + &Xnrg11, +#endif + +#ifdef XNRG_12 + &Xnrg12, +#endif + +#ifdef XNRG_13 + &Xnrg13, +#endif + +#ifdef XNRG_14 + &Xnrg14, +#endif + +#ifdef XNRG_15 + &Xnrg15, +#endif + +#ifdef XNRG_16 + &Xnrg16, +#endif + +#ifdef XNRG_17 + &Xnrg17, +#endif + +#ifdef XNRG_18 + &Xnrg18, +#endif + +#ifdef XNRG_19 + &Xnrg19, +#endif + +#ifdef XNRG_20 + &Xnrg20, +#endif + +#ifdef XNRG_21 + &Xnrg21, +#endif + +#ifdef XNRG_22 + &Xnrg22, +#endif + +#ifdef XNRG_23 + &Xnrg23, +#endif + +#ifdef XNRG_24 + &Xnrg24, +#endif + +#ifdef XNRG_25 + &Xnrg25, +#endif + +#ifdef XNRG_26 + &Xnrg26, +#endif + +#ifdef XNRG_27 + &Xnrg27, +#endif + +#ifdef XNRG_28 + &Xnrg28, +#endif + +#ifdef XNRG_29 + &Xnrg29, +#endif + +#ifdef XNRG_30 + &Xnrg30, +#endif + +#ifdef XNRG_31 + &Xnrg31, +#endif + +#ifdef XNRG_32 + &Xnrg32 +#endif +}; + +const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); + +uint8_t xnrg_active = 0; + +bool XnrgCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("NRG: %d"), function); + + if (FUNC_PRE_INIT == function) { + for (uint32_t x = 0; x < xnrg_present; x++) { + xnrg_func_ptr[x](function); + if (energy_flg) { + xnrg_active = x; + return true; + } + } + } + else if (energy_flg) { + return xnrg_func_ptr[xnrg_active](function); + } + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_01_counter.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_01_counter.ino" +#ifdef USE_COUNTER + + + + +#define XSNS_01 1 + +#define USE_AC_ZERO_CROSS_DIMMER 1 + +#define D_PRFX_COUNTER "Counter" +#define D_CMND_COUNTERTYPE "Type" +#define D_CMND_COUNTERDEBOUNCE "Debounce" +#define D_CMND_COUNTERDEBOUNCELOW "DebounceLow" +#define D_CMND_COUNTERDEBOUNCEHIGH "DebounceHigh" + +const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" + "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_COUNTERDEBOUNCELOW "|" D_CMND_COUNTERDEBOUNCEHIGH ; + +void (* const CounterCommand[])(void) PROGMEM = { + &CmndCounter, &CmndCounterType, &CmndCounterDebounce, &CmndCounterDebounceLow, &CmndCounterDebounceHigh }; + +uint8_t ctr_index[MAX_COUNTERS] = { 0, 1, 2, 3 }; + +struct COUNTER { + uint32_t timer[MAX_COUNTERS]; + uint32_t timer_low_high[MAX_COUNTERS]; + uint8_t no_pullup = 0; + uint8_t pin_state = 0; + bool any_counter = false; + +} Counter; + +#ifdef USE_AC_ZERO_CROSS_DIMMER +struct AC_ZERO_CROSS_DIMMER { + bool startReSync = false; + uint32_t current_cycle_ClockCycles = 0; + uint32_t dimm_timeClockCycles = 0; + uint32_t currentCycleCount = 0; + uint32_t tobe_cycle_timeClockCycles = 0; + uint32_t lastCycleCount = 0; + uint32_t currentSteps = 100; +} ac_zero_cross_dimmer; +#endif + +void ICACHE_RAM_ATTR CounterIsrArg(void *arg) { + uint32_t index = *static_cast(arg); + + uint32_t time = micros(); + uint32_t debounce_time; + + if (Counter.pin_state) { + + if (digitalRead(Pin(GPIO_CNTR1, index)) == bitRead(Counter.pin_state, index)) { + + return; + } + debounce_time = time - Counter.timer_low_high[index]; + if bitRead(Counter.pin_state, index) { + + if (debounce_time <= Settings.pulse_counter_debounce_high * 1000) return; + } else { + + if (debounce_time <= Settings.pulse_counter_debounce_low * 1000) return; + } + + Counter.timer_low_high[index] = time; + Counter.pin_state ^= (1<0) { + ac_zero_cross_dimmer.startReSync = true; + ac_zero_cross_dimmer.currentSteps = (ac_zero_cross_dimmer.currentCycleCount-ac_zero_cross_dimmer.lastCycleCount+(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles/2))/(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles); + ac_zero_cross_dimmer.current_cycle_ClockCycles = (ac_zero_cross_dimmer.currentCycleCount-ac_zero_cross_dimmer.lastCycleCount)/ac_zero_cross_dimmer.currentSteps; + } + ac_zero_cross_dimmer.lastCycleCount = ac_zero_cross_dimmer.currentCycleCount; + } +#endif + return; + } + } + + debounce_time = time - Counter.timer[index]; + if (debounce_time > Settings.pulse_counter_debounce * 1000) { + Counter.timer[index] = time; + if (bitRead(Settings.pulse_counter_type, index)) { + RtcSettings.pulse_counter[index] = debounce_time; + } else { + RtcSettings.pulse_counter[index]++; + } + } +} + + + +void CounterInterruptDisable(bool state) { + if (state) { + if (Counter.any_counter) { + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (PinUsed(GPIO_CNTR1, i)) { + detachInterrupt(Pin(GPIO_CNTR1, i)); + } + } + Counter.any_counter = false; + } + } else { + if (!Counter.any_counter) { + CounterInit(); + } + } +} + +bool CounterPinState(void) +{ + if ((XdrvMailbox.index >= AGPIO(GPIO_CNTR1_NP)) && (XdrvMailbox.index < (AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS))) { + bitSet(Counter.no_pullup, XdrvMailbox.index - AGPIO(GPIO_CNTR1_NP)); + XdrvMailbox.index -= (AGPIO(GPIO_CNTR1_NP) - AGPIO(GPIO_CNTR1)); + return true; + } + return false; +} + +void CounterInit(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (PinUsed(GPIO_CNTR1, i)) { +#ifdef USE_AC_ZERO_CROSS_DIMMER + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles = microsecondsToClockCycles(1000000 / Settings.pwm_frequency); +#endif + Counter.any_counter = true; + pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high) && !Settings.flag4.zerocross_dimmer) { + Counter.pin_state = 0; + attachInterruptArg(Pin(GPIO_CNTR1, i), CounterIsrArg, &ctr_index[i], FALLING); + } else { + Counter.pin_state = 0x8f; + attachInterruptArg(Pin(GPIO_CNTR1, i), CounterIsrArg, &ctr_index[i], CHANGE); + } + } + } +} + +void CounterEverySecond(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (PinUsed(GPIO_CNTR1, i)) { + if (bitRead(Settings.pulse_counter_type, i)) { + uint32_t time = micros() - Counter.timer[i]; + if (time > 4200000000) { + RtcSettings.pulse_counter[i] = 4200000000; + } + } + } + } +} + +void CounterSaveState(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (PinUsed(GPIO_CNTR1, i)) { + Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; + } + } +} + +void CounterShow(bool json) +{ + bool header = false; + uint8_t dsxflg = 0; + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (PinUsed(GPIO_CNTR1, i)) { + char counter[33]; + if (bitRead(Settings.pulse_counter_type, i)) { + dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); + } else { + dsxflg++; + snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); + } + + if (json) { + if (!header) { + ResponseAppend_P(PSTR(",\"COUNTER\":{")); + } + ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); + header = true; +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (1 == dsxflg)) { + DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); + dsxflg++; + } +#endif + if ((0 == tele_period ) && (Settings.flag3.counter_reset_on_tele)) { + RtcSettings.pulse_counter[i] = 0; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), + i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); +#endif + } + } + } + if (header) { + ResponseJsonEnd(); + } +} + +#ifdef USE_AC_ZERO_CROSS_DIMMER +void SyncACDimmer(void) +{ + if (ac_zero_cross_dimmer.startReSync) { + + for (uint32_t i = 0; i < 1; i++) { + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) + { + + const uint32_t current_cycle = ESP.getCycleCount(); + digitalWrite(Pin(GPIO_PWM1, i), LOW); + + ac_zero_cross_dimmer.startReSync = false; + + + ac_zero_cross_dimmer.dimm_timeClockCycles = (ac_zero_cross_dimmer.tobe_cycle_timeClockCycles * (1024 - (Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]))) / 1024; + ac_zero_cross_dimmer.dimm_timeClockCycles = tmax(ac_zero_cross_dimmer.dimm_timeClockCycles, 16000); + + uint32_t timelag_ClockCycles = (current_cycle - ac_zero_cross_dimmer.currentCycleCount)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles; + timelag_ClockCycles = ((ac_zero_cross_dimmer.dimm_timeClockCycles + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles) - timelag_ClockCycles)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles; + delayMicroseconds(clockCyclesToMicroseconds(timelag_ClockCycles)); + + #ifdef ESP8266 + pinMode(Pin(GPIO_PWM1, i), OUTPUT); + + uint32_t high = ac_zero_cross_dimmer.current_cycle_ClockCycles / 256; + uint32_t low = ac_zero_cross_dimmer.current_cycle_ClockCycles - high; + + startWaveformClockCycles(Pin(GPIO_PWM1, i), high, low, 0, -1, 0, true); + #else + analogWrite(Pin(GPIO_PWM1, i), 5); + #endif + + } + } + } +} +#endif + + + + + +void CmndCounter(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.data_len > 0) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { + if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { + RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + } else { + RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + } + ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); + } +} + +void CmndCounterType(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { + bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); + RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; + Settings.pulse_counter[XdrvMailbox.index -1] = 0; + } + ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); + } +} + +void CmndCounterDebounce(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.pulse_counter_debounce); +} + +void CmndCounterDebounceLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_low = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_low); +} + +void CmndCounterDebounceHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_high = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_high); +} + + + + + +bool Xsns01(uint8_t function) +{ + bool result = false; + + if (Counter.any_counter) { + switch (function) { + case FUNC_EVERY_SECOND: + CounterEverySecond(); + break; + case FUNC_JSON_APPEND: + CounterShow(1); + break; +#ifdef USE_AC_ZERO_CROSS_DIMMER + case FUNC_LOOP: + SyncACDimmer(); + break; +#endif +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CounterShow(0); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + case FUNC_SAVE_AT_MIDNIGHT: + CounterSaveState(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kCounterCommands, CounterCommand); + break; + } + } else { + switch (function) { + case FUNC_INIT: + CounterInit(); + break; + case FUNC_PIN_STATE: + result = CounterPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_02_analog.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_02_analog.ino" +#ifdef USE_ADC + + + + +#define XSNS_02 2 + +#ifdef ESP8266 +#define ANALOG_RESOLUTION 10 +#define ANALOG_RANGE 1023 +#else +#undef ANALOG_RESOLUTION +#define ANALOG_RESOLUTION 12 +#undef ANALOG_RANGE +#define ANALOG_RANGE 4095 +#endif + +#define TO_CELSIUS(x) ((x) - 273.15) +#define TO_KELVIN(x) ((x) + 273.15) + + +#define ANALOG_V33 3.3 +#define ANALOG_T0 TO_KELVIN(25.0) + + + + + +#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 +#define ANALOG_NTC_RESISTANCE 10000 +#define ANALOG_NTC_B_COEFFICIENT 3350 + + + + + +#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 +#define ANALOG_LDR_LUX_CALC_SCALAR 12518931 +#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 +# 68 "/workspace/Tasmota/tasmota/xsns_02_analog.ino" +#define ANALOG_CT_FLAGS 0 +#define ANALOG_CT_MULTIPLIER 2146 +#define ANALOG_CT_VOLTAGE 2300 + +#define CT_FLAG_ENERGY_RESET (1 << 0) +# 83 "/workspace/Tasmota/tasmota/xsns_02_analog.ino" +#define ANALOG_BUTTON 128 +# 94 "/workspace/Tasmota/tasmota/xsns_02_analog.ino" +#define ANALOG_JOYSTICK (ANALOG_RANGE / 3) +100 + +struct { + uint8_t present = 0; + uint8_t type = 0; +} Adcs; + +struct { + float temperature = 0; + float current = 0; + float energy = 0; + uint32_t param1 = 0; + uint32_t param2 = 0; + int param3 = 0; + int param4 = 0; + uint32_t previous_millis = 0; + uint16_t last_value = 0; + uint8_t type = 0; + uint8_t pin = 0; +} Adc[MAX_ADCS]; + +#ifdef ESP8266 +bool adcAttachPin(uint8_t pin) { + return (ADC0_PIN == pin); +} +#endif + +void AdcSaveSettings(uint32_t idx) { + char parameters[32]; + snprintf_P(parameters, sizeof(parameters), PSTR("%d,%d,%d,%d,%d"), + Adc[idx].type, Adc[idx].param1, Adc[idx].param2, Adc[idx].param3, Adc[idx].param4); + SettingsUpdateText(SET_ADC_PARAM1 + idx, parameters); +} + +void AdcGetSettings(uint32_t idx) { + char parameters[32]; + Adcs.type = 0; + Adc[idx].param1 = 0; + Adc[idx].param2 = 0; + Adc[idx].param3 = 0; + Adc[idx].param4 = 0; + if (strstr(SettingsText(SET_ADC_PARAM1 + idx), ",") != nullptr) { + Adcs.type = atoi(subStr(parameters, SettingsText(SET_ADC_PARAM1 + idx), ",", 1)); + Adc[idx].param1 = atoi(subStr(parameters, SettingsText(SET_ADC_PARAM1 + idx), ",", 2)); + Adc[idx].param2 = atoi(subStr(parameters, SettingsText(SET_ADC_PARAM1 + idx), ",", 3)); + Adc[idx].param3 = atoi(subStr(parameters, SettingsText(SET_ADC_PARAM1 + idx), ",", 4)); + Adc[idx].param4 = atoi(subStr(parameters, SettingsText(SET_ADC_PARAM1 + idx), ",", 5)); + } +} + +void AdcInitParams(uint8_t idx) { + if ((Adcs.type != Adc[idx].type) || (Adc[idx].param1 > 1000000)) { + if (ADC_TEMP == Adc[idx].type) { + + Adc[idx].param1 = ANALOG_NTC_BRIDGE_RESISTANCE; + Adc[idx].param2 = ANALOG_NTC_RESISTANCE; + Adc[idx].param3 = ANALOG_NTC_B_COEFFICIENT * 10000; + } + else if (ADC_LIGHT == Adc[idx].type) { + Adc[idx].param1 = ANALOG_LDR_BRIDGE_RESISTANCE; + Adc[idx].param2 = ANALOG_LDR_LUX_CALC_SCALAR; + Adc[idx].param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; + } + else if (ADC_RANGE == Adc[idx].type) { + Adc[idx].param1 = 0; + Adc[idx].param2 = ANALOG_RANGE; + Adc[idx].param3 = 0; + Adc[idx].param4 = 100; + } + else if (ADC_CT_POWER == Adc[idx].type) { + Adc[idx].param1 = ANALOG_CT_FLAGS; + Adc[idx].param2 = ANALOG_CT_MULTIPLIER; + Adc[idx].param3 = ANALOG_CT_VOLTAGE; + } + } + if ((Adcs.type != Adc[idx].type) || (0 == Adc[idx].param1) || (Adc[idx].param1 > ANALOG_RANGE)) { + if ((ADC_BUTTON == Adc[idx].type) || (ADC_BUTTON_INV == Adc[idx].type)) { + Adc[idx].param1 = ANALOG_BUTTON; + } + else if (ADC_JOY == Adc[idx].type) { + Adc[idx].param1 = ANALOG_JOYSTICK; + } + } +} + +void AdcAttach(uint8_t pin, uint8_t type) { + if (Adcs.present == MAX_ADCS) { return; } + Adc[Adcs.present].pin = pin; + if (adcAttachPin(Adc[Adcs.present].pin)) { + Adc[Adcs.present].type = type; + + Adcs.present++; + } +} + +void AdcInit(void) { + Adcs.present = 0; + for (uint32_t i = 0; i < MAX_ADCS; i++) { + if (PinUsed(GPIO_ADC_INPUT, i)) { + AdcAttach(Pin(GPIO_ADC_INPUT, i), ADC_INPUT); + } + if (PinUsed(GPIO_ADC_TEMP, i)) { + AdcAttach(Pin(GPIO_ADC_TEMP, i), ADC_TEMP); + } + if (PinUsed(GPIO_ADC_LIGHT, i)) { + AdcAttach(Pin(GPIO_ADC_LIGHT, i), ADC_LIGHT); + } + if (PinUsed(GPIO_ADC_RANGE, i)) { + AdcAttach(Pin(GPIO_ADC_RANGE, i), ADC_RANGE); + } + if (PinUsed(GPIO_ADC_CT_POWER, i)) { + AdcAttach(Pin(GPIO_ADC_CT_POWER, i), ADC_CT_POWER); + } + if (PinUsed(GPIO_ADC_JOY, i)) { + AdcAttach(Pin(GPIO_ADC_JOY, i), ADC_JOY); + } + } + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if (PinUsed(GPIO_ADC_BUTTON, i)) { + AdcAttach(Pin(GPIO_ADC_BUTTON, i), ADC_BUTTON); + } + else if (PinUsed(GPIO_ADC_BUTTON_INV, i)) { + AdcAttach(Pin(GPIO_ADC_BUTTON_INV, i), ADC_BUTTON_INV); + } + } + + if (Adcs.present) { +#ifdef ESP32 + analogSetClockDiv(1); + analogSetWidth(ANALOG_RESOLUTION); + analogSetAttenuation(ADC_11db); +#endif + for (uint32_t idx = 0; idx < Adcs.present; idx++) { + AdcGetSettings(idx); + AdcInitParams(idx); + AdcSaveSettings(idx); + } + } +} + +uint16_t AdcRead(uint32_t pin, uint32_t factor) { + + + + + + uint32_t samples = 1 << factor; + uint32_t analog = 0; + for (uint32_t i = 0; i < samples; i++) { + analog += analogRead(pin); + delay(1); + } + analog >>= factor; + return analog; +} + +#ifdef USE_RULES +void AdcEvery250ms(void) { + char adc_idx[3] = { 0 }; + uint32_t offset = 0; + for (uint32_t idx = 0; idx < Adcs.present; idx++) { +#ifdef ESP32 + snprintf_P(adc_idx, sizeof(adc_idx), PSTR("%d"), idx +1); + offset = 1; +#endif + if (ADC_INPUT == Adc[idx].type) { + uint16_t new_value = AdcRead(Adc[idx].pin, 5); + if ((new_value < Adc[idx].last_value -10) || (new_value > Adc[idx].last_value +10)) { + Adc[idx].last_value = new_value; + uint16_t value = Adc[idx].last_value / 10; + Response_P(PSTR("{\"ANALOG\":{\"A%ddiv10\":%d}}"), idx + offset, (value > 99) ? 100 : value); + XdrvRulesProcess(); + } + } + else if (ADC_JOY == Adc[idx].type) { + uint16_t new_value = AdcRead(Adc[idx].pin, 1); + if (new_value && (new_value != Adc[idx].last_value)) { + Adc[idx].last_value = new_value; + uint16_t value = new_value / Adc[idx].param1; + Response_P(PSTR("{\"ANALOG\":{\"Joy%s\":%d}}"), adc_idx, value); + XdrvRulesProcess(); + } else { + Adc[idx].last_value = 0; + } + } + } +} +#endif + +uint8_t AdcGetButton(uint32_t pin) { + for (uint32_t idx = 0; idx < Adcs.present; idx++) { + if (Adc[idx].pin == pin) { + if (ADC_BUTTON_INV == Adc[idx].type) { + return (AdcRead(Adc[idx].pin, 1) < Adc[idx].param1); + } + else if (ADC_BUTTON == Adc[idx].type) { + return (AdcRead(Adc[idx].pin, 1) > Adc[idx].param1); + } + } + } + return 0; +} + +uint16_t AdcGetLux(uint32_t idx) { + int adc = AdcRead(Adc[idx].pin, 2); + + double resistorVoltage = ((double)adc / ANALOG_RANGE) * ANALOG_V33; + double ldrVoltage = ANALOG_V33 - resistorVoltage; + double ldrResistance = ldrVoltage / resistorVoltage * (double)Adc[idx].param1; + double ldrLux = (double)Adc[idx].param2 * FastPrecisePow(ldrResistance, (double)Adc[idx].param3 / 10000); + + return (uint16_t)ldrLux; +} + +uint16_t AdcGetRange(uint32_t idx) { + + + + int adc = AdcRead(Adc[idx].pin, 2); + double adcrange = ( ((double)Adc[idx].param2 - (double)adc) / ( ((double)Adc[idx].param2 - (double)Adc[idx].param1)) * ((double)Adc[idx].param3 - (double)Adc[idx].param4) + (double)Adc[idx].param4 ); + return (uint16_t)adcrange; +} + +void AdcGetCurrentPower(uint8_t idx, uint8_t factor) { + + + + + + uint8_t samples = 1 << factor; + uint16_t analog = 0; + uint16_t analog_min = ANALOG_RANGE; + uint16_t analog_max = 0; + + if (0 == Adc[idx].param1) { + for (uint32_t i = 0; i < samples; i++) { + analog = analogRead(Adc[idx].pin); + if (analog < analog_min) { + analog_min = analog; + } + if (analog > analog_max) { + analog_max = analog; + } + delay(1); + } + Adc[idx].current = (float)(analog_max-analog_min) * ((float)(Adc[idx].param2) / 100000); + } + else { + analog = AdcRead(Adc[idx].pin, 5); + if (analog > Adc[idx].param1) { + Adc[idx].current = ((float)(analog) - (float)Adc[idx].param1) * ((float)(Adc[idx].param2) / 100000); + } + else { + Adc[idx].current = 0; + } + } + + float power = Adc[idx].current * (float)(Adc[idx].param3) / 10; + uint32_t current_millis = millis(); + Adc[idx].energy = Adc[idx].energy + ((power * (current_millis - Adc[idx].previous_millis)) / 3600000000); + Adc[idx].previous_millis = current_millis; +} + +void AdcEverySecond(void) { + for (uint32_t idx = 0; idx < Adcs.present; idx++) { + if (ADC_TEMP == Adc[idx].type) { + int adc = AdcRead(Adc[idx].pin, 2); + + double Rt = (adc * Adc[idx].param1) / (1024.0 * ANALOG_V33 - (double)adc); + double BC = (double)Adc[idx].param3 / 10000; + double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Adc[idx].param2)); + Adc[idx].temperature = ConvertTemp(TO_CELSIUS(T)); + } + else if (ADC_CT_POWER == Adc[idx].type) { + AdcGetCurrentPower(idx, 5); + } + } +} + +void AdcShowContinuation(bool *jsonflg) { + if (*jsonflg) { + ResponseAppend_P(PSTR(",")); + } else { + ResponseAppend_P(PSTR(",\"ANALOG\":{")); + *jsonflg = true; + } +} + +void AdcShow(bool json) { + bool domo_flag[ADC_END] = { false }; + char adc_name[10] = { 0 }; + char adc_idx[3] = { 0 }; + uint32_t offset = 0; + + bool jsonflg = false; + for (uint32_t idx = 0; idx < Adcs.present; idx++) { +#ifdef ESP32 + snprintf_P(adc_name, sizeof(adc_name), PSTR("Analog%d"), idx +1); + snprintf_P(adc_idx, sizeof(adc_idx), PSTR("%d"), idx +1); + offset = 1; +#endif + + switch (Adc[idx].type) { + case ADC_INPUT: { + uint16_t analog = AdcRead(Adc[idx].pin, 5); + + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"A%d\":%d"), idx + offset, analog); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ANALOG, "", idx + offset, analog); +#endif + } + break; + } + case ADC_TEMP: { + char temperature[33]; + dtostrfd(Adc[idx].temperature, Settings.flag2.temperature_resolution, temperature); + + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "%s\":%s"), adc_idx, temperature); + if ((0 == tele_period) && (!domo_flag[ADC_TEMP])) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); + domo_flag[ADC_TEMP] = true; +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, Adc[idx].temperature); +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, adc_name, temperature, TempUnit()); +#endif + } + break; + } + case ADC_LIGHT: { + uint16_t adc_light = AdcGetLux(idx); + + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"" D_JSON_ILLUMINANCE "%s\":%d"), adc_idx, adc_light); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (!domo_flag[ADC_LIGHT])) { + DomoticzSensor(DZ_ILLUMINANCE, adc_light); + domo_flag[ADC_LIGHT] = true; + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, adc_name, adc_light); +#endif + } + break; + } + case ADC_RANGE: { + uint16_t adc_range = AdcGetRange(idx); + + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"" D_JSON_RANGE "%s\":%d"), adc_idx, adc_range); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_RANGE, adc_name, adc_range); +#endif + } + break; + } + case ADC_CT_POWER: { + AdcGetCurrentPower(idx, 5); + + float voltage = (float)(Adc[idx].param3) / 10; + char voltage_chr[FLOATSZ]; + dtostrfd(voltage, Settings.flag2.voltage_resolution, voltage_chr); + char current_chr[FLOATSZ]; + dtostrfd(Adc[idx].current, Settings.flag2.current_resolution, current_chr); + char power_chr[FLOATSZ]; + dtostrfd(voltage * Adc[idx].current, Settings.flag2.wattage_resolution, power_chr); + char energy_chr[FLOATSZ]; + dtostrfd(Adc[idx].energy, Settings.flag2.energy_resolution, energy_chr); + + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"CTEnergy%s\":{\"" D_JSON_ENERGY "\":%s,\"" D_JSON_POWERUSAGE "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), + adc_idx, energy_chr, power_chr, voltage_chr, current_chr); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (!domo_flag[ADC_CT_POWER])) { + DomoticzSensor(DZ_POWER_ENERGY, power_chr); + DomoticzSensor(DZ_VOLTAGE, voltage_chr); + DomoticzSensor(DZ_CURRENT, current_chr); + domo_flag[ADC_CT_POWER] = true; + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VOLTAGE, voltage_chr); + WSContentSend_PD(HTTP_SNS_CURRENT, current_chr); + WSContentSend_PD(HTTP_SNS_POWER, power_chr); + WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, energy_chr); +#endif + } + break; + } + case ADC_JOY: { + uint16_t new_value = AdcRead(Adc[idx].pin, 1); + uint16_t value = new_value / Adc[idx].param1; + if (json) { + AdcShowContinuation(&jsonflg); + ResponseAppend_P(PSTR("\"Joy%s\":%d"), adc_idx, value); + } + break; + } + } + } + if (jsonflg) { + ResponseJsonEnd(); + } +} + + + + + +const char kAdcCommands[] PROGMEM = "|" + D_CMND_ADCPARAM; + +void (* const AdcCommand[])(void) PROGMEM = { + &CmndAdcParam }; + +void CmndAdcParam(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_ADCS)) { + uint8_t idx = XdrvMailbox.index -1; + if (XdrvMailbox.data_len) { + if (XdrvMailbox.payload > ADC_INPUT) { + AdcGetSettings(idx); + if (ChrCount(XdrvMailbox.data, ",") > 2) { + char sub_string[XdrvMailbox.data_len +1]; + + + + + + + + Adc[idx].type = XdrvMailbox.payload; + Adc[idx].param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Adc[idx].param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + if (ADC_RANGE == XdrvMailbox.payload) { + Adc[idx].param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); + Adc[idx].param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); + } else { + Adc[idx].param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + } + if (ADC_CT_POWER == XdrvMailbox.payload) { + if (((1 == Adc[idx].param1) & CT_FLAG_ENERGY_RESET) > 0) { + for (uint32_t idx = 0; idx < MAX_ADCS; idx++) { + Adc[idx].energy = 0; + } + Adc[idx].param1 ^= CT_FLAG_ENERGY_RESET; + } + } + } else { + + + + + + + + Adcs.type = 0; + AdcInitParams(idx); + } + AdcSaveSettings(idx); + } + } + + + AdcGetSettings(idx); + Response_P(PSTR("{\"" D_CMND_ADCPARAM "%d\":[%d,%d,%d"), idx +1, Adcs.type, Adc[idx].param1, Adc[idx].param2); + if (ADC_RANGE == Adc[idx].type) { + ResponseAppend_P(PSTR(",%d,%d"), Adc[idx].param3, Adc[idx].param4); + } else { + int value = Adc[idx].param3; + uint8_t precision; + for (precision = 4; precision > 0; precision--) { + if (value % 10) { break; } + value /= 10; + } + char param3[33]; + dtostrfd(((double)Adc[idx].param3)/10000, precision, param3); + ResponseAppend_P(PSTR(",%s"), param3); + } + ResponseAppend_P(PSTR("]}")); + } +} + + + + + +bool Xsns02(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kAdcCommands, AdcCommand); + break; + case FUNC_MODULE_INIT: + AdcInit(); + break; + default: + if (Adcs.present) { + switch (function) { +#ifdef USE_RULES + case FUNC_EVERY_250_MSECOND: + AdcEvery250ms(); + break; +#endif + case FUNC_EVERY_SECOND: + AdcEverySecond(); + break; + case FUNC_JSON_APPEND: + AdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AdcShow(0); + break; +#endif + } + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_04_snfsc.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_04_snfsc.ino" +#ifdef USE_SONOFF_SC +# 57 "/workspace/Tasmota/tasmota/xsns_04_snfsc.ino" +#define XSNS_04 4 + +uint16_t sc_value[5] = { 0 }; + +void SonoffScSend(const char *data) +{ + Serial.write(data); + Serial.write('\x1B'); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); +} + +void SonoffScInit(void) +{ + + SonoffScSend("AT+START"); + +} + +void SonoffScSerialInput(char *rcvstat) +{ + char *p; + char *str; + uint16_t value[5] = { 0 }; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); + + if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { + int8_t i = -1; + for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(nullptr, ":", &p)) { + value[i++] = atoi(str); + } + if (value[0] > 0) { + for (uint32_t i = 0; i < 5; i++) { + sc_value[i] = value[i]; + } + sc_value[2] = (11 - sc_value[2]) * 10; + sc_value[3] *= 10; + sc_value[4] = (11 - sc_value[4]) * 10; + SonoffScSend("AT+SEND=ok"); + } else { + SonoffScSend("AT+SEND=fail"); + } + } + else if (!strcasecmp_P(rcvstat, PSTR("AT+STATUS?"))) { + SonoffScSend("AT+STATUS=4"); + } +} + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SCPLUS[] PROGMEM = + "{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; +#endif + +void SonoffScShow(bool json) +{ + if (sc_value[0] > 0) { + float t = ConvertTemp(sc_value[1]); + float h = ConvertHumidity(sc_value[0]); + + if (json) { + ResponseAppend_P(PSTR(",\"SonoffSC\":{")); + ResponseAppendTHD(t, h); + ResponseAppend_P(PSTR(",\"" D_JSON_LIGHT "\":%d,\"" D_JSON_NOISE "\":%d,\"" D_JSON_AIRQUALITY "\":%d}"), sc_value[2], sc_value[3], sc_value[4]); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumPressureSensor(t, h); + DomoticzSensor(DZ_ILLUMINANCE, sc_value[2]); + DomoticzSensor(DZ_COUNT, sc_value[3]); + DomoticzSensor(DZ_AIRQUALITY, 500 + ((100 - sc_value[4]) * 20)); + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, t); + KnxSensor(KNX_HUMIDITY, h); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD("", t, h); + WSContentSend_PD(HTTP_SNS_SCPLUS, sc_value[2], sc_value[3], sc_value[4]); +#endif + } + } +} + + + + + +bool Xsns04(uint8_t function) +{ + bool result = false; + + if (SONOFF_SC == my_module_type) { + switch (function) { + case FUNC_JSON_APPEND: + SonoffScShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SonoffScShow(0); + break; +#endif + case FUNC_INIT: + SonoffScInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_05_ds18x20.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_05_ds18x20.ino" +#ifdef ESP8266 +#ifdef USE_DS18x20 + + + + +#define XSNS_05 5 + + + +#define DS18S20_CHIPID 0x10 +#define DS1822_CHIPID 0x22 +#define DS18B20_CHIPID 0x28 +#define MAX31850_CHIPID 0x3B + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_WRITE_EEPROM 0x48 +#define W1_WRITE_SCRATCHPAD 0x4E +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; + +uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; + +struct DS18X20STRUCT { + uint8_t address[8]; + uint8_t index; + uint8_t valid; + float temperature; +} ds18x20_sensor[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_sensors = 0; +uint8_t ds18x20_pin = 0; +uint8_t ds18x20_pin_out = 0; +bool ds18x20_dual_mode = false; +char ds18x20_types[12]; +#ifdef W1_PARASITE_POWER +uint8_t ds18x20_sensor_curr = 0; +unsigned long w1_power_until = 0; +#endif + + + + + +#define W1_MATCH_ROM 0x55 +#define W1_SEARCH_ROM 0xF0 + +uint8_t onewire_last_discrepancy = 0; +uint8_t onewire_last_family_discrepancy = 0; +bool onewire_last_device_flag = false; +unsigned char onewire_rom_id[8] = { 0 }; + + + +uint8_t OneWireReset(void) +{ + uint8_t retries = 125; + + if (!ds18x20_dual_mode) { + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(480); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + delayMicroseconds(70); + uint8_t r = !digitalRead(ds18x20_pin); + delayMicroseconds(410); + return r; + } else { + digitalWrite(ds18x20_pin_out, HIGH); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(480); + digitalWrite(ds18x20_pin_out, HIGH); + delayMicroseconds(70); + uint8_t r = !digitalRead(ds18x20_pin); + delayMicroseconds(410); + return r; + } +} + +void OneWireWriteBit(uint8_t v) +{ + static const uint8_t delay_low[2] = { 65, 10 }; + static const uint8_t delay_high[2] = { 5, 55 }; + + v &= 1; + if (!ds18x20_dual_mode) { + digitalWrite(ds18x20_pin, LOW); + pinMode(ds18x20_pin, OUTPUT); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin, HIGH); + } else { + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin_out, HIGH); + } + delayMicroseconds(delay_high[v]); +} + +uint8_t OneWire1ReadBit(void) +{ + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(3); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + delayMicroseconds(10); + uint8_t r = digitalRead(ds18x20_pin); + delayMicroseconds(53); + return r; +} + +uint8_t OneWire2ReadBit(void) +{ + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(3); + digitalWrite(ds18x20_pin_out, HIGH); + delayMicroseconds(10); + uint8_t r = digitalRead(ds18x20_pin); + delayMicroseconds(53); + return r; +} + + + +void OneWireWrite(uint8_t v) +{ + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + OneWireWriteBit((bit_mask & v) ? 1 : 0); + } +} + +uint8_t OneWireRead(void) +{ + uint8_t r = 0; + + if (!ds18x20_dual_mode) { + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + if (OneWire1ReadBit()) { + r |= bit_mask; + } + } + } else { + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + if (OneWire2ReadBit()) { + r |= bit_mask; + } + } + } + return r; +} + +void OneWireSelect(const uint8_t rom[8]) +{ + OneWireWrite(W1_MATCH_ROM); + for (uint32_t i = 0; i < 8; i++) { + OneWireWrite(rom[i]); + } +} + +void OneWireResetSearch(void) +{ + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + for (uint32_t i = 0; i < 8; i++) { + onewire_rom_id[i] = 0; + } +} + +uint8_t OneWireSearch(uint8_t *newAddr) +{ + uint8_t id_bit_number = 1; + uint8_t last_zero = 0; + uint8_t rom_byte_number = 0; + uint8_t search_result = 0; + uint8_t id_bit; + uint8_t cmp_id_bit; + unsigned char rom_byte_mask = 1; + unsigned char search_direction; + + if (!onewire_last_device_flag) { + if (!OneWireReset()) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + return false; + } + OneWireWrite(W1_SEARCH_ROM); + do { + if (!ds18x20_dual_mode) { + id_bit = OneWire1ReadBit(); + cmp_id_bit = OneWire1ReadBit(); + } else { + id_bit = OneWire2ReadBit(); + cmp_id_bit = OneWire2ReadBit(); + } + if ((id_bit == 1) && (cmp_id_bit == 1)) { + break; + } else { + if (id_bit != cmp_id_bit) { + search_direction = id_bit; + } else { + if (id_bit_number < onewire_last_discrepancy) { + search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0); + } else { + search_direction = (id_bit_number == onewire_last_discrepancy); + } + if (search_direction == 0) { + last_zero = id_bit_number; + if (last_zero < 9) { + onewire_last_family_discrepancy = last_zero; + } + } + } + if (search_direction == 1) { + onewire_rom_id[rom_byte_number] |= rom_byte_mask; + } else { + onewire_rom_id[rom_byte_number] &= ~rom_byte_mask; + } + OneWireWriteBit(search_direction); + id_bit_number++; + rom_byte_mask <<= 1; + if (rom_byte_mask == 0) { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); + if (!(id_bit_number < 65)) { + onewire_last_discrepancy = last_zero; + if (onewire_last_discrepancy == 0) { + onewire_last_device_flag = true; + } + search_result = true; + } + } + if (!search_result || !onewire_rom_id[0]) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + search_result = false; + } + for (uint32_t i = 0; i < 8; i++) { + newAddr[i] = onewire_rom_id[i]; + } + return search_result; +} + +bool OneWireCrc8(uint8_t *addr) +{ + uint8_t crc = 0; + uint8_t len = 8; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) { + crc ^= 0x8C; + } + inbyte >>= 1; + } + } + return (crc == *addr); +} + + + +void Ds18x20Init(void) +{ + uint64_t ids[DS18X20_MAX_SENSORS]; + + ds18x20_pin = Pin(GPIO_DSB); + + if (PinUsed(GPIO_DSB_OUT)) { + ds18x20_pin_out = Pin(GPIO_DSB_OUT); + ds18x20_dual_mode = true; + pinMode(ds18x20_pin_out, OUTPUT); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + } + + OneWireResetSearch(); + + ds18x20_sensors = 0; + while (ds18x20_sensors < DS18X20_MAX_SENSORS) { + if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { + break; + } + if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) && + ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) { + ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors; + ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; + for (uint32_t j = 6; j > 0; j--) { + ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j]; + } + ds18x20_sensors++; + } + } + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + for (uint32_t j = i + 1; j < ds18x20_sensors; j++) { + if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { + std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); + } + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); +} + +void Ds18x20Convert(void) +{ + OneWireReset(); +#ifdef W1_PARASITE_POWER + + if (++ds18x20_sensor_curr >= ds18x20_sensors) + ds18x20_sensor_curr = 0; + OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address); +#else + OneWireWrite(W1_SKIP_ROM); +#endif + OneWireWrite(W1_CONVERT_TEMP); + +} + +bool Ds18x20Read(uint8_t sensor) +{ + uint8_t data[9]; + int8_t sign = 1; + + uint8_t index = ds18x20_sensor[sensor].index; + if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } + for (uint32_t retry = 0; retry < 3; retry++) { + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_READ_SCRATCHPAD); + for (uint32_t i = 0; i < 9; i++) { + data[i] = OneWireRead(); + } + if (OneWireCrc8(data)) { + switch(ds18x20_sensor[index].address[0]) { + case DS18S20_CHIPID: { +# 389 "/workspace/Tasmota/tasmota/xsns_05_ds18x20.ino" + int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); + ds18x20_sensor[index].temperature = ConvertTemp(tempS * 0.0625 - 0.250); + + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case DS1822_CHIPID: + case DS18B20_CHIPID: { + if (data[4] != 0x7F) { + data[4] = 0x7F; + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_SCRATCHPAD); + OneWireWrite(data[2]); + OneWireWrite(data[3]); + OneWireWrite(data[4]); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_EEPROM); +#ifdef W1_PARASITE_POWER + w1_power_until = millis() + 10; +#endif + } + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + } + } + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + return false; +} + +void Ds18x20Name(uint8_t sensor) +{ + uint8_t index = sizeof(ds18x20_chipids); + while (index) { + if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { + break; + } + index--; + } + GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); + if (ds18x20_sensors > 1) { + snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + } +} + + + +void Ds18x20EverySecond(void) +{ + if (!ds18x20_sensors) { return; } + +#ifdef W1_PARASITE_POWER + + unsigned long now = millis(); + if (now < w1_power_until) + return; +#endif + if (uptime & 1 +#ifdef W1_PARASITE_POWER + + || ds18x20_sensors >= 2 +#endif + ) { + + Ds18x20Convert(); + } else { + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + + if (!Ds18x20Read(i)) { + Ds18x20Name(i); + AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid); +#ifdef USE_DS18x20_RECONFIGURE + if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) { + memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor)); + Ds18x20Init(); + } +#endif + } + } + } +} + +void Ds18x20Show(bool json) +{ + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + uint8_t index = ds18x20_sensor[i].index; + + if (ds18x20_sensor[index].valid) { + char temperature[33]; + dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); + + Ds18x20Name(i); + + if (json) { + char address[17]; + for (uint32_t j = 0; j < 6; j++) { + sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); + } + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); +#endif + } + } + } +} + + + + + +bool Xsns05(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_DSB)) { + switch (function) { + case FUNC_INIT: + Ds18x20Init(); + break; + case FUNC_EVERY_SECOND: + Ds18x20EverySecond(); + break; + case FUNC_JSON_APPEND: + Ds18x20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ds18x20Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_05_ds18x20_esp32.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_05_ds18x20_esp32.ino" +#ifdef ESP32 +#ifdef USE_DS18x20 + + + + +#define XSNS_05 5 + +#define DS18S20_CHIPID 0x10 +#define DS1822_CHIPID 0x22 +#define DS18B20_CHIPID 0x28 +#define MAX31850_CHIPID 0x3B + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; + +uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; + +uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8]; +uint8_t ds18x20_index[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_valid[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_sensors = 0; +char ds18x20_types[12]; + + + +#include + +OneWire *ds = nullptr; + +void Ds18x20Init(void) { + ds = new OneWire(Pin(GPIO_DSB)); + + Ds18x20Search(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); +} + +void Ds18x20Search(void) { + uint8_t num_sensors=0; + uint8_t sensor = 0; + + ds->reset_search(); + for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { + if (!ds->search(ds18x20_address[num_sensors])) { + ds->reset_search(); + break; + } + + if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) && + ((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) || + (ds18x20_address[num_sensors][0]==DS1822_CHIPID) || + (ds18x20_address[num_sensors][0]==DS18B20_CHIPID) || + (ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) { + num_sensors++; + } + } + for (uint32_t i = 0; i < num_sensors; i++) { + ds18x20_index[i] = i; + } + for (uint32_t i = 0; i < num_sensors; i++) { + for (uint32_t j = i + 1; j < num_sensors; j++) { + if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) { + std::swap(ds18x20_index[i], ds18x20_index[j]); + } + } + } + ds18x20_sensors = num_sensors; +} + +void Ds18x20Convert(void) { + ds->reset(); + ds->write(W1_SKIP_ROM); + ds->write(W1_CONVERT_TEMP); + +} + +bool Ds18x20Read(uint8_t sensor, float &t) +{ + uint8_t data[12]; + int8_t sign = 1; + + t = NAN; + + uint8_t index = ds18x20_index[sensor]; + if (ds18x20_valid[index]) { ds18x20_valid[index]--; } + + ds->reset(); + ds->select(ds18x20_address[index]); + ds->write(W1_READ_SCRATCHPAD); + + for (uint32_t i = 0; i < 9; i++) { + data[i] = ds->read(); + } + if (OneWire::crc8(data, 8) == data[8]) { + switch(ds18x20_address[index][0]) { + case DS18S20_CHIPID: { + int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); + t = ConvertTemp(tempS * 0.0625 - 0.250); + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + case DS1822_CHIPID: + case DS18B20_CHIPID: { + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + t = ConvertTemp(sign * temp12 * 0.0625); + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + t = ConvertTemp(temp14 * 0.0625); + ds18x20_valid[index] = SENSOR_MAX_MISS; + return true; + } + } + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + return false; +} + +void Ds18x20Name(uint8_t sensor) +{ + uint8_t index = sizeof(ds18x20_chipids); + while (index) { + if (ds18x20_address[ds18x20_index[sensor]][0] == ds18x20_chipids[index]) { + break; + } + index--; + } + GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); + if (ds18x20_sensors > 1) { + snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + } +} + + + +void Ds18x20EverySecond(void) +{ + if (!ds18x20_sensors) { return; } + + if (uptime & 1) { + + + Ds18x20Convert(); + } else { + float t; + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + + if (!Ds18x20Read(i, t)) { + Ds18x20Name(i); + AddLogMissed(ds18x20_types, ds18x20_valid[ds18x20_index[i]]); + } + } + } +} + +void Ds18x20Show(bool json) +{ + float t; + + uint8_t dsxflg = 0; + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + if (Ds18x20Read(i, t)) { + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + + Ds18x20Name(i); + + if (json) { + char address[17]; + for (uint32_t j = 0; j < 6; j++) { + sprintf(address+2*j, "%02X", ds18x20_address[ds18x20_index[i]][6-j]); + } + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); + dsxflg++; +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (1 == dsxflg)) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (1 == dsxflg)) { + KnxSensor(KNX_TEMPERATURE, t); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); +#endif + } + } + } +} + + + + + +bool Xsns05(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_DSB)) { + switch (function) { + case FUNC_INIT: + Ds18x20Init(); + break; + case FUNC_EVERY_SECOND: + Ds18x20EverySecond(); + break; + case FUNC_JSON_APPEND: + Ds18x20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ds18x20Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_06_dht.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_06_dht.ino" +#ifdef USE_DHT +# 30 "/workspace/Tasmota/tasmota/xsns_06_dht.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + uint8_t lastresult; + char stype[12]; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +bool DhtWaitState(uint32_t sensor, uint32_t level) +{ + unsigned long timeout = micros() + 100; + while (digitalRead(Dht[sensor].pin) != level) { + if (TimeReachedUsec(timeout)) { + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), + (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); + return false; + } + delayMicroseconds(1); + } + return true; +} + +bool DhtRead(uint32_t sensor) +{ + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + delay(19); + break; + case GPIO_DHT22: + + delayMicroseconds(2000); + break; + case GPIO_SI7021: + delayMicroseconds(500); + break; + } + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + case GPIO_DHT22: + delayMicroseconds(50); + break; + case GPIO_SI7021: + delayMicroseconds(20); + break; + } +# 134 "/workspace/Tasmota/tasmota/xsns_06_dht.ino" + uint32_t i = 0; + noInterrupts(); + if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { + for (i = 0; i < 40; i++) { + if (!DhtWaitState(sensor, 1)) { break; } + delayMicroseconds(35); + if (digitalRead(Dht[sensor].pin)) { + dht_data[i / 8] |= (1 << (7 - i % 8)); + } + if (!DhtWaitState(sensor, 0)) { break; } + } + } + interrupts(); + if (i < 40) { return false; } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + float temperature = NAN; + float humidity = NAN; + switch (Dht[sensor].type) { + case GPIO_DHT11: + humidity = dht_data[0]; + temperature = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + temperature *= -1; + } + break; + } + if (isnan(temperature) || isnan(humidity)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); + return false; + } + + if (humidity > 100) { humidity = 100.0; } + if (humidity < 0) { humidity = 0.1; } + Dht[sensor].h = ConvertHumidity(humidity); + Dht[sensor].t = ConvertTemp(temperature); + Dht[sensor].lastresult = 0; + + return true; +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= AGPIO(GPIO_DHT11)) && (XdrvMailbox.index <= AGPIO(GPIO_SI7021))) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = BGPIO(XdrvMailbox.index); + dht_sensors++; + XdrvMailbox.index = AGPIO(GPIO_DHT11); + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + if (PinUsed(GPIO_DHT11_OUT)) { + dht_pin_out = Pin(GPIO_DHT11_OUT); + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastresult = DHT_MAX_RETRY; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { + + if (!DhtRead(sensor)) { + Dht[sensor].lastresult++; + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + } + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), Dht[i].stype, Dht[i].t, Dht[i].h); + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_07_sht1x.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_07_sht1x.ino" +#ifdef USE_I2C +#ifdef USE_SHT +# 31 "/workspace/Tasmota/tasmota/xsns_07_sht1x.ino" +#define XSNS_07 7 +#define XI2C_08 8 + +enum { + SHT1X_CMD_MEASURE_TEMP = B00000011, + SHT1X_CMD_MEASURE_RH = B00000101, + SHT1X_CMD_SOFT_RESET = B00011110 +}; + +uint8_t sht_sda_pin; +uint8_t sht_scl_pin; +uint8_t sht_type = 0; +char sht_types[] = "SHT1X"; +uint8_t sht_valid = 0; +float sht_temperature = 0; +float sht_humidity = 0; + +bool ShtReset(void) +{ + pinMode(sht_sda_pin, INPUT_PULLUP); + pinMode(sht_scl_pin, OUTPUT); + delay(11); + for (uint32_t i = 0; i < 9; i++) { + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + } + bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); + delay(11); + return success; +} + +bool ShtSendCommand(const uint8_t cmd) +{ + pinMode(sht_sda_pin, OUTPUT); + + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + + shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); + + bool ackerror = false; + digitalWrite(sht_scl_pin, HIGH); + pinMode(sht_sda_pin, INPUT_PULLUP); + if (digitalRead(sht_sda_pin) != LOW) { + ackerror = true; + } + digitalWrite(sht_scl_pin, LOW); + delayMicroseconds(1); + if (digitalRead(sht_sda_pin) != HIGH) { + ackerror = true; + } + if (ackerror) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); + } + return (!ackerror); +} + +bool ShtAwaitResult(void) +{ + + for (uint32_t i = 0; i < 16; i++) { + if (LOW == digitalRead(sht_sda_pin)) { + return true; + } + delay(20); + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); + + return false; +} + +int ShtReadData(void) +{ + int val = 0; + + + val = shiftIn(sht_sda_pin, sht_scl_pin, 8); + val <<= 8; + + pinMode(sht_sda_pin, OUTPUT); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + pinMode(sht_sda_pin, INPUT_PULLUP); + + val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); + + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + return val; +} + +bool ShtRead(void) +{ + if (sht_valid) { sht_valid--; } + if (!ShtReset()) { return false; } + if (!ShtSendCommand(SHT1X_CMD_MEASURE_TEMP)) { return false; } + if (!ShtAwaitResult()) { return false; } + float tempRaw = ShtReadData(); + if (!ShtSendCommand(SHT1X_CMD_MEASURE_RH)) { return false; } + if (!ShtAwaitResult()) { return false; } + float humRaw = ShtReadData(); + + + const float d1 = -39.7; + const float d2 = 0.01; + sht_temperature = d1 + (tempRaw * d2); + const float c1 = -2.0468; + const float c2 = 0.0367; + const float c3 = -1.5955E-6; + const float t1 = 0.01; + const float t2 = 0.00008; + float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; + sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; + sht_temperature = ConvertTemp(sht_temperature); + sht_humidity = ConvertHumidity(sht_humidity); + + sht_valid = SENSOR_MAX_MISS; + return true; +} + + + +void ShtDetect(void) +{ + sht_sda_pin = Pin(GPIO_I2C_SDA); + sht_scl_pin = Pin(GPIO_I2C_SCL); + if (ShtRead()) { + sht_type = 1; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); + } else { + Wire.begin(sht_sda_pin, sht_scl_pin); + sht_type = 0; + } +} + +void ShtEverySecond(void) +{ + if (!(uptime %4)) { + + if (!ShtRead()) { + AddLogMissed(sht_types, sht_valid); + } + } +} + +void ShtShow(bool json) +{ + if (sht_valid) { + TempHumDewShow(json, (0 == tele_period), sht_types, sht_temperature, sht_humidity); + } +} + + + + + +bool Xsns07(uint8_t function) +{ + if (!I2cEnabled(XI2C_08)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + ShtDetect(); + } + else if (sht_type) { + switch (function) { + case FUNC_EVERY_SECOND: + ShtEverySecond(); + break; + case FUNC_JSON_APPEND: + ShtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ShtShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_08_htu21.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_08_htu21.ino" +#ifdef USE_I2C +#ifdef USE_HTU +# 30 "/workspace/Tasmota/tasmota/xsns_08_htu21.ino" +#define XSNS_08 8 +#define XI2C_09 9 + +#define HTU21_ADDR 0x40 + +#define SI7013_CHIPID 0x0D +#define SI7020_CHIPID 0x14 +#define SI7021_CHIPID 0x15 +#define HTU21_CHIPID 0x32 + +#define HTU21_READTEMP 0xE3 +#define HTU21_READHUM 0xE5 +#define HTU21_WRITEREG 0xE6 +#define HTU21_READREG 0xE7 +#define HTU21_RESET 0xFE +#define HTU21_HEATER_WRITE 0x51 +#define HTU21_HEATER_READ 0x11 +#define HTU21_SERIAL2_READ1 0xFC +#define HTU21_SERIAL2_READ2 0xC9 + +#define HTU21_HEATER_ON 0x04 +#define HTU21_HEATER_OFF 0xFB + +#define HTU21_RES_RH12_T14 0x00 +#define HTU21_RES_RH8_T12 0x01 +#define HTU21_RES_RH10_T13 0x80 +#define HTU21_RES_RH11_T11 0x81 + +#define HTU21_CRC8_POLYNOM 0x13100 + +const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; + +struct { + float temperature = 0; + float humidity = 0; + uint8_t address; + uint8_t type = 0; + uint8_t delay_temp; + uint8_t delay_humidity = 50; + uint8_t valid = 0; + char types[7]; +} Htu; + + + +uint8_t HtuCheckCrc8(uint16_t data) +{ + for (uint32_t bit = 0; bit < 16; bit++) { + if (data & 0x8000) { + data = (data << 1) ^ HTU21_CRC8_POLYNOM; + } else { + data <<= 1; + } + } + return data >>= 8; +} + +uint8_t HtuReadDeviceId(void) +{ + HtuReset(); + + uint16_t deviceID = 0; + uint8_t checksum = 0; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_SERIAL2_READ1); + Wire.write(HTU21_SERIAL2_READ2); + Wire.endTransmission(); + + Wire.requestFrom(HTU21_ADDR, 3); + deviceID = Wire.read() << 8; + deviceID |= Wire.read(); + checksum = Wire.read(); + if (HtuCheckCrc8(deviceID) == checksum) { + deviceID = deviceID >> 8; + } else { + deviceID = 0; + } + return (uint8_t)deviceID; +} + +void HtuSetResolution(uint8_t resolution) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + current &= 0x7E; + current |= resolution; + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuReset(void) +{ + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_RESET); + Wire.endTransmission(); + delay(15); +} + +void HtuHeater(uint8_t heater) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + + switch(heater) + { + case HTU21_HEATER_ON : current |= heater; + break; + case HTU21_HEATER_OFF : current &= heater; + break; + default : current &= heater; + break; + } + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuInit(void) +{ + HtuReset(); + HtuHeater(HTU21_HEATER_OFF); + HtuSetResolution(HTU21_RES_RH12_T14); +} + +bool HtuRead(void) +{ + uint8_t checksum = 0; + uint16_t sensorval = 0; + + if (Htu.valid) { Htu.valid--; } + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READTEMP); + if (Wire.endTransmission() != 0) { return false; } + delay(Htu.delay_temp); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 == Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + Htu.temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READHUM); + if (Wire.endTransmission() != 0) { return false; } + delay(Htu.delay_humidity); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 <= Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + sensorval ^= 0x02; + Htu.humidity = 0.001907 * (float)sensorval - 6; + if (Htu.humidity > 100) { Htu.humidity = 100.0; } + if (Htu.humidity < 0) { Htu.humidity = 0.01; } + + if ((0.00 == Htu.humidity) && (0.00 == Htu.temperature)) { + Htu.humidity = 0.0; + } + if ((Htu.temperature > 0.00) && (Htu.temperature < 80.00)) { + Htu.humidity = (-0.15) * (25 - Htu.temperature) + Htu.humidity; + } + Htu.humidity = ConvertHumidity(Htu.humidity); + + Htu.valid = SENSOR_MAX_MISS; + return true; +} + + + +void HtuDetect(void) +{ + Htu.address = HTU21_ADDR; + if (I2cActive(Htu.address)) { return; } + + Htu.type = HtuReadDeviceId(); + if (Htu.type) { + uint8_t index = 0; + HtuInit(); + switch (Htu.type) { + case HTU21_CHIPID: + Htu.delay_temp = 50; + Htu.delay_humidity = 16; + break; + case SI7021_CHIPID: + index++; + case SI7020_CHIPID: + index++; + case SI7013_CHIPID: + index++; + Htu.delay_temp = 12; + Htu.delay_humidity = 23; + break; + default: + index = 4; + Htu.delay_temp = 50; + Htu.delay_humidity = 23; + } + GetTextIndexed(Htu.types, sizeof(Htu.types), index, kHtuTypes); + I2cSetActiveFound(Htu.address, Htu.types); + } +} + +void HtuEverySecond(void) +{ + if (uptime &1) { + + if (!HtuRead()) { + AddLogMissed(Htu.types, Htu.valid); + } + } +} + +void HtuShow(bool json) +{ + if (Htu.valid) { + TempHumDewShow(json, (0 == tele_period), Htu.types, Htu.temperature, Htu.humidity); + } +} + + + + + +bool Xsns08(uint8_t function) +{ + if (!I2cEnabled(XI2C_09)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + HtuDetect(); + } + else if (Htu.type) { + switch (function) { + case FUNC_EVERY_SECOND: + HtuEverySecond(); + break; + case FUNC_JSON_APPEND: + HtuShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HtuShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_09_bmp.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_09_bmp.ino" +#ifdef USE_I2C +#ifdef USE_BMP +# 30 "/workspace/Tasmota/tasmota/xsns_09_bmp.ino" +#define XSNS_09 9 +#define XI2C_10 10 + +#define BMP_ADDR1 0x76 +#define BMP_ADDR2 0x77 + +#define BMP180_CHIPID 0x55 +#define BMP280_CHIPID 0x58 +#define BME280_CHIPID 0x60 +#define BME680_CHIPID 0x61 + +#define BMP_REGISTER_CHIPID 0xD0 + +#define BMP_REGISTER_RESET 0xE0 + +#define BMP_CMND_RESET 0xB6 + +#define BMP_MAX_SENSORS 2 + +const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; + +typedef struct { + uint8_t bmp_address; + char bmp_name[7]; + uint8_t bmp_type; + uint8_t bmp_model; +#ifdef USE_BME680 + uint8_t bme680_state; + float bmp_gas_resistance; +#endif + float bmp_temperature; + float bmp_pressure; + float bmp_humidity; +} bmp_sensors_t; + +uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; +uint8_t bmp_count = 0; +uint8_t bmp_once = 1; + +bmp_sensors_t *bmp_sensors = nullptr; + + + + + +#define BMP180_REG_CONTROL 0xF4 +#define BMP180_REG_RESULT 0xF6 +#define BMP180_TEMPERATURE 0x2E +#define BMP180_PRESSURE3 0xF4 + +#define BMP180_AC1 0xAA +#define BMP180_AC2 0xAC +#define BMP180_AC3 0xAE +#define BMP180_AC4 0xB0 +#define BMP180_AC5 0xB2 +#define BMP180_AC6 0xB4 +#define BMP180_VB1 0xB6 +#define BMP180_VB2 0xB8 +#define BMP180_MB 0xBA +#define BMP180_MC 0xBC +#define BMP180_MD 0xBE + +#define BMP180_OSS 3 + +typedef struct { + int16_t cal_ac1; + int16_t cal_ac2; + int16_t cal_ac3; + int16_t cal_b1; + int16_t cal_b2; + int16_t cal_mc; + int16_t cal_md; + uint16_t cal_ac4; + uint16_t cal_ac5; + uint16_t cal_ac6; +} bmp180_cal_data_t; + +bmp180_cal_data_t *bmp180_cal_data = nullptr; + +bool Bmp180Calibration(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { + bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); + } + if (!bmp180_cal_data) { return false; } + + bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); + bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); + bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); + bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4); + bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5); + bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6); + bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1); + bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2); + bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC); + bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD); + + + if (!bmp180_cal_data[bmp_idx].cal_ac1 | + !bmp180_cal_data[bmp_idx].cal_ac2 | + !bmp180_cal_data[bmp_idx].cal_ac3 | + !bmp180_cal_data[bmp_idx].cal_ac4 | + !bmp180_cal_data[bmp_idx].cal_ac5 | + !bmp180_cal_data[bmp_idx].cal_ac6 | + !bmp180_cal_data[bmp_idx].cal_b1 | + !bmp180_cal_data[bmp_idx].cal_b2 | + !bmp180_cal_data[bmp_idx].cal_mc | + !bmp180_cal_data[bmp_idx].cal_md) { + return false; + } + + if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) { + return false; + } + return true; +} + +void Bmp180Read(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { return; } + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); + delay(5); + int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15; + int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md); + int32_t bmp180_b5 = xt1 + xt2; + bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0; + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); + delay(2 + (4 << BMP180_OSS)); + uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + up >>= (8 - BMP180_OSS); + + int32_t b6 = bmp180_b5 - 4000; + int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11; + int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11; + int32_t x3 = x1 + x2; + int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2; + + x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13; + x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; + uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS); + + int32_t p; + if (b7 < 0x80000000) { + p = (b7 * 2) / b4; + } + else { + p = (b7 / b4) * 2; + } + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + p += ((x1 + x2 + (int32_t)3791) >> 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; +} + + + + + + + +#define BME280_REGISTER_CONTROLHUMID 0xF2 +#define BME280_REGISTER_CONTROL 0xF4 +#define BME280_REGISTER_CONFIG 0xF5 +#define BME280_REGISTER_PRESSUREDATA 0xF7 +#define BME280_REGISTER_TEMPDATA 0xFA +#define BME280_REGISTER_HUMIDDATA 0xFD + +#define BME280_REGISTER_DIG_T1 0x88 +#define BME280_REGISTER_DIG_T2 0x8A +#define BME280_REGISTER_DIG_T3 0x8C +#define BME280_REGISTER_DIG_P1 0x8E +#define BME280_REGISTER_DIG_P2 0x90 +#define BME280_REGISTER_DIG_P3 0x92 +#define BME280_REGISTER_DIG_P4 0x94 +#define BME280_REGISTER_DIG_P5 0x96 +#define BME280_REGISTER_DIG_P6 0x98 +#define BME280_REGISTER_DIG_P7 0x9A +#define BME280_REGISTER_DIG_P8 0x9C +#define BME280_REGISTER_DIG_P9 0x9E +#define BME280_REGISTER_DIG_H1 0xA1 +#define BME280_REGISTER_DIG_H2 0xE1 +#define BME280_REGISTER_DIG_H3 0xE3 +#define BME280_REGISTER_DIG_H4 0xE4 +#define BME280_REGISTER_DIG_H5 0xE5 +#define BME280_REGISTER_DIG_H6 0xE7 + +typedef struct { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + int16_t dig_H2; + int16_t dig_H4; + int16_t dig_H5; + uint8_t dig_H1; + uint8_t dig_H3; + int8_t dig_H6; +} Bme280CalibrationData_t; + +Bme280CalibrationData_t *Bme280CalibrationData = nullptr; + +bool Bmx280Calibrate(uint8_t bmp_idx) +{ + + + if (!Bme280CalibrationData) { + Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); + } + if (!Bme280CalibrationData) { return false; } + + Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); + Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); + Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); + Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1); + Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2); + Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3); + Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4); + Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5); + Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6); + Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7); + Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8); + Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9); + if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1); + Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2); + Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3); + Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF); + Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4); + Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); + } else { + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); + } + + return true; +} + +void Bme280Read(uint8_t bmp_idx) +{ + if (!Bme280CalibrationData) { return; } + + int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); + adc_T >>= 4; + + int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11; + int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14; + int32_t t_fine = vart1 + vart2; + float T = (t_fine * 5 + 128) >> 8; + bmp_sensors[bmp_idx].bmp_temperature = T / 100.0; + + int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA); + adc_P >>= 4; + + int64_t var1 = ((int64_t)t_fine) - 128000; + int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6; + var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17); + var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12); + var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33; + if (0 == var1) { + return; + } + int64_t p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0; + + if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; } + + int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA); + + int32_t v_x1_u32r = (t_fine - ((int32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) - + (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * + (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + + ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + float h = (v_x1_u32r >> 12); + bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0; +} + +#ifdef USE_BME680 + + + + +#include + +struct bme680_dev *gas_sensor = nullptr; + +static void BmeDelayMs(uint32_t ms) +{ + delay(ms); +} + +bool Bme680Init(uint8_t bmp_idx) +{ + if (!gas_sensor) { + gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); + } + if (!gas_sensor) { return false; } + + gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; + gas_sensor[bmp_idx].intf = BME680_I2C_INTF; + gas_sensor[bmp_idx].read = &I2cReadBuffer; + gas_sensor[bmp_idx].write = &I2cWriteBuffer; + gas_sensor[bmp_idx].delay_ms = BmeDelayMs; + + + + gas_sensor[bmp_idx].amb_temp = 25; + + int8_t rslt = BME680_OK; + rslt = bme680_init(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + + gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X; + gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X; + gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X; + gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3; + + + gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; + + gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; + gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; + + + + gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE; + + + uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; + + + rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + bmp_sensors[bmp_idx].bme680_state = 0; + + return true; +} + +void Bme680Read(uint8_t bmp_idx) +{ + if (!gas_sensor) { return; } + + int8_t rslt = BME680_OK; + + if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + if (0 == bmp_sensors[bmp_idx].bme680_state) { + + rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + + + + + + + bmp_sensors[bmp_idx].bme680_state = 1; + } else { + bmp_sensors[bmp_idx].bme680_state = 0; + + struct bme680_field_data data; + rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0; + bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0; + bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0; + + if (data.status & BME680_GASM_VALID_MSK) { + bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0; + } else { + bmp_sensors[bmp_idx].bmp_gas_resistance = 0; + } + } + } + return; +} + +#endif + + + +void BmpDetect(void) +{ + int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); + if (!bmp_sensors) { + bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); + } + if (!bmp_sensors) { return; } + memset(bmp_sensors, 0, bmp_sensor_size); + + for (uint32_t i = 0; i < BMP_MAX_SENSORS; i++) { + if (I2cActive(bmp_addresses[i])) { continue; } + uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); + if (bmp_type) { + bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; + bmp_sensors[bmp_count].bmp_type = bmp_type; + bmp_sensors[bmp_count].bmp_model = 0; + + bool success = false; + switch (bmp_type) { + case BMP180_CHIPID: + success = Bmp180Calibration(bmp_count); + break; + case BME280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + case BMP280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + success = Bmx280Calibrate(bmp_count); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + bmp_sensors[bmp_count].bmp_model = 3; + success = Bme680Init(bmp_count); + break; +#endif + } + if (success) { + GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); + I2cSetActiveFound(bmp_sensors[bmp_count].bmp_address, bmp_sensors[bmp_count].bmp_name); + bmp_count++; + } + } + } +} + +void BmpRead(void) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + Bmp180Read(bmp_idx); + break; + case BMP280_CHIPID: + case BME280_CHIPID: + Bme280Read(bmp_idx); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + Bme680Read(bmp_idx); + break; +#endif + } + } +} + +void BmpShow(bool json) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + if (bmp_sensors[bmp_idx].bmp_type) { + float bmp_sealevel = 0.0; + if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { + bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; + bmp_sealevel = ConvertPressure(bmp_sealevel); + } + float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); + float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); + + char name[10]; + strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); + if (bmp_count > 1) { + snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); + } + + char temperature[33]; + dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); + char pressure[33]; + dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); + char sea_pressure[33]; + dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); + + float bmp_humidity = ConvertHumidity(bmp_sensors[bmp_idx].bmp_humidity); + char humidity[33]; + dtostrfd(bmp_humidity, Settings.flag2.humidity_resolution, humidity); + float f_dewpoint = CalcTempHumToDew(bmp_temperature, bmp_humidity); + char dewpoint[33]; + dtostrfd(f_dewpoint, Settings.flag2.temperature_resolution, dewpoint); +#ifdef USE_BME680 + char gas_resistance[33]; + dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); +#endif + + if (json) { + char json_humidity[80]; + snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), humidity, dewpoint); + char json_sealevel[40]; + snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); +#ifdef USE_BME680 + char json_gas[40]; + snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); + + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), + name, + temperature, + (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", + pressure, + (Settings.altitude != 0) ? json_sealevel : "", + (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), + name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); +#endif + +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == bmp_idx)) { + DomoticzTempHumPressureSensor(bmp_temperature, bmp_humidity, bmp_pressure); +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); } +#endif + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, bmp_temperature); + KnxSensor(KNX_HUMIDITY, bmp_humidity); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); + if (bmp_sensors[bmp_idx].bmp_model >= 2) { + WSContentSend_PD(HTTP_SNS_HUM, name, humidity); + WSContentSend_PD(HTTP_SNS_DEW, name, dewpoint, TempUnit()); + } + WSContentSend_PD(HTTP_SNS_PRESSURE, name, pressure, PressureUnit().c_str()); + if (Settings.altitude != 0) { + WSContentSend_PD(HTTP_SNS_SEAPRESSURE, name, sea_pressure, PressureUnit().c_str()); + } +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { + WSContentSend_PD(PSTR("{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), name, gas_resistance); + } +#endif + +#endif + } + } + } +} + +#ifdef USE_DEEPSLEEP + +void BMP_EnterSleep(void) +{ + if (DeepSleepEnabled()) { + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + case BMP280_CHIPID: + case BME280_CHIPID: + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET); + break; + default: + break; + } + } + } +} + +#endif + + + + + +bool Xsns09(uint8_t function) +{ + if (!I2cEnabled(XI2C_10)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + BmpDetect(); + } + else if (bmp_count) { + switch (function) { + case FUNC_EVERY_SECOND: + BmpRead(); + break; + case FUNC_JSON_APPEND: + BmpShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + BmpShow(0); + break; +#endif +#ifdef USE_DEEPSLEEP + case FUNC_SAVE_BEFORE_RESTART: + BMP_EnterSleep(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_10_bh1750.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_10_bh1750.ino" +#ifdef USE_I2C +#ifdef USE_BH1750 +# 33 "/workspace/Tasmota/tasmota/xsns_10_bh1750.ino" +#define XSNS_10 10 +#define XI2C_11 11 + +#define BH1750_ADDR1 0x23 +#define BH1750_ADDR2 0x5C + +#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 +#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 + +#define BH1750_MEASUREMENT_TIME_HIGH 0x40 +#define BH1750_MEASUREMENT_TIME_LOW 0x60 + +#define D_PRFX_BH1750 "Bh1750" +#define D_CMND_RESOLUTION "Resolution" +#define D_CMND_MTREG "MTime" + +const char kBh1750Commands[] PROGMEM = D_PRFX_BH1750 "|" + D_CMND_RESOLUTION "|" D_CMND_MTREG ; + +void (* const Bh1750Command[])(void) PROGMEM = { + &CmndBh1750Resolution, &CmndBh1750MTime }; + +struct { + uint8_t addresses[2] = { BH1750_ADDR1, BH1750_ADDR2 }; + uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE }; + uint8_t count = 0; + char types[7] = "BH1750"; +} Bh1750; + +struct { + uint8_t address; + uint8_t valid = 0; + uint8_t mtreg = 69; + uint16_t illuminance = 0; +} Bh1750_sensors[2]; + + + +uint8_t Bh1750Resolution(uint32_t sensor_index) { + uint8_t settings_resolution = Settings.SensorBits1.bh1750_1_resolution; + if (1 == sensor_index) { + settings_resolution = Settings.SensorBits1.bh1750_2_resolution; + } + return settings_resolution; +} + +bool Bh1750SetResolution(uint32_t sensor_index) { + Wire.beginTransmission(Bh1750_sensors[sensor_index].address); + Wire.write(Bh1750.resolution[Bh1750Resolution(sensor_index)]); + return (!Wire.endTransmission()); +} + +bool Bh1750SetMTreg(uint32_t sensor_index) { + Wire.beginTransmission(Bh1750_sensors[sensor_index].address); + uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750_sensors[sensor_index].mtreg >> 5) & 0x07); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + Wire.beginTransmission(Bh1750_sensors[sensor_index].address); + data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750_sensors[sensor_index].mtreg & 0x1F); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + return Bh1750SetResolution(sensor_index); +} + +bool Bh1750Read(uint32_t sensor_index) { + if (Bh1750_sensors[sensor_index].valid) { Bh1750_sensors[sensor_index].valid--; } + + if (2 != Wire.requestFrom(Bh1750_sensors[sensor_index].address, (uint8_t)2)) { return false; } + + float illuminance = (Wire.read() << 8) | Wire.read(); + illuminance /= (1.2 * (69 / (float)Bh1750_sensors[sensor_index].mtreg)); + if (1 == Bh1750Resolution(sensor_index)) { + illuminance /= 2; + } + Bh1750_sensors[sensor_index].illuminance = illuminance; + + Bh1750_sensors[sensor_index].valid = SENSOR_MAX_MISS; + return true; +} + + + +void Bh1750Detect(void) { + for (uint32_t i = 0; i < sizeof(Bh1750.addresses); i++) { + if (I2cActive(Bh1750.addresses[i])) { continue; } + + Bh1750_sensors[Bh1750.count].address = Bh1750.addresses[i]; + if (Bh1750SetMTreg(Bh1750.count)) { + I2cSetActiveFound(Bh1750_sensors[Bh1750.count].address, Bh1750.types); + Bh1750.count++; + } + } +} + +void Bh1750EverySecond(void) { + for (uint32_t i = 0; i < Bh1750.count; i++) { + + if (!Bh1750Read(i)) { + + } + } +} + + + + + +void CmndBh1750Resolution(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Bh1750.count)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + if (1 == XdrvMailbox.index) { + Settings.SensorBits1.bh1750_1_resolution = XdrvMailbox.payload; + } else { + Settings.SensorBits1.bh1750_2_resolution = XdrvMailbox.payload; + } + Bh1750SetResolution(XdrvMailbox.index -1); + } + ResponseCmndIdxNumber(Bh1750Resolution(XdrvMailbox.index -1)); + } +} + +void CmndBh1750MTime(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Bh1750.count)) { + if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) { + Bh1750_sensors[XdrvMailbox.index -1].mtreg = XdrvMailbox.payload; + Bh1750SetMTreg(XdrvMailbox.index -1); + } + ResponseCmndIdxNumber(Bh1750_sensors[XdrvMailbox.index -1].mtreg); + } +} + + + +void Bh1750Show(bool json) { + for (uint32_t sensor_index = 0; sensor_index < Bh1750.count; sensor_index++) { + if (Bh1750_sensors[sensor_index].valid) { + char sensor_name[10]; + strlcpy(sensor_name, Bh1750.types, sizeof(sensor_name)); + if (Bh1750.count > 1) { + snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), Bh1750_sensors[sensor_index].address); + } + + if (json) { + ResponseAppend_P(JSON_SNS_ILLUMINANCE, sensor_name, Bh1750_sensors[sensor_index].illuminance); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == sensor_index)) { + DomoticzSensor(DZ_ILLUMINANCE, Bh1750_sensors[sensor_index].illuminance); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor_name, Bh1750_sensors[sensor_index].illuminance); +#endif + } + } + } +} + + + + + +bool Xsns10(uint8_t function) { + if (!I2cEnabled(XI2C_11)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Bh1750Detect(); + } + else if (Bh1750.count) { + switch (function) { + case FUNC_EVERY_SECOND: + Bh1750EverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kBh1750Commands, Bh1750Command); + break; + case FUNC_JSON_APPEND: + Bh1750Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Bh1750Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_11_veml6070.ino" +# 89 "/workspace/Tasmota/tasmota/xsns_11_veml6070.ino" +#ifdef USE_I2C +#ifdef USE_VEML6070 + + + + + + +#define XSNS_11 11 +#define XI2C_12 12 + +#define VEML6070_ADDR_H 0x39 +#define VEML6070_ADDR_L 0x38 +#define VEML6070_INTEGRATION_TIME 3 +#define VEML6070_ENABLE 1 +#define VEML6070_DISABLE 0 +#define VEML6070_RSET_DEFAULT 270000 +#define VEML6070_UV_MAX_INDEX 15 +#define VEML6070_UV_MAX_DEFAULT 11 +#define VEML6070_POWER_COEFFCIENT 0.025 +#define VEML6070_TABLE_COEFFCIENT 32.86270591 + + + + + +const char kVemlTypes[] PROGMEM = "VEML6070"; +double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +double uvrisk = 0; +double uvpower = 0; +uint16_t uvlevel = 0; +uint8_t veml6070_addr_low = VEML6070_ADDR_L; +uint8_t veml6070_addr_high = VEML6070_ADDR_H; +uint8_t itime = VEML6070_INTEGRATION_TIME; +uint8_t veml6070_type = 0; +char veml6070_name[9]; +char str_uvrisk_text[10]; + + + +void Veml6070Detect(void) +{ + if (I2cActive(VEML6070_ADDR_L)) { return; } + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((itime << 2) | 0x02); + uint8_t status = Wire.endTransmission(); + + if (!status) { + veml6070_type = 1; + Veml6070UvTableInit(); + uint8_t veml_model = 0; + GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); + I2cSetActiveFound(VEML6070_ADDR_L, veml6070_name); + } +} + + + +void Veml6070UvTableInit(void) +{ + + for (uint32_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { +#ifdef USE_VEML6070_RSET + if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) { + uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + } else { + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); + } +#else + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); +#endif + } +} + + + +void Veml6070EverySecond(void) +{ + + Veml6070ModeCmd(1); + uvlevel = Veml6070ReadUv(); + uvrisk = Veml6070UvRiskLevel(uvlevel); + uvpower = Veml6070UvPower(uvrisk); + Veml6070ModeCmd(0); +} + + + +void Veml6070ModeCmd(bool mode_cmd) +{ + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((mode_cmd << 0) | 0x02 | (itime << 2)); + uint8_t status = Wire.endTransmission(); + + if (!status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); + } +} + + + +uint16_t Veml6070ReadUv(void) +{ + uint16_t uv_raw = 0; + + if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) { + return -1; + } + uv_raw = Wire.read(); + uv_raw <<= 8; + + if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) { + return -1; + } + uv_raw |= Wire.read(); + + return uv_raw; +} + + + +double Veml6070UvRiskLevel(uint16_t uv_level) +{ + double risk = 0; + if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { + risk = (double)uv_level / uv_risk_map[0]; + + if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); } + else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); } + else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); } + else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); } + else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); } + else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); } + else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); } + return risk; + } else { + + snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); + return ( risk = 99 ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); + } +} + + + +double Veml6070UvPower(double uvrisk) +{ + + double power = 0; + return ( power = VEML6070_POWER_COEFFCIENT * uvrisk ); +} + + + + +#ifdef USE_WEBSERVER + +#ifdef USE_VEML6070_SHOW_RAW + const char HTTP_SNS_UV_LEVEL[] PROGMEM = "{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}"; +#endif + + const char HTTP_SNS_UV_INDEX[] PROGMEM = "{s}VEML6070 " D_UV_INDEX "{m}%s %s{e}"; + const char HTTP_SNS_UV_POWER[] PROGMEM = "{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}"; +#endif + + + +void Veml6070Show(bool json) +{ + + char str_uvlevel[33]; + dtostrfd((double)uvlevel, 0, str_uvlevel); + char str_uvrisk[33]; + dtostrfd(uvrisk, 2, str_uvrisk); + char str_uvpower[33]; + dtostrfd(uvpower, 3, str_uvpower); + if (json) { +#ifdef USE_VEML6070_SHOW_RAW + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); } +#endif +#ifdef USE_WEBSERVER + } else { +#ifdef USE_VEML6070_SHOW_RAW + WSContentSend_PD(HTTP_SNS_UV_LEVEL, str_uvlevel); +#endif + WSContentSend_PD(HTTP_SNS_UV_INDEX, str_uvrisk, str_uvrisk_text); + WSContentSend_PD(HTTP_SNS_UV_POWER, str_uvpower); +#endif + } +} + + + + + +bool Xsns11(uint8_t function) +{ + if (!I2cEnabled(XI2C_12)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Veml6070Detect(); + } + else if (veml6070_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Veml6070EverySecond(); + break; + case FUNC_JSON_APPEND: + Veml6070Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Veml6070Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_12_ads1115.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_12_ads1115.ino" +#ifdef USE_I2C +#ifdef USE_ADS1115 +# 43 "/workspace/Tasmota/tasmota/xsns_12_ads1115.ino" +#define XSNS_12 12 +#define XI2C_13 13 + +#define ADS1115_ADDRESS_ADDR_GND 0x48 +#define ADS1115_ADDRESS_ADDR_VDD 0x49 +#define ADS1115_ADDRESS_ADDR_SDA 0x4A +#define ADS1115_ADDRESS_ADDR_SCL 0x4B + +#define ADS1115_CONVERSIONDELAY (8) + + + + +#define ADS1115_REG_POINTER_MASK (0x03) +#define ADS1115_REG_POINTER_CONVERT (0x00) +#define ADS1115_REG_POINTER_CONFIG (0x01) +#define ADS1115_REG_POINTER_LOWTHRESH (0x02) +#define ADS1115_REG_POINTER_HITHRESH (0x03) + + + + +#define ADS1115_REG_CONFIG_OS_MASK (0x8000) +#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) +#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) +#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) + +#define ADS1115_REG_CONFIG_MUX_MASK (0x7000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) +#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) +#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) + +#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00) +#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) +#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) +#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) +#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) +#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) +#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) + +#define ADS1115_REG_CONFIG_MODE_MASK (0x0100) +#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) +#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) + +#define ADS1115_REG_CONFIG_DR_MASK (0x00E0) +#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) +#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) +#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) +#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) +#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) +#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) +#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) +#define ADS1115_REG_CONFIG_DR_6000SPS (0x00E0) + +#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010) +#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) +#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) + +#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008) +#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) +#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) + +#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) +#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) +#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) + +#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003) +#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) +#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) +#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) +#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) + +struct ADS1115 { + uint8_t count = 0; + uint8_t address; + uint8_t addresses[4] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; + uint8_t found[4] = {false,false,false,false}; + int16_t last_values[4][4] = {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}; +} Ads1115; + + + +void Ads1115StartComparator(uint8_t channel, uint16_t mode) +{ + + uint16_t config = mode | + ADS1115_REG_CONFIG_CQUE_NONE | + ADS1115_REG_CONFIG_CLAT_NONLAT | + ADS1115_REG_CONFIG_PGA_6_144V | + ADS1115_REG_CONFIG_CPOL_ACTVLOW | + ADS1115_REG_CONFIG_CMODE_TRAD | + ADS1115_REG_CONFIG_DR_6000SPS; + + + config |= (ADS1115_REG_CONFIG_MUX_SINGLE_0 + (0x1000 * channel)); + + + I2cWrite16(Ads1115.address, ADS1115_REG_POINTER_CONFIG, config); +} + +int16_t Ads1115GetConversion(uint8_t channel) +{ + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); + + delay(ADS1115_CONVERSIONDELAY); + + I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); + delay(ADS1115_CONVERSIONDELAY); + + uint16_t res = I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + return (int16_t)res; +} + + + +void Ads1115Detect(void) +{ + for (uint32_t i = 0; i < sizeof(Ads1115.addresses); i++) { + if (!Ads1115.found[i]) { + Ads1115.address = Ads1115.addresses[i]; + if (I2cActive(Ads1115.address)) { continue; } + uint16_t buffer; + if (I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONVERT) && + I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONFIG)) { + Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); + I2cSetActiveFound(Ads1115.address, "ADS1115"); + Ads1115.found[i] = 1; + Ads1115.count++; + } + } + } +} + + +void Ads1115Label(char* label, uint32_t maxsize, uint8_t address) { + if (1 == Ads1115.count) { + + snprintf_P(label, maxsize, PSTR("ADS1115")); + } else { + + snprintf_P(label, maxsize, PSTR("ADS1115%c%02x"), IndexSeparator(), address); + } +} + +#ifdef USE_RULES + + +void AdsEvery250ms(void) +{ + int16_t value; + + for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { + if (Ads1115.found[t]) { + + uint8_t old_address = Ads1115.address; + Ads1115.address = Ads1115.addresses[t]; + + + uint32_t changed = 0; + for (uint32_t i = 0; i < 4; i++) { + value = Ads1115GetConversion(i); + + + + if (value >= Ads1115.last_values[t][i] + 327 || value <= Ads1115.last_values[t][i] - 327) { + Ads1115.last_values[t][i] = value; + bitSet(changed, i); + } + } + Ads1115.address = old_address; + if (changed) { + char label[15]; + Ads1115Label(label, sizeof(label), Ads1115.addresses[t]); + + Response_P(PSTR("{\"%s\":{"), label); + + bool first = true; + for (uint32_t i = 0; i < 4; i++) { + if (bitRead(changed, i)) { + ResponseAppend_P(PSTR("%s\"A%ddiv10\":%d"), (first) ? "" : ",", i, Ads1115.last_values[t][i]); + first = false; + } + } + ResponseJsonEndEnd(); + + XdrvRulesProcess(); + } + + } + } +} +#endif + +void Ads1115Show(bool json) +{ + int16_t values[4]; + + for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { + + if (Ads1115.found[t]) { + + uint8_t old_address = Ads1115.address; + Ads1115.address = Ads1115.addresses[t]; + for (uint32_t i = 0; i < 4; i++) { + values[i] = Ads1115GetConversion(i); + + } + Ads1115.address = old_address; + + char label[15]; + Ads1115Label(label, sizeof(label), Ads1115.addresses[t]); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), label); + for (uint32_t i = 0; i < 4; i++) { + ResponseAppend_P(PSTR("%s\"A%d\":%d"), (0 == i) ? "" : ",", i, values[i]); + } + ResponseJsonEnd(); + } +#ifdef USE_WEBSERVER + else { + for (uint32_t i = 0; i < 4; i++) { + WSContentSend_PD(HTTP_SNS_ANALOG, label, i, values[i]); + } + } +#endif + } + } +} + + + + + +bool Xsns12(uint8_t function) +{ + if (!I2cEnabled(XI2C_13)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ads1115Detect(); + } + else if (Ads1115.count) { + switch (function) { +#ifdef USE_RULES + case FUNC_EVERY_250_MSECOND: + AdsEvery250ms(); + break; +#endif + case FUNC_JSON_APPEND: + Ads1115Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ads1115Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_13_ina219.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_13_ina219.ino" +#ifdef USE_I2C +#ifdef USE_INA219 +# 30 "/workspace/Tasmota/tasmota/xsns_13_ina219.ino" +#define XSNS_13 13 +#define XI2C_14 14 + +#define INA219_ADDRESS1 (0x40) +#define INA219_ADDRESS2 (0x41) +#define INA219_ADDRESS3 (0x44) +#define INA219_ADDRESS4 (0x45) + +#define INA219_READ (0x01) +#define INA219_REG_CONFIG (0x00) + +#define INA219_CONFIG_RESET (0x8000) + +#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) +#define INA219_CONFIG_BVOLTAGERANGE_16V (0x0000) +#define INA219_CONFIG_BVOLTAGERANGE_32V (0x2000) + +#define INA219_CONFIG_GAIN_MASK (0x1800) +#define INA219_CONFIG_GAIN_1_40MV (0x0000) +#define INA219_CONFIG_GAIN_2_80MV (0x0800) +#define INA219_CONFIG_GAIN_4_160MV (0x1000) +#define INA219_CONFIG_GAIN_8_320MV (0x1800) + +#define INA219_CONFIG_BADCRES_MASK (0x0780) +#define INA219_CONFIG_BADCRES_9BIT_1S_84US (0x0<<7) +#define INA219_CONFIG_BADCRES_10BIT_1S_148US (0x1<<7) +#define INA219_CONFIG_BADCRES_11BIT_1S_276US (0x2<<7) +#define INA219_CONFIG_BADCRES_12BIT_1S_532US (0x3<<7) +#define INA219_CONFIG_BADCRES_12BIT_2S_1060US (0x9<<7) +#define INA219_CONFIG_BADCRES_12BIT_4S_2130US (0xA<<7) +#define INA219_CONFIG_BADCRES_12BIT_8S_4260US (0xB<<7) +#define INA219_CONFIG_BADCRES_12BIT_16S_8510US (0xC<<7) +#define INA219_CONFIG_BADCRES_12BIT_32S_17MS (0xD<<7) +#define INA219_CONFIG_BADCRES_12BIT_64S_34MS (0xE<<7) +#define INA219_CONFIG_BADCRES_12BIT_128S_69MS (0xF<<7) + +#define INA219_CONFIG_SADCRES_MASK (0x0078) +#define INA219_CONFIG_SADCRES_9BIT_1S_84US (0x0<<3) +#define INA219_CONFIG_SADCRES_10BIT_1S_148US (0x1<<3) +#define INA219_CONFIG_SADCRES_11BIT_1S_276US (0x2<<3) +#define INA219_CONFIG_SADCRES_12BIT_1S_532US (0x3<<3) +#define INA219_CONFIG_SADCRES_12BIT_2S_1060US (0x9<<3) +#define INA219_CONFIG_SADCRES_12BIT_4S_2130US (0xA<<3) +#define INA219_CONFIG_SADCRES_12BIT_8S_4260US (0xB<<3) +#define INA219_CONFIG_SADCRES_12BIT_16S_8510US (0xC<<3) +#define INA219_CONFIG_SADCRES_12BIT_32S_17MS (0xD<<3) +#define INA219_CONFIG_SADCRES_12BIT_64S_34MS (0xE<<3) +#define INA219_CONFIG_SADCRES_12BIT_128S_69MS (0xF<<3) + +#define INA219_CONFIG_MODE_MASK (0x0007) +#define INA219_CONFIG_MODE_POWERDOWN (0x0000) +#define INA219_CONFIG_MODE_SVOLT_TRIGGERED (0x0001) +#define INA219_CONFIG_MODE_BVOLT_TRIGGERED (0x0002) +#define INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED (0x0003) +#define INA219_CONFIG_MODE_ADCOFF (0x0004) +#define INA219_CONFIG_MODE_SVOLT_CONTINUOUS (0x0005) +#define INA219_CONFIG_MODE_BVOLT_CONTINUOUS (0x0006) +#define INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS (0x0007) + +#define INA219_REG_SHUNTVOLTAGE (0x01) +#define INA219_REG_BUSVOLTAGE (0x02) +#define INA219_REG_POWER (0x03) +#define INA219_REG_CURRENT (0x04) +#define INA219_REG_CALIBRATION (0x05) + +#define INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS (100.0) + +uint8_t ina219_type[4] = {0,0,0,0}; +uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; + +#ifdef DEBUG_TASMOTA_SENSOR + +char __ina219_dbg1[10]; +char __ina219_dbg2[10]; +#endif + + + + +float ina219_current_multiplier; + +uint8_t ina219_valid[4] = {0,0,0,0}; +float ina219_voltage[4] = {0,0,0,0}; +float ina219_current[4] = {0,0,0,0}; +char ina219_types[] = "INA219"; +uint8_t ina219_count = 0; +# 132 "/workspace/Tasmota/tasmota/xsns_13_ina219.ino" +bool Ina219SetCalibration(uint8_t mode, uint16_t addr) +{ + uint16_t config = 0; + + DEBUG_SENSOR_LOG("Ina219SetCalibration: mode=%d",mode); + if (mode < 5) + { + + ina219_current_multiplier = 1.0 / INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS; + #ifdef DEBUG_TASMOTA_SENSOR + dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); + DEBUG_SENSOR_LOG("Ina219SetCalibration: cur_mul=%s",__ina219_dbg1); + #endif + } + else if (mode >= 10) + { + int mult = mode % 10; + int shunt_milliOhms = mode / 10; + for ( ; mult > 0 ; mult-- ) + shunt_milliOhms *= 10; + ina219_current_multiplier = 1.0 / shunt_milliOhms; + #ifdef DEBUG_TASMOTA_SENSOR + dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); + DEBUG_SENSOR_LOG("Ina219SetCalibration: shunt=%dmO => cur_mul=%s",shunt_milliOhms,__ina219_dbg1); + #endif + } + config = INA219_CONFIG_BVOLTAGERANGE_32V + | INA219_CONFIG_GAIN_8_320MV + | INA219_CONFIG_BADCRES_12BIT_16S_8510US + | INA219_CONFIG_SADCRES_12BIT_16S_8510US + | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + + return I2cWrite16(addr, INA219_REG_CONFIG, config); +} + +float Ina219GetShuntVoltage_mV(uint16_t addr) +{ + + int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); + DEBUG_SENSOR_LOG("Ina219GetShuntVoltage_mV: ShReg = 0x%04X",value); + + return value * 0.01; +} + +float Ina219GetBusVoltage_V(uint16_t addr) +{ + + uint16_t value = I2cRead16(addr, INA219_REG_BUSVOLTAGE) >> 3; + DEBUG_SENSOR_LOG("Ina219GetBusVoltage_V: BusReg = 0x%04X",value); + + return value * 0.004; +} +# 201 "/workspace/Tasmota/tasmota/xsns_13_ina219.ino" +bool Ina219Read(void) +{ + for (int i=0; i= 0) && (XdrvMailbox.payload <= 255)) { + Settings.ina219_mode = XdrvMailbox.payload; + restart_flag = 2; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_13, Settings.ina219_mode); + + return true; +} + + + +void Ina219Detect(void) +{ + for (uint32_t i = 0; i < sizeof(ina219_type); i++) { + uint16_t addr = ina219_addresses[i]; + if (I2cActive(addr)) { continue; } + if (Ina219SetCalibration(Settings.ina219_mode, addr)) { + I2cSetActiveFound(addr, ina219_types); + ina219_type[i] = 1; + ina219_count++; + } + } +} + +void Ina219EverySecond(void) +{ + + Ina219Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA219_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina219Show(bool json) +{ + int num_found=0; + for (int i=0; i1) + snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); + else + snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, ina219_addresses[i], voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); +#endif + } + } +} + + + + + +bool Xsns13(uint8_t function) +{ + if (!I2cEnabled(XI2C_14)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ina219Detect(); + } + else if (ina219_count) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_13 == XdrvMailbox.index) { + result = Ina219CommandSensor(); + } + break; + case FUNC_EVERY_SECOND: + Ina219EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina219Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina219Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_14_sht3x.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_14_sht3x.ino" +#ifdef USE_I2C +#ifdef USE_SHT3X + + + + + + +#define XSNS_14 14 +#define XI2C_15 15 + +#define SHT3X_ADDR_GND 0x44 +#define SHT3X_ADDR_VDD 0x45 +#define SHTC3_ADDR 0x70 + +#define SHT3X_MAX_SENSORS 3 + +const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3"; +uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR }; + +uint8_t sht3x_count = 0; +struct SHT3XSTRUCT { + uint8_t address; + char types[6]; +} sht3x_sensors[SHT3X_MAX_SENSORS]; + +bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) +{ + unsigned int data[6]; + + t = NAN; + h = NAN; + + Wire.beginTransmission(sht3x_address); + if (SHTC3_ADDR == sht3x_address) { + Wire.write(0x35); + Wire.write(0x17); + Wire.endTransmission(); + Wire.beginTransmission(sht3x_address); + Wire.write(0x78); + Wire.write(0x66); + } else { + Wire.write(0x2C); + Wire.write(0x06); + } + if (Wire.endTransmission() != 0) { + return false; + } + delay(30); + Wire.requestFrom(sht3x_address, (uint8_t)6); + for (uint32_t i = 0; i < 6; i++) { + data[i] = Wire.read(); + }; + t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); + h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); + return (!isnan(t) && !isnan(h) && (h != 0)); +} + + + +void Sht3xDetect(void) +{ + for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) { + if (I2cActive(sht3x_addresses[i])) { continue; } + float t; + float h; + if (Sht3xRead(t, h, sht3x_addresses[i])) { + sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; + GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); + I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types); + sht3x_count++; + } + } +} + +void Sht3xShow(bool json) +{ + for (uint32_t i = 0; i < sht3x_count; i++) { + float t; + float h; + if (Sht3xRead(t, h, sht3x_sensors[i].address)) { + char types[11]; + snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, t, h); + } + } +} + + + + + +bool Xsns14(uint8_t function) +{ + if (!I2cEnabled(XI2C_15)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Sht3xDetect(); + } + else if (sht3x_count) { + switch (function) { + case FUNC_JSON_APPEND: + Sht3xShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sht3xShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" +#ifdef USE_MHZ19 +# 33 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" +#define XSNS_15 15 + +enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; + +#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST +# 58 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define MHZ19_READ_TIMEOUT 400 +#define MHZ19_RETRY_COUNT 8 + +TasmotaSerial *MhzSerial; + +const char kMhzModels[] PROGMEM = "|B"; + +const char ABC_ENABLED[] = "ABC is Enabled"; +const char ABC_DISABLED[] = "ABC is Disabled"; + +enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; +const uint8_t kMhzCommands[][4] PROGMEM = { + + {0x86,0x00,0x00,0x00}, + {0x79,0xA0,0x00,0x00}, + {0x79,0x00,0x00,0x00}, + {0x87,0x00,0x00,0x00}, + {0x8D,0x00,0x00,0x00}, + {0x99,0x00,0x03,0xE8}, + {0x99,0x00,0x07,0xD0}, + {0x99,0x00,0x0B,0xB8}, + {0x99,0x00,0x13,0x88}}; + +uint8_t mhz_type = 1; +uint16_t mhz_last_ppm = 0; +uint8_t mhz_filter = MHZ19_FILTER_OPTION; +bool mhz_abc_must_apply = false; + +float mhz_temperature = 0; +uint8_t mhz_retry = MHZ19_RETRY_COUNT; +uint8_t mhz_received = 0; +uint8_t mhz_state = 0; + + + +uint8_t MhzCalculateChecksum(uint8_t *array) +{ + uint8_t checksum = 0; + for (uint32_t i = 1; i < 8; i++) { + checksum += array[i]; + } + checksum = 255 - checksum; + return (checksum +1); +} + +size_t MhzSendCmd(uint8_t command_id) +{ + uint8_t mhz_send[9] = { 0 }; + + mhz_send[0] = 0xFF; + mhz_send[1] = 0x01; + memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(uint16_t)); + + + + + memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); + mhz_send[8] = MhzCalculateChecksum(mhz_send); + + + + return MhzSerial->write(mhz_send, sizeof(mhz_send)); +} + + + +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) +{ + if (1 == s) { + return false; + } + if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { + + + mhz_last_ppm = ppm; + return true; + } + int32_t difference = ppm - mhz_last_ppm; + if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) { +# 154 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" + difference *= s; + difference /= 64; + } + if (MHZ19_FILTER_OFF == mhz_filter) { + if (s != 0 && s != 64) { + return false; + } + } else { + difference >>= (mhz_filter -1); + } + mhz_last_ppm = static_cast(mhz_last_ppm + difference); + return true; +} + +void MhzEverySecond(void) +{ + mhz_state++; + if (8 == mhz_state) { + mhz_state = 0; + + if (mhz_retry) { + mhz_retry--; + if (!mhz_retry) { + mhz_last_ppm = 0; + mhz_temperature = 0; + } + } + + MhzSerial->flush(); + MhzSendCmd(MHZ_CMND_READPPM); + mhz_received = 0; + } + + if ((mhz_state > 2) && !mhz_received) { + uint8_t mhz_response[9]; + + unsigned long start = millis(); + uint8_t counter = 0; + while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { + if (MhzSerial->available() > 0) { + mhz_response[counter++] = MhzSerial->read(); + } else { + delay(5); + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); + + if (counter < 9) { + + return; + } + + uint8_t crc = MhzCalculateChecksum(mhz_response); + if (mhz_response[8] != crc) { + + return; + } + if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { + + return; + } + + mhz_received = 1; + + uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; + if (15000 == u) { + if (Settings.SensorBits1.mhz19b_abc_disable) { + + + mhz_abc_must_apply = true; + } + } else { + uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; + mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); + uint8_t s = mhz_response[5]; + mhz_type = (s) ? 1 : 2; + if (MhzCheckAndApplyFilter(ppm, s)) { + mhz_retry = MHZ19_RETRY_COUNT; +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); +#endif + + if (0 == s || 64 == s) { + if (mhz_abc_must_apply) { + mhz_abc_must_apply = false; + if (!Settings.SensorBits1.mhz19b_abc_disable) { + MhzSendCmd(MHZ_CMND_ABCENABLE); + } else { + MhzSendCmd(MHZ_CMND_ABCDISABLE); + } + } + } + + } + } + + } +} +# 268 "/workspace/Tasmota/tasmota/xsns_15_mhz19.ino" +#define D_JSON_RANGE_1000 "1000 ppm range" +#define D_JSON_RANGE_2000 "2000 ppm range" +#define D_JSON_RANGE_3000 "3000 ppm range" +#define D_JSON_RANGE_5000 "5000 ppm range" + +bool MhzCommandSensor(void) +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + Settings.SensorBits1.mhz19b_abc_disable = true; + MhzSendCmd(MHZ_CMND_ABCDISABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + break; + case 1: + Settings.SensorBits1.mhz19b_abc_disable = false; + MhzSendCmd(MHZ_CMND_ABCENABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + break; + case 2: + MhzSendCmd(MHZ_CMND_ZEROPOINT); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); + break; + case 9: + MhzSendCmd(MHZ_CMND_RESET); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET); + break; + case 1000: + MhzSendCmd(MHZ_CMND_RANGE_1000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_1000); + break; + case 2000: + MhzSendCmd(MHZ_CMND_RANGE_2000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_2000); + break; + case 3000: + MhzSendCmd(MHZ_CMND_RANGE_3000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_3000); + break; + case 5000: + MhzSendCmd(MHZ_CMND_RANGE_5000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); + break; + default: + if (!Settings.SensorBits1.mhz19b_abc_disable) { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + } else { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + } + } + + return serviced; +} + + + +void MhzInit(void) +{ + mhz_type = 0; + if (PinUsed(GPIO_MHZ_RXD) && PinUsed(GPIO_MHZ_TXD)) { + MhzSerial = new TasmotaSerial(Pin(GPIO_MHZ_RXD), Pin(GPIO_MHZ_TXD), 1); + if (MhzSerial->begin(9600)) { + if (MhzSerial->hardwareSerial()) { ClaimSerial(); } + mhz_type = 1; + } + + } +} + +void MhzShow(bool json) +{ + char types[7] = "MHZ19B"; + char temperature[33]; + dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); + char model[3]; + GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); + WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns15(uint8_t function) +{ + bool result = false; + + if (mhz_type) { + switch (function) { + case FUNC_INIT: + MhzInit(); + break; + case FUNC_EVERY_SECOND: + MhzEverySecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_15 == XdrvMailbox.index) { + result = MhzCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MhzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MhzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_16_tsl2561.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_16_tsl2561.ino" +#ifdef USE_I2C +#ifdef USE_TSL2561 +# 30 "/workspace/Tasmota/tasmota/xsns_16_tsl2561.ino" +#define XSNS_16 16 +#define XI2C_16 16 + +#include + +Tsl2561 Tsl(Wire); + +uint8_t tsl2561_type = 0; +uint8_t tsl2561_valid = 0; +uint32_t tsl2561_milliLux = 0; +uint32_t tsl2561_full = 0; +uint32_t tsl2561_ir = 0; +char tsl2561_types[] = "TSL2561"; + +bool Tsl2561Read(void) +{ + if (tsl2561_valid) { tsl2561_valid--; } + + uint8_t id; + bool gain; + Tsl2561::exposure_t exposure; + uint16_t scaledFull, scaledIr; + + if (Tsl.on()) { + if (Tsl.id(id) + && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) + && Tsl2561Util::normalizedLuminosity(gain, exposure, tsl2561_full = scaledFull, tsl2561_ir = scaledIr)) { + if (! Tsl2561Util::milliLux(tsl2561_full, tsl2561_ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + tsl2561_milliLux = 0; + } + } else{ + tsl2561_milliLux = 0; + tsl2561_full = 0; + tsl2561_ir = 0; + } + } + tsl2561_valid = SENSOR_MAX_MISS; + return true; +} + +void Tsl2561Detect(void) +{ + if (I2cSetDevice(0x29) || I2cSetDevice(0x39) || I2cSetDevice(0x49)) { + uint8_t id; + Tsl.begin(); + if (!Tsl.id(id)) return; + if (Tsl.on()) { + tsl2561_type = 1; + I2cSetActiveFound(Tsl.address(), tsl2561_types); + } + } +} + +void Tsl2561EverySecond(void) +{ + if (!(uptime %2)) { + + if (!Tsl2561Read()) { + AddLogMissed(tsl2561_types, tsl2561_valid); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2561[] PROGMEM = + "{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; +#endif + +void Tsl2561Show(bool json) +{ + if (tsl2561_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u,\"IR\":%u,\"Broadband\":%u}"), + tsl2561_milliLux / 1000, tsl2561_milliLux % 1000, tsl2561_ir, tsl2561_full); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2561, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); +#endif + } + } +} + + + + + +bool Xsns16(uint8_t function) +{ + if (!I2cEnabled(XI2C_16)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2561Detect(); + } + else if (tsl2561_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2561EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2561Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2561Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_17_senseair.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_17_senseair.ino" +#ifdef USE_SENSEAIR +# 29 "/workspace/Tasmota/tasmota/xsns_17_senseair.ino" +#define XSNS_17 17 + +#define SENSEAIR_MODBUS_SPEED 9600 +#define SENSEAIR_DEVICE_ADDRESS 0xFE +#define SENSEAIR_READ_REGISTER 0x04 + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#include +TasmotaModbus *SenseairModbus; + +const char kSenseairTypes[] PROGMEM = "Kx0|S8"; + +uint8_t senseair_type = 1; +char senseair_types[7]; + +uint16_t senseair_co2 = 0; +float senseair_temperature = 0; +float senseair_humidity = 0; + + + +const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; + +uint8_t senseair_read_state = 0; +uint8_t senseair_send_retry = 0; + +void Senseair250ms(void) +{ + + + + + uint16_t value = 0; + bool data_ready = SenseairModbus->ReceiveReady(); + + if (data_ready) { + uint8_t error = SenseairModbus->Receive16BitRegister(&value); + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); + } else { + switch(senseair_read_state) { + case 0: + senseair_type = 2; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); + break; + case 1: + if (value) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); + } + break; + case 2: + senseair_co2 = value; +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); +#endif + break; + case 3: + senseair_temperature = ConvertTemp((float)value / 100); + break; + case 4: + senseair_humidity = ConvertHumidity((float)value / 100); + break; + case 5: + { + bool relay_state = value >> 8 & 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); + break; + } + case 6: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); + break; + } + } + senseair_read_state++; + if (2 == senseair_type) { + if (3 == senseair_read_state) { + senseair_read_state = 1; + } + } else { + if (sizeof(start_addresses) == senseair_read_state) { + senseair_read_state = 1; + } + } + } + + if (0 == senseair_send_retry || data_ready) { + senseair_send_retry = 5; + SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1); + } else { + senseair_send_retry--; + } + + +} + + + +void SenseairInit(void) +{ + senseair_type = 0; + if (PinUsed(GPIO_SAIR_RX) && PinUsed(GPIO_SAIR_TX)) { + SenseairModbus = new TasmotaModbus(Pin(GPIO_SAIR_RX), Pin(GPIO_SAIR_TX)); + uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + senseair_type = 1; + } + } +} + +void SenseairShow(bool json) +{ + GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d"), senseair_types, senseair_co2); + if (senseair_type != 2) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(senseair_temperature, senseair_humidity); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, senseair_co2); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, senseair_types, senseair_co2); + if (senseair_type != 2) { + WSContentSend_THD(senseair_types, senseair_temperature, senseair_humidity); + } +#endif + } +} + + + + + +bool Xsns17(uint8_t function) +{ + bool result = false; + + if (senseair_type) { + switch (function) { + case FUNC_INIT: + SenseairInit(); + break; + case FUNC_EVERY_250_MSECOND: + Senseair250ms(); + break; + case FUNC_JSON_APPEND: + SenseairShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SenseairShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_18_pms5003.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_18_pms5003.ino" +#ifdef USE_PMS5003 +# 31 "/workspace/Tasmota/tasmota/xsns_18_pms5003.ino" +#define XSNS_18 18 + +#include + +#ifndef WARMUP_PERIOD +#define WARMUP_PERIOD 30 +#endif + +#ifndef MIN_INTERVAL_PERIOD +#define MIN_INTERVAL_PERIOD 60 +#endif + +TasmotaSerial *PmsSerial; + +struct PMS5003 { + uint16_t time = 0; + uint8_t type = 1; + uint8_t valid = 0; + uint8_t wake_mode = 1; + uint8_t ready = 1; +} Pms; + +enum PmsCommands +{ + CMD_MODE_ACTIVE, + CMD_SLEEP, + CMD_WAKEUP, + CMD_MODE_PASSIVE, + CMD_READ_DATA +}; + +const uint8_t kPmsCommands[][7] PROGMEM = { + + {0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71}, + {0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73}, + {0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74}, + {0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, + {0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; + +struct pmsX003data { + uint16_t framelen; + uint16_t pm10_standard, pm25_standard, pm100_standard; + uint16_t pm10_env, pm25_env, pm100_env; +#ifdef PMS_MODEL_PMS3003 + uint16_t reserved1, reserved2, reserved3; +#else + uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; + uint16_t unused; +#endif + uint16_t checksum; +} pms_data; + + + +size_t PmsSendCmd(uint8_t command_id) +{ + return PmsSerial->write(kPmsCommands[command_id], sizeof(kPmsCommands[command_id])); +} + + + +bool PmsReadData(void) +{ + if (! PmsSerial->available()) { + return false; + } + while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { + PmsSerial->read(); + } +#ifdef PMS_MODEL_PMS3003 + if (PmsSerial->available() < 22) { +#else + if (PmsSerial->available() < 32) { +#endif + return false; + } + +#ifdef PMS_MODEL_PMS3003 + uint8_t buffer[22]; + PmsSerial->readBytes(buffer, 22); +#else + uint8_t buffer[32]; + PmsSerial->readBytes(buffer, 32); +#endif + uint16_t sum = 0; + PmsSerial->flush(); + +#ifdef PMS_MODEL_PMS3003 + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); +#else + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); +#endif + + +#ifdef PMS_MODEL_PMS3003 + for (uint32_t i = 0; i < 20; i++) { +#else + for (uint32_t i = 0; i < 30; i++) { +#endif + sum += buffer[i]; + } + +#ifdef PMS_MODEL_PMS3003 + uint16_t buffer_u16[10]; + for (uint32_t i = 0; i < 10; i++) { +#else + uint16_t buffer_u16[15]; + for (uint32_t i = 0; i < 15; i++) { +#endif + buffer_u16[i] = buffer[2 + i*2 + 1]; + buffer_u16[i] += (buffer[2 + i*2] << 8); + } +#ifdef PMS_MODEL_PMS3003 + if (sum != buffer_u16[9]) { +#else + if (sum != buffer_u16[14]) { +#endif + AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); + return false; + } + +#ifdef PMS_MODEL_PMS3003 + memcpy((void *)&pms_data, (void *)buffer_u16, 20); +#else + memcpy((void *)&pms_data, (void *)buffer_u16, 30); +#endif + Pms.valid = 10; + + return true; +} +# 172 "/workspace/Tasmota/tasmota/xsns_18_pms5003.ino" +bool PmsCommandSensor(void) +{ + if (PinUsed(GPIO_PMS5003_TX) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + if (XdrvMailbox.payload < MIN_INTERVAL_PERIOD) { + + Settings.pms_wake_interval = 0; + Pms.wake_mode = 1; + Pms.ready = 1; + PmsSendCmd(CMD_MODE_ACTIVE); + PmsSendCmd(CMD_WAKEUP); + } else { + + Settings.pms_wake_interval = XdrvMailbox.payload; + PmsSendCmd(CMD_MODE_PASSIVE); + PmsSendCmd(CMD_SLEEP); + Pms.wake_mode = 0; + Pms.ready = 0; + } + } + + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_18, Settings.pms_wake_interval); + + return true; +} + + + +void PmsSecond(void) +{ + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + + Pms.time++; + if ((Settings.pms_wake_interval - Pms.time <= WARMUP_PERIOD) && !Pms.wake_mode) { + + Pms.wake_mode = 1; + PmsSendCmd(CMD_WAKEUP); + } + if (Pms.time >= Settings.pms_wake_interval) { + + PmsSendCmd(CMD_READ_DATA); + Pms.ready = 1; + Pms.time = 0; + } + } + + if (Pms.ready) { + if (PmsReadData()) { + Pms.valid = 10; + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + PmsSendCmd(CMD_SLEEP); + Pms.wake_mode = 0; + Pms.ready = 0; + } + } else { + if (Pms.valid) { + Pms.valid--; + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + PmsSendCmd(CMD_READ_DATA); + Pms.ready = 1; + } + } + } + } +} + + + +void PmsInit(void) +{ + Pms.type = 0; + if (PinUsed(GPIO_PMS5003_RX)) { + PmsSerial = new TasmotaSerial(Pin(GPIO_PMS5003_RX), (PinUsed(GPIO_PMS5003_TX)) ? Pin(GPIO_PMS5003_TX) : -1, 1); + if (PmsSerial->begin(9600)) { + if (PmsSerial->hardwareSerial()) { ClaimSerial(); } + + if (!PinUsed(GPIO_PMS5003_TX)) { + Settings.pms_wake_interval = 0; + Pms.ready = 1; + } + + Pms.type = 1; + } + } +} + +#ifdef USE_WEBSERVER +#ifdef PMS_MODEL_PMS3003 +const char HTTP_PMS3003_SNS[] PROGMEM = + + + + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#else +const char HTTP_PMS5003_SNS[] PROGMEM = + + + + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; +#endif +#endif + +void PmsShow(bool json) +{ + if (Pms.valid) { + if (json) { +#ifdef PMS_MODEL_PMS3003 + ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, pms_data.pm10_env); + DomoticzSensor(DZ_VOLTAGE, pms_data.pm25_env); + DomoticzSensor(DZ_CURRENT, pms_data.pm100_env); + } +#endif +#ifdef USE_WEBSERVER + } else { + +#ifdef PMS_MODEL_PMS3003 + WSContentSend_PD(HTTP_PMS3003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + WSContentSend_PD(HTTP_PMS5003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#endif + } + } +} + + + + + +bool Xsns18(uint8_t function) +{ + bool result = false; + + if (Pms.type) { + switch (function) { + case FUNC_INIT: + PmsInit(); + break; + case FUNC_EVERY_SECOND: + PmsSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_18 == XdrvMailbox.index) { + result = PmsCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + PmsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + PmsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_19_mgs.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_19_mgs.ino" +#ifdef USE_I2C +#ifdef USE_MGS + + + + + + + +#define XSNS_19 19 +#define XI2C_17 17 + +#ifndef MGS_SENSOR_ADDR +#define MGS_SENSOR_ADDR 0x04 +#endif + +#include "MutichannelGasSensor.h" + +bool mgs_detected = false; + +void MGSInit(void) { + gas.begin(MGS_SENSOR_ADDR); +} + +void MGSPrepare(void) +{ + if (I2cActive(MGS_SENSOR_ADDR)) { return; } + + gas.begin(MGS_SENSOR_ADDR); + if (!gas.isError()) { + I2cSetActiveFound(MGS_SENSOR_ADDR, "MultiGas"); + mgs_detected = true; + } +} + +char* measure_gas(int gas_type, char* buffer) +{ + float f = gas.calcGas(gas_type); + dtostrfd(f, 2, buffer); + return buffer; +} + +#ifdef USE_WEBSERVER +const char HTTP_MGS_GAS[] PROGMEM = "{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +#endif + +void MGSShow(bool json) +{ + char buffer[33]; + if (json) { + ResponseAppend_P(PSTR(",\"MGS\":{\"NH3\":%s"), measure_gas(NH3, buffer)); + ResponseAppend_P(PSTR(",\"CO\":%s"), measure_gas(CO, buffer)); + ResponseAppend_P(PSTR(",\"NO2\":%s"), measure_gas(NO2, buffer)); + ResponseAppend_P(PSTR(",\"C3H8\":%s"), measure_gas(C3H8, buffer)); + ResponseAppend_P(PSTR(",\"C4H10\":%s"), measure_gas(C4H10, buffer)); + ResponseAppend_P(PSTR(",\"CH4\":%s"), measure_gas(GAS_CH4, buffer)); + ResponseAppend_P(PSTR(",\"H2\":%s"), measure_gas(H2, buffer)); + ResponseAppend_P(PSTR(",\"C2H5OH\":%s}"), measure_gas(C2H5OH, buffer)); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGS_GAS, "NH3", measure_gas(NH3, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CO", measure_gas(CO, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "NO2", measure_gas(NO2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C3H8", measure_gas(C3H8, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C4H10", measure_gas(C4H10, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CH4", measure_gas(GAS_CH4, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "H2", measure_gas(H2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C2H5OH", measure_gas(C2H5OH, buffer)); +#endif + } +} + + + + + +bool Xsns19(uint8_t function) +{ + if (!I2cEnabled(XI2C_17)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MGSPrepare(); + } + else if (mgs_detected) { + switch (function) { + case FUNC_JSON_APPEND: + MGSShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGSShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_20_novasds.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_20_novasds.ino" +#ifdef USE_NOVA_SDS +# 30 "/workspace/Tasmota/tasmota/xsns_20_novasds.ino" +#define XSNS_20 20 + +#include + +#ifndef STARTING_OFFSET +#define STARTING_OFFSET 30 +#endif +#if STARTING_OFFSET < 10 +#error "Please set STARTING_OFFSET >= 10" +#endif +#ifndef NOVA_SDS_RECDATA_TIMEOUT +#define NOVA_SDS_RECDATA_TIMEOUT 150 +#endif +#ifndef NOVA_SDS_DEVICE_ID +#define NOVA_SDS_DEVICE_ID 0xFFFF +#endif + +TasmotaSerial *NovaSdsSerial; + +uint8_t novasds_type = 1; +uint8_t novasds_valid = 0; +uint8_t cont_mode = 1; + +struct sds011data { + uint16_t pm100; + uint16_t pm25; +} novasds_data; +uint16_t pm100_sum; +uint16_t pm25_sum; + + +#define NOVA_SDS_REPORTING_MODE 2 +#define NOVA_SDS_QUERY_DATA 4 +#define NOVA_SDS_SET_DEVICE_ID 5 +#define NOVA_SDS_SLEEP_AND_WORK 6 +#define NOVA_SDS_WORKING_PERIOD 8 +#define NOVA_SDS_CHECK_FIRMWARE_VER 7 + #define NOVA_SDS_QUERY_MODE 0 + #define NOVA_SDS_SET_MODE 1 + #define NOVA_SDS_REPORT_ACTIVE 0 + #define NOVA_SDS_REPORT_QUERY 1 + #define NOVA_SDS_SLEEP 0 + #define NOVA_SDS_WORK 1 + + +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) +{ + uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; + + + for (uint32_t i = 2; i < 17; i++) { + novasds_cmnd[17] += novasds_cmnd[i]; + } + + + + + + NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); + NovaSdsSerial->flush(); + + + unsigned long cmndtime = millis(); + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( ! NovaSdsSerial->available() ) ); + if ( ! NovaSdsSerial->available() ) { + + return false; + } + uint8_t recbuf[10]; + memset(recbuf, 0, sizeof(recbuf)); + + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); + if ( 0xAA != recbuf[0] ) { + + return false; + } + + + NovaSdsSerial->readBytes(&recbuf[1], 9); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); + + if ( nullptr != buffer ) { + + memcpy(buffer, recbuf, sizeof(recbuf)); + } + + + if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); + return false; + } + + return true; +} + +void NovaSdsSetWorkPeriod(void) +{ + + NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); + + NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); +} + +bool NovaSdsReadData(void) +{ + uint8_t d[10]; + if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { + return false; + } + novasds_data.pm25 = (d[2] + 256 * d[3]); + novasds_data.pm100 = (d[4] + 256 * d[5]); + + return true; +} + + + +void NovaSdsSecond(void) +{ + if (!novasds_valid) + { + NovaSdsSetWorkPeriod(); + novasds_valid=1; + } + if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) + { + if(!cont_mode) + { + cont_mode = 1; + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + } + else + cont_mode = 0; + + if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) + { + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) + { + if(!(NovaSdsReadData())) novasds_valid=0; + pm100_sum += novasds_data.pm100; + pm25_sum += novasds_data.pm25; + } + if(tele_period == Settings.tele_period-1) + { + novasds_data.pm100 = pm100_sum >> 2; + novasds_data.pm25 = pm25_sum >> 2; + if(!cont_mode) + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); + pm100_sum = pm25_sum = 0; + } +} + + + + + + + +bool NovaSdsCommandSensor(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { + if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; + else Settings.novasds_startingoffset = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); + + return true; +} + +void NovaSdsInit(void) +{ + novasds_type = 0; + if (PinUsed(GPIO_SDS0X1_RX) && PinUsed(GPIO_SDS0X1_TX)) { + NovaSdsSerial = new TasmotaSerial(Pin(GPIO_SDS0X1_RX), Pin(GPIO_SDS0X1_TX), 1); + if (NovaSdsSerial->begin(9600)) { + if (NovaSdsSerial->hardwareSerial()) { + ClaimSerial(); + } + novasds_type = 1; + NovaSdsSetWorkPeriod(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SDS0X1_SNS[] PROGMEM = + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void NovaSdsShow(bool json) +{ + if (novasds_valid) { + float pm10f = (float)(novasds_data.pm100) / 10.0f; + float pm2_5f = (float)(novasds_data.pm25) / 10.0f; + char pm10[33]; + dtostrfd(pm10f, 1, pm10); + char pm2_5[33]; + dtostrfd(pm2_5f, 1, pm2_5); + if (json) { + ResponseAppend_P(PSTR(",\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), pm2_5, pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SDS0X1_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns20(uint8_t function) +{ + bool result = false; + + if (novasds_type) { + switch (function) { + case FUNC_INIT: + NovaSdsInit(); + break; + case FUNC_EVERY_SECOND: + NovaSdsSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_20 == XdrvMailbox.index) { + result = NovaSdsCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + NovaSdsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + NovaSdsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_21_sgp30.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_21_sgp30.ino" +#ifdef USE_I2C +#ifdef USE_SGP30 +# 30 "/workspace/Tasmota/tasmota/xsns_21_sgp30.ino" +#define XSNS_21 21 +#define XI2C_18 18 + +#define SGP30_ADDRESS 0x58 + +#include "Adafruit_SGP30.h" +Adafruit_SGP30 sgp; + +bool sgp30_type = false; +bool sgp30_ready = false; +float sgp30_abshum; + + + +void sgp30_Init(void) +{ + if (I2cActive(SGP30_ADDRESS)) { return; } + + if (sgp.begin()) { + sgp30_type = true; + + I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); + } +} + + +#define POW_FUNC FastPrecisePow + +float sgp30_AbsoluteHumidity(float temperature, float humidity) { + + + + + + float temp = NAN; + const float mw = 18.01534; + const float r = 8.31447215; + + if (isnan(temperature) || isnan(humidity) ) { + return NAN; + } + + temp = POW_FUNC(2.718281828, (17.67 * temperature) / (temperature + 243.5)); + + + return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); +} + +#define SAVE_PERIOD 30 + +void Sgp30Update(void) +{ + sgp30_ready = false; + if (!sgp.IAQmeasure()) { + return; + } + if (global_update && (global_humidity > 0) && !isnan(global_temperature_celsius)) { + + sgp30_abshum = sgp30_AbsoluteHumidity(global_temperature_celsius, global_humidity); + sgp.setHumidity(sgp30_abshum*1000); + } + sgp30_ready = true; + + + if (!(uptime%SAVE_PERIOD)) { + + uint16_t TVOC_base; + uint16_t eCO2_base; + + if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; + + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SGP30[] PROGMEM = + "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; +const char HTTP_SNS_AHUM[] PROGMEM = "{s}SGP30 Abs Humidity{m}%s g/m3{e}"; +#endif + +#define D_JSON_AHUM "aHumidity" + +void Sgp30Show(bool json) +{ + if (sgp30_ready) { + char abs_hum[33]; + + if (global_update && (global_humidity > 0) && !isnan(global_temperature_celsius)) { + + dtostrfd(sgp30_abshum,4,abs_hum); + } + if (json) { + ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); + if (global_update && global_humidity>0 && !isnan(global_temperature_celsius)) { + ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SGP30, sgp.eCO2, sgp.TVOC); + if (global_update) { + WSContentSend_PD(HTTP_SNS_AHUM, abs_hum); + } +#endif + } + } +} + + + + + +bool Xsns21(uint8_t function) +{ + if (!I2cEnabled(XI2C_18)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + sgp30_Init(); + } + else if (sgp30_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Sgp30Update(); + break; + case FUNC_JSON_APPEND: + Sgp30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sgp30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_22_sr04.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_22_sr04.ino" +#ifdef USE_SR04 + +#include +#include +# 32 "/workspace/Tasmota/tasmota/xsns_22_sr04.ino" +#define XSNS_22 22 + +uint8_t sr04_type = 1; +real64_t distance; + +NewPing* sonar = nullptr; +TasmotaSerial* sonar_serial = nullptr; + +uint8_t Sr04TModeDetect(void) +{ + sr04_type = 0; + if (!PinUsed(GPIO_SR04_ECHO)) { return sr04_type; } + + int sr04_echo_pin = Pin(GPIO_SR04_ECHO); + int sr04_trig_pin = (PinUsed(GPIO_SR04_TRIG)) ? Pin(GPIO_SR04_TRIG) : Pin(GPIO_SR04_ECHO); + sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); + + if (sonar_serial->begin(9600,1)) { + DEBUG_SENSOR_LOG(PSTR("SR04: Detect mode")); + + if (sr04_trig_pin != -1) { + sr04_type = (Sr04TMiddleValue(Sr04TMode3Distance(), Sr04TMode3Distance(), Sr04TMode3Distance()) != NO_ECHO) ? 3 : 1; + } else { + sr04_type = 2; + } + } else { + sr04_type = 1; + } + + if (sr04_type < 2) { + delete sonar_serial; + sonar_serial = nullptr; + if (-1 == sr04_trig_pin) { + sr04_trig_pin = Pin(GPIO_SR04_ECHO); + } + sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); + } else { + if (sonar_serial->hardwareSerial()) { + ClaimSerial(); + } + } + + AddLog_P2(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); + return sr04_type; +} + +uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third) +{ + uint16_t ret = first; + if (first > second) { + first = second; + second = ret; + } + + if (third < first) { + return first; + } else if (third > second) { + return second; + } else { + return third; + } +} + +uint16_t Sr04TMode3Distance() { + + sonar_serial->write(0x55); + sonar_serial->flush(); + + return Sr04TMode2Distance(); +} + +uint16_t Sr04TMode2Distance(void) +{ + sonar_serial->setTimeout(300); + const char startByte = 0xff; + + if (!sonar_serial->find(startByte)) { + + return NO_ECHO; + } + + delay(5); + + uint8_t crc = sonar_serial->read(); + + uint16_t distance = ((uint16_t)crc) << 8; + + + distance += sonar_serial->read(); + crc += distance & 0x00ff; + crc += 0x00FF; + + + if (crc != sonar_serial->read()) { + AddLog_P2(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); + return NO_ECHO; + } + + return distance; +} + +void Sr04TReading(void) { + + if (sonar_serial==nullptr && sonar==nullptr) { + Sr04TModeDetect(); + } + + switch (sr04_type) { + case 3: + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance()))/ 10; + break; + case 2: + + while(sonar_serial->available()) sonar_serial->read(); + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode2Distance(),Sr04TMode2Distance(),Sr04TMode2Distance()))/10; + break; + case 1: + distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; + break; + default: + distance = NO_ECHO; + } + + return; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_DISTANCE[] PROGMEM = + "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; +#endif + +void Sr04Show(bool json) +{ + + if (distance != 0) { + char distance_chr[33]; + dtostrfd(distance, 3, distance_chr); + + if(json) { + ResponseAppend_P(PSTR(",\"SR04\":{\"" D_JSON_DISTANCE "\":%s}"), distance_chr); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, distance_chr); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); +#endif + } + } +} + + + + + +bool Xsns22(uint8_t function) +{ + bool result = false; + + if (sr04_type) { + switch (function) { + case FUNC_INIT: + result = (PinUsed(GPIO_SR04_ECHO)); + break; + case FUNC_EVERY_SECOND: + Sr04TReading(); + result = true; + break; + case FUNC_JSON_APPEND: + Sr04Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sr04Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_24_si1145.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_24_si1145.ino" +#ifdef USE_I2C +#ifdef USE_SI1145 +# 30 "/workspace/Tasmota/tasmota/xsns_24_si1145.ino" +#define XSNS_24 24 +#define XI2C_19 19 + +#define SI114X_ADDR 0X60 + + + +#define SI114X_QUERY 0X80 +#define SI114X_SET 0XA0 +#define SI114X_NOP 0X00 +#define SI114X_RESET 0X01 +#define SI114X_BUSADDR 0X02 +#define SI114X_PS_FORCE 0X05 +#define SI114X_GET_CAL 0X12 +#define SI114X_ALS_FORCE 0X06 +#define SI114X_PSALS_FORCE 0X07 +#define SI114X_PS_PAUSE 0X09 +#define SI114X_ALS_PAUSE 0X0A +#define SI114X_PSALS_PAUSE 0X0B +#define SI114X_PS_AUTO 0X0D +#define SI114X_ALS_AUTO 0X0E +#define SI114X_PSALS_AUTO 0X0F + + + +#define SI114X_PART_ID 0X00 +#define SI114X_REV_ID 0X01 +#define SI114X_SEQ_ID 0X02 +#define SI114X_INT_CFG 0X03 +#define SI114X_IRQ_ENABLE 0X04 +#define SI114X_IRQ_MODE1 0x05 +#define SI114X_IRQ_MODE2 0x06 +#define SI114X_HW_KEY 0X07 +#define SI114X_MEAS_RATE0 0X08 +#define SI114X_MEAS_RATE1 0X09 +#define SI114X_PS_RATE 0X0A +#define SI114X_PS_LED21 0X0F +#define SI114X_PS_LED3 0X10 +#define SI114X_UCOEFF0 0X13 +#define SI114X_UCOEFF1 0X14 +#define SI114X_UCOEFF2 0X15 +#define SI114X_UCOEFF3 0X16 +#define SI114X_WR 0X17 +#define SI114X_COMMAND 0X18 +#define SI114X_RESPONSE 0X20 +#define SI114X_IRQ_STATUS 0X21 +#define SI114X_ALS_VIS_DATA0 0X22 +#define SI114X_ALS_VIS_DATA1 0X23 +#define SI114X_ALS_IR_DATA0 0X24 +#define SI114X_ALS_IR_DATA1 0X25 +#define SI114X_PS1_DATA0 0X26 +#define SI114X_PS1_DATA1 0X27 +#define SI114X_PS2_DATA0 0X28 +#define SI114X_PS2_DATA1 0X29 +#define SI114X_PS3_DATA0 0X2A +#define SI114X_PS3_DATA1 0X2B +#define SI114X_AUX_DATA0_UVINDEX0 0X2C +#define SI114X_AUX_DATA1_UVINDEX1 0X2D +#define SI114X_RD 0X2E +#define SI114X_CHIP_STAT 0X30 + + + +#define SI114X_CHLIST 0X01 +#define SI114X_CHLIST_ENUV 0x80 +#define SI114X_CHLIST_ENAUX 0x40 +#define SI114X_CHLIST_ENALSIR 0x20 +#define SI114X_CHLIST_ENALSVIS 0x10 +#define SI114X_CHLIST_ENPS1 0x01 +#define SI114X_CHLIST_ENPS2 0x02 +#define SI114X_CHLIST_ENPS3 0x04 + +#define SI114X_PSLED12_SELECT 0X02 +#define SI114X_PSLED3_SELECT 0X03 + +#define SI114X_PS_ENCODE 0X05 +#define SI114X_ALS_ENCODE 0X06 + +#define SI114X_PS1_ADCMUX 0X07 +#define SI114X_PS2_ADCMUX 0X08 +#define SI114X_PS3_ADCMUX 0X09 + +#define SI114X_PS_ADC_COUNTER 0X0A +#define SI114X_PS_ADC_GAIN 0X0B +#define SI114X_PS_ADC_MISC 0X0C + +#define SI114X_ALS_IR_ADC_MUX 0X0E +#define SI114X_AUX_ADC_MUX 0X0F + +#define SI114X_ALS_VIS_ADC_COUNTER 0X10 +#define SI114X_ALS_VIS_ADC_GAIN 0X11 +#define SI114X_ALS_VIS_ADC_MISC 0X12 + +#define SI114X_LED_REC 0X1C + +#define SI114X_ALS_IR_ADC_COUNTER 0X1D +#define SI114X_ALS_IR_ADC_GAIN 0X1E +#define SI114X_ALS_IR_ADC_MISC 0X1F + + + + +#define SI114X_ADCMUX_SMALL_IR 0x00 +#define SI114X_ADCMUX_VISIABLE 0x02 +#define SI114X_ADCMUX_LARGE_IR 0x03 +#define SI114X_ADCMUX_NO 0x06 +#define SI114X_ADCMUX_GND 0x25 +#define SI114X_ADCMUX_TEMPERATURE 0x65 +#define SI114X_ADCMUX_VDD 0x75 + +#define SI114X_PSLED12_SELECT_PS1_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS1_LED1 0x01 +#define SI114X_PSLED12_SELECT_PS1_LED2 0x02 +#define SI114X_PSLED12_SELECT_PS1_LED3 0x04 +#define SI114X_PSLED12_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED12_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED12_SELECT_PS2_LED3 0x40 +#define SI114X_PSLED3_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED3_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED3_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED3_SELECT_PS2_LED3 0x40 + +#define SI114X_ADC_GAIN_DIV1 0X00 +#define SI114X_ADC_GAIN_DIV2 0X01 +#define SI114X_ADC_GAIN_DIV4 0X02 +#define SI114X_ADC_GAIN_DIV8 0X03 +#define SI114X_ADC_GAIN_DIV16 0X04 +#define SI114X_ADC_GAIN_DIV32 0X05 + +#define SI114X_LED_CURRENT_5MA 0X01 +#define SI114X_LED_CURRENT_11MA 0X02 +#define SI114X_LED_CURRENT_22MA 0X03 +#define SI114X_LED_CURRENT_45MA 0X04 + +#define SI114X_ADC_COUNTER_1ADCCLK 0X00 +#define SI114X_ADC_COUNTER_7ADCCLK 0X01 +#define SI114X_ADC_COUNTER_15ADCCLK 0X02 +#define SI114X_ADC_COUNTER_31ADCCLK 0X03 +#define SI114X_ADC_COUNTER_63ADCCLK 0X04 +#define SI114X_ADC_COUNTER_127ADCCLK 0X05 +#define SI114X_ADC_COUNTER_255ADCCLK 0X06 +#define SI114X_ADC_COUNTER_511ADCCLK 0X07 + +#define SI114X_ADC_MISC_LOWRANGE 0X00 +#define SI114X_ADC_MISC_HIGHRANGE 0X20 +#define SI114X_ADC_MISC_ADC_NORMALPROXIMITY 0X00 +#define SI114X_ADC_MISC_ADC_RAWADC 0X04 + +#define SI114X_INT_CFG_INTOE 0X01 + +#define SI114X_IRQEN_ALS 0x01 +#define SI114X_IRQEN_PS1 0x04 +#define SI114X_IRQEN_PS2 0x08 +#define SI114X_IRQEN_PS3 0x10 + +uint16_t si1145_visible; +uint16_t si1145_infrared; +uint16_t si1145_uvindex; + +bool si1145_type = false; +uint8_t si1145_valid = 0; + + + +uint8_t Si1145ReadByte(uint8_t reg) +{ + return I2cRead8(SI114X_ADDR, reg); +} + +uint16_t Si1145ReadHalfWord(uint8_t reg) +{ + return I2cRead16LE(SI114X_ADDR, reg); +} + +bool Si1145WriteByte(uint8_t reg, uint16_t val) +{ + I2cWrite8(SI114X_ADDR, reg, val); +} + +uint8_t Si1145WriteParamData(uint8_t p, uint8_t v) +{ + Si1145WriteByte(SI114X_WR, v); + Si1145WriteByte(SI114X_COMMAND, p | SI114X_SET); + return Si1145ReadByte(SI114X_RD); +} + + + +bool Si1145Present(void) +{ + return (Si1145ReadByte(SI114X_PART_ID) == 0X45); +} + +void Si1145Reset(void) +{ + Si1145WriteByte(SI114X_MEAS_RATE0, 0); + Si1145WriteByte(SI114X_MEAS_RATE1, 0); + Si1145WriteByte(SI114X_IRQ_ENABLE, 0); + Si1145WriteByte(SI114X_IRQ_MODE1, 0); + Si1145WriteByte(SI114X_IRQ_MODE2, 0); + Si1145WriteByte(SI114X_INT_CFG, 0); + Si1145WriteByte(SI114X_IRQ_STATUS, 0xFF); + + Si1145WriteByte(SI114X_COMMAND, SI114X_RESET); + delay(10); + Si1145WriteByte(SI114X_HW_KEY, 0x17); + delay(10); +} + +void Si1145DeInit(void) +{ + + + Si1145WriteByte(SI114X_UCOEFF0, 0x29); + Si1145WriteByte(SI114X_UCOEFF1, 0x89); + Si1145WriteByte(SI114X_UCOEFF2, 0x02); + Si1145WriteByte(SI114X_UCOEFF3, 0x00); + Si1145WriteParamData(SI114X_CHLIST, SI114X_CHLIST_ENUV | SI114X_CHLIST_ENALSIR | SI114X_CHLIST_ENALSVIS | SI114X_CHLIST_ENPS1); + + + + Si1145WriteParamData(SI114X_PS1_ADCMUX, SI114X_ADCMUX_LARGE_IR); + Si1145WriteByte(SI114X_PS_LED21, SI114X_LED_CURRENT_22MA); + Si1145WriteParamData(SI114X_PSLED12_SELECT, SI114X_PSLED12_SELECT_PS1_LED1); + + + + Si1145WriteParamData(SI114X_PS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_PS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_PS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE | SI114X_ADC_MISC_ADC_RAWADC); + + + + Si1145WriteParamData(SI114X_ALS_VIS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteParamData(SI114X_ALS_IR_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_IR_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_IR_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteByte(SI114X_INT_CFG, SI114X_INT_CFG_INTOE); + Si1145WriteByte(SI114X_IRQ_ENABLE, SI114X_IRQEN_ALS); + + + + Si1145WriteByte(SI114X_MEAS_RATE0, 0xFF); + Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); +} + +bool Si1145Begin(void) +{ + if (!Si1145Present()) { return false; } + + Si1145Reset(); + Si1145DeInit(); + return true; +} + + +uint16_t Si1145ReadUV(void) +{ + return Si1145ReadHalfWord(SI114X_AUX_DATA0_UVINDEX0); +} + + +uint16_t Si1145ReadVisible(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_VIS_DATA0); +} + + +uint16_t Si1145ReadIR(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_IR_DATA0); +} + + + +bool Si1145Read(void) +{ + if (si1145_valid) { si1145_valid--; } + + if (!Si1145Present()) { return false; } + + si1145_visible = Si1145ReadVisible(); + si1145_infrared = Si1145ReadIR(); + si1145_uvindex = Si1145ReadUV(); + si1145_valid = SENSOR_MAX_MISS; + return true; +} + +void Si1145Detect(void) +{ + if (I2cActive(SI114X_ADDR)) { return; } + + if (Si1145Begin()) { + si1145_type = true; + I2cSetActiveFound(SI114X_ADDR, "SI1145"); + } +} + +void Si1145Update(void) +{ + if (!Si1145Read()) { + AddLogMissed("SI1145", si1145_valid); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SI1145[] PROGMEM = + "{s}SI1145 " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_INFRARED "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; +#endif + +void Si1145Show(bool json) +{ + if (si1145_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), + si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, si1145_visible); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SI1145, si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#endif + } + } +} + + + + + +bool Xsns24(uint8_t function) +{ + if (!I2cEnabled(XI2C_19)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Si1145Detect(); + } + else if (si1145_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Si1145Update(); + break; + case FUNC_JSON_APPEND: + Si1145Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Si1145Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_26_lm75ad.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_26_lm75ad.ino" +#ifdef USE_I2C +#ifdef USE_LM75AD +# 31 "/workspace/Tasmota/tasmota/xsns_26_lm75ad.ino" +#define XSNS_26 26 +#define XI2C_20 20 + +#define LM75AD_ADDRESS1 0x48 +#define LM75AD_ADDRESS2 0x49 +#define LM75AD_ADDRESS3 0x4A +#define LM75AD_ADDRESS4 0x4B +#define LM75AD_ADDRESS5 0x4C +#define LM75AD_ADDRESS6 0x4D +#define LM75AD_ADDRESS7 0x4E +#define LM75AD_ADDRESS8 0x4F + +#define LM75_TEMP_REGISTER 0x00 +#define LM75_CONF_REGISTER 0x01 +#define LM75_THYST_REGISTER 0x02 +#define LM75_TOS_REGISTER 0x03 + +bool lm75ad_type = false; +uint8_t lm75ad_address; +uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; + +void LM75ADDetect(void) +{ + for (uint32_t i = 0; i < sizeof(lm75ad_addresses); i++) { + lm75ad_address = lm75ad_addresses[i]; + if (I2cActive(lm75ad_address)) { + continue; } + if (!I2cSetDevice(lm75ad_address)) { + break; + } + uint16_t buffer; + if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { + if (buffer == 0x4B00) { + lm75ad_type = true; + I2cSetActiveFound(lm75ad_address, "LM75AD"); + break; + } + } + } +} + +float LM75ADGetTemp(void) +{ + int16_t sign = 1; + + uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); + if (t & 0x8000) { + t = (~t) +0x20; + sign = -1; + } + t = t >> 5; + return ConvertTemp(sign * t * 0.125); +} + +void LM75ADShow(bool json) +{ + float t = LM75ADGetTemp(); + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, "LM75AD", temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns26(uint8_t function) +{ + if (!I2cEnabled(XI2C_20)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + LM75ADDetect(); + } + else if (lm75ad_type) { + switch (function) { + case FUNC_JSON_APPEND: + LM75ADShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + LM75ADShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +# 28 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +#ifdef USE_I2C +#ifdef USE_APDS9960 +# 52 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +#define XSNS_27 27 +#define XI2C_21 21 + + +#define APDS9960_I2C_ADDR 0x39 + +#define APDS9960_CHIPID_1 0xAB +#define APDS9960_CHIPID_2 0x9C +#define APDS9960_CHIPID_3 0xA8 + +#define APDS9930_CHIPID_1 0x12 +#define APDS9930_CHIPID_2 0x39 + +#define APDS9960_MODE_GESTURE 0 +#define APDS9960_MODE_COLOR 1 + + +#define GESTURE_THRESHOLD_OUT 10 +#define GESTURE_SENSITIVITY_1 50 +#define GESTURE_SENSITIVITY_2 20 + +#define APDS9960_LONG_RECOVERY 50 +#define APDS9960_MAX_GESTURE_CYCLES 50 + + + + + +const char APDS9960_TAG[] PROGMEM = "APDS9960"; + +#ifdef USE_WEBSERVER + +#ifdef USE_APDS9960_GESTURE +const char HTTP_SNS_GESTURE[] PROGMEM = "{s}%s " D_GESTURE "{m}%s{e}"; +#endif + +#ifdef USE_APDS9960_COLOR +const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u{e}"; +const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u{e}"; +const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u{e}"; +const char HTTP_SNS_CCT[] PROGMEM = "{s}%s " D_CCT "{m}%u " D_UNIT_KELVIN "{e}"; +#endif + +#ifdef USE_APDS9960_PROXIMITY +const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; +#endif + +#endif +# 108 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +#define FIFO_PAUSE_TIME 30 + + +#define APDS9960_ENABLE 0x80 +#define APDS9960_ATIME 0x81 +#define APDS9960_WTIME 0x83 +#define APDS9960_AILTL 0x84 +#define APDS9960_AILTH 0x85 +#define APDS9960_AIHTL 0x86 +#define APDS9960_AIHTH 0x87 +#define APDS9960_PILT 0x89 +#define APDS9960_PIHT 0x8B +#define APDS9960_PERS 0x8C +#define APDS9960_CONFIG1 0x8D +#define APDS9960_PPULSE 0x8E +#define APDS9960_CONTROL 0x8F +#define APDS9960_CONFIG2 0x90 +#define APDS9960_ID 0x92 +#define APDS9960_STATUS 0x93 +#define APDS9960_CDATAL 0x94 +#define APDS9960_CDATAH 0x95 +#define APDS9960_RDATAL 0x96 +#define APDS9960_RDATAH 0x97 +#define APDS9960_GDATAL 0x98 +#define APDS9960_GDATAH 0x99 +#define APDS9960_BDATAL 0x9A +#define APDS9960_BDATAH 0x9B +#define APDS9960_PDATA 0x9C +#define APDS9960_POFFSET_UR 0x9D +#define APDS9960_POFFSET_DL 0x9E +#define APDS9960_CONFIG3 0x9F +#define APDS9960_GPENTH 0xA0 +#define APDS9960_GEXTH 0xA1 +#define APDS9960_GCONF1 0xA2 +#define APDS9960_GCONF2 0xA3 +#define APDS9960_GOFFSET_U 0xA4 +#define APDS9960_GOFFSET_D 0xA5 +#define APDS9960_GOFFSET_L 0xA7 +#define APDS9960_GOFFSET_R 0xA9 +#define APDS9960_GPULSE 0xA6 +#define APDS9960_GCONF3 0xAA +#define APDS9960_GCONF4 0xAB +#define APDS9960_GFLVL 0xAE +#define APDS9960_GSTATUS 0xAF +#define APDS9960_IFORCE 0xE4 +#define APDS9960_PICLEAR 0xE5 +#define APDS9960_CICLEAR 0xE6 +#define APDS9960_AICLEAR 0xE7 +#define APDS9960_GFIFO_U 0xFC +#define APDS9960_GFIFO_D 0xFD +#define APDS9960_GFIFO_L 0xFE +#define APDS9960_GFIFO_R 0xFF + + +#define APDS9960_PON 0b00000001 +#define APDS9960_AEN 0b00000010 +#define APDS9960_PEN 0b00000100 +#define APDS9960_WEN 0b00001000 +#define APDS9960_AIEN 0b00010000 +#define APDS9960_PIEN 0b00100000 +#define APDS9960_GEN 0b01000000 + +#define APDS9960_GVALID 0b00000001 + + +#define OFF 0 +#define ON 1 + + +#define POWER 0 +#define AMBIENT_LIGHT 1 +#define PROXIMITY 2 +#define WAIT 3 +#define AMBIENT_LIGHT_INT 4 +#define PROXIMITY_INT 5 +#define GESTURE 6 +#define ALL 7 + + +#define LED_DRIVE_100MA 0 +#define LED_DRIVE_50MA 1 +#define LED_DRIVE_25MA 2 +#define LED_DRIVE_12_5MA 3 + + +#define PGAIN_1X 0 +#define PGAIN_2X 1 +#define PGAIN_4X 2 +#define PGAIN_8X 3 + + +#define AGAIN_1X 0 +#define AGAIN_4X 1 +#define AGAIN_16X 2 +#define AGAIN_64X 3 + + +#define GGAIN_1X 0 +#define GGAIN_2X 1 +#define GGAIN_4X 2 +#define GGAIN_8X 3 + + +#define LED_BOOST_100 0 +#define LED_BOOST_150 1 +#define LED_BOOST_200 2 +#define LED_BOOST_300 3 + + +#define GWTIME_0MS 0 +#define GWTIME_2_8MS 1 +#define GWTIME_5_6MS 2 +#define GWTIME_8_4MS 3 +#define GWTIME_14_0MS 4 +#define GWTIME_22_4MS 5 +#define GWTIME_30_8MS 6 +#define GWTIME_39_2MS 7 + + + + +#define DEFAULT_ATIME 0xdb +#define DEFAULT_WTIME 246 +#define DEFAULT_PROX_PPULSE 0x87 +#define DEFAULT_GESTURE_PPULSE 0x89 +#define DEFAULT_POFFSET_UR 0 +#define DEFAULT_POFFSET_DL 0 +#define DEFAULT_CONFIG1 0x60 +#define DEFAULT_LDRIVE LED_DRIVE_100MA +#define DEFAULT_PGAIN PGAIN_4X +#define DEFAULT_AGAIN AGAIN_4X +#define DEFAULT_PILT 0 +#define DEFAULT_PIHT 50 +#define DEFAULT_AILT 0xFFFF +#define DEFAULT_AIHT 0 +#define DEFAULT_PERS 0x11 +#define DEFAULT_CONFIG2 0x01 +#define DEFAULT_CONFIG3 0 +#define DEFAULT_GPENTH 40 +#define DEFAULT_GEXTH 30 +#define DEFAULT_GCONF1 0x40 +#define DEFAULT_GGAIN GGAIN_4X +#define DEFAULT_GLDRIVE LED_DRIVE_100MA +#define DEFAULT_GWTIME GWTIME_2_8MS +#define DEFAULT_GOFFSET 0 +#define DEFAULT_GPULSE 0xC9 +#define DEFAULT_GCONF3 0 +#define DEFAULT_GIEN 0 + +#define APDS9960_ERROR 0xFF + +#ifdef USE_APDS9960_GESTURE + + +const char GESTURE_UP[] PROGMEM = "Up"; +const char GESTURE_DOWN[] PROGMEM = "Down"; +const char GESTURE_LEFT[] PROGMEM = "Left"; +const char GESTURE_RIGHT[] PROGMEM = "Right"; +const char GESTURE_LONG[] PROGMEM = "Long"; +const char GESTURE_NONE[] PROGMEM = "None"; + +enum { + DIR_NONE, + DIR_LEFT, + DIR_RIGHT, + DIR_UP, + DIR_DOWN, + DIR_NEAR, + DIR_FAR, + DIR_ALL +}; +# 291 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +typedef struct gesture_data_type { + uint8_t u_data[32]; + uint8_t d_data[32]; + uint8_t l_data[32]; + uint8_t r_data[32]; + uint8_t index; + uint8_t total_gestures; + uint8_t in_threshold; + uint8_t out_threshold; +} gesture_data_t; + +typedef struct gesture_type { + int16_t ud_delta_ = 0; + int16_t lr_delta_ = 0; + int16_t ud_count_ = 0; + int16_t lr_count_ = 0; + int16_t state_ = 0; + int16_t motion_ = DIR_NONE; +} gesture_t; + +#endif + +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) +typedef struct color_data_type { + uint16_t a; + uint16_t r; + uint16_t g; + uint16_t b; + uint8_t p; + + uint16_t cct; + uint16_t lux; +} color_data_t; +#endif + + + + + +#ifdef USE_APDS9960_GESTURE +gesture_data_t gesture_data; +gesture_t gesture; +char currentGesture[6]; +#endif + +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) +color_data_t color_data; +#endif + +volatile uint8_t recovery_loop_counter = 0; +bool APDS9960_overload = false; +uint8_t APDS9960_aTime = DEFAULT_ATIME; +uint8_t APDS9960_type = 0; +uint8_t gesture_mode = 1; + + + + + +#ifdef USE_APDS9960_COLOR + + + + + +void calculateColorTemperature(void) { + float X, Y, Z; + float xc, yc; + float n; + float cct; + + + + + + X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); + Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); + Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + (+0.56332F * color_data.b); + + + xc = (X) / (X + Y + Z); + yc = (Y) / (X + Y + Z); + + + n = (xc - 0.3320F) / (0.1858F - yc); + + + color_data.cct = (449.0F * FastPrecisePowf(n, 3)) + (3525.0F * FastPrecisePowf(n, 2)) + (6823.3F * n) + 5520.33F; + + return; +} +#endif +# 393 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getProxIntLowThresh(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); + + return val; +} + + + + + + +inline void setProxIntLowThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); +} + + + + + + +uint8_t getProxIntHighThresh(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT); + + return val; +} + + + + + + +inline void setProxIntHighThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); +} +# 445 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getLEDDrive(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + val = (val >> 6) & 0b00000011; + + return val; +} +# 468 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setLEDDrive(uint8_t drive) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 6; + val &= 0b00111111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} +# 495 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getProximityGain(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + val = (val >> 2) & 0b00000011; + + return val; +} +# 518 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setProximityGain(uint8_t drive) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 2; + val &= 0b11110011; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} +# 545 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getAmbientLightGain() { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + val &= 0b00000011; + + return val; +} +# 568 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setAmbientLightGain(uint8_t drive) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + val &= 0b11111100; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); +} +# 594 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getLEDBoost(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2); + + + val = (val >> 4) & 0b00000011; + + return val; +} +# 617 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setLEDBoost(uint8_t boost) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2); + + + boost &= 0b00000011; + boost = boost << 4; + val &= 0b11001111; + val |= boost; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val); +} + + + + + + +uint8_t getProxGainCompEnable(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + + val = (val >> 5) & 0b00000001; + + return val; +} + + + + + + +void setProxGainCompEnable(uint8_t enable) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val); +} +# 683 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getProxPhotoMask(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + + val &= 0b00001111; + + return val; +} +# 707 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setProxPhotoMask(uint8_t mask) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3); + + + mask &= 0b00001111; + val &= 0b11110000; + val |= mask; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val); +} + +#ifdef USE_APDS9960_GESTURE + + + + + + +uint8_t getGestureEnterThresh(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH); + + return val; +} + + + + + + +inline void setGestureEnterThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold); +} + + + + + + +uint8_t getGestureExitThresh(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH); + + return val; +} + + + + + + +inline void setGestureExitThresh(uint8_t threshold) { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold); +} +# 781 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getGestureGain(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + val = (val >> 5) & 0b00000011; + + return val; +} +# 804 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setGestureGain(uint8_t gain) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + gain &= 0b00000011; + gain = gain << 5; + val &= 0b10011111; + val |= gain; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} +# 831 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getGestureLEDDrive(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + val = (val >> 3) & 0b00000011; + + return val; +} +# 854 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setGestureLEDDrive(uint8_t drive) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + drive &= 0b00000011; + drive = drive << 3; + val &= 0b11100111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} +# 885 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getGestureWaitTime(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + val &= 0b00000111; + + return val; +} +# 912 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +void setGestureWaitTime(uint8_t time) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2); + + + time &= 0b00000111; + val &= 0b11111000; + val |= time; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); +} + +#endif + + + + + + +void getLightIntLowThreshold(uint16_t &threshold) { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL); + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte); + threshold = threshold + ((uint16_t)val_byte << 8); +} + + + + + + +void setLightIntLowThreshold(uint16_t threshold) { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low); + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high); +} + + + + + + +void getLightIntHighThreshold(uint16_t &threshold) { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte); + threshold = threshold + ((uint16_t)val_byte << 8); +} + + + + + + +void setLightIntHighThreshold(uint16_t threshold) { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high); +} + + + + + + +void getProximityIntLowThreshold(uint8_t &threshold) { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); +} + + + + + + +void setProximityIntLowThreshold(uint8_t threshold) { + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); +} + + + + + + +void getProximityIntHighThreshold(uint8_t &threshold) { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT); +} + + + + + + +void setProximityIntHighThreshold(uint8_t threshold) { + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); +} + + + + + + +uint8_t getAmbientLightIntEnable(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + val = (val >> 4) & 0b00000001; + + return val; +} + + + + + + +void setAmbientLightIntEnable(uint8_t enable) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + enable &= 0b00000001; + enable = enable << 4; + val &= 0b11101111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val); +} + + + + + + +uint8_t getProximityIntEnable(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + val = (val >> 5) & 0b00000001; + + return val; +} + + + + + + +void setProximityIntEnable(uint8_t enable) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val); +} + + + + + + +uint8_t getGestureIntEnable(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + + val = (val >> 1) & 0b00000001; + + return val; +} + + + + + + +void setGestureIntEnable(uint8_t enable) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + + enable &= 0b00000001; + enable = enable << 1; + val &= 0b11111101; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val); +} + + + + + +void clearAmbientLightInt(void) { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); +} + + + + + +void clearProximityInt(void) { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR); +} + + + + + + +uint8_t getGestureMode(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + + val &= 0b00000001; + + return val; +} + + + + + + +void setGestureMode(uint8_t mode) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4); + + + mode &= 0b00000001; + val &= 0b11111110; + val |= mode; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val); +} + + +bool APDS9960_init(void) { + setMode(ALL, OFF); + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1); + + setLEDDrive(DEFAULT_LDRIVE); + setProximityGain(DEFAULT_PGAIN); + setAmbientLightGain(DEFAULT_AGAIN); + setProxIntLowThresh(DEFAULT_PILT); + setProxIntHighThresh(DEFAULT_PIHT); + setLightIntLowThreshold(DEFAULT_AILT); + setLightIntHighThreshold(DEFAULT_AIHT); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3); + + +#ifdef USE_APDS9960_GESTURE + setGestureEnterThresh(DEFAULT_GPENTH); + setGestureExitThresh(DEFAULT_GEXTH); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1); + + setGestureGain(DEFAULT_GGAIN); + setGestureLEDDrive(DEFAULT_GLDRIVE); + setGestureWaitTime(DEFAULT_GWTIME); + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3); + + setGestureIntEnable(DEFAULT_GIEN); +#endif + + disablePower(); + + return true; +} +# 1278 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +inline uint8_t getMode(void) { + uint8_t enable_value; + + + enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + return enable_value; +} + + + + + + + +void setMode(uint8_t mode, uint8_t enable) { + uint8_t reg_val; + + + reg_val = getMode(); + + + enable = enable & 0x01; + if (mode <= 6) { + if (enable) { + reg_val |= (1 << mode); + } else { + reg_val &= ~(1 << mode); + } + } else if (mode == ALL) { + if (enable) { + reg_val = 0x7F; + } else { + reg_val = 0x00; + } + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val); +} + + + + + + +void enableLightSensor(void) { + + setAmbientLightGain(DEFAULT_AGAIN); + setAmbientLightIntEnable(OFF); + enablePower(); + setMode(AMBIENT_LIGHT, ON); +} + + + + + +void disableLightSensor(void) { + setAmbientLightIntEnable(OFF); + setMode(AMBIENT_LIGHT, OFF); +} + + + + + + +void enableProximitySensor(void) { + + setProximityGain(DEFAULT_PGAIN); + setLEDDrive(DEFAULT_LDRIVE); + setProximityIntEnable(OFF); + enablePower(); + setMode(PROXIMITY, ON); +} + + + + + +void disableProximitySensor(void) { + setProximityIntEnable(OFF); + setMode(PROXIMITY, OFF); +} + +#ifdef USE_APDS9960_GESTURE + + + + + +void enableGestureSensor(void) { + + + + + + + + resetGestureParameters(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE); + setLEDBoost(LED_BOOST_100); + setGestureIntEnable(OFF); + setGestureMode(ON); + enablePower(); + setMode(WAIT, ON); + setMode(PROXIMITY, ON); + setMode(GESTURE, ON); +} + + + + + +void disableGestureSensor(void) { + resetGestureParameters(); + setGestureIntEnable(OFF); + setGestureMode(OFF); + setMode(GESTURE, OFF); +} + + + + + + +bool isGestureAvailable(void) { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); + + + val &= APDS9960_GVALID; + + + return (val == 1); +} + + + + + + +int16_t readGesture(void) { + uint8_t fifo_level = 0; + uint8_t fifo_data[128]; + uint8_t gstatus; + int16_t motion; + uint16_t i; + uint8_t gesture_loop_counter = 0; + int8_t bytes_read = 0; + + + if (!isGestureAvailable() || !(getMode() & 0b01000001)) { + return DIR_NONE; + } + + + while (1) { + if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES) { + disableGestureSensor(); + APDS9960_overload = true; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); + } + gesture_loop_counter += 1; + + + delay(FIFO_PAUSE_TIME); + + + gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); + + + if ((gstatus & APDS9960_GVALID) == APDS9960_GVALID) { + + fifo_level = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GFLVL); + +#ifdef USE_DEBUG_DRIVER + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Level : %d"), fifo_level); +#endif + + + if (fifo_level > 0) { + bytes_read = (fifo_level * 4); + + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_GFIFO_U, (uint8_t*)fifo_data, bytes_read)) { + return APDS9960_ERROR; + } + +#ifdef USE_DEBUG_DRIVER + char output[(bytes_read * 2) + 1]; + char *ptr = &output[0]; + + for ( i = 0; i < bytes_read; i++ ) { + ptr += sprintf(ptr, "%02X", fifo_data[i]); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Dump : %s"), output); +#endif + + + if (bytes_read >= 4) { + for (i = 0; i < bytes_read; i += 4) { + gesture_data.u_data[gesture_data.index] = fifo_data[i + 0]; + gesture_data.d_data[gesture_data.index] = fifo_data[i + 1]; + gesture_data.l_data[gesture_data.index] = fifo_data[i + 2]; + gesture_data.r_data[gesture_data.index] = fifo_data[i + 3]; + gesture_data.index++; + gesture_data.total_gestures++; + } + + + if (processGestureData()) { + if (decodeGesture()) { + + } + } + + gesture_data.index = 0; + gesture_data.total_gestures = 0; + } + } + } else { + + delay(FIFO_PAUSE_TIME); + decodeGesture(); + motion = gesture.motion_; + resetGestureParameters(); + return motion; + } + } +} + +#endif + + + + + +inline void enablePower(void) { + setMode(POWER, ON); +} + + + + + +inline void disablePower(void) { + setMode(POWER, OFF); +} + + + + + + +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) + + + +inline void readAllColorAndProximityData(void) { + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) { + + + } +} + +void APDS9960_adjustATime(void) { + + I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); + + + if (color_data.a < (uint16_t)20) { + APDS9960_aTime = 0x40; + } else if (color_data.a < (uint16_t)40) { + APDS9960_aTime = 0x80; + } else if (color_data.a < (uint16_t)50) { + APDS9960_aTime = DEFAULT_ATIME; + } else if (color_data.a < (uint16_t)70) { + APDS9960_aTime = 0xc0; + } + + if (color_data.a < 200) { + APDS9960_aTime = 0xe9; + } + + + + else { + APDS9960_aTime = 0xff; + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); + enablePower(); + enableLightSensor(); + delay(20); +} +#endif + + + + + +#ifdef USE_APDS9960_GESTURE + + + + +void resetGestureParameters(void) { + gesture_data.index = 0; + gesture_data.total_gestures = 0; + + gesture.ud_delta_ = 0; + gesture.lr_delta_ = 0; + + gesture.ud_count_ = 0; + gesture.lr_count_ = 0; + + gesture.state_ = 0; + gesture.motion_ = DIR_NONE; +} + + + + + + +bool processGestureData(void) { + uint8_t u_first = 0; + uint8_t d_first = 0; + uint8_t l_first = 0; + uint8_t r_first = 0; + uint8_t u_last = 0; + uint8_t d_last = 0; + uint8_t l_last = 0; + uint8_t r_last = 0; + uint16_t ud_ratio_first; + uint16_t lr_ratio_first; + uint16_t ud_ratio_last; + uint16_t lr_ratio_last; + uint16_t ud_delta; + uint16_t lr_delta; + uint16_t i; + + + if (gesture_data.total_gestures <= 4) { + return false; + } + + + if ((gesture_data.total_gestures <= 32) && \ + (gesture_data.total_gestures > 0)) { + + for (i = 0; i < gesture_data.total_gestures; i++) { + if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + u_first = gesture_data.u_data[i]; + d_first = gesture_data.d_data[i]; + l_first = gesture_data.l_data[i]; + r_first = gesture_data.r_data[i]; + break; + } + } + + + if ((u_first == 0) || (d_first == 0) || (l_first == 0) || (r_first == 0)) { + return false; + } + + + for (i = gesture_data.total_gestures - 1; i >= 0; i--) { + if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT)) { + u_last = gesture_data.u_data[i]; + d_last = gesture_data.d_data[i]; + l_last = gesture_data.l_data[i]; + r_last = gesture_data.r_data[i]; + break; + } + } + } + + + ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); + lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); + ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); + lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); + + + ud_delta = ud_ratio_last - ud_ratio_first; + lr_delta = lr_ratio_last - lr_ratio_first; + + + gesture.ud_delta_ += ud_delta; + gesture.lr_delta_ += lr_delta; + + + if (gesture.ud_delta_ >= GESTURE_SENSITIVITY_1) { + gesture.ud_count_ = 1; + } else if (gesture.ud_delta_ <= -GESTURE_SENSITIVITY_1) { + gesture.ud_count_ = -1; + } else { + gesture.ud_count_ = 0; + } + + + if (gesture.lr_delta_ >= GESTURE_SENSITIVITY_1) { + gesture.lr_count_ = 1; + } else if (gesture.lr_delta_ <= -GESTURE_SENSITIVITY_1) { + gesture.lr_count_ = -1; + } else { + gesture.lr_count_ = 0; + } + return false; +} + + + + + + +bool decodeGesture(void) { + + if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == 0)) { + gesture.motion_ = DIR_UP; + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == 0)) { + gesture.motion_ = DIR_DOWN; + } else if ((gesture.ud_count_ == 0) && (gesture.lr_count_ == 1)) { + gesture.motion_ = DIR_RIGHT; + } else if ((gesture.ud_count_ == 0) && (gesture.lr_count_ == -1)) { + gesture.motion_ = DIR_LEFT; + } else if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == 1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_UP; + } else { + gesture.motion_ = DIR_RIGHT; + } + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == -1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_DOWN; + } else { + gesture.motion_ = DIR_LEFT; + } + } else if ((gesture.ud_count_ == -1) && (gesture.lr_count_ == -1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_UP; + } else { + gesture.motion_ = DIR_LEFT; + } + } else if ((gesture.ud_count_ == 1) && (gesture.lr_count_ == 1)) { + if (abs(gesture.ud_delta_) > abs(gesture.lr_delta_)) { + gesture.motion_ = DIR_DOWN; + } else { + gesture.motion_ = DIR_RIGHT; + } + } else { + return false; + } + + return true; +} + +void handleGesture(void) { + if (isGestureAvailable()) { + switch (readGesture()) { + case DIR_UP: + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_UP); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_UP); + break; + case DIR_DOWN: + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_DOWN); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_DOWN); + break; + case DIR_LEFT: + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LEFT); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LEFT); + break; + case DIR_RIGHT: + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_RIGHT); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_RIGHT); + break; + default: + if (APDS9960_overload) { + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LONG); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LONG); + } else { + AddLog_P(LOG_LEVEL_DEBUG, GESTURE_NONE); + snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_NONE); + } + break; + } + MqttPublishSensor(); + } + currentGesture[0] = '\0'; +} + +void APDS9960_loop(void) { + if (recovery_loop_counter > 0) { + recovery_loop_counter -= 1; + } + + if (recovery_loop_counter == 1 && APDS9960_overload) { + enableGestureSensor(); + APDS9960_overload = false; + Response_P(PSTR("{\"Gesture\":\"On\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 1; + } + + if (gesture_mode) { + if (recovery_loop_counter == 0) { + handleGesture(); + + if (APDS9960_overload) { + disableGestureSensor(); + recovery_loop_counter = APDS9960_LONG_RECOVERY; + Response_P(PSTR("{\"Gesture\":\"Off\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 0; + } + } + } +} + +#endif + +void APDS9960_detect(void) { + if (APDS9960_type || I2cActive(APDS9960_I2C_ADDR)) { return; } + + APDS9960_type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); + +#ifdef USE_DEBUG_DRIVER + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960_type); +#endif + + if (APDS9960_type == APDS9960_CHIPID_1 || APDS9960_type == APDS9960_CHIPID_2 || APDS9960_type == APDS9960_CHIPID_3) { + if (APDS9960_init()) { + I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960_TAG); + + enableProximitySensor(); + +#if defined(USE_APDS9960_GESTURE) && USE_APDS9960_STARTMODE == APDS9960_MODE_GESTURE + gesture_mode = 1; + enableGestureSensor(); +#endif + +#if ( defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) ) && USE_APDS9960_STARTMODE == APDS9960_MODE_COLOR + gesture_mode = 0; + enableLightSensor(); + APDS9960_overload = false; +#endif + } else { + APDS9960_type = 0; + } + } else { + APDS9960_type = 0; + } + +#ifdef USE_APDS9960_GESTURE + currentGesture[0] = '\0'; +#endif +} + + + + + +void APDS9960_show(bool json) { + if (!APDS9960_type) { return; } + + if (!gesture_mode && !APDS9960_overload) { + +#if defined(USE_APDS9960_COLOR) || defined(USE_APDS9960_PROXIMITY) + uint16_t ambient; + + readAllColorAndProximityData(); + ambient = color_data.a/4; + + + + + +#ifdef USE_APDS9960_COLOR + calculateColorTemperature(); +#endif + if (json) { +#if defined(USE_APDS9960_COLOR) && defined(USE_APDS9960_PROXIMITY) + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"" D_JSON_ILLUMINANCE "\":%u,\"CCT\":%u,\"Proximity\":%u}"), + APDS9960_TAG, + color_data.r, + color_data.g, + color_data.b, + ambient, + color_data.cct, + color_data.p); +#else + +#ifdef USE_APDS9960_COLOR + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"" D_JSON_ILLUMINANCE "\":%u,\"CCT\":%u}"), + APDS9960_TAG, + color_data.r, + color_data.g, + color_data.b, + ambient, + color_data.cct); +#endif + +#ifdef USE_APDS9960_PROXIMITY + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), + APDS9960_TAG, + color_data.p); +#endif + +#endif +#ifdef USE_WEBSERVER + } else { + +#ifdef USE_APDS9960_COLOR + WSContentSend_PD(HTTP_SNS_COLOR_RED, APDS9960_TAG, color_data.r); + WSContentSend_PD(HTTP_SNS_COLOR_GREEN, APDS9960_TAG, color_data.g); + WSContentSend_PD(HTTP_SNS_COLOR_BLUE, APDS9960_TAG, color_data.b); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, APDS9960_TAG, ambient); + WSContentSend_PD(HTTP_SNS_CCT, APDS9960_TAG, color_data.cct); +#endif + +#ifdef USE_APDS9960_PROXIMITY + WSContentSend_PD(HTTP_SNS_PROXIMITY, APDS9960_TAG, color_data.p); +#endif + +#endif + } +#endif + +#ifdef USE_APDS9960_GESTURE + } else { + if (currentGesture[0] != '\0') { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960_TAG, currentGesture); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GESTURE, APDS9960_TAG, currentGesture); +#endif + currentGesture[0] = '\0'; + } + } +#endif + + } +} +# 1947 "/workspace/Tasmota/tasmota/xsns_27_apds9960.ino" +bool APDS9960CommandSensor(void) { + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: +#ifdef USE_APDS9960_GESTURE + disableGestureSensor(); +#endif + gesture_mode = 0; + enableLightSensor(); + APDS9960_overload = false; + break; +#ifdef USE_APDS9960_GESTURE + case 1: + if (APDS9960_type) { + setGestureGain(DEFAULT_GGAIN); + setProximityGain(DEFAULT_PGAIN); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; + case 2: + if (APDS9960_type) { + setGestureGain(GGAIN_2X); + setProximityGain(PGAIN_2X); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; +#endif + default: + int temp_aTime = (uint8_t)XdrvMailbox.payload; + if (temp_aTime > 2 && temp_aTime < 256) { + disablePower(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); + enablePower(); + enableLightSensor(); + } + break; + } + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); + + return serviced; +} + + + + + +bool Xsns27(uint8_t function) { + if (!I2cEnabled(XI2C_21)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + APDS9960_detect(); + } else if (APDS9960_type) { + switch (function) { +#ifdef USE_APDS9960_GESTURE + case FUNC_EVERY_50_MSECOND: + APDS9960_loop(); + break; +#endif + case FUNC_COMMAND_SENSOR: + if (XSNS_27 == XdrvMailbox.index) { + result = APDS9960CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + APDS9960_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + APDS9960_show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_28_tm1638.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_28_tm1638.ino" +#ifdef USE_TM1638 + + + + + + +#define XSNS_28 28 + +#define TM1638_COLOR_NONE 0 +#define TM1638_COLOR_RED 1 +#define TM1638_COLOR_GREEN 2 + +#define TM1638_CLOCK_DELAY 1 + +uint8_t tm1638_type = 1; +uint8_t tm1638_clock_pin = 0; +uint8_t tm1638_data_pin = 0; +uint8_t tm1638_strobe_pin = 0; +uint8_t tm1638_displays = 8; +uint8_t tm1638_active_display = 1; +uint8_t tm1638_intensity = 0; +uint8_t tm1638_state = 0; + + + + + + +void Tm16XXSend(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(tm1638_data_pin, !!(data & (1 << i))); + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + digitalWrite(tm1638_clock_pin, HIGH); + } +} + +void Tm16XXSendCommand(uint8_t cmd) +{ + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(cmd); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +void TM16XXSendData(uint8_t address, uint8_t data) +{ + Tm16XXSendCommand(0x44); + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0 | address); + Tm16XXSend(data); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +uint8_t Tm16XXReceive(void) +{ + uint8_t temp = 0; + + + pinMode(tm1638_data_pin, INPUT); + digitalWrite(tm1638_data_pin, HIGH); + + for (uint32_t i = 0; i < 8; ++i) { + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + temp |= digitalRead(tm1638_data_pin) << i; + digitalWrite(tm1638_clock_pin, HIGH); + } + + + pinMode(tm1638_data_pin, OUTPUT); + digitalWrite(tm1638_data_pin, LOW); + + return temp; +} + + + +void Tm16XXClearDisplay(void) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + TM16XXSendData(i << 1, 0); + } +} + +void Tm1638SetLED(uint8_t color, uint8_t pos) +{ + TM16XXSendData((pos << 1) + 1, color); +} + +void Tm1638SetLEDs(word leds) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + uint8_t color = 0; + + if ((leds & (1 << i)) != 0) { + color |= TM1638_COLOR_RED; + } + + if ((leds & (1 << (i + 8))) != 0) { + color |= TM1638_COLOR_GREEN; + } + + Tm1638SetLED(color, i); + } +} + +uint8_t Tm1638GetButtons(void) +{ + uint8_t keys = 0; + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0x42); + for (uint32_t i = 0; i < 4; i++) { + keys |= Tm16XXReceive() << i; + } + digitalWrite(tm1638_strobe_pin, HIGH); + + return keys; +} + + + +void TmInit(void) +{ + tm1638_type = 0; + if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { + tm1638_clock_pin = Pin(GPIO_TM16CLK); + tm1638_data_pin = Pin(GPIO_TM16DIO); + tm1638_strobe_pin = Pin(GPIO_TM16STB); + + pinMode(tm1638_data_pin, OUTPUT); + pinMode(tm1638_clock_pin, OUTPUT); + pinMode(tm1638_strobe_pin, OUTPUT); + + digitalWrite(tm1638_strobe_pin, HIGH); + digitalWrite(tm1638_clock_pin, HIGH); + + Tm16XXSendCommand(0x40); + Tm16XXSendCommand(0x80 | (tm1638_active_display ? 8 : 0) | tmin(7, tm1638_intensity)); + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0); + for (uint32_t i = 0; i < 16; i++) { + Tm16XXSend(0x00); + } + digitalWrite(tm1638_strobe_pin, HIGH); + + tm1638_type = 1; + tm1638_state = 1; + } +} + +void TmLoop(void) +{ + if (tm1638_state) { + uint8_t buttons = Tm1638GetButtons(); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + SwitchSetVirtual(i, (buttons &1) ^1); + uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; + Tm1638SetLED(color, i); + buttons >>= 1; + } + SwitchHandler(1); + } +} +# 201 "/workspace/Tasmota/tasmota/xsns_28_tm1638.ino" +bool Xsns28(uint8_t function) +{ + bool result = false; + + if (tm1638_type) { + switch (function) { + case FUNC_INIT: + TmInit(); + break; + case FUNC_EVERY_50_MSECOND: + TmLoop(); + break; +# 223 "/workspace/Tasmota/tasmota/xsns_28_tm1638.ino" + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_29_mcp230xx.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#ifdef USE_I2C +#ifdef USE_MCP230xx +# 31 "/workspace/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#define XSNS_29 29 +#define XI2C_22 22 + + + + + +uint8_t MCP230xx_IODIR = 0x00; +uint8_t MCP230xx_GPINTEN = 0x02; +uint8_t MCP230xx_IOCON = 0x05; +uint8_t MCP230xx_GPPU = 0x06; +uint8_t MCP230xx_INTF = 0x07; +uint8_t MCP230xx_INTCAP = 0x08; +uint8_t MCP230xx_GPIO = 0x09; + +uint8_t mcp230xx_type = 0; +uint8_t mcp230xx_pincount = 0; +uint8_t mcp230xx_oldoutpincount = 0; +#ifdef USE_MCP230xx_OUTPUT +uint8_t mcp230xx_outpinmapping[16]; +#endif +uint8_t mcp230xx_int_en = 0; +uint8_t mcp230xx_int_prio_counter = 0; +uint8_t mcp230xx_int_counter_en = 0; +uint8_t mcp230xx_int_retainer_en = 0; +uint8_t mcp230xx_int_sec_counter = 0; + +uint8_t mcp230xx_int_report_defer_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint16_t mcp230xx_int_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint8_t mcp230xx_int_retainer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +unsigned long int_millis[16]; + +const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29_D%i\":{\"MODE\":%i,\"PULL_UP\":\"%s\",\"INT_MODE\":\"%s\",\"STATE\":\"%s\"}}"; + +const char MCP230XX_INTCFG_RESPONSE[] PROGMEM = "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; + +#ifdef USE_MCP230xx_OUTPUT +const char MCP230XX_CMND_RESPONSE[] PROGMEM = "{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"}}"; +#endif + +void MCP230xx_CheckForIntCounter(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_count_en) { + en=1; + } + } + if (!Settings.mcp230xx_int_timer) en=0; + mcp230xx_int_counter_en=en; + if (!mcp230xx_int_counter_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_counter[ca] = 0; + } + } +} + +void MCP230xx_CheckForIntRetainer(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_retain_flag) { + en=1; + } + } + mcp230xx_int_retainer_en=en; + if (!mcp230xx_int_retainer_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_retainer[ca] = 0; + } + } +} + +const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { +#ifdef USE_MCP230xx_OUTPUT +if ((6 == pinmod) && (statu < 2)) { statu = abs(statu-1); } +#endif + switch (statu) { + case 0: + return "OFF"; + break; + case 1: + return "ON"; + break; +#ifdef USE_MCP230xx_OUTPUT + case 2: + return "TOGGLE"; + break; +#endif + } + return ""; +} + +const char* IntModeTxt(uint8_t intmo) { + switch (intmo) { + case 0: + return "ALL"; + break; + case 1: + return "EVENT"; + break; + case 2: + return "TELE"; + break; + case 3: + return "DISABLED"; + break; + } + return ""; +} + +uint8_t MCP230xx_readGPIO(uint8_t port) { + return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); +} + +void MCP230xx_ApplySettings(void) +{ + uint8_t int_en = 0; + for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { + uint8_t reg_gppu = 0; + uint8_t reg_gpinten = 0; + uint8_t reg_iodir = 0xFF; +#ifdef USE_MCP230xx_OUTPUT + uint8_t reg_portpins = 0x00; +#endif + for (uint32_t idx = 0; idx < 8; idx++) { + switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { + case 0 ... 1: + reg_iodir |= (1 << idx); + break; + case 2 ... 4: + reg_iodir |= (1 << idx); + reg_gpinten |= (1 << idx); + int_en = 1; + break; +#ifdef USE_MCP230xx_OUTPUT + case 5 ... 6: + reg_iodir &= ~(1 << idx); + if (Settings.flag.save_state) { + reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); + } else { + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_portpins |= (1 << idx); + } + } + break; +#endif + default: + break; + } +#ifdef USE_MCP230xx_OUTPUT + if ((Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) && (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode < 5)) { + reg_gppu |= (1 << idx); + } +#else + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_gppu |= (1 << idx); + } +#endif + } + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); +#ifdef USE_MCP230xx_OUTPUT + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); +#endif + } + devices_present -= mcp230xx_oldoutpincount; + mcp230xx_oldoutpincount = 0; + for (uint32_t idx=0;idx= 5) { + mcp230xx_outpinmapping[mcp230xx_oldoutpincount] = idx; + mcp230xx_oldoutpincount++; + } + int_millis[idx]=millis(); + } + devices_present += mcp230xx_oldoutpincount; + mcp230xx_int_en = int_en; + MCP230xx_CheckForIntCounter(); + MCP230xx_CheckForIntRetainer(); +} + +void MCP230xx_Detect(void) +{ + if (I2cActive(USE_MCP230xx_ADDR)) { return; } + + uint8_t buffer; + + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x80); + if (I2cValidRead8(&buffer, USE_MCP230xx_ADDR, MCP230xx_IOCON)) { + if (0x00 == buffer) { + mcp230xx_type = 1; + I2cSetActiveFound(USE_MCP230xx_ADDR, "MCP23008"); + mcp230xx_pincount = 8; + MCP230xx_ApplySettings(); + } else { + if (0x80 == buffer) { + mcp230xx_type = 2; + I2cSetActiveFound(USE_MCP230xx_ADDR, "MCP23017"); + mcp230xx_pincount = 16; + + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x00); + + MCP230xx_GPINTEN = 0x04; + MCP230xx_GPPU = 0x0C; + MCP230xx_INTF = 0x0E; + MCP230xx_INTCAP = 0x10; + MCP230xx_GPIO = 0x12; + MCP230xx_ApplySettings(); + } + } + } +} + +void MCP230xx_CheckForInterrupt(void) { + uint8_t intf; + uint8_t mcp230xx_intcap = 0; + uint8_t report_int; + for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { + if (I2cValidRead8(&intf,USE_MCP230xx_ADDR,MCP230xx_INTF+mcp230xx_port)) { + if (intf > 0) { + if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { + for (uint32_t intp = 0; intp < 8; intp++) { + if ((intf >> intp) & 0x01) { + report_int = 0; + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode > 1) { + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode) { + case 2: + report_int = 1; + break; + case 3: + if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; + break; + case 4: + if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; + break; + default: + break; + } + + if ((mcp230xx_int_counter_en) && (report_int)) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + mcp230xx_int_counter[intp+(mcp230xx_port*8)]++; + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]++; + if (mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)] >= Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]=0; + } else { + report_int = 0; + } + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_retain_flag) { + mcp230xx_int_retainer[intp+(mcp230xx_port*8)] = 1; + report_int = 0; + } + } + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + report_int = 0; + } + if (report_int) { + bool int_tele = false; + bool int_event = false; + unsigned long millis_now = millis(); + unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; + int_millis[intp+(mcp230xx_port*8)]=millis_now; + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { + case 0: + int_tele=true; + int_event=true; + break; + case 1: + int_event=true; + break; + case 2: + int_tele=true; + break; + } + if (int_tele) { + ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), + intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishSensor(); + } + } + if (int_event) { + char command[19]; + sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); + ExecuteCommand(command, SRC_RULE); + } + } + } + } + } + } + } + } + } +} + +void MCP230xx_Show(bool json) +{ + if (json) { + uint8_t gpio = MCP230xx_readGPIO(0); + ResponseAppend_P(PSTR(",\"MCP230XX\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i"), + (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); + uint8_t gpiob = 0; + if (2 == mcp230xx_type) { + gpiob = MCP230xx_readGPIO(1); + ResponseAppend_P(PSTR(",\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i"), + (gpiob>>0)&1, (gpiob>>1)&1, (gpiob>>2)&1, (gpiob>>3)&1, (gpiob>>4)&1, (gpiob>>5)&1, (gpiob>>6)&1, (gpiob>>7)&1); + } + +#ifdef USE_MCP230xx_OUTPUT + uint8_t outputcount = 0; + for (uint32_t pinx = 0; pinx < mcp230xx_pincount; pinx++) { + if (Settings.mcp230xx_config[pinx].pinmode >= 5) { outputcount++; } + } + if (outputcount) { + uint16_t gpiototal = ((uint16_t)gpiob << 8) | gpio; + ResponseAppend_P(PSTR(",\"MCP230_OUT\":{")); + char stt[7]; + for (uint32_t pinx = 0; pinx < mcp230xx_pincount; pinx++) { + if (Settings.mcp230xx_config[pinx].pinmode >= 5) { + sprintf(stt, ConvertNumTxt(((gpiototal>>pinx)&1), Settings.mcp230xx_config[pinx].pinmode)); + ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","), pinx, stt); + } + } + ResponseAppend_P(PSTR("\"END\":1}")); + } +#endif + ResponseJsonEnd(); + } +} + +#ifdef USE_MCP230xx_OUTPUT + +void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { + uint8_t portpins; + uint8_t port = 0; + uint8_t pinmo = Settings.mcp230xx_config[pin].pinmode; + uint8_t interlock = Settings.flag.interlock; + int pinadd = (pin % 2)+1-(3*(pin % 2)); + char cmnd[7], stt[4]; + if (pin > 7) { port = 1; } + portpins = MCP230xx_readGPIO(port); + + if (pinstate < 2) { + if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); + } else { + portpins ^= (1 << (pin-(port*8))); + } + + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); + if (Settings.flag.save_state) { + Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; + Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; + } + sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); + sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); + if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { + char stt1[4]; + sprintf(stt1,ConvertNumTxt((portpins >> (pin+pinadd-(port*8))&1), pinmo)); + Response_P(PSTR("{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"},\"S29cmnd_D%i\":{\"STATE\":\"%s\"}}"),pin, cmnd, stt, pin+pinadd, stt1); + } else { + Response_P(MCP230XX_CMND_RESPONSE, pin, cmnd, stt); + } +} + +#endif + +void MCP230xx_Reset(uint8_t pinmode) { + uint8_t pullup = 0; + if ((pinmode > 1) && (pinmode < 5)) { pullup=1; } + for (uint32_t pinx=0;pinx<16;pinx++) { + Settings.mcp230xx_config[pinx].pinmode=pinmode; + Settings.mcp230xx_config[pinx].pullup=pullup; + Settings.mcp230xx_config[pinx].saved_state=0; + if ((pinmode > 1) && (pinmode < 5)) { + Settings.mcp230xx_config[pinx].int_report_mode=0; + } else { + Settings.mcp230xx_config[pinx].int_report_mode=3; + } + Settings.mcp230xx_config[pinx].int_report_defer=0; + Settings.mcp230xx_config[pinx].int_count_en=0; + Settings.mcp230xx_config[pinx].int_retain_flag=0; + Settings.mcp230xx_config[pinx].spare13=0; + Settings.mcp230xx_config[pinx].spare14=0; + Settings.mcp230xx_config[pinx].spare15=0; + } + Settings.mcp230xx_int_prio = 0; + Settings.mcp230xx_int_timer = 0; + MCP230xx_ApplySettings(); + char pulluptxt[7]; + char intmodetxt[9]; + sprintf(pulluptxt,ConvertNumTxt(pullup)); + uint8_t intmode = 3; + if ((pinmode > 1) && (pinmode < 5)) { intmode = 0; } + sprintf(intmodetxt,IntModeTxt(intmode)); + Response_P(MCP230XX_SENSOR_RESPONSE,99,pinmode,pulluptxt,intmodetxt,""); +} + +bool MCP230xx_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((intpri >= 0) && (intpri <= 20)) { + Settings.mcp230xx_int_prio = intpri; + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { + if (paramcount > 1) { + uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((inttim >= 0) && (inttim <= 3600)) { + Settings.mcp230xx_int_timer = inttim; + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intdef >= 0) && (intdef <= 15)) { + Settings.mcp230xx_config[pin].int_report_defer=intdef; + if (Settings.mcp230xx_config[pin].int_count_en) { + Settings.mcp230xx_config[pin].int_count_en=0; + MCP230xx_CheckForIntCounter(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); + } + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intcnt >= 0) && (intcnt <= 1)) { + Settings.mcp230xx_config[pin].int_count_en=intcnt; + if (Settings.mcp230xx_config[pin].int_report_defer) { + Settings.mcp230xx_config[pin].int_report_defer=0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); + } + if (Settings.mcp230xx_config[pin].int_report_mode < 3) { + Settings.mcp230xx_config[pin].int_report_mode=3; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); + } + if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); + } + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((int_retain >= 0) && (int_retain <= 1)) { + Settings.mcp230xx_config[pin].int_retain_flag=int_retain; + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + MCP230xx_CheckForIntRetainer(); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + + if (pin < mcp230xx_pincount) { + if (0 == pin) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; + } else { + validpin=true; + } + } + if (validpin && (paramcount > 1)) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtr[7],pinstatustxtr[7]; + char intmodetxt[9]; + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); + sprintf(pulluptxtr,ConvertNumTxt(Settings.mcp230xx_config[pin].pullup)); +#ifdef USE_MCP230xx_OUTPUT + uint8_t pinmod = Settings.mcp230xx_config[pin].pinmode; + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1,pinmod)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmod,pulluptxtr,intmodetxt,pinstatustxtr); +#else + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,pulluptxtr,intmodetxt,pinstatustxtr); +#endif + return serviced; + } +#ifdef USE_MCP230xx_OUTPUT + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; + uint8_t relay_no = 0; + for (relay_no = 0; relay_no < mcp230xx_pincount ; relay_no ++) { + if ( mcp230xx_outpinmapping[relay_no] == pin) break; + } + relay_no = devices_present - mcp230xx_oldoutpincount + relay_no +1; + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { + ExecuteCommandPower(relay_no, 1, SRC_IGNORE); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { + ExecuteCommandPower(relay_no, 0, SRC_IGNORE); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { + ExecuteCommandPower(relay_no, 2, SRC_IGNORE); + return serviced; + } + } +#endif + uint8_t pinmode = 0; + uint8_t pullup = 0; + uint8_t intmode = 0; + if (paramcount > 1) { + pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + } + if (paramcount > 2) { + pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + } + if (paramcount > 3) { + intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + } +#ifdef USE_MCP230xx_OUTPUT + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { +#else + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { +#endif + Settings.mcp230xx_config[pin].pinmode=pinmode; + Settings.mcp230xx_config[pin].pullup=pullup; + if ((pinmode > 1) && (pinmode < 5)) { + if ((intmode >= 0) && (intmode <= 3)) { + Settings.mcp230xx_config[pin].int_report_mode=intmode; + } + } else { + Settings.mcp230xx_config[pin].int_report_mode=3; + } + MCP230xx_ApplySettings(); + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtc[7], pinstatustxtc[7]; + char intmodetxt[9]; + sprintf(pulluptxtc,ConvertNumTxt(pullup)); + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); +#ifdef USE_MCP230xx_OUTPUT + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1,Settings.mcp230xx_config[pin].pinmode)); +#else + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1)); +#endif + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmode,pulluptxtc,intmodetxt,pinstatustxtc); + return serviced; + } + } else { + serviced=false; + return serviced; + } + return serviced; +} + +#ifdef USE_MCP230xx_DISPLAYOUTPUT + +const char HTTP_SNS_MCP230xx_OUTPUT[] PROGMEM = "{s}MCP230XX D%d{m}%s{e}"; + +void MCP230xx_UpdateWebData(void) +{ + uint8_t gpio1 = MCP230xx_readGPIO(0); + uint8_t gpio2 = 0; + if (2 == mcp230xx_type) { + gpio2 = MCP230xx_readGPIO(1); + } + uint16_t gpio = (gpio2 << 8) + gpio1; + for (uint32_t pin = 0; pin < mcp230xx_pincount; pin++) { + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + char stt[7]; + sprintf(stt,ConvertNumTxt((gpio>>pin)&1,Settings.mcp230xx_config[pin].pinmode)); + WSContentSend_PD(HTTP_SNS_MCP230xx_OUTPUT, pin, stt); + } + } +} + +#endif +# 771 "/workspace/Tasmota/tasmota/xsns_29_mcp230xx.ino" +void MCP230xx_Interrupt_Counter_Report(void) { + ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].int_count_en) { + ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); + mcp230xx_int_counter[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"END\":1}}")); + MqttPublishTeleSensor(); + mcp230xx_int_sec_counter = 0; +} + +void MCP230xx_Interrupt_Retain_Report(void) { + uint16_t retainresult = 0; + ResponseTime_P(PSTR(",\"MCP_INTRETAIN\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].int_retain_flag) { + ResponseAppend_P(PSTR("\"D%i\":%i,"),pinx,mcp230xx_int_retainer[pinx]); + retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); + mcp230xx_int_retainer[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"Value\":%u}}"),retainresult); + MqttPublishTeleSensor(); +} + +#ifdef USE_MCP230xx_OUTPUT +void MCP230xx_SwitchRelay() { + for (uint32_t i = devices_present - mcp230xx_oldoutpincount; i < devices_present; i++) { + uint8_t pin = mcp230xx_outpinmapping[i - (devices_present - mcp230xx_oldoutpincount)]; + uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; + uint8_t relay_state = bitRead(XdrvMailbox.index, i); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MCP: relay %d pin_no %d state %d"), i,pin, relay_state); + switch (relay_state) { + case 1: + MCP230xx_SetOutPin(pin,abs(pincmd-1)); + break; + case 0: + MCP230xx_SetOutPin(pin,pincmd); + break; + } + } +} +#endif + + + + + +bool Xsns29(uint8_t function) +{ + if (!I2cEnabled(XI2C_22)) { return false; } + + bool result = false; + + if (FUNC_PRE_INIT == function) { + MCP230xx_Detect(); + } + else if (mcp230xx_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (mcp230xx_int_en) { + mcp230xx_int_prio_counter++; + if ((mcp230xx_int_prio_counter) >= (Settings.mcp230xx_int_prio)) { + MCP230xx_CheckForInterrupt(); + mcp230xx_int_prio_counter=0; + } + } + break; + case FUNC_EVERY_SECOND: + if (mcp230xx_int_counter_en) { + mcp230xx_int_sec_counter++; + if (mcp230xx_int_sec_counter >= Settings.mcp230xx_int_timer) { + MCP230xx_Interrupt_Counter_Report(); + } + } + if (tele_period == 0) { + if (mcp230xx_int_retainer_en) { + MCP230xx_Interrupt_Retain_Report(); + } + + + + + + } + break; +#ifdef USE_MCP230xx_OUTPUT + case FUNC_SET_POWER: + MCP230xx_SwitchRelay(); + break; +#endif + case FUNC_JSON_APPEND: + MCP230xx_Show(1); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_29 == XdrvMailbox.index) { + result = MCP230xx_Command(); + } + break; +#ifdef USE_WEBSERVER +#ifdef USE_MCP230xx_OUTPUT +#ifdef USE_MCP230xx_DISPLAYOUTPUT + case FUNC_WEB_SENSOR: + MCP230xx_UpdateWebData(); + break; +#endif +#endif +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +# 46 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +#ifdef USE_I2C +#ifdef USE_MPR121 + + + + + +#define XSNS_30 30 +#define XI2C_23 23 + + + + + + + +#define MPR121_ELEX_REG 0x00 + + +#define MPR121_MHDR_REG 0x2B + + +#define MPR121_MHDR_VAL 0x01 + + +#define MPR121_NHDR_REG 0x2C + + +#define MPR121_NHDR_VAL 0x01 + + +#define MPR121_NCLR_REG 0x2D + + +#define MPR121_NCLR_VAL 0x0E + + +#define MPR121_MHDF_REG 0x2F + + +#define MPR121_MHDF_VAL 0x01 + + +#define MPR121_NHDF_REG 0x30 + + +#define MPR121_NHDF_VAL 0x05 + + +#define MPR121_NCLF_REG 0x31 + + +#define MPR121_NCLF_VAL 0x01 + + +#define MPR121_MHDPROXR_REG 0x36 + + +#define MPR121_MHDPROXR_VAL 0x3F + + +#define MPR121_NHDPROXR_REG 0x37 + + +#define MPR121_NHDPROXR_VAL 0x5F + + +#define MPR121_NCLPROXR_REG 0x38 + + +#define MPR121_NCLPROXR_VAL 0x04 + + +#define MPR121_FDLPROXR_REG 0x39 + + +#define MPR121_FDLPROXR_VAL 0x00 + + +#define MPR121_MHDPROXF_REG 0x3A + + +#define MPR121_MHDPROXF_VAL 0x01 + + +#define MPR121_NHDPROXF_REG 0x3B + + +#define MPR121_NHDPROXF_VAL 0x01 + + +#define MPR121_NCLPROXF_REG 0x3C + + +#define MPR121_NCLPROXF_VAL 0x1F + + +#define MPR121_FDLPROXF_REG 0x3D + + +#define MPR121_FDLPROXF_VAL 0x04 + + +#define MPR121_E0TTH_REG 0x41 + + +#define MPR121_E0TTH_VAL 12 + + +#define MPR121_E0RTH_REG 0x42 + + +#define MPR121_E0RTH_VAL 6 + + +#define MPR121_CDT_REG 0x5D + + +#define MPR121_CDT_VAL 0x20 + + +#define MPR121_ECR_REG 0x5E + + +#define MPR121_ECR_VAL 0x8F + + + +#define MPR121_SRST_REG 0x80 + + +#define MPR121_SRST_VAL 0x63 + + +#define BITC(sensor,position) ((pS->current[sensor] >> position) & 1) + + +#define BITP(sensor,position) ((pS->previous[sensor] >> position) & 1) +# 195 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +typedef struct mpr121 mpr121; +struct mpr121 { + const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; + const char id[4] = { 'A', 'B', 'C', 'D' }; + bool connected[4] = { false, false, false, false }; + bool running[4] = { false, false, false, false }; + uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; + uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; +}; + +bool mpr21_found = false; +# 217 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Init(struct mpr121 *pS, bool initial) +{ + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + if (initial && I2cActive(pS->i2c_addr[i])) { continue; } + + + pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL) + && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D))); + if (pS->connected[i]) { + + + mpr21_found = true; + char device_name[16]; + snprintf_P(device_name, sizeof(device_name), PSTR("MPR121(%c)"), pS->id[i]); + I2cSetActiveFound(pS->i2c_addr[i], device_name); + + + for (uint32_t j = 0; j < 13; j++) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL); + } + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL); + + + pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); + + } else { + + + pS->running[i] = false; + } + } + + + if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] + || pS->connected[3])) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); + } +} +# 326 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Show(struct mpr121 *pS, uint8_t function) +{ + + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + + if (pS->connected[i]) { + + + if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); + Mpr121Init(pS, false); + return; + } + + if (BITC(i, 15)) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); + Mpr121Init(pS, false); + return; + } + } + + if (pS->running[i]) { + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR(",\"MPR121%c\":{"), pS->id[i]); + } + + for (uint32_t j = 0; j < 13; j++) { + + + if ((FUNC_EVERY_50_MSECOND == function) + && (BITC(i, j) != BITP(i, j))) { + Response_P(PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); + } + +#ifdef USE_WEBSERVER + if (FUNC_WEB_SENSOR == function) { + WSContentSend_PD(PSTR("{s}MPR121%c Button%d{m}%d{e}"), pS->id[i], j, BITC(i, j)); + } +#endif + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR("%s\"Button%i\":%i"), (j > 0 ? "," : ""), j, BITC(i, j)); + } + } + + + pS->previous[i] = pS->current[i]; + + + if (FUNC_JSON_APPEND == function) { + ResponseJsonEnd(); + } + } + } +} +# 410 "/workspace/Tasmota/tasmota/xsns_30_mpr121.ino" +bool Xsns30(uint8_t function) +{ + if (!I2cEnabled(XI2C_23)) { return false; } + + bool result = false; + + + static struct mpr121 mpr121; + + if (FUNC_INIT == function) { + + Mpr121Init(&mpr121, true); + } + else if (mpr21_found) { + + switch (function) { + + + case FUNC_EVERY_50_MSECOND: + Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND); + break; + + + case FUNC_JSON_APPEND: + Mpr121Show(&mpr121, FUNC_JSON_APPEND); + break; + +#ifdef USE_WEBSERVER + + case FUNC_WEB_SENSOR: + Mpr121Show(&mpr121, FUNC_WEB_SENSOR); + break; +#endif + } + } + + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_31_ccs811.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_31_ccs811.ino" +#ifdef USE_I2C +#ifdef USE_CCS811 +# 30 "/workspace/Tasmota/tasmota/xsns_31_ccs811.ino" +#define XSNS_31 31 +#define XI2C_24 24 + +#define EVERYNSECONDS 5 + +#include "Adafruit_CCS811.h" + +Adafruit_CCS811 ccs; +uint8_t CCS811_ready = 0; +uint8_t CCS811_type = 0;; +uint16_t eCO2; +uint16_t TVOC; +uint8_t tcnt = 0; +uint8_t ecnt = 0; + + + +void CCS811Detect(void) +{ + if (I2cActive(CCS811_ADDRESS)) { return; } + + if (!ccs.begin(CCS811_ADDRESS)) { + CCS811_type = 1; + I2cSetActiveFound(CCS811_ADDRESS, "CCS811"); + } +} + +void CCS811Update(void) +{ + tcnt++; + if (tcnt >= EVERYNSECONDS) { + tcnt = 0; + CCS811_ready = 0; + if (ccs.available()) { + if (!ccs.readData()){ + TVOC = ccs.getTVOC(); + eCO2 = ccs.geteCO2(); + CCS811_ready = 1; + if (global_update && (global_humidity > 0) && !isnan(global_temperature_celsius)) { + ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature_celsius); + } + ecnt = 0; + } + } else { + + ecnt++; + if (ecnt > 6) { + + ccs.begin(CCS811_ADDRESS); + } + } + } +} + +const char HTTP_SNS_CCS811[] PROGMEM = + "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +void CCS811Show(bool json) +{ + if (CCS811_ready) { + if (json) { + ResponseAppend_P(PSTR(",\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), eCO2,TVOC); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CCS811, eCO2, TVOC); +#endif + } + } +} + + + + + +bool Xsns31(uint8_t function) +{ + if (!I2cEnabled(XI2C_24)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + CCS811Detect(); + } + else if (CCS811_type) { + switch (function) { + case FUNC_EVERY_SECOND: + CCS811Update(); + break; + case FUNC_JSON_APPEND: + CCS811Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CCS811Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_32_mpu6050.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_32_mpu6050.ino" +#ifdef USE_I2C +#ifdef USE_MPU6050 +# 30 "/workspace/Tasmota/tasmota/xsns_32_mpu6050.ino" +#define XSNS_32 32 +#define XI2C_25 25 + +#define D_SENSOR_MPU6050 "MPU6050" + +#define MPU_6050_ADDR_AD0_LOW 0x68 +#define MPU_6050_ADDR_AD0_HIGH 0x69 + +uint8_t MPU_6050_address; +uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH }; +uint8_t MPU_6050_found; + +int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0; +int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0; +int16_t MPU_6050_temperature = 0; + +#ifdef USE_MPU6050_DMP + #include "MPU6050_6Axis_MotionApps20.h" + #include "I2Cdev.h" + #include + typedef struct MPU6050_DMP{ + uint8_t devStatus; + uint16_t packetSize; + uint16_t fifoCount; + uint8_t fifoBuffer[64]; + Quaternion q; + VectorInt16 aa; + VectorInt16 aaReal; + VectorFloat gravity; + float euler[3]; + float yawPitchRoll[3]; + } MPU6050_DMP; + + MPU6050_DMP MPU6050_dmp; +#else + #include +#endif +MPU6050 mpu6050; + +void MPU_6050PerformReading(void) +{ +#ifdef USE_MPU6050_DMP + mpu6050.resetFIFO(); + MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); + MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize; + + mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q); + mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); + mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); + mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); + MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; + MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; + MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; + MPU_6050_ax = MPU6050_dmp.aaReal.x; + MPU_6050_ay = MPU6050_dmp.aaReal.y; + MPU_6050_az = MPU6050_dmp.aaReal.z; +#else + mpu6050.getMotion6( + &MPU_6050_ax, + &MPU_6050_ay, + &MPU_6050_az, + &MPU_6050_gx, + &MPU_6050_gy, + &MPU_6050_gz + ); +#endif + MPU_6050_temperature = mpu6050.getTemperature(); +} +# 119 "/workspace/Tasmota/tasmota/xsns_32_mpu6050.ino" +void MPU_6050Detect(void) +{ + for (uint32_t i = 0; i < sizeof(MPU_6050_addresses); i++) + { + MPU_6050_address = MPU_6050_addresses[i]; + if (!I2cSetDevice(MPU_6050_address)) { break; } + mpu6050.setAddr(MPU_6050_addresses[i]); + +#ifdef USE_MPU6050_DMP + MPU6050_dmp.devStatus = mpu6050.dmpInitialize(); + mpu6050.setXGyroOffset(220); + mpu6050.setYGyroOffset(76); + mpu6050.setZGyroOffset(-85); + mpu6050.setZAccelOffset(1788); + if (MPU6050_dmp.devStatus == 0) { + mpu6050.setDMPEnabled(true); + MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); + MPU_6050_found = true; + } +#else + mpu6050.initialize(); + MPU_6050_found = mpu6050.testConnection(); +#endif + Settings.flag2.axis_resolution = 2; + } + + if (MPU_6050_found) { + I2cSetActiveFound(MPU_6050_address, D_SENSOR_MPU6050); + } +} + +#define D_YAW "Yaw" +#define D_PITCH "Pitch" +#define D_ROLL "Roll" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_AXIS[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; +#ifdef USE_MPU6050_DMP +const char HTTP_SNS_YPR[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; +#endif +#endif + +#define D_JSON_AXIS_AX "AccelXAxis" +#define D_JSON_AXIS_AY "AccelYAxis" +#define D_JSON_AXIS_AZ "AccelZAxis" +#define D_JSON_AXIS_GX "GyroXAxis" +#define D_JSON_AXIS_GY "GyroYAxis" +#define D_JSON_AXIS_GZ "GyroZAxis" +#define D_JSON_YAW "Yaw" +#define D_JSON_PITCH "Pitch" +#define D_JSON_ROLL "Roll" + +void MPU_6050Show(bool json) +{ + MPU_6050PerformReading(); + + float tempConv = ConvertTemp(MPU_6050_temperature / 340.0 + 35.53); + char temperature[33]; + dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); + char axis_ax[33]; + dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); + char axis_ay[33]; + dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); + char axis_az[33]; + dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); + char axis_gx[33]; + dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); + char axis_gy[33]; + dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); + char axis_gz[33]; + dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); +#ifdef USE_MPU6050_DMP + char axis_yaw[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); + char axis_pitch[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); + char axis_roll[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); +#endif + + if (json) { + char json_axis_ax[25]; + snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax); + char json_axis_ay[25]; + snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay); + char json_axis_az[25]; + snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az); + char json_axis_gx[25]; + snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx); + char json_axis_gy[25]; + snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); + char json_axis_gz[25]; + snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); +#ifdef USE_MPU6050_DMP + char json_ypr_y[25]; + snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); + char json_ypr_p[25]; + snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); + char json_ypr_r[25]; + snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, + json_ypr_y, json_ypr_p, json_ypr_r); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); +#endif +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); +#ifdef USE_MPU6050_DMP + WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); +#endif +#endif + } +} + + + + + +bool Xsns32(uint8_t function) +{ + if (!I2cEnabled(XI2C_25)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MPU_6050Detect(); + } + else if (MPU_6050_found) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == Settings.tele_period -3) { + MPU_6050PerformReading(); + } + break; + case FUNC_JSON_APPEND: + MPU_6050Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MPU_6050Show(0); + MPU_6050PerformReading(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_33_ds3231.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_33_ds3231.ino" +#ifdef USE_I2C +#ifdef USE_DS3231 +# 35 "/workspace/Tasmota/tasmota/xsns_33_ds3231.ino" +#define XSNS_33 33 +#define XI2C_26 26 + + +#ifndef USE_RTC_ADDR +#define USE_RTC_ADDR 0x68 +#endif + + +#define RTC_SECONDS 0x00 +#define RTC_MINUTES 0x01 +#define RTC_HOURS 0x02 +#define RTC_DAY 0x03 +#define RTC_DATE 0x04 +#define RTC_MONTH 0x05 +#define RTC_YEAR 0x06 +#define RTC_CONTROL 0x0E +#define RTC_STATUS 0x0F + +#define OSF 7 +#define EOSC 7 +#define BBSQW 6 +#define CONV 5 +#define RS2 4 +#define RS1 3 +#define INTCN 2 + + +#define HR1224 6 +#define CENTURY 7 +#define DYDT 6 +bool ds3231ReadStatus = false; +bool ds3231WriteStatus = false; +bool DS3231chipDetected = false; + + + + +void DS3231Detect(void) +{ + if (I2cActive(USE_RTC_ADDR)) { return; } + + if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { + I2cSetActiveFound(USE_RTC_ADDR, "DS3231"); + DS3231chipDetected = true; + } +} + + + + +uint8_t bcd2dec(uint8_t n) +{ + return n - 6 * (n >> 4); +} + + + + +uint8_t dec2bcd(uint8_t n) +{ + return n + 6 * (n / 10); +} + + + + +uint32_t ReadFromDS3231(void) +{ + TIME_T tm; + tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS)); + tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES)); + tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); + tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY); + tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE)); + tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); + tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR)); + return MakeTime(tm); +} + + + +void SetDS3231Time (uint32_t epoch_time) { + TIME_T tm; + BreakTime(epoch_time, tm); + I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second)); + I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute)); + I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour)); + I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week); + I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month)); + I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month)); + I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year)); + I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); +} + +void DS3231EverySecond(void) +{ + TIME_T tmpTime; + if (!ds3231ReadStatus && Rtc.utc_time < START_VALID_TIME ) { + ntp_force_sync = true; + Rtc.utc_time = ReadFromDS3231(); + + + BreakTime(Rtc.utc_time, tmpTime); + if (Rtc.utc_time < START_VALID_TIME ) { + ds3231ReadStatus = true; + } + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); + if (Rtc.local_time < START_VALID_TIME) { + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } + } + else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); + SetDS3231Time (Rtc.utc_time); + ds3231WriteStatus = true; + } +} + + + + + +bool Xsns33(uint8_t function) +{ + if (!I2cEnabled(XI2C_26)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + DS3231Detect(); + } + else if (DS3231chipDetected) { + switch (function) { + case FUNC_EVERY_SECOND: + DS3231EverySecond(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_34_hx711.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_34_hx711.ino" +#ifdef USE_HX711 +# 35 "/workspace/Tasmota/tasmota/xsns_34_hx711.ino" +#define XSNS_34 34 + +#ifndef HX_MAX_WEIGHT +#define HX_MAX_WEIGHT 20000 +#endif +#ifndef HX_REFERENCE +#define HX_REFERENCE 250 +#endif +#ifndef HX_SCALE +#define HX_SCALE 120 +#endif + +#define HX_TIMEOUT 120 +#define HX_SAMPLES 10 +#define HX_CAL_TIMEOUT 15 + +#define HX_GAIN_128 1 +#define HX_GAIN_32 2 +#define HX_GAIN_64 3 + +#define D_JSON_WEIGHT_REF "WeightRef" +#define D_JSON_WEIGHT_CAL "WeightCal" +#define D_JSON_WEIGHT_MAX "WeightMax" +#define D_JSON_WEIGHT_ITEM "WeightItem" +#define D_JSON_WEIGHT_CHANGE "WeightChange" +#define D_JSON_WEIGHT_RAW "WeightRaw" +#define D_JSON_WEIGHT_DELTA "WeightDelta" + +enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; + +const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; + +struct HX { + long weight = 0; + long raw = 0; + long last_weight = 0; + long sum_weight = 0; + long sum_raw = 0; + long offset = 0; + long scale = 1; + long weight_diff = 0; + uint8_t type = 1; + uint8_t sample_count = 0; + uint8_t calibrate_step = HX_CAL_END; + uint8_t calibrate_timer = 0; + uint8_t calibrate_msg = 0; + uint8_t pin_sck; + uint8_t pin_dout; + bool tare_flg = false; + bool weight_changed = false; + uint16_t weight_delta = 4; +} Hx; + + + +bool HxIsReady(uint16_t timeout) +{ + + uint32_t start = millis(); + while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } + return (digitalRead(Hx.pin_dout) == LOW); +} + +long HxRead(void) +{ + if (!HxIsReady(HX_TIMEOUT)) { return -1; } + + uint8_t data[3] = { 0 }; + uint8_t filler = 0x00; + + + data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + + + for (unsigned int i = 0; i < HX_GAIN_128; i++) { + digitalWrite(Hx.pin_sck, HIGH); + digitalWrite(Hx.pin_sck, LOW); + } + + + if (data[2] & 0x80) { filler = 0xFF; } + + + unsigned long value = ( static_cast(filler) << 24 + | static_cast(data[2]) << 16 + | static_cast(data[1]) << 8 + | static_cast(data[0]) ); + + return static_cast(value); +} + + + +void HxResetPart(void) +{ + Hx.tare_flg = true; + Hx.sum_weight = 0; + Hx.sample_count = 0; + Hx.last_weight = 0; +} + +void HxReset(void) +{ + HxResetPart(); + Settings.energy_frequency_calibration = 0; +} + +void HxCalibrationStateTextJson(uint8_t msg_id) +{ + char cal_text[30]; + + Hx.calibrate_msg = msg_id; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + + if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } +} + +void SetWeightDelta() +{ + + if (Settings.weight_change == 0) { + Hx.weight_delta = 4; + return; + } + + + if (Settings.weight_change > 100) { + Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; + return; + } + + + Hx.weight_delta = Settings.weight_change - 1; +} +# 192 "/workspace/Tasmota/tasmota/xsns_34_hx711.ino" +bool HxCommand(void) +{ + bool serviced = true; + bool show_parms = false; + char sub_string[XdrvMailbox.data_len +1]; + + for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { + if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } + } + + switch (XdrvMailbox.payload) { + case 1: + HxReset(); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, "Reset"); + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + Hx.scale = 1; + HxReset(); + Hx.calibrate_step = HX_CAL_START; + Hx.calibrate_timer = 1; + HxCalibrationStateTextJson(3); + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + show_parms = true; + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Hx.scale = Settings.weight_calibration; + } + show_parms = true; + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; + } + show_parms = true; + break; + case 6: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); + } + show_parms = true; + break; + case 7: + Settings.energy_frequency_calibration = Hx.weight; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); + break; + case 8: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; + } + show_parms = true; + break; + case 9: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + SetWeightDelta(); + } + show_parms = true; + break; + default: + show_parms = true; + } + + if (show_parms) { + char item[33]; + dtostrfd((float)Settings.weight_item / 10, 1, item); + Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" + D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), + Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, + item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); + } + + return serviced; +} + + + +long HxWeight(void) +{ + return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; +} + +void HxInit(void) +{ + Hx.type = 0; + if (PinUsed(GPIO_HX711_DAT) && PinUsed(GPIO_HX711_SCK)) { + Hx.pin_sck = Pin(GPIO_HX711_SCK); + Hx.pin_dout = Pin(GPIO_HX711_DAT); + + pinMode(Hx.pin_sck, OUTPUT); + pinMode(Hx.pin_dout, INPUT); + + digitalWrite(Hx.pin_sck, LOW); + + SetWeightDelta(); + + if (HxIsReady(8 * HX_TIMEOUT)) { + if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } + if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } + if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } + Hx.scale = Settings.weight_calibration; + HxRead(); + HxResetPart(); + Hx.type = 1; + } + } +} + +void HxEvery100mSecond(void) +{ + long raw = HxRead(); + Hx.sum_raw += raw; + Hx.sum_weight += raw; + + Hx.sample_count++; + if (HX_SAMPLES == Hx.sample_count) { + long average = Hx.sum_weight / Hx.sample_count; + long raw_average = Hx.sum_raw / Hx.sample_count; + long value = average - Hx.offset; + Hx.weight = value / Hx.scale; + Hx.raw = raw_average / Hx.scale; + if (Hx.weight < 0) { + if (Settings.energy_frequency_calibration) { + long difference = Settings.energy_frequency_calibration + Hx.weight; + Hx.last_weight = difference; + if (difference < 0) { HxReset(); } + } + Hx.weight = 0; + } else { + Hx.last_weight = Settings.energy_frequency_calibration; + } + + if (Hx.tare_flg) { + Hx.tare_flg = false; + Hx.offset = average; + } + + if (Hx.calibrate_step) { + Hx.calibrate_timer--; + + if (HX_CAL_START == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + } + else if (HX_CAL_RESET == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight < (long)Settings.weight_reference) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + HxCalibrationStateTextJson(2); + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_FIRST == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step--; + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_DONE == Hx.calibrate_step) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step = HX_CAL_FINISH; + Settings.weight_calibration = Hx.weight / Settings.weight_reference; + Hx.weight = 0; + HxCalibrationStateTextJson(1); + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + + if (HX_CAL_FAIL == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.tare_flg = true; + HxCalibrationStateTextJson(0); + } + if (HX_CAL_FINISH == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); + Hx.scale = Settings.weight_calibration; + } + + if (!Hx.calibrate_timer) { + Hx.calibrate_step = HX_CAL_END; + } + } else { + Hx.weight += Hx.last_weight; + + if (Settings.SensorBits1.hx711_json_weight_change) { + if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { + Hx.weight_diff = Hx.weight; + Hx.weight_changed = true; + } + else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { + mqtt_data[0] = '\0'; + ResponseAppendTime(); + HxShow(true); + ResponseJsonEnd(); + MqttPublishTeleSensor(); + Hx.weight_changed = false; + } + } + } + + Hx.sum_weight = 0; + Hx.sum_raw = 0; + Hx.sample_count = 0; + } +} + +void HxSaveBeforeRestart(void) +{ + Settings.energy_frequency_calibration = Hx.weight; + Hx.sample_count = HX_SAMPLES +1; +} + +#ifdef USE_WEBSERVER +const char HTTP_HX711_WEIGHT[] PROGMEM = + "{s}HX711 " D_WEIGHT "{m}%s " D_UNIT_KILOGRAM "{e}"; +const char HTTP_HX711_COUNT[] PROGMEM = + "{s}HX711 " D_COUNT "{m}%d{e}"; +const char HTTP_HX711_CAL[] PROGMEM = + "{s}HX711 %s{m}{e}"; +#endif + +void HxShow(bool json) +{ + char scount[30] = { 0 }; + + uint16_t count = 0; + float weight = 0; + if (Hx.calibrate_step < HX_CAL_FAIL) { + if (Hx.weight && Settings.weight_item) { + count = (Hx.weight * 10) / Settings.weight_item; + if (count > 1) { + snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); + } + } + weight = (float)Hx.weight / 1000; + } + char weight_chr[33]; + dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); + if (count > 1) { + WSContentSend_PD(HTTP_HX711_COUNT, count); + } + if (Hx.calibrate_step) { + char cal_text[30]; + WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + } +#endif + } +} + +#ifdef USE_WEBSERVER +#ifdef USE_HX711_GUI + + + + +#define WEB_HANDLE_HX711 "s34" + +const char S_CONFIGURE_HX711[] PROGMEM = D_CONFIGURE_HX711; + +const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = + "

"; + +const char HTTP_BTN_MENU_HX711[] PROGMEM = + "

"; + +const char HTTP_FORM_HX711[] PROGMEM = + "
 " D_CALIBRATION " " + "
" + "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" + "
" + "
" + "


" + + "
 " D_HX711_PARAMETERS " " + "
" + "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; + +void HandleHxAction(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); + + if (Webserver->hasArg("save")) { + HxSaveSettings(); + HandleConfiguration(); + return; + } + + char stemp1[20]; + + if (Webserver->hasArg("reset")) { + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + if (Webserver->hasArg("calibrate")) { + WebGetArg("p1", stemp1, sizeof(stemp1)); + Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); + + HxLogUpdates(); + + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + WSContentStart_P(S_CONFIGURE_HX711); + WSContentSendStyle(); + dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); + char stemp2[20]; + dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); + WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void HxSaveSettings(void) +{ + char tmp[100]; + + WebGetArg("p2", tmp, sizeof(tmp)); + Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); + + HxLogUpdates(); +} + +void HxLogUpdates(void) +{ + char weigth_ref_chr[33]; + dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); + char weigth_item_chr[33]; + dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); +} + +#endif +#endif + + + + + +bool Xsns34(uint8_t function) +{ + bool result = false; + + if (Hx.type) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + HxEvery100mSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_34 == XdrvMailbox.index) { + result = HxCommand(); + } + break; + case FUNC_JSON_APPEND: + HxShow(1); + break; + case FUNC_SAVE_BEFORE_RESTART: + HxSaveBeforeRestart(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HxShow(0); + break; +#ifdef USE_HX711_GUI + case FUNC_WEB_ADD_MAIN_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); + break; + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_HX711); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_HX711), HandleHxAction); + break; +#endif +#endif + case FUNC_INIT: + HxInit(); + break; + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_35_tx20.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_35_tx20.ino" +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) +# 51 "/workspace/Tasmota/tasmota/xsns_35_tx20.ino" +#define XSNS_35 35 + +#if defined(USE_TX20_WIND_SENSOR) && defined(USE_TX23_WIND_SENSOR) +#undef USE_TX20_WIND_SENSOR +#warning **** use USE_TX20_WIND_SENSOR or USE_TX23_WIND_SENSOR but not both together, TX20 disabled **** +#endif + + +#define TX2X_BIT_TIME 1220 +#define TX2X_WEIGHT_AVG_SAMPLE 150 +#define TX2X_TIMEOUT 10 +#define TX23_READ_INTERVAL 4 + + + +extern "C" { +#include "gpio.h" +} + +#ifdef USE_TX20_WIND_SENSOR +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX20" +#else +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX23" +#endif + +#ifdef USE_WEBSERVER +#define D_TX20_WIND_AVG "∅" +#define D_TX20_WIND_ANGLE "∠" +#define D_TX20_WIND_DEGREE "°" +const char HTTP_SNS_TX2X[] PROGMEM = + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" +#endif + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s" D_TX20_WIND_DEGREE "{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %s" D_TX20_WIND_DEGREE "{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%s" D_TX20_WIND_DEGREE " (%s,%s)" D_TX20_WIND_DEGREE; +#endif + ; +#endif + + +float const tx2x_f_pi = 3.1415926535897932384626433; +float const tx2x_f_halfpi = tx2x_f_pi / 2.0; +float const tx2x_f_pi180 = tx2x_f_pi / 180.0; + +#define TX2X_DIRECTIONS_MAXSIZE 3 +const char kTx2xDirections[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +int32_t tx2x_wind_speed = 0; +int32_t tx2x_wind_direction = 0; + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +int32_t tx2x_wind_speed_min = 0xfff; +int32_t tx2x_wind_speed_max = 0; +float tx2x_wind_speed_avg = 0; +float tx2x_wind_direction_avg_x = 0; +float tx2x_wind_direction_avg_y = 0; +float tx2x_wind_direction_avg = 0; +int32_t tx2x_wind_direction_min = 0; +int32_t tx2x_wind_direction_max = 0; + +uint32_t tx2x_count = 0; +uint32_t tx2x_avg_samples; +uint32_t tx2x_last_uptime = 0; +bool tx2x_valuesread = false; +#endif + +#ifdef DEBUG_TASMOTA_SENSOR +uint32_t tx2x_sa = 0; +uint32_t tx2x_sb = 0; +uint32_t tx2x_sc = 0; +uint32_t tx2x_sd = 0; +uint32_t tx2x_se = 0; +uint32_t tx2x_sf = 0; +#endif +uint32_t tx2x_last_available = 0; + +#ifdef USE_TX23_WIND_SENSOR +uint32_t tx23_stage = 0; +#endif + +void ICACHE_RAM_ATTR TX2xStartRead(void) +{ +# 180 "/workspace/Tasmota/tasmota/xsns_35_tx20.ino" +#ifdef USE_TX23_WIND_SENSOR + if (0!=tx23_stage) + { + if ((2==tx23_stage) || (3==tx23_stage)) + { +#endif +#ifdef DEBUG_TASMOTA_SENSOR + tx2x_sa = 0; + tx2x_sb = 0; + tx2x_sc = 0; + tx2x_sd = 0; + tx2x_se = 0; + tx2x_sf = 0; +#else + uint32_t tx2x_sa = 0; + uint32_t tx2x_sb = 0; + uint32_t tx2x_sc = 0; + uint32_t tx2x_sd = 0; + uint32_t tx2x_se = 0; + uint32_t tx2x_sf = 0; +#endif + + delayMicroseconds(TX2X_BIT_TIME / 2); + + for (int32_t bitcount = 41; bitcount > 0; bitcount--) { + uint32_t dpin = (digitalRead(Pin(GPIO_TX2X_TXD_BLACK))); +#ifdef USE_TX23_WIND_SENSOR + dpin ^= 1; +#endif + if (bitcount > 41 - 5) { + + tx2x_sa = (tx2x_sa << 1) | (dpin ^ 1); + } else if (bitcount > 41 - 5 - 4) { + + tx2x_sb = tx2x_sb >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12) { + + tx2x_sc = tx2x_sc >> 1 | ((dpin ^ 1) << 11); + } else if (bitcount > 41 - 5 - 4 - 12 - 4) { + + tx2x_sd = tx2x_sd >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) { + + tx2x_se = tx2x_se >> 1 | (dpin << 3); + } else { + + tx2x_sf = tx2x_sf >> 1 | (dpin << 11); + } + delayMicroseconds(TX2X_BIT_TIME); + } + + uint32_t chk = (tx2x_sb + (tx2x_sc & 0xf) + ((tx2x_sc >> 4) & 0xf) + ((tx2x_sc >> 8) & 0xf)); + chk &= 0xf; + + + ; +#ifdef USE_TX23_WIND_SENSOR + if ((chk == tx2x_sd) && (0x1b==tx2x_sa) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { +#else + if ((chk == tx2x_sd) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { +#endif + tx2x_last_available = uptime; + + tx2x_wind_speed = tx2x_sc; + tx2x_wind_direction = tx2x_sb; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + if (!tx2x_valuesread) { + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; + tx2x_valuesread = true; + } +#endif + } + +#ifdef USE_TX23_WIND_SENSOR + } + tx23_stage++; + } +#endif + + + + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << Pin(GPIO_TX2X_TXD_BLACK)); +} + +bool Tx2xAvailable(void) +{ + return ((uptime - tx2x_last_available) < TX2X_TIMEOUT); +} + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +float atan2f(float a, float b) +{ + float atan2val; + if (b > 0) { + atan2val = atanf(a/b); + } else if ((b < 0) && (a >= 0)) { + atan2val = atanf(a/b) + tx2x_f_pi; + } else if ((b < 0) && (a < 0)) { + atan2val = atanf(a/b) - tx2x_f_pi; + } else if ((b == 0) && (a > 0)) { + atan2val = tx2x_f_halfpi; + } else if ((b == 0) && (a < 0)) { + atan2val = 0 - (tx2x_f_halfpi); + } else if ((b == 0) && (a == 0)) { + atan2val = 1000; + } + return atan2val; +} + +void Tx2xCheckSampleCount(void) +{ + uint32_t tx2x_prev_avg_samples = tx2x_avg_samples; + if (Settings.tele_period) { + + tx2x_avg_samples = Settings.tele_period; + } else { + + tx2x_avg_samples = TX2X_WEIGHT_AVG_SAMPLE; + } + if (tx2x_prev_avg_samples != tx2x_avg_samples) { + tx2x_wind_speed_avg = tx2x_wind_speed; + tx2x_count = 0; + } +} + +void Tx2xResetStat(void) +{ + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": reset statistics")); + tx2x_last_uptime = uptime; + Tx2xResetStatData(); +} + +void Tx2xResetStatData(void) +{ + tx2x_wind_speed_min = tx2x_wind_speed; + tx2x_wind_speed_max = tx2x_wind_speed; + + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; +} +#endif + +void Tx2xRead(void) +{ +#ifdef USE_TX23_WIND_SENSOR + + + + + + + + if ((uptime % TX23_READ_INTERVAL)==0) { + + + tx23_stage = 0; + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); + } else if ((uptime % TX23_READ_INTERVAL)==1) { + + + tx23_stage = 1; + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT_PULLUP); + } +#endif + if (Tx2xAvailable()) { +#ifdef DEBUG_TASMOTA_SENSOR + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": sa=0x%02lx sb=%ld (0x%02lx), sc=%ld (0x%03lx), sd=0x%02lx, se=%ld, sf=%ld"), tx2x_sa,tx2x_sb,tx2x_sb,tx2x_sc,tx2x_sc,tx2x_sd,tx2x_se,tx2x_sf); +#endif +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + if (tx2x_wind_speed < tx2x_wind_speed_min) { + tx2x_wind_speed_min = tx2x_wind_speed; + } + if (tx2x_wind_speed > tx2x_wind_speed_max) { + tx2x_wind_speed_max = tx2x_wind_speed; + } + + + + + if (tx2x_count <= tx2x_avg_samples) { + tx2x_count++; + } + tx2x_wind_speed_avg -= tx2x_wind_speed_avg / tx2x_count; + tx2x_wind_speed_avg += float(tx2x_wind_speed) / tx2x_count; + + tx2x_wind_direction_avg_x -= tx2x_wind_direction_avg_x / tx2x_count; + tx2x_wind_direction_avg_x += cosf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; + tx2x_wind_direction_avg_y -= tx2x_wind_direction_avg_y / tx2x_count; + tx2x_wind_direction_avg_y += sinf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; + tx2x_wind_direction_avg = atan2f(tx2x_wind_direction_avg_y, tx2x_wind_direction_avg_x) * 180.0f / tx2x_f_pi; + if (tx2x_wind_direction_avg<0.0) { + tx2x_wind_direction_avg += 360.0; + } + if (tx2x_wind_direction_avg>360.0) { + tx2x_wind_direction_avg -= 360.0; + } + + int32_t tx2x_wind_direction_avg_int = int((tx2x_wind_direction_avg/22.5)+0.5) % 16; + + + if (tx2x_wind_direction > tx2x_wind_direction_avg_int) { + + if ((tx2x_wind_direction-tx2x_wind_direction_avg_int)>8) { + + if ((tx2x_wind_direction - 16) < tx2x_wind_direction_min) { + + tx2x_wind_direction_min = tx2x_wind_direction - 16; + } + } else { + + if (tx2x_wind_direction > tx2x_wind_direction_max) { + + tx2x_wind_direction_max = tx2x_wind_direction; + } + } + } else { + + if ((tx2x_wind_direction_avg_int-tx2x_wind_direction)>8) { + + if ((tx2x_wind_direction + 16) > tx2x_wind_direction_max) { + + tx2x_wind_direction_max = tx2x_wind_direction + 16; + } + } else { + + if (tx2x_wind_direction < tx2x_wind_direction_min) { + + tx2x_wind_direction_min = tx2x_wind_direction; + } + } + } + +#ifdef DEBUG_TASMOTA_SENSOR + char diravg[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg, 1, diravg); + char cosx[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg_x, 1, cosx); + char siny[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg_y, 1, siny); + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%s (cosx=%s, siny=%s), min %d, max %d"), + (uptime-tx2x_last_uptime), + tx2x_wind_direction, + tx2x_wind_direction_avg_int, + diravg, + cosx, + siny, + tx2x_wind_direction_min, + tx2x_wind_direction_max + ); +#endif +#endif + } else { + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": not available")); + tx2x_wind_speed = 0; + tx2x_wind_direction = 0; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_wind_speed_avg = 0; + tx2x_wind_direction_avg = 0; + Tx2xResetStatData(); +#endif + } + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + Tx2xCheckSampleCount(); + if (0==Settings.tele_period && (uptime-tx2x_last_uptime)>=tx2x_avg_samples) { + Tx2xResetStat(); + } +#endif +} + +void Tx2xInit(void) +{ + if (!Settings.flag2.speed_conversion) { + Settings.flag2.speed_conversion = 2; + } +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_valuesread = false; + Tx2xResetStat(); + Tx2xCheckSampleCount(); +#endif +#ifdef USE_TX23_WIND_SENSOR + tx23_stage = 0; + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); +#else + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT); +#endif + attachInterrupt(Pin(GPIO_TX2X_TXD_BLACK), TX2xStartRead, RISING); +} + +int32_t Tx2xNormalize(int32_t value) +{ + while (value>15) { + value -= 16; + } + while (value<0) { + value += 16; + } + return value; +} + +void Tx2xShow(bool json) +{ + if (!Tx2xAvailable()) { return; } + + char wind_speed_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed)/10, 1, wind_speed_string); + char wind_direction_string[FLOATSZ]; + dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_string); + char wind_direction_cardinal_string[TX2X_DIRECTIONS_MAXSIZE+1]; + GetTextIndexed(wind_direction_cardinal_string, sizeof(wind_direction_cardinal_string), tx2x_wind_direction, kTx2xDirections); +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + char wind_speed_min_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_min)/10, 1, wind_speed_min_string); + char wind_speed_max_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_max)/10, 1, wind_speed_max_string); + char wind_speed_avg_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_avg)/10, 1, wind_speed_avg_string); + char wind_direction_avg_string[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg, 1, wind_direction_avg_string); + char wind_direction_avg_cardinal_string[4]; + GetTextIndexed(wind_direction_avg_cardinal_string, sizeof(wind_direction_avg_cardinal_string), int((tx2x_wind_direction_avg/22.5f)+0.5f) % 16, kTx2xDirections); + char wind_direction_range_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max-tx2x_wind_direction_min)*22.5, 1, wind_direction_range_string); + char wind_direction_min_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_min)*22.5, 1, wind_direction_min_string); + char wind_direction_max_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max)*22.5, 1, wind_direction_max_string); +#endif + + if (json) { +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +#ifdef USE_TX2x_LEGACY_JSON + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), + wind_speed_string, + wind_speed_avg_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string + ); +#else + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), + wind_speed_string, + wind_speed_avg_string, + wind_speed_min_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string, + wind_direction_avg_string, + wind_direction_avg_cardinal_string, + wind_direction_min_string, + wind_direction_max_string, + wind_direction_range_string + ); +#endif +#else +#ifdef USE_TX2x_LEGACY_JSON + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#else + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#endif +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TX2X, + wind_speed_string, + SpeedUnit().c_str(), +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + wind_speed_avg_string, + SpeedUnit().c_str(), + wind_speed_min_string, + SpeedUnit().c_str(), + wind_speed_max_string, + SpeedUnit().c_str(), +#endif + wind_direction_cardinal_string, + wind_direction_string +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + ,wind_direction_avg_cardinal_string, + wind_direction_avg_string, + wind_direction_range_string, + wind_direction_min_string, + wind_direction_max_string +#endif + ); +#endif + } +} + + + + + +bool Xsns35(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_TX2X_TXD_BLACK)) { + switch (function) { + case FUNC_INIT: + Tx2xInit(); + break; + case FUNC_EVERY_SECOND: + Tx2xRead(); + break; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + Tx2xResetStat(); + break; +#endif + case FUNC_JSON_APPEND: + Tx2xShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tx2xShow(false); + break; +#endif + + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_36_mgc3130.ino" +# 22 "/workspace/Tasmota/tasmota/xsns_36_mgc3130.ino" +#ifdef USE_I2C +#ifdef USE_MGC3130 +# 35 "/workspace/Tasmota/tasmota/xsns_36_mgc3130.ino" +#define XSNS_36 36 +#define XI2C_27 27 + +#warning **** MGC3130: It is recommended to disable all unneeded I2C-drivers **** + +#define MGC3130_I2C_ADDR 0x42 + +uint8_t MGC3130_xfer = 0; +uint8_t MGC3130_reset = 0; +bool MGC3130_type = false; +char MGC3130stype[] = "MGC3130"; + +#define MGC3130_SYSTEM_STATUS 0x15 +#define MGC3130_REQUEST_MSG 0x06 +#define MGC3130_FW_VERSION 0x83 +#define MGC3130_SET_RUNTIME 0xA2 +#define MGC3130_SENSOR_DATA 0x91 + + +#define MGC3130_GESTURE_GARBAGE 1 +#define MGC3130_FLICK_WEST_EAST 2 +#define MGC3130_FLICK_EAST_WEST 3 +#define MGC3130_FLICK_SOUTH_NORTH 4 +#define MGC3130_FLICK_NORTH_SOUTH 5 +#define MGC3130_CIRCLE_CLOCKWISE 6 +#define MGC3130_CIRCLE_CCLOCKWISE 7 + +#define MGC3130_MIN_ROTVALUE 0 +#define MGC3130_MAX_ROTVALUE 1023 +#define MGC3130_MIN_ZVALUE 32768 + + +#ifdef USE_WEBSERVER +const char HTTP_MGC_3130_SNS[] PROGMEM = + "{s}" "%s" "{m}%s{e}" + "{s}" "HwRev" "{m}%u.%u{e}" + "{s}" "loaderVer" "{m}%u.%u{e}" + "{s}" "platVer" "{m}%u{e}"; +#endif + + + + + + + +#pragma pack(1) +union MGC3130_Union{ + uint8_t buffer[132]; + struct + { + + uint8_t msgSize; + uint8_t flag; + uint8_t counter; + uint8_t id; + + struct { + uint8_t DSPStatus:1; + uint8_t gestureInfo:1; + uint8_t touchInfo:1; + uint8_t airWheelInfo:1; + uint8_t xyzPosition:1; + uint8_t noisePower:1; + uint8_t reserved:2; + uint8_t electrodeConfiguration:3; + uint8_t CICData:1; + uint8_t SDData:1; + uint16_t reserved2:3; + } outputConfigMask; + uint8_t timestamp; + struct { + uint8_t positionValid:1; + uint8_t airWheelValid:1; + uint8_t rawDataValid:1; + uint8_t noisePowerValid:1; + uint8_t environmentalNoise:1; + uint8_t clipping:1; + uint8_t reserved:1; + uint8_t DSPRunning:1; + } systemInfo; + uint16_t dspInfo; + struct { + uint8_t gestureCode:8; + uint8_t reserved:4; + uint8_t gestureType:4; + uint8_t edgeFlick:1; + uint16_t reserved2:14; + uint8_t gestureInProgress:1; + } gestureInfo; + struct { + uint8_t touchSouth:1; + uint8_t touchWest:1; + uint8_t touchNorth:1; + uint8_t touchEast:1; + uint8_t touchCentre:1; + uint8_t tapSouth:1; + uint8_t tapWest:1; + uint8_t tapNorth:1; + uint8_t tapEast :1; + uint8_t tapCentre:1; + uint8_t doubleTapSouth:1; + uint8_t doubleTapWest:1; + uint8_t doubleTapNorth:1; + uint8_t doubleTapEast:1; + uint8_t doubleTapCentre:1; + uint8_t reserved:1; + uint8_t touchCounter; + uint8_t reserved2; + } touchInfo; + int8_t airWheel; + uint8_t reserved; + uint16_t x; + uint16_t y; + uint16_t z; + float noisePower; + float CICData[4]; + float SDData[4]; + } out; + struct { + uint8_t header[3]; + + uint8_t valid; + uint8_t hwRev[2]; + uint8_t parameterStartAddr; + uint8_t loaderVersion[2]; + uint8_t loaderPlatform; + uint8_t fwStartAddr; + char fwVersion[120]; + } fw; + struct{ + uint8_t id; + uint8_t size; + uint16_t error; + uint32_t reserved; + uint32_t reserved1; + } status; +} MGC_data; +#pragma pack() + +char MGC3130_currentGesture[12]; + +int8_t MGC3130_delta, MGC3130_lastrotation = 0; +int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; + +uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; + +uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; +char MGC3130_firmwareInfo[20]; + +uint8_t MGC3130_touchTimeout = 0; +uint16_t MGC3130_touchCounter = 1; +uint32_t MGC3130_touchTimeStamp = millis(); +bool MGC3130_triggeredByTouch = false; + +uint8_t MGC3130_mode = 1; + + + +uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; +uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; +uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + +void MGC3130_handleSensorData(){ + if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ + if (MGC3130_handleTouch()){ + MGC3130_triggeredByTouch = true; + MqttPublishSensor(); + } + } + + if(MGC3130_mode == 1){ + if( MGC_data.out.outputConfigMask.gestureInfo && MGC_data.out.gestureInfo.gestureCode > 0){ + MGC3130_handleGesture(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 2){ + if(MGC_data.out.outputConfigMask.airWheelInfo && MGC_data.out.systemInfo.airWheelValid){ + MGC3130_handleAirWheel(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 3){ + if(MGC_data.out.systemInfo.positionValid && (MGC_data.out.z > MGC3130_MIN_ZVALUE)){ + MqttPublishSensor(); + } + } +} + +void MGC3130_sendMessage(uint8_t data[], uint8_t length){ + Wire.beginTransmission(MGC3130_I2C_ADDR); + Wire.write(data,length); + Wire.endTransmission(); + delay(2); + MGC3130_receiveMessage(); +} + + +void MGC3130_handleGesture(){ + + char edge[5]; + if (MGC_data.out.gestureInfo.edgeFlick){ + snprintf_P(edge, sizeof(edge), PSTR("ED_")); + } + else{ + snprintf_P(edge, sizeof(edge), PSTR("")); + } + switch(MGC_data.out.gestureInfo.gestureCode){ + case MGC3130_GESTURE_GARBAGE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); + break; + case MGC3130_FLICK_WEST_EAST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); + break; + case MGC3130_FLICK_EAST_WEST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); + break; + case MGC3130_FLICK_SOUTH_NORTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); + break; + case MGC3130_FLICK_NORTH_SOUTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); + break; + case MGC3130_CIRCLE_CLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); + break; + case MGC3130_CIRCLE_CCLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); + break; + } + +} + +bool MGC3130_handleTouch(){ + + bool success = false; + if (MGC_data.out.touchInfo.doubleTapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + if (MGC_data.out.touchInfo.tapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.touchCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); + success = true; + MGC3130_touchCounter++; + } + + return success; +} + +void MGC3130_handleAirWheel(){ + MGC3130_delta = MGC_data.out.airWheel - MGC3130_lastrotation; + MGC3130_lastrotation = MGC_data.out.airWheel; + + MGC3130_rotValue = MGC3130_rotValue + MGC3130_delta; + if(MGC3130_rotValue < MGC3130_MIN_ROTVALUE){ + MGC3130_rotValue = MGC3130_MIN_ROTVALUE; + } + if(MGC3130_rotValue > MGC3130_MAX_ROTVALUE){ + MGC3130_rotValue = MGC3130_MAX_ROTVALUE; + } +} + +void MGC3130_handleSystemStatus(){ + +} + +bool MGC3130_receiveMessage(){ + if(MGC3130_readData()){ + switch(MGC_data.out.id){ + case MGC3130_SENSOR_DATA: + MGC3130_handleSensorData(); + break; + case MGC3130_SYSTEM_STATUS: + MGC3130_handleSystemStatus(); + break; + case MGC3130_FW_VERSION: + hwRev[0] = MGC_data.fw.hwRev[1]; + hwRev[1] = MGC_data.fw.hwRev[0]; + loaderVersion[0] = MGC_data.fw.loaderVersion[0]; + loaderVersion[1] = MGC_data.fw.loaderVersion[1]; + loaderPlatform = MGC_data.fw.loaderPlatform; + snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); + MGC3130_firmwareInfo[20] = '\0'; + + break; + } + return true; + } + return false; +} + +bool MGC3130_readData() +{ + bool success = false; + if (!digitalRead(MGC3130_xfer)){ + pinMode(MGC3130_xfer, OUTPUT); + digitalWrite(MGC3130_xfer, LOW); + Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); + + MGC_data.buffer[0] = 4; + unsigned char i = 0; + while(Wire.available() && (i < MGC_data.buffer[0])){ + MGC_data.buffer[i] = Wire.read(); + i++; + } + digitalWrite(MGC3130_xfer, HIGH); + pinMode(MGC3130_xfer, INPUT); + success = true; + } + return success; +} + +void MGC3130_nextMode(){ + if (MGC3130_mode < 3){ + MGC3130_mode++; + } + else{ + MGC3130_mode = 1; + } + switch(MGC3130_mode){ + case 1: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } +} + +void MGC3130_loop() +{ + if(MGC3130_touchTimeout > 0){ + MGC3130_touchTimeout--; + } + MGC3130_receiveMessage(); +} + +void MGC3130_detect(void) +{ + if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } + + MGC3130_xfer = Pin(GPIO_MGC3130_XFER); + MGC3130_reset = Pin(GPIO_MGC3130_RESET); + + pinMode(MGC3130_xfer, INPUT_PULLUP); + pinMode(MGC3130_reset, OUTPUT); + digitalWrite(MGC3130_reset, LOW); + delay(10); + digitalWrite(MGC3130_reset, HIGH); + delay(50); + + if (MGC3130_receiveMessage()) { + I2cSetActiveFound(MGC3130_I2C_ADDR, MGC3130stype); + MGC3130_currentGesture[0] = '\0'; + MGC3130_type = true; + } +} + + + + + +void MGC3130_show(bool json) +{ + if (!MGC3130_type) { return; } + + char status_chr[2]; + if (MGC_data.out.systemInfo.DSPRunning) { + sprintf (status_chr, "1"); + } + else{ + sprintf (status_chr, "0"); + } + + if (json) { + if (MGC3130_mode == 3 && !MGC3130_triggeredByTouch) { + if (MGC_data.out.systemInfo.positionValid && !(MGC_data.out.x == MGC3130_lastSentX && MGC_data.out.y == MGC3130_lastSentY && MGC_data.out.z == MGC3130_lastSentZ)) { + ResponseAppend_P(PSTR(",\"%s\":{\"X\":%u,\"Y\":%u,\"Z\":%u}"), + MGC3130stype, MGC_data.out.x/64, MGC_data.out.y/64, (MGC_data.out.z-(uint16_t)MGC3130_MIN_ZVALUE)/64); + MGC3130_lastSentX = MGC_data.out.x; + MGC3130_lastSentY = MGC_data.out.y; + MGC3130_lastSentZ = MGC_data.out.z; + } + } + MGC3130_triggeredByTouch = false; + + if (MGC3130_mode == 2) { + if (MGC_data.out.systemInfo.airWheelValid && (MGC3130_rotValue != MGC3130_lastSentRotValue)) { + ResponseAppend_P(PSTR(",\"%s\":{\"AW\":%i}"), MGC3130stype, MGC3130_rotValue); + MGC3130_lastSentRotValue = MGC3130_rotValue; + } + } + + if (MGC3130_currentGesture[0] != '\0') { + if (millis() - MGC3130_touchTimeStamp > 220 ) { + MGC3130_touchCounter = 1; + } + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), MGC3130stype, MGC3130_currentGesture, MGC3130_touchCounter); + MGC3130_currentGesture[0] = '\0'; + MGC3130_touchTimeStamp = millis(); + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); +#endif + } +} +# 557 "/workspace/Tasmota/tasmota/xsns_36_mgc3130.ino" +bool MGC3130CommandSensor() +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + MGC3130_nextMode(); + break; + case 1: + MGC3130_mode = 1; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_mode = 2; + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_mode = 3; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } + return serviced; +} + + + + + +bool Xsns36(uint8_t function) +{ + if (!I2cEnabled(XI2C_27)) { return false; } + + bool result = false; + + if ((FUNC_INIT == function) && PinUsed(GPIO_MGC3130_XFER) && PinUsed(GPIO_MGC3130_RESET)) { + MGC3130_detect(); + } + else if (MGC3130_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + MGC3130_loop(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_36 == XdrvMailbox.index) { + result = MGC3130CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MGC3130_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGC3130_show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_37_rfsensor.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_37_rfsensor.ino" +#ifdef USE_RF_SENSOR +# 33 "/workspace/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define XSNS_37 37 + + + + +#define RFSNS_VALID_WINDOW 1800 + +#define RFSNS_LOOPS_PER_MILLI 1900 +#define RFSNS_RAW_BUFFER_SIZE 180 +#define RFSNS_MIN_RAW_PULSES 112 + +#define RFSNS_MIN_PULSE_LENGTH 300 +#define RFSNS_RAWSIGNAL_SAMPLE 50 +#define RFSNS_SIGNAL_TIMEOUT 10 +#define RFSNS_SIGNAL_REPEAT_TIME 500 + +typedef struct RawSignalStruct +{ + int Number; + uint8_t Repeats; + uint8_t Multiply; + unsigned long Time; + uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; + +} raw_signal_t; + +raw_signal_t *rfsns_raw_signal = nullptr; +uint8_t rfsns_rf_bit; +uint8_t rfsns_rf_port; +uint8_t rfsns_any_sensor = 0; + + + + + +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) +{ + uint8_t Fbit = digitalPinToBitMask(DataPin); + uint8_t Fport = digitalPinToPort(DataPin); + uint8_t FstateMask = (StateSignal ? Fbit : 0); + + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + const unsigned long LoopsPerMilli = RFSNS_LOOPS_PER_MILLI; + + + + + + + unsigned long PulseLength = 0; + if (rfsns_raw_signal->Time) { + if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + } + } + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); + } + } + + int RawCodeLength = 1; + bool Ftoggle = false; + unsigned long numloops = 0; + unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; + rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; + do { + numloops = 0; + while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { + if (numloops++ == maxloops) { break; } + } + PulseLength = (numloops *1000) / LoopsPerMilli; + if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } + Ftoggle = !Ftoggle; + rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; + } + while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); + + if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { + rfsns_raw_signal->Repeats = 0; + rfsns_raw_signal->Number = RawCodeLength -1; + rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; + rfsns_raw_signal->Time = millis(); + return true; + } + else + rfsns_raw_signal->Number = 0; + } + + return false; +} + +#ifdef USE_THEO_V2 +# 149 "/workspace/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_THEOV2_MAX_CHANNEL 2 + +#define RFSNS_THEOV2_PULSECOUNT 114 +#define RFSNS_THEOV2_RF_PULSE_MID 1000 + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t lux; + uint8_t volt; +} theo_v2_t1_t; + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t hum; + uint8_t volt; +} theo_v2_t2_t; + +theo_v2_t1_t *rfsns_theo_v2_t1 = nullptr; +theo_v2_t2_t *rfsns_theo_v2_t2 = nullptr; + +void RfSnsInitTheoV2(void) +{ + rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); + rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeTheov2(void) +{ + if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } + + uint8_t Checksum; + uint8_t Channel; + uint8_t Type; + uint8_t Voltage; + int Payload1; + int Payload2; + + uint8_t b, bytes, bits, id; + + uint8_t idx = 3; + uint8_t chksum = 0; + for (bytes = 0; bytes < 7; bytes++) { + b = 0; + for (bits = 0; bits <= 7; bits++) + { + if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { + b |= 1 << bits; + } + idx += 2; + } + if (bytes > 0) { chksum += b; } + + switch (bytes) { + case 0: + Checksum = b; + break; + case 1: + id = b; + Channel = b & 0x7; + Type = (b >> 3) & 0x1f; + break; + case 2: + Voltage = b; + break; + case 3: + Payload1 = b; + break; + case 4: + Payload1 = (b << 8) | Payload1; + break; + case 5: + Payload2 = b; + break; + case 6: + Payload2 = (b << 8) | Payload2; + break; + } + } + + if (Checksum != chksum) { return; } + if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } + Channel--; + + rfsns_raw_signal->Repeats = 1; + + int Payload3 = Voltage & 0x3f; + + switch (Type) { + case 1: + rfsns_theo_v2_t1[Channel].time = LocalTime(); + rfsns_theo_v2_t1[Channel].volt = Payload3; + rfsns_theo_v2_t1[Channel].temp = Payload1; + rfsns_theo_v2_t1[Channel].lux = Payload2; + break; + case 2: + rfsns_theo_v2_t2[Channel].time = LocalTime(); + rfsns_theo_v2_t2[Channel].volt = Payload3; + rfsns_theo_v2_t2[Channel].temp = Payload1; + rfsns_theo_v2_t2[Channel].hum = Payload2; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), + chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); +} + +void RfSnsTheoV2Show(bool json) +{ + bool sensor_once = false; + + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t1[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED "\":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); + } + } else { + char temperature[33]; + dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), + sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && !sensor_once) { + DomoticzSensor(DZ_TEMP, temperature); + DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); + sensor_once = true; + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); +#endif + } + } + } + } + + sensor_once = false; + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t2[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED" \":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t2[i].time).c_str(), voltage); + } + } else { + float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); + float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), sensor); + ResponseAppendTHD(temp, humi); + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s}"), voltage); + + if ((0 == tele_period) && !sensor_once) { +#ifdef USE_DOMOTICZ + DomoticzTempHumPressureSensor(temp, humi); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, temp); + KnxSensor(KNX_HUMIDITY, humi); +#endif + sensor_once = true; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD(sensor, temp, humi); +#endif + } + } + } + } +} + +#endif + +#ifdef USE_ALECTO_V2 +# 389 "/workspace/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_DKW2012_PULSECOUNT 176 +#define RFSNS_ACH2010_MIN_PULSECOUNT 160 +#define RFSNS_ACH2010_MAX_PULSECOUNT 160 + +#define D_ALECTOV2 "AlectoV2" + +const char kAlectoV2Directions[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +typedef struct { + uint32_t time; + float temp; + float rain; + float wind; + float gust; + uint8_t type; + uint8_t humi; + uint8_t wdir; +} alecto_v2_t; + +alecto_v2_t *rfsns_alecto_v2 = nullptr; +uint16_t rfsns_alecto_rain_base = 0; + +void RfSnsInitAlectoV2(void) +{ + rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeAlectov2() +{ + if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && + (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } + + uint8_t c = 0; + uint8_t rfbit; + uint8_t data[9] = { 0 }; + uint8_t msgtype = 0; + uint8_t rc = 0; + int temp; + uint8_t checksum = 0; + uint8_t checksumcalc = 0; + uint8_t maxidx = 8; + unsigned long atime; + float factor; + char buf1[16]; + + if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } + + uint8_t idx = maxidx; + for (uint32_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { + if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { + rfbit = 0x80; + } else { + rfbit = 0; + } + data[idx] = (data[idx] >> 1) | rfbit; + c++; + if (c == 8) { + if (idx == 0) { break; } + c = 0; + idx--; + } + } + + checksum = data[maxidx]; + checksumcalc = RfSnsAlectoCRC8(data, maxidx); + + msgtype = (data[0] >> 4) & 0xf; + rc = (data[0] << 4) | (data[1] >> 4); + + if (checksum != checksumcalc) { return; } + if ((msgtype != 10) && (msgtype != 5)) { return; } + + rfsns_raw_signal->Repeats = 1; + + + + + + factor = 1.22; + + + + + + rfsns_alecto_v2->time = LocalTime(); + rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); + rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; + rfsns_alecto_v2->humi = data[3]; + uint16_t rain = (data[6] * 256) + data[7]; + + if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } + if (rfsns_alecto_rain_base > 0) { + rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; + } + rfsns_alecto_rain_base = rain; + rfsns_alecto_v2->wind = (float)data[4] * factor; + rfsns_alecto_v2->gust = (float)data[5] * factor; + if (rfsns_alecto_v2->type) { + rfsns_alecto_v2->wdir = data[8] & 0xf; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), + checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); +} + +void RfSnsAlectoResetRain(void) +{ + if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { + rfsns_alecto_v2->rain = 0; + } +} + + + + + + + +uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x80; + crc <<= 1; + if (mix) { crc ^= 0x31; } + inbyte <<= 1; + } + } + return crc; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_ALECTOV2[] PROGMEM = + "{s}" D_ALECTOV2 " " D_RAIN "{m}%s " D_UNIT_MILLIMETER "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"; +const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = + "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; +#endif + +void RfSnsAlectoV2Show(bool json) +{ + if (rfsns_alecto_v2->time) { + if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), GetDT(rfsns_alecto_v2->time).c_str()); + } + } else { + float temp = ConvertTemp(rfsns_alecto_v2->temp); + float humi = ConvertHumidity((float)rfsns_alecto_v2->humi); + + char rain[33]; + dtostrfd(rfsns_alecto_v2->rain, 2, rain); + char wind[33]; + dtostrfd(rfsns_alecto_v2->wind, 2, wind); + char gust[33]; + dtostrfd(rfsns_alecto_v2->gust, 2, gust); + char wdir[4]; + char direction[20]; + if (rfsns_alecto_v2->type) { + GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); + snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); + } + + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{")); + ResponseAppendTHD(temp, humi); + ResponseAppend_P(PSTR(",\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); + + if (0 == tele_period) { +#ifdef USE_DOMOTICZ + + + + +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD(D_ALECTOV2, temp, humi); + WSContentSend_PD(HTTP_SNS_ALECTOV2, rain, wind, gust); + if (rfsns_alecto_v2->type) { + WSContentSend_PD(HTTP_SNS_ALECTOV2_WDIR, wdir); + } +#endif + } + } + } +} +#endif + +void RfSnsInit(void) +{ + rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); + if (rfsns_raw_signal) { + memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); +#ifdef USE_THEO_V2 + RfSnsInitTheoV2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsInitAlectoV2(); +#endif + if (rfsns_any_sensor) { + rfsns_rf_bit = digitalPinToBitMask(Pin(GPIO_RF_SENSOR)); + rfsns_rf_port = digitalPinToPort(Pin(GPIO_RF_SENSOR)); + pinMode(Pin(GPIO_RF_SENSOR), INPUT); + } else { + free(rfsns_raw_signal); + rfsns_raw_signal = nullptr; + } + } +} + +void RfSnsAnalyzeRawSignal(void) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); + +#ifdef USE_THEO_V2 + RfSnsAnalyzeTheov2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAnalyzeAlectov2(); +#endif +} + +void RfSnsEverySecond(void) +{ +#ifdef USE_ALECTO_V2 + RfSnsAlectoResetRain(); +#endif +} + +void RfSnsShow(bool json) +{ +#ifdef USE_THEO_V2 + RfSnsTheoV2Show(json); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAlectoV2Show(json); +#endif +} + + + + + +bool Xsns37(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_RF_SENSOR) && (FUNC_INIT == function)) { + RfSnsInit(); + } + else if (rfsns_raw_signal) { + switch (function) { + case FUNC_LOOP: + if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { + if (RfSnsFetchSignal(Pin(GPIO_RF_SENSOR), HIGH)) { + RfSnsAnalyzeRawSignal(); + } + } + ssleep = 0; + break; + case FUNC_EVERY_SECOND: + RfSnsEverySecond(); + break; + case FUNC_JSON_APPEND: + RfSnsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RfSnsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_38_az7798.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_38_az7798.ino" +#ifdef USE_AZ7798 + +#define XSNS_38 38 +# 112 "/workspace/Tasmota/tasmota/xsns_38_az7798.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define AZ_READ_TIMEOUT 400 + +#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) +#define AZ_EPOCH (946684800UL) + +TasmotaSerial *AzSerial; + +const char ktype[] = "AZ7798"; +uint8_t az_type = 1; +uint16_t az_co2 = 0; +double az_temperature = 0; +double az_humidity = 0; +uint8_t az_received = 0; +uint8_t az_state = 0; +unsigned long az_clock_update = 10; + + + +void AzEverySecond(void) +{ + unsigned long start = millis(); + + az_state++; + if (5 == az_state) { + az_state = 0; + + AzSerial->flush(); + AzSerial->write(":\r", 2); + az_received = 0; + + uint8_t az_response[32]; + uint8_t counter = 0; + uint8_t i, j; + uint8_t response_substr[16]; + + do { + if (AzSerial->available() > 0) { + az_response[counter] = AzSerial->read(); + if(az_response[counter] == 0x0d) { az_received = 1; } + counter++; + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); + + if (!az_received) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); + return; + } + + i = 0; + while((az_response[i] != 'T') && (i < counter)) {i++;} + if(az_response[i] != 'T') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if((az_response[i] != 'C') && (az_response[i] != 'F')){ + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); + return; + } + response_substr[j] = 0; + az_temperature = CharToFloat((char*)response_substr); + if(az_response[i] == 'C') { + az_temperature = ConvertTemp((float)az_temperature); + } else { + az_temperature = ConvertTemp((az_temperature - 32) / 1.8); + } + i++; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); + return; + } + i++; + if(az_response[i] != 'C') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'p') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != 'p') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); + return; + } + response_substr[j] = 0; + az_co2 = atoi((char*)response_substr); +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); +#endif + i += 3; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); + return; + } + i++; + if(az_response[i] != 'H') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); + return; + } + i++; + j = 0; + + while((az_response[i] != '%') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != '%') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); + return; + } + response_substr[j] = 0; + az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); + } + + + if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { + char tmpString[16]; + sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); + AzSerial->write(tmpString); + + do { + if (AzSerial->available() > 0) { + if(AzSerial->read() == 0x0d) { break; } + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT)); + az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); + } else { + az_clock_update--; + } +} + + + +void AzInit(void) +{ + az_type = 0; + if (PinUsed(GPIO_AZ_RXD) && PinUsed(GPIO_AZ_TXD)) { + AzSerial = new TasmotaSerial(Pin(GPIO_AZ_RXD), Pin(GPIO_AZ_TXD), 1); + if (AzSerial->begin(9600)) { + if (AzSerial->hardwareSerial()) { ClaimSerial(); } + az_type = 1; + } + } +} + +void AzShow(bool json) +{ + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d,"), ktype, az_co2); + ResponseAppendTHD(az_temperature, az_humidity); + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, ktype, az_co2); + WSContentSend_THD(ktype, az_temperature, az_humidity); +#endif + } +} + + + + + +bool Xsns38(uint8_t function) +{ + bool result = false; + + if(az_type){ + switch (function) { + case FUNC_INIT: + AzInit(); + break; + case FUNC_EVERY_SECOND: + AzEverySecond(); + break; + case FUNC_JSON_APPEND: + AzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_39_max31855.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_39_max31855.ino" +#ifdef USE_MAX31855 + + + + + + + +#define XSNS_39 39 + +const char kMax31855Types[] PROGMEM = "MAX31855|MAX6675"; + +bool max31855_initialized = false; + +struct MAX31855_ResultStruct { + uint8_t ErrorCode; + float ProbeTemperature; + float ReferenceTemperature; +} MAX31855_Result; + +void MAX31855_Init(void) { + if (PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)) { + + + pinMode(Pin(GPIO_MAX31855CS), OUTPUT); + pinMode(Pin(GPIO_MAX31855CLK), OUTPUT); + pinMode(Pin(GPIO_MAX31855DO), INPUT); + + + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); + + max31855_initialized = true; + } +} + + + + + +int32_t MAX31855_ShiftIn(uint8_t Length) { + int32_t dataIn = 0; + + digitalWrite(Pin(GPIO_MAX31855CS), LOW); + delayMicroseconds(1); + + for (uint32_t i = 0; i < Length; i++) { + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); + delayMicroseconds(1); + dataIn <<= 1; + if (digitalRead(Pin(GPIO_MAX31855DO))) { + dataIn |= 1; + } + digitalWrite(Pin(GPIO_MAX31855CLK), HIGH); + delayMicroseconds(1); + } + + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); + return dataIn; +} + + + + + +float MAX31855_GetProbeTemperature(int32_t RawData) { + if (RawData & 0x80000000) { + RawData = (RawData >> 18) | 0xFFFFC000; + } else { + RawData >>= 18; + } + float result = (RawData * 0.25); + + return ConvertTemp(result); +} + + + + + +float MAX31855_GetReferenceTemperature(int32_t RawData) { + if (RawData & 0x8000) { + RawData = (RawData >> 4) | 0xFFFFF000; + } else { + RawData = (RawData >> 4) & 0x00000FFF; + } + float result = (RawData * 0.0625); + + return ConvertTemp(result); +} + + + + + +void MAX31855_GetResult(void) { + if (Settings.flag4.max6675) { + int32_t RawData = MAX31855_ShiftIn(16); + int32_t temp = (RawData >> 3) & ((1 << 12) - 1); + + + if (temp == ((1 << 12) - 1)) { return; } + + MAX31855_Result.ErrorCode = 0; + MAX31855_Result.ReferenceTemperature = NAN; + MAX31855_Result.ProbeTemperature = ConvertTemp(0.25 * temp); + } else { + int32_t RawData = MAX31855_ShiftIn(32); + uint8_t probeerror = RawData & 0x7; + + MAX31855_Result.ErrorCode = probeerror; + MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); + if (probeerror) { + MAX31855_Result.ProbeTemperature = NAN; + } else { + MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); + } + } +} + +void MAX31855_Show(bool Json) { + char probetemp[33]; + char referencetemp[33]; + dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); + dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); + + char sensor_name[10]; + GetTextIndexed(sensor_name, sizeof(sensor_name), Settings.flag4.max6675, kMax31855Types); + + if (Json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + sensor_name, probetemp, referencetemp, MAX31855_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, probetemp); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, probetemp, TempUnit()); +#endif + } +} + + + + + +bool Xsns39(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + MAX31855_Init(); + } + else if (max31855_initialized) { + switch (function) { + case FUNC_EVERY_SECOND: + MAX31855_GetResult(); + break; + case FUNC_JSON_APPEND: + MAX31855_Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31855_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_40_pn532.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_40_pn532.ino" +#ifdef USE_PN532_HSU + +#define XSNS_40 40 + +#include + +TasmotaSerial *PN532_Serial; + +#define PN532_INVALID_ACK -1 +#define PN532_TIMEOUT -2 +#define PN532_INVALID_FRAME -3 +#define PN532_NO_SPACE -4 + +#define PN532_PREAMBLE 0x00 +#define PN532_STARTCODE1 0x00 +#define PN532_STARTCODE2 0xFF +#define PN532_POSTAMBLE 0x00 + +#define PN532_HOSTTOPN532 0xD4 +#define PN532_PN532TOHOST 0xD5 + +#define PN532_ACK_WAIT_TIME 0x0A + +#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 +#define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_RFCONFIGURATION 0x32 +#define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A + +#define PN532_MIFARE_ISO14443A 0x00 +#define MIFARE_CMD_READ 0x30 +#define MIFARE_CMD_AUTH_A 0x60 +#define MIFARE_CMD_AUTH_B 0x61 +#define MIFARE_CMD_WRITE 0xA0 + +uint8_t pn532_model = 0; +uint8_t pn532_command = 0; +uint8_t pn532_scantimer = 0; + +uint8_t pn532_packetbuffer[64]; + +#ifdef USE_PN532_DATA_FUNCTION +uint8_t pn532_function = 0; +uint8_t pn532_newdata[16]; +uint8_t pn532_newdata_len = 0; +#endif + +void PN532_Init(void) +{ + if (PinUsed(GPIO_PN532_RXD) && PinUsed(GPIO_PN532_TXD)) { + PN532_Serial = new TasmotaSerial(Pin(GPIO_PN532_RXD), Pin(GPIO_PN532_TXD), 1); + if (PN532_Serial->begin(115200)) { + if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } + PN532_wakeup(); + uint32_t ver = PN532_getFirmwareVersion(); + if (ver) { + PN532_setPassiveActivationRetries(0xFF); + PN532_SAMConfig(); + pn532_model = 1; + AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); + } + } + } +} + +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) +{ + int read_bytes = 0; + int ret; + unsigned long start_millis; + while (read_bytes < len) { + start_millis = millis(); + do { + ret = PN532_Serial->read(); + if (ret >= 0) { + break; + } + } while((timeout == 0) || ((millis()- start_millis ) < timeout)); + + if (ret < 0) { + if (read_bytes) { + return read_bytes; + } else { + return PN532_TIMEOUT; + } + } + buf[read_bytes] = (uint8_t)ret; + read_bytes++; + } + return read_bytes; +} + +int8_t PN532_readAckFrame(void) +{ + const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; + uint8_t ackBuf[sizeof(PN532_ACK)]; + + if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { + return PN532_TIMEOUT; + } + + if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { + return PN532_INVALID_ACK; + } + return 0; +} + +int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) +{ + + PN532_Serial->flush(); + + pn532_command = header[0]; + PN532_Serial->write((uint8_t)PN532_PREAMBLE); + PN532_Serial->write((uint8_t)PN532_STARTCODE1); + PN532_Serial->write(PN532_STARTCODE2); + + uint8_t length = hlen + blen + 1; + PN532_Serial->write(length); + PN532_Serial->write(~length + 1); + + PN532_Serial->write(PN532_HOSTTOPN532); + uint8_t sum = PN532_HOSTTOPN532; + + PN532_Serial->write(header, hlen); + for (uint32_t i = 0; i < hlen; i++) { + sum += header[i]; + } + + PN532_Serial->write(body, blen); + for (uint32_t i = 0; i < blen; i++) { + sum += body[i]; + } + + uint8_t checksum = ~sum + 1; + PN532_Serial->write(checksum); + PN532_Serial->write((uint8_t)PN532_POSTAMBLE); + + return PN532_readAckFrame(); +} + +int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) +{ + uint8_t tmp[3]; + + + if (PN532_receive(tmp, 3, timeout)<=0) { + return PN532_TIMEOUT; + } + if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { + return PN532_INVALID_FRAME; + } + + + uint8_t length[2]; + if (PN532_receive(length, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + + if (0 != (uint8_t)(length[0] + length[1])) { + return PN532_INVALID_FRAME; + } + length[0] -= 2; + if (length[0] > len) { + return PN532_NO_SPACE; + } + + + uint8_t cmd = pn532_command + 1; + if (PN532_receive(tmp, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { + return PN532_INVALID_FRAME; + } + + if (PN532_receive(buf, length[0], timeout) != length[0]) { + return PN532_TIMEOUT; + } + + uint8_t sum = PN532_PN532TOHOST + cmd; + for (uint32_t i=0; i status) { + return 0; + } + + response = pn532_packetbuffer[0]; + response <<= 8; + response |= pn532_packetbuffer[1]; + response <<= 8; + response |= pn532_packetbuffer[2]; + response <<= 8; + response |= pn532_packetbuffer[3]; + + return response; +} + +void PN532_wakeup(void) +{ + uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; + PN532_Serial->write(wakeup,sizeof(wakeup)); + + + PN532_Serial->flush(); +} + +bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = cardbaudrate; + if (PN532_writeCommand(pn532_packetbuffer, 3)) { + return 0x0; + } + + if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { + return 0x0; + } +# 274 "/workspace/Tasmota/tasmota/xsns_40_pn532.ino" + if (pn532_packetbuffer[0] != 1) { + return 0; + } + + uint16_t sens_res = pn532_packetbuffer[2]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[3]; + + + *uidLength = pn532_packetbuffer[5]; + + for (uint32_t i = 0; i < pn532_packetbuffer[5]; i++) { + uid[i] = pn532_packetbuffer[6 + i]; + } + + return 1; +} + +bool PN532_setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; + pn532_packetbuffer[2] = 0xFF; + pn532_packetbuffer[3] = 0x01; + pn532_packetbuffer[4] = maxRetries; + if (PN532_writeCommand(pn532_packetbuffer, 5)) { + return 0; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +bool PN532_SAMConfig(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; + pn532_packetbuffer[2] = 0x14; + pn532_packetbuffer[3] = 0x00; + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return false; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#ifdef USE_PN532_DATA_FUNCTION + +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) +{ + uint8_t i; + uint8_t _key[6]; + uint8_t _uid[7]; + uint8_t _uidLen; + + + memcpy(&_key, keyData, 6); + memcpy(&_uid, uid, uidLen); + _uidLen = uidLen; + + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], &_key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; + } + + if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + + + if (pn532_packetbuffer[0] != 0x00) { + + return 0; + } + + return 1; +} + +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_READ; + pn532_packetbuffer[3] = blockNumber; + + + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return 0; + } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + if (pn532_packetbuffer[0] != 0x00) { + return 0; + } + + + + memcpy (data, &pn532_packetbuffer[1], 16); + + return 1; +} + +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], data, 16); + + + if (PN532_writeCommand(pn532_packetbuffer, 20)) { + return 0; + } + + + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#endif + +void PN532_ScanForTag(void) +{ + if (!pn532_model) { return; } + uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; + uint8_t uid_len = 0; + uint8_t card_data[16]; + bool erase_success = false; + bool set_success = false; + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { + char uids[15]; + +#ifdef USE_PN532_DATA_FUNCTION + char card_datas[34]; +#endif + + ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); + +#ifdef USE_PN532_DATA_FUNCTION + if (uid_len == 4) { + uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { + if (mifareclassic_ReadDataBlock(1, card_data)) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_datas,&card_data,sizeof(card_data)); +#else + for (uint32_t i = 0;i < sizeof(card_data);i++) { + if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { + card_datas[i] = char(card_data[i]); + } else { + card_datas[i] = '\0'; + } + } +#endif + } + if (pn532_function == 1) { + for (uint32_t i = 0;i<16;i++) { + card_data[i] = 0x00; + } + if (mifareclassic_WriteDataBlock(1, card_data)) { + erase_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } + if (pn532_function == 2) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_data,&pn532_newdata,sizeof(card_data)); + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } +#else + bool IsAlphaNumeric = true; + for (uint32_t i = 0;i < pn532_newdata_len;i++) { + if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { + IsAlphaNumeric = false; + } + } + if (IsAlphaNumeric) { + memcpy(&card_data,&pn532_newdata,pn532_newdata_len); + card_data[pn532_newdata_len] = '\0'; + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); + } +#endif + } + } else { + sprintf(card_datas,"AUTHFAIL"); + } + } + switch (pn532_function) { + case 0x01: + if (!erase_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + } + break; + case 0x02: + if (!set_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + } + default: + break; + } + pn532_function = 0; +#endif + +#ifdef USE_PN532_DATA_FUNCTION + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"" D_JSON_DATA "\":\"%s\"}}"), uids, card_datas); +#else + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); +#endif + + MqttPublishTeleSensor(); + +#ifdef USE_PN532_CAUSE_EVENTS + + char command[71]; +#ifdef USE_PN532_DATA_FUNCTION + sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); +#else + sprintf(command,"event PN532_UID=%s",uids); +#endif + ExecuteCommand(command, SRC_RULE); +#endif + + pn532_scantimer = 7; + } +} + +#ifdef USE_PN532_DATA_FUNCTION + +bool PN532_Command(void) +{ + bool serviced = true; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + char sub_string_tmp[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { + serviced = false; + return serviced; + } + sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); + pn532_newdata_len = strlen(sub_string_tmp); + if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } + memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); + pn532_newdata[pn532_newdata_len] = 0x00; + pn532_function = 2; + AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); + return serviced; + } + } + return false; +} + +#endif + +bool Xsns40(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + PN532_Init(); + result = true; + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_EVERY_100_MSECOND: + break; + case FUNC_EVERY_250_MSECOND: + if (pn532_scantimer > 0) { + pn532_scantimer--; + } else { + PN532_ScanForTag(); + } + break; + case FUNC_EVERY_SECOND: + break; +#ifdef USE_PN532_DATA_FUNCTION + case FUNC_COMMAND_SENSOR: + if (XSNS_40 == XdrvMailbox.index) { + result = PN532_Command(); + } + break; +#endif + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_41_max44009.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_41_max44009.ino" +#ifdef USE_I2C +#ifdef USE_MAX44009 + + + + + + +#define XSNS_41 41 +#define XI2C_28 28 + +#define MAX44009_ADDR1 0x4A +#define MAX44009_ADDR2 0x4B +#define MAX44009_NO_REGISTERS 8 +#define REG_CONFIG 0x02 +#define REG_LUMINANCE 0x03 +#define REG_LOWER_THRESHOLD 0x06 +#define REG_THRESHOLD_TIMER 0x07 + +#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 + +uint8_t max44009_address; +uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; +uint8_t max44009_found = 0; +uint8_t max44009_valid = 0; +float max44009_illuminance = 0; +char max44009_types[] = "MAX44009"; + +bool Max4409Read_lum(void) +{ + max44009_valid = 0; + uint8_t regdata[2]; + + + if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { + int exponent = (regdata[0] & 0xF0) >> 4; + int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); + max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); + max44009_valid = 1; + return true; + } else { + return false; + } +} + + + +void Max4409Detect(void) +{ + uint8_t buffer1; + uint8_t buffer2; + for (uint32_t i = 0; 0 != max44009_addresses[i]; i++) { + + max44009_address = max44009_addresses[i]; + if (I2cActive(max44009_address)) { continue; } + + if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && + (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { + + if ((0x00 == buffer1) && + (0xFF == buffer2)) { + + + + Wire.beginTransmission(max44009_address); + + + Wire.write(REG_CONFIG); + Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); + if (0 == Wire.endTransmission()) { + I2cSetActiveFound(max44009_address, max44009_types); + max44009_found = 1; + break; + } + } + } + } +} + +void Max4409EverySecond(void) +{ + Max4409Read_lum(); +} + +void Max4409Show(bool json) +{ + char illum_str[8]; + + if (max44009_valid) { + + + + uint8_t prec = 0; + if (10 > max44009_illuminance ) { + prec = 3; + } else if (100 > max44009_illuminance) { + prec = 2; + } else if (1000 > max44009_illuminance) { + prec = 1; + } + dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), max44009_types, illum_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, illum_str); + } +#endif +#ifdef USE_WEBSERVER + } else { + + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, max44009_types, (int)max44009_illuminance); +#endif + } + } +} + + + + + +bool Xsns41(uint8_t function) +{ + if (!I2cEnabled(XI2C_28)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Max4409Detect(); + } + else if (max44009_found) { + switch (function) { + case FUNC_EVERY_SECOND: + Max4409EverySecond(); + break; + case FUNC_JSON_APPEND: + Max4409Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Max4409Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_42_scd30.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_42_scd30.ino" +#ifdef USE_I2C +#ifdef USE_SCD30 + +#define XSNS_42 42 +#define XI2C_29 29 + + + +#define SCD30_ADDRESS 0x61 + +#define SCD30_MAX_MISSED_READS 3 +#define SCD30_STATE_NO_ERROR 0 +#define SCD30_STATE_ERROR_DATA_CRC 1 +#define SCD30_STATE_ERROR_READ_MEAS 2 +#define SCD30_STATE_ERROR_SOFT_RESET 3 +#define SCD30_STATE_ERROR_I2C_RESET 4 +#define SCD30_STATE_ERROR_UNKNOWN 5 + +#include "Arduino.h" +#include + +#define D_CMND_SCD30 "SCD30" + +const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; +const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; +const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; +const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; + + + + + +enum SCD30_Commands { + CMND_SCD30_ALTITUDE, + CMND_SCD30_AUTOMODE, + CMND_SCD30_CALIBRATE, + CMND_SCD30_FW, + CMND_SCD30_INTERVAL, + CMND_SCD30_PRESSURE, + CMND_SCD30_TEMPOFFSET +}; + +FrogmoreScd30 scd30; + +bool scd30Found = false; +bool scd30IsDataValid = false; +int scd30ErrorState = SCD30_STATE_NO_ERROR; +uint16_t scd30Interval_sec; +int scd30Loop_count = 0; +int scd30DataNotAvailable_count = 0; +int scd30GoodMeas_count = 0; +int scd30Reset_count = 0; +int scd30CrcError_count = 0; +int scd30Co2Zero_count = 0; +int i2cReset_count = 0; +uint16_t scd30_CO2 = 0; +uint16_t scd30_CO2EAvg = 0; +float scd30_Humid = 0.0; +float scd30_Temp = 0.0; + +void Scd30Detect(void) +{ + if (I2cActive(SCD30_ADDRESS)) { return; } + + scd30.begin(); + + uint8_t major = 0; + uint8_t minor = 0; + if (scd30.getFirmwareVersion(&major, &minor)) { return; } + uint16_t interval_sec; + if (scd30.getMeasurementInterval(&scd30Interval_sec)) { return; } + if (scd30.beginMeasuring()) { return; } + + I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); + scd30Found = true; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); +} + + +void Scd30Update(void) +{ + scd30Loop_count++; + if (scd30Loop_count > (scd30Interval_sec - 1)) { + uint32_t error = 0; + switch (scd30ErrorState) { + case SCD30_STATE_NO_ERROR: { + error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); + switch (error) { + case ERROR_SCD30_NO_ERROR: + scd30Loop_count = 0; + scd30IsDataValid = true; + scd30GoodMeas_count++; + break; + + case ERROR_SCD30_NO_DATA: + scd30DataNotAvailable_count++; + break; + + case ERROR_SCD30_CRC_ERROR: + scd30ErrorState = SCD30_STATE_ERROR_DATA_CRC; + scd30CrcError_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + case ERROR_SCD30_CO2_ZERO: + scd30Co2Zero_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + default: { + scd30ErrorState = SCD30_STATE_ERROR_READ_MEAS; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld"), error, scd30Loop_count); +#endif + return; + } + break; + } + } + break; + + case SCD30_STATE_ERROR_DATA_CRC: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: got CRC error, try again, counter: %ld"), scd30Loop_count); +#endif + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + break; + + case SCD30_STATE_ERROR_READ_MEAS: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: not answering, sending soft reset, counter: %ld"), scd30Loop_count); +#endif + scd30Reset_count++; + error = scd30.softReset(); + if (error) { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: resetting got error: 0x%lX"), error); +#endif + error >>= 8; + if (error == 4) { + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } else { + scd30ErrorState = SCD30_STATE_ERROR_UNKNOWN; + } + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + case SCD30_STATE_ERROR_SOFT_RESET: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P(LOG_LEVEL_ERROR, PSTR("SCD30: clearing i2c bus")); +#endif + i2cReset_count++; + error = scd30.clearI2CBus(); + if (error) { + scd30ErrorState = SCD30_STATE_ERROR_I2C_RESET; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error clearing i2c bus: 0x%lX"), error); +#endif + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + default: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: unknown error state: 0x%lX"), scd30ErrorState); +#endif + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } + } + + if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) { + scd30IsDataValid = false; + } + } +} + + +int Scd30GetCommand(int command_code, uint16_t *pvalue) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.getAltitudeCompensation(pvalue); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.getCalibrationType(pvalue); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.getForcedRecalibrationFactor(pvalue); + break; + + case CMND_SCD30_INTERVAL: + return scd30.getMeasurementInterval(pvalue); + break; + + case CMND_SCD30_PRESSURE: + return scd30.getAmbientPressure(pvalue); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.getTemperatureOffset(pvalue); + break; + + default: + + break; + } + return 0; +} + +int Scd30SetCommand(int command_code, uint16_t value) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.setAltitudeCompensation(value); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.setCalibrationType(value); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.setForcedRecalibrationFactor(value); + break; + + case CMND_SCD30_INTERVAL: + { + int error = scd30.setMeasurementInterval(value); + if (!error) + { + scd30Interval_sec = value; + } + + return error; + } + break; + + case CMND_SCD30_PRESSURE: + return scd30.setAmbientPressure(value); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.setTemperatureOffset(value); + break; + + default: + + break; + } + return 0; +} + + + + + +bool Scd30CommandSensor() +{ + char command[CMDSZ]; + bool serviced = true; + uint8_t prefix_len = strlen(D_CMND_SCD30); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); + + switch (command_code) { + case CMND_SCD30_ALTITUDE: + case CMND_SCD30_AUTOMODE: + case CMND_SCD30_CALIBRATE: + case CMND_SCD30_INTERVAL: + case CMND_SCD30_PRESSURE: + case CMND_SCD30_TEMPOFFSET: + { + uint16_t value = 0; + if (XdrvMailbox.data_len > 0) + { + value = XdrvMailbox.payload; + Scd30SetCommand(command_code, value); + } + else + { + Scd30GetCommand(command_code, &value); + } + + Response_P(S_JSON_SCD30_COMMAND_NVALUE, command, value); + } + break; + + case CMND_SCD30_FW: + { + uint8_t major = 0; + uint8_t minor = 0; + int error; + error = scd30.getFirmwareVersion(&major, &minor); + if (error) + { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error getting FW version: 0x%lX"), error); +#endif + serviced = false; + } + else + { + Response_P(S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); + } + } + break; + + default: + + serviced = false; + break; + } + } + return serviced; +} + +void Scd30Show(bool json) +{ + if (scd30IsDataValid) + { + float t = ConvertTemp(scd30_Temp); + float h = ConvertHumidity(scd30_Humid); + + if (json) { + ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,"), scd30_CO2, scd30_CO2EAvg); + ResponseAppendTHD(t, h); + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); + DomoticzTempHumPressureSensor(t, h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2EAVG, "SCD30", scd30_CO2EAvg); + WSContentSend_PD(HTTP_SNS_CO2, "SCD30", scd30_CO2); + WSContentSend_THD("SCD30", t, h); +#endif + } + } +} + + + + + +bool Xsns42(byte function) +{ + if (!I2cEnabled(XI2C_29)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Scd30Detect(); + } + else if (scd30Found) { + switch (function) { + case FUNC_EVERY_SECOND: + Scd30Update(); + break; + case FUNC_COMMAND: + result = Scd30CommandSensor(); + break; + case FUNC_JSON_APPEND: + Scd30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Scd30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_43_hre.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_43_hre.ino" +#ifdef USE_HRE +# 49 "/workspace/Tasmota/tasmota/xsns_43_hre.ino" +#define XSNS_43 43 + +enum hre_states { + hre_idle, + hre_sync, + hre_syncing, + hre_read, + hre_reading, + hre_sleep, + hre_sleeping +}; + +hre_states hre_state = hre_idle; + +float hre_usage = 0; +float hre_rate = 0; +uint32_t hre_usage_time = 0; + +int hre_read_errors = 0; +bool hre_good = false; + + + +int hreReadBit() +{ + digitalWrite(Pin(GPIO_HRE_CLOCK), HIGH); + delay(1); + int bit = digitalRead(Pin(GPIO_HRE_DATA)); + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); + delay(1); + return bit; +} + + + +char hreReadChar(int &parity_errors) +{ + + hreReadBit(); + + unsigned ch=0; + int sum=0; + for (uint32_t i=0; i<7; i++) + { + int b = hreReadBit(); + ch |= b << i; + sum += b; + } + + + if ( (sum & 0x1) != hreReadBit()) + parity_errors++; + + + hreReadBit(); + + return ch; +} + +void hreInit(void) +{ + hre_read_errors = 0; + hre_good = false; + + pinMode(Pin(GPIO_HRE_CLOCK), OUTPUT); + pinMode(Pin(GPIO_HRE_DATA), INPUT); + + + + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); + + hre_state = hre_sync; +} + + +void hreEvery50ms(void) +{ + static int sync_counter = 0; + static int sync_run = 0; + + static uint32_t curr_start = 0; + static int read_counter = 0; + static int parity_errors = 0; + static char buff[46]; + + static char ch; + static size_t i; + + switch (hre_state) + { + case hre_sync: + if (uptime < 10) + break; + sync_run = 0; + sync_counter = 0; + hre_state = hre_syncing; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); + break; + + case hre_syncing: + + + for (uint32_t i=0; i<20; i++) + { + if (hreReadBit()) + sync_run++; + else + sync_run = 0; + if (sync_run == 62) + { + hre_state = hre_read; + break; + } + sync_counter++; + } + + if (sync_counter > 1000) + { + hre_state = hre_sleep; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); + } + break; + + + case hre_read: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); + read_counter = 0; + parity_errors = 0; + curr_start = uptime; + memset(buff, 0, sizeof(buff)); + hre_state = hre_reading; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); + + + + + + case hre_reading: + + buff[read_counter++] = hreReadChar(parity_errors); + buff[read_counter++] = hreReadChar(parity_errors); + + if (read_counter == 46) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), + parity_errors, hre_read_errors, buff); + if (parity_errors == 0) + { + float curr_usage; + curr_usage = 0.01 * atol(buff+24); + if (hre_usage_time) + { + double dt = 1.666e-2 * (curr_start - hre_usage_time); + hre_rate = (curr_usage - hre_usage)/dt; + } + hre_usage = curr_usage; + hre_usage_time = curr_start; + hre_good = true; + + hre_state = hre_sleep; + } + else + { + hre_read_errors++; + hre_state = hre_sleep; + } + } + break; + + case hre_sleep: + hre_usage_time = curr_start; + hre_state = hre_sleeping; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); + + case hre_sleeping: + + + if (uptime - hre_usage_time >= 27) + hre_state = hre_sync; + } +} + +void hreShow(boolean json) +{ + if (!hre_good) + return; + + const char *id = "HRE"; + + char usage[16]; + char rate[16]; + dtostrfd(hre_usage, 2, usage); + dtostrfd(hre_rate, 3, rate); + + if (json) + { + ResponseAppend_P(JSON_SNS_GNGPM, id, usage, rate); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_GALLONS, id, usage); + WSContentSend_PD(HTTP_SNS_GPM, id, rate); +#endif + } +} + + + + + +bool Xsns43(byte function) +{ + + if (!PinUsed(GPIO_HRE_CLOCK) || !PinUsed(GPIO_HRE_DATA)) { return false; } + + switch (function) + { + case FUNC_INIT: + hreInit(); + break; + case FUNC_EVERY_50_MSECOND: + hreEvery50ms(); + break; + case FUNC_EVERY_SECOND: + break; + case FUNC_JSON_APPEND: + hreShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + hreShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_44_sps30.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_44_sps30.ino" +#ifdef USE_I2C +#ifdef USE_SPS30 + +#define XSNS_44 44 +#define XI2C_30 30 + +#define SPS30_ADDR 0x69 + +#include +#include + +uint8_t sps30_ready = 0; +uint8_t sps30_running; + +struct SPS30 { + float PM1_0; + float PM2_5; + float PM4_0; + float PM10; + float NCPM0_5; + float NCPM1_0; + float NCPM2_5; + float NCPM4_0; + float NCPM10; + float TYPSIZ; +} sps30_result; + +#define SPS_CMD_START_MEASUREMENT 0x0010 +#define SPS_CMD_START_MEASUREMENT_ARG 0x0300 +#define SPS_CMD_STOP_MEASUREMENT 0x0104 +#define SPS_CMD_READ_MEASUREMENT 0x0300 +#define SPS_CMD_GET_DATA_READY 0x0202 +#define SPS_CMD_AUTOCLEAN_INTERVAL 0x8004 +#define SPS_CMD_CLEAN 0x5607 +#define SPS_CMD_GET_ACODE 0xd025 +#define SPS_CMD_GET_SERIAL 0xd033 +#define SPS_CMD_RESET 0xd304 +#define SPS_WRITE_DELAY_US 20000 +#define SPS_MAX_SERIAL_LEN 32 + +uint8_t sps30_calc_CRC(uint8_t *data) { + uint8_t crc = 0xFF; + for (uint32_t i = 0; i < 2; i++) { + crc ^= data[i]; + for (uint32_t bit = 8; bit > 0; --bit) { + if(crc & 0x80) { + crc = (crc << 1) ^ 0x31u; + } else { + crc = (crc << 1); + } + } + } + return crc; +} + +void CmdClean(void); + +unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); + +void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen) { +unsigned char cmdb[2]; +uint8_t tmp[3]; +uint8_t index=0; +memset(data,0,dlen); +uint8_t twi_buff[64]; + + Wire.beginTransmission(SPS30_ADDR); + cmdb[0]=cmd>>8; + cmdb[1]=cmd; + Wire.write(cmdb,2); + Wire.endTransmission(); + + + dlen/=2; + dlen*=3; + + twi_readFrom(SPS30_ADDR,twi_buff,dlen,1); + + uint8_t bind=0; + while (bind>8; + cmdb[1]=cmd; + + if (cmd==SPS_CMD_START_MEASUREMENT) { + cmdb[2]=SPS_CMD_START_MEASUREMENT_ARG>>8; + cmdb[3]=SPS_CMD_START_MEASUREMENT_ARG&0xff; + cmdb[4]=sps30_calc_CRC(&cmdb[2]); + Wire.write(cmdb,5); + } else { + Wire.write(cmdb,2); + } + Wire.endTransmission(); +} + +void SPS30_Detect(void) +{ + if (!I2cSetDevice(SPS30_ADDR)) { return; } + I2cSetActiveFound(SPS30_ADDR, "SPS30"); + + uint8_t dcode[32]; + sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); + sps30_cmd(SPS_CMD_START_MEASUREMENT); + sps30_running = 1; + sps30_ready = 1; +} + +#define D_UNIT_PM "ug/m3" +#define D_UNIT_NCPM "#/m3" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SPS30_a[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_PM "{e}"; +const char HTTP_SNS_SPS30_b[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_NCPM "{e}"; +const char HTTP_SNS_SPS30_c[] PROGMEM ="{s}SPS30 " "TYPSIZ" "{m}%s " "um" "{e}"; +#endif + +#define PMDP 2 + +#define SPS30_HOURS Settings.sps30_inuse_hours + + + +void SPS30_Every_Second() { + if (!sps30_running) return; + + if (uptime%10==0) { + uint8_t vars[sizeof(float)*10]; + sps30_get_data(SPS_CMD_READ_MEASUREMENT,vars,sizeof(vars)); + float *fp=&sps30_result.PM1_0; + + typedef union { + uint8_t array[4]; + float value; + } ByteToFloat; + + ByteToFloat conv; + + for (uint32_t count=0; count<10; count++) { + for (uint32_t i = 0; i < 4; i++){ + conv.array[3-i] = vars[count*sizeof(float)+i]; + } + *fp++=conv.value; + } + } + + if (uptime%3600==0 && uptime>60) { + + + SPS30_HOURS++; + if (SPS30_HOURS>(7*24)) { + CmdClean(); + SPS30_HOURS=0; + } + } + +} + +void SPS30_Show(bool json) +{ + if (!sps30_running) { return; } + + char str[64]; + if (json) { + dtostrfd(sps30_result.PM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"SPS30\":{\"" "PM1_0" "\":%s"), str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM2_5" "\":%s"), str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM4_0" "\":%s"), str); + dtostrfd(sps30_result.PM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM10" "\":%s"), str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM0_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM1_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM2_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM4_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM10" "\":%s"), str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + ResponseAppend_P(PSTR(",\"" "TYPSIZ" "\":%s}"), str); + +#ifdef USE_WEBSERVER + } else { + dtostrfd(sps30_result.PM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 1.0",str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 2.5",str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 4.0",str); + dtostrfd(sps30_result.PM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 10",str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 0.5",str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 1.0",str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 2.5",str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 4.0",str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 10",str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_c,str); +#endif + } +} + +void CmdClean(void) +{ + sps30_cmd(SPS_CMD_CLEAN); + ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); + MqttPublishTeleSensor(); +} + +bool SPS30_cmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='c') { + + CmdClean(); + } else if (*cp=='0' || *cp=='1') { + sps30_running=*cp&1; + sps30_cmd(sps30_running?SPS_CMD_START_MEASUREMENT:SPS_CMD_STOP_MEASUREMENT); + } else { + serviced=false; + } + } + Response_P(PSTR("{\"SPS30\":\"%s\"}"), sps30_running?"running":"stopped"); + + return serviced; +} + + + + + +bool Xsns44(byte function) +{ + if (!I2cEnabled(XI2C_30)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + SPS30_Detect(); + } + else if (sps30_ready) { + switch (function) { + case FUNC_EVERY_SECOND: + SPS30_Every_Second(); + break; + case FUNC_JSON_APPEND: + SPS30_Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SPS30_Show(0); + break; + #endif + case FUNC_COMMAND_SENSOR: + if (XSNS_44 == XdrvMailbox.index) { + result = SPS30_cmd(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_45_vl53l0x.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_45_vl53l0x.ino" +#ifdef USE_I2C +#ifdef USE_VL53L0X + + + + + + +#define XSNS_45 45 +#define XI2C_31 31 + +#include +#include "VL53L0X.h" +VL53L0X sensor; + +struct { + uint16_t distance; + uint16_t distance_prev; + uint16_t buffer[5]; + uint8_t ready = 0; + uint8_t index; +} Vl53l0x; + + + +void Vl53l0Detect(void) { + if (!I2cSetDevice(0x29)) { return; } + + if (!sensor.init()) { return; } + + I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); + + sensor.setTimeout(500); + + + + + + sensor.startContinuous(); + Vl53l0x.ready = 1; + + Vl53l0x.index = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L0X[] PROGMEM = + "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; +#endif + +#define USE_VL_MEDIAN + +void Vl53l0Every_250MSecond(void) { + + uint16_t dist = sensor.readRangeContinuousMillimeters(); + if ((0 == dist) || (dist > 2000)) { + dist = 9999; + } + +#ifdef USE_VL_MEDIAN + + Vl53l0x.buffer[Vl53l0x.index] = dist; + Vl53l0x.index++; + if (Vl53l0x.index >= 5) { + Vl53l0x.index = 0; + } + + + uint16_t tbuff[5]; + memmove(tbuff, Vl53l0x.buffer, sizeof(tbuff)); + uint16_t tmp; + uint8_t flag; + for (uint32_t ocnt = 0; ocnt < 5; ocnt++) { + flag = 0; + for (uint32_t count = 0; count < 4; count++) { + if (tbuff[count] > tbuff[count +1]) { + tmp = tbuff[count]; + tbuff[count] = tbuff[count +1]; + tbuff[count +1] = tmp; + flag = 1; + } + } + if (!flag) { break; } + } + Vl53l0x.distance = tbuff[2]; +#else + Vl53l0x.distance = dist; +#endif +} + +#ifdef USE_DOMOTICZ +void Vl53l0Every_Second(void) { + if (abs(Vl53l0x.distance - Vl53l0x.distance_prev) > 8) { + Vl53l0x.distance_prev = Vl53l0x.distance; + DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance); + } +} +#endif + +void Vl53l0Show(boolean json) { + if (json) { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), Vl53l0x.distance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L0X, Vl53l0x.distance); +#endif + } +} + + + + + +bool Xsns45(byte function) +{ + if (!I2cEnabled(XI2C_31)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Vl53l0Detect(); + } + else if (Vl53l0x.ready) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + Vl53l0Every_250MSecond(); + break; +#ifdef USE_DOMOTICZ + case FUNC_EVERY_SECOND: + Vl53l0Every_Second(); + break; +#endif + case FUNC_JSON_APPEND: + Vl53l0Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l0Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_46_MLX90614.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_46_MLX90614.ino" +#ifdef USE_I2C +#ifdef USE_MLX90614 + +#define XSNS_46 46 +#define XI2C_32 32 + +#define I2_ADR_IRT 0x5a + +#define MLX90614_RAWIR1 0x04 +#define MLX90614_RAWIR2 0x05 +#define MLX90614_TA 0x06 +#define MLX90614_TOBJ1 0x07 +#define MLX90614_TOBJ2 0x08 + +struct { + union { + uint16_t value; + uint32_t i2c_buf; + }; + float obj_temp; + float amb_temp; + bool ready = false; +} mlx90614; + +void MLX90614_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IRT)) { return; } + I2cSetActiveFound(I2_ADR_IRT, "MLX90614"); + mlx90614.ready = true; +} + +void MLX90614_Every_Second(void) +{ + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT, MLX90614_TOBJ1); + if (mlx90614.value & 0x8000) { + mlx90614.obj_temp = -999; + } else { + mlx90614.obj_temp = ((float)mlx90614.value * 0.02) - 273.15; + } + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT,MLX90614_TA); + if (mlx90614.value & 0x8000) { + mlx90614.amb_temp = -999; + } else { + mlx90614.amb_temp = ((float)mlx90614.value * 0.02) - 273.15; + } +} + +#ifdef USE_WEBSERVER + const char HTTP_IRTMP[] PROGMEM = + "{s}MXL90614 " "OBJ-" D_TEMPERATURE "{m}%s C" "{e}" + "{s}MXL90614 " "AMB-" D_TEMPERATURE "{m}%s C" "{e}"; +#endif + +void MLX90614_Show(uint8_t json) +{ + char obj_tstr[16]; + dtostrfd(mlx90614.obj_temp, Settings.flag2.temperature_resolution, obj_tstr); + char amb_tstr[16]; + dtostrfd(mlx90614.amb_temp, Settings.flag2.temperature_resolution, amb_tstr); + + if (json) { + ResponseAppend_P(PSTR(",\"MLX90614\":{\"OBJTMP\":%s,\"AMBTMP\":%s}"), obj_tstr, amb_tstr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_IRTMP, obj_tstr, amb_tstr); +#endif + } +} + + + + + +bool Xsns46(byte function) +{ + if (!I2cEnabled(XI2C_32)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MLX90614_Init(); + } + else if (mlx90614.ready) { + switch (function) { + case FUNC_EVERY_SECOND: + MLX90614_Every_Second(); + break; + case FUNC_JSON_APPEND: + MLX90614_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MLX90614_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_47_max31865.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_47_max31865.ino" +#ifdef USE_MAX31865 + + + + +#define XSNS_47 47 + +#if MAX31865_PTD_WIRES == 4 + #define PTD_WIRES MAX31865_4WIRE +#elif MAX31865_PTD_WIRES == 3 + #define PTD_WIRES MAX31865_3WIRE +#else + #define PTD_WIRES MAX31865_2WIRE +#endif + +#include "Adafruit_MAX31865.h" + +int8_t max31865_init_status = 0; +uint8_t max31865_pins_used = 0; + +Adafruit_MAX31865 max31865[MAX_MAX31865S]; + +struct MAX31865_Result_Struct { + uint8_t ErrorCode; + uint16_t Rtd; + float PtdResistance; + float PtdTemp; +} MAX31865_Result[MAX_MAX31865S]; + +void MAX31865_Init(void) { + if (max31865_init_status) { return; } + + max31865_init_status = 1; + for (uint32_t i = 0; i < MAX_MAX31865S; i++) { + if (PinUsed(GPIO_SSPI_MAX31865_CS1, i)) { + max31865_pins_used |= 1 << i; + max31865[0].setPins( + Pin(GPIO_SSPI_MAX31865_CS1, i), + Pin(GPIO_SSPI_MOSI), + Pin(GPIO_SSPI_MISO), + Pin(GPIO_SSPI_SCLK) + ); + + if (!max31865[i].begin(PTD_WIRES)) { + max31865_init_status = -1; + } + } + } +} + + + + + +void MAX31865_GetResult(void) { + for (uint32_t i = 0; i < MAX_MAX31865S; i++) { + if (max31865_pins_used & (1 << i)) { + uint16_t rtd; + + rtd = max31865[i].readRTD(); + MAX31865_Result[i].Rtd = rtd; + MAX31865_Result[i].PtdResistance = max31865[i].rtd_to_resistance(rtd, MAX31865_REF_RES); + MAX31865_Result[i].PtdTemp = max31865[i].rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; + } + } +} + +void MAX31865_Show(bool Json) { + uint8_t report_once = 0; + for (uint32_t i = 0; i < MAX_MAX31865S; i++) { + if (max31865_pins_used & (1 << i)) { + char temperature[33]; + char resistance[33]; + + dtostrfd(MAX31865_Result[i].PtdResistance, Settings.flag2.temperature_resolution, resistance); + dtostrfd(MAX31865_Result[i].PtdTemp, Settings.flag2.temperature_resolution, temperature); + + if (Json) { + ResponseAppend_P(PSTR(",\"MAX31865%c%d\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + IndexSeparator(), i, temperature, resistance, MAX31865_Result[i].ErrorCode); + if ((0 == tele_period) && (!report_once)) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, MAX31865_Result[i].PtdTemp); +#endif + report_once++; + } +#ifdef USE_WEBSERVER + } else { + char sensorname[33]; + sprintf(sensorname, "MAX31865%c%d", IndexSeparator(), i); + WSContentSend_PD(HTTP_SNS_TEMP, sensorname, temperature, TempUnit()); +#endif + } + } + } +} + + + + + +bool Xsns47(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_SSPI_MAX31865_CS1, GPIO_ANY) && PinUsed(GPIO_SSPI_MISO) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { + switch (function) { + case FUNC_INIT: + MAX31865_Init(); + break; + + case FUNC_EVERY_SECOND: + MAX31865_GetResult(); + break; + + case FUNC_JSON_APPEND: + MAX31865_Show(1); + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31865_Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_48_chirp.ino" +# 37 "/workspace/Tasmota/tasmota/xsns_48_chirp.ino" +#ifdef USE_I2C +#ifdef USE_CHIRP +# 49 "/workspace/Tasmota/tasmota/xsns_48_chirp.ino" +#define XSNS_48 48 +#define XI2C_33 33 + +#define CHIRP_MAX_SENSOR_COUNT 3 + +#define CHIRP_ADDR_STANDARD 0x20 + + + + + +#define D_CMND_CHIRP "CHIRP" + +const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; +const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; +const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; + +const char kChirpTypes[] PROGMEM = "CHIRP"; + + + + + +enum CHIRP_Commands { + CMND_CHIRP_SELECT, + CMND_CHIRP_SET, + CMND_CHIRP_SCAN, + CMND_CHIRP_RESET, + CMND_CHIRP_SLEEP, + CMND_CHIRP_WAKE }; + + + + + + +#define CHIRP_GET_CAPACITANCE 0x00 +#define CHIRP_SET_ADDRESS 0x01 +#define CHIRP_GET_ADDRESS 0x02 +#define CHIRP_MEASURE_LIGHT 0x03 +#define CHIRP_GET_LIGHT 0x04 +#define CHIRP_GET_TEMPERATURE 0x05 +#define CHIRP_RESET 0x06 +#define CHIRP_GET_VERSION 0x07 +#define CHIRP_SLEEP 0x08 +#define CHIRP_GET_BUSY 0x09 + + + + + +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.endTransmission(); +} + +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { + Wire.requestFrom(addr,(uint8_t)2); + uint16_t t = Wire.read() << 8; + t = t | Wire.read(); + return t; +} + + + + + +uint8_t chirp_current = 0; +uint8_t chirp_found_sensors = 0; + +char chirp_name[7]; +uint8_t chirp_next_job = 0; +uint32_t chirp_timeout_count = 0; + +#pragma pack(1) +struct ChirpSensor_t{ + uint16_t moisture = 0; + uint16_t light = 0; + int16_t temperature = 0; + uint8_t version = 0; + uint8_t address:7; + uint8_t explicitSleep:1; +}; +#pragma pack() + +ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; + + + +void ChirpReset(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_RESET); +} + + + +void ChirpResetAll(void) { + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + ChirpReset(chirp_sensor[i].address); + } + } +} + + +void ChirpClockSet() { + Wire.setClockStretchLimit(4000); + Wire.setClock(50000); +} + + + +void ChirpSleep(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_SLEEP); +} +# 187 "/workspace/Tasmota/tasmota/xsns_48_chirp.ino" +void ChirpSelect(uint8_t sensor) { + if(sensor < chirp_found_sensors) { + chirp_current = sensor; + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); + } + if (sensor == 255) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); + } +} + + + +uint8_t ChirpReadVersion(uint8_t addr) { + return (I2cRead8(addr, CHIRP_GET_VERSION)); +} + + + +bool ChirpSet(uint8_t addr) { + if(addr < 128){ + if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ + if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ + delay(5); + I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); + + } + DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); + ChirpReset(chirp_sensor[chirp_current].address); + chirp_sensor[chirp_current].address = addr; + chirp_timeout_count = 10; + chirp_next_job = 0; + if(chirp_sensor[chirp_current].version == 255){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); + chirp_sensor[chirp_current].version == 0; + } + return true; + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); + return false; +} + + + +bool ChirpScan() +{ + ChirpClockSet(); + chirp_found_sensors = 0; + for (uint8_t address = 1; address <= 127; address++) { + chirp_sensor[chirp_found_sensors].version = 0; + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + delay(2); + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + if (chirp_sensor[chirp_found_sensors].version > 0) { + I2cSetActiveFound(address, "CHIRP"); + if (chirp_found_sensors 0); +} + + + +void ChirpDetect(void) +{ + if (chirp_next_job > 0) { return; } + + DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); + if (ChirpScan()) { + uint8_t chirp_model = 0; + GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); + } +} + + +void ChirpServiceAllSensors(uint8_t job){ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); + switch(job){ + case 0: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); + break; + case 1: + chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 2: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); + break; + case 3: + chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 4: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); + break; + case 5: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); + break; + case 6: + chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + default: + break; + } + } + } +} + + + +void ChirpEvery100MSecond(void) +{ + + if(chirp_timeout_count == 0) { + switch(chirp_next_job) { + case 0: + DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); + ChirpResetAll(); + chirp_timeout_count = 10; + chirp_next_job++; + break; + case 1: + + + chirp_next_job++; + break; + case 2: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 3: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 4: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 5: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 6: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 7: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 8: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 9: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 10: + DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); + ChirpServiceAllSensors(4); + chirp_timeout_count = 90; + chirp_next_job++; + break; + case 11: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); + ChirpServiceAllSensors(5); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 12: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); + ChirpServiceAllSensors(6); + chirp_next_job++; + break; + case 13: + DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); + chirp_next_job++; + break; + case 14: + if (Settings.tele_period > 16){ + chirp_timeout_count = (Settings.tele_period - 16) * 10; + DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); + } + else{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); + + } + chirp_next_job = 1; + break; + } + } + else { + chirp_timeout_count--; + } +} + + + + +#ifdef USE_WEBSERVER + + +const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %%{e}"; +const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" + "{s} FW-version{m}%s {e}"; ; +const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; +#endif + + + +void ChirpShow(bool json) +{ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + + char str_temperature[33]; + double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; + dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_light[33]; + dtostrfd(chirp_sensor[i].light, 0, str_light); + char str_version[7]; + if(chirp_sensor[i].version == 0xff){ + strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); + } + else{ + sprintf(str_version, "%x", chirp_sensor[i].version); + } + + if (json) { + if(!chirp_sensor[i].explicitSleep) { + ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); + if(chirp_sensor[i].temperature!=-1){ + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); + } + ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); + } + else { + ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); + } + #ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumPressureSensor(t_temperature, chirp_sensor[i].moisture); + DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); + } + #endif + #ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); + if (chirp_sensor[i].explicitSleep){ + WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); + } + else { + WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); + WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); + if (chirp_sensor[i].temperature!=-1) { + WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); + } + } + + #endif + } + } + } +} + + + + + +bool ChirpCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_CHIRP); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); + + switch (command_code) { + case CMND_CHIRP_SELECT: + case CMND_CHIRP_SET: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } + if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } + Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + else { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + } + break; + case CMND_CHIRP_SCAN: + case CMND_CHIRP_SLEEP: + case CMND_CHIRP_WAKE: + case CMND_CHIRP_RESET: + if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; + ChirpDetect(); } + if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; + ChirpSleep(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; + ChirpReadVersion(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } + return serviced; +} + + + + + +bool Xsns48(uint8_t function) +{ + if (!I2cEnabled(XI2C_33)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if(chirp_found_sensors > 0){ + ChirpEvery100MSecond(); + } + break; + case FUNC_COMMAND: + result = ChirpCmd(); + break; + case FUNC_JSON_APPEND: + ChirpShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ChirpShow(0); + break; +#endif + case FUNC_INIT: + ChirpDetect(); + break; + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_50_paj7620.ino" +# 31 "/workspace/Tasmota/tasmota/xsns_50_paj7620.ino" +#ifdef USE_I2C +#ifdef USE_PAJ7620 + + + + + + +#define XSNS_50 50 +#define XI2C_34 34 + +#define PAJ7620_ADDR 0x73 + +#define PAJ7620_BANK_SEL 0xEF + + + +#define PAJ7620_GET_GESTURE 0x43 +#define PAJ7620_PROXIMITY_AVG_Y 0x6c + +#define PAJ7620_OBJECT_CENTER_X 0xad +#define PAJ7620_OBJECT_CENTER_Y 0xaf + +#define PAJ7620_DOWN 1 +#define PAJ7620_UP 2 +#define PAJ7620_RIGHT 4 +#define PAJ7620_LEFT 8 +#define PAJ7620_NEAR 16 +#define PAJ7620_FAR 32 +#define PAJ7620_CW 64 +#define PAJ7620_CCW 128 + + + + +const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { + {0xEF,0x00}, + {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, + {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, + {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, + {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, + {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, + {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, + {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, + {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, + {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, + {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, + {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, + {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, + {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, + {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, + {0xEF,0x01}, + {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, + {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, + {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, + {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, + {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, + {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, + {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, + {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, + {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, + {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, + {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, + {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, + {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, + {0x7E,0x01}, + {0xEF,0x00} +}; + + + + + +const char kPaj7620Directions[] PROGMEM = "Down|Up|Right|Left|Near|Far|CW|CCW"; + +const uint8_t PAJ7620_PIN[]= {1,2,3,4}; + + + + + +char PAJ7620_name[] = "PAJ7620"; + +uint32_t PAJ7620_timeout_counter = 10; +uint32_t PAJ7620_next_job = 0; +uint32_t PAJ7620_mode = 1; + +struct { + uint8_t current; + uint8_t last; + uint8_t same; + uint8_t unfinished; +} PAJ7620_gesture; + +bool PAJ7620_finished_gesture = false; +char PAJ7620_currentGestureName[6]; + +struct{ + uint8_t x; + uint8_t y; + uint8_t last_x; + uint8_t last_y; + uint8_t proximity; + uint8_t last_proximity; + uint8_t corner; + struct { + uint8_t step:3; + uint8_t countdown:3; + uint8_t valid:1; + } PIN; +} PAJ7620_state; + + + + + +void PAJ7620SelectBank(uint8_t bank) +{ + I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, bank &1, 1); +} + + + +void PAJ7620DecodeGesture(void) +{ + uint32_t index = 0; + switch (PAJ7620_gesture.current) { + case PAJ7620_LEFT: + index++; + case PAJ7620_RIGHT: + index++; + case PAJ7620_UP: + index++; + case PAJ7620_DOWN: + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_NEAR: + index = 4; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_FAR: + index = 5; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_CW: + index = 6; + PAJ7620_finished_gesture = true; + break; + case PAJ7620_CCW: + index = 7; + PAJ7620_finished_gesture = true; + break; + default: + index = 8; + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + } + break; + } + if (index < 8) { + GetTextIndexed(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), index, kPaj7620Directions); + } + + if (PAJ7620_finished_gesture) { + if (PAJ7620_gesture.unfinished) { + if ((PAJ7620_gesture.current != PAJ7620_NEAR) && (PAJ7620_gesture.current != PAJ7620_FAR)) { + PAJ7620_gesture.current = PAJ7620_gesture.unfinished; + } + } + if (PAJ7620_gesture.current == PAJ7620_gesture.last) { + PAJ7620_gesture.same++; + } else { + PAJ7620_gesture.same = 1; + } + PAJ7620_gesture.last = PAJ7620_gesture.current; + PAJ7620_finished_gesture = false; + PAJ7620_gesture.unfinished = 0; + PAJ7620_timeout_counter += 3; + MqttPublishSensor(); + } +} + + + +void PAJ7620ReadGesture(void) +{ + switch (PAJ7620_mode) { + case 1: + PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); + if ((PAJ7620_gesture.current > 0) || PAJ7620_gesture.unfinished) { + DEBUG_SENSOR_LOG(PSTR("PAJ: gesture: %u"), PAJ7620_gesture.current); + PAJ7620DecodeGesture(); + } + break; + case 2: + PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); + if ((PAJ7620_state.proximity > 0) || (PAJ7620_state.last_proximity > 0)) { + if (PAJ7620_state.proximity != PAJ7620_state.last_proximity) { + PAJ7620_state.last_proximity = PAJ7620_state.proximity; + DEBUG_SENSOR_LOG(PSTR("PAJ: Proximity: %u"), PAJ7620_state.proximity); + MqttPublishSensor(); + } + } + break; + case 3: + case 4: + case 5: + PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); + PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); + if ((PAJ7620_state.y > 0) && (PAJ7620_state.x > 0)) { + if ((PAJ7620_state.y != PAJ7620_state.last_y) || (PAJ7620_state.x != PAJ7620_state.last_x)) { + PAJ7620_state.last_y = PAJ7620_state.y; + PAJ7620_state.last_x = PAJ7620_state.x; + DEBUG_SENSOR_LOG(PSTR("PAJ: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); + + PAJ7620_state.corner = 0; + + + + switch (PAJ7620_state.y) { + case 0: case 1: case 2: case 3: case 4: case 5: + PAJ7620_state.corner = 3; + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner = 1; + break; + } + if (PAJ7620_state.corner != 0) { + switch (PAJ7620_state.x) { + case 0: case 1: case 2: case 3: case 4: case 5: + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner++; + break; + default: + PAJ7620_state.corner = 0; + break; + } + } + DEBUG_SENSOR_LOG(PSTR("PAJ: corner: %u"), PAJ7620_state.corner); + + if (PAJ7620_state.PIN.countdown == 0) { + PAJ7620_state.PIN.step = 0; + PAJ7620_state.PIN.valid = 0; + } + if (!PAJ7620_state.PIN.step) { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step = 1; + PAJ7620_state.PIN.countdown = 7; + } + } else { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step += 1; + PAJ7620_state.PIN.countdown = 7; + } else { + PAJ7620_state.PIN.countdown -= 1; + } + } + if (PAJ7620_state.PIN.step == 4) { + PAJ7620_state.PIN.valid = 1; + DEBUG_SENSOR_LOG(PSTR("PAJ: PIN valid!!")); + PAJ7620_state.PIN.countdown = 0; + } + MqttPublishSensor(); + } + } + break; + } +} + + + +void PAJ7620Detect(void) +{ + if (I2cActive(PAJ7620_ADDR)) { return; } + + PAJ7620SelectBank(0); + PAJ7620SelectBank(0); + uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); + uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); + if (0x7620 == PAJ7620_id) { + I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); + PAJ7620_next_job = 1; + } + else { + DEBUG_SENSOR_LOG(PSTR("PAJ: sensor not found, false ID 0x%x"), PAJ7620_id); + } +} + + + +void PAJ7620Init(void) +{ + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor start %u"),millis()); + union{ + uint32_t raw; + uint8_t reg_val[4]; + } buf; + + for (uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray) / 2); i += 2) + { + buf.raw = pgm_read_dword(PAJ7620initRegisterArray + i); + DEBUG_SENSOR_LOG("PAJ: %x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); + I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); + I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); + } + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor done %u"),millis()); + PAJ7620_next_job = 2; +} + + + +void PAJ7620Loop(void) +{ + if (0 == PAJ7620_timeout_counter) { + switch (PAJ7620_next_job) { + case 1: + PAJ7620Init(); + break; + case 2: + if (PAJ7620_mode != 0) { + PAJ7620ReadGesture(); + } + break; + } + } else { + PAJ7620_timeout_counter--; + } +} + + + +void PAJ7620Show(bool json) +{ + if (json) { + if (PAJ7620_currentGestureName[0] != '\0' ) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); + PAJ7620_currentGestureName[0] = '\0'; + return; + } + switch (PAJ7620_mode) { + case 2: + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); + break; + case 3: + if (PAJ7620_state.corner > 0) { + ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); + } + break; + case 4: + if (PAJ7620_state.PIN.valid) { + ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); + PAJ7620_state.PIN.valid = 0; + } + break; + case 5: + ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); + break; + } + } +} +# 411 "/workspace/Tasmota/tasmota/xsns_50_paj7620.ino" +bool PAJ7620CommandSensor(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + PAJ7620_mode = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_50, PAJ7620_mode); + + return true; +} + + + + + +bool Xsns50(uint8_t function) +{ + if (!I2cEnabled(XI2C_34)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PAJ7620Detect(); + } + else if (PAJ7620_next_job) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_50 == XdrvMailbox.index){ + result = PAJ7620CommandSensor(); + } + break; + case FUNC_EVERY_100_MSECOND: + PAJ7620Loop(); + break; + case FUNC_JSON_APPEND: + PAJ7620Show(1); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_51_rdm6300.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_51_rdm6300.ino" +#ifdef USE_RDM6300 +# 31 "/workspace/Tasmota/tasmota/xsns_51_rdm6300.ino" +#define XSNS_51 51 + +#define RDM6300_BAUDRATE 9600 +#define RDM_TIMEOUT 100 +#define RDM6300_BLOCK 2 * 10 + +#include +TasmotaSerial *RDM6300Serial = nullptr; + +struct { + uint32_t uid = 0; + uint8_t block_time = 0; +} Rdm; + + + +uint8_t RDM6300HexNibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { rVal = chr - '0'; } + else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; +} + + +void RDM6300HexStringToArray(uint8_t array[], uint8_t len, char buffer[]) { + char *cp = buffer; + for (uint32_t i = 0; i < len; i++) { + uint8_t val = RDM6300HexNibble(*cp++) << 4; + array[i] = val | RDM6300HexNibble(*cp++); + } +} + + + +void RDM6300Init() { + if (PinUsed(GPIO_RDM6300_RX)) { + RDM6300Serial = new TasmotaSerial(Pin(GPIO_RDM6300_RX), -1, 1); + if (RDM6300Serial->begin(RDM6300_BAUDRATE)) { + if (RDM6300Serial->hardwareSerial()) { + ClaimSerial(); + } + } + } +} + +void RDM6300ScanForTag() { + if (!RDM6300Serial) { return; } + + if (Rdm.block_time > 0) { + Rdm.block_time--; + while (RDM6300Serial->available()) { + RDM6300Serial->read(); + } + return; + } + + if (RDM6300Serial->available()) { + + char c = RDM6300Serial->read(); + if (c != 2) { return; } + + + char rdm_buffer[14]; + uint8_t rdm_index = 0; + + rdm_buffer[rdm_index++] = c; + + uint32_t cmillis = millis(); + while (1) { + if (RDM6300Serial->available()) { + c = RDM6300Serial->read(); + rdm_buffer[rdm_index++] = c; + + if (3 == c) { break; } + if (rdm_index > 14) { break; } + } + if ((millis() - cmillis) > RDM_TIMEOUT) { + return; + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)rdm_buffer, sizeof(rdm_buffer)); + + if (rdm_buffer[13] != 3) { return; } + + Rdm.block_time = RDM6300_BLOCK; + + uint8_t rdm_array[6]; + RDM6300HexStringToArray(rdm_array, sizeof(rdm_array), (char*)rdm_buffer +1); + uint8_t accu = 0; + for (uint32_t count = 0; count < 5; count++) { + accu ^= rdm_array[count]; + } + if (accu != rdm_array[5]) { return; } + + rdm_buffer[11] = '\0'; + uint32_t uid = strtoul(rdm_buffer +3, nullptr, 16); + if (uid > 0) { + Rdm.uid = uid; + ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%08X\"}}"), Rdm.uid); + MqttPublishTeleSensor(); + } + } +} + +#ifdef USE_WEBSERVER +void RDM6300Show(void) { + if (!RDM6300Serial) { return; } + WSContentSend_PD(PSTR("{s}RDM6300 UID{m}%08X {e}"), Rdm.uid); +} +#endif + + + + + +bool Xsns51(byte function) { + bool result = false; + + switch (function) { + case FUNC_INIT: + RDM6300Init(); + break; + case FUNC_EVERY_100_MSECOND: + RDM6300ScanForTag(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RDM6300Show(); + break; +#endif + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_52_ibeacon.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_52_ibeacon.ino" +#ifdef USE_IBEACON + + + +#define XSNS_52 52 + +#include + +#define TMSBSIZ52 512 + +#define HM17_BAUDRATE 9600 + +#define IBEACON_DEBUG + + +#define HM17_V110 + + + +#define IB_TIMEOUT_INTERVAL 30 + +#define IB_UPDATE_TIME_INTERVAL 10 + +TasmotaSerial *IBEACON_Serial = nullptr; + + +uint8_t hm17_found,hm17_cmd,hm17_flag; + +#ifdef IBEACON_DEBUG +uint8_t hm17_debug=0; +#endif + + + +#define HM17_BSIZ 128 +char hm17_sbuffer[HM17_BSIZ]; +uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; +uint32_t hm17_lastms; +char ib_mac[14]; + + +#if 1 +uint8_t ib_upd_interval,ib_tout_interval; +#define IB_UPDATE_TIME ib_upd_interval +#define IB_TIMEOUT_TIME ib_tout_interval +#else +#undef IB_UPDATE_TIME +#undef IB_TIMEOUT_TIME +#define IB_UPDATE_TIME Settings.ib_upd_interval +#define IB_TIMEOUT_TIME Settings.ib_tout_interval +#endif + +enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; +#define HM17_SUCESS 99 + +struct IBEACON { + char FACID[8]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + char PWR[2]; + char MAC[12]; + char RSSI[4]; +}; + +#define MAX_IBEACONS 16 + +struct IBEACON_UID { + char MAC[12]; + char RSSI[4]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + uint8_t FLAGS; + uint8_t TIME; +} ibeacons[MAX_IBEACONS]; + + +void IBEACON_Init() { + + hm17_found=0; + + + if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) { + IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1,0,TMSBSIZ52); + if (IBEACON_Serial->begin(HM17_BAUDRATE)) { + if (IBEACON_Serial->hardwareSerial()) { + ClaimSerial(); + } + hm17_sendcmd(HM17_TEST); + hm17_lastms=millis(); + + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + } +} + +void hm17_every_second(void) { + if (!IBEACON_Serial) return; + + if (hm17_found) { + if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { + if (hm17_cmd!=99) { + if (hm17_flag&2) { + ib_sendbeep(); + } else { + if (!hm17_connecting) { + hm17_sendcmd(HM17_DISI); + } + } + } + } + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR); + } + } + } + } else { + if (uptime%20==0) { + hm17_sendcmd(HM17_TEST); + } + } +} + +void hm17_sbclr(void) { + memset(hm17_sbuffer,0,HM17_BSIZ); + hm17_sindex=0; + +} + +void hm17_sendcmd(uint8_t cmd) { + hm17_sbclr(); + hm17_cmd=cmd; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); +#endif + switch (cmd) { + case HM17_TEST: + IBEACON_Serial->write("AT"); + break; + case HM17_ROLE: + IBEACON_Serial->write("AT+ROLE1"); + break; + case HM17_IMME: + IBEACON_Serial->write("AT+IMME1"); + break; + case HM17_DISI: + IBEACON_Serial->write("AT+DISI?"); + hm17_scanning=1; + break; + case HM17_IBEA: + IBEACON_Serial->write("AT+IBEA1"); + break; + case HM17_RESET: + IBEACON_Serial->write("AT+RESET"); + break; + case HM17_RENEW: + IBEACON_Serial->write("AT+RENEW"); + break; + case HM17_SCAN: + IBEACON_Serial->write("AT+SCAN5"); + break; + case HM17_DISC: + IBEACON_Serial->write("AT+DISC?"); + hm17_scanning=1; + break; + case HM17_CON: + IBEACON_Serial->write((const uint8_t*)"AT+CON",6); + IBEACON_Serial->write((const uint8_t*)ib_mac,12); + hm17_connecting=1; + break; + } +} + +uint32_t ibeacon_add(struct IBEACON *ib) { + + + + + if (!strncmp(ib->RSSI,"0",1)) { + return 0; + } + + + if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { + for (uint32_t cnt=0;cntUID,PSTR("00000000000000000000000000000000"),32)) { + if (!strncmp(ibeacons[cnt].MAC,ib->MAC,12)) { + + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; + return 1; + } + } else { + if (!strncmp(ibeacons[cnt].UID,ib->UID,32)) { + + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; + return 1; + } + } + } + } + for (uint32_t cnt=0;cntMAC,12); + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + memcpy(ibeacons[cnt].UID,ib->UID,32); + memcpy(ibeacons[cnt].MAJOR,ib->MAJOR,4); + memcpy(ibeacons[cnt].MINOR,ib->MINOR,4); + ibeacons[cnt].FLAGS=1; + ibeacons[cnt].TIME=0; + return 1; + } + } + } + return 0; +} + +void hm17_decode(void) { + struct IBEACON ib; + switch (hm17_cmd) { + case HM17_TEST: + if (!strncmp(hm17_sbuffer,"OK",2)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + hm17_found=1; + } + break; + case HM17_ROLE: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IMME: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IBEA: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_SCAN: + if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RESET: + if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RENEW: + if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_CON: + if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); +#endif + hm17_connecting=2; + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); +#endif + break; + } + if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); +#endif + hm17_connecting=3; + hm17_sendcmd(HM17_TEST); + hm17_connecting=0; + break; + } + break; + + case HM17_DISI: + case HM17_DISC: + if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISIS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { + hm17_sbclr(); + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); +#endif + hm17_scanning=0; + break; + } + if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { + if (hm17_sbuffer[hm17_sindex-1]=='\n') { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { + if (hm17_cmd==HM17_DISI) { +#ifdef HM17_V110 + goto hm17_v110; +#endif + } else { + if (hm17_sindex==20) { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { +hm17_v110: + if (hm17_cmd==HM17_DISI) { + if (hm17_sindex==78) { +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + memcpy(ib.FACID,&hm17_sbuffer[8],8); + memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); + memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); + memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); + memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); + memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); + memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR); + } + hm17_sbclr(); + hm17_result=1; + } + } else { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]); +#endif + } + break; + } + } +} + +void IBEACON_loop() { + + if (!IBEACON_Serial) return; + +uint32_t difftime=millis()-hm17_lastms; + + while (IBEACON_Serial->available()) { + hm17_lastms=millis(); + + if (hm17_sindexread(); + hm17_sindex++; + hm17_decode(); + } else { + hm17_sindex=0; + break; + } + } + + if (hm17_cmd==99) { + if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + hm17_sbclr(); + } + } + +} + +#ifdef USE_WEBSERVER +const char HTTP_IBEACON_mac[] PROGMEM = + "{s}IBEACON-MAC : %s" " - RSSI : %s" "{m}{e}"; +const char HTTP_IBEACON_uid[] PROGMEM = + "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; + +void IBEACON_Show(void) { +char mac[14]; +char rssi[6]; +char uid[34]; + + for (uint32_t cnt=0;cnt 0) { + char *cp=XdrvMailbox.data; + if (*cp>='0' && *cp<='8') { + hm17_sendcmd(*cp&7); + Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); + } else if (*cp=='s') { + cp++; + len--; + while (*cp==' ') { + len--; + cp++; + } + IBEACON_Serial->write((uint8_t*)cp,len); + hm17_cmd=99; + Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); + } else if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt + + +#ifndef ESP32 +#define SPECIAL_SS +#endif + +#undef TMSBSIZ +#define TMSBSIZ 256 + + + + + + +#define DJ_TPWRIN "Total_in" +#define DJ_TPWROUT "Total_out" +#define DJ_TPWRCURR "Power_curr" +#define DJ_TPWRCURR1 "Power_p1" +#define DJ_TPWRCURR2 "Power_p2" +#define DJ_TPWRCURR3 "Power_p3" +#define DJ_CURR1 "Curr_p1" +#define DJ_CURR2 "Curr_p2" +#define DJ_CURR3 "Curr_p3" +#define DJ_VOLT1 "Volt_p1" +#define DJ_VOLT2 "Volt_p2" +#define DJ_VOLT3 "Volt_p3" +#define DJ_METERNR "Meter_number" +#define DJ_METERSID "Meter_id" +#define DJ_CSUM "Curr_summ" +#define DJ_VAVG "Volt_avg" +#define DJ_COUNTER "Count" + +struct METER_DESC { + uint8_t srcpin; + uint8_t type; + uint16_t flag; + int32_t params; + char prefix[8]; + int8_t trxpin; + uint8_t tsecs; + char *txmem; + uint8_t index; + uint8_t max_index; +}; + + + + + + +#define EHZ161_0 1 +#define EHZ161_1 2 +#define EHZ363 3 +#define EHZH 4 +#define EDL300 5 +#define Q3B 6 +#define COMBO3 7 +#define COMBO2 8 +#define COMBO3a 9 +#define Q3B_V1 10 +#define EHZ363_2 11 +#define COMBO3b 12 +#define WGS_COMBO 13 +#define EBZD_G 14 + + +#define METER EHZ161_1 + + +#if METER==EHZ161_0 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==EHZ161_1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZH +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + + + +#if METER==EDL300 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==EBZD_G +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + + +#if METER==Q3B +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==COMBO3 + +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, + [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; + + +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO2 + +#undef METERS_USED +#define METERS_USED 2 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; + + +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + +"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO3a +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, + [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; + + +const uint8_t meter[]= +"1,=h --- Zähler Nr 1 ---|" +"1,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,=h --- Zähler Nr 2 ---|" +"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"3,=h --- Zähler Nr 3 ---|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==Q3B_V1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,=d 1 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363_2 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + +#if METER==COMBO3b +#undef METERS_USED +#define METERS_USED 3 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'c',0,50,"Gas"}, + [2]={1,'c',0,10,"Wasser"}}; + + +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + + +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" + +"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; +#endif + + +#if METER==WGS_COMBO +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={1,'c',0,10,"H20",-1,1,0}, + [1]={4,'c',0,50,"GAS",-1,1,0}, + [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + + +"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" + + +"2,=h==================|" +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" + +"3,=h==================|" + +"3,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",3|" +"3,=h==================|" + +"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" +"3,=h -------------------------------|" +"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" + +"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" +"3,=h==================|" + +"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" + +"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" + +"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" +"3,=h -------------------------------|" + +"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" + +"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" + +"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" +"3,=h -------------------------------|" + +"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" + +"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" + +"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" +"3,=h==================|" + +"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" +"3,=h--------------------------------"; +#endif +# 440 "/workspace/Tasmota/tasmota/xsns_53_sml.ino" +#define USE_SML_MEDIAN_FILTER + + +#ifndef SML_MAX_VARS +#define SML_MAX_VARS 20 +#endif + + +#ifndef MAX_METERS +#define MAX_METERS 5 +#endif +double meter_vars[SML_MAX_VARS]; + +#define MAX_DVARS MAX_METERS*2 +double dvalues[MAX_DVARS]; +uint32_t dtimes[MAX_DVARS]; +uint8_t meters_used; + +struct METER_DESC const *meter_desc_p; +const uint8_t *meter_p; +uint8_t meter_spos[MAX_METERS]; + + +#ifdef ESP32 +HardwareSerial *meter_ss[MAX_METERS]; +#else +TasmotaSerial *meter_ss[MAX_METERS]; +#endif + + +#ifndef SML_BSIZ +#define SML_BSIZ 48 +#endif +uint8_t smltbuf[MAX_METERS][SML_BSIZ]; + + +#define METER_ID_SIZE 24 +char meter_id[MAX_METERS][METER_ID_SIZE]; + +#define EBUS_SYNC 0xaa +#define EBUS_ESC 0xa9 + +uint8_t sml_send_blocks; +uint8_t sml_100ms_cnt; +uint8_t sml_desc_cnt; + +#ifdef USE_SML_MEDIAN_FILTER + +#define MEDIAN_SIZE 5 +struct SML_MEDIAN_FILTER { +double buffer[MEDIAN_SIZE]; +int8_t index; +} sml_mf[SML_MAX_VARS]; + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +double sml_median_array(double *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + double min=FLT_MAX; + + for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + + return sml_median_array(mf->buffer,MEDIAN_SIZE); +# 552 "/workspace/Tasmota/tasmota/xsns_53_sml.ino" +} +#endif + +#ifdef ANALOG_OPTO_SENSOR + +uint8_t ads1115_up; + + +#define SAMPLE_BIT (0x8000) + +#define ADS1115_COMP_QUEUE_SHIFT 0 +#define ADS1115_COMP_LATCH_SHIFT 2 +#define ADS1115_COMP_POLARITY_SHIFT 3 +#define ADS1115_COMP_MODE_SHIFT 4 +#define ADS1115_DATA_RATE_SHIFT 5 +#define ADS1115_MODE_SHIFT 8 +#define ADS1115_PGA_SHIFT 9 +#define ADS1115_MUX_SHIFT 12 + +enum ads1115_comp_queue { + ADS1115_COMP_QUEUE_AFTER_ONE = 0, + ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, +}; + +enum ads1115_comp_latch { + ADS1115_COMP_LATCH_NO = 0, + ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, + ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, +}; + +enum ads1115_comp_polarity { + ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, + ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, + ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, +}; + +enum ads1115_comp_mode { + ADS1115_COMP_MODE_WINDOW = 0, + ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, + ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, +}; + +enum ads1115_data_rate { + ADS1115_DATA_RATE_8_SPS = 0, + ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, +}; + +enum ads1115_mode { + ADS1115_MODE_CONTINUOUS = 0, + ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, + ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, +}; + +enum ads1115_pga { + ADS1115_PGA_TWO_THIRDS = 0, + ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, + ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, + ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, + ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, + ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, + ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, +}; + + +enum ads1115_mux { + ADS1115_MUX_DIFF_AIN0_AIN1 = 0, + ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, + ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, +}; + +class ADS1115 { +public: + ADS1115(uint8_t address = 0x48); + + void begin(); + uint8_t trigger_sample(); + uint8_t reset(); + bool is_sample_in_progress(); + int16_t read_sample(); + float sample_to_float(int16_t val); + float read_sample_float(); + + void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } + void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } + void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } + void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } + void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } + void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } + void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } + void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } + +private: + void set_config(uint16_t val, uint16_t mask) { + m_config = (m_config & ~mask) | val; + } + + uint8_t write_register(uint8_t reg, uint16_t val); + uint16_t read_register(uint8_t reg); + + uint8_t m_address; + uint16_t m_config; + int m_voltage_range; +}; + + +enum ads1115_register { + ADS1115_REGISTER_CONVERSION = 0, + ADS1115_REGISTER_CONFIG = 1, + ADS1115_REGISTER_LOW_THRESH = 2, + ADS1115_REGISTER_HIGH_THRESH = 3, +}; + +#define FACTOR 32768.0 +static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; + +ADS1115::ADS1115(uint8_t address) +{ + m_address = address; + m_config = ADS1115_COMP_QUEUE_AFTER_ONE | + ADS1115_COMP_LATCH_NO | + ADS1115_COMP_POLARITY_ACTIVE_LOW | + ADS1115_COMP_MODE_WINDOW | + ADS1115_DATA_RATE_128_SPS | + ADS1115_MODE_SINGLE_SHOT | + ADS1115_MUX_GND_AIN0; + set_pga(ADS1115_PGA_ONE); +} + +uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.write(val>>8); + Wire.write(val & 0xFF); + return Wire.endTransmission(); +} + +uint16_t ADS1115::read_register(uint8_t reg) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.endTransmission(); + + uint8_t result = Wire.requestFrom((int)m_address, 2, 1); + if (result != 2) { + return 0; + } + + uint16_t val; + + val = Wire.read() << 8; + val |= Wire.read(); + return val; +} + +void ADS1115::begin() +{ + Wire.begin(); +} + +uint8_t ADS1115::trigger_sample() +{ + return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); +} + +uint8_t ADS1115::reset() +{ + Wire.beginTransmission(0); + Wire.write(0x6); + return Wire.endTransmission(); +} + +bool ADS1115::is_sample_in_progress() +{ + uint16_t val = read_register(ADS1115_REGISTER_CONFIG); + return (val & SAMPLE_BIT) == 0; +} + +int16_t ADS1115::read_sample() +{ + return read_register(ADS1115_REGISTER_CONVERSION); +} + +float ADS1115::sample_to_float(int16_t val) +{ + return val * ranges[m_voltage_range]; +} + +float ADS1115::read_sample_float() +{ + return sample_to_float(read_sample()); +} + +ADS1115 adc; + +void ADS1115_init(void) { + + ads1115_up=0; + if (!i2c_flg) return; + + adc.begin(); + adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); + adc.set_mode(ADS1115_MODE_CONTINUOUS); + adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); + adc.set_pga(ADS1115_PGA_TWO); + + int16_t val = adc.read_sample(); + ads1115_up=1; +} + +#endif + +char sml_start; +uint8_t dump2log=0; + +#define SML_SAVAILABLE Serial_available() +#define SML_SREAD Serial_read() +#define SML_SPEAK Serial_peek() + +bool Serial_available() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + if (!meter_ss[num-1]) return 0; + return meter_ss[num-1]->available(); +} + +uint8_t Serial_read() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + if (!meter_ss[num-1]) return 0; + return meter_ss[num-1]->read(); +} + +uint8_t Serial_peek() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + if (!meter_ss[num-1]) return 0; + return meter_ss[num-1]->peek(); +} + +uint8_t sml_logindex; + +void Dump2log(void) { + +int16_t index=0,hcnt=0; +uint32_t d_lastms; +uint8_t dchars[16]; + + + + if (dump2log&8) { + + while (SML_SAVAILABLE) { + log_data[index]=':'; + index++; + log_data[index]=' '; + index++; + d_lastms=millis(); + while ((millis()-d_lastms)<40) { + if (SML_SAVAILABLE) { + uint8_t c=SML_SREAD; + sprintf(&log_data[index],"%02x ",c); + dchars[hcnt]=c; + index+=3; + hcnt++; + if (hcnt>15) { + + log_data[index]='='; + index++; + log_data[index]='>'; + index++; + log_data[index]=' '; + index++; + for (uint8_t ccnt=0; ccnt<16; ccnt++) { + if (isprint(dchars[ccnt])) { + log_data[index]=dchars[ccnt]; + } else { + log_data[index]=' '; + } + index++; + } + break; + } + } + } + if (index>0) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + index=0; + hcnt=0; + } + } + } else { + if (meter_desc_p[(dump2log&7)-1].type=='o') { + + while (SML_SAVAILABLE) { + char c=SML_SREAD&0x7f; + if (c=='\n' || c=='\r') { + log_data[sml_logindex]=0; + AddLog(LOG_LEVEL_INFO); + sml_logindex=2; + log_data[0]=':'; + log_data[1]=' '; + break; + } + log_data[sml_logindex]=c; + if (sml_logindex2) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + } + } + } +} + +#ifdef ED300L +uint8_t sml_status[MAX_METERS]; +uint8_t g_mindex; +#endif + + +uint8_t *skip_sml(uint8_t *cp,int16_t *res) { + uint8_t len,len1,type; + len=*cp&0xf; + type=*cp&0x70; + if (type==0x70) { + + + cp++; + while (len--) { + len1=*cp&0x0f; + cp+=len1; + } + *res=0; + } else { + + *res=(signed char)*(cp+1); + cp+=len; + } + return cp; +} + + + +double sml_getvalue(unsigned char *cp,uint8_t index) { +uint8_t len,unit,type; +int16_t scaler,result; +int64_t value; +double dval; + + + +#ifdef ED300L + unsigned char *cpx=cp-5; + + if (*cp==0x64 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { + sml_status[g_mindex]=*(cp+3); + } + if (*cp==0x63 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { + sml_status[g_mindex]=*(cp+2); + } +#endif + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + scaler=result; + + + type=*cp&0x70; + len=*cp&0x0f; + cp++; + if (type==0x50 || type==0x60) { + + uint64_t uvalue=0; + uint8_t nlen=len; + while (--nlen) { + uvalue<<=8; + uvalue|=*cp++; + } + if (type==0x50) { + + switch (len-1) { + case 1: + + value=(signed char)uvalue; + break; + case 2: + +#ifdef DWS74_BUG + if (scaler==-2) { + value=(uint32_t)uvalue; + } else { + value=(int16_t)uvalue; + } +#else + value=(int16_t)uvalue; +#endif + break; + case 3: + case 4: + + value=(int32_t)uvalue; + break; + case 5: + case 6: + case 7: + case 8: + + value=(int64_t)uvalue; + break; + } + } else { + + value=uvalue; + } + + } else { + if (!(type&0xf0)) { + + + + if (len==9) { + + cp++; + uint32_t s1,s2; + s1=*cp<<16|*(cp+1)<<8|*(cp+2); + cp+=4; + s2=*cp<<16|*(cp+1)<<8|*(cp+2); + sprintf(&meter_id[index][0],"%u-%u",s1,s2); + } else { + + char *str=&meter_id[index][0]; + for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + } + return rVal; +} + +uint8_t sb_counter; + + +double CharToDouble(const char *str) +{ + + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } + + double left = 0; + if (*pt != '.') { + left = atoi(pt); + while (isdigit(*pt)) { pt++; } + } + + double right = 0; + if (*pt == '.') { + pt++; + right = atoi(pt); + while (isdigit(*pt)) { + pt++; + right /= 10.0; + } + } + + double result = left + right; + if (sign < 0) { + return -result; + } + return result; +} + + + +void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { + short count,count1; + for (count=0; countavailable()) { + meter_ss[meters]->read(); + } +} + + +void sml_shift_in(uint32_t meters,uint32_t shard) { + uint32_t count; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R') { + + for (count=0; countread(); + + if (meter_desc_p[meters].type=='o') { + smltbuf[meters][SML_BSIZ-1]=iob&0x7f; + } else if (meter_desc_p[meters].type=='s') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='r') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + if (meter_spos[meters]>=8) { + uint32_t mlen=smltbuf[meters][2]+5; + if (mlen>SML_BSIZ) mlen=SML_BSIZ; + if (meter_spos[meters]>=mlen) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } + } else if (meter_desc_p[meters].type=='p') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=7) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else if (meter_desc_p[meters].type=='R') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + } + else { + if (iob==EBUS_SYNC) { + + + if (meter_spos[meters]>4+5) { + + uint8_t tlen=smltbuf[meters][4]+5; + + if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { + ebus_esc(smltbuf[meters],tlen); + SML_Decode(meters); + } else { + + + } + } + meter_spos[meters]=0; + return; + } + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + } + sb_counter++; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R') SML_Decode(meters); +} + + + +void SML_Poll(void) { +uint32_t meters; + + for (meters=0; metersavailable()) { + sml_shift_in(meters,0); + } + } + } +} + + +void SML_Decode(uint8_t index) { + const char *mp=(const char*)meter_p; + int8_t mindex; + uint8_t *cp; + uint8_t dindex=0,vindex=0; + delay(0); + while (mp != NULL) { + + if (*mp==0) break; + + + mindex=((*mp)&7)-1; + + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + if (index!=mindex) goto nextsect; + + + cp=&smltbuf[mindex][0]; + + + if (*mp=='=') { + + mp++; + + if (*mp=='m' && !sb_counter) { + + + mp++; + while (*mp==' ') mp++; + + double dvar; + uint8_t opr; + uint32_t ind; + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + dvar=meter_vars[ind-1]; + for (uint8_t p=0;p<5;p++) { + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + opr=*mp; + mp++; + uint8_t iflg=0; + if (*mp=='#') { + iflg=1; + mp++; + } + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + switch (opr) { + case '+': + if (iflg) dvar+=ind; + else dvar+=meter_vars[ind-1]; + break; + case '-': + if (iflg) dvar-=ind; + else dvar-=meter_vars[ind-1]; + break; + case '*': + if (iflg) dvar*=ind; + else dvar*=meter_vars[ind-1]; + break; + case '/': + if (iflg) dvar/=ind; + else dvar/=meter_vars[ind-1]; + break; + } + while (*mp==' ') mp++; + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + } + } else if (*mp=='d') { + + if (dindex='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + uint32_t delay=atoi(mp)*1000; + uint32_t dtime=millis()-dtimes[dindex]; + if (dtime>delay) { + + dtimes[dindex]=millis(); + double vdiff = meter_vars[ind-1]-dvalues[dindex]; + dvalues[dindex]=meter_vars[ind-1]; + meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); + + mp=strchr(mp,'@'); + if (mp) { + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + dindex++; + } + } else if (*mp=='h') { + + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + } else { + + uint8_t found=1; + uint32_t ebus_dval=99; + float mbus_dval=99; + while (*mp!='@') { + if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { + if (*mp++!=*cp++) { + found=0; + } + } else { + if (meter_desc_p[mindex].type=='s') { + + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } else { + + + if (*mp=='x' && *(mp+1)=='x') { + + mp+=2; + cp++; + } else if (!strncmp(mp,"UUuuUUuu",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (!strncmp(mp,"SSssSSss",8)) { + int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ + uint16_t val = cp[0]|(cp[1]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='u' && *(mp+1)=='u') { + uint8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { + int16_t val = *cp|(*(cp+1)<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { + int16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } + else if (*mp=='s' && *(mp+1)=='s') { + int8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } + else if (!strncmp(mp,"ffffffff",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"FFffFFff",8)) { + + uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"eeeeee",6)) { + uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); + mbus_dval=val; + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"vvvvvv",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"cccccc",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"pppp",4)) { + mbus_dval=(float)((cp[0]<<8)|cp[1]); + mp+=4; + cp+=2; + } + else { + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } + } + } + } + if (found) { + + mp++; +#ifdef ED300L + g_mindex=mindex; +#endif + if (*mp=='#') { + + mp++; + if (meter_desc_p[mindex].type=='o') { + for (uint8_t p=0;p>=shift; + ebus_dval&=1; + mp+=2; + } + if (*mp=='i') { + + mp++; + uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); + if (mb_index!=meter_desc_p[mindex].index) { + goto nextsect; + } + uint16_t pos = smltbuf[mindex][2]+3; + if (pos>32) pos=32; + uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],pos); + if (lowByte(crc)!=smltbuf[mindex][pos]) goto nextsect; + if (highByte(crc)!=smltbuf[mindex][pos+1]) goto nextsect; + dval=mbus_dval; + + mp++; + } else { + if (meter_desc_p[mindex].type=='p') { + uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); + if (crc!=smltbuf[mindex][6]) goto nextsect; + dval=mbus_dval; + } else { + dval=ebus_dval; + } + } + + } +#ifdef USE_SML_MEDIAN_FILTER + if (meter_desc_p[mindex].flag&16) { + meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); + } else { + meter_vars[vindex]=dval; + } +#else + meter_vars[vindex]=dval; +#endif + + + double fac=CharToDouble((char*)mp); + meter_vars[vindex]/=fac; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + } +nextsect: + + if (vindex=meters_used) lastmind=0; + while (mp != NULL) { + if (*mp==0) break; + + mindex=((*mp)&7)-1; + + if (mindex<0 || mindex>=meters_used) mindex=0; + if (meter_desc_p[mindex].prefix[0]=='*' && meter_desc_p[mindex].prefix[1]==0) { + nojson=1; + } else { + nojson=0; + } + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp+=2; + + if (json) { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + uint8_t i; + for (i=0;isml_counters[index].sml_debounce) { + RtcSettings.pulse_counter[index]++; + sml_counters[index].sml_cnt_updated=1; + + } + } else { + + sml_counters[index].sml_counter_ltime=millis(); + } +} + +void ICACHE_RAM_ATTR SML_CounterUpd1(void) { + SML_CounterUpd(0); +} + +void ICACHE_RAM_ATTR SML_CounterUpd2(void) { + SML_CounterUpd(1); +} + +void ICACHE_RAM_ATTR SML_CounterUpd3(void) { + SML_CounterUpd(2); +} + +void ICACHE_RAM_ATTR SML_CounterUpd4(void) { + SML_CounterUpd(3); +} + +#ifdef USE_SCRIPT +struct METER_DESC script_meter_desc[MAX_METERS]; +uint8_t *script_meter; +#endif + +#ifndef METER_DEF_SIZE +#define METER_DEF_SIZE 3000 +#endif + + + +#ifdef SML_REPLACE_VARS + +#define SML_SRCBSIZE 256 + +uint32_t SML_getlinelen(char *lp) { +uint32_t cnt; + for (cnt=0; cnt') break; + if (*lp==0) break; + } + + return mlen+32; +} +#else +uint32_t SML_getscriptsize(char *lp) { + uint32_t mlen=0; + for (uint32_t cnt=0;cnt 0)) { + return true; + } + return false; +} + +void SML_Init(void) { + meters_used=METERS_USED; + meter_desc_p=meter_desc; + meter_p=meter; + + sml_desc_cnt=0; + + for (uint32_t cnt=0;cntM",-2,0); + if (meter_script==99) { + + if (script_meter) free(script_meter); + script_meter=0; + uint8_t *tp=0; + uint16_t index=0; + uint8_t section=0; + uint8_t srcpin=0; + char *lp=glob_script_mem.scriptptr; + sml_send_blocks=0; + while (lp) { + if (!section) { + if (*lp=='>' && *(lp+1)=='M') { + lp+=2; + meters_used=strtol(lp,0,10); + section=1; + uint32_t mlen=SML_getscriptsize(lp); + if (mlen==0) return; + script_meter=(uint8_t*)calloc(mlen,1); + if (!script_meter) { + goto dddef_exit; + } + tp=script_meter; + goto next_line; + } + } + else { + if (!*lp || *lp=='#' || *lp=='>') { + if (*(tp-1)=='|') *(tp-1)=0; + break; + } + if (*lp=='+') { + + + lp++; + index=*lp&7; + lp+=2; + if (index<1 || index>meters_used) goto next_line; + index--; + srcpin=strtol(lp,&lp,10); + if (Gpio_used(srcpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); +dddef_exit: + if (script_meter) free(script_meter); + script_meter=0; + meters_used=METERS_USED; + goto init10; + } + script_meter_desc[index].srcpin=srcpin; + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].type=*lp; + lp+=2; + script_meter_desc[index].flag=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].params=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].prefix[7]=0; + for (uint32_t cnt=0; cnt<8; cnt++) { + if (*lp==SCRIPT_EOL || *lp==',') { + script_meter_desc[index].prefix[cnt]=0; + break; + } + script_meter_desc[index].prefix[cnt]=*lp++; + } + if (*lp==',') { + lp++; + script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (Gpio_used(script_meter_desc[index].trxpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); + goto dddef_exit; + } + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].tsecs=strtol(lp,&lp,10); + if (*lp==',') { + lp++; + char txbuff[256]; + uint32_t txlen=0,tx_entries=1; + for (uint32_t cnt=0; cntmeters_used) goto next_line; + while (1) { + if (*lp1==0) { + *tp++='|'; + goto next_line; + } + *tp++=*lp1++; + index++; + if (index>=METER_DEF_SIZE) break; + } + } +#else + + if (*lp=='-' || isdigit(*lp)) { + + + if (*lp=='-') lp++; + uint8_t mnum=strtol(lp,0,10); + if (mnum<1 || mnum>meters_used) goto next_line; + while (1) { + if (*lp==SCRIPT_EOL) { + if (*(tp-1)!='|') *tp++='|'; + goto next_line; + } + *tp++=*lp++; + index++; + if (index>=METER_DEF_SIZE) break; + } + } +#endif + + } + +next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + *tp=0; + meter_desc_p=script_meter_desc; + meter_p=script_meter; + } + } +#endif + +init10: + typedef void (*function)(); + function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; + uint8_t cindex=0; + + for (byte i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; + sml_counters[i].sml_cnt_last_ts=millis(); + } + uint32_t uart_index=2; + for (uint8_t meters=0; meterssetRxBufferSize(TMSBSIZ); +#else + meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,0,TMSBSIZ); +#endif +#endif + +#ifdef ESP32 + if (meter_desc_p[meters].type=='M') { + meter_ss[meters]->begin(meter_desc_p[meters].params, SERIAL_8E1,meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin); + } else { + meter_ss[meters]->begin(meter_desc_p[meters].params,SERIAL_8N1,meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin); + } +#else + if (meter_ss[meters]->begin(meter_desc_p[meters].params)) { + meter_ss[meters]->flush(); + } + if (meter_ss[meters]->hardwareSerial()) { + if (meter_desc_p[meters].type=='M') { + Serial.begin(meter_desc_p[meters].params, SERIAL_8E1); + } + ClaimSerial(); + + } +#endif + } + } + +} + + +#ifdef USE_SML_SCRIPT_CMD +uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + +#ifdef ESP32 + meter_ss[meter]->flush(); + meter_ss[meter]->updateBaudRate(br); + + + + + + +#else + if (meter_ss[meter]->begin(br)) { + meter_ss[meter]->flush(); + } + if (meter_ss[meter]->hardwareSerial()) { + if (meter_desc_p[meter].type=='M') { + Serial.begin(br, SERIAL_8E1); + } + } +#endif + return 1; +} + +uint32_t SML_Status(uint32_t meter) { + if (meter<1 || meter>meters_used) return 0; + meter--; +#ifdef ED300L + return sml_status[meter]; +#else + return 0; +#endif +} + + +uint32_t SML_Write(uint32_t meter,char *hstr) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + SML_Send_Seq(meter,hstr); + return 1; +} + +uint32_t SML_Read(int32_t meter,char *str, uint32_t slen) { +uint8_t hflg=0; + if (meter<0) { + meter=abs(meter); + hflg=1; + } + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + + if (!meter_spos[meter]) { + return 0; + } + + smltbuf[meter][meter_spos[meter]]=0; + + if (!hflg) { + strlcpy(str,(char*)&smltbuf[meter][0],slen); + } else { + uint32_t index=0; + for (uint32_t cnt=0; cnt=slen-2) break; + } + } + meter_spos[meter]=0; + return 1; +} + +float SML_GetVal(uint32_t index) { + if (index<1 && index>SML_MAX_VARS) { index = 1;} + return meter_vars[index-1]; +} + +#endif + + +void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { + pinMode(ledpin, OUTPUT); + if (digitalRead(srcpin)) { + digitalWrite(ledpin,LOW); + } else { + digitalWrite(ledpin,HIGH); + } +} + + +void SML_Counter_Poll(void) { +uint16_t meters,cindex=0; +uint32_t ctime=millis(); + + for (meters=0; meters0) { + if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { + sml_counters[cindex].sml_cnt_last_ts=ctime; + + if (meter_desc_p[meters].flag&2) { + +#ifdef ANALOG_OPTO_SENSOR + if (ads1115_up) { + int16_t val = adc.read_sample(); + if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; + if (val10) { + sml_counters[cindex].sml_cnt_last_ts=ctime; +#ifdef DEBUG_CNT_LED1 + if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); +#endif +#ifdef DEBUG_CNT_LED2 + if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); +#endif + } + + if (sml_counters[cindex].sml_cnt_updated) { + InjektCounterValue(sml_counters[cindex].sml_cnt_old_state,RtcSettings.pulse_counter[cindex]); + sml_counters[cindex].sml_cnt_updated=0; + } + + + } + cindex++; + } + } +} + +#ifdef USE_SCRIPT +char *SML_Get_Sequence(char *cp,uint32_t index) { + if (!index) return cp; + uint32_t cindex=0; + while (cp) { + cp=strchr(cp,','); + if (cp) { + cp++; + cindex++; + if (cindex==index) { + return cp; + } + } + } + return cp; +} + +void SML_Check_Send(void) { + sml_100ms_cnt++; + char *cp; + for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { + + if ((sml_100ms_cnt>=script_meter_desc[cnt].tsecs)) { + sml_100ms_cnt=0; + + if (script_meter_desc[cnt].max_index>1) { + script_meter_desc[cnt].index++; + if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { + script_meter_desc[cnt].index=0; + sml_desc_cnt++; + } + cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + + } else { + cp=script_meter_desc[cnt].txmem; + + sml_desc_cnt++; + } + + SML_Send_Seq(cnt,cp); + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + break; + } + } else { + sml_desc_cnt++; + } + + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + } +} + +uint8_t sml_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + + +void SML_Send_Seq(uint32_t meter,char *seq) { + uint8_t sbuff[32]; + uint8_t *ucp=sbuff,slen=0; + char *cp=seq; + uint8_t rflg = 0; + if (*cp=='r') { + rflg = 1; + cp++; + } + while (*cp) { + if (!*cp || !*(cp+1)) break; + if (*cp==',') break; + uint8_t iob=(sml_hexnibble(*cp) << 4) | sml_hexnibble(*(cp+1)); + cp+=2; + *ucp++=iob; + slen++; + if (slen>=sizeof(sbuff)) break; + } + if (script_meter_desc[meter].type=='m' || script_meter_desc[meter].type=='M') { + if (!rflg) { + *ucp++=0; + *ucp++=2; + slen+=2; + } + + uint16_t crc = MBUS_calculateCRC(sbuff,slen); + *ucp++=lowByte(crc); + *ucp++=highByte(crc); + slen+=2; + } + if (script_meter_desc[meter].type=='o') { + for (uint32_t cnt=0;cntwrite(sbuff,slen); +} +#endif + +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { + uint16_t crc, flag; + crc = 0xFFFF; + for (uint32_t i = 0; i < num; i++) { + crc ^= frame[i]; + for (uint32_t j = 8; j; j--) { + if ((crc & 0x0001) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} + +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { + uint16_t crc = 0; + for (uint32_t i = 0; i < len; i++) crc += *data++; + return (uint8_t)(crc & 0xFF); +} + + +uint8_t CalcEvenParity(uint8_t data) { +uint8_t parity=0; + + while(data) { + parity^=(data &1); + data>>=1; + } + return parity; +} +# 2540 "/workspace/Tasmota/tasmota/xsns_53_sml.ino" +bool XSNS_53_cmd(void) { + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='d') { + + cp++; + uint8_t index=atoi(cp); + if ((index&7)>meters_used) index=1; + if (index>0 && meter_desc_p[(index&7)-1].type=='c') { + index=0; + } + dump2log=index; + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); + } else if (*cp=='c') { + + cp++; + uint8_t index=*cp&7; + if (index<1 || index>MAX_COUNTERS) index=1; + cp++; + while (*cp==' ') cp++; + if (isdigit(*cp)) { + uint32_t cval=atoi(cp); + while (isdigit(*cp)) cp++; + RtcSettings.pulse_counter[index-1]=cval; + uint8_t cindex=0; + for (uint8_t meters=0; metersaddress, INA226_REG_CALIBRATION, si->calibrationValue); + +} + + + + + + +bool Ina226TestPresence(uint8_t device) +{ + + + + uint16_t config = I2cRead16( Ina226Info[device].address, INA226_REG_CONFIG ); + + + if (config != Ina226Info[device].config) + return false; + + return true; + +} + +void Ina226ResetActive(void) +{ + Ina226Info_t *p = Ina226Info; + + for (uint32_t i = 0; i < INA226_MAX_ADDRESSES; i++) { + p = &Ina226Info[i]; + + uint8_t addr = p->address; + if (addr) { + I2cResetActive(addr); + } + } +} + + + + + +void Ina226Init() +{ + uint32_t i; + + Ina226sFound = 0; + + Ina226Info_t *p = Ina226Info; +# 215 "/workspace/Tasmota/tasmota/xsns_54_ina226.ino" + for (i = 0; i < 4; i++){ + *p = {0}; + } + + + + + + for (i = 0; i < INA226_MAX_ADDRESSES; i++){ + uint8_t addr = pgm_read_byte(probeAddresses + i); + + if (I2cActive(addr)) { continue; } + + + + + if (!Settings.ina226_i_fs[i]) + continue; + + + + + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ + + AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); + continue; + } + + + + uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); + + + if (INA226_RES_CONFIG != config) + continue; + + + config = INA226_DEF_CONFIG; + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) + continue; + + + p = &Ina226Info[i]; + + p->address = addr; + + p->config = config; + + + p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; + + + + uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); + + + + p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); + + p->present = true; + + + Ina226SetCalibration(i); + + I2cSetActiveFound(addr, Ina226Str); + + Ina226sFound++; + } +} + + + + + +float Ina226ReadBus_v(uint8_t device) +{ + uint8_t addr = Ina226Info[device].address; + int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); + + float result = ((float) reg_bus_v) * 0.00125f; + + return result; + +} + + + + + +float Ina226ReadShunt_i(uint8_t device) +{ + uint8_t addr = Ina226Info[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); + + float result = ((float) reg_shunt_i) * Ina226Info[device].i_lsb; + + return result; +} + + + + + +float Ina226ReadPower_w(uint8_t device) +{ + uint8_t addr = Ina226Info[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); + + float result = ((float) reg_shunt_i) * (Ina226Info[device].i_lsb * 25.0); + + return result; +} + + + + + + +void Ina226Read(uint8_t device) +{ + + voltages[device] = Ina226ReadBus_v(device); + currents[device] = Ina226ReadShunt_i(device); + powers[device] = Ina226ReadPower_w(device); + + + + +} + + + + + +void Ina226EverySecond() +{ + + for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ + + if (Ina226sFound && Ina226Info[device].present && Ina226TestPresence(device)){ + Ina226Read(device); + } + else { + powers[device] = currents[device] = voltages[device] = 0.0f; + + + + + + + Ina226Info[device].present = false; + } + } +} + + + + + +bool Ina226CommandSensor() +{ + bool serviced = true; + bool show_config = false; + char param_str[64]; + char *cp, *params[4]; + uint8_t i, param_count, device, p1 = XdrvMailbox.payload; + uint32_t r_shunt_uohms; + uint16_t compact_r_shunt_uohms; + + + + + + if (XdrvMailbox.data_len > 62){ + return false; + } + + strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); + param_str[XdrvMailbox.data_len] = 0; + + + for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) + if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ + param_str[i] = 0; + params[param_count] = cp; + + param_count++; + cp = param_str + i + 1; + } + + + if (p1 < 10 || p1 >= 50){ + + switch (p1){ + case 1: + Ina226ResetActive(); + Ina226Init(); + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Ina226sFound\":%d}}"),Ina226sFound); + break; + + case 2: + restart_flag = 2; + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); + break; + + default: + serviced = false; + } + } + else if (p1 < 50){ + + device = (p1 / 10) - 1; + switch (p1 % 10){ + case 0: + show_config = true; + break; + + case 1: + r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); + + + + if (r_shunt_uohms > 32767){ + uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; + Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); + } + else + Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; + + + show_config = true; + break; + + case 2: + Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); + + show_config = true; + break; + + + default: + serviced = false; + break; + } + } + else + serviced = false; + + if (show_config) { + char shunt_r_str[16]; + char fs_i_str[16]; + + + r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); + dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); + + dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); + + Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), + device + 1, shunt_r_str, fs_i_str); + } + + return serviced; +} + + + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA226_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina226Show(bool json) +{ + int i, num_found; + for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { + + if (!Ina226Info[i].present) + continue; + + num_found++; + + char voltage[16]; + dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); + char current[16]; + dtostrfd(currents[i], Settings.flag2.current_resolution, current); + char power[16]; + dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); + char name[16]; + snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); + + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%d,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, i, voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); +#endif + } + + } + +} +# 546 "/workspace/Tasmota/tasmota/xsns_54_ina226.ino" +bool Xsns54(byte callback_id) +{ + if (!I2cEnabled(XI2C_35)) { return false; } + + + bool result = false; + + + switch (callback_id) { + case FUNC_EVERY_SECOND: + Ina226EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina226Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina226Show(0); + break; +#endif + case FUNC_COMMAND_SENSOR: + if (XSNS_54 == XdrvMailbox.index) { + result = Ina226CommandSensor(); + } + break; + case FUNC_INIT: + Ina226Init(); + break; + } + + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_55_hih_series.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_55_hih_series.ino" +#ifdef USE_I2C +#ifdef USE_HIH6 +# 33 "/workspace/Tasmota/tasmota/xsns_55_hih_series.ino" +#define XSNS_55 55 +#define XI2C_36 36 + +#define HIH6_ADDR 0x27 + +struct HIH6 { + float temperature = 0; + float humidity = 0; + uint8_t valid = 0; + uint8_t type = 0; + char types[4] = "HIH"; +} Hih6; + +bool Hih6Read(void) +{ + Wire.beginTransmission(HIH6_ADDR); + if (Wire.endTransmission() != 0) { return false; } + + delay(40); + + uint8_t data[4]; + Wire.requestFrom(HIH6_ADDR, 4); + if (4 == Wire.available()) { + data[0] = Wire.read(); + data[1] = Wire.read(); + data[2] = Wire.read(); + data[3] = Wire.read(); + } else { return false; } + + + + Hih6.humidity = ConvertHumidity(((float)(((data[0] & 0x3F) << 8) | data[1]) * 100.0) / 16383.0); + + int temp = ((data[2] << 8) | (data[3] & 0xFC)) / 4; + Hih6.temperature = ConvertTemp(((float)temp / 16384.0) * 165.0 - 40.0); + + Hih6.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Hih6Detect(void) +{ + if (I2cActive(HIH6_ADDR)) { return; } + + if (uptime < 2) { delay(20); } + Hih6.type = Hih6Read(); + if (Hih6.type) { + I2cSetActiveFound(HIH6_ADDR, Hih6.types); + } +} + +void Hih6EverySecond(void) +{ + if (uptime &1) { + + if (!Hih6Read()) { + AddLogMissed(Hih6.types, Hih6.valid); + } + } +} + +void Hih6Show(bool json) +{ + if (Hih6.valid) { + TempHumDewShow(json, (0 == tele_period), Hih6.types, Hih6.temperature, Hih6.humidity); + } +} + + + + + +bool Xsns55(uint8_t function) +{ + if (!I2cEnabled(XI2C_36)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Hih6Detect(); + } + else if (Hih6.type) { + switch (function) { + case FUNC_EVERY_SECOND: + Hih6EverySecond(); + break; + case FUNC_JSON_APPEND: + Hih6Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Hih6Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_56_hpma.ino" +# 21 "/workspace/Tasmota/tasmota/xsns_56_hpma.ino" +#ifdef USE_HPMA +# 31 "/workspace/Tasmota/tasmota/xsns_56_hpma.ino" +#define XSNS_56 56 + +#include +#include + +TasmotaSerial *HpmaSerial; +HPMA115S0 *hpma115S0; + +uint8_t hpma_type = 1; +uint8_t hpma_valid = 0; + +struct hpmadata { + unsigned int pm10; + unsigned int pm2_5; +} hpma_data; + + + +void HpmaSecond(void) +{ + unsigned int pm2_5, pm10; + + + + + if (hpma115S0->ReadParticleMeasurement(&pm2_5, &pm10)) { + hpma_data.pm2_5 = pm2_5; + hpma_data.pm10 = pm10; + hpma_valid = 1; + } + +} + +void HpmaInit(void) +{ + hpma_type = 0; + if (PinUsed(GPIO_HPMA_RX) && PinUsed(GPIO_HPMA_TX)) { + HpmaSerial = new TasmotaSerial(Pin(GPIO_HPMA_RX), Pin(GPIO_HPMA_TX), 1); + hpma115S0 = new HPMA115S0(*HpmaSerial); + + if (HpmaSerial->begin(9600)) { + if (HpmaSerial->hardwareSerial()) { + ClaimSerial(); + } + hpma_type = 1; + hpma115S0->Init(); + hpma115S0->StartParticleMeasurement(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_HPMA_SNS[] PROGMEM = + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void HpmaShow(bool json) +{ + if (hpma_valid) { + char pm10[33]; + snprintf_P(pm10, 33, PSTR("%d"), hpma_data.pm10); + char pm2_5[33]; + snprintf_P(pm2_5, 33, PSTR("%d"), hpma_data.pm2_5); + + if (json) { + ResponseAppend_P(PSTR(",\"HPMA\":{\"PM2.5\":%d,\"PM10\":%d}"), hpma_data.pm2_5, hpma_data.pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HPMA_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns56(uint8_t function) +{ + bool result = false; + + if (hpma_type) { + switch (function) { + case FUNC_INIT: + HpmaInit(); + break; + case FUNC_EVERY_SECOND: + HpmaSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_56 == XdrvMailbox.index) { + return true; + } + break; + case FUNC_JSON_APPEND: + HpmaShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HpmaShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_57_tsl2591.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_57_tsl2591.ino" +#ifdef USE_I2C +#ifdef USE_TSL2591 + + + + + + + +#define XSNS_57 57 +#define XI2C_40 40 + +#define TSL2591_ADDRESS 0x29 + +#include + +Adafruit_TSL2591 tsl = Adafruit_TSL2591(); + +uint8_t tsl2591_type = 0; +uint8_t tsl2591_valid = 0; +float tsl2591_lux = 0; + +void Tsl2591Init(void) +{ + + if (I2cSetDevice(0x29)) { + if (tsl.begin()) { + tsl.setGain(TSL2591_GAIN_MED); + tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); + tsl2591_type = 1; + I2cSetActiveFound(TSL2591_ADDRESS, "TSL2591"); + } + } +} + +bool Tsl2591Read(void) +{ + uint32_t lum = tsl.getFullLuminosity(); + uint16_t ir, full; + ir = lum >> 16; + full = lum & 0xFFFF; + tsl2591_lux = tsl.calculateLux(full, ir); + tsl2591_valid = 1; +} + +void Tsl2591EverySecond(void) +{ + Tsl2591Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2591[] PROGMEM = + "{s}TSL2591 " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"; +#endif + +void Tsl2591Show(bool json) +{ + if (tsl2591_valid) { + char lux_str[10]; + dtostrf(tsl2591_lux, sizeof(lux_str)-1, 3, lux_str); + if (json) { + ResponseAppend_P(PSTR(",\"TSL2591\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, tsl2591_lux); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2591, lux_str); +#endif + } + } +} + + + + + +bool Xsns57(uint8_t function) +{ + if (!I2cEnabled(XI2C_40)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2591Init(); + } + else if (tsl2591_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2591EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2591Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2591Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_58_dht12.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_58_dht12.ino" +#ifdef USE_I2C +#ifdef USE_DHT12 + + + + + + +#define XSNS_58 58 +#define XI2C_41 41 + +#define DHT12_ADDR 0x5C + +struct DHT12 { + float temperature = NAN; + float humidity = NAN; + uint8_t valid = 0; + uint8_t count = 0; + char name[6] = "DHT12"; +} Dht12; + +bool Dht12Read(void) +{ + if (Dht12.valid) { Dht12.valid--; } + + Wire.beginTransmission(DHT12_ADDR); + Wire.write(0); + if (Wire.endTransmission() != 0) { return false; } + + delay(50); + + Wire.requestFrom(DHT12_ADDR, 5); + delay(5); + uint8_t humidity = Wire.read(); + uint8_t humidityTenth = Wire.read(); + uint8_t temp = Wire.read(); + uint8_t tempTenth = Wire.read(); + uint8_t checksum = Wire.read(); + + Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); + Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); + + if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } + + Dht12.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Dht12Detect(void) +{ + if (I2cActive(DHT12_ADDR)) { return; } + + if (Dht12Read()) { + I2cSetActiveFound(DHT12_ADDR, Dht12.name); + Dht12.count = 1; + } +} + +void Dht12EverySecond(void) +{ + if (uptime &1) { + + if (!Dht12Read()) { + AddLogMissed(Dht12.name, Dht12.valid); + } + } +} + +void Dht12Show(bool json) +{ + if (Dht12.valid) { + TempHumDewShow(json, (0 == tele_period), Dht12.name, Dht12.temperature, Dht12.humidity); + } +} + + + + + +bool Xsns58(uint8_t function) +{ + if (!I2cEnabled(XI2C_41)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Dht12Detect(); + } + else if (Dht12.count) { + switch (function) { + case FUNC_EVERY_SECOND: + Dht12EverySecond(); + break; + case FUNC_JSON_APPEND: + Dht12Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Dht12Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_59_ds1624.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_59_ds1624.ino" +#ifdef USE_I2C +#ifdef USE_DS1624 + + + + + + + +#define XSNS_59 59 +#define XI2C_42 42 + +#define DS1624_MEM_REGISTER 0x17 +#define DS1624_CONF_REGISTER 0xAC +#define DS1624_TEMP_REGISTER 0xAA +#define DS1624_START_REGISTER 0xEE +#define DS1624_STOP_REGISTER 0x22 + +#define DS1621_COUNTER_REGISTER 0xA8 +#define DS1621_SLOPE_REGISTER 0xA9 + +#define DS1621_CFG_1SHOT (1<<0) +#define DS1621_CFG_DONE (1<<7) + +enum { + DS1624_TYPE_DS1624, + DS1624_TYPE_DS1621 +}; + +#define DS1624_MAX_SENSORS 8 + +bool ds1624_init = false; + +struct { + float value; + uint8_t type; + int errcnt; + int misscnt; + bool valid; + char name[9]; +} ds1624_sns[DS1624_MAX_SENSORS]; + +uint32_t DS1624_Idx2Addr(uint32_t idx) { + return 0x48 + idx; +} + +void DS1624_Restart(uint8_t config, uint32_t idx) { + uint32_t addr = DS1624_Idx2Addr(idx); + if ((config & 1) == 1) { + config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + delay(10); + AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); + } + I2cValidRead(addr, DS1624_START_REGISTER, 1); +} + +void DS1624_HotPlugUp(uint32_t idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + if (I2cActive(addr)) { return; } + if (!I2cSetDevice(addr)) { return; } + + uint8_t config; + if (I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + uint8_t tmp; + ds1624_sns[idx].type = (I2cValidRead8(&tmp, addr, DS1624_MEM_REGISTER)) ? DS1624_TYPE_DS1624 : DS1624_TYPE_DS1621; + + snprintf_P(ds1624_sns[idx].name, sizeof(ds1624_sns[idx].name), PSTR("DS162%c%c%d"), + (ds1624_sns[idx].type == DS1624_TYPE_DS1621) ? '1' : '4', IndexSeparator(), idx); + I2cSetActiveFound(addr, ds1624_sns[idx].name); + + ds1624_sns[idx].valid = true; + ds1624_sns[idx].errcnt = 0; + ds1624_sns[idx].misscnt = 0; + DS1624_Restart(config,idx); + AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); + } +} + +void DS1624_HotPlugDown(int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + if (!I2cActive(addr)) { return; } + I2cResetActive(addr); + ds1624_sns[idx].valid = false; + AddLog_P2(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); +} + +bool DS1624GetTemp(float *value, int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + uint8_t config; + if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + ds1624_sns[idx].misscnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); + return false; + } + ds1624_sns[idx].misscnt=0; + if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { + ds1624_sns[idx].errcnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); + DS1624_Restart(config, idx); + return false; + } + + uint16_t t; + if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; } + if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) { + *value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625; + } else { + + *value = ((float)(int8_t)(t>>8)); + uint8_t remain; + if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; } + uint8_t perc; + if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; } + float fix=(float)(perc - remain)/(float)perc; + *value+=fix; + } + ds1624_sns[idx].errcnt=0; + config &= ~(DS1621_CFG_DONE); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + return true; +} + +void DS1624HotPlugScan(void) +{ + uint16_t t; + + for (uint32_t idx = 0; idx < DS1624_MAX_SENSORS; idx++) { + uint32_t addr = DS1624_Idx2Addr(idx); + if (I2cActive(addr) && !ds1624_sns[idx].valid) { + continue; + } + if (ds1624_sns[idx].valid) { + if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) { + DS1624_HotPlugDown(idx); + continue; + } + } + DS1624_HotPlugUp(idx); + } +} + +void DS1624EverySecond(void) +{ + float t; + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + if (!DS1624GetTemp(&t, i)) { continue; } + ds1624_sns[i].value = ConvertTemp(t); + } +} + +void DS1624Show(bool json) +{ + char temperature[33]; + bool once = true; + + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + + dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, ds1624_sns[i].name, temperature); + if ((0 == tele_period) && once) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, ds1624_sns[i].value); +#endif + once = false; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); +#endif + } + } +} + + + + + +bool Xsns59(uint8_t function) +{ + if (!I2cEnabled(XI2C_42)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + if (!ds1624_init) { + memset(ds1624_sns, 0, sizeof(ds1624_sns)); + ds1624_init = true; + DS1624HotPlugScan(); + } + } + switch (function) { + case FUNC_HOTPLUG_SCAN: + DS1624HotPlugScan(); + break; + case FUNC_EVERY_SECOND: + DS1624EverySecond(); + break; + case FUNC_JSON_APPEND: + DS1624Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DS1624Show(0); + break; +#endif + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_60_GPS.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_60_GPS.ino" +#ifdef USE_GPS +#if defined(ESP32) && defined(USE_FLOG) + #undef USE_FLOG + #warning FLOG deactivated on ESP32 +#endif +# 117 "/workspace/Tasmota/tasmota/xsns_60_GPS.ino" +#define XSNS_60 60 + +#include "NTPServer.h" +#include "NTPPacket.h" + + + + + +#define D_CMND_UBX "UBX" + +const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}"; + +const char kUBXTypes[] PROGMEM = "UBX"; + +#define UBX_LAT_LON_THRESHOLD 1000 + +#define UBX_SERIAL_BUFFER_SIZE 256 +#define UBX_TCP_PORT 1234 +#define NTP_MILLIS_OFFSET 50 + + + + + +const char UBLOX_INIT[] PROGMEM = { + + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, + + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, + + + + + + +}; + +char UBX_name[4]; + +struct UBX_t { + const char UBX_HEADER[2] = { 0xB5, 0x62 }; + const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 }; + const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 }; + const char NAV_TIME_HEADER[2] = { 0x01, 0x21 }; + + struct entry_t { + int32_t lat; + int32_t lon; + uint32_t time; + }; + + union { + entry_t values; + uint8_t bytes[sizeof(entry_t)]; + } rec_buffer; + + struct POLL_MSG { + uint8_t cls; + uint8_t id; + uint16_t zero; + }; + + struct NAV_POSLLH { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + int32_t lon; + int32_t lat; + int32_t alt; + int32_t hMSL; + uint32_t hAcc; + uint32_t vAcc; + }; + + struct NAV_STATUS { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint8_t gpsFix; + uint8_t flags; + uint8_t fixStat; + uint8_t flags2; + uint32_t ttff; + uint32_t msss; + }; + + struct NAV_TIME_UTC { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint32_t tAcc; + int32_t nano; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + struct { + uint8_t UTC:1; + uint8_t WKN:1; + uint8_t TOW:1; + uint8_t padding:5; + } valid; + }; + + struct CFG_RATE { + uint8_t cls; + uint8_t id; + uint16_t len; + uint16_t measRate; + uint16_t navRate; + uint16_t timeRef; + char CK[2]; + }; + + struct { + uint32_t last_iTOW; + int32_t last_alt; + uint32_t last_hAcc; + uint32_t last_vAcc; + uint8_t gpsFix; + uint8_t non_empty_loops; + uint16_t log_interval; + int32_t timeOffset; + } state; + + struct { + uint32_t init:1; + uint32_t filter_noise:1; + uint32_t send_when_new:1; + uint32_t send_UI_only:1; + uint32_t runningNTP:1; + + uint32_t forceUTCupdate:1; + uint32_t runningVPort:1; + + } mode; + + union { + NAV_POSLLH navPosllh; + NAV_STATUS navStatus; + NAV_TIME_UTC navTime; + POLL_MSG pollMsg; + CFG_RATE cfgRate; + } Message; + + uint8_t TCPbuf[UBX_SERIAL_BUFFER_SIZE]; + size_t TCPbufSize; +} UBX; + +enum UBXMsgType { + MT_NONE, + MT_NAV_POSLLH, + MT_NAV_STATUS, + MT_NAV_TIME, + MT_POLL +}; + +#ifdef USE_FLOG +FLOG *Flog = nullptr; +#endif +TasmotaSerial *UBXSerial; + +NtpServer timeServer(PortUdp); + +WiFiServer vPortServer(UBX_TCP_PORT); +WiFiClient vPortClient; + + + + + +void UBXcalcChecksum(char* CK, size_t msgSize) +{ + memset(CK, 0, 2); + for (int i = 0; i < msgSize; i++) { + CK[0] += ((char*)(&UBX.Message))[i]; + CK[1] += CK[0]; + } +} + +bool UBXcompareMsgHeader(const char* msgHeader) +{ + char* ptr = (char*)(&UBX.Message); + return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1]; +} + +void UBXinitCFG(void) +{ + for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA")); +} + +void UBXsendCFGLine(uint8_t _line) +{ + if (_line>sizeof(UBLOX_INIT)/16) return; + for (uint32_t i = 0; i < 16; i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i+(_line*16)) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: send line %u of UBLOX_INIT"), _line); +} + +void UBXTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); +#endif + } +} + + + +void UBXDetect(void) +{ + UBX.mode.init = 0; + if (PinUsed(GPIO_GPS_RX) && PinUsed(GPIO_GPS_TX)) { + UBXSerial = new TasmotaSerial(Pin(GPIO_GPS_RX), Pin(GPIO_GPS_TX), 1, 0, UBX_SERIAL_BUFFER_SIZE); + if (UBXSerial->begin(9600)) { + DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); + if (UBXSerial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); + } + } + } + else { + return; + } + + UBXinitCFG(); + UBX.mode.init = 1; + +#ifdef USE_FLOG + if (!Flog) { + Flog = new FLOG; + Flog->init(); + } +#endif + + UBX.state.log_interval = 10; + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + +uint32_t UBXprocessGPS() +{ + static uint32_t fpos = 0; + static char checksum[2]; + static uint8_t currentMsgType = MT_NONE; + static size_t payloadSize = sizeof(UBX.Message); + + + uint32_t data_bytes = 0; + while ( UBXSerial->available() ) { + data_bytes++; + byte c = UBXSerial->read(); + if (UBX.mode.runningVPort){ + UBX.TCPbuf[data_bytes-1] = c; + UBX.TCPbufSize = data_bytes; + } + if ( fpos < 2 ) { + + if ( c == UBX.UBX_HEADER[fpos] ) { + fpos++; + } else { + fpos = 0; + } + } else { + + + + + + if ( (fpos-2) < payloadSize ) { + ((char*)(&UBX.Message))[fpos-2] = c; + } + fpos++; + + if ( fpos == 4 ) { + + + if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) { + currentMsgType = MT_NAV_POSLLH; + payloadSize = sizeof(UBX_t::NAV_POSLLH); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) { + currentMsgType = MT_NAV_STATUS; + payloadSize = sizeof(UBX_t::NAV_STATUS); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) { + currentMsgType = MT_NAV_TIME; + payloadSize = sizeof(UBX_t::NAV_TIME_UTC); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC")); + } + else { + + fpos = 0; + continue; + } + } + + if ( fpos == (payloadSize+2) ) { + + + UBXcalcChecksum(checksum, payloadSize); + } + else if ( fpos == (payloadSize+3) ) { + + + if ( c != checksum[0] ) { + + fpos = 0; + } + } + else if ( fpos == (payloadSize+4) ) { + + + fpos = 0; + if ( c == checksum[1] ) { + + return currentMsgType; + } + } + else if ( fpos > (payloadSize+4) ) { + + + fpos = 0; + } + } + } + + if (data_bytes!=0) { + UBX.state.non_empty_loops++; + DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops); + } else { + UBX.state.non_empty_loops = 0; + } + return MT_NONE; +} + + + + + +#ifdef USE_FLOG +void UBXsendHeader(void) +{ + Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); + WSSend(200, CT_STREAM, F( + "\r\n" + "\r\n" + "\r\n\r\n")); +} + +void UBXsendRecord(uint8_t *buf) +{ + char record[100]; + char stime[32]; + UBX_t::entry_t *entry = (UBX_t::entry_t*)buf; + snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str()); + char lat[12]; + char lon[12]; + dtostrfd((double)entry->lat/10000000.0f,7,lat); + dtostrfd((double)entry->lon/10000000.0f,7,lon); + snprintf_P(record, sizeof(record),PSTR("\n\t\n\n"),lat ,lon, stime); + + Webserver->sendContent_P(record); +} + +void UBXsendFooter(void) +{ + Webserver->sendContent(F("\n\n")); + Webserver->sendContent(""); + Rtc.user_time_entry = false; +} + + + +void UBXsendFile(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); +} +#endif + + + +void UBXSetRate(uint16_t interval) +{ + UBX.Message.cfgRate.cls = 0x06; + UBX.Message.cfgRate.id = 0x08; + UBX.Message.cfgRate.len = 6; + uint32_t measRate = (1000*(uint32_t)interval); + if (measRate > 0xffff) { + measRate = 0xffff; + } + UBX.Message.cfgRate.measRate = (uint16_t)measRate; + UBX.Message.cfgRate.navRate = 1; + UBX.Message.cfgRate.timeRef = 1; + UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK)); + DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate); + UBXSerial->write(UBX.UBX_HEADER[0]); + UBXSerial->write(UBX.UBX_HEADER[1]); + for (uint32_t i =0; iwrite(((uint8_t*)(&UBX.Message.cfgRate))[i]); + DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]); + } + UBX.state.log_interval = 10*interval; +} + +void UBXSelectMode(uint16_t mode) +{ + DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode); + switch(mode){ +#ifdef USE_FLOG + case 0: + Flog->mode = 0; + break; + case 1: + Flog->mode = 1; + break; + case 2: + UBX.mode.filter_noise = true; + break; + case 3: + UBX.mode.filter_noise = false; + break; + case 4: + Flog->startRecording(true); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); + break; + case 5: + Flog->startRecording(false); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); + break; + case 6: + if(Flog->recording == true){ + Flog->stopRecording(); + } + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); + break; +#endif + case 7: + UBX.mode.send_when_new = 1; + break; + case 8: + UBX.mode.send_when_new = 0; + break; + case 9: + if (timeServer.beginListening()) { + UBX.mode.runningNTP = true; + } + break; + case 10: + UBX.mode.runningNTP = false; + UBXsendCFGLine(10); + UBXsendCFGLine(11); + break; + case 11: + UBX.mode.forceUTCupdate = true; + break; + case 12: + UBX.mode.forceUTCupdate = false; + break; + case 13: + Settings.latitude = UBX.rec_buffer.values.lat/10; + Settings.longitude = UBX.rec_buffer.values.lon/10; + break; + case 14: + vPortServer.begin(); + UBX.mode.runningVPort = 1; + break; + case 15: + + UBX.mode.runningVPort = 0; + break; + default: + if (mode>1000 && mode <1066) { + UBXSetRate(mode-1000); + } + break; + } + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + + + +bool UBXHandlePOSLLH() +{ + DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW); + if (UBX.state.gpsFix>1) { + if (UBX.mode.filter_noise) { + if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat6) { + if(UBX.mode.runningVPort) return; + UBXinitCFG(); + AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); + UBXSerial->flush(); + UBX.state.non_empty_loops = 0; + } +} + + + +void UBXLoop50msec(void) +{ + + if (UBX.mode.runningVPort){ + if(!vPortClient.connected()) { + vPortClient = vPortServer.available(); + } + while(vPortClient.available()) { + byte _newByte = vPortClient.read(); + UBXSerial->write(_newByte); + } + + if (UBX.TCPbufSize!=0){ + vPortClient.write((char*)UBX.TCPbuf, UBX.TCPbufSize); + UBX.TCPbufSize = 0; + } + } + + if(UBX.mode.runningNTP){ + timeServer.processOneRequest(UBX.rec_buffer.values.time, UBX.state.timeOffset - NTP_MILLIS_OFFSET); + } +} + +void UBXLoop(void) +{ + static uint16_t counter; + static bool new_position; + + uint32_t msgType = UBXprocessGPS(); + + switch(msgType){ + case MT_NAV_POSLLH: + new_position = UBXHandlePOSLLH(); + break; + case MT_NAV_STATUS: + UBXHandleSTATUS(); + break; + case MT_NAV_TIME: + UBXHandleTIME(); + break; + default: + UBXHandleOther(); + break; + } + +#ifdef USE_FLOG + if (counter>UBX.state.log_interval) { + if (Flog->recording && new_position) { + UBX.rec_buffer.values.time = Rtc.local_time; + Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes)); + counter = 0; + } + } +#endif + + counter++; +} + + + + +#ifdef USE_WEBSERVER + + +#ifdef USE_FLOG +#ifdef DEBUG_TASMOTA_SENSOR + const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}
{m}
{e}{s} FLOG with %u sectors: {m}%u bytes{e}" + "{s} FLOG next sector for REC: {m} %u {e}" + "{s} %u sector(s) with data at sector: {m} %u {e}"; + const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}"; +#endif + + const char HTTP_SNS_FLOG[] PROGMEM = "{s}
{m}
{e}{s} Flash-Log {m} %s{e}"; + const char kFLOG_STATE0[] PROGMEM = "ready"; + const char kFLOG_STATE1[] PROGMEM = "recording"; + const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1}; + + const char HTTP_BTN_FLOG_DL[] PROGMEM = ""; + +#endif + const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}"; + + const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}" + "{s} GPS longitude {m}%s{e}" + "{s} GPS altitude {m}%s m{e}" + "{s} GPS hor. Accuracy {m}%s m{e}" + "{s} GPS vert. Accuracy {m}%s m{e}" + "{s} GPS sat-fix status {m}%s{e}"; + + const char kGPSFix0[] PROGMEM = "no fix"; + const char kGPSFix1[] PROGMEM = "dead reckoning only"; + const char kGPSFix2[] PROGMEM = "2D-fix"; + const char kGPSFix3[] PROGMEM = "3D-fix"; + const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined"; + const char kGPSFix5[] PROGMEM = "Time only fix"; + const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5}; + + + + +#endif + + + +void UBXShow(bool json) +{ + char lat[12]; + char lon[12]; + char alt[12]; + char hAcc[12]; + char vAcc[12]; + dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat); + dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon); + dtostrfd((double)UBX.state.last_alt/1000.0f,3,alt); + dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc); + dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc); + + if (json) { + ResponseAppend_P(PSTR(",\"GPS\":{")); + if (UBX.mode.send_UI_only) { + uint32_t i = UBX.state.log_interval / 10; + ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i); + } else { + ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"alt\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, alt, hAcc, vAcc); + } +#ifdef USE_FLOG + ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left); +#endif + UBX.mode.send_UI_only = false; +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); + +#ifdef DEBUG_TASMOTA_SENSOR +#ifdef USE_FLOG + WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector); + if (Flog->recording) { + WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8); + } +#endif +#endif +#ifdef USE_FLOG + if (Flog->ready) { + WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]); + } + if (!Flog->recording && Flog->found_saved_data) { + WSContentSend_P(HTTP_BTN_FLOG_DL); + } +#endif + if (UBX.mode.runningNTP) { + WSContentSend_P(HTTP_SNS_NTPSERVER); + } +#endif + } +} + + + + + +bool UBXCmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + UBXSelectMode(XdrvMailbox.payload); + Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); + } + return serviced; +} + + + + + +bool Xsns60(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + UBXDetect(); + } + + if (UBX.mode.init) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_60 == XdrvMailbox.index) { + result = UBXCmd(); + } + break; + case FUNC_EVERY_50_MSECOND: + UBXLoop50msec(); + break; + case FUNC_EVERY_100_MSECOND: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXLoop(); + } + break; +#ifdef USE_FLOG + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/UBX"), UBXsendFile); + break; +#endif + case FUNC_JSON_APPEND: + UBXShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXShow(0); + } + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +# 55 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 +#ifdef USE_MIBLE + +#ifdef DEBUG_TASMOTA_SENSOR + #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x); +#else + #define MINRF_LOG_BUFFER(x) +#endif + +#define USE_MI_DECRYPTION + + + + + + + +#define XSNS_61 61 + +#include +#ifdef USE_MI_DECRYPTION +#include +#endif + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03 4 +#define CGG1 5 +#define CGD1 6 +#define NLIGHT 7 +#define MJYD2S 8 +#define YEERC 9 +#define MHOC401 10 +#define MHOC303 11 +#define ATC 12 + +#define MI_TYPES 12 + +#define D_CMND_NRF "NRF" + +const char S_JSON_NRF_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_NRF "%s\":%d}"; +const char S_JSON_NRF_COMMAND[] PROGMEM = "{\"" D_CMND_NRF "%s\":\"%s\"}"; +const char kNRF_Commands[] PROGMEM = "Ignore|Use|Page|Scan|Beacon|Chan|Nlight" +#ifdef USE_MI_DECRYPTION + "|Mjyd2s" + "|Key" +#endif + ; + +enum NRF_Commands { + CMND_NRF_IGNORE, + CMND_NRF_USE, + CMND_NRF_PAGE, + CMND_NRF_SCAN, + CMND_NRF_BEACON, + CMND_NRF_CHAN, + CMND_NRF_NLIGHT +#ifdef USE_MI_DECRYPTION + , CMND_NRF_MJYD2S + , CMND_NRF_KEY +#endif + }; + +const uint16_t kMINRFDeviceID[MI_TYPES]={ 0x0098, + 0x01aa, + 0x045b, + 0x055b, + 0x0347, + 0x0576, + 0x03dd, + 0x07f6, + 0x0153, + 0x0387, + 0x06d3, + 0x0a1c + }; + +const char kMINRFDeviceType1[] PROGMEM = "Flora"; +const char kMINRFDeviceType2[] PROGMEM = "MJ_HT_V1"; +const char kMINRFDeviceType3[] PROGMEM = "LYWSD02"; +const char kMINRFDeviceType4[] PROGMEM = "LYWSD03"; +const char kMINRFDeviceType5[] PROGMEM = "CGG1"; +const char kMINRFDeviceType6[] PROGMEM = "CGD1"; +const char kMINRFDeviceType7[] PROGMEM = "NLIGHT"; +const char kMINRFDeviceType8[] PROGMEM = "MJYD2S"; +const char kMINRFDeviceType9[] PROGMEM = "YEERC"; +const char kMINRFDeviceType10[] PROGMEM = "MHOC401"; +const char kMINRFDeviceType11[] PROGMEM = "MHOC303"; +const char kMINRFDeviceType12[] PROGMEM = "ATC"; +const char * kMINRFDeviceType[] PROGMEM = {kMINRFDeviceType1,kMINRFDeviceType2,kMINRFDeviceType3,kMINRFDeviceType4,kMINRFDeviceType5,kMINRFDeviceType6,kMINRFDeviceType7,kMINRFDeviceType8,kMINRFDeviceType9,kMINRFDeviceType10,kMINRFDeviceType11,kMINRFDeviceType12}; + + +const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; +const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; +const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71dafb46}; +const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0x33049deb}; + +const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; +const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; + +const uint32_t kMINRFYRCPDU[3] = {0x216D63E2,0x5C3DD47E,0x0A5D0E96}; +const uint32_t kMINRFATCPDU[3] = {0xA6E4D00A,0xD0CDAD5A,0x8B03FB3A}; + + +const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; +const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; +const uint8_t kMINRFlsfrList_C[3] = {0x38,0x25,0x2e}; +const uint8_t kMINRFlsfrList_D[3] = {0x26,0x23,0x20}; + + +#pragma pack(1) +struct mi_beacon_t{ + uint16_t PID; + uint8_t counter; + uint8_t MAC[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ + int16_t temp; + uint16_t hum; + }HT; + uint8_t bat; + uint16_t temp; + uint16_t hum; + uint32_t lux:24; + uint8_t moist; + uint16_t fert; + struct{ + uint16_t num; + uint8_t longPress; + }Btn; + }; +}; + +struct CGDPacket_t { + uint8_t MAC[6]; + uint16_t mode; + union { + struct { + int16_t temp; + uint16_t hum; + }; + uint8_t bat; + }; +}; + +struct bleAdvPacket_t { + uint8_t pduType; + uint8_t payloadSize; + uint8_t MAC[6]; +}; + +#ifdef USE_MI_DECRYPTION +struct encPayload_t { + uint8_t cipher[5]; + uint8_t ExtCnt[3]; + uint8_t tag[4]; +}; + +struct encPacket_t{ + + uint16_t PID; + uint8_t frameCnt; + uint8_t MAC[6]; + encPayload_t payload; +}; + +struct mjysd02_Packet_t{ + uint8_t padding[11]; + uint8_t payloadSize; + uint8_t padding3; + uint16_t UUID; + uint16_t frameCtrl; + uint16_t PID; + uint8_t frameCnt; + uint8_t data[18]; +}; + +struct ATCPacket_t{ + uint8_t MAC[6]; + int16_t temp; + uint8_t hum; + uint8_t batPer; + uint16_t batMV; + uint8_t frameCnt; +}; + +union mi_bindKey_t{ + struct{ + uint8_t key[16]; + uint8_t MAC[6]; + }; + uint8_t buf[22]; +}; +#endif +union FIFO_t{ + bleAdvPacket_t bleAdv; + mi_beacon_t miBeacon; + CGDPacket_t CGDPacket; + uint8_t raw[32]; +}; + +#pragma pack(0) + +struct { + const uint8_t channel[3] = {37,38,39}; + const uint8_t frequency[3] = { 2,26,80}; + + uint16_t timer; + uint16_t ignore = 0; + uint8_t currentChan=0; + uint8_t channelIgnore = 0; + uint8_t confirmedSensors = 0; + uint8_t packetMode; + uint8_t perPage = 4; + uint8_t firstUsedPacketMode = 1; + uint8_t activeLight = 0; + + FIFO_t buffer; + + struct { + uint8_t MAC[6]; + uint32_t time; + uint32_t PDU[3]; + bool active = false; + } beacon; + + struct { + uint32_t allwaysAggregate:1; + + uint32_t noSummary:1; + uint32_t minimalSummary:1; + uint32_t directBridgeMode:1; + } option; + + struct { + uint32_t shallTriggerTele:1; + uint32_t triggeredTele:1; + uint32_t activeScan:1; + uint32_t stopScan:1; + + } mode; + + + + + +#ifdef DEBUG_TASMOTA_SENSOR + uint8_t streamBuffer[sizeof(buffer)]; + uint8_t lsfrBuffer[sizeof(buffer)]; +#endif +} MINRF; + +struct mi_sensor_t{ + uint8_t type; + uint8_t lastCnt; + uint8_t shallSendMQTT; + uint8_t showedUp; + uint8_t MAC[6]; + + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t PIR:1; + uint32_t Btn:1; + }; + uint32_t raw; + } feature; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t motion:1; + uint32_t noMotion:1; + uint32_t Btn:1; + }; + uint32_t raw; + } eventType; + + uint32_t lastTime; + uint32_t lux; + float temp; + union { + struct { + uint8_t moisture; + uint16_t fertility; + }; + struct { + float hum; + }; + struct { + uint16_t events; + uint32_t NMT; + }; + uint16_t Btn; + }; + union { + uint8_t bat; + }; +}; + +struct mi_light_t{ + uint8_t MAC[6]; + uint32_t PDU[3]; + uint8_t type; + uint8_t bat; + struct { + uint16_t events; + uint8_t lastCnt; + uint8_t shallSendMQTT; + }; + uint32_t NMT; + uint32_t lastTime; + uint8_t lux; + uint8_t eventType; +}; + +struct scan_entry_t { + uint8_t MAC[6]; + uint16_t cid; + uint16_t svc; + uint16_t uuid; + uint8_t showedUp; +}; + +std::vector MIBLEsensors; +std::vector MINRFscanResult; +#ifdef USE_MI_DECRYPTION +std::vector MIBLEbindKeys; +#endif +std::vector MIBLElights; + +static union{ + scan_entry_t MINRFdummyEntry; + uint8_t MINRFtempBuf[23]; +}; + + + +void MINRFinit(void){ + MINRFinitBLE(1); + + MINRF.option.allwaysAggregate = 1; + + MINRF.option.noSummary = 0; + MINRF.option.minimalSummary = 0; + MINRF.option.directBridgeMode = 0; +} +# 431 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +bool MINRFinitBLE(uint8_t _mode) +{ + if (MINRF.timer%1000 == 0){ + NRF24radio.begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC)); + NRF24radio.setAutoAck(false); + NRF24radio.setDataRate(RF24_1MBPS); + NRF24radio.disableCRC(); + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); + NRF24radio.setRetries(0,0); + NRF24radio.setPALevel(RF24_PA_MIN); + NRF24radio.setAddressWidth(4); + + + NRF24radio.powerUp(); + } + if(NRF24radio.isChipConnected()){ + + MINRFchangePacketModeTo(_mode); + return true; + } + + return false; +} + + + + + +void MINRFhopChannel() +{ + for (uint32_t i = 0; i<3;i++){ + MINRF.currentChan++; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + if(MINRF.currentChan >= sizeof(MINRF.channel)) { + MINRF.currentChan = 0; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + } + break; + } + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); +} + + + + + + + +bool MINRFreceivePacket(void) +{ + if(!NRF24radio.available()) { + return false; + } + while(NRF24radio.available()) { + + + NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) ); +#ifdef DEBUG_TASMOTA_SENSOR + memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); +#endif + MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) ); + + + + switch (MINRF.packetMode) { + case 0: case NLIGHT: case MJYD2S: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + break; + case FLORA: case LYWSD02: case MHOC303: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); + break; + case MJ_HT_V1: case LYWSD03: case CGG1: case CGD1: case MHOC401: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + case YEERC: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_C[MINRF.currentChan]); + break; + case ATC: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_D[MINRF.currentChan]); + break; + } + + + } + + return true; +} + + +void MINRFshowBuffer(uint8_t (&buf)[32]){ + + + + + DEBUG_SENSOR_LOG(PSTR("NRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x ") + ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], + buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23], + buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31] + ); +} + + + + + + + +void MINRFswapbuf(uint8_t *buf, uint8_t len) +{ + + while(len--) { + uint8_t a = *buf; + uint8_t v = 0; + if (a & 0x80) v |= 0x01; + if (a & 0x40) v |= 0x02; + if (a & 0x20) v |= 0x04; + if (a & 0x10) v |= 0x08; + if (a & 0x08) v |= 0x10; + if (a & 0x04) v |= 0x20; + if (a & 0x02) v |= 0x40; + if (a & 0x01) v |= 0x80; + *(buf++) = v; + } +} +# 566 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) +{ + while(len--) { + uint8_t res = 0; + + for (uint8_t i = 1; i; i <<= 1) { + if (lfsr & 0x01) { + lfsr ^= 0x88; + res |= i; + } + lfsr >>= 1; + } + *(buf++) ^= res; +#ifdef DEBUG_TASMOTA_SENSOR + MINRF.lsfrBuffer[31-len] = lfsr; +#endif + } +} + + + + +bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); + + + + + +void MINRFhandleScan(void){ + if(MINRFscanResult.size()>20 || MINRF.mode.stopScan) { + MINRF.mode.activeScan=false; + MINRFcomputefirstUsedPacketMode(); + uint32_t i = 0; + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [&i](scan_entry_t e) { + if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.MAC[0],e.MAC[1],e.MAC[2],e.MAC[3],e.MAC[4],e.MAC[5],e.cid,e.svc,e.uuid); + i++; + return ((e.showedUp < 3)); + }), + MINRFscanResult.end()); + MINRF.mode.stopScan=false; + return; + } + + MINRFreverseMAC(MINRF.buffer.bleAdv.MAC); + for(uint32_t i=0; i30) break; + uint32_t ADtype = _buf[i+1]; + + if (size+i>32+offset) size=32-i+offset-2; + if (size>30) break; + char _stemp[(size*2)]; + uint32_t backupSize; + switch(ADtype){ + case 0x01: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Flags: %02x"), _buf[i+2]); + break; + case 0x02: case 0x03: + entry->uuid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: UUID: %04x"), entry->uuid); + success = true; + break; + case 0x08: case 0x09: + backupSize = _buf[i+size+1]; + _buf[i+size+1] = 0; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Name: %s"), (char*)&_buf[i+2]); + success = true; + _buf[i+size+1] = backupSize; + break; + case 0x0a: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: TxPow: %02u"), _buf[i+2]); + break; + case 0xff: + entry->cid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Cid: %04x"), entry->cid); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + case 0x16: + entry->svc = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Svc: %04x"), entry->svc); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + default: + ToHex_P((unsigned char*)&_buf+i+2,size-1,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + } + i+=size; + } + MINRF.beacon.time = 0; + } + return success; +} + + + + + +void MINRFbeaconCounter(void) { + if (MINRF.beacon.active) { + MINRF.beacon.time++; + + + + + + + Response_P(PSTR("{%s:{\"Beacon\":%u}}"), D_CMND_NRF, MINRF.beacon.time); + XdrvRulesProcess(); + } +} + + + + + +void MINRFcomputeBeaconPDU(uint8_t (&_MAC)[6], uint32_t (&PDU)[3], uint32_t offset){ + uint32_t _PDU[3]; + for (uint32_t i = 0; i<3; i++){ + bleAdvPacket_t packet; + memcpy((uint8_t *)&packet.MAC, (uint8_t *)&_MAC, sizeof(packet.MAC)); + MINRFreverseMAC(packet.MAC); + MINRFwhiten((uint8_t *)&packet, sizeof(packet), MINRF.channel[i] | 0x40); + MINRFswapbuf((uint8_t*)&packet,sizeof(packet)); + uint32_t pdu = packet.MAC[0+offset]<<24 | packet.MAC[1+offset]<<16 | packet.MAC[2+offset]<<8 | packet.MAC[3+offset]; + _PDU[i] = pdu; + } + memcpy(PDU,_PDU,sizeof(_PDU)); +} + +#ifdef USE_MI_DECRYPTION +int MINRFdecryptPacket(char *_buf){ + encPacket_t *packet = (encPacket_t*)_buf; + + + + + int ret = 0; + unsigned char output[16] = {0}; + uint8_t nonce[12]; + const unsigned char authData[1] = {0x11}; + + + for (uint32_t i = 0; i<6; i++){ + nonce[i] = packet->MAC[5-i]; + } + memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2); + nonce[8] = packet->frameCnt; + memcpy((uint8_t*)&nonce+9,(uint8_t*)&packet->payload.ExtCnt,3); + + uint8_t _bindkey[16] = {0x0}; + for(uint32_t i=0; iMAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){ + + memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey)); + break; + } + + + + + } + + memcpy(output,packet->payload.cipher, sizeof(packet->payload.cipher)); + + br_aes_small_ctrcbc_keys keyCtx; + br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey)); + + br_ccm_context ctx; + br_ccm_init(&ctx, &keyCtx.vtable); + br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData),sizeof(packet->payload.cipher),sizeof(packet->payload.tag)); + br_ccm_aad_inject(&ctx, authData, sizeof(authData)); + br_ccm_flip(&ctx); + br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher)); + + ret = br_ccm_check_tag(&ctx, packet->payload.tag); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); + memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher)); + return ret; +} + +int MINRFdecryptMJYD2SPacket(char *_buf, uint8_t _light, char* _output){ + int ret = 0; + uint8_t nonce[12]; + const unsigned char authData[1] = {0x11}; + uint8_t tag[4]; + mjysd02_Packet_t *packet = (mjysd02_Packet_t*)_buf; + + + for (uint32_t i = 0; i<6; i++){ + nonce[i] = MIBLElights[_light-1].MAC[5-i]; + } + memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2); + nonce[8] = packet->frameCnt; + memcpy((uint8_t*)&nonce+9,(uint8_t*)&packet->padding[0] + packet->payloadSize + 5, 3); + + + uint8_t _bindkey[16]; + for(uint32_t i=0; ipayloadSize){ + case 22: + _size = 7; + _offset = 2; + _tagSize = 4; + break; + case 25: + _size = packet->payloadSize - 21; + _offset = -1; + _tagSize = 4; + break; + case 27: + _size = packet->payloadSize - 21; + _offset = 1; + _tagSize = 3; + break; + default: + return 0; + break; + } + + memcpy(_output,(uint8_t*)&packet->padding[0] + packet->payloadSize - _offset, _size); + + + br_aes_small_ctrcbc_keys keyCtx; + br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey)); + + br_ccm_context ctx; + br_ccm_init(&ctx, &keyCtx.vtable); + br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData),_size,4); + br_ccm_aad_inject(&ctx, authData, sizeof(authData)); + br_ccm_flip(&ctx); + br_ccm_run(&ctx, 0, _output, _size); + + + br_ccm_get_tag(&ctx, tag); + ret = memcmp(tag,(uint8_t*)&packet->padding[0] + packet->payloadSize + 8, _tagSize); + return ret; +} +#endif +# 891 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFreverseMAC(uint8_t _MAC[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _MAC[i]; + } + memcpy(_MAC,_reversedMAC, sizeof(_reversedMAC)); +} +#ifdef USE_MI_DECRYPTION +void MINRFAddKey(char* payload){ + mi_bindKey_t keyMAC; + memset(keyMAC.buf,0,sizeof(keyMAC)); + MINRFKeyMACStringToBytes(payload,keyMAC.buf); + bool unknownKey = true; + for(uint32_t i=0; i= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _keyMAC[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } + DEBUG_SENSOR_LOG(PSTR("NRF: %s to:"),_string); + DEBUG_SENSOR_LOG(PSTR("NRF: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]); + DEBUG_SENSOR_LOG(PSTR("NRF: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]); +} +#endif + + + + + + +void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { + uint32_t index = 0; + UpperCase(_string,_string); + while (index < 12) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _MAC[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } + +} + + + + + +void MINRFcomputefirstUsedPacketMode(void){ + for (uint32_t i = 0; iMI_TYPES) MINRF.firstUsedPacketMode=0; + break; + } + } +} +# 985 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFrecalcBuffer(uint8_t *_buf, uint32_t offset){ + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + MINRFswapbuf((uint8_t*)&MINRF.buffer,sizeof(MINRF.buffer)); + memcpy(_buf+offset,MINRF.buffer.raw,32); + MINRFswapbuf(_buf,32+offset); + MINRFwhiten(_buf, 32+offset, MINRF.channel[MINRF.currentChan] | 0x40); +} + + + + + + +void MINRFchangePacketModeTo(uint8_t _mode) { + uint32_t (_nextchannel) = MINRF.currentChan+1; + if (_nextchannel>2) _nextchannel=0; + + switch(_mode){ + case 0: + NRF24radio.openReadingPipe(0,0x6B7D9171); + break; + case FLORA: + NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); + break; + case MJ_HT_V1: + NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); + break; + case LYWSD02: case MHOC303: + NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]); + break; + case LYWSD03: case MHOC401: + NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]); + break; + case CGG1: + NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); + break; + case CGD1: + NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); + break; + case NLIGHT: case MJYD2S: + if (MIBLElights.size()==0) break; + NRF24radio.openReadingPipe(0,MIBLElights[MINRF.activeLight].PDU[_nextchannel]); + MINRF.activeLight++; + break; + case YEERC: + NRF24radio.openReadingPipe(0,kMINRFYRCPDU[_nextchannel]); + break; + case ATC: + NRF24radio.openReadingPipe(0,kMINRFATCPDU[_nextchannel]); + break; + } + + MINRF.packetMode = _mode; +} +# 1047 "/workspace/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +uint32_t MINRFgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("NRF: will test ID-type: %x"), _type); + bool _success = false; + for (uint32_t i=0;i 2){ + MINRF.confirmedSensors++; + } + } +} + + + + + +void MINRFtriggerTele(void){ + MINRF.mode.triggeredTele= true; + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + #ifdef USE_RULES + RulesTeleperiod(); + #endif + } +} + + + + + +void MINRFhandleMiBeaconPacket(void){ + MINRFreverseMAC(MINRF.buffer.miBeacon.MAC); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.MAC, MINRF.buffer.miBeacon.PID); + if(_slot==0xff) return; + DEBUG_SENSOR_LOG(PSTR("NRF: slot %u, size vector: %u %u"),_slot,MIBLEsensors.size()); + mi_sensor_t *_sensorVec = &MIBLEsensors[_slot]; + DEBUG_SENSOR_LOG(PSTR("NRF: %u %u %u"),_slot,_sensorVec->type,MINRF.buffer.miBeacon.type); + float _tempFloat; + int decryptRet; + + switch(_sensorVec->type){ + case MJ_HT_V1: case CGG1: case YEERC: + memcpy(MINRFtempBuf,(uint8_t*)&MINRF.buffer.miBeacon.spare, 32-9); + memcpy((uint8_t*)&MINRF.buffer.miBeacon.type,MINRFtempBuf, 32-9); + break; +#ifdef USE_MI_DECRYPTION + case LYWSD03: case MHOC401: + decryptRet = MINRFdecryptPacket((char*)&MINRF.buffer); + if(decryptRet==1) _sensorVec->showedUp=255; + break; +#endif + } + + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kNRFSlaveType[_sensorVec->type-1],_slot); + switch(MINRF.buffer.miBeacon.type){ + case 0x1: + if(MINRF.buffer.miBeacon.counter==_sensorVec->lastCnt) break; + + _sensorVec->lastCnt=MINRF.buffer.miBeacon.counter; + _sensorVec->Btn=MINRF.buffer.miBeacon.Btn.num + (MINRF.buffer.miBeacon.Btn.longPress/2)*6; + _sensorVec->eventType.Btn = 1; + break; + case 0x04: + _tempFloat=(float)(MINRF.buffer.miBeacon.temp)/10.0f; + if(_tempFloat<60){ + _sensorVec->temp=_tempFloat; + _sensorVec->eventType.temp = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), MINRF.buffer.miBeacon.temp ); + break; + case 0x06: + _tempFloat=(float)(MINRF.buffer.miBeacon.hum)/10.0f; + if(_tempFloat<101){ + _sensorVec->hum=_tempFloat; + _sensorVec->eventType.hum = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), MINRF.buffer.miBeacon.hum); + break; + case 0x07: + _sensorVec->lux=MINRF.buffer.miBeacon.lux & 0x00ffffff; + _sensorVec->eventType.lux = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)MINRF.buffer.miBeacon.moist; + if(_tempFloat<100){ + _sensorVec->moisture=_tempFloat; + _sensorVec->eventType.moist = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), MINRF.buffer.miBeacon.moist); + break; + case 0x09: + _tempFloat=(float)(MINRF.buffer.miBeacon.fert); + if(_tempFloat<65535){ + _sensorVec->fertility=_tempFloat; + _sensorVec->eventType.fert = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), MINRF.buffer.miBeacon.fert); + break; + case 0x0a: + if(MINRF.buffer.miBeacon.bat<101){ + _sensorVec->bat = MINRF.buffer.miBeacon.bat; + _sensorVec->eventType.bat = 1; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), MINRF.buffer.miBeacon.bat); + break; + case 0x0d: + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.temp)/10.0f; + if(_tempFloat<60){ + _sensorVec->temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.hum)/10.0f; + if(_tempFloat<100){ + _sensorVec->hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + _sensorVec->eventType.tempHum = 1; + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), MINRF.buffer.miBeacon.HT.temp, MINRF.buffer.miBeacon.HT.hum); + break; + } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + if (MINRF.option.directBridgeMode) MINRF.mode.shallTriggerTele = 1; +} + + + + + +void MINRFhandleCGD1Packet(void){ + MINRFreverseMAC(MINRF.buffer.CGDPacket.MAC); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.MAC, 0x0576); + DEBUG_SENSOR_LOG(PSTR("NRF: Sensor slot: %u"), _slot); + if(_slot==0xff) return; + + switch (MINRF.buffer.CGDPacket.mode){ + case 0x0401: + float _tempFloat; + _tempFloat=(float)(MINRF.buffer.CGDPacket.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.CGDPacket.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), MINRF.buffer.CGDPacket.temp, MINRF.buffer.CGDPacket.hum); + MIBLEsensors[_slot].eventType.tempHum = 1; + break; + case 0x0102: + if(MINRF.buffer.CGDPacket.bat<101){ + MIBLEsensors[_slot].bat = MINRF.buffer.CGDPacket.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + MIBLEsensors[_slot].eventType.bat = 1; + break; + default: + DEBUG_SENSOR_LOG(PSTR("NRF: unexpected CGD1-packet")); + MINRF_LOG_BUFFER(MINRF.buffer.raw); + } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + if (MINRF.option.directBridgeMode) MINRF.mode.shallTriggerTele = 1; +} + +void MINRFhandleNlightPacket(void){ + uint32_t offset = 6; + uint8_t _buf[32+offset]; + MINRFrecalcBuffer((uint8_t*)&_buf,offset); + + uint32_t _frame_PID = _buf[15]<<24 | _buf[16]<<16 | _buf[17]<<8 | _buf[18]; + if(_frame_PID!=0x4030dd03) return; + + uint32_t _idx = MINRF.activeLight-1; + if((millis() - MIBLElights[_idx].lastTime)<1500) return; + if(_buf[19]!=MIBLElights[_idx].lastCnt){ + MIBLElights[_idx].lastCnt = _buf[19]; + MIBLElights[_idx].events++; + MIBLElights[_idx].shallSendMQTT = 1; + MIBLElights[_idx].lastTime = millis(); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt); + } +} + +void MINRFhandleMJYD2SPacket(void){ + uint32_t offset = 8; + uint8_t _buf[32+offset]; + MINRFrecalcBuffer((uint8_t*)&_buf,offset); + mjysd02_Packet_t *_packet = (mjysd02_Packet_t*)&_buf; + if(_packet->PID!=0x07f6) return; + + + uint32_t _idx = MINRF.activeLight-1; + switch(_packet->frameCtrl){ + case 0x5910: + if(_packet->frameCnt!=MIBLElights[_idx].lastCnt){ + + MIBLElights[_idx].lastCnt = _packet->frameCnt; + if(millis()-MIBLElights[_idx].lastTime>120000){ + MIBLElights[_idx].eventType = 1; + MIBLElights[_idx].events++; + MIBLElights[_idx].shallSendMQTT = 1; + MIBLElights[_idx].lastTime = millis(); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S secondary PIR")); + } + } + break; + case 0x5948: case 0x5958: + uint8_t output[16]; + if(_packet->frameCnt==MIBLElights[_idx].lastCnt) break; + int32_t ret = MINRFdecryptMJYD2SPacket((char*)&_buf, MINRF.activeLight,(char*)&output); + if(ret==0){ + MIBLElights[_idx].lastCnt = _packet->frameCnt; + switch(output[0]){ + case 0x0f: + if(output[1] == 0){ + if(millis()-MIBLElights[_idx].lastTime>1000){ + MIBLElights[_idx].eventType = 1; + MIBLElights[_idx].shallSendMQTT = 1; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S primary PIR")); + MIBLElights[_idx].events++; + } + MIBLElights[_idx].lastTime = millis(); + MIBLElights[_idx].lux = output[3]; + } + break; + case 0x07: + if(output[1] == 0x10){ + MIBLElights[_idx].eventType = 2; + MIBLElights[_idx].lux = output[3]; + MIBLElights[_idx].shallSendMQTT = 1; + } + break; + case 0x0a: + MIBLElights[_idx].bat = output[3]; + break; + case 0x17: + MIBLElights[_idx].NMT = output[6]<<24 | output[5]<<16 | output[4]<<8 | output[3]; + MIBLElights[_idx].eventType = 3; + MIBLElights[_idx].shallSendMQTT = 1; + + break; + } + } + } + +} + + +void MINRFhandleLightPacket(void){ + switch(MIBLElights[MINRF.activeLight-1].type){ + case NLIGHT: + + MINRFhandleNlightPacket(); + break; + case MJYD2S: + + MINRFhandleMJYD2SPacket(); + break; + } + if(MIBLElights[MINRF.activeLight-1].shallSendMQTT==1) MINRFtriggerTele(); +} + +void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ + for(uint32_t i=0; iMAC, 0x0a1c); + + + if(_slot==0xff) return; + + MIBLEsensors[_slot].temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors[_slot].hum = (float)_packet->hum; + MIBLEsensors[_slot].bat = _packet->batPer; + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].eventType.bat = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + if (MINRF.option.directBridgeMode) MINRF.mode.shallTriggerTele = 1; +} + + + + + +void MINRF_EVERY_50_MSECOND() { + + if(MINRF.timer>6000){ + DEBUG_SENSOR_LOG(PSTR("NRF: check for FAKE sensors")); + MINRFpurgeFakeSensors(); + MINRF.timer=0; + } + MINRF.timer++; + + if (!MINRFreceivePacket()){ + if(MINRF.mode.shallTriggerTele){ + MINRFtriggerTele(); + MINRF.mode.shallTriggerTele = 0; + } + + + } + + else { + switch (MINRF.packetMode) { + case 0: + if (MINRF.beacon.active){ + MINRFhandleBeacon(&MINRFdummyEntry,6); + } + else MINRFhandleScan(); + break; + case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: case LYWSD03: case YEERC: case MHOC401: case MHOC303: + MINRFhandleMiBeaconPacket(); + break; + case CGD1: + MINRFhandleCGD1Packet(); + break; + case NLIGHT: + MINRFhandleLightPacket(); + break; + case ATC: + MINRFhandleATCPacket(); + break; + default: + break; + } + } + if (MINRF.beacon.active || MINRF.mode.activeScan) { + MINRF.firstUsedPacketMode=0; + } + + if(MINRF.packetMode==NLIGHT){ + if(MINRF.activeLight+1>MIBLElights.size()){ + MINRF.activeLight=0; + MINRF.packetMode+=2; + } + else MINRF.packetMode+=2; + } + else{ + MINRF.packetMode = (MINRF.packetMode+1>MI_TYPES) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; + for (uint32_t i = MINRF.packetMode; i 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MINRF.perPage; + MINRF.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = MINRF.perPage; + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_NRF_IGNORE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0){ + MINRF.ignore = 0; + MINRF.firstUsedPacketMode = 1; + } + else if (XdrvMailbox.payload < MI_TYPES+1) { + bitSet(MINRF.ignore,XdrvMailbox.payload); + MINRFcomputefirstUsedPacketMode(); + MINRF.timer = 5900; + Response_P(S_JSON_NRF_COMMAND, command, kMINRFDeviceType[XdrvMailbox.payload-1]); + } + else if (XdrvMailbox.payload == 65535) { + MINRF.ignore = 65535; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.ignore); + break; + case CMND_NRF_USE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0){ + MINRF.ignore = 65535; + MINRF.firstUsedPacketMode = 1; + } + else if (XdrvMailbox.payload < MI_TYPES+1) { + bitClear(MINRF.ignore,XdrvMailbox.payload); + MINRFcomputefirstUsedPacketMode(); + MINRF.timer = 5900; + Response_P(S_JSON_NRF_COMMAND, command, kMINRFDeviceType[XdrvMailbox.payload-1]); + } + else if (XdrvMailbox.payload == 65535) { + MINRF.ignore = 0; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.ignore); + break; + case CMND_NRF_SCAN: + if (XdrvMailbox.data_len > 0) { + MINRF.beacon.active = false; + switch(XdrvMailbox.payload){ + case 0: + MINRF.mode.activeScan = true; + MINRF.mode.stopScan = false; + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [](scan_entry_t&) { return true; }), + MINRFscanResult.end()); + break; + case 1: + MINRF.mode.activeScan = true; + MINRF.mode.stopScan = false; + break; + case 2: + MINRF.mode.stopScan = true; + break; + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + break; + case CMND_NRF_BEACON: + if (XdrvMailbox.data_len > 0) { + if(XdrvMailbox.data_len<3){ + if (XdrvMailbox.payload < MINRFscanResult.size()) { + MINRFstartBeacon(XdrvMailbox.payload); + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + } + if (XdrvMailbox.data_len==12){ + memset(MINRF.beacon.MAC,0,sizeof(MINRF.beacon.MAC)); + MINRFMACStringToBytes(XdrvMailbox.data, MINRF.beacon.MAC); + MINRF.beacon.time=0; + MINRF.beacon.active=true; + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + } + MINRFcomputeBeaconPDU(MINRF.beacon.MAC,MINRF.beacon.PDU,0); + } + break; + case CMND_NRF_NLIGHT: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len==12){ + uint8_t _MAC[6] = {0}; + MINRFMACStringToBytes(XdrvMailbox.data, _MAC); + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + MINRFaddLight(_MAC, 7); + } + } + break; + case CMND_NRF_CHAN: + if (XdrvMailbox.data_len == 1) { + switch(XdrvMailbox.payload){ + case 0: case 1: case 2: + bitRead(MINRF.channelIgnore,XdrvMailbox.payload) == 0 ? bitSet(MINRF.channelIgnore,XdrvMailbox.payload) : bitClear(MINRF.channelIgnore,XdrvMailbox.payload); + break; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.channelIgnore); + break; +#ifdef USE_MI_DECRYPTION + case CMND_NRF_MJYD2S: + if (XdrvMailbox.data_len==44){ + MINRFAddKey(XdrvMailbox.data); + uint8_t _MAC[6] = {0}; + MINRFMACStringToBytes((XdrvMailbox.data)+32, _MAC); + MINRFaddLight(_MAC, 8); + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + } + break; + + case CMND_NRF_KEY: + if (XdrvMailbox.data_len==44){ + MINRFAddKey(XdrvMailbox.data); + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + } + break; +#endif + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%dus/cm{e}"; +const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; +const char HTTP_NRF24NEW[] PROGMEM = "{s}%sL01%c{m}%u%s / %u{e}"; + +void MINRFShow(bool json) +{ + if (json) { +#ifdef USE_HOME_ASSISTANT + bool _noSummarySave = MINRF.option.noSummary; + bool _minimalSummarySave = MINRF.option.minimalSummary; + if(hass_mode==2){ + MINRF.option.noSummary = false; + MINRF.option.minimalSummary = false; + } +#endif + if(!MINRF.mode.triggeredTele){ + if(MINRF.option.noSummary) return; + } + + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MINRF.mode.triggeredTele && MIBLEsensors[i].eventType.raw == 0) continue; + if(MINRF.mode.triggeredTele && MIBLEsensors[i].shallSendMQTT==0) continue; + + if(MIBLEsensors[i].showedUp < 3){ + DEBUG_SENSOR_LOG(PSTR("NRF: sensor not fully registered yet")); + if(MIBLEsensors[i].type != YEERC) break; + } + + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":"),kMINRFDeviceType[MIBLEsensors[i].type-1],MIBLEsensors[i].MAC[3],MIBLEsensors[i].MAC[4],MIBLEsensors[i].MAC[5]); + + uint32_t _positionCurlyBracket = strlen(mqtt_data); + + if((!MINRF.mode.triggeredTele && !MINRF.option.minimalSummary)||MINRF.mode.triggeredTele){ + bool tempHumSended = false; + if(MIBLEsensors[i].feature.tempHum){ + if(MIBLEsensors[i].eventType.tempHum || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate){ + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(!hass_mode==2) +#endif + ) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + tempHumSended = true; + } + } + } + if(MIBLEsensors[i].feature.temp && !tempHumSended){ + if(MIBLEsensors[i].eventType.temp || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ) { + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + } + } + if(MIBLEsensors[i].feature.hum && !tempHumSended){ + if(MIBLEsensors[i].eventType.hum || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].hum)) { + char hum[FLOATSZ]; + dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum); + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); + } + } + } + if (MIBLEsensors[i].feature.lux){ + if(MIBLEsensors[i].eventType.lux || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate){ + if (MIBLEsensors[i].lux!=0x0ffffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + } + } + if (MIBLEsensors[i].feature.moist){ + if(MIBLEsensors[i].eventType.moist || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate){ + if (MIBLEsensors[i].moisture!=0xff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture); + } + } + } + if (MIBLEsensors[i].feature.fert){ + if(MIBLEsensors[i].eventType.fert || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate){ + if (MIBLEsensors[i].fertility!=0xffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ) { + ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility); + } + } + } + if (MIBLEsensors[i].feature.Btn){ + if(MIBLEsensors[i].eventType.Btn){ + ResponseAppend_P(PSTR(",\"Btn\":%u"),MIBLEsensors[i].Btn); + } + } + } + if (MIBLEsensors[i].feature.PIR){ + if(MIBLEsensors[i].eventType.motion || !MINRF.mode.triggeredTele){ + if(MINRF.mode.triggeredTele) ResponseAppend_P(PSTR(",\"PIR\":1")); + ResponseAppend_P(PSTR(",\"Events\":%u"),MIBLEsensors[i].events); + } + else if(MIBLEsensors[i].eventType.noMotion && MINRF.mode.triggeredTele){ + ResponseAppend_P(PSTR(",\"PIR\":0")); + } + } + + if (MIBLEsensors[i].feature.NMT || !MINRF.mode.triggeredTele){ + if(MIBLEsensors[i].eventType.NMT){ + ResponseAppend_P(PSTR(",\"NMT\":%u"), MIBLEsensors[i].NMT); + } + } + if (MIBLEsensors[i].feature.bat){ + if(MIBLEsensors[i].eventType.bat || !MINRF.mode.triggeredTele || MINRF.option.allwaysAggregate){ + if (MIBLEsensors[i].bat != 0x00 +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ) { + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } + } + } + if(_positionCurlyBracket==strlen(mqtt_data)) ResponseAppend_P(PSTR(",")); + ResponseAppend_P(PSTR("}")); + mqtt_data[_positionCurlyBracket] = '{'; + MIBLEsensors[i].eventType.raw = 0; + if(MIBLEsensors[i].shallSendMQTT==1){ + MIBLEsensors[i].shallSendMQTT = 0; + continue; + } + } + for(uint32_t i=0; iMINRF.confirmedSensors){ + j = MINRF.confirmedSensors; + } + char stemp[5] ={0}; + if (MINRF.confirmedSensors-(_page*MINRF.perPage)>1 && MINRF.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MINRF.confirmedSensors==0) i=-1; + + WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors); + for (i ; iFLORA){ + WSContentSend_THD(kMINRFDeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + if(MIBLEsensors[i].bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kMINRFDeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } + } + } + if(MINRF.beacon.active){ + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.MAC[0], MINRF.beacon.MAC[1],MINRF.beacon.MAC[2],MINRF.beacon.MAC[3],MINRF.beacon.MAC[4],MINRF.beacon.MAC[5]); + WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); + } + + for(uint32_t i=0; i0){ + WSContentSend_PD(HTTP_BATTERY, kMINRFDeviceType[MIBLElights[i].type-1], MIBLElights[i].bat); + } + if(MIBLElights[i].lux>0){ + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFDeviceType[MIBLElights[i].type-1], MIBLElights[i].lux); + } + } + } + + if(counter>3) { + _page++; + counter = 0; + } + counter++; + if(MINRF.confirmedSensors%MINRF.perPage==0 && _page==MINRF.confirmedSensors/MINRF.perPage) _page=0; + if(_page>MINRF.confirmedSensors/MINRF.perPage) _page=0; +#endif + } +} + + + + + +bool Xsns61(uint8_t function) +{ + bool result = false; + if (NRF24.chipType) { + switch (function) { + case FUNC_INIT: + MINRFinit(); + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: started")); + break; + case FUNC_EVERY_50_MSECOND: + MINRF_EVERY_50_MSECOND(); + break; + case FUNC_EVERY_SECOND: + MINRFbeaconCounter(); + break; + case FUNC_COMMAND: + result = NRFCmd(); + break; + case FUNC_JSON_APPEND: + MINRFShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MINRFShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +# 46 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +#ifdef ESP32 + +#ifdef USE_MI_ESP32 + +#define XSNS_62 62 +#define USE_MI_DECRYPTION + +#include +#include +#ifdef USE_MI_DECRYPTION +#include +#endif + +void MI32scanEndedCB(NimBLEScanResults results); +void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + +struct { + uint16_t perPage = 4; + uint32_t period; + union { + struct { + uint32_t init:1; + uint32_t connected:1; + uint32_t autoScan:1; + uint32_t canScan:1; + uint32_t runningScan:1; + uint32_t canConnect:1; + uint32_t willConnect:1; + uint32_t readingDone:1; + uint32_t shallSetTime:1; + uint32_t willSetTime:1; + uint32_t shallReadBatt:1; + uint32_t willReadBatt:1; + uint32_t shallSetUnit:1; + uint32_t willSetUnit:1; + uint32_t shallTriggerTele:1; + uint32_t triggeredTele:1; + uint32_t shallClearResults:1; + uint32_t shallShowStatusInfo:1; + uint32_t firstAutodiscoveryDone:1; + uint32_t activeBeacon; + }; + uint32_t all = 0; + } mode; + struct { + uint8_t sensor; + uint8_t beaconScanCounter; + } state; + struct { + uint32_t allwaysAggregate:1; + uint32_t noSummary:1; + uint32_t directBridgeMode:1; + uint32_t holdBackFirstAutodiscovery:1; + uint32_t showRSSI:1; + uint32_t ignoreBogusBattery:1; + uint32_t minimalSummary:1; + } option; +} MI32; + +#pragma pack(1) + + struct { + uint16_t temp; + uint8_t hum; + uint16_t volt; + } LYWSD0x_HT; + struct { + uint8_t spare; + uint16_t temp; + uint16_t hum; + } CGD1_HT; + struct { + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + } Flora_TLMF; + + +struct mi_beacon_t{ + uint16_t frame; + uint16_t productID; + uint8_t counter; + uint8_t MAC[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ + uint16_t temp; + uint16_t hum; + }HT; + uint8_t bat; + uint16_t temp; + uint16_t hum; + uint32_t lux; + uint8_t moist; + uint16_t fert; + uint32_t NMT; + struct{ + uint16_t num; + uint8_t longPress; + }Btn; + }; + uint8_t padding[12]; +}; + +struct cg_packet_t { + uint16_t frameID; + uint8_t MAC[6]; + uint16_t mode; + union { + struct { + int16_t temp; + uint16_t hum; + }; + uint8_t bat; + }; +}; + +struct encPacket_t{ + + uint16_t PID; + uint8_t frameCnt; + uint8_t MAC[6]; + uint8_t payload[16]; +}; + +union mi_bindKey_t{ + struct{ + uint8_t key[16]; + uint8_t MAC[6]; + }; + uint8_t buf[22]; +}; + +struct ATCPacket_t{ + uint8_t MAC[6]; + int16_t temp; + uint8_t hum; + uint8_t batPer; + uint16_t batMV; + uint8_t frameCnt; +}; + +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; + uint8_t lastCnt; + uint8_t shallSendMQTT; + uint8_t MAC[6]; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t PIR:1; + uint32_t Btn:1; + }; + uint32_t raw; + } feature; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t motion:1; + uint32_t noMotion:1; + uint32_t Btn:1; + }; + uint32_t raw; + } eventType; + + int RSSI; + uint32_t lastTime; + uint32_t lux; + float temp; + union { + struct { + uint8_t moisture; + uint16_t fertility; + char firmware[6]; + }; + struct { + float hum; + }; + struct { + uint16_t events; + uint32_t NMT; + }; + uint16_t Btn; + }; + union { + uint8_t bat; + }; +}; + +struct scan_entry_t { + uint8_t MAC[6]; + uint16_t CID; + uint16_t SVC; + uint16_t UUID; + int32_t RSSI; +}; + +struct generic_beacon_t { + uint8_t MAC[6]; + uint32_t time; + int32_t RSSI; + uint16_t CID; + uint16_t UUID; + uint16_t SVC; + bool active = false; +}; + +std::vector MIBLEsensors; +std::vector MIBLEbindKeys; +std::array MIBLEbeacons; +std::vector MINBLEscanResult; + +static BLEScan* MI32Scan; + + + + + +#define D_CMND_MI32 "MI32" + +const char S_JSON_MI32_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s\":%d}"; +const char S_JSON_MI32_COMMAND[] PROGMEM = "{\"" D_CMND_MI32 "%s%s\"}"; + +const char S_JSON_MI32_BCOMMAND_SVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s%u\":\"%s\"}"; +const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery|Unit|Key|Beacon"; + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03MMC 4 +#define CGG1 5 +#define CGD1 6 +#define NLIGHT 7 +#define MJYD2S 8 +#define YEERC 9 +#define MHOC401 10 +#define MHOC303 11 +#define ATC 12 + +#define MI32_TYPES 12 + +const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, + 0x01aa, + 0x045b, + 0x055b, + 0x0347, + 0x0576, + 0x03dd, + 0x07f6, + 0x0153, + 0x0387, + 0x06d3, + 0x0a1c + }; + +const char kMI32DeviceType1[] PROGMEM = "Flora"; +const char kMI32DeviceType2[] PROGMEM = "MJ_HT_V1"; +const char kMI32DeviceType3[] PROGMEM = "LYWSD02"; +const char kMI32DeviceType4[] PROGMEM = "LYWSD03"; +const char kMI32DeviceType5[] PROGMEM = "CGG1"; +const char kMI32DeviceType6[] PROGMEM = "CGD1"; +const char kMI32DeviceType7[] PROGMEM = "NLIGHT"; +const char kMI32DeviceType8[] PROGMEM = "MJYD2S"; +const char kMI32DeviceType9[] PROGMEM = "YEERC"; +const char kMI32DeviceType10[] PROGMEM ="MHOC401"; +const char kMI32DeviceType11[] PROGMEM ="MHOC303"; +const char kMI32DeviceType12[] PROGMEM ="ATC"; +const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12}; + + + + + +enum MI32_Commands { + CMND_MI32_PERIOD, + CMND_MI32_TIME, + CMND_MI32_PAGE, + CMND_MI32_BATTERY, + CMND_MI32_UNIT, + CMND_MI32_KEY, + CMND_MI32_BEACON + }; + +enum MI32_TASK { + MI32_TASK_SCAN = 0, + MI32_TASK_CONN = 1, + MI32_TASK_TIME = 2, + MI32_TASK_BATT = 3, + MI32_TASK_UNIT = 4, +}; + +enum MI32_BEACON_CMND { + MI32_BEACON_ON = 0, + MI32_BEACON_OFF = 1, + MI32_BEACON_DEL = 2, +}; + + + + + +class MI32SensorCallback : public NimBLEClientCallbacks { + void onConnect(NimBLEClient* pclient) { + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); + MI32.mode.willConnect = 0; + MI32.mode.connected = 1; + } + void onDisconnect(NimBLEClient* pclient) { + MI32.mode.connected = 0; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("disconnected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); + } + bool onConnParamsUpdateRequest(NimBLEClient* MI32Client, const ble_gap_upd_params* params) { + if(params->itvl_min < 24) { + return false; + } else if(params->itvl_max > 40) { + return false; + } else if(params->latency > 2) { + return false; + } else if(params->supervision_timeout > 100) { + return false; + } + return true; + } +}; + +class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + + int RSSI = advertisedDevice->getRSSI(); + uint8_t addr[6]; + memcpy(addr,advertisedDevice->getAddress().getNative(),6); + MI32_ReverseMAC(addr); + if (advertisedDevice->getServiceDataCount() == 0) { + + if(MI32.state.beaconScanCounter==0 && !MI32.mode.activeBeacon){ + MI32Scan->erase(advertisedDevice->getAddress()); + return; + } + else{ + MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr); + return; + } + + } + uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; + + + size_t ServiceDataLength = advertisedDevice->getServiceData(0).length(); + if(UUID==0xfe95) { + MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); + } + else if(UUID==0xfdcd) { + MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); + } + else if(UUID==0x181a) { + MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); + } + else { + if(MI32.state.beaconScanCounter!=0 || MI32.mode.activeBeacon){ + MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr); + } + + MI32Scan->erase(advertisedDevice->getAddress()); + } + }; +}; + + +static MI32AdvCallbacks MI32ScanCallbacks; +static MI32SensorCallback MI32SensorCB; +static NimBLEClient* MI32Client; + + + + + +void MI32scanEndedCB(NimBLEScanResults results){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Scan ended")); + MI32.mode.runningScan = 0; +} + +void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length); + switch(MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case LYWSD02: case MHOC401: + MI32readHT_LY((char*)pData); + MI32.mode.readingDone = 1; + break; + default: + MI32.mode.readingDone = 1; + break; + } +} +# 469 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +void MI32stripColon(char* _string){ + uint32_t _length = strlen(_string); + uint32_t _index = 0; + while (_index < _length) { + char c = _string[_index]; + if(c==':'){ + memmove(_string+_index,_string+_index+1,_length-_index); + } + _index++; + } + _string[_index] = 0; +} +# 489 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +void MI32HexStringToBytes(char* _string, uint8_t* _byteArray) { + MI32stripColon(_string); + UpperCase(_string,_string); + uint32_t index = 0; + uint32_t _end = strlen(_string); + memset(_byteArray,0,_end/2); + while (index < _end) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _byteArray[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } +} + + + + + + +void MI32_ReverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + +#ifdef USE_MI_DECRYPTION +void MI32AddKey(char* payload){ + mi_bindKey_t keyMAC; + MI32HexStringToBytes(payload,keyMAC.buf); + bool unknownKey = true; + for(uint32_t i=0; iMAC[i]; + } + memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2); + nonce[8] = packet->frameCnt; + memcpy((uint8_t*)&nonce+9,(uint8_t*)&_buf[_bufSize-9],3); + + memcpy((uint8_t*)&tag,(uint8_t*)&_buf[_bufSize-6],4); + + + MI32_ReverseMAC(packet->MAC); + uint8_t _bindkey[16] = {0x0}; + bool foundNoKey = true; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); + for(uint32_t i=0; iMAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){ + memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: decryption Key found")); + foundNoKey = false; + break; + } + } + if(foundNoKey){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: no Key found !!")); + return -2; + } + + br_aes_small_ctrcbc_keys keyCtx; + br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey)); + + br_ccm_context ctx; + br_ccm_init(&ctx, &keyCtx.vtable); + br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData), data_len, sizeof(tag)); + br_ccm_aad_inject(&ctx, authData, sizeof(authData)); + br_ccm_flip(&ctx); + + + memcpy(payload,packet->payload,data_len); + br_ccm_run(&ctx, 0, payload, data_len); + memcpy((uint8_t*)packet->payload+1,payload,data_len); + + ret = br_ccm_check_tag(&ctx, &tag); + + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]); + return ret-1; +} +#endif + +#ifdef USE_HOME_ASSISTANT + + + + +void MI32nullifyEndOfMQTT_DATA(){ + char *p = mqtt_data + strlen(mqtt_data); + while(true){ + *p--; + if(p[0]==':'){ + p[1] = 0; + break; + } + } + ResponseAppend_P(PSTR("null")); +} +#endif +# 636 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter){ + + DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type); + bool _success = false; + for (uint32_t i=0;i= NIMBLE_MAX_CONNECTIONS) { + MI32.mode.willConnect = 0; + DEBUG_SENSOR_LOG(PSTR("%s: max connection already reached"),D_CMND_MI32); + return false; + } + if(!MI32Client) { + + MI32Client = NimBLEDevice::createClient(); + MI32Client->setClientCallbacks(&MI32SensorCB , false); + MI32Client->setConnectionParams(12,12,0,48); + MI32Client->setConnectTimeout(30); + + } + vTaskDelay(300/ portTICK_PERIOD_MS); + if (!MI32Client->connect(_address,false)) { + MI32.mode.willConnect = 0; + + + return false; + } + return true; + +} + +void MI32StartScanTask(){ + if (MI32.mode.connected) return; + MI32.mode.runningScan = 1; + xTaskCreatePinnedToCore( + MI32ScanTask, + "MI32ScanTask", + 4096, + NULL, + 0, + NULL, + 0); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),D_CMND_MI32); +} + +void MI32ScanTask(void *pvParameters){ + if (MI32Scan == nullptr) MI32Scan = NimBLEDevice::getScan(); + + MI32Scan->setInterval(70); + MI32Scan->setWindow(50); + MI32Scan->setAdvertisedDeviceCallbacks(&MI32ScanCallbacks,true); + MI32Scan->setActiveScan(false); + MI32Scan->start(0, MI32scanEndedCB, true); + + uint32_t timer = 0; + for(;;){ + if(MI32.mode.shallClearResults){ + MI32Scan->clearResults(); + MI32.mode.shallClearResults=0; + } + vTaskDelay(10000/ portTICK_PERIOD_MS); + } + vTaskDelete( NULL ); +} + +void MI32StartSensorTask(){ + MI32.mode.willConnect = 1; + switch(MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case MHOC401: + break; + default: + MI32.mode.willConnect = 0; + return; + } + + xTaskCreatePinnedToCore( + MI32SensorTask, + "MI32SensorTask", + 4096, + NULL, + 15, + NULL, + 0); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start sensor connections"),D_CMND_MI32); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); +} + +void MI32SensorTask(void *pvParameters){ + if (MI32ConnectActiveSensor()){ + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + MI32Client->disconnect(); + + MI32.mode.willConnect = 0; + MI32.mode.willReadBatt = 0; + vTaskDelay(100/ portTICK_PERIOD_MS); + vTaskDelete( NULL ); + } + timer++; + vTaskDelay(10/ portTICK_PERIOD_MS); + } + + timer = 150; + switch(MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case MHOC401: + MI32.mode.readingDone = 0; + if(MI32connectLYWSD03forNotification()) timer=0; + break; + default: + break; + } + + while (!MI32.mode.readingDone){ + if (timer>150){ + break; + } + timer++; + vTaskDelay(100/ portTICK_PERIOD_MS); + } + MI32Client->disconnect(); + DEBUG_SENSOR_LOG(PSTR("%s: requested disconnect"),D_CMND_MI32); + } + + MI32.mode.connected = 0; + vTaskDelete( NULL ); +} + +bool MI32connectLYWSD03forNotification(){ + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID serviceUUID(0xebe0ccb0,0x7a0a,0x4b0c,0x8a1a6ff2997da3a6); + static BLEUUID charUUID(0xebe0ccc1,0x7a0a,0x4b0c,0x8a1a6ff2997da3a6); + pSvc = MI32Client->getService(serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(charUUID); + } + if (pChr){ + if(pChr->canNotify()) { + if(pChr->subscribe(true,MI32notifyCB,false)) { + return true; + } + } + } + return false; +} + +void MI32StartTimeTask(){ + MI32.mode.willConnect = 1; + xTaskCreatePinnedToCore( + MI32TimeTask, + "MI32TimeTask", + 4096, + NULL, + 15, + NULL, + 0); + + +} + +void MI32TimeTask(void *pvParameters){ + if (MIBLEsensors[MI32.state.sensor].type != LYWSD02 && MIBLEsensors[MI32.state.sensor].type != MHOC303) { + MI32.mode.shallSetTime = 0; + vTaskDelete( NULL ); + } + if(MI32ConnectActiveSensor()){ + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10/ portTICK_PERIOD_MS); + } + + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + static BLEUUID charUUID(0xEBE0CCB7,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + pSvc = MI32Client->getService(serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(charUUID); + + } + if (pChr){ + if(pChr->canWrite()) { + union { + uint8_t buf[5]; + uint32_t time; + } _utc; + _utc.time = Rtc.utc_time; + _utc.buf[4] = Rtc.time_timezone / 60; + + if(!pChr->writeValue(_utc.buf,sizeof(_utc.buf),true)) { + MI32.mode.willConnect = 0; + MI32Client->disconnect(); + } + else { + MI32.mode.shallSetTime = 0; + MI32.mode.willSetTime = 0; + } + } + } + MI32Client->disconnect(); + } + + MI32.mode.connected = 0; + MI32.mode.canScan = 1; + vTaskDelete( NULL ); +} + +void MI32StartUnitTask(){ + MI32.mode.willConnect = 1; + xTaskCreatePinnedToCore( + MI32UnitTask, + "MI32UnitTask", + 4096, + NULL, + 15, + NULL, + 0); + + +} + +void MI32UnitTask(void *pvParameters){ + if (MIBLEsensors[MI32.state.sensor].type != LYWSD02 && MIBLEsensors[MI32.state.sensor].type != MHOC303) { + MI32.mode.shallSetUnit = 0; + vTaskDelete( NULL ); + } + + if(MI32ConnectActiveSensor()){ + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10/ portTICK_PERIOD_MS); + } + + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + static BLEUUID serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + static BLEUUID charUUID(0xEBE0CCBE,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + pSvc = MI32Client->getService(serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(charUUID); + } + + if(pChr->canRead()){ + uint8_t curUnit; + const char *buf = pChr->readValue().c_str(); + if( buf[0] != 0 && buf[0]<101 ){ + curUnit = buf[0]; + } + + if(pChr->canWrite()) { + curUnit = curUnit == 0x01?0xFF:0x01; + + if(!pChr->writeValue(&curUnit,sizeof(curUnit),true)) { + MI32.mode.willConnect = 0; + MI32Client->disconnect(); + } + else { + MI32.mode.shallSetUnit = 0; + MI32.mode.willSetUnit = 0; + } + } + } + MI32Client->disconnect(); + } + + MI32.mode.connected = 0; + MI32.mode.canScan = 1; + vTaskDelete( NULL ); +} + +void MI32StartBatteryTask(){ + if (MI32.mode.connected) return; + MI32.mode.willReadBatt = 1; + MI32.mode.willConnect = 1; + MI32.mode.canScan = 0; + + switch (MIBLEsensors[MI32.state.sensor].type){ + case LYWSD03MMC: case MJ_HT_V1: case CGG1: case NLIGHT: case MJYD2S: case YEERC: case MHOC401: + MI32.mode.willConnect = 0; + MI32.mode.willReadBatt = 0; + return; + } + + xTaskCreatePinnedToCore( + MI32BatteryTask, + "MI32BatteryTask", + 4096, + NULL, + 15, + NULL, + 0); +} + +void MI32BatteryTask(void *pvParameters){ + + + MI32.mode.connected = 0; + if(MI32ConnectActiveSensor()){ + uint32_t timer = 0; + while (MI32.mode.connected == 0){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(30/ portTICK_PERIOD_MS); + } + + switch(MIBLEsensors[MI32.state.sensor].type){ + case FLORA: case LYWSD02: case CGD1: + MI32batteryRead(MIBLEsensors[MI32.state.sensor].type); + break; + } + MI32Client->disconnect(); + } + MI32.mode.willReadBatt = 0; + MI32.mode.connected = 0; + vTaskDelete( NULL ); +} + +void MI32batteryRead(uint32_t _type){ + uint32_t timer = 0; + while (!MI32.mode.connected){ + if (timer>1000){ + break; + } + timer++; + vTaskDelay(10/ portTICK_PERIOD_MS); + } + DEBUG_SENSOR_LOG(PSTR("%s connected for battery"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + + switch(_type){ + case FLORA: + { + static BLEUUID _serviceUUID(0x00001204,0x0000,0x1000,0x800000805f9b34fb); + static BLEUUID _charUUID(0x00001a02,0x0000,0x1000,0x800000805f9b34fb); + pSvc = MI32Client->getService(_serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(_charUUID); + } + } + break; + case LYWSD02: + { + static BLEUUID _serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + static BLEUUID _charUUID(0xEBE0CCC4,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6); + pSvc = MI32Client->getService(_serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(_charUUID); + } + } + break; + case CGD1: + { + static BLEUUID _serviceUUID((uint16_t)0x180F); + static BLEUUID _charUUID((uint16_t)0x2A19); + pSvc = MI32Client->getService(_serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(_charUUID); + } + } + break; + } + + if (pChr){ + DEBUG_SENSOR_LOG(PSTR("%s: got %s char %s"),D_CMND_MI32, kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1], pChr->getUUID().toString().c_str()); + if(pChr->canRead()) { + const char *buf = pChr->readValue().c_str(); + MI32readBat((char*)buf); + } + } + MI32.mode.readingDone = 1; +} + + + + + +void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){ + float _tempFloat; + mi_beacon_t _beacon; + + if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1 || MIBLEsensors[_slot].type==YEERC){ + memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)-1); + memcpy((uint8_t*)&_beacon.MAC,(uint8_t*)&_beacon.MAC+1,6); + _beacon.counter = _buf[4]; + } + else{ + memcpy((char *)&_beacon, _buf, _bufSize); + } + + MIBLEsensors[_slot].lastCnt = _beacon.counter; +#ifdef USE_MI_DECRYPTION + int decryptRet = 0; + switch(MIBLEsensors[_slot].type){ + case LYWSD03MMC: case MHOC401: + if (_beacon.frame == 0x5858){ + decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize, LYWSD03MMC); + + } + else return; + break; + case MJYD2S: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MJYD2S: %x"),_beacon.frame); + if (_beacon.frame == 0x5948){ + memmove((uint8_t*)&_beacon.MAC+6,(uint8_t*)&_beacon.MAC, _bufSize); + memcpy((uint8_t*)&_beacon.MAC,MIBLEsensors[_slot].MAC,6); + _bufSize+=6; + MI32_ReverseMAC(_beacon.MAC); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MJYD2S: special packet")); + } + if (_beacon.frame != 0x5910){ + decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); + } + break; + } +if(decryptRet!=0){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: decryption failed with error: %d"),decryptRet); + return; +} +#endif + + if(MIBLEsensors[_slot].type==6){ + DEBUG_SENSOR_LOG(PSTR("CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); + return; + } + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_beacon.type); + switch(_beacon.type){ + case 0x01: + MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6; + MIBLEsensors[_slot].eventType.Btn = 1; + MI32.mode.shallTriggerTele = 1; + + break; + case 0x04: + _tempFloat=(float)(_beacon.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + MIBLEsensors[_slot].eventType.temp = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + + break; + case 0x06: + _tempFloat=(float)(_beacon.hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors[_slot].hum=_tempFloat; + MIBLEsensors[_slot].eventType.hum = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + + break; + case 0x07: + MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; + if(MIBLEsensors[_slot].type==MJYD2S){ + MIBLEsensors[_slot].eventType.noMotion = 1; + } + MIBLEsensors[_slot].eventType.lux = 1; + + break; + case 0x08: + MIBLEsensors[_slot].moisture=_beacon.moist; + MIBLEsensors[_slot].eventType.moist = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + + break; + case 0x09: + MIBLEsensors[_slot].fertility=_beacon.fert; + MIBLEsensors[_slot].eventType.fert = 1; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + + break; + case 0x0a: + if(MI32.option.ignoreBogusBattery){ + if(MIBLEsensors[_slot].type==LYWSD03MMC || MIBLEsensors[_slot].type==MHOC401){ + break; + } + } + if(_beacon.bat<101){ + MIBLEsensors[_slot].bat = _beacon.bat; + MIBLEsensors[_slot].eventType.bat = 1; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + + break; + case 0x0d: + _tempFloat=(float)(_beacon.HT.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(_beacon.HT.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + + break; +#ifdef USE_MI_DECRYPTION + case 0x0f: + if (_beacon.ten!=0) break; + MIBLEsensors[_slot].eventType.motion = 1; + MIBLEsensors[_slot].lastTime = millis(); + MIBLEsensors[_slot].events++; + MIBLEsensors[_slot].lux = _beacon.lux; + MIBLEsensors[_slot].eventType.lux = 1; + MIBLEsensors[_slot].NMT = 0; + MI32.mode.shallTriggerTele = 1; + + break; + case 0x17: + MIBLEsensors[_slot].NMT = _beacon.NMT; + MIBLEsensors[_slot].eventType.NMT = 1; + MI32.mode.shallTriggerTele = 1; + + break; +#endif + default: + if (MIBLEsensors[_slot].type==NLIGHT){ + MIBLEsensors[_slot].eventType.motion = 1; + MIBLEsensors[_slot].events++; + MIBLEsensors[_slot].NMT = 0; + MIBLEsensors[_slot].lastTime = millis(); + MI32.mode.shallTriggerTele = 1; + + } + else{ + AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf,_bufSize); + } + break; + } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; +} + +void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ + ATCPacket_t *_packet = (ATCPacket_t*)_buf; + uint32_t _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->frameCnt); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + if(_slot==0xff) return; + + MIBLEsensors[_slot].RSSI=RSSI; + + MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors.at(_slot).hum = (float)_packet->hum; + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors.at(_slot).bat = _packet->batPer; + MIBLEsensors[_slot].eventType.bat = 1; + + MIBLEsensors[_slot].shallSendMQTT = 1; + if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; + +} + +void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ + uint8_t _addr[6]; + memcpy(_addr,addr,6); + uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576, 0); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + if(_slot==0xff) return; + MIBLEsensors[_slot].RSSI=RSSI; + cg_packet_t _packet; + memcpy((char*)&_packet,_buf,sizeof(_packet)); + switch (_packet.mode){ + case 0x0401: + float _tempFloat; + _tempFloat=(float)(_packet.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp = _tempFloat; + MIBLEsensors[_slot].eventType.temp = 1; + DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); + } + _tempFloat=(float)(_packet.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + MIBLEsensors[_slot].eventType.hum = 1; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), _packet.temp, _packet.hum); + break; + case 0x0102: + if(_packet.bat<101){ + MIBLEsensors.at(_slot).bat = _packet.bat; + MIBLEsensors[_slot].eventType.bat = 1; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + break; + default: + DEBUG_SENSOR_LOG(PSTR("MI32: unexpected CGD1-packet")); + } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; +} + +void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) { + if(bufsize<9) { + return; + } + uint16_t _type= buf[3]*256 + buf[2]; + + uint8_t _addr[6]; + memcpy(_addr,addr,6); + uint16_t _slot = MIBLEgetSensorSlot(_addr, _type, buf[4]); + if(_slot!=0xff) { + MIBLEsensors[_slot].RSSI=RSSI; + MI32parseMiBeacon(buf,_slot,bufsize); + } +} +# 1456 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID){ + AddLog_P2(LOG_LEVEL_DEBUG_MORE,PSTR("MI32: Beacon:____________")); + for (uint32_t i = 0; i19) { + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: Scan buffer full")); + MI32.state.beaconScanCounter = 1; + return; + } + for(auto _scanResult : MINBLEscanResult){ + if(memcmp(addr,_scanResult.MAC,6)==0){ + + return; + } + } + scan_entry_t _new; + _new.RSSI = RSSI; + _new.CID = 0; + _new.SVC = 0; + _new.UUID = 0; + memcpy(_new.MAC,addr,sizeof(_new.MAC)); + MI32ParseGenericBeacon(payload,payloadLength,&_new.CID,&_new.SVC,&_new.UUID); + MINBLEscanResult.push_back(_new); +} +# 1542 "/workspace/Tasmota/tasmota/xsns_62_MI_ESP32.ino" +void MI32addBeacon(uint8_t index, char* data){ + auto &_new = MIBLEbeacons[index-1]; + MI32HexStringToBytes(data,_new.MAC); + char _MAC[18]; + ToHex_P(MIBLEbeacons[index-1].MAC,6,_MAC,18,':'); + char _empty[6] = {0}; + _new.time = 0; + if(memcmp(_empty,_new.MAC,6) == 0){ + _new.active = false; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: beacon%u deactivated"), index); + } + else{ + _new.active = true; + MI32.mode.activeBeacon = 1; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: beacon added with MAC: %s"), _MAC); + } +} + + + + + +void MI32showScanResults(){ + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: found %u devices in scan:"), MINBLEscanResult.size()); + for(auto _scanResult : MINBLEscanResult){ + char _MAC[18]; + ToHex_P(_scanResult.MAC,6,_MAC,18,':'); + AddLog_P2(LOG_LEVEL_INFO,PSTR("MAC: %s _ CID: %04x _ SVC: %04x _ UUID: %04x _ RSSI: %d"), _MAC, _scanResult.CID, _scanResult.SVC, _scanResult.UUID, _scanResult.RSSI); + } + MINBLEscanResult.clear(); +} + + + + +void MI32readHT_LY(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0 && _buf[1] != 0){ + memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); + uint32_t _slot = MI32.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + + } + _tempFloat=(float)LYWSD0x_HT.hum; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("LYWSD0x: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401){ + MIBLEsensors[_slot].bat = ((float)LYWSD0x_HT.volt-2100.0f)/12.0f; + MI32.mode.willReadBatt = 0; + MIBLEsensors[_slot].eventType.bat = 1; + } + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } +} + +bool MI32readBat(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_MI32,_buf[0]); + uint32_t _slot = MI32.state.sensor; + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + if(_buf[0]<101){ + MIBLEsensors[_slot].bat=_buf[0]; + if(MIBLEsensors[_slot].type==FLORA){ + memcpy(MIBLEsensors[_slot].firmware, _buf+2, 5); + MIBLEsensors[_slot].firmware[5] = '\0'; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Firmware: %s"),D_CMND_MI32,MIBLEsensors[_slot].firmware); + } + MIBLEsensors[_slot].eventType.bat = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + return true; + } + } + return false; +} + + + + + + +void MI32Every50mSecond(){ + if(MI32.mode.shallTriggerTele){ + MI32.mode.shallTriggerTele = 0; + MI32triggerTele(); + } +} + + + + + + +void MI32EverySecond(bool restart){ + static uint32_t _counter = MI32.period - 15; + static uint32_t _nextSensorSlot = 0; + + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MIBLEsensors[i].type==NLIGHT || MIBLEsensors[i].type==MJYD2S){ + MIBLEsensors[i].NMT++; + } + } + + uint32_t _idx = 0; + uint32_t _activeBeacons = 0; + for (auto &_beacon : MIBLEbeacons){ + _idx++; + if(_beacon.active == false) continue; + _activeBeacons++; + _beacon.time++; + Response_P(PSTR("{\"Beacon%u\":{\"Time\":%u}}"), _beacon.time); + XdrvRulesProcess(); + } + if(_activeBeacons==0) MI32.mode.activeBeacon = 0; + + if(MI32.state.beaconScanCounter!=0){ + MI32.state.beaconScanCounter--; + if(MI32.state.beaconScanCounter==0){ + MI32showScanResults(); + } + } + + if(MI32.mode.shallShowStatusInfo == 1){ + MI32StatusInfo(); + } + + if(restart){ + _counter = 0; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 1; + MI32.mode.willReadBatt = 0; + MI32.mode.willConnect = 0; + return; + } + + if (MI32.mode.shallSetTime) { + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + if (MI32.mode.willSetTime == 0){ + MI32.mode.willSetTime = 1; + MI32StartTask(MI32_TASK_TIME); + } + } + + if (MI32.mode.shallSetUnit) { + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + if (MI32.mode.willSetUnit == 0){ + MI32.mode.willSetUnit = 1; + MI32StartTask(MI32_TASK_UNIT); + } + } + + if (MI32.mode.willReadBatt) return; + + if (_counter>MI32.period) { + _counter = 0; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 1; + } + + if(MI32.mode.connected == 1 || MI32.mode.willConnect == 1) return; + + if(MIBLEsensors.size()==0) { + if (MI32.mode.runningScan == 0 && MI32.mode.canScan == 1) MI32StartTask(MI32_TASK_SCAN); + return; + } + + if(_counter==0) { + + MI32.state.sensor = _nextSensorSlot; + MI32.mode.canScan = 0; + + if (MI32.mode.connected || MI32.mode.willConnect) return; + _nextSensorSlot++; + MI32.mode.canConnect = 1; + if(MI32.mode.connected == 0) { + if (MI32.mode.shallReadBatt) { + + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); + + MI32StartTask(MI32_TASK_BATT); + } +#ifndef USE_MI_DECRYPTION + else{ + MI32StartTask(MI32_TASK_CONN); + } +#endif + } + if (_nextSensorSlot>(MIBLEsensors.size()-1)) { + _nextSensorSlot= 0; + _counter++; + if (MI32.mode.shallReadBatt){ + MI32.mode.shallReadBatt = 0; + } + MI32.mode.canConnect = 0; + MI32.mode.canScan = 1; + } + } + else _counter++; + if (MI32.state.sensor>MIBLEsensors.size()-1) { + _nextSensorSlot = 0; + MI32.mode.canScan = 1; + } + MI32StartTask(MI32_TASK_SCAN); +} + + + + + +bool MI32Cmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_MI32); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MI32), disp_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMI32_Commands); + switch (command_code) { + case CMND_MI32_PERIOD: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload==1) { + MI32EverySecond(true); + XdrvMailbox.payload = MI32.period; + } + else { + MI32.period = XdrvMailbox.payload; + } + } + else { + XdrvMailbox.payload = MI32.period; + } + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_TIME: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02 || MIBLEsensors[XdrvMailbox.payload].type == MHOC303){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: will set Time"),D_CMND_MI32); + MI32.state.sensor = XdrvMailbox.payload; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + MI32.mode.shallSetTime = 1; + MI32.mode.willSetTime = 0; + } + } + } + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_UNIT: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02 || MIBLEsensors[XdrvMailbox.payload].type == MHOC303){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: will set Unit"),D_CMND_MI32); + MI32.state.sensor = XdrvMailbox.payload; + MI32.mode.canScan = 0; + MI32.mode.canConnect = 0; + MI32.mode.shallSetUnit = 1; + MI32.mode.willSetUnit = 0; + } + } + } + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_PAGE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MI32.perPage; + MI32.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = MI32.perPage; + Response_P(S_JSON_MI32_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MI32_BATTERY: + MI32EverySecond(true); + MI32.mode.shallReadBatt = 1; + MI32.mode.canConnect = 1; + XdrvMailbox.payload = MI32.period; + Response_P(S_JSON_MI32_COMMAND, command, ""); + break; +#ifdef USE_MI_DECRYPTION + case CMND_MI32_KEY: + if (XdrvMailbox.data_len==44){ + MI32AddKey(XdrvMailbox.data); + Response_P(S_JSON_MI32_COMMAND, command, XdrvMailbox.data); + } + break; +#endif + case CMND_MI32_BEACON: + if (XdrvMailbox.data_len == 0) { + switch(XdrvMailbox.index){ + case 0: + MI32.state.beaconScanCounter = 8; + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,PSTR("\"scanning\"")); + break; + case 1: case 2: case 3: case 4: + char _MAC[18]; + ToHex_P(MIBLEbeacons[XdrvMailbox.index-1].MAC,6,_MAC,18,':'); + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,_MAC); + break; + } + } + else { + if(XdrvMailbox.data_len == 12 || XdrvMailbox.data_len == 17){ + switch(XdrvMailbox.index){ + case 1: case 2: case 3: case 4: + MI32addBeacon(XdrvMailbox.index,XdrvMailbox.data); + break; + } + } + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,XdrvMailbox.data); + } + break; + + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + + +const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0916{m}%u%s / %u{e}"; +const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; +const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u %%{e}"; +const char HTTP_LASTBUTTON[] PROGMEM = "{s}%s Last Button{m}%u {e}"; +const char HTTP_EVENTS[] PROGMEM = "{s}%s Events{m}%u {e}"; +const char HTTP_NMT[] PROGMEM = "{s}%s No motion{m}> %u seconds{e}"; +const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}"; +const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; + +void MI32Show(bool json) +{ + if (json) { +#ifdef USE_HOME_ASSISTANT + bool _noSummarySave = MI32.option.noSummary; + bool _minimalSummarySave = MI32.option.minimalSummary; + if(hass_mode==2){ + if(MI32.option.holdBackFirstAutodiscovery){ + if(!MI32.mode.firstAutodiscoveryDone){ + MI32.mode.firstAutodiscoveryDone = 1; + return; + } + } + MI32.option.noSummary = false; + MI32.option.minimalSummary = false; + } +#endif + + if(!MI32.mode.triggeredTele){ + MI32.mode.shallClearResults=1; + if(MI32.option.noSummary) return; + } + + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MI32.mode.triggeredTele && MIBLEsensors[i].eventType.raw == 0) continue; + if(MI32.mode.triggeredTele && MIBLEsensors[i].shallSendMQTT==0) continue; + + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":"), + kMI32DeviceType[MIBLEsensors[i].type-1], + MIBLEsensors[i].MAC[3], MIBLEsensors[i].MAC[4], MIBLEsensors[i].MAC[5]); + + uint32_t _positionCurlyBracket = strlen(mqtt_data); + + if((!MI32.mode.triggeredTele && !MI32.option.minimalSummary)||MI32.mode.triggeredTele){ + bool tempHumSended = false; + if(MIBLEsensors[i].feature.tempHum){ + if(MIBLEsensors[i].eventType.tempHum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + tempHumSended = true; + } + } + } + if(MIBLEsensors[i].feature.temp && !tempHumSended){ + if(MIBLEsensors[i].eventType.temp || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + } + } + if(MIBLEsensors[i].feature.hum && !tempHumSended){ + if(MIBLEsensors[i].eventType.hum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].hum) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + char hum[FLOATSZ]; + dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum); + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); + } + } + } + if (MIBLEsensors[i].feature.lux){ + if(MIBLEsensors[i].eventType.lux || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (MIBLEsensors[i].lux!=0x0ffffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); +#ifdef USE_HOME_ASSISTANT + if (MIBLEsensors[i].lux==0x0ffffff) MI32nullifyEndOfMQTT_DATA(); +#endif + } + } + } + if (MIBLEsensors[i].feature.moist){ + if(MIBLEsensors[i].eventType.moist || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (MIBLEsensors[i].moisture!=0xff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture); +#ifdef USE_HOME_ASSISTANT + if (MIBLEsensors[i].moisture==0xff) MI32nullifyEndOfMQTT_DATA(); +#endif + } + } + } + if (MIBLEsensors[i].feature.fert){ + if(MIBLEsensors[i].eventType.fert || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (MIBLEsensors[i].fertility!=0xffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility); +#ifdef USE_HOME_ASSISTANT + if (MIBLEsensors[i].fertility==0xffff) MI32nullifyEndOfMQTT_DATA(); +#endif + } + } + } + if (MIBLEsensors[i].feature.Btn){ + if(MIBLEsensors[i].eventType.Btn +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif + ){ + ResponseAppend_P(PSTR(",\"Btn\":%u"),MIBLEsensors[i].Btn); + } + } + } + if (MIBLEsensors[i].feature.PIR){ + if(MIBLEsensors[i].eventType.motion || !MI32.mode.triggeredTele){ + if(MI32.mode.triggeredTele) ResponseAppend_P(PSTR(",\"PIR\":1")); + ResponseAppend_P(PSTR(",\"Events\":%u"),MIBLEsensors[i].events); + } + else if(MIBLEsensors[i].eventType.noMotion && MI32.mode.triggeredTele){ + ResponseAppend_P(PSTR(",\"PIR\":0")); + } + } + + if (MIBLEsensors[i].type == FLORA && !MI32.mode.triggeredTele) { + if (MIBLEsensors[i].firmware[0] != '\0') { + ResponseAppend_P(PSTR(",\"Firmware\":\"%s\""), MIBLEsensors[i].firmware); + } + } + + if (MIBLEsensors[i].feature.NMT || !MI32.mode.triggeredTele){ + if(MIBLEsensors[i].eventType.NMT){ + ResponseAppend_P(PSTR(",\"NMT\":%u"), MIBLEsensors[i].NMT); + } + } + if (MIBLEsensors[i].feature.bat){ + if(MIBLEsensors[i].eventType.bat || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (MIBLEsensors[i].bat != 0x00 +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif + ) { + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); +#ifdef USE_HOME_ASSISTANT + if (MIBLEsensors[i].bat == 0x00) MI32nullifyEndOfMQTT_DATA(); +#endif + } + } + } + if (MI32.option.showRSSI) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].RSSI); + + if(_positionCurlyBracket==strlen(mqtt_data)) ResponseAppend_P(PSTR(",")); + ResponseAppend_P(PSTR("}")); + mqtt_data[_positionCurlyBracket] = '{'; + MIBLEsensors[i].eventType.raw = 0; + if(MIBLEsensors[i].shallSendMQTT==1){ + MIBLEsensors[i].shallSendMQTT = 0; + continue; + } + } + MI32.mode.triggeredTele = 0; + + uint32_t _idx = 0; + for (auto _beacon : MIBLEbeacons){ + _idx++; + if(!_beacon.active) continue; + char _MAC[18]; + ToHex_P(_beacon.MAC,6,_MAC,18,':'); + ResponseAppend_P(PSTR(",\"Beacon%u\":{\"MAC\":\"%s\",\"CID\":\"0x%04x\",\"SVC\":\"0x%04x\"," + "\"UUID\":\"0x%04x\",\"Time\":%u,\"RSSI\":%d}"), + _idx,_MAC,_beacon.CID,_beacon.SVC,_beacon.UUID,_beacon.time,_beacon.RSSI); + } +#ifdef USE_HOME_ASSISTANT + if(hass_mode==2){ + MI32.option.noSummary = _noSummarySave; + MI32.option.minimalSummary = _minimalSummarySave; + } +#endif +#ifdef USE_WEBSERVER + } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + int32_t i = _page * MI32.perPage; + uint32_t j = i + MI32.perPage; + if (j+1>MIBLEsensors.size()){ + j = MIBLEsensors.size(); + } + char stemp[5] ={0}; + if (MIBLEsensors.size()-(_page*MI32.perPage)>1 && MI32.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MIBLEsensors.size()==0) i=-1; + + WSContentSend_PD(HTTP_MI32, i+1,stemp,MIBLEsensors.size()); + for (i; iFLORA) { + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + WSContentSend_THD(kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } +#ifdef USE_MI_DECRYPTION + if (MIBLEsensors[i].type==NLIGHT || MIBLEsensors[i].type==MJYD2S) { +#else + if (MIBLEsensors[i].type==NLIGHT) { +#endif + WSContentSend_PD(HTTP_EVENTS, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].events); + if(MIBLEsensors[i].NMT>0) WSContentSend_PD(HTTP_NMT, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].NMT); + } + if (MIBLEsensors[i].lux!=0x00ffffff) { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); + } + if(MIBLEsensors[i].bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } + if (MIBLEsensors[i].type==YEERC){ + WSContentSend_PD(HTTP_LASTBUTTON, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].Btn); + } + } + _counter++; + if(_counter>3) { + _page++; + _counter=0; + } + if (MIBLEsensors.size()%MI32.perPage==0 && _page==MIBLEsensors.size()/MI32.perPage) { _page = 0; } + if (_page>MIBLEsensors.size()/MI32.perPage) { _page = 0; } + + + uint32_t _idx=0; + if(MI32.mode.activeBeacon){ + WSContentSend_PD(HTTP_MI32_HL); + char _sbeacon[] = "Beacon1"; + for (auto &_beacon : MIBLEbeacons){ + _idx++; + if(!_beacon.active) continue; + WSContentSend_PD(HTTP_MI32_HL); + _sbeacon[6] = _idx + 0x30; + char _MAC[18]; + ToHex_P(_beacon.MAC,6,_MAC,18,':'); + WSContentSend_PD(HTTP_MI32_MAC, _sbeacon, D_MAC_ADDRESS, _MAC); + WSContentSend_PD(HTTP_RSSI, _sbeacon, _beacon.RSSI); + if(_beacon.CID!=0) WSContentSend_PD(PSTR("{s}Beacon%u CID{m}0x%04X{e}"),_idx, _beacon.CID); + if(_beacon.SVC!=0) WSContentSend_PD(PSTR("{s}Beacon%u SVC{m}0x%04X{e}"),_idx, _beacon.SVC); + if(_beacon.UUID!=0) WSContentSend_PD(PSTR("{s}Beacon%u UUID{m}0x%04X{e}"),_idx, _beacon.UUID); + WSContentSend_PD(PSTR("{s}Beacon%u Time{m}%u seconds{e}"),_idx, _beacon.time); + } + } +#endif + } +} + + + + + +bool Xsns62(uint8_t function) +{ + bool result = false; + if (FUNC_INIT == function){ + MI32Init(); + } + + if (MI32.mode.init) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + MI32Every50mSecond(); + break; + case FUNC_EVERY_SECOND: + MI32EverySecond(false); + break; + case FUNC_COMMAND: + result = MI32Cmd(); + break; + case FUNC_JSON_APPEND: + MI32Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MI32Show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_62_MI_HM10.ino" +# 42 "/workspace/Tasmota/tasmota/xsns_62_MI_HM10.ino" +#ifdef ESP8266 + +#ifdef USE_HM10 + +#define XSNS_62 62 + +#include +#include + +TasmotaSerial *HM10Serial; +#define HM10_BAUDRATE 115200 + +#define HM10_MAX_TASK_NUMBER 12 +uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; + +#define HM10_MAX_RX_BUF 384 + +struct { + uint8_t current_task_delay; + uint8_t last_command; + uint16_t perPage = 4; + uint16_t firmware; + uint32_t period; + uint32_t serialSpeed; + union { + uint32_t time; + uint8_t timebuf[4]; + }; + uint16_t autoScanInterval; + struct { + uint32_t awaiting:8; + uint32_t init:1; + uint32_t pending_task:1; + uint32_t connected:1; + uint32_t subscribed:1; + uint32_t autoScan:1; + uint32_t shallTriggerTele:1; + uint32_t triggeredTele:1; + } mode; + struct { + uint8_t sensor; + + } state; + struct { + uint32_t allwaysAggregate:1; + uint32_t showRSSI:1; + uint32_t ignoreBogusBattery:1; + uint32_t noSummary:1; + uint32_t minimalSummary:1; + uint32_t noRealTime:1; + } option; + char *rxBuffer; +} HM10; + +#pragma pack(1) + +struct LYWSD0x_HT_t{ + uint16_t temp; + uint8_t hum; + uint16_t volt; +}; +struct CGD1_HT_t{ + uint8_t spare; + uint16_t temp; + uint16_t hum; +}; +struct Flora_TLMF_t{ + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + uint32_t ID; +}; + +struct mi_beacon_t{ + uint16_t frame; + uint16_t productID; + uint8_t counter; + uint8_t MAC[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ + uint16_t temp; + uint16_t hum; + }HT; + uint8_t bat; + uint16_t temp; + uint16_t hum; + uint32_t lux; + uint8_t moist; + uint16_t fert; + uint32_t NMT; + struct{ + uint16_t num; + uint8_t longPress; + }Btn; + }; + uint8_t padding[12]; +}; + +struct ATCPacket_t{ + uint8_t MAC[6]; + int16_t temp; + uint8_t hum; + uint8_t batPer; + uint16_t batMV; + uint8_t frameCnt; +}; + +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; + uint8_t lastCnt; + uint8_t shallSendMQTT; + uint8_t showedUp; + uint8_t MAC[6]; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t PIR:1; + uint32_t Btn:1; + }; + uint32_t raw; + } feature; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t motion:1; + uint32_t noMotion:1; + uint32_t Btn:1; + }; + uint32_t raw; + } eventType; + + int rssi; + uint32_t lastTime; + uint32_t lux; + float temp; + union { + struct { + uint8_t moisture; + uint16_t fertility; + char firmware[6]; + }; + struct { + float hum; + }; + struct { + uint16_t events; + uint32_t NMT; + }; + uint16_t Btn; + }; + union { + uint8_t bat; + }; +}; + + +std::vector MIBLEsensors; + + + + + +#define D_CMND_HM10 "HM10" + +const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; +const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; +const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page"; + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03MMC 4 +#define CGG1 5 +#define CGD1 6 +#define NLIGHT 7 +#define MJYD2S 8 +#define YEERC 9 +#define MHOC401 10 +#define MHOC303 11 +#define ATC 12 + +#define HM10_TYPES 12 + +const uint16_t kHM10SlaveID[HM10_TYPES]={ + 0x0098, + 0x01aa, + 0x045b, + 0x055b, + 0x0347, + 0x0576, + 0x03dd, + 0x07f6, + 0x0153, + 0x0387, + 0x06d3, + 0x0a1c + }; + +const char kHM10DeviceType1[] PROGMEM = "Flora"; +const char kHM10DeviceType2[] PROGMEM = "MJ_HT_V1"; +const char kHM10DeviceType3[] PROGMEM = "LYWSD02"; +const char kHM10DeviceType4[] PROGMEM = "LYWSD03"; +const char kHM10DeviceType5[] PROGMEM = "CGG1"; +const char kHM10DeviceType6[] PROGMEM = "CGD1"; +const char kHM10DeviceType7[] PROGMEM = "NLIGHT"; +const char kHM10DeviceType8[] PROGMEM = "MJYD2S"; +const char kHM10DeviceType9[] PROGMEM = "YEERC"; +const char kHM10DeviceType10[] PROGMEM ="MHOC401"; +const char kHM10DeviceType11[] PROGMEM ="MHOC303"; +const char kHM10DeviceType12[] PROGMEM ="ATC"; + +const char * kHM10DeviceType[] PROGMEM = {kHM10DeviceType1,kHM10DeviceType2,kHM10DeviceType3,kHM10DeviceType4,kHM10DeviceType5,kHM10DeviceType6,kHM10DeviceType7,kHM10DeviceType8,kHM10DeviceType9,kHM10DeviceType10,kHM10DeviceType11,kHM10DeviceType12}; + + + + + +enum HM10_Commands { + CMND_HM10_DISC_SCAN, + CMND_HM10_AT, + CMND_HM10_PERIOD, + CMND_HM10_BAUD, + CMND_HM10_TIME, + CMND_HM10_AUTO, + CMND_HM10_PAGE + }; + +enum HM10_awaitData: uint8_t { + none = 0, + tempHumLY = 1, + TLMF = 2, + bat = 3, + tempHumCGD1 = 4, + discScan = 5, + tempHumMJ = 6 + }; + + + + + +#define TASK_HM10_NOTASK 0 +#define TASK_HM10_ROLE1 1 +#define TASK_HM10_IMME1 2 +#define TASK_HM10_RENEW 3 +#define TASK_HM10_RESET 4 +#define TASK_HM10_DISC 5 +#define TASK_HM10_CONN 6 +#define TASK_HM10_VERSION 7 +#define TASK_HM10_NAME 8 +#define TASK_HM10_FEEDBACK 9 +#define TASK_HM10_DISCONN 10 +#define TASK_HM10_SUB_L3 11 + +#define TASK_HM10_SCAN9 13 +#define TASK_HM10_UN_L3 14 + +#define TASK_HM10_READ_BT_L3 16 +#define TASK_HM10_SUB_L2 17 +#define TASK_HM10_UN_L2 18 +#define TASK_HM10_READ_BT_L2 19 +#define TASK_HM10_TIME_L2 20 +#define TASK_HM10_SHOW0 21 +#define TASK_HM10_READ_BF_FL 22 +#define TASK_HM10_CALL_TLMF_FL 23 +#define TASK_HM10_READ_TLMF_FL 24 +#define TASK_HM10_SUB_HT_CGD1 25 +#define TASK_HM10_UN_HT_CGD1 26 +#define TASK_HM10_READ_B_CGD1 27 + +#define TASK_HM10_READ_B_MJ 29 +#define TASK_HM10_SUB_HT_MJ 30 + +#define TASK_HM10_STATUS_EVENT 32 + +#define TASK_HM10_DONE 99 + + + + + +void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ + HM10_TASK_LIST[slot][0] = task; + HM10_TASK_LIST[slot][1] = delay; + HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; + HM10.current_task_delay = HM10_TASK_LIST[0][1]; +} + +void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ + HM10.last_command = HM10_TASK_LIST[slot][0]; + HM10_TASK_LIST[slot][0] = task; +} + +void HM10_ReverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + + + + + +void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_ROLE1,1,1); + HM10_Launchtask(TASK_HM10_IMME1,2,1); + HM10_Launchtask(TASK_HM10_RESET,3,1); + HM10_Launchtask(TASK_HM10_VERSION,4,10); + HM10_Launchtask(TASK_HM10_SCAN9,5,2); + HM10_Launchtask(TASK_HM10_DISC,6,2); + HM10_Launchtask(TASK_HM10_STATUS_EVENT,7,2); + } + +void HM10_Discovery_Scan(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_DISC,1,1); + HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,1); + } + +void HM10_Read_LYWSD03(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L3,2,20); + HM10_Launchtask(TASK_HM10_UN_L3,3,80); + + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } + +void HM10_Read_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L2,2,20); + HM10_Launchtask(TASK_HM10_UN_L2,3,80); + HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Time_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); + HM10_Launchtask(TASK_HM10_CONN,1,5); + HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); + HM10_Launchtask(TASK_HM10_TIME_L2,3,20); + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } + +void HM10_Read_Flora(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); + HM10_Launchtask(TASK_HM10_CONN,1,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,2,5); + HM10_Launchtask(TASK_HM10_READ_BF_FL,3,20); + HM10_Launchtask(TASK_HM10_CALL_TLMF_FL,4,30); + HM10_Launchtask(TASK_HM10_DISCONN,5,10); + } + +void HM10_Read_CGD1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_HT_CGD1,2,20); + HM10_Launchtask(TASK_HM10_UN_HT_CGD1,3,10); + HM10_Launchtask(TASK_HM10_READ_B_CGD1,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Read_MJ_HT_V1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_READ_B_MJ,2,20); + HM10_Launchtask(TASK_HM10_SUB_HT_MJ,3,10); + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } +# 444 "/workspace/Tasmota/tasmota/xsns_62_MI_HM10.ino" +uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint32_t _rssi){ + + DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type); + bool _success = false; + for (uint32_t i=0;ibegin(HM10.serialSpeed)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); + if (HM10Serial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("%s: claim HW"),D_CMND_HM10); + } + HM10_Reset(); + HM10.mode.pending_task = 1; + HM10.mode.init = 1; + HM10.period = Settings.tele_period; + DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10); + + + HM10.option.noRealTime = 1; + HM10.option.allwaysAggregate = 1; + HM10.option.showRSSI = 0; + HM10.option.ignoreBogusBattery = 1; + HM10.option.noSummary = 0; + HM10.option.minimalSummary = 0; + + HM10.rxBuffer = new char[HM10_MAX_RX_BUF]; + } + return; +} + + + + + +void HM10parseMiBeacon(char * _buf, uint32_t _slot){ + float _tempFloat; + mi_beacon_t _beacon; + if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1){ + memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)-1); + memcpy((uint8_t*)&_beacon.MAC,(uint8_t*)&_beacon.MAC+1,6); + } + else{ + memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon)); + } + HM10_ReverseMAC(_beacon.MAC); + if(memcmp(_beacon.MAC,MIBLEsensors[_slot].MAC,sizeof(_beacon.MAC))!=0){ + if (MIBLEsensors[_slot].showedUp>3) return; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].MAC[5], MIBLEsensors[_slot].MAC[4],MIBLEsensors[_slot].MAC[3],MIBLEsensors[_slot].MAC[2],MIBLEsensors[_slot].MAC[1],MIBLEsensors[_slot].MAC[0]); + DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.MAC[5], _beacon.MAC[4], _beacon.MAC[3],_beacon.MAC[2],_beacon.MAC[1],_beacon.MAC[0]); + MIBLEsensors.erase(MIBLEsensors.begin()+_slot); + return; + } + if (MIBLEsensors[_slot].showedUp<4) MIBLEsensors[_slot].showedUp++; + + DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); + DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + + + if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){ + DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); + return; + } + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10DeviceType[MIBLEsensors[_slot].type-1],_slot); + switch(_beacon.type){ + case 0x01: + MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6; + MIBLEsensors[_slot].eventType.Btn = 1; + + break; + case 0x04: + _tempFloat=(float)(_beacon.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + MIBLEsensors[_slot].eventType.temp = 1; + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + break; + case 0x06: + _tempFloat=(float)(_beacon.hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors[_slot].hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + MIBLEsensors[_slot].eventType.hum = 1; + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + break; + case 0x07: + if(MIBLEsensors[_slot].type==MJYD2S){ + MIBLEsensors[_slot].eventType.noMotion = 1; + } + MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + break; + case 0x08: + if(_beacon.moist<101){ + MIBLEsensors[_slot].moisture=_beacon.moist; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + MIBLEsensors[_slot].eventType.moist = 1; + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + break; + case 0x09: + if(_beacon.fert<65535){ + MIBLEsensors[_slot].fertility=_beacon.fert; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + MIBLEsensors[_slot].eventType.fert = 1; + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + break; + case 0x0a: + if(_beacon.bat<101){ + MIBLEsensors[_slot].bat = _beacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + MIBLEsensors[_slot].eventType.bat = 1; + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); + break; + case 0x0d: + _tempFloat=(float)(_beacon.HT.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(_beacon.HT.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + break; + } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; +} + +void HM10parseATC(char * _buf, uint32_t _slot){ + ATCPacket_t *_packet = (ATCPacket_t*)_buf; + if(memcmp(_packet->MAC,MIBLEsensors.at(_slot).MAC,6)!=0) return; + MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; + MIBLEsensors.at(_slot).hum = (float)_packet->hum; + MIBLEsensors.at(_slot).bat = _packet->batPer; + MIBLEsensors[_slot].shallSendMQTT = 1; +} + +char* HM10ParseResponse(char *buf, uint16_t bufsize) { + if (!strncmp(buf,"HMSoft",6)) { + const char* _fw = "000"; + memcpy((void *)_fw,(void *)(buf+8),3); + HM10.firmware = atoi(_fw); + DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware); + return buf; + } + char * _pos = nullptr; + uint32_t _idx = 0; + char _subStr[] = "SA:"; + while(_pos = (char*) memchr(buf+_idx, 'I', 60)){ + _idx=_pos-buf; + if(memcmp(&_pos+1,_subStr,3)){ + break; + } + } + if(_pos) { + uint8_t _newMacArray[6] = {0}; + memcpy((void *)_newMacArray,(void *)(_pos+4),6); + uint32_t _rssi = 255- (uint8_t)(_pos[11]); + HM10_ReverseMAC(_newMacArray); + DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); + uint16_t _type=0xffff; + + for (_idx =10;_idx<32;_idx++){ + if((uint8_t)_pos[_idx] == 0xfe){ + if((uint8_t)_pos[_idx-2] == 0x16 && (uint8_t)_pos[_idx-1] == 0x95){ + _pos = _pos+_idx+1; + _type = (uint8_t)_pos[3]*256 + (uint8_t)_pos[2]; + DEBUG_SENSOR_LOG(PSTR("%s: type %04x _ %02x %02x"),D_CMND_HM10,_type, _pos[3],_pos[2]); + break; + } + } + else if ((uint8_t)_pos[_idx] == 0x1a){ + if ((uint8_t)_pos[_idx+1] == 0x18){ + if((uint8_t)_pos[_idx+4] == 0x95 && (uint8_t)_pos[_idx+3] == 0x16) continue; + _type = 0xa1c; + _pos = _pos+_idx+2; + break; + } + } + } + uint16_t _slot = MIBLEgetSensorSlot(_newMacArray, _type, _rssi); + if(_slot!=0xff){ + if (_type==0xa1c) HM10parseATC(_pos,_slot); + else HM10parseMiBeacon(_pos,_slot); + } + if(bufsize>64) return _pos+12; + else return nullptr; + } + else if (strstr(buf, "LOST")){ + HM10.current_task_delay = 0; + HM10.mode.connected = false; + } + else if (strstr(buf, "CONNF")){ + HM10.mode.connected = false; + HM10.current_task_delay = 0; + } + else if (strstr(buf, "CONN")){ + HM10.current_task_delay = 0; + } + else { + DEBUG_SENSOR_LOG(PSTR("%s: empty response"),D_CMND_HM10); + return buf; + } + return _pos; +} + +void HM10readHT_LY(char *_buf){ + + if(_buf[0]==0x4f && _buf[1]==0x4b) return; + if(_buf[0] != 0 && _buf[1] != 0){ + LYWSD0x_HT_t *packet = (LYWSD0x_HT_t*)_buf; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,packet->temp,packet->hum); + uint32_t _slot = HM10.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(packet->temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + MIBLEsensors[_slot].showedUp=255; + } + _tempFloat=(float)packet->hum; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("LYWSD0x: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401){ + MIBLEsensors[_slot].bat = ((float)packet->volt-2100.0f)/12.0f; + MIBLEsensors[_slot].eventType.bat = 1; + } + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; + } +} + +void HM10readHT_CGD1(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b) return; + if(_buf[0] == 0){ + if(_buf[1]==0 && _buf[2]==0 && _buf[3]==0 && _buf[4]==0) return; + CGD1_HT_t *_packet = (CGD1_HT_t*)_buf; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H * 100: %u"),D_CMND_HM10,_packet->temp,_packet->hum); + uint32_t _slot = HM10.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(_packet->temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + MIBLEsensors[_slot].showedUp=255; + } + _tempFloat=(float)_packet->hum/100.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; + } +} + +void HM10readHT_MJ_HT_V1(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]!=0x54 && _buf[1]!=0x3d) return; + + + uint32_t _temp = (atoi(_buf+2) * 10) + atoi(_buf+5); + uint32_t _hum = (atoi(_buf+9) * 10) + atoi(_buf+12); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, H * 10: %u"),D_CMND_HM10,_temp,_hum); + uint32_t _slot = HM10.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)_temp/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + MIBLEsensors[_slot].showedUp=255; + } + _tempFloat=(float)_hum/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; +} + +void HM10readTLMF(char *_buf){ + AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)_buf,16); + Flora_TLMF_t *_packet = (Flora_TLMF_t*)_buf; + if(_packet->ID==0xFB003C02){ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,_packet->temp,_packet->lux,_packet->moist,_packet->fert); + uint32_t _slot = HM10.state.sensor; + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + MIBLEsensors[_slot].showedUp=255; + + static float _tempFloat; + _tempFloat=(float)(_packet->temp)/10.0f; + MIBLEsensors[_slot].temp=_tempFloat; + + MIBLEsensors[_slot].lux = _packet->lux; + MIBLEsensors[_slot].moisture = _packet->moist; + MIBLEsensors[_slot].fertility = _packet->fert; + MIBLEsensors[_slot].eventType.temp = 1; + MIBLEsensors[_slot].eventType.lux = 1; + MIBLEsensors[_slot].eventType.moist = 1; + MIBLEsensors[_slot].eventType.fert = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; + + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + } +} + +bool HM10readBat(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b) return false; + uint32_t _slot = HM10.state.sensor; + + + + if(_buf[0] != 0){ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_HM10,_buf[0]); + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + if(_buf[0]<101){ + MIBLEsensors[_slot].bat=_buf[0]; + MIBLEsensors[_slot].showedUp=255; + MIBLEsensors[_slot].eventType.bat = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; + return true; + } + } + return false; +} + + + + + +bool HM10SerialHandleFeedback(){ + bool success = false; + uint32_t i = 0; + + while(HM10Serial->available()) { + if(iread(); + } + i++; + success = true; + } + + if(i==0){ + if(HM10.mode.shallTriggerTele){ + HM10.mode.shallTriggerTele=0; + if(HM10.option.noRealTime){ + HM10.mode.triggeredTele=0; + return success; + } + HM10.mode.triggeredTele=1; + HM10triggerTele(); + } + return success; + } + + switch (HM10.mode.awaiting){ + case bat: + if (HM10.mode.connected) { + if (HM10readBat(HM10.rxBuffer)){ + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + } + } + break; + case tempHumLY: + if (HM10.mode.connected) HM10readHT_LY(HM10.rxBuffer); + break; + case tempHumCGD1: + if (HM10.mode.connected) HM10readHT_CGD1(HM10.rxBuffer); + break; + case TLMF: + if (HM10.mode.connected) HM10readTLMF(HM10.rxBuffer); + break; + case discScan: + if(success) { + char *_src = HM10ParseResponse(HM10.rxBuffer,i); + if(_src){ + HM10ParseResponse(_src,i-(_src-HM10.rxBuffer)); + } + } + break; + case tempHumMJ: + if (HM10.mode.connected) HM10readHT_MJ_HT_V1(HM10.rxBuffer); + break; + case none: + if(success) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)HM10.rxBuffer); + + + + HM10ParseResponse(HM10.rxBuffer,i); + } + break; + } + memset(HM10.rxBuffer,0,i); + return success; +} + + + + + +void HM10_TaskEvery100ms(){ + if (HM10.current_task_delay == 0) { + uint8_t i = 0; + bool runningTaskLoop = true; + while (runningTaskLoop) { + switch(HM10_TASK_LIST[i][0]) { + case TASK_HM10_ROLE1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set role to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+ROLE1"); + break; + case TASK_HM10_IMME1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+IMME1"); + break; + case TASK_HM10_DISC: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); + HM10.current_task_delay = 90; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = discScan; + HM10Serial->write("AT+DISA?"); + break; + case TASK_HM10_VERSION: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+VERR?"); + break; + case TASK_HM10_NAME: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NAME?"); + break; + case TASK_HM10_CONN: + char _con[20]; + sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].MAC[0],MIBLEsensors[HM10.state.sensor].MAC[1],MIBLEsensors[HM10.state.sensor].MAC[2],MIBLEsensors[HM10.state.sensor].MAC[3],MIBLEsensors[HM10.state.sensor].MAC[4],MIBLEsensors[HM10.state.sensor].MAC[5]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: %s connect %s"),D_CMND_HM10,kHM10DeviceType[MIBLEsensors[HM10.state.sensor].type-1],_con); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write(_con); + HM10.mode.awaiting = none; + HM10.mode.connected = true; + break; + case TASK_HM10_DISCONN: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT"); + break; + case TASK_HM10_RESET: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); + HM10Serial->write("AT+RESET"); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + break; + case TASK_HM10_SUB_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + HM10.current_task_delay = 25; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + HM10.mode.awaiting = tempHumLY; + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON0037"); + break; + case TASK_HM10_UN_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF0037"); + break; + case TASK_HM10_SUB_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + HM10.current_task_delay = 85; + HM10.mode.awaiting = tempHumLY; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+NOTIFY_ON003C"); + else HM10Serial->write("AT+NOTIFY_ON004B"); + break; + case TASK_HM10_UN_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+NOTIFY_OFF003C"); + else HM10Serial->write("AT+NOTIFY_OFF004B"); + break; + case TASK_HM10_TIME_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.time = Rtc.utc_time; + HM10Serial->write("AT+SEND_DATAWR002F"); + HM10Serial->write(HM10.timebuf,4); + HM10Serial->write(Rtc.time_timezone / 60); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); + break; +# 1079 "/workspace/Tasmota/tasmota/xsns_62_MI_HM10.ino" + case TASK_HM10_READ_BT_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+READDATA0043?"); + else HM10Serial->write("AT+READDATA0052?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_READ_BF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0038?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_CALL_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_READ_TLMF_FL,i); + runningTaskLoop = false; + HM10Serial->write("AT+SEND_DATAWR0033"); + HM10Serial->write(0xa0); + HM10Serial->write(0x1f); + HM10.mode.awaiting = none; + break; + case TASK_HM10_READ_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0035?"); + HM10.mode.awaiting = TLMF; + break; + case TASK_HM10_READ_B_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0011?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = tempHumCGD1; + HM10Serial->write("AT+NOTIFY_ON004b"); + break; + case TASK_HM10_UN_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF004b"); + break; + case TASK_HM10_SCAN9: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+SCAN9"); + break; + case TASK_HM10_READ_B_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0018?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); + HM10.current_task_delay = 10; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON000F"); + HM10.mode.awaiting = tempHumMJ; + break; + case TASK_HM10_FEEDBACK: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); + HM10SerialHandleFeedback(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + runningTaskLoop = false; + break; + case TASK_HM10_STATUS_EVENT: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); + HM10StatusInfo(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + runningTaskLoop = false; + break; + case TASK_HM10_DONE: + + + if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { + DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); + for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j); + HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; + HM10_TASK_LIST[j][1] = 0; + } + runningTaskLoop = false; + HM10.mode.pending_task = 0; + break; + } + } + i++; + } + } + else { + HM10.current_task_delay--; + } +} + +void HM10StatusInfo() { + + + + + + + Response_P(PSTR("{%s:{\"found\":%u}}"), D_CMND_HM10, MIBLEsensors.size()); + XdrvRulesProcess(); +} + + + + + + +void HM10EverySecond(bool restart){ + static uint32_t _counter = 0; + static uint32_t _nextSensorSlot = 0; + static uint32_t _lastDiscovery = 0; + + if(restart){ + _counter = 0; + _lastDiscovery = 0; + return; + } + + if(HM10.firmware == 0) return; + if(HM10.mode.pending_task == 1) return; + if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return; + + if((HM10.period-_counter)>15 && HM10.mode.autoScan) { + if(_counter-_lastDiscovery>HM10.autoScanInterval){ + HM10_Discovery_Scan(); + HM10.mode.pending_task = 1; + _counter+=12; + _lastDiscovery = _counter; + return; + } + } + + if(_counter==0) { + HM10.state.sensor = _nextSensorSlot; + _nextSensorSlot++; + HM10.mode.pending_task = 1; + switch(MIBLEsensors[HM10.state.sensor].type){ + case FLORA: + HM10_Read_Flora(); + break; + case MJ_HT_V1: case CGG1: + HM10_Read_MJ_HT_V1(); + break; + case LYWSD02: case MHOC303: + HM10_Read_LYWSD02(); + break; + case LYWSD03MMC: case MHOC401: + HM10_Read_LYWSD03(); + break; + case CGD1: + HM10_Read_CGD1(); + break; + default: + HM10.mode.pending_task = 0; + } + if (HM10.state.sensor==MIBLEsensors.size()-1) { + _nextSensorSlot= 0; + _counter++; + } + DEBUG_SENSOR_LOG(PSTR("%s: active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); + } + else _counter++; + if (_counter>HM10.period) { + _counter = 0; + _lastDiscovery = 0; + } +} + + + + + +bool HM10Cmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_HM10); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands); + switch (command_code) { + case CMND_HM10_PERIOD: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload==1) { + HM10EverySecond(true); + XdrvMailbox.payload = HM10.period; + } + else { + HM10.period = XdrvMailbox.payload; + } + } + else { + XdrvMailbox.payload = HM10.period; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AUTO: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload>0) { + HM10.mode.autoScan = 1; + HM10.autoScanInterval = XdrvMailbox.payload; + } + else { + HM10.mode.autoScan = 0; + HM10.autoScanInterval = 0; + } + } + else { + XdrvMailbox.payload = HM10.autoScanInterval; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_BAUD: + if (XdrvMailbox.data_len > 0) { + HM10.serialSpeed = XdrvMailbox.payload; + HM10Serial->begin(HM10.serialSpeed); + } + else { + XdrvMailbox.payload = HM10.serialSpeed; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_TIME: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02){ + HM10.state.sensor = XdrvMailbox.payload; + HM10_Time_LYWSD02(); + } + } + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_PAGE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = HM10.perPage; + HM10.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = HM10.perPage; + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AT: + HM10Serial->write("AT"); + if (strlen(XdrvMailbox.data)!=0) { + HM10Serial->write("+"); + HM10Serial->write(XdrvMailbox.data); + Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data); + } + else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); + break; + case CMND_HM10_DISC_SCAN: + HM10_Discovery_Scan(); + Response_P(S_JSON_HM10_COMMAND, command, ""); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +void HM10triggerTele(void){ + HM10.mode.triggeredTele = 1; + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + #ifdef USE_RULES + RulesTeleperiod(); + #endif + } +} + + + + + +const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}"; +const char HTTP_HM10_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; +const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}"; +const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; + +void HM10Show(bool json) +{ + if (json) { + if(!HM10.mode.triggeredTele){ + if(HM10.option.noSummary) return; + } + + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(HM10.mode.triggeredTele && MIBLEsensors[i].eventType.raw == 0) continue; + if(HM10.mode.triggeredTele && MIBLEsensors[i].shallSendMQTT==0) continue; + + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":"), + kHM10DeviceType[MIBLEsensors[i].type-1], + MIBLEsensors[i].MAC[3], MIBLEsensors[i].MAC[4], MIBLEsensors[i].MAC[5]); + + uint32_t _positionCurlyBracket = strlen(mqtt_data); + + if((!HM10.mode.triggeredTele && !HM10.option.minimalSummary)||HM10.mode.triggeredTele){ + bool tempHumSended = false; + if(MIBLEsensors[i].feature.tempHum){ + if(MIBLEsensors[i].eventType.tempHum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + tempHumSended = true; + } + } + } + if(MIBLEsensors[i].feature.temp && !tempHumSended){ + if(MIBLEsensors[i].eventType.temp || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].temp)) { + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + } + } + if(MIBLEsensors[i].feature.hum && !tempHumSended){ + if(MIBLEsensors[i].eventType.hum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].hum)) { + char hum[FLOATSZ]; + dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum); + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); + } + } + } + if (MIBLEsensors[i].feature.lux){ + if(MIBLEsensors[i].eventType.lux || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].lux!=0x0ffffff) { + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + } + } + if (MIBLEsensors[i].feature.moist){ + if(MIBLEsensors[i].eventType.moist || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].moisture!=0xff) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture); + } + } + } + if (MIBLEsensors[i].feature.fert){ + if(MIBLEsensors[i].eventType.fert || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].fertility!=0xffff) { + ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility); + } + } + } + if (MIBLEsensors[i].feature.Btn){ + if(MIBLEsensors[i].eventType.Btn){ + ResponseAppend_P(PSTR(",\"Btn\":%u"),MIBLEsensors[i].Btn); + } + } + } + if (MIBLEsensors[i].feature.PIR){ + if(MIBLEsensors[i].eventType.motion || !HM10.mode.triggeredTele){ + if(HM10.mode.triggeredTele) ResponseAppend_P(PSTR(",\"PIR\":1")); + ResponseAppend_P(PSTR(",\"Events\":%u"),MIBLEsensors[i].events); + } + else if(MIBLEsensors[i].eventType.noMotion && HM10.mode.triggeredTele){ + ResponseAppend_P(PSTR(",\"PIR\":0")); + } + } + + if (MIBLEsensors[i].type == FLORA && !HM10.mode.triggeredTele) { + if (MIBLEsensors[i].firmware[0] != '\0') { + ResponseAppend_P(PSTR(",\"Firmware\":\"%s\""), MIBLEsensors[i].firmware); + } + } + + if (MIBLEsensors[i].feature.NMT || !HM10.mode.triggeredTele){ + if(MIBLEsensors[i].eventType.NMT){ + ResponseAppend_P(PSTR(",\"NMT\":%u"), MIBLEsensors[i].NMT); + } + } + if (MIBLEsensors[i].feature.bat){ + if(MIBLEsensors[i].eventType.bat || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].bat != 0x00) { + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } + } + } + if (HM10.option.showRSSI && HM10.mode.triggeredTele) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].rssi); + + + if(_positionCurlyBracket==strlen(mqtt_data)) ResponseAppend_P(PSTR(",")); + ResponseAppend_P(PSTR("}")); + mqtt_data[_positionCurlyBracket] = '{'; + MIBLEsensors[i].eventType.raw = 0; + if(MIBLEsensors[i].shallSendMQTT==1){ + MIBLEsensors[i].shallSendMQTT = 0; + continue; + } + } + HM10.mode.triggeredTele = 0; + +#ifdef USE_WEBSERVER + } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + int32_t i = _page * HM10.perPage; + uint32_t j = i + HM10.perPage; + if (j+1>MIBLEsensors.size()){ + j = MIBLEsensors.size(); + } + char stemp[5] ={0}; + if (MIBLEsensors.size()-(_page*HM10.perPage)>1 && HM10.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MIBLEsensors.size()==0) i=-1; + + WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); + for (i; iFLORA){ + if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ + WSContentSend_THD(kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } + if(MIBLEsensors[i].bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } + WSContentSend_PD(HTTP_RSSI, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].rssi); + } + _counter++; + if(_counter>3) { + _page++; + _counter=0; + } + if(MIBLEsensors.size()%HM10.perPage==0 && _page==MIBLEsensors.size()/HM10.perPage) _page=0; + if(_page>MIBLEsensors.size()/HM10.perPage) _page=0; +#endif + } +} + + + + + +bool Xsns62(uint8_t function) +{ + bool result = false; + + if (PinUsed(GPIO_HM10_RX) && PinUsed(GPIO_HM10_TX)) { + switch (function) { + case FUNC_INIT: + HM10SerialInit(); + break; + case FUNC_EVERY_50_MSECOND: + HM10SerialHandleFeedback(); + break; + case FUNC_EVERY_100_MSECOND: + if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) { + HM10_TaskEvery100ms(); + } + break; + case FUNC_EVERY_SECOND: + HM10EverySecond(false); + break; + case FUNC_COMMAND: + result = HM10Cmd(); + break; + case FUNC_JSON_APPEND: + HM10Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HM10Show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_63_aht1x.ino" +# 21 "/workspace/Tasmota/tasmota/xsns_63_aht1x.ino" +#ifdef USE_I2C +#ifdef USE_AHT1x +# 45 "/workspace/Tasmota/tasmota/xsns_63_aht1x.ino" +#define XSNS_63 63 +#define XI2C_43 43 + +#define AHT1X_ADDR1 0x38 +#define AHT1X_ADDR2 0x39 + +#define AHT1X_MAX_SENSORS 2 + +#define AHT_HUMIDITY_CONST 100 +#define AHT_TEMPERATURE_CONST 200 +#define AHT_TEMPERATURE_OFFSET 50 +#define KILOBYTE_CONST 1048576.0f + +#define AHT1X_CMD_DELAY 40 +#define AHT1X_RST_DELAY 30 +#define AHT1X_MEAS_DELAY 80 + +#ifdef USE_AHT2x + #define AHTX_CMD 0xB1 + const char ahtTypes[] PROGMEM = "AHT2X|AHT2X"; +#else + #define AHTX_CMD 0xE1 + const char ahtTypes[] PROGMEM = "AHT1X|AHT1X"; +#endif + +uint8_t AHTSetCalCmd[3] = { AHTX_CMD, 0x08, 0x00 }; +uint8_t AHTSetCycleCmd[3] = { AHTX_CMD, 0x28, 0x00 }; +uint8_t AHTMeasureCmd[3] = { 0xAC, 0x33, 0x00 }; +uint8_t AHTResetCmd = 0xBA; + +struct { + bool write_ok = false; + uint8_t addresses[2] = { AHT1X_ADDR1, AHT1X_ADDR2 }; + uint8_t count = 0; + uint8_t Pcount = 0; +} aht1x; + +struct { + float humidity = NAN; + float temperature = NAN; + uint8_t address; + char types[6]; +} aht1x_sensors[AHT1X_MAX_SENSORS]; + +bool AHT1XWrite(uint8_t aht1x_idx) { + Wire.beginTransmission(aht1x_sensors[aht1x_idx].address); + Wire.write(AHTMeasureCmd, 3); + if (Wire.endTransmission() != 0) + return false; + return true; +} + +bool AHT1XRead(uint8_t aht1x_idx) { + uint8_t data[6]; + Wire.requestFrom(aht1x_sensors[aht1x_idx].address, (uint8_t) 6); + + for(uint8_t i = 0; Wire.available() > 0; i++) { + data[i] = Wire.read(); + } + if (data[0] & 0x80) + return false; + + aht1x_sensors[aht1x_idx].humidity = (((data[1] << 12)| (data[2] << 4) | data[3] >> 4) * AHT_HUMIDITY_CONST / KILOBYTE_CONST); + aht1x_sensors[aht1x_idx].temperature = ((AHT_TEMPERATURE_CONST * (((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]) / KILOBYTE_CONST) - AHT_TEMPERATURE_OFFSET); + + return (!isnan(aht1x_sensors[aht1x_idx].temperature) && !isnan(aht1x_sensors[aht1x_idx].humidity) && (aht1x_sensors[aht1x_idx].humidity != 0)); +} + + + + + +void AHT1XPoll(void) { + aht1x.Pcount++; + switch (aht1x.Pcount) { + case 10: + aht1x.write_ok = AHT1XWrite(0); + break; + case 11: + if (aht1x.write_ok) AHT1XRead(0); + aht1x.Pcount = 0; + break; + } +} + +unsigned char AHT1XReadStatus(uint8_t aht1x_address) { + uint8_t result = 0; + + + + + Wire.requestFrom(aht1x_address, (uint8_t) 1); + result = Wire.read(); + return result; +} + +void AHT1XReset(uint8_t aht1x_address) { + Wire.beginTransmission(aht1x_address); + Wire.write(AHTResetCmd); + Wire.endTransmission(); + delay(AHT1X_RST_DELAY); +} + + +bool AHT1XInit(uint8_t aht1x_address) { + Wire.beginTransmission(aht1x_address); + Wire.write(AHTSetCalCmd, 3); + if (Wire.endTransmission() != 0) + return false; + delay(AHT1X_CMD_DELAY); + if(AHT1XReadStatus(aht1x_address) & 0x08) + return true; + return false; +} + +void AHT1XDetect(void) { + for (uint8_t i = 0; i < AHT1X_MAX_SENSORS; i++) { + if (!I2cSetDevice(aht1x.addresses[i])) {continue;} + if (AHT1XInit(aht1x.addresses[i])) { + aht1x_sensors[aht1x.count].address = aht1x.addresses[i]; + GetTextIndexed(aht1x_sensors[aht1x.count].types, sizeof(aht1x_sensors[aht1x.count].types), i, ahtTypes); + I2cSetActiveFound(aht1x_sensors[aht1x.count].address, aht1x_sensors[aht1x.count].types); + aht1x.count++; + break; + } + } +} + +void AHT1XShow(bool json) { + for (uint32_t i = 0; i < aht1x.count; i++) { + float tem = ConvertTemp(aht1x_sensors[i].temperature); + float hum = ConvertHumidity(aht1x_sensors[i].humidity); + char types[11]; + snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), aht1x_sensors[i].types, IndexSeparator(), aht1x_sensors[i].address); + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, tem, hum); + } +} + + + + + +bool Xsns63(uint8_t function) +{ + if (!I2cEnabled(XI2C_43)) { return false; } + bool result = false; + + if (FUNC_INIT == function) { + AHT1XDetect(); + } + else if (aht1x.count) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + AHT1XPoll(); + break; + case FUNC_JSON_APPEND: + AHT1XShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AHT1XShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_64_hrxl.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_64_hrxl.ino" +#ifdef USE_HRXL + + + + + + + +#define XSNS_64 64 + +#include + +#define HRXL_READ_TIMEOUT 400 + +TasmotaSerial *HRXLSerial = nullptr; + +uint32_t hrxl_distance_mm = 0; +bool hrxl_found = false; + + + +void HRXLInit(void) +{ + hrxl_found = false; + if (PinUsed(GPIO_HRXL_RX)) + { + HRXLSerial = new TasmotaSerial(Pin(GPIO_HRXL_RX), -1, 1); + if (HRXLSerial->begin(9600)) + { + if (HRXLSerial->hardwareSerial()) + ClaimSerial(); + hrxl_found = true; + HRXLSerial->setTimeout(HRXL_READ_TIMEOUT); + } + } +} + +void HRXLEverySecond(void) +{ + if (!hrxl_found) + return; + + int num_read=0; + int sum=0; + while (HRXLSerial->available()>5) + { + if (HRXLSerial->read() != 'R') + continue; + + int d = HRXLSerial->parseInt(); + if (d >= 30 && d<=5000) + { + sum += d; + num_read++; + } + } + if (num_read>1) + hrxl_distance_mm = int(sum / num_read); + +} + + +void HRXLShow(bool json) +{ + char types[5] = "HRXL"; + if (json) + { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_DISTANCE "\":%d}"), types, hrxl_distance_mm); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_RANGE, types, hrxl_distance_mm); +#endif + } +} + + + + + +bool Xsns64(uint8_t function) +{ + if (!PinUsed(GPIO_HRXL_RX)) { return false; } + + switch (function) + { + case FUNC_INIT: + HRXLInit(); + break; + case FUNC_EVERY_SECOND: + HRXLEverySecond(); + break; + case FUNC_JSON_APPEND: + HRXLShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HRXLShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +#ifdef USE_I2C +#ifdef USE_HDC1080 +# 31 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +#define XSNS_65 65 +#define XI2C_45 45 + +#define HDC1080_ADDR 0x40 + + + +#define HDC_REG_TEMP 0x00 +#define HDC_REG_RH 0x01 +#define HDC_REG_CONFIG 0x02 +#define HDC_REG_SERIAL1 0xFB +#define HDC_REG_SERIAL2 0xFC +#define HDC_REG_SERIAL3 0xFD +#define HDC_REG_MAN_ID 0xFE +#define HDC_REG_DEV_ID 0xFF + + + +#define HDC1080_MAN_ID 0x5449 +#define HDC1080_DEV_ID 0x1050 + + + +#define HDC1080_RST_ON 0x8000 +#define HDC1080_HEAT_ON 0x2000 +#define HDC1080_MODE_ON 0x1000 +#define HDC1080_TRES_11 0x400 +#define HDC1080_HRES_11 0x100 +#define HDC1080_HRES_8 0x80 + + + +#define HDC1080_CONV_TIME 15 +#define HDC1080_TEMP_MULT 0.0025177 +#define HDC1080_RH_MULT 0.0015258 +#define HDC1080_TEMP_OFFSET 40.0 + +const char* hdc_type_name = "HDC1080"; +uint16_t hdc_manufacturer_id = 0; +uint16_t hdc_device_id = 0; + +float hdc_temperature = 0.0; +float hdc_humidity = 0.0; + +uint8_t hdc_valid = 0; + +bool is_reading = false; +uint32_t hdc_next_read; + + + + + +uint16_t HdcReadDeviceId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_DEV_ID); +} + + + + + +uint16_t HdcReadManufacturerId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_MAN_ID); +} + + + + +void HdcConfig(uint16_t config) { + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, config); +} + + + + + + + +void HdcReset(void) { + uint16_t current = I2cRead16(HDC1080_ADDR, HDC_REG_CONFIG); + + + + + current |= 0x8000; + + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, current); + + delay(HDC1080_CONV_TIME); +} +# 132 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg) { + Wire.beginTransmission((uint8_t) addr); + Wire.write((uint8_t) reg); + return Wire.endTransmission(); +} +# 147 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len) { + if (len != Wire.requestFrom((uint8_t) addr, (uint8_t) len)) { + return 1; + } + + while (len--) { + *reg_data = (uint8_t)Wire.read(); + reg_data++; + } + + return 0; +} + + + + + +void HdcInit(void) { + HdcReset(); + HdcConfig(HDC1080_MODE_ON); +} + + + + + +bool HdcTriggerRead(void) { + int8_t status = HdcTransactionOpen(HDC1080_ADDR, HDC_REG_TEMP); + + hdc_next_read = millis() + HDC1080_CONV_TIME; + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + is_reading = true; + + return true; +} +# 197 "/workspace/Tasmota/tasmota/xsns_65_hdc1080.ino" +bool HdcRead(void) { + int8_t status = 0; + uint8_t sensor_data[4]; + uint16_t temp_data = 0; + uint16_t rh_data = 0; + + is_reading = false; + + status = HdcTransactionClose(HDC1080_ADDR, sensor_data, 4); + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + temp_data = (uint16_t) ((sensor_data[0] << 8) | sensor_data[1]); + rh_data = (uint16_t) ((sensor_data[2] << 8) | sensor_data[3]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); + + + + hdc_temperature = ConvertTemp(HDC1080_TEMP_MULT * (float) (temp_data) - HDC1080_TEMP_OFFSET); + + hdc_humidity = HDC1080_RH_MULT * (float) (rh_data); + + if (hdc_humidity > 100) { hdc_humidity = 100.0; } + if (hdc_humidity < 0) { hdc_humidity = 0.01; } + hdc_humidity = ConvertHumidity(hdc_humidity); + + hdc_valid = SENSOR_MAX_MISS; + + return true; +} + + + + + + +void HdcDetect(void) { + if (I2cActive(HDC1080_ADDR)) { + + + return; + } + + hdc_manufacturer_id = HdcReadManufacturerId(); + hdc_device_id = HdcReadDeviceId(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); + + if (hdc_device_id == HDC1080_DEV_ID) { + HdcInit(); + I2cSetActiveFound(HDC1080_ADDR, hdc_type_name); + } +} + + + + + + +void HdcEverySecond(void) { + if (uptime &1) { + if (!HdcTriggerRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } +} + + + + + + +void HdcShow(bool json) { + if (hdc_valid) { + TempHumDewShow(json, (0 == tele_period), hdc_type_name, hdc_temperature, hdc_humidity); + } +} + + + + + +bool Xsns65(uint8_t function) +{ + if (!I2cEnabled(XI2C_45)) { + + + return false; + } + + bool result = false; + + if (FUNC_INIT == function) { + HdcDetect(); + } + else if (hdc_device_id) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if(is_reading && TimeReached(hdc_next_read)) { + if(!HdcRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } + break; + case FUNC_EVERY_SECOND: + HdcEverySecond(); + break; + case FUNC_JSON_APPEND: + HdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HdcShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_66_iAQ.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_66_iAQ.ino" +#ifdef USE_I2C +#ifdef USE_IAQ + +#define XSNS_66 66 +#define XI2C_46 46 + +#define I2_ADR_IAQ 0x5a + +#define IAQ_STATUS_OK 0x00 +#define IAQ_STATUS_BUSY 0x01 +#define IAQ_STATUS_WARM 0x10 +#define IAQ_STATUS_ERR 0x80 +#define IAQ_STATUS_I2C_ERR 0xFF + +struct { + int32_t resistance; + uint16_t pred; + uint16_t Tvoc; + uint8_t status; + bool ready; +} iAQ; + +void IAQ_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IAQ)) { return; } + I2cSetActiveFound(I2_ADR_IAQ, "IAQ"); + iAQ.ready = true; +} + +void IAQ_Read(void) +{ + uint8_t buf[9]; + buf[2] = IAQ_STATUS_I2C_ERR; + Wire.requestFrom((uint8_t)I2_ADR_IAQ,sizeof(buf)); + for( uint32_t i=0; i<9; i++ ) { + buf[i]= Wire.read(); + } + + iAQ.pred = (buf[0]<<8) + buf[1]; + iAQ.status = buf[2]; + iAQ.resistance = ((uint32_t)buf[3]<<24) + ((uint32_t)buf[4]<<16) + ((uint32_t)buf[5]<<8) + (uint32_t)buf[6]; + iAQ.Tvoc = (buf[7]<<8) + buf[8]; +} + + + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_IAQ[] PROGMEM = + "{s}iAQ-Core " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}iAQ-Core " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +const char HTTP_SNS_IAQ_ERROR[] PROGMEM = + "{s}iAQ-Core {m} %s {e}"; +#endif + +void IAQ_Show(uint8_t json) +{ + IAQ_Read(); + + if (json) { + if (iAQ.status!=IAQ_STATUS_OK){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); + return; + } + else { + ResponseAppend_P(PSTR(",\"IAQ\":{\"" D_JSON_ECO2 "\":%u,\"" D_JSON_TVOC "\":%u,\"" D_JSON_RESISTANCE "\":%u}"), iAQ.pred, iAQ.Tvoc, iAQ.resistance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, iAQ.pred); +#endif + } +#ifdef USE_WEBSERVER + } else { + switch(iAQ.status){ + case IAQ_STATUS_OK: + WSContentSend_PD(HTTP_SNS_IAQ, iAQ.pred, iAQ.Tvoc); + break; + case IAQ_STATUS_WARM: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_START); + break; + default: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_ERROR); + } +#endif + } +} + + + + + +bool Xsns66(byte function) +{ + if (!I2cEnabled(XI2C_46)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + IAQ_Init(); + } + else if (iAQ.ready) { + switch (function) { + case FUNC_JSON_APPEND: + IAQ_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + IAQ_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_67_as3935.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_67_as3935.ino" +#ifdef USE_I2C +#ifdef USE_AS3935 + + + + + + +#define XSNS_67 67 +#define XI2C_48 48 + +#define D_NAME_AS3935 "AS3935" +#define AS3935_ADDR 0x03 + + +#define PWR_REG 0x00, 0x01, 0 +#define AFE_GB 0x00, 0x3E, 0 +#define WDTH 0x01, 0x0F, 0 +#define NF_LEVEL 0x01, 0x70, 4 +#define SPIKE_REJECT 0x02, 0x0F, 0 +#define MIN_NUM_LIGH 0x02, 0x30, 4 +#define DISTURBER 0x03, 0x20, 5 +#define LCO_FDIV 0x03, 0xC0, 6 +#define IRQ_TBL 0x03, 0x0F, 0 +#define ENERGY_RAW_1 0x04, 0xFF, 0 +#define ENERGY_RAW_2 0x05, 0xFF, 0 +#define ENERGY_RAW_3 0x06, 0x1F, 0 +#define LGHT_DIST 0x07, 0x3F, 0 +#define DISP_TRCO 0x08, 0x20, 5 +#define DISP_SRCO 0x08, 0x40, 6 +#define DISP_LCO 0x08, 0x80, 7 +#define TUNE_CAPS 0x08, 0x0F, 0 +#define CAL_TRCO_NOK 0x3A, 0x40, 6 +#define CAL_TRCO_DONE 0x3A, 0x80, 7 +#define CAL_SRCO_NOK 0x3B, 0x40, 6 +#define CAL_SRCO_DONE 0x3B, 0x80, 7 + + +#define RESET_DEFAULT 0x3C, 0x96 +#define CALIBATE_RCO 0x3D, 0x96 + + +#define INDOORS 0x24 +#define OUTDOORS 0x1C + + +#define SETREG00MASK 0x3E +#define SETREG03MASK 0xF0 + + +const char HTTP_SNS_AS3935_ENERGY[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_ENERGY " {m}%d{e}"; +const char HTTP_SNS_AS3935_DISTANZ[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_DISTANCE " {m}%u " D_UNIT_KILOMETER "{e}"; +const char HTTP_SNS_AS3935_VRMS[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_VRMS "{m}%#4u (%d){e}"; + +const char HTTP_SNS_AS3935_OUTDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_OUTDOORS " {e}"; +const char HTTP_SNS_AS3935_INDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_INDOORS " {e}"; +const char* const HTTP_SNS_AS3935_GAIN[] PROGMEM = {HTTP_SNS_AS3935_INDOORS, HTTP_SNS_AS3935_OUTDOORS}; + +const char HTTP_SNS_AS3935_DIST_ON[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_ON " {e}"; +const char HTTP_SNS_AS3935_DIST_OFF[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_OFF " {e}"; +const char* const HTTP_SNS_AS3935_DISTURBER[] PROGMEM = {HTTP_SNS_AS3935_DIST_OFF, HTTP_SNS_AS3935_DIST_ON}; + +const char HTTP_SNS_AS3935_EMPTY[] PROGMEM = "{s}%s " D_AS3935_NOMESS "{e}"; +const char HTTP_SNS_AS3935_OUT[] PROGMEM = "{s}%s " D_AS3935_OUT "{e}"; +const char HTTP_SNS_AS3935_NOT[] PROGMEM = "{s}%s " D_AS3935_NOT "{e}"; +const char HTTP_SNS_AS3935_ABOVE[] PROGMEM = "{s}%s " D_AS3935_ABOVE "{e}"; +const char HTTP_SNS_AS3935_NOISE[] PROGMEM = "{s}%s " D_AS3935_NOISE "{e}"; +const char HTTP_SNS_AS3935_DISTURB[] PROGMEM = "{s}%s " D_AS3935_DISTDET "{e}"; +const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s " D_AS3935_INTNOEV "{e}"; +const char HTTP_SNS_AS3935_FLICKER[] PROGMEM = "{s}%s " D_AS3935_FLICKER "{e}"; +const char HTTP_SNS_AS3935_POWEROFF[] PROGMEM = "{s}%s " D_AS3935_POWEROFF "{e}"; + +const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}"; +const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV, HTTP_SNS_AS3935_FLICKER, HTTP_SNS_AS3935_POWEROFF }; + +const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u,\"" D_JSON_STAGE "\":%d}"; + +const char* const S_JSON_AS3935_COMMAND_ONOFF[] PROGMEM = {"\"" D_AS3935_OFF "\"","\"" D_AS3935_ON"\""}; +const char* const S_JSON_AS3935_COMMAND_GAIN[] PROGMEM = {"\"" D_AS3935_INDOORS "\"", "\"" D_AS3935_OUTDOORS "\""}; +const char* const S_JSON_AS3935_COMMAND_CAL[] PROGMEM = {"" D_AS3935_CAL_FAIL "","" D_AS3935_CAL_OK ""}; + +const char S_JSON_AS3935_COMMAND_STRING[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%s}}"; +const char S_JSON_AS3935_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%d}}"; +const char S_JSON_AS3935_COMMAND_SETTINGS[] PROGMEM = "{\"AS3935_Settings\":{\"Gain\":%s,\"NFfloor\":%d,\"uVrms\":%d,\"Tunecaps\":%d,\"MinNumLight\":%d,\"Rejektion\":%d,\"Wdthreshold\":%d,\"MinNFstage\":%d,\"NFAutoTime\":%d,\"DisturberAutoTime\":%d,\"Disturber\":%s,\"NFauto\":%s,\"Disturberauto\":%s,\"NFautomax\":%s,\"Mqttlightevent\":%s,\"Mqttnoirqevent\":%s}}"; + +const char kAS3935_Commands[] PROGMEM = "power|setnf|setminstage|setml|default|setgain|settunecaps|setrej|setwdth|disttime|nftime|disturber|autonf|autodisturber|autonfmax|lightevent|noirqevent|settings|calibrate"; + +const uint8_t AS3935_VrmsIndoor[] PROGMEM = { 28, 45, 62, 78, 95, 112, 130, 146 }; +const uint16_t AS3935_VrmsOutdoor[] PROGMEM = { 390, 630, 860, 1100, 1140, 1570, 1800, 2000 }; + +enum AS3935_Commands { + CMND_AS3935_POWER, + CMND_AS3935_SET_NF, + CMND_AS3935_SET_MINNF, + CMND_AS3935_SET_MINLIGHT, + CMND_AS3935_SET_DEF, + CMND_AS3935_SET_GAIN, + CMND_AS3935_SET_TUNE, + CMND_AS3935_SET_REJ, + CMND_AS3935_SET_WDTH, + CMND_AS3935_DISTTIME, + CMND_AS3935_NFTIME, + CMND_AS3935_SET_DISTURBER, + CMND_AS3935_NF_AUTOTUNE, + CMND_AS3935_DIST_AUTOTUNE, + CMND_AS3935_NF_ATUNE_BOTH, + CMND_AS3935_MQTT_LIGHT_EVT, + CMND_AS3935_MQTT_NO_IRQ_EVT, + CMND_AS3935_SETTINGS, + CMND_AS3935_CALIBRATE +}; + +struct { + bool active = false; + bool http_count_start = false; + bool poweroff = false; + volatile bool detected = false; + volatile bool dispLCO = false; + volatile uint8_t icount = 0; + uint8_t irq = 0; + uint8_t mqtt_event = 0; + uint8_t http_event = 0; + uint8_t http_time = 0; + uint8_t http_count = 0; + int16_t http_distance = 0; + int16_t distance = 0; + uint16_t nftimer = 0; + uint16_t disttimer = 0; + uint32_t intensity = 0; + uint32_t http_intensity = 0; + volatile uint32_t pulse = 0; +} as3935_sensor; + +void ICACHE_RAM_ATTR AS3935Isr(void) { + as3935_sensor.detected = true; + as3935_sensor.icount++; +} + + +uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift) { + uint8_t data = I2cRead8(AS3935_ADDR, reg); + if (reg == 0x08) Settings.as3935_sensor_cfg[4] = data; + if (reg <= 0x03) Settings.as3935_sensor_cfg[reg] = data; + return ((data & mask) >> shift); +} + +void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data) { + uint8_t currentReg = I2cRead8(AS3935_ADDR, reg); + currentReg &= (~mask); + data <<= shift; + data &= mask; + data |= currentReg; + I2cWrite8(AS3935_ADDR, reg, data); + if (reg == 0x08) Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, reg); + if (reg <= 0x03) Settings.as3935_sensor_cfg[reg] = I2cRead8(AS3935_ADDR, reg); +} + + + +void ICACHE_RAM_ATTR AS3935CountFreq(void) { + if (as3935_sensor.dispLCO) + as3935_sensor.pulse++; +} + +bool AS3935AutoTuneCaps(uint8_t irqpin) { + int32_t maxtune = 17500; + uint8_t besttune; + uint8_t oldvalue = AS3935GetTuneCaps(); + AS3935WriteRegister(LCO_FDIV, 0); + delay(2); + for (uint8_t tune = 0; tune < 16; tune++) { + AS3935WriteRegister(TUNE_CAPS, tune); + delay(2); + AS3935WriteRegister(DISP_LCO, 1); + delay(1); + as3935_sensor.dispLCO = true; + as3935_sensor.pulse = 0; + attachInterrupt(digitalPinToInterrupt(irqpin), AS3935CountFreq, RISING); + delay(50); + as3935_sensor.dispLCO = false; + detachInterrupt(irqpin); + AS3935WriteRegister(DISP_LCO, 0); + int32_t currentfreq = 500000 - ((as3935_sensor.pulse * 20) * 16); + if(currentfreq < 0) currentfreq = -currentfreq; + if(maxtune > currentfreq) { + maxtune = currentfreq; + besttune = tune; + } + } + if (maxtune >= 17500) { + AS3935SetTuneCaps(oldvalue); + return false; + } + AS3935SetTuneCaps(besttune); + return true; +} + + + +bool AS3935CalRCOResult(void) { + if(AS3935ReadRegister(CAL_SRCO_NOK) || AS3935ReadRegister(CAL_TRCO_NOK)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: AS3935 Fatal Failure of TRCO or SRCO calibration")); + return false; + } + return true; +} + +bool AS3935CalibrateRCO(void) { + detachInterrupt(Pin(GPIO_AS3935)); + I2cWrite8(AS3935_ADDR, CALIBATE_RCO); + AS3935WriteRegister(DISP_TRCO, 1); + delay(2); + AS3935WriteRegister(DISP_TRCO, 0); + if(!AS3935CalRCOResult()) + return false; + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING); + return true; +} + +void AS3935Reset(void) { + I2cWrite8(AS3935_ADDR, RESET_DEFAULT); + delay(2); +} + +void AS3935PwrDown(void) { + AS3935WriteRegister(PWR_REG ,1); + detachInterrupt(Pin(GPIO_AS3935)); + as3935_sensor.poweroff = true; + as3935_sensor.mqtt_event = 9; + as3935_sensor.http_event = 9; + as3935_sensor.intensity = 0; + as3935_sensor.distance = 0; +} + +void AS3935PwrUp(void) { + AS3935WriteRegister(PWR_REG ,0); + AS3935CalibrateRCO(); + as3935_sensor.poweroff = false; + as3935_sensor.mqtt_event = 0; + as3935_sensor.http_event = 0; +} + +uint8_t AS3935GetPwrStat(void) { + if (AS3935ReadRegister(PWR_REG)) + return 0; + return 1; +} + +uint8_t AS3935GetIRQ(void) { + delay(2); + return AS3935ReadRegister(IRQ_TBL); +} + +uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance) { + switch(irq) { + case 0: return 7; + case 1: return 5; + case 4: return 6; + case 8: + if (distance == -1) return 2; + else if (distance == 0) return 3; + else if (distance == 1) return 4; + else return 1; + } + return 0; +} + +uint8_t AS3935GetDistance(void) { + return AS3935ReadRegister(LGHT_DIST); +} + +int16_t AS3935CalcDistance(void) { + uint8_t dist = AS3935GetDistance(); + switch (dist) { + case 0x3F: return -1; + case 0x00: return 0; + case 0x01: return 1; + default: + if (40 < dist) return 40; + return dist; + } +} + +uint32_t AS3935GetIntensity(void) { + uint32_t energy_raw = (AS3935ReadRegister(ENERGY_RAW_3) << 8); + energy_raw |= AS3935ReadRegister(ENERGY_RAW_2); + energy_raw <<= 8; + energy_raw |= AS3935ReadRegister(ENERGY_RAW_1); + return energy_raw; +} + +uint8_t AS3935GetTuneCaps(void) { + return AS3935ReadRegister(TUNE_CAPS); +} + +void AS3935SetTuneCaps(uint8_t tune) { + AS3935WriteRegister(TUNE_CAPS, tune); + delay(2); + AS3935CalibrateRCO(); +} + +uint8_t AS3935GetDisturber(void) { + return AS3935ReadRegister(DISTURBER); +} + +void AS3935SetDisturber(uint8_t stat) { + AS3935WriteRegister(DISTURBER, stat); +} + +uint8_t AS3935GetMinLights(void) { + return AS3935ReadRegister(MIN_NUM_LIGH); +} + +void AS3935SetMinLights(uint8_t stat) { + AS3935WriteRegister(MIN_NUM_LIGH, stat); +} + +uint8_t AS3935TransMinLights(uint8_t min_lights) { + if (5 > min_lights) return 0; + else if (9 > min_lights) return 1; + else if (16 > min_lights) return 2; + else return 3; +} + +uint8_t AS3935TranslMinLightsInt(uint8_t min_lights) { + switch (min_lights) { + case 0: return 1; + case 1: return 5; + case 2: return 9; + case 3: return 16; + } + return 0; +} + +uint8_t AS3935GetNoiseFloor(void) { + return AS3935ReadRegister(NF_LEVEL); +} + +void AS3935SetNoiseFloor(uint8_t noise) { + AS3935WriteRegister(NF_LEVEL , noise); +} + +uint8_t AS3935GetGain(void) { + if (AS3935ReadRegister(AFE_GB) == OUTDOORS) + return OUTDOORS; + return INDOORS; +} + +void AS3935SetGain(uint8_t room) { + AS3935WriteRegister(AFE_GB, room); +} + +uint8_t AS3935GetGainInt(void) { + if (AS3935ReadRegister(AFE_GB) == OUTDOORS) + return 1; + return 0; +} + +void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage) { + uint8_t room = AS3935GetGain(); + uint8_t nflev = AS3935GetNoiseFloor(); + if (room == INDOORS) { + vrms = pgm_read_byte(AS3935_VrmsIndoor + nflev); + stage = nflev; + } else { + vrms = pgm_read_word(AS3935_VrmsOutdoor + nflev); + stage = nflev + 8; + } +} + +uint8_t AS3935GetSpikeRejection(void) { + return AS3935ReadRegister(SPIKE_REJECT); +} + +void AS3935SetSpikeRejection(uint8_t rej) { + AS3935WriteRegister(SPIKE_REJECT, rej); +} + +uint8_t AS3935GetWdth(void) { + return AS3935ReadRegister(WDTH); +} + +void AS3935SetWdth(uint8_t wdth) { + AS3935WriteRegister(WDTH, wdth); +} + +bool AS3935AutoTune(void) { + detachInterrupt(Pin(GPIO_AS3935)); + bool result = AS3935AutoTuneCaps(Pin(GPIO_AS3935)); + return result; +} + + + +bool AS3935LowerNoiseFloor(void) { + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + if (Settings.as3935_functions.nf_autotune_both) { + if (stage == 8 && stage > Settings.as3935_parameter.nf_autotune_min) { + AS3935SetGain(INDOORS); + AS3935SetNoiseFloor(7); + return true; + } + } + uint8_t noise = AS3935GetNoiseFloor(); + if (0 < noise && stage > Settings.as3935_parameter.nf_autotune_min) { + noise--; + AS3935SetNoiseFloor(noise); + return true; + } + return false; +} + +bool AS3935RaiseNoiseFloor(void) { + uint8_t noise = AS3935GetNoiseFloor(); + uint8_t room = AS3935GetGain(); + if (Settings.as3935_functions.nf_autotune_both) { + if (7 == noise && room == INDOORS) { + AS3935SetGain(OUTDOORS); + AS3935SetNoiseFloor(0); + return true; + } + } + if (7 > noise) { + noise++; + AS3935SetNoiseFloor(noise); + return true; + } + return false; +} + + + +bool AS3935SetDefault(void) { + AS3935Reset(); + AS3935SetDisturber(1); + AS3935SetNoiseFloor(7); + Settings.as3935_sensor_cfg[0] = I2cRead8(AS3935_ADDR, 0x00); + Settings.as3935_sensor_cfg[1] = I2cRead8(AS3935_ADDR, 0x01); + Settings.as3935_sensor_cfg[2] = I2cRead8(AS3935_ADDR, 0x02); + Settings.as3935_sensor_cfg[3] = I2cRead8(AS3935_ADDR, 0x03); + Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, 0x08); + + Settings.as3935_functions.data = 0x00; + Settings.as3935_parameter.nf_autotune_min = 0x00; + Settings.as3935_parameter.nf_autotune_time = 4; + Settings.as3935_parameter.dist_autotune_time = 1; + return true; +} + +void AS3935InitSettings(void) { + if(Settings.as3935_functions.nf_autotune) { + if(Settings.as3935_parameter.nf_autotune_min) { + if (Settings.as3935_parameter.nf_autotune_min > 7) { + AS3935SetGain(OUTDOORS); + AS3935SetNoiseFloor(Settings.as3935_parameter.nf_autotune_min - 8); + } else { + AS3935SetGain(INDOORS); + AS3935SetNoiseFloor(Settings.as3935_parameter.nf_autotune_min); + } + } + } + I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0] & SETREG00MASK); + I2cWrite8(AS3935_ADDR, 0x01, Settings.as3935_sensor_cfg[1]); + I2cWrite8(AS3935_ADDR, 0x02, Settings.as3935_sensor_cfg[2]); + I2cWrite8(AS3935_ADDR, 0x03, Settings.as3935_sensor_cfg[3] & SETREG03MASK); + I2cWrite8(AS3935_ADDR, 0x08, Settings.as3935_sensor_cfg[4]); + delay(2); +} + +bool AS3935Setup(void) { + if (Settings.as3935_sensor_cfg[0] == 0x00) { + AS3935SetDefault(); + } else { + AS3935InitSettings(); + } + return AS3935CalibrateRCO(); +} + +bool AS3935init(void) { + AS3935Reset(); + uint8_t afe_gb = I2cRead8(AS3935_ADDR, 0x00) & SETREG00MASK; + if(INDOORS == afe_gb) + return true; + return false; +} + +void AS3935Detect(void) { + if (!I2cSetDevice(AS3935_ADDR)) return; + if (AS3935init()) { + I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935); + if (PinUsed(GPIO_AS3935)) { + pinMode(Pin(GPIO_AS3935), INPUT); + if (!AS3935Setup()) return; + as3935_sensor.active = true; + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: AS3935 GPIO Pin not defined!")); + } + } +} + +void AS3935EverySecond(void) { + if (!as3935_sensor.poweroff) { + if (as3935_sensor.detected) { + as3935_sensor.detected = false; + as3935_sensor.irq = AS3935GetIRQ(); + + if (10 > as3935_sensor.icount) { + switch (as3935_sensor.irq) { + case 1: + if (Settings.as3935_functions.nf_autotune) { + if (AS3935RaiseNoiseFloor()) + as3935_sensor.nftimer = 0; + } + break; + case 4: + if (Settings.as3935_functions.dist_autotune) { + AS3935SetDisturber(1); + } + break; + case 8: + as3935_sensor.intensity = AS3935GetIntensity(); + as3935_sensor.distance = AS3935CalcDistance(); + as3935_sensor.http_intensity = as3935_sensor.intensity; + as3935_sensor.http_distance = as3935_sensor.distance; + break; + } + + as3935_sensor.http_event = AS3935TranslIrq(as3935_sensor.irq, as3935_sensor.distance); + } else { + as3935_sensor.http_event = 8; + } + + + as3935_sensor.mqtt_event = as3935_sensor.http_event; + + switch (as3935_sensor.mqtt_event) { + case 5: + case 6: + if (!Settings.as3935_functions.mqtt_only_Light_Event) { + MqttPublishSensor(); + as3935_sensor.http_time = 10; + } + break; + case 7: + if (!Settings.as3935_functions.suppress_irq_no_Event) { + MqttPublishSensor(); + as3935_sensor.http_time = 10; + } + break; + default: + as3935_sensor.http_time = 30; + MqttPublishSensor(); + } + + as3935_sensor.irq = 0; + + as3935_sensor.intensity = 0; + as3935_sensor.distance = 0; + as3935_sensor.mqtt_event = 0; + + as3935_sensor.http_count_start = true; + as3935_sensor.http_count = 0; + } + as3935_sensor.icount = 0; + + + if (as3935_sensor.http_count_start) + as3935_sensor.http_count++; + + if (as3935_sensor.http_count > as3935_sensor.http_time) { + as3935_sensor.http_count_start = false; + as3935_sensor.http_intensity = 0; + as3935_sensor.http_distance = 0; + as3935_sensor.http_event = 0; + } + + if (Settings.as3935_functions.nf_autotune) { + as3935_sensor.nftimer++; + if (as3935_sensor.nftimer > Settings.as3935_parameter.nf_autotune_time * 60) { + AS3935LowerNoiseFloor(); + as3935_sensor.nftimer = 0; + } + } + + if (Settings.as3935_functions.dist_autotune) { + if (AS3935GetDisturber()) { + as3935_sensor.disttimer++; + } + if (as3935_sensor.disttimer >= Settings.as3935_parameter.dist_autotune_time * 60) { + AS3935SetDisturber(0); + as3935_sensor.disttimer = 0; + } + } + } +} + +bool AS3935Cmd(void) { + char command[CMDSZ]; + uint8_t name_len = strlen(D_NAME_AS3935); + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_AS3935), name_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kAS3935_Commands); + switch (command_code) { + case CMND_AS3935_POWER: + if (XdrvMailbox.data_len) { + if (!XdrvMailbox.payload) { + AS3935PwrDown(); + } else { + AS3935PwrUp(); + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[AS3935GetPwrStat()]); + break; + case CMND_AS3935_SET_NF: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetNoiseFloor(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetNoiseFloor()); + break; + case CMND_AS3935_SET_MINNF: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.nf_autotune_min = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_min); + break; + case CMND_AS3935_SET_MINLIGHT: + if (XdrvMailbox.data_len) { + AS3935SetMinLights(AS3935TransMinLights(XdrvMailbox.payload)); + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935TranslMinLightsInt(AS3935GetMinLights())); + break; + case CMND_AS3935_SET_DEF: + if (!XdrvMailbox.data_len) { + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935SetDefault()); + } + break; + case CMND_AS3935_SET_GAIN: + if (XdrvMailbox.data_len > 6) { + uint8_t data_len = strlen(D_AS3935_OUTDOORS); + if (!strncasecmp_P(XdrvMailbox.data, PSTR(D_AS3935_OUTDOORS), data_len)) { + AS3935SetGain(OUTDOORS); + } else { + AS3935SetGain(INDOORS); + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_GAIN[AS3935GetGainInt()]); + break; + case CMND_AS3935_SET_TUNE: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetTuneCaps(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetTuneCaps()); + break; + case CMND_AS3935_SET_REJ: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetSpikeRejection(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetSpikeRejection()); + break; + case CMND_AS3935_SET_WDTH: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetWdth(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetWdth()); + break; + case CMND_AS3935_DISTTIME: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.dist_autotune_time = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.dist_autotune_time); + break; + case CMND_AS3935_NFTIME: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.nf_autotune_time = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_time); + break; + case CMND_AS3935_SET_DISTURBER: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + AS3935SetDisturber(XdrvMailbox.payload); + if (!XdrvMailbox.payload) Settings.as3935_functions.dist_autotune = 0; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[AS3935GetDisturber()]); + break; + case CMND_AS3935_NF_AUTOTUNE: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.nf_autotune = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune]); + break; + case CMND_AS3935_DIST_AUTOTUNE: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.dist_autotune = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.dist_autotune]); + break; + case CMND_AS3935_NF_ATUNE_BOTH: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.nf_autotune_both = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune_both]); + break; + case CMND_AS3935_MQTT_LIGHT_EVT: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.mqtt_only_Light_Event = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.mqtt_only_Light_Event]); + break; + case CMND_AS3935_MQTT_NO_IRQ_EVT: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.suppress_irq_no_Event = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.suppress_irq_no_Event]); + break; + case CMND_AS3935_SETTINGS: { + if (!XdrvMailbox.data_len) { + uint8_t gain = AS3935GetGainInt(); + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + uint8_t nf_floor = AS3935GetNoiseFloor(); + uint8_t tunecaps = AS3935GetTuneCaps(); + uint8_t minnumlight = AS3935TranslMinLightsInt(AS3935GetMinLights()); + uint8_t disturber = AS3935GetDisturber(); + uint8_t reinj = AS3935GetSpikeRejection(); + uint8_t wdth = AS3935GetWdth(); + uint8_t min_nf = Settings.as3935_parameter.nf_autotune_min; + uint8_t nf_time = Settings.as3935_parameter.nf_autotune_time; + uint8_t nfauto = Settings.as3935_functions.nf_autotune; + uint8_t nfautomax = Settings.as3935_functions.nf_autotune_both; + uint8_t distauto = Settings.as3935_functions.dist_autotune; + uint8_t jsonlight = Settings.as3935_functions.mqtt_only_Light_Event; + uint8_t jsonirq = Settings.as3935_functions.suppress_irq_no_Event; + uint8_t dist_time = Settings.as3935_parameter.dist_autotune_time; + Response_P(S_JSON_AS3935_COMMAND_SETTINGS, S_JSON_AS3935_COMMAND_GAIN[gain], nf_floor, vrms, tunecaps, minnumlight, reinj, wdth, min_nf, nf_time, dist_time, S_JSON_AS3935_COMMAND_ONOFF[disturber], S_JSON_AS3935_COMMAND_ONOFF[nfauto], S_JSON_AS3935_COMMAND_ONOFF[distauto], S_JSON_AS3935_COMMAND_ONOFF[nfautomax], S_JSON_AS3935_COMMAND_ONOFF[jsonlight], S_JSON_AS3935_COMMAND_ONOFF[jsonirq]); + } + } + break; + case CMND_AS3935_CALIBRATE: { + bool calreslt; + if (!XdrvMailbox.data_len) calreslt = AS3935AutoTune(); + Response_P(S_JSON_AS3935_COMMAND_NVALUE, S_JSON_AS3935_COMMAND_CAL[calreslt], AS3935GetTuneCaps()); + } + break; + default: + return false; + } + return true; + } else { + return false; + } +} + +void AH3935Show(bool json) { + if (json) { + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_event, as3935_sensor.distance, as3935_sensor.intensity, stage); +#ifdef USE_WEBSERVER + } else { + uint8_t gain = AS3935GetGainInt(); + uint8_t disturber = AS3935GetDisturber(); + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + + WSContentSend_PD(HTTP_SNS_AS3935_TABLE_1[as3935_sensor.http_event], D_NAME_AS3935, as3935_sensor.http_distance); + WSContentSend_PD(HTTP_SNS_AS3935_DISTANZ, as3935_sensor.http_distance); + WSContentSend_PD(HTTP_SNS_AS3935_ENERGY, as3935_sensor.http_intensity); + WSContentSend_PD(HTTP_SNS_AS3935_GAIN[gain], D_NAME_AS3935); + WSContentSend_PD(HTTP_SNS_AS3935_DISTURBER[disturber], D_NAME_AS3935); + WSContentSend_PD(HTTP_SNS_AS3935_VRMS, vrms, stage); + +#endif + } +} + + + + + +bool Xsns67(uint8_t function) { + if (!I2cEnabled(XI2C_48)) { return false; } + bool result = false; + if (FUNC_INIT == function) { + AS3935Detect(); + } + else if (as3935_sensor.active) { + switch (function) { + case FUNC_EVERY_SECOND: + AS3935EverySecond(); + break; + case FUNC_COMMAND: + result = AS3935Cmd(); + break; + case FUNC_JSON_APPEND: + AH3935Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AH3935Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_68_windmeter.ino" +# 21 "/workspace/Tasmota/tasmota/xsns_68_windmeter.ino" +#ifdef USE_WINDMETER + + + + +#define XSNS_68 68 + +#define D_WINDMETER_NAME "WindMeter" + +#define WINDMETER_DEF_RADIUS 61 +#define WINDMETER_DEF_PULSES_X_ROT 1 +#define WINDMETER_DEF_PULSE_DEBOUNCE 10 +#define WINDMETER_DEF_COMP_FACTOR 1.18 +#define WINDMETER_DEF_TELE_PCHANGE 255 +#define WINDMETER_WEIGHT_AVG_SAMPLE 150 + +#ifdef USE_WEBSERVER +#define D_WINDMETER_WIND_AVG "∅" +#define D_WINDMETER_WIND_ANGLE "∠" +#define D_WINDMETER_WIND_DEGREE "°" +const char HTTP_SNS_WINDMETER[] PROGMEM = + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" +#ifndef USE_WINDMETER_NOSTATISTICS + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED " " D_WINDMETER_WIND_AVG "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" +#endif + + + + + + ; +#endif + + +float const windmeter_pi = 3.1415926535897932384626433; +float const windmeter_2pi = windmeter_pi * 2; + +struct WINDMETER { + volatile uint32_t counter_time; + volatile unsigned long counter = 0; + + float speed = 0; + float last_tele_speed = 0; +#ifndef USE_WINDMETER_NOSTATISTICS + float speed_min = 0; + float speed_max = 0; + float speed_avg = 0; + uint32_t samples_count = 0; + uint32_t avg_samples_no; +#endif +} WindMeter; + +void ICACHE_RAM_ATTR WindMeterUpdateSpeed(void) +{ + uint32_t time = micros(); + uint32_t time_diff = time - WindMeter.counter_time; + if (time_diff > Settings.windmeter_pulse_debounce * 1000) { + WindMeter.counter_time = time; + WindMeter.counter++; + + } +} + + + +void WindMeterInit(void) +{ + if (!Settings.flag2.speed_conversion) { + Settings.flag2.speed_conversion = 2; + } + if (!Settings.windmeter_radius) { + Settings.windmeter_radius = WINDMETER_DEF_RADIUS; + } + if (!Settings.windmeter_pulses_x_rot) { + Settings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT; + } + if (!Settings.windmeter_pulse_debounce) { + Settings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE; + } + if (!Settings.windmeter_speed_factor) { + Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); + } + if (!Settings.windmeter_tele_pchange) { + Settings.windmeter_tele_pchange = WINDMETER_DEF_TELE_PCHANGE; + } + +#ifndef USE_WINDMETER_NOSTATISTICS + WindMeterResetStatData(); + WindMeterCheckSampleCount(); +#endif + + pinMode(Pin(GPIO_WINDMETER_SPEED), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_WINDMETER_SPEED), WindMeterUpdateSpeed, FALLING); +} + +void WindMeterEverySecond(void) +{ + + + + + + WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000); + + WindMeter.counter = 0; +# 136 "/workspace/Tasmota/tasmota/xsns_68_windmeter.ino" +#ifndef USE_WINDMETER_NOSTATISTICS + if (WindMeter.speed < WindMeter.speed_min) { + WindMeter.speed_min = WindMeter.speed; + } + if (WindMeter.speed > WindMeter.speed_max) { + WindMeter.speed_max = WindMeter.speed; + } + + + + + if (WindMeter.samples_count <= WindMeter.avg_samples_no) { + WindMeter.samples_count++; + } + WindMeter.speed_avg -= WindMeter.speed_avg / WindMeter.samples_count; + WindMeter.speed_avg += float(WindMeter.speed) / WindMeter.samples_count; + + WindMeterCheckSampleCount(); + if (0==Settings.tele_period) { + WindMeterResetStatData(); + } +#endif + + if (WindMeterShouldTriggerTele()) { + WindMeterTriggerTele(); + } +} + +bool WindMeterShouldTriggerTele() +{ + if (Settings.windmeter_tele_pchange > 100) { + return false; + } else if (WindMeter.last_tele_speed == 0) { + return WindMeter.speed > 0; + } else { + float perc_change = (WindMeter.speed / WindMeter.last_tele_speed) -1; + return (perc_change * ((perc_change < 0) ? -100 : 100)) >= Settings.windmeter_tele_pchange; + } +} + +void WindMeterResetStatData(void) +{ + WindMeter.speed_min = WindMeter.speed; + WindMeter.speed_max = WindMeter.speed; + + +} + +void WindMeterCheckSampleCount(void) +{ + uint32_t prev_avg_samples_no = WindMeter.avg_samples_no; + if (Settings.tele_period) { + + WindMeter.avg_samples_no = Settings.tele_period; + } else { + + WindMeter.avg_samples_no = WINDMETER_WEIGHT_AVG_SAMPLE; + } + if (prev_avg_samples_no != WindMeter.avg_samples_no) { + WindMeter.speed_avg = WindMeter.speed; + WindMeter.samples_count = 0; + } +} + +void WindMeterShow(bool json) +{ + char speed_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed), 2, speed_string); +#ifndef USE_WINDMETER_NOSTATISTICS + char speed_min_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_min), 2, speed_min_string); + char speed_max_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_max), 2, speed_max_string); + char speed_avg_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_avg), 2, speed_avg_string); +# 221 "/workspace/Tasmota/tasmota/xsns_68_windmeter.ino" +#endif + + if (json) { + WindMeter.last_tele_speed = WindMeter.speed; +#ifndef USE_WINDMETER_NOSTATISTICS + + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s}}"), + speed_string, + speed_avg_string, + speed_min_string, + speed_max_string + + + + + + + + ); +#else + + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s}}"), + speed_string + + + ); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_WINDMETER, + speed_string, + SpeedUnit().c_str(), +#ifndef USE_WINDMETER_NOSTATISTICS + speed_avg_string, + SpeedUnit().c_str(), + speed_min_string, + SpeedUnit().c_str(), + speed_max_string, + SpeedUnit().c_str(), +#endif + "n/a", + "n/a" +#ifndef USE_WINDMETER_NOSTATISTICS + ,"n/a", + "n/a", + "n/a", + "n/a", + "n/a" +#endif + ); +#endif + } +} + +void WindMeterTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); +#endif + } +} + + + + + +bool Xsns68Cmnd(void) +{ + bool serviced = true; + bool show_parms = true; + char sub_string[XdrvMailbox.data_len +1]; + switch (XdrvMailbox.payload) { + case 1: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); + } + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + } + + if (show_parms) { + char speed_factor_string[FLOATSZ]; + dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); + char tele_pchange_string[4] = "off"; + if (Settings.windmeter_tele_pchange <= 100) { + itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); + } + Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"), + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string); + } + return serviced; +} + + + + + +bool Xsns68(uint8_t function) +{ + bool result = false; + if (PinUsed(GPIO_WINDMETER_SPEED)) { + switch (function) { + case FUNC_INIT: + WindMeterInit(); + break; + case FUNC_EVERY_SECOND: + WindMeterEverySecond(); + break; +#ifndef USE_WINDMETER_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + WindMeterResetStatData(); + break; +#endif + case FUNC_JSON_APPEND: + WindMeterShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + WindMeterShow(false); + break; +#endif + case FUNC_COMMAND_SENSOR: + if (XSNS_68 == XdrvMailbox.index) { + result = Xsns68Cmnd(); + } + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_69_opentherm.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_69_opentherm.ino" +#ifdef USE_OPENTHERM + +#define XSNS_69 69 + +#include + + +#define OT_HOT_WATER_MIN 23 +#define OT_HOT_WATER_MAX 55 +#define OT_BOILER_MIN 40 +#define OT_BOILER_MAX 85 + +#define OT_HOT_WATER_DEFAULT 36; +#define OT_BOILER_DEFAULT 85; + + +#define SNS_OT_DISCONNECT_COOLDOWN_SECONDS 10 + + +#define OT_FLAGS_COUNT 6 +enum OpenThermSettingsFlags +{ + + + EnableCentralHeatingOnDiagnostics = 0x01, + + EnableHotWater = 0x02, + + EnableCentralHeating = 0x04, + EnableCooling = 0x08, + EnableTemperatureCompensation = 0x10, + EnableCentralHeating2 = 0x20, +}; + +enum OpenThermConnectionStatus +{ + OTC_NONE, + OTC_DISCONNECTED, + OTC_CONNECTING, + OTC_HANDSHAKE, + OTC_READY, + OTC_INFLIGHT +}; + +OpenThermConnectionStatus sns_ot_connection_status = OpenThermConnectionStatus::OTC_NONE; +uint8_t sns_ot_disconnect_cooldown = 0; + +OpenTherm *sns_ot_master = NULL; + + +typedef struct OT_BOILER_STATUS_T +{ + + uint8_t m_fault_code; + + uint8_t m_oem_fault_code; + + uint16_t m_oem_diag_code; + + uint8_t m_slave_flags; + + unsigned long m_slave_raw_status; + + bool m_enableCentralHeating; + bool m_enableHotWater; + bool m_enableCooling; + bool m_enableOutsideTemperatureCompensation; + bool m_enableCentralHeating2; + + + + + + + + bool m_useDiagnosticIndicationAsHeatRequest; + + + float m_hotWaterSetpoint_read; + + float m_flame_modulation_read; + + float m_boiler_temperature_read; + + + float m_boilerSetpoint; + float m_hotWaterSetpoint; + +} OT_BOILER_STATUS; + +OT_BOILER_STATUS sns_ot_boiler_status; + +const char *sns_opentherm_connection_stat_to_str(int status) +{ + switch (status) + { + case OpenThermConnectionStatus::OTC_NONE: + return "NONE"; + case OpenThermConnectionStatus::OTC_DISCONNECTED: + return "FAULT"; + case OpenThermConnectionStatus::OTC_CONNECTING: + return "CONNECTING"; + case OpenThermConnectionStatus::OTC_HANDSHAKE: + return "HANDSHAKE"; + case OpenThermConnectionStatus::OTC_READY: + return "READY"; + case OpenThermConnectionStatus::OTC_INFLIGHT: + return "BUSY"; + default: + return "UNKNOWN"; + } +} + +void sns_opentherm_init_boiler_status() +{ + memset(&sns_ot_boiler_status, 0, sizeof(OT_BOILER_STATUS)); + + + sns_ot_boiler_status.m_useDiagnosticIndicationAsHeatRequest = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics; + sns_ot_boiler_status.m_enableHotWater = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableHotWater; + sns_ot_boiler_status.m_enableCentralHeating = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating; + sns_ot_boiler_status.m_enableCooling = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCooling; + sns_ot_boiler_status.m_enableOutsideTemperatureCompensation = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableTemperatureCompensation; + sns_ot_boiler_status.m_enableCentralHeating2 = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating2; + + sns_ot_boiler_status.m_boilerSetpoint = (float)Settings.ot_boiler_setpoint; + sns_ot_boiler_status.m_hotWaterSetpoint = (float)Settings.ot_hot_water_setpoint; + + sns_ot_boiler_status.m_fault_code = 0; + sns_ot_boiler_status.m_oem_fault_code = 0; + sns_ot_boiler_status.m_oem_diag_code = 0; + sns_ot_boiler_status.m_hotWaterSetpoint_read = 0; + sns_ot_boiler_status.m_flame_modulation_read = 0; + sns_ot_boiler_status.m_boiler_temperature_read = 0; +} + +void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt() +{ + sns_ot_master->handleInterrupt(); +} + +void sns_opentherm_processResponseCallback(unsigned long response, int st) +{ + OpenThermResponseStatus status = (OpenThermResponseStatus)st; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, + PSTR("[OTH]: Processing response. Status=%s, Response=0x%lX"), + sns_ot_master->statusToString(status), response); + + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_HANDSHAKE) + { + return sns_ot_process_handshake(response, st); + } + + switch (status) + { + case OpenThermResponseStatus::SUCCESS: + if (sns_ot_master->isValidResponse(response)) + { + sns_opentherm_process_success_response(&sns_ot_boiler_status, response); + } + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; + break; + + case OpenThermResponseStatus::INVALID: + sns_opentherm_check_retry_request(); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; + break; + + + + + + case OpenThermResponseStatus::TIMEOUT: + sns_opentherm_check_retry_request(); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED; + break; + } +} + +bool sns_opentherm_Init() +{ + if (PinUsed(GPIO_BOILER_OT_RX) && PinUsed(GPIO_BOILER_OT_TX)) + { + sns_ot_master = new OpenTherm(Pin(GPIO_BOILER_OT_RX), Pin(GPIO_BOILER_OT_TX)); + sns_ot_master->begin(sns_opentherm_handleInterrupt, sns_opentherm_processResponseCallback); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING; + + sns_opentherm_init_boiler_status(); + return true; + } + return false; + +} + +void sns_opentherm_stat(bool json) +{ + if (!sns_ot_master) + { + return; + } + const char *statusStr = sns_opentherm_connection_stat_to_str(sns_ot_connection_status); + + if (json) + { + ResponseAppend_P(PSTR(",\"OPENTHERM\":{")); + ResponseAppend_P(PSTR("\"conn\":\"%s\","), statusStr); + ResponseAppend_P(PSTR("\"settings\":%d,"), Settings.ot_flags); + sns_opentherm_dump_telemetry(); + ResponseJsonEnd(); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_P(PSTR("{s}OpenTherm status{m}%s (0x%X){e}"), statusStr, (int)sns_ot_boiler_status.m_slave_flags); + if (sns_ot_connection_status < OpenThermConnectionStatus::OTC_READY) + { + return; + } + WSContentSend_P(PSTR("{s}Std/OEM Fault Codes{m}%d / %d{e}"), + (int)sns_ot_boiler_status.m_fault_code, + (int)sns_ot_boiler_status.m_oem_fault_code); + + WSContentSend_P(PSTR("{s}OEM Diagnostic Code{m}%d{e}"), + (int)sns_ot_boiler_status.m_oem_diag_code); + + WSContentSend_P(PSTR("{s}Hot Water Setpoint{m}%d{e}"), + (int)sns_ot_boiler_status.m_hotWaterSetpoint_read); + + WSContentSend_P(PSTR("{s}Flame Modulation{m}%d{e}"), + (int)sns_ot_boiler_status.m_flame_modulation_read); + + WSContentSend_P(PSTR("{s}Boiler Temp/Setpnt{m}%d / %d{e}"), + (int)sns_ot_boiler_status.m_boiler_temperature_read, + (int)sns_ot_boiler_status.m_boilerSetpoint); + + if (OpenTherm::isCentralHeatingActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Central Heating is ACTIVE{m}{e}")); + } + + if (sns_ot_boiler_status.m_enableHotWater) + { + WSContentSend_P(PSTR("{s}Hot Water is Enabled{m}{e}")); + } + + if (OpenTherm::isHotWaterActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Hot Water is ACTIVE{m}{e}")); + } + + if (OpenTherm::isFlameOn(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Flame is ACTIVE{m}{e}")); + } + + if (sns_ot_boiler_status.m_enableCooling) + { + WSContentSend_P(PSTR("{s}Cooling is Enabled{m}{e}")); + } + + if (OpenTherm::isCoolingActive(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Cooling is ACTIVE{m}{e}")); + } + + if (OpenTherm::isDiagnostic(sns_ot_boiler_status.m_slave_raw_status)) + { + WSContentSend_P(PSTR("{s}Diagnostic Indication{m}{e}")); + } + +#endif + } +} + +void sns_ot_start_handshake() +{ + if (!sns_ot_master) + { + return; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake")); + + sns_ot_master->sendRequestAync( + OpenTherm::buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0)); + + sns_ot_connection_status = OpenThermConnectionStatus::OTC_HANDSHAKE; +} + +void sns_ot_process_handshake(unsigned long response, int st) +{ + OpenThermResponseStatus status = (OpenThermResponseStatus)st; + + if (status != OpenThermResponseStatus::SUCCESS || !sns_ot_master->isValidResponse(response)) + { + AddLog_P2(LOG_LEVEL_ERROR, + PSTR("[OTH]: getSlaveConfiguration failed. Status=%s"), + sns_ot_master->statusToString(status)); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED; + return; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response); + + sns_ot_boiler_status.m_slave_flags = (response & 0xFF00) >> 8; + + sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY; +} + +void sns_opentherm_CheckSettings(void) +{ + bool settingsValid = true; + + settingsValid &= Settings.ot_hot_water_setpoint >= OT_HOT_WATER_MIN; + settingsValid &= Settings.ot_hot_water_setpoint <= OT_HOT_WATER_MAX; + settingsValid &= Settings.ot_boiler_setpoint >= OT_BOILER_MIN; + settingsValid &= Settings.ot_boiler_setpoint <= OT_BOILER_MAX; + + if (!settingsValid) + { + Settings.ot_hot_water_setpoint = OT_HOT_WATER_DEFAULT; + Settings.ot_boiler_setpoint = OT_BOILER_DEFAULT; + Settings.ot_flags = + OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics | + OpenThermSettingsFlags::EnableHotWater; + } +} + + + +const char *sns_opentherm_flag_text(uint8_t mode) +{ + switch ((OpenThermSettingsFlags)mode) + { + case OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics: + return "CHOD"; + case OpenThermSettingsFlags::EnableHotWater: + return "DHW"; + case OpenThermSettingsFlags::EnableCentralHeating: + return "CH"; + case OpenThermSettingsFlags::EnableCooling: + return "COOL"; + case OpenThermSettingsFlags::EnableTemperatureCompensation: + return "OTC"; + case OpenThermSettingsFlags::EnableCentralHeating2: + return "CH2"; + default: + return "?"; + } +} + +uint8_t sns_opentherm_parse_flag(char *flag) +{ + if (!strncmp(flag, "CHOD", 4)) + { + return OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics; + } + else if (!strncmp(flag, "COOL", 4)) + { + return OpenThermSettingsFlags::EnableCooling; + } + else if (!strncmp(flag, "DHW", 3)) + { + return OpenThermSettingsFlags::EnableHotWater; + } + else if (!strncmp(flag, "OTC", 3)) + { + return OpenThermSettingsFlags::EnableTemperatureCompensation; + } + else if (!strncmp(flag, "CH2", 3)) + { + return OpenThermSettingsFlags::EnableCentralHeating2; + } + else if (!strncmp(flag, "CH", 2)) + { + return OpenThermSettingsFlags::EnableCentralHeating; + } + return 0; +} + +uint8_t sns_opentherm_read_flags(char *data, uint32_t len) +{ + uint8_t tokens = 1; + for (int i = 0; i < len; ++i) + { + if (data[i] == ',') + { + ++tokens; + } + } + uint8_t result = 0; + char sub_string[XdrvMailbox.data_len + 1]; + for (int i = 1; i <= tokens; ++i) + { + char *flag = subStr(sub_string, data, ",", i); + if (!flag) + { + break; + } + result |= sns_opentherm_parse_flag(flag); + } + return result; +} +#define D_PRFX_OTHERM "ot_" + + +#define D_CMND_OTHERM_BOILER_SETPOINT "tboiler" + + +#define D_CMND_OTHERM_DHW_SETPOINT "twater" + + + +#define D_CMND_OTHERM_SAVE_SETTINGS "save_setpoints" +# 442 "/workspace/Tasmota/tasmota/xsns_69_opentherm.ino" +#define D_CMND_OTHERM_FLAGS "flags" + + + + + + + +#define D_CMND_SET_CENTRAL_HEATING_ENABLED "ch" + +const char kOpenThermCommands[] PROGMEM = D_PRFX_OTHERM "|" D_CMND_OTHERM_BOILER_SETPOINT "|" D_CMND_OTHERM_DHW_SETPOINT + "|" D_CMND_OTHERM_SAVE_SETTINGS "|" D_CMND_OTHERM_FLAGS "|" D_CMND_SET_CENTRAL_HEATING_ENABLED; + +void (*const OpenThermCommands[])(void) PROGMEM = { + &sns_opentherm_boiler_setpoint_cmd, + &sns_opentherm_hot_water_setpoint_cmd, + &sns_opentherm_save_settings_cmd, + &sns_opentherm_flags_cmd, + &sns_opentherm_set_central_heating_cmd}; + +void sns_opentherm_cmd(void) { } +void sns_opentherm_boiler_setpoint_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_boilerSetpoint = atof(XdrvMailbox.data); + } + ResponseCmndFloat(sns_ot_boiler_status.m_boilerSetpoint, Settings.flag2.temperature_resolution); +} + +void sns_opentherm_hot_water_setpoint_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_hotWaterSetpoint = atof(XdrvMailbox.data); + } + ResponseCmndFloat(sns_ot_boiler_status.m_hotWaterSetpoint, Settings.flag2.temperature_resolution); +} + +void sns_opentherm_save_settings_cmd(void) +{ + Settings.ot_hot_water_setpoint = (uint8_t)sns_ot_boiler_status.m_hotWaterSetpoint; + Settings.ot_boiler_setpoint = (uint8_t)sns_ot_boiler_status.m_boilerSetpoint; + ResponseCmndDone(); +} + +void sns_opentherm_flags_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + + Settings.ot_flags = sns_opentherm_read_flags(XdrvMailbox.data, XdrvMailbox.data_len); + + sns_opentherm_init_boiler_status(); + } + bool addComma = false; + mqtt_data[0] = 0; + for (int pos = 0; pos < OT_FLAGS_COUNT; ++pos) + { + int mask = 1 << pos; + int mode = Settings.ot_flags & (uint8_t)mask; + if (mode > 0) + { + if (addComma) + { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, sns_opentherm_flag_text(mode)); + addComma = true; + } + } +} + +void sns_opentherm_set_central_heating_cmd(void) +{ + bool query = strlen(XdrvMailbox.data) == 0; + if (!query) + { + sns_ot_boiler_status.m_enableCentralHeating = atoi(XdrvMailbox.data); + } + ResponseCmndNumber(sns_ot_boiler_status.m_enableCentralHeating ? 1 : 0); +} + + + + + +bool Xsns69(uint8_t function) +{ + bool result = false; + if (FUNC_INIT == function) + { + if (sns_opentherm_Init()) + { + sns_opentherm_CheckSettings(); + } + } + + if (!sns_ot_master) + { + return result; + } + + switch (function) + { + case FUNC_LOOP: + sns_ot_master->process(); + break; + case FUNC_EVERY_100_MSECOND: + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_READY && sns_ot_master->isReady()) + { + unsigned long request = sns_opentherm_get_next_request(&sns_ot_boiler_status); + if (-1 != request) + { + sns_ot_master->sendRequestAync(request); + sns_ot_connection_status = OpenThermConnectionStatus::OTC_INFLIGHT; + } + } + break; + case FUNC_EVERY_SECOND: + if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_DISCONNECTED) + { + + if (sns_ot_disconnect_cooldown == 0) + { + sns_ot_disconnect_cooldown = SNS_OT_DISCONNECT_COOLDOWN_SECONDS; + } + else if (--sns_ot_disconnect_cooldown == 0) + { + sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING; + } + } + else if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_CONNECTING) + { + sns_ot_start_handshake(); + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kOpenThermCommands, OpenThermCommands); + break; + case FUNC_JSON_APPEND: + sns_opentherm_stat(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + sns_opentherm_stat(0); + break; +#endif + } + + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_69_opentherm_protocol.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_69_opentherm_protocol.ino" +#ifdef USE_OPENTHERM + +#include "OpenTherm.h" + + + +#define OPENTHERM_BOILER_SETPOINT_TOLERANCE 1.0 + +typedef union { + uint8_t m_flags; + struct + { + uint8_t notSupported : 1; + uint8_t supported : 1; + uint8_t retryCount : 2; + }; +} OpenThermParamFlags; + +typedef union { + float m_float; + uint8_t m_u8; + uint16_t m_u16; + unsigned long m_ul; + bool m_bool; +} ResponseStorage; + +typedef struct OpenThermCommandT +{ + const char *m_command_name; + uint8_t m_command_code; + OpenThermParamFlags m_flags; + ResponseStorage m_results[2]; + unsigned long (*m_ot_make_request)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus); + void (*m_ot_parse_response)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus, unsigned long response); + void (*m_ot_appent_telemetry)(OpenThermCommandT *self); +} OpenThermCommand; + +OpenThermCommand sns_opentherm_commands[] = { + { + .m_command_name = "SLAVE", + .m_command_code = 0, + + + .m_flags = {.supported = 1}, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_slave_flags, + .m_ot_parse_response = sns_opentherm_parse_slave_flags, + .m_ot_appent_telemetry = sns_opentherm_tele_slave_flags}, + { + .m_command_name = "BTMP", + .m_command_code = 0, + + .m_flags = {.supported = 1}, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_boiler_temperature, + .m_ot_parse_response = sns_opentherm_parse_set_boiler_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_boiler_temperature}, + { + .m_command_name = "HWTMP", + .m_command_code = 0, + + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_set_boiler_dhw_temperature, + .m_ot_parse_response = sns_opentherm_parse_boiler_dhw_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_boiler_dhw_temperature}, + { + .m_command_name = "ASFF", + .m_command_code = 0, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_flags, + .m_ot_parse_response = sns_opentherm_parse_flags, + .m_ot_appent_telemetry = sns_opentherm_tele_flags}, + { + .m_command_name = "OEMD", + .m_command_code = 0, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_oem_diag, + .m_ot_parse_response = sns_opentherm_parse_oem_diag, + .m_ot_appent_telemetry = sns_opentherm_tele_oem_diag}, + { + .m_command_name = "FLM", + .m_command_code = (uint8_t)OpenThermMessageID::RelModLevel, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_flame_modulation, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "TB", + .m_command_code = (uint8_t)OpenThermMessageID::Tboiler, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_boiler_temperature, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "TDHW", + .m_command_code = (uint8_t)OpenThermMessageID::Tdhw, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "TOUT", + .m_command_code = (uint8_t)OpenThermMessageID::Toutside, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "TRET", + .m_command_code = (uint8_t)OpenThermMessageID::Tret, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "DHWS", + .m_command_code = (uint8_t)OpenThermMessageID::TdhwSet, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_dhw_setpoint, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + { + .m_command_name = "TMAX", + .m_command_code = (uint8_t)OpenThermMessageID::MaxTSet, + .m_flags = 0, + .m_results = {{.m_u8 = 0}, {.m_u8 = 0}}, + .m_ot_make_request = sns_opentherm_get_generic_float, + .m_ot_parse_response = sns_opentherm_parse_generic_float, + .m_ot_appent_telemetry = sns_opentherm_tele_generic_float}, + +}; + + +unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + bool centralHeatingIsOn = status->m_enableCentralHeating; + + if (status->m_useDiagnosticIndicationAsHeatRequest) { + centralHeatingIsOn |= OpenTherm::isDiagnostic(status->m_slave_raw_status); + } + + if (self->m_results[1].m_bool != centralHeatingIsOn) { + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Central Heating transitioning from %s to %s"), + self->m_results[1].m_bool ? "on" : "off", + status->m_enableCentralHeating ? "on" : "off"); + } + self->m_results[1].m_bool = centralHeatingIsOn; + + unsigned int data = centralHeatingIsOn | + (status->m_enableHotWater << 1) | + (status->m_enableCooling << 2) | + (status->m_enableOutsideTemperatureCompensation << 3) | + (status->m_enableCentralHeating2 << 4); + + data <<= 8; + + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Status, data); +} + +void sns_opentherm_parse_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + boilerStatus->m_slave_raw_status = response; + self->m_results[0].m_ul = response; +} + +#define OT_FLAG_TO_ON_OFF(status,flag) ((((status) & (flag)) != 0) ? 1 : 0) +void sns_opentherm_tele_slave_flags(struct OpenThermCommandT *self) +{ + unsigned long st = self->m_results[0].m_ul; + ResponseAppend_P(PSTR("{\"FAULT\":%d,\"CH\":%d,\"DHW\":%d,\"FL\":%d,\"COOL\":%d,\"CH2\":%d,\"DIAG\":%d,\"RAW\":%lu}"), + OT_FLAG_TO_ON_OFF(st, 0x01), + OT_FLAG_TO_ON_OFF(st, 0x02), + OT_FLAG_TO_ON_OFF(st, 0x04), + OT_FLAG_TO_ON_OFF(st, 0x08), + OT_FLAG_TO_ON_OFF(st, 0x10), + OT_FLAG_TO_ON_OFF(st, 0x20), + OT_FLAG_TO_ON_OFF(st, 0x40), + st); +} + + +unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + + + + float diff = abs(status->m_boilerSetpoint - self->m_results[0].m_float); + + if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE) + { + return -1; + } + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"), + (int)self->m_results[0].m_float, + (int)status->m_boilerSetpoint); + self->m_results[0].m_float = status->m_boilerSetpoint; + + unsigned int data = OpenTherm::temperatureToData(status->m_boilerSetpoint); + return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data); +} +void sns_opentherm_parse_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[1].m_float = OpenTherm::getFloat(response); +} +void sns_opentherm_tele_boiler_temperature(struct OpenThermCommandT *self) +{ + char requested[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested); + char actual[FLOATSZ]; + dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual); + + + bool isFault = abs(self->m_results[1].m_float - self->m_results[0].m_float) > OPENTHERM_BOILER_SETPOINT_TOLERANCE; + + ResponseAppend_P(PSTR("{\"FAULT\":%d,\"REQ\":%s,\"ACT\": %s}"), + (int)isFault, + requested, + actual); +} + + +unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status) +{ + + float diff = abs(status->m_hotWaterSetpoint - self->m_results[0].m_float); + + if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE) + { + return -1; + } + AddLog_P2(LOG_LEVEL_INFO, + PSTR("[OTH]: Setting Hot Water Temp. Old: %d, New: %d"), + (int)self->m_results[0].m_float, + (int)status->m_hotWaterSetpoint); + + self->m_results[0].m_float = status->m_hotWaterSetpoint; + + unsigned int data = OpenTherm::temperatureToData(status->m_hotWaterSetpoint); + return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data); +} +void sns_opentherm_parse_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[1].m_float = OpenTherm::getFloat(response); +} +void sns_opentherm_tele_boiler_dhw_temperature(struct OpenThermCommandT *self) +{ + char requested[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested); + char actual[FLOATSZ]; + dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual); + + ResponseAppend_P(PSTR("{\"REQ\":%s,\"ACT\": %s}"), + requested, + actual); +} + + +unsigned long sns_opentherm_get_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0); +} + +void sns_opentherm_parse_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + uint8_t fault_code = (response >> 8) & 0xFF; + uint8_t oem_fault_code = response & 0xFF; + boilerStatus->m_fault_code = fault_code; + boilerStatus->m_oem_fault_code = fault_code; + self->m_results[0].m_u8 = fault_code; + self->m_results[1].m_u8 = oem_fault_code; +} + +void sns_opentherm_tele_flags(struct OpenThermCommandT *self) +{ + ResponseAppend_P(PSTR("{\"FC\":%d,\"OFC\":%d}"), + (int)self->m_results[0].m_u8, + (int)self->m_results[1].m_u8); +} + + +unsigned long sns_opentherm_get_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OEMDiagnosticCode, 0); +} + +void sns_opentherm_parse_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + uint16_t diag_code = (uint16_t)response & 0xFFFF; + boilerStatus->m_oem_diag_code = diag_code; + self->m_results[0].m_u16 = diag_code; +} + +void sns_opentherm_tele_oem_diag(struct OpenThermCommandT *self) +{ + ResponseAppend_P(PSTR("%d"), (int)self->m_results[0].m_u16); +} + + +unsigned long sns_opentherm_get_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *) +{ + return OpenTherm::buildRequest(OpenThermRequestType::READ, (OpenThermMessageID)self->m_command_code, 0); +} + +void sns_opentherm_parse_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); +} + +void sns_opentherm_tele_generic_float(struct OpenThermCommandT *self) +{ + char str[FLOATSZ]; + dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, str); + ResponseAppend_P(PSTR("%s"), str); +} + + +void sns_opentherm_parse_dhw_setpoint(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_hotWaterSetpoint_read = self->m_results[0].m_float; +} + +void sns_opentherm_parse_flame_modulation(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_flame_modulation_read = self->m_results[0].m_float; +} + +void sns_opentherm_parse_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + self->m_results[0].m_float = OpenTherm::getFloat(response); + boilerStatus->m_boiler_temperature_read = self->m_results[0].m_float; +} + + + + + +#define SNS_OT_COMMANDS_COUNT (sizeof(sns_opentherm_commands) / sizeof(OpenThermCommand)) +int sns_opentherm_current_command = SNS_OT_COMMANDS_COUNT; + +unsigned long sns_opentherm_get_next_request(struct OT_BOILER_STATUS_T *boilerStatus) +{ + + if (++sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + sns_opentherm_current_command = 0; + } + + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + + if (cmd->m_flags.notSupported) + { + return -1; + } + + return cmd->m_ot_make_request(cmd, boilerStatus); +} + +void sns_opentherm_check_retry_request() +{ + if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + return; + } + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + + bool canRetry = ++cmd->m_flags.retryCount < 3; + + if (!canRetry && !cmd->m_flags.supported) + { + cmd->m_flags.notSupported = true; + AddLog_P2(LOG_LEVEL_ERROR, + PSTR("[OTH]: command %s is not supported by the boiler. Last status: %s"), + cmd->m_command_name, + sns_ot_master->statusToString(sns_ot_master->getLastResponseStatus())); + } +} + +void sns_opentherm_process_success_response(struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response) +{ + if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT) + { + return; + } + struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command]; + + cmd->m_flags.supported = true; + + cmd->m_ot_parse_response(cmd, boilerStatus, response); +} + +void sns_opentherm_dump_telemetry() +{ + bool add_coma = false; + for (int i = 0; i < SNS_OT_COMMANDS_COUNT; ++i) + { + struct OpenThermCommandT *cmd = &sns_opentherm_commands[i]; + if (!cmd->m_flags.supported) + { + continue; + } + + ResponseAppend_P(PSTR("%s\"%s\":"), add_coma ? "," : "", cmd->m_command_name); + + cmd->m_ot_appent_telemetry(cmd); + + add_coma = true; + } +} +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_70_veml6075.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_70_veml6075.ino" +#ifdef USE_I2C +#ifdef USE_VEML6075 + + + + + + +#define XSNS_70 70 +#define XI2C_49 49 + + +#define VEML6075_ADDR 0x10 +#define VEML6075_CHIP_ID 0x26 + + +#define VEML6075_REG_CONF 0x00 +#define VEML6075_REG_UVA 0x07 +#define VEML6075_REG_DARK 0x08 +#define VEML6075_REG_UVB 0x09 +#define VEML6075_REG_UVCOMP1 0x0A +#define VEML6075_REG_UVCOMP2 0x0B +#define VEML6075_REG_ID 0x0C + + +#define VEML6075_DEFAULT_UVA_A_COEFF 2.22 +#define VEML6075_DEFAULT_UVA_B_COEFF 1.33 +#define VEML6075_DEFAULT_UVB_C_COEFF 2.95 +#define VEML6075_DEFAULT_UVB_D_COEFF 1.74 +#define UVA_RESPONSIVITY_100MS_UNCOVERED 0.001461 +#define UVB_RESPONSIVITY_100MS_UNCOVERED 0.002591 + +const float UVA_RESPONSIVITY[] PROGMEM = +{ + UVA_RESPONSIVITY_100MS_UNCOVERED / 0.5016286645, + UVA_RESPONSIVITY_100MS_UNCOVERED, + UVA_RESPONSIVITY_100MS_UNCOVERED / 2.039087948, + UVA_RESPONSIVITY_100MS_UNCOVERED / 3.781758958, + UVA_RESPONSIVITY_100MS_UNCOVERED / 7.371335505 +}; + +const float UVB_RESPONSIVITY[] PROGMEM = +{ + UVB_RESPONSIVITY_100MS_UNCOVERED / 0.5016286645, + UVB_RESPONSIVITY_100MS_UNCOVERED, + UVB_RESPONSIVITY_100MS_UNCOVERED / 2.039087948, + UVB_RESPONSIVITY_100MS_UNCOVERED / 3.781758958, + UVB_RESPONSIVITY_100MS_UNCOVERED / 7.371335505 +}; + + +#define D_NAME_VEML6075 "VEML6075" +#define D_UVA_INTENSITY "UVA intensity" +#define D_UVB_INTENSITY "UVB intensity" + +const char HTTP_SNS_UVA[] PROGMEM = "{s}%s " D_UVA_INTENSITY "{m}%d " D_UNIT_WATT_METER_QUADRAT "{e}"; +const char HTTP_SNS_UVB[] PROGMEM = "{s}%s " D_UVB_INTENSITY "{m}%d " D_UNIT_WATT_METER_QUADRAT "{e}"; +const char HTTP_SNS_UVINDEX[] PROGMEM = "{s}%s " D_UV_INDEX "{m}%s {e}"; +const char JSON_SNS_VEML6075[] PROGMEM = ",\"%s\":{\"" D_JSON_UVA_INTENSITY "\":%d,\"" D_JSON_UVB_INTENSITY "\":%d,\"" D_JSON_UV_INDEX "\":%s}"; +const char S_JSON_VEML6075_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_VEML6075 "\":{\"%s\":%d}}"; + +const char kVEML6075_Commands[] PROGMEM = D_CMND_VEML6075_POWER "|" D_CMND_VEML6075_DYNAMIC "|" D_CMND_VEML6075_INTTIME; + +enum VEML6075_Commands { + CMND_VEML6075_PWR, + CMND_VEML6075_SET_HD, + CMND_VEML6075_SET_UVIT, + }; + + +struct VEML6075STRUCT +{ + char types[9] = D_NAME_VEML6075; + uint8_t address = VEML6075_ADDR; + uint8_t inttime = 0; + uint16_t uva = 0; + uint16_t uvb = 0; + uint16_t uva_raw = 0; + uint16_t uvb_raw = 0; + uint16_t comp1 = 0; + uint16_t comp2 = 0; + uint16_t conf = 0; + float uvi = 0.0f; +} veml6075_sensor; + +uint8_t veml6075_active = 0; + + +typedef union { + struct { + uint8_t pwr:1; + uint8_t forded_auto:1; + uint8_t forced_trigger:1; + uint8_t hd:1; + uint8_t inttime:3; + uint8_t spare7:1; + }; + uint16_t config; +} veml6075configRegister; + +veml6075configRegister veml6075Config; + + + +uint16_t VEML6075read16 (uint8_t reg) { + uint16_t swap = I2cRead16(VEML6075_ADDR, reg); + uint16_t ret = ((swap & 0xFF) << 8) | (swap >> 8); + return ret; +} + +void VEML6075write16 (uint8_t reg, uint16_t val) { + uint16_t swap = ((val & 0xFF) << 8) | (val >> 8); + I2cWrite16(VEML6075_ADDR, reg, swap); +} + +float VEML6075calcUVA (void) { + float uva_calc = veml6075_sensor.uva_raw - (VEML6075_DEFAULT_UVA_A_COEFF * veml6075_sensor.comp1) - (VEML6075_DEFAULT_UVA_B_COEFF * veml6075_sensor.comp2); + return uva_calc; +} + +float VEML6075calcUVB (void) { + float uvb_calc = veml6075_sensor.uvb_raw - (VEML6075_DEFAULT_UVB_C_COEFF * veml6075_sensor.comp1) - (VEML6075_DEFAULT_UVB_D_COEFF * veml6075_sensor.comp2); + return uvb_calc; +} + +float VEML6075calcUVI (void) { + float uvi_calc = ((veml6075_sensor.uva * UVA_RESPONSIVITY[veml6075_sensor.inttime]) + (veml6075_sensor.uvb * UVB_RESPONSIVITY[veml6075_sensor.inttime])) / 2; + return uvi_calc; +} + +void VEML6075SetHD(uint8_t val){ + veml6075Config.hd = val; + VEML6075write16 (VEML6075_REG_CONF, veml6075Config.config); +} + +uint8_t VEML6075ReadHD(void){ + veml6075Config.config = VEML6075read16 (VEML6075_REG_CONF); + return veml6075Config.hd; +} + +void VEML6075SetUvIt(uint8_t val){ + veml6075Config.inttime = val; + VEML6075Pwr(1); + VEML6075write16 (VEML6075_REG_CONF, veml6075Config.config); + VEML6075Pwr(0); +} + +uint8_t VEML6075GetUvIt(void){ + veml6075Config.config = VEML6075read16 (VEML6075_REG_CONF); + return veml6075Config.inttime; +} + +void VEML6075Pwr(uint8_t val){ + veml6075Config.pwr = val; + VEML6075write16 (VEML6075_REG_CONF, veml6075Config.config); +} + +uint8_t VEML6075GetPwr(void){ + veml6075Config.config = VEML6075read16 (VEML6075_REG_CONF); + return veml6075Config.pwr; +} + +void VEML6075ReadData(void) +{ + veml6075_sensor.uva_raw = VEML6075read16 (VEML6075_REG_UVA); + veml6075_sensor.uvb_raw = VEML6075read16 (VEML6075_REG_UVB); + veml6075_sensor.comp1 = VEML6075read16 (VEML6075_REG_UVCOMP1); + veml6075_sensor.comp2 = VEML6075read16 (VEML6075_REG_UVCOMP2); + veml6075_sensor.inttime = VEML6075GetUvIt(); + veml6075_sensor.uva = VEML6075calcUVA(); + veml6075_sensor.uvb = VEML6075calcUVB(); + veml6075_sensor.uvi = VEML6075calcUVI(); +} + +bool VEML6075init(void) +{ + uint8_t id = VEML6075read16 (VEML6075_REG_ID); + if(id == VEML6075_CHIP_ID) + return true; + return false; +} + +void VEML6075Detect(void) { + if (I2cActive(veml6075_sensor.address)) return; + + if (VEML6075init()) { + I2cSetActiveFound(veml6075_sensor.address, veml6075_sensor.types); + VEML6075write16 (VEML6075_REG_CONF, 0x10); + veml6075_active = 1; + } +} + +void VEML6075EverySecond(void) { + VEML6075ReadData(); +} + +bool VEML6075Cmd(void) { + char command[CMDSZ]; + uint8_t name_len = strlen(D_NAME_VEML6075); + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_VEML6075), name_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kVEML6075_Commands); + switch (command_code) { + case CMND_VEML6075_PWR: + if (XdrvMailbox.data_len) { + if (2 >= XdrvMailbox.payload) { + VEML6075Pwr(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML6075_COMMAND_NVALUE, command, VEML6075GetPwr()); + break; + case CMND_VEML6075_SET_HD: + if (XdrvMailbox.data_len) { + if (2 >= XdrvMailbox.payload) { + VEML6075SetHD(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML6075_COMMAND_NVALUE, command, VEML6075ReadHD()); + break; + case CMND_VEML6075_SET_UVIT: + if (XdrvMailbox.data_len) { + if (4 >= XdrvMailbox.payload) { + VEML6075SetUvIt(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML6075_COMMAND_NVALUE, command, VEML6075GetUvIt()); + break; + default: + return false; + } + return true; + } else { + return false; + } +} + +void VEML6075Show(bool json) +{ + char s_uvindex[FLOATSZ]; + dtostrfd(veml6075_sensor.uvi,1, s_uvindex); + + if (json) { + ResponseAppend_P(JSON_SNS_VEML6075, D_NAME_VEML6075, veml6075_sensor.uva, veml6075_sensor.uvb, s_uvindex); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_UVA, D_NAME_VEML6075, veml6075_sensor.uva); + WSContentSend_PD(HTTP_SNS_UVB, D_NAME_VEML6075, veml6075_sensor.uvb); + WSContentSend_PD(HTTP_SNS_UVINDEX, D_NAME_VEML6075 ,s_uvindex); +#endif + } +} + + + + + +bool Xsns70(uint8_t function) +{ + if (!I2cEnabled(XI2C_49)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + VEML6075Detect(); + } + else if (veml6075_active) { + switch (function) { + case FUNC_EVERY_SECOND: + VEML6075EverySecond(); + break; + case FUNC_COMMAND: + result = VEML6075Cmd(); + break; + case FUNC_JSON_APPEND: + VEML6075Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + VEML6075Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_71_veml7700.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_71_veml7700.ino" +#ifdef USE_I2C +#ifdef USE_VEML7700 + + + + + + +#define XSNS_71 71 +#define XI2C_50 50 + +#include "Adafruit_VEML7700.h" +Adafruit_VEML7700 veml7700 = Adafruit_VEML7700(); + +#define D_NAME_VEML7700 "VEML7700" +#define D_WHITE_CONTENT "White content" + +const char HTTP_SNS_WHITE[] PROGMEM = "{s}%s " D_WHITE_CONTENT "{m}%d {e}"; +const char JSON_SNS_VEML7700[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_WHITE_CONTENT "\":%d}"; + +#define D_CMND_VEML7700_PWR "power" +#define D_CMND_VEML7700_GAIN "gain" +#define D_CMND_VEML7700_INTTIME "inttime" +#define D_CMND_VEML7700_PERSIST "persist" + +const char S_JSON_VEML7700_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_VEML7700 "\":{\"%s\":%d}}"; +const char kVEML7700_Commands[] PROGMEM = D_CMND_VEML7700_PWR "|" D_CMND_VEML7700_GAIN "|" D_CMND_VEML7700_INTTIME "|" D_CMND_VEML7700_PERSIST; + +enum VEML7700_Commands { + CMND_VEML7700_PWR, + CMND_VEML7700_GAIN, + CMND_VEML7700_SET_IT, + CMND_VEML7700_PERSIST, +}; + +struct VEML7700STRUCT +{ + bool active = 0; + char types[9] = D_NAME_VEML7700; + uint8_t address = VEML7700_I2CADDR_DEFAULT; + uint32_t lux_normalized = 0; + uint32_t white_normalized = 0; +} veml7700_sensor; + + + + +void VEML7700Detect(void) { + if (!I2cSetDevice(veml7700_sensor.address)) return; + if (veml7700.begin()) { + I2cSetActiveFound(veml7700_sensor.address, veml7700_sensor.types); + veml7700_sensor.active = 1; + } +} + +uint16_t VEML7700TranslateItMs (uint8_t ittime){ + switch (ittime) { + case 0: return 100; + case 1: return 200; + case 2: return 400; + case 3: return 800; + case 8: return 50; + case 12: return 25; + default: return 0xFFFF; + } +} + +uint8_t VEML7700TranslateItInt (uint16_t ittimems){ + switch (ittimems) { + case 100: return 0; + case 200: return 1; + case 400: return 2; + case 800: return 3; + case 50: return 8; + case 25: return 12; + default: return 0xFF; + } +} + +void VEML7700EverySecond(void) { + veml7700_sensor.lux_normalized = (uint32_t) veml7700.readLuxNormalized(); + veml7700_sensor.white_normalized = (uint32_t) veml7700.readWhiteNormalized(); +} + +void VEML7700Show(bool json) +{ + if (json) { + ResponseAppend_P(JSON_SNS_VEML7700, D_NAME_VEML7700, veml7700_sensor.lux_normalized, veml7700_sensor.white_normalized); + +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, veml7700_sensor.lux_normalized); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, D_NAME_VEML7700, veml7700_sensor.lux_normalized); + WSContentSend_PD(HTTP_SNS_WHITE, D_NAME_VEML7700, veml7700_sensor.white_normalized); +#endif + } +} + +bool VEML7700Cmd(void) { + char command[CMDSZ]; + uint8_t name_len = strlen(D_NAME_VEML7700); + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_VEML7700), name_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kVEML7700_Commands); + switch (command_code) { + case CMND_VEML7700_PWR: + if (XdrvMailbox.data_len) { + if (2 >= XdrvMailbox.payload) { + veml7700.enable(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.enabled()); + break; + case CMND_VEML7700_GAIN: + if (XdrvMailbox.data_len) { + if (4 >= XdrvMailbox.payload) { + veml7700.setGain(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.getGain()); + break; + case CMND_VEML7700_SET_IT: { + if (XdrvMailbox.data_len) { + uint8_t data = VEML7700TranslateItInt(XdrvMailbox.payload); + if (0xFF != data) { + veml7700.setIntegrationTime(data); + } + } + uint16_t dataret = VEML7700TranslateItMs(veml7700.getIntegrationTime()); + Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, dataret); + } + break; + case CMND_VEML7700_PERSIST: + if (XdrvMailbox.data_len) { + if (4 >= XdrvMailbox.payload) { + veml7700.setPersistence(XdrvMailbox.payload); + } + } + Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.getPersistence()); + break; + default: + return false; + } + return true; + } + else { + return false; + } +} + + + + +bool Xsns71(uint8_t function) +{ + if (!I2cEnabled(XI2C_50)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + VEML7700Detect(); + } + else if (veml7700_sensor.active) { + switch (function) { + case FUNC_EVERY_SECOND: + VEML7700EverySecond(); + break; + case FUNC_COMMAND: + result = VEML7700Cmd(); + break; + case FUNC_JSON_APPEND: + VEML7700Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + VEML7700Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_72_mcp9808.ino" +# 21 "/workspace/Tasmota/tasmota/xsns_72_mcp9808.ino" +#ifdef USE_I2C +#ifdef USE_MCP9808 + + + + + + + +#define XSNS_72 72 +#define XI2C_51 51 + +#include "Adafruit_MCP9808.h" +Adafruit_MCP9808 mcp9808 = Adafruit_MCP9808(); + +#define MCP9808_MAX_SENSORS 8 +#define MCP9808_START_ADDRESS 0x18 + +struct { +char types[9] = "MCP9808"; +uint8_t count = 0; +} mcp9808_cfg; + +struct { + float temperature = NAN; + uint8_t address; +} mcp9808_sensors[MCP9808_MAX_SENSORS]; + + + +float MCP9808Read(uint8_t addr) { + float t = mcp9808.readTempC(addr); + return t; +} + +void MCP9808Detect(void) { + for (uint8_t i = 0; i < MCP9808_MAX_SENSORS; i++) { + if (!I2cSetDevice(MCP9808_START_ADDRESS + i)) { continue; } + + if (mcp9808.begin(MCP9808_START_ADDRESS + i)) { + mcp9808_sensors[mcp9808_cfg.count].address = MCP9808_START_ADDRESS + i; + I2cSetActiveFound(mcp9808_sensors[mcp9808_cfg.count].address, mcp9808_cfg.types); + mcp9808.setResolution (mcp9808_sensors[mcp9808_cfg.count].address, 2); + mcp9808_cfg.count++; + } + } +} + +void MCP9808EverySecond(void) { + for (uint32_t i = 0; i < mcp9808_cfg.count; i++) { + float t = MCP9808Read(mcp9808_sensors[i].address); + mcp9808_sensors[i].temperature = ConvertTemp(t); + } +} + +void MCP9808Show(bool json) { + for (uint32_t i = 0; i < mcp9808_cfg.count; i++) { + char temperature[33]; + dtostrfd(mcp9808_sensors[i].temperature, Settings.flag2.temperature_resolution, temperature); + + char sensor_name[11]; + strlcpy(sensor_name, mcp9808_cfg.types, sizeof(sensor_name)); + if (mcp9808_cfg.count > 1) { + snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), mcp9808_sensors[i].address); + } + + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, sensor_name, temperature); + if ((0 == tele_period) && (0 == i)) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, mcp9808_sensors[i].temperature); +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, temperature, TempUnit()); +#endif + } + } +} + + + + + +bool Xsns72(uint8_t function) +{ + if (!I2cEnabled(XI2C_51)) { return false; } + bool result = false; + + if (FUNC_INIT == function) { + MCP9808Detect(); + } + else if (mcp9808_cfg.count){ + switch (function) { + case FUNC_EVERY_SECOND: + MCP9808EverySecond(); + break; + case FUNC_JSON_APPEND: + MCP9808Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MCP9808Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_73_hp303b.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_73_hp303b.ino" +#ifdef USE_I2C +#ifdef USE_HP303B +# 30 "/workspace/Tasmota/tasmota/xsns_73_hp303b.ino" +#define XSNS_73 73 +#define XI2C_52 52 + +#define HP303B_MAX_SENSORS 2 +#define HP303B_START_ADDRESS 0x76 + +#include + +LOLIN_HP303B HP303BSensor = LOLIN_HP303B(); + +struct { + int16_t oversampling = 7; + char types[7] = "HP303B"; + uint8_t count = 0; +} hp303b_cfg; + +struct BHP303B { + float temperature = NAN; + float pressure = NAN; + uint8_t address; + uint8_t valid = 0; +} hp303b_sensor[HP303B_MAX_SENSORS]; + + + +bool HP303B_Read(uint32_t hp303b_idx) { + if (hp303b_sensor[hp303b_idx].valid) { hp303b_sensor[hp303b_idx].valid--; } + + float t; + if (HP303BSensor.measureTempOnce(t, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling) != 0) { + return false; + } + + float p; + if (HP303BSensor.measurePressureOnce(p, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling) != 0) { + return false; + } + + hp303b_sensor[hp303b_idx].temperature = (float)ConvertTemp(t); + hp303b_sensor[hp303b_idx].pressure = (float)ConvertPressure(p / 100); + + hp303b_sensor[hp303b_idx].valid = SENSOR_MAX_MISS; + return true; +} + + + +void HP303B_Detect(void) { + for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++) { + if (!I2cSetDevice(HP303B_START_ADDRESS + i)) { continue; } + + if (HP303BSensor.begin(HP303B_START_ADDRESS + i)) { + hp303b_sensor[hp303b_cfg.count].address = HP303B_START_ADDRESS + i; + I2cSetActiveFound(hp303b_sensor[hp303b_cfg.count].address, hp303b_cfg.types); + hp303b_cfg.count++; + } + } +} + +void HP303B_EverySecond(void) { + for (uint32_t i = 0; i < hp303b_cfg.count; i++) { + if (uptime &1) { + if (!HP303B_Read(i)) { + AddLogMissed(hp303b_cfg.types, hp303b_sensor[i].valid); + } + } + } +} + +void HP303B_Show(bool json) { + for (uint32_t i = 0; i < hp303b_cfg.count; i++) { + if (hp303b_sensor[i].valid) { + char sensor_name[12]; + strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name)); + if (hp303b_cfg.count > 1) { + snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); + } + + float sealevel = 0.0; + if (hp303b_sensor[i].pressure != 0.0) { + sealevel = (hp303b_sensor[i].pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; + sealevel = ConvertPressure(sealevel); + } + + char str_temperature[33]; + dtostrfd(hp303b_sensor[i].temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_pressure[33]; + dtostrfd(hp303b_sensor[i].pressure, Settings.flag2.pressure_resolution, str_pressure); + char sea_pressure[33]; + dtostrfd(sealevel, Settings.flag2.pressure_resolution, sea_pressure); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure); + if (Settings.altitude != 0) { + ResponseAppend_P(PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, str_temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_PRESSURE, sensor_name, str_pressure, PressureUnit().c_str()); + if (Settings.altitude != 0) { + WSContentSend_PD(HTTP_SNS_SEAPRESSURE, sensor_name, sea_pressure, PressureUnit().c_str()); + } +#endif + } + } + } +} + + + + + +bool Xsns73(uint8_t function) +{ + if (!I2cEnabled(XI2C_52)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + HP303B_Detect(); + } + else if (hp303b_cfg.count) { + switch (function) { + case FUNC_EVERY_SECOND: + HP303B_EverySecond(); + break; + case FUNC_JSON_APPEND: + HP303B_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HP303B_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_74_lmt01.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_74_lmt01.ino" +#ifdef USE_LMT01 + + + + + + + +#define XSNS_74 74 + +#define LMT01_TIMEOUT 200 + +bool lmt01_initialized = false; +float lmt01_temperature = NAN; + +void LMT01_Init(void) { + if (PinUsed(GPIO_LMT01)) { + pinMode(Pin(GPIO_LMT01), INPUT); + attachInterrupt(Pin(GPIO_LMT01), LMT01_countPulse, FALLING); + lmt01_initialized = true; + } +} + +volatile int lmt01_pulseCount = 0; + +void ICACHE_RAM_ATTR LMT01_countPulse(void) { + lmt01_pulseCount++; +} + +void LMT01_GetTemperature(void) { + int pulses = 0; + pulses = LMT01_getPulses(); + if (pulses >= 0) { + + + lmt01_temperature = ConvertTemp(0.0625 * pulses - 50); + } else { + lmt01_temperature = NAN; + } +} + +int LMT01_getPulses(void) { + int timeout = LMT01_TIMEOUT; + int hold = -1; + + while(lmt01_pulseCount != hold && --timeout > 0) { + hold = lmt01_pulseCount; + delay(1); + } + lmt01_pulseCount = 0; + + while(lmt01_pulseCount == 0 && --timeout > 0) { + delay(1); + } + hold = -1; + + while(lmt01_pulseCount != hold && --timeout > 0) { + hold = lmt01_pulseCount; + delay(1); + } + + if (timeout > 0 && hold >= 10) { + return hold; + } + return -1; +} + +void LMT01_Show(bool Json) { + char temp[33]; + dtostrfd(lmt01_temperature, Settings.flag2.temperature_resolution, temp); + + if (Json) { + ResponseAppend_P(JSON_SNS_TEMP, "LMT01", temp); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temp); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, lmt01_temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "LMT01", temp, TempUnit()); +#endif + } +} + + + + + +bool Xsns74(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + LMT01_Init(); + } + else if (lmt01_initialized) { + switch (function) { + case FUNC_EVERY_SECOND: + LMT01_GetTemperature(); + break; + case FUNC_JSON_APPEND: + LMT01_Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + LMT01_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_75_prometheus.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_75_prometheus.ino" +#ifdef USE_PROMETHEUS + + + + +#define XSNS_75 75 + +void HandleMetrics(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR("Prometheus")); + + WSContentBegin(200, CT_PLAIN); + + + char parameter[FLOATSZ]; + + + WSContentSend_P(PSTR("# TYPE tasmota_info gauge\ntasmota_info{version=\"%s\",image=\"%s\",build_timestamp=\"%s\"} 1\n"), + my_version, my_image, GetBuildDateAndTime().c_str()); + WSContentSend_P(PSTR("# TYPE tasmota_uptime_seconds gauge\ntasmota_uptime_seconds %d\n"), uptime); + WSContentSend_P(PSTR("# TYPE tasmota_boot_count counter\ntasmota_boot_count %d\n"), Settings.bootcount); + WSContentSend_P(PSTR("# TYPE tasmota_flash_writes_total counter\ntasmota_flash_writes_total %d\n"), Settings.save_flag); + + + + WSContentSend_P(PSTR("# TYPE tasmota_wifi_station_info gauge\ntasmota_wifi_station_info{bssid=\"%s\",ssid=\"%s\"} 1\n"), WiFi.BSSIDstr().c_str(), WiFi.SSID().c_str()); + + + WSContentSend_P(PSTR("# TYPE tasmota_wifi_station_signal_dbm gauge\ntasmota_wifi_station_signal_dbm{mac_address=\"%s\"} %d\n"), WiFi.BSSIDstr().c_str(), WiFi.RSSI()); + + if (!isnan(global_temperature_celsius)) { + dtostrfd(global_temperature_celsius, Settings.flag2.temperature_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_temperature_celsius gauge\nglobal_temperature_celsius %s\n"), parameter); + } + if (global_humidity != 0) { + dtostrfd(global_humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_humidity gauge\nglobal_humidity %s\n"), parameter); + } + if (global_pressure_hpa != 0) { + dtostrfd(global_pressure_hpa, Settings.flag2.pressure_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_pressure_hpa gauge\nglobal_pressure_hpa %s\n"), parameter); + } + +#ifdef USE_ENERGY_SENSOR + dtostrfd(Energy.voltage[0], Settings.flag2.voltage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_voltage_volts gauge\nenergy_voltage_volts %s\n"), parameter); + dtostrfd(Energy.current[0], Settings.flag2.current_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_current_amperes gauge\nenergy_current_amperes %s\n"), parameter); + dtostrfd(Energy.active_power[0], Settings.flag2.wattage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_power_active_watts gauge\nenergy_power_active_watts %s\n"), parameter); + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_power_kilowatts_daily counter\nenergy_power_kilowatts_daily %s\n"), parameter); + dtostrfd(Energy.total, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_power_kilowatts_total counter\nenergy_power_kilowatts_total %s\n"), parameter); +#endif +# 92 "/workspace/Tasmota/tasmota/xsns_75_prometheus.ino" + WSContentEnd(); +} + + + + + +bool Xsns75(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/metrics"), HandleMetrics); + break; + } + return result; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_76_dyp.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_76_dyp.ino" +#ifdef USE_DYP +# 34 "/workspace/Tasmota/tasmota/xsns_76_dyp.ino" +#define XSNS_76 76 + +#include + +TasmotaSerial *DYPSerial = nullptr; + +#define DYP_CRCERROR -1 +#define DYP_BELOWMIN 0 +#define DYP_MIN 300 +#define DYP_MAX 4000 +#define DYP_ABOVEMAX 4999 +#define DYP_NOSENSOR 5999 + +uint16_t DYPDistance = 0; +bool DYPSensor = false; + + + +void DYPInit(void) { + DYPSensor = false; + if (PinUsed(GPIO_DYP_RX)) { + DYPSerial = new TasmotaSerial(Pin(GPIO_DYP_RX), -1, 1); + if (DYPSerial->begin(9600)) { + if (DYPSerial->hardwareSerial()) { + ClaimSerial(); + } + DYPSensor = true; + } + } +} + +void DYPEverySecond(void) { + if (!DYPSensor) { return; } + + + if (DYPSerial->available() < 6) { + DYPDistance = DYP_NOSENSOR; + return; + } + + + while (DYPSerial->available() > 7) { + DYPSerial->read(); + } + + + while (DYPSerial->available() > 3) { + + if (DYPSerial->read() != 0xFF) { + continue; + } + + + if (DYPSerial->available() > 2) { + uint8_t msb = DYPSerial->read(); + uint8_t lsb = DYPSerial->read(); + if (((uint16_t)(0xFF + msb + lsb) & 0xFF) == DYPSerial->read()) { + uint16_t data = (msb << 8) | lsb; + if (data < DYP_MIN) { + data = DYP_BELOWMIN; + } + if (data > DYP_MAX) { + data = DYP_ABOVEMAX; + } + DYPDistance = data; + } else { + DYPDistance = DYP_CRCERROR; + } + } + } +} + +void DYPShow(bool json) { + char types[5] = "DYP"; + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_DISTANCE "\":%d}"), types, DYPDistance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_RANGE, types, DYPDistance); +#endif + } +} + + + + + +bool Xsns76(uint8_t function) { + if (!PinUsed(GPIO_DYP_RX)) { return false; } + + switch (function) { + case FUNC_INIT: + DYPInit(); + break; + case FUNC_EVERY_SECOND: + DYPEverySecond(); + break; + case FUNC_JSON_APPEND: + DYPShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DYPShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_77_vl53l1x.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_77_vl53l1x.ino" +#ifdef USE_I2C +#ifdef USE_VL53L1X +# 30 "/workspace/Tasmota/tasmota/xsns_77_vl53l1x.ino" +#define XSNS_77 77 +#define XI2C_54 54 + +#include "VL53L1X.h" +VL53L1X vl53l1x = VL53L1X(); + +#define VL53L1X_ADDRESS 0x29 + +struct { + bool ready = false; + uint16_t distance = 0; +} vl53l1x_sensors; + + + +void Vl53l1Detect(void) { + if (!I2cSetDevice(VL53L1X_ADDRESS)) { return; } + if (!vl53l1x.init()) { return; } + + I2cSetActiveFound(vl53l1x.getAddress(), "VL53L1X"); + vl53l1x.setTimeout(500); + vl53l1x.setDistanceMode(VL53L1X::Long); + vl53l1x.setMeasurementTimingBudget(140000); + vl53l1x.startContinuous(50); + vl53l1x_sensors.ready = true; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L1X[] PROGMEM = + "{s}VL53L1X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; +#endif + +void Vl53l1Every_250MSecond(void) { + + uint16_t dist = vl53l1x.read(); + if (!dist || dist > 4000) { + dist = 9999; + } + vl53l1x_sensors.distance = dist; +} + +#ifdef USE_DOMOTICZ +void Vl53l1Every_Second(void) { + char distance[FLOATSZ]; + dtostrfd((float)vl53l1x_sensors.distance / 10, 1, distance); + DomoticzSensor(DZ_ILLUMINANCE, distance); +} +#endif + +void Vl53l1Show(bool json) { + if (json) { +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + Vl53l1Every_Second(); + } +#endif + ResponseAppend_P(PSTR(",\"VL53L1X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l1x_sensors.distance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L1X, vl53l1x_sensors.distance); +#endif + } +} + + + + + +bool Xsns77(uint8_t function) +{ + if (!I2cEnabled(XI2C_54)) { return false; } + bool result = false; + + if (FUNC_INIT == function) { + Vl53l1Detect(); + } + else if (vl53l1x_sensors.ready) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + Vl53l1Every_250MSecond(); + break; +#ifdef USE_DOMOTICZ + case FUNC_EVERY_SECOND: + Vl53l1Every_Second(); + break; +#endif + case FUNC_JSON_APPEND: + Vl53l1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l1Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezo.ino" +# 19 "/workspace/Tasmota/tasmota/xsns_78_ezo.ino" +#ifdef USE_I2C + +#if defined(USE_EZOPH) || defined(USE_EZOORP) || defined(USE_EZORTD) || defined(USE_EZOHUM) || defined(USE_EZOEC) || defined(USE_EZOCO2) + #define USE_EZO +#endif +#if defined(USE_EZO) + +#define D_EZO_DELAY 300 +#define D_EZO_MAX_BUF 40 + +const char D_EZO_NAME[] PROGMEM = "EZO"; + + + + +struct EZOStruct { + EZOStruct(uint32_t addr) : valid(0), addr(addr), lastRead(-2000) {} + + void MeasureRequest(void) + { + const uint8_t EZOMeasureCmd[2] = {'R', 0}; + + if (valid) { + valid--; + } + + Wire.beginTransmission(addr); + Wire.write(EZOMeasureCmd, sizeof(EZOMeasureCmd)); + Wire.endTransmission(); + + lastRead = millis(); + } + + void HandleCommand(char *cmd, uint32_t len) const + { + + Wire.beginTransmission(addr); + Wire.write(cmd, len); + if (Wire.endTransmission() != 0) { + return; + } + + + char data[D_EZO_MAX_BUF]; + for (uint32_t code = 254; code == 254; code = Wire.read()) { + delay(D_EZO_DELAY); + Wire.requestFrom(addr, sizeof(data)); + } + + for (uint32_t i = 0; Wire.available() > 0; i++) { + data[i] = Wire.read(); + } + + ResponseCmndChar((char *)data); + } + + bool isValid(void) + { + return valid; + } + + virtual void ProcessMeasurement(void); + virtual void Show(bool json, const char *name); + + static const char id[] PROGMEM; + +protected: + void ProcessMeasurement(char *const data, const uint32_t len, const uint32_t latency) + { + + const int32_t dur = lastRead + latency - millis(); + + + if (dur > 0) { + delay(dur); + } + + Wire.requestFrom(addr, len); + const char code = Wire.read(); + + if (code == 1) { + for (uint32_t i = 0; (Wire.available() > 0) && (i < len); i++) { + data[i] = Wire.read(); + } + + valid = SENSOR_MAX_MISS; + } + } + + uint8_t valid; + uint8_t addr; + uint32_t lastRead; +}; + +const char EZOStruct::id[] PROGMEM = ""; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezoco2.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezoco2.ino" +#ifdef USE_I2C +#ifdef USE_EZOCO2 + +#define EZO_CO2_READ_LATENCY 900 + +struct EZOCO2 : public EZOStruct { + EZOCO2(uint32_t addr) : EZOStruct(addr), CO2(0) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_CO2_READ_LATENCY); + + + if (uptime >= 10) { + CO2 = atoi(data); + } + } + + virtual void Show(bool json, const char *name) + { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d}" ), name, CO2); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_CO2, name, CO2); +#endif + } + } + + static const char id[] PROGMEM; + +private: + uint16_t CO2; +}; + +const char EZOCO2::id[] PROGMEM = "CO2"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezoec.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezoec.ino" +#ifdef USE_I2C +#ifdef USE_EZOEC + +#define EZO_EC_READ_LATENCY 600 + +struct EZOEC : public EZOStruct { + EZOEC(uint32_t addr) : EZOStruct(addr), EC(NAN) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_EC_READ_LATENCY); + EC = CharToFloat(data); + } + + virtual void Show(bool json, const char *name) + { + char str[10]; + dtostrfd(EC, 3, str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_EC "\":%s}" ), name, str); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_EC, name, str); +#endif + } + } + + static const char id[] PROGMEM; + +private: + float EC; +}; + +const char EZOEC::id[] PROGMEM = "EC"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezohum.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezohum.ino" +#ifdef USE_I2C +#ifdef USE_EZOHUM + +#define EZO_HUM_READ_LATENCY 300 + +struct EZOHUM : public EZOStruct { + EZOHUM(uint32_t addr) : EZOStruct(addr), humidity(NAN), temperature(NAN) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_HUM_READ_LATENCY); + + humidity = CharToFloat(data); + + char *next = strchr(data, ','); + if (next) { + temperature = CharToFloat(next + 1); + } + } + + virtual void Show(bool json, const char *name) + { + if (isnan(temperature)) { + char parameter[FLOATSZ]; + + dtostrfd(humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_PD(HTTP_SNS_HUM, name, parameter); + } else { + TempHumDewShow(json, (0 == tele_period), name, temperature, humidity); + } + } + + static const char id[] PROGMEM; + +private: + float humidity; + float temperature; +}; + +const char EZOHUM::id[] PROGMEM = "HUM"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezoorp.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezoorp.ino" +#ifdef USE_I2C +#ifdef USE_EZOORP + +#define EZO_ORP_READ_LATENCY 900 + +struct EZOORP : public EZOStruct { + EZOORP(uint32_t addr) : EZOStruct(addr), ORP(NAN) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_ORP_READ_LATENCY); + ORP = CharToFloat(data); + } + + virtual void Show(bool json, const char *name) + { + char str[8]; + dtostrfd(ORP, 2, str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ORP "\":%s}" ), name, str); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_ORP, name, str); +#endif + } + } + + static const char id[] PROGMEM; + +private: + float ORP; +}; + +const char EZOORP::id[] PROGMEM = "ORP"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezoph.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezoph.ino" +#ifdef USE_I2C +#ifdef USE_EZOPH + +#define EZO_PH_READ_LATENCY 900 + +struct EZOPH : public EZOStruct { + EZOPH(uint32_t addr) : EZOStruct(addr), pH(NAN) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_PH_READ_LATENCY); + pH = CharToFloat(data); + } + + virtual void Show(bool json, const char *name) + { + char str[6]; + dtostrfd(pH, 2, str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PH "\":%s}" ), name, str); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_PH, name, str); +#endif + } + } + + static const char id[] PROGMEM; + +private: + float pH; +}; + +const char EZOPH::id[] PROGMEM = "pH"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_ezortd.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_ezortd.ino" +#ifdef USE_I2C +#ifdef USE_EZORTD + +#define EZO_RTD_READ_LATENCY 600 + +struct EZORTD : public EZOStruct { + EZORTD(uint32_t addr) : EZOStruct(addr), temperature(NAN) {} + + virtual void ProcessMeasurement(void) + { + char data[D_EZO_MAX_BUF]; + + EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_RTD_READ_LATENCY); + temperature = CharToFloat(data); + } + + virtual void Show(bool json, const char *name) + { + char str[10]; + dtostrfd(ConvertTemp(temperature), Settings.flag2.temperature_resolution, str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), name, str); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_TEMP, name, str, TempUnit()); +#endif + } + } + + static const char id[] PROGMEM; + +private: + float temperature; +}; + +const char EZORTD::id[] PROGMEM = "RTD"; + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_78_xezo.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_78_xezo.ino" +#ifdef USE_I2C +#if defined(USE_EZO) + +#define XSNS_78 78 +#define XI2C_55 55 + +#define EZO_ADDR_0 0x61 +#define EZO_ADDR_n 16 + + + +enum { + EZO_DO = 0x61, + EZO_ORP = 0x62, + EZO_PH = 0x63, + EZO_EC = 0x64, + + EZO_RTD = 0x66, + EZO_PMP = 0x67, + EZO_FLO = 0x68, + EZO_CO2 = 0x69, + EZO_PRS = 0x6A, + + EZO_O2 = 0x6C, + + + EZO_HUM = 0x6F, + EZO_RGB = 0x70, +}; + + + + +template struct IsComplete : std::false_type {}; +template struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type {}; + +#define GET_EZO_CLASS(CLASS) std::conditional::value, CLASS, EZOStruct>::type + + +const char *const EZOSupport[EZO_ADDR_n] PROGMEM = { + EZOStruct::id, + GET_EZO_CLASS(EZOORP)::id, + GET_EZO_CLASS(EZOPH)::id, + GET_EZO_CLASS(EZOEC)::id, + EZOStruct::id, + GET_EZO_CLASS(EZORTD)::id, + EZOStruct::id, + EZOStruct::id, + GET_EZO_CLASS(EZOCO2)::id, + EZOStruct::id, + EZOStruct::id, + EZOStruct::id, + EZOStruct::id, + EZOStruct::id, + GET_EZO_CLASS(EZOHUM)::id, + EZOStruct::id, +}; + +#define CREATE_EZO_CLASS(CLASS) \ + case EZO_ ## CLASS: \ + sensor[count] = new EZO ## CLASS(addr); \ + break; + + +struct EZOManager { + void Command() + { + char *at = XdrvMailbox.data; + uint32_t len = XdrvMailbox.data_len; + + + + if (at[0] == '-') { + int32_t idx = atoi(&at[1]) - 1; + at = strchr(at, ' '); + + if (!at++) { + return; + } + + len -= (at - XdrvMailbox.data); + + if ((idx >= 0) && (idx < count)) { + sensor[idx]->ProcessMeasurement(); + sensor[idx]->HandleCommand(at, len); + } + } else { + for (uint32_t i = 0; i < count; i++) { + sensor[i]->ProcessMeasurement(); + sensor[i]->HandleCommand(at, len); + } + } + } + + void EverySecond() + { + + if (count < 0) { + + if (uptime >= next) { + count++; + + if (count == -1) { + DetectRequest(); + next = uptime + 1; + } else if (count == 0) { + ProcessDetection(); + } + } + } + + for (int32_t i = 0; i < count; i++) { + sensor[i]->ProcessMeasurement(); + sensor[i]->MeasureRequest(); + } + } + + void Show(bool json) + { + for (int32_t i = 0; i < count; i++) { + if (sensor[i]->isValid()) { + char name[7]; + snprintf_P(name, sizeof(name), PSTR("%s%c%X"), D_EZO_NAME, IndexSeparator(), i + 1); + + if (count == 1) { + name[sizeof(D_EZO_NAME) - 1] = 0; + } + + sensor[i]->Show(json, name); + } + } + } + +private: + void DetectRequest(void) + { + const uint8_t EZOInfoCmd[2] = {'i', 0}; + alive = 0; + + + uint16_t shift = 1; + for (uint8_t i = EZO_ADDR_0; shift; i++) { + if (!I2cActive(i)) { + + Wire.beginTransmission(i); + Wire.write(EZOInfoCmd, sizeof(EZOInfoCmd)); + + int c = Wire.endTransmission(); + + if (c == 0) { + alive |= shift; + } + } + shift <<= 1; + } + } + + void ProcessDetection(void) + { + + for (uint8_t addr = EZO_ADDR_0; addr < EZO_ADDR_0 + EZO_ADDR_n; addr++) { + if (alive & 1) { + char data[D_EZO_MAX_BUF]; + Wire.requestFrom(addr, sizeof(data)); + char code = Wire.read(); + + if (code == 1) { + uint32_t i; + + for (i = 0; Wire.available() > 0; i++) { + char c = Wire.read(); + + + data[i] = (c == ',') ? 0 : c; + } + + + if (i >= 3) { + for (uint32_t j = 0; j < EZO_ADDR_n; j++) { + if (strcasecmp_P(&data[3], EZOSupport[j]) == 0) { + data[0] = 'E'; + data[1] = 'Z'; + data[2] = 'O'; + I2cSetActiveFound(addr, data); + + + switch (j + EZO_ADDR_0) { +#ifdef USE_EZOORP + CREATE_EZO_CLASS(ORP) +#endif +#ifdef USE_EZOPH + CREATE_EZO_CLASS(PH) +#endif +#ifdef USE_EZOEC + CREATE_EZO_CLASS(EC) +#endif +#ifdef USE_EZORTD + CREATE_EZO_CLASS(RTD) +#endif +#ifdef USE_EZOCO2 + CREATE_EZO_CLASS(CO2) +#endif +#ifdef USE_EZOHUM + CREATE_EZO_CLASS(HUM) +#endif + } + + count++; + } + } + } + } + } + + alive >>= 1; + } + } + + uint32_t next = 2; + int8_t count = -2; + + + uint16_t alive; + EZOStruct *sensor[EZO_ADDR_n]; +} EZOManager; + + + + + +bool Xsns78(uint8_t function) +{ + if (!I2cEnabled(XI2C_55)) { + return false; + } + + switch (function) { + case FUNC_COMMAND_SENSOR: + EZOManager.Command(); + break; + + case FUNC_EVERY_SECOND: + EZOManager.EverySecond(); + break; + + case FUNC_JSON_APPEND: + EZOManager.Show(1); + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + EZOManager.Show(0); + break; +#endif + } + + return false; +} + +#endif +#endif +# 1 "/workspace/Tasmota/tasmota/xsns_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xsns_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xsns_func_ptr[])(uint8_t) = { +#endif + +#ifdef XSNS_01 + &Xsns01, +#endif + +#ifdef XSNS_02 + &Xsns02, +#endif + +#ifdef XSNS_03 + &Xsns03, +#endif + +#ifdef XSNS_04 + &Xsns04, +#endif + +#ifdef XSNS_05 + &Xsns05, +#endif + +#ifdef XSNS_06 + &Xsns06, +#endif + +#ifdef XSNS_07 + &Xsns07, +#endif + +#ifdef XSNS_08 + &Xsns08, +#endif + +#ifdef XSNS_09 + &Xsns09, +#endif + +#ifdef XSNS_10 + &Xsns10, +#endif + +#ifdef XSNS_11 + &Xsns11, +#endif + +#ifdef XSNS_12 + &Xsns12, +#endif + +#ifdef XSNS_13 + &Xsns13, +#endif + +#ifdef XSNS_14 + &Xsns14, +#endif + +#ifdef XSNS_15 + &Xsns15, +#endif + +#ifdef XSNS_16 + &Xsns16, +#endif + +#ifdef XSNS_17 + &Xsns17, +#endif + +#ifdef XSNS_18 + &Xsns18, +#endif + +#ifdef XSNS_19 + &Xsns19, +#endif + +#ifdef XSNS_20 + &Xsns20, +#endif + +#ifdef XSNS_21 + &Xsns21, +#endif + +#ifdef XSNS_22 + &Xsns22, +#endif + +#ifdef XSNS_23 + &Xsns23, +#endif + +#ifdef XSNS_24 + &Xsns24, +#endif + +#ifdef XSNS_25 + &Xsns25, +#endif + +#ifdef XSNS_26 + &Xsns26, +#endif + +#ifdef XSNS_27 + &Xsns27, +#endif + +#ifdef XSNS_28 + &Xsns28, +#endif + +#ifdef XSNS_29 + &Xsns29, +#endif + +#ifdef XSNS_30 + &Xsns30, +#endif + +#ifdef XSNS_31 + &Xsns31, +#endif + +#ifdef XSNS_32 + &Xsns32, +#endif + +#ifdef XSNS_33 + &Xsns33, +#endif + +#ifdef XSNS_34 + &Xsns34, +#endif + +#ifdef XSNS_35 + &Xsns35, +#endif + +#ifdef XSNS_36 + &Xsns36, +#endif + +#ifdef XSNS_37 + &Xsns37, +#endif + +#ifdef XSNS_38 + &Xsns38, +#endif + +#ifdef XSNS_39 + &Xsns39, +#endif + +#ifdef XSNS_40 + &Xsns40, +#endif + +#ifdef XSNS_41 + &Xsns41, +#endif + +#ifdef XSNS_42 + &Xsns42, +#endif + +#ifdef XSNS_43 + &Xsns43, +#endif + +#ifdef XSNS_44 + &Xsns44, +#endif + +#ifdef XSNS_45 + &Xsns45, +#endif + +#ifdef XSNS_46 + &Xsns46, +#endif + +#ifdef XSNS_47 + &Xsns47, +#endif + +#ifdef XSNS_48 + &Xsns48, +#endif + +#ifdef XSNS_49 + &Xsns49, +#endif + +#ifdef XSNS_50 + &Xsns50, +#endif + +#ifdef XSNS_51 + &Xsns51, +#endif + +#ifdef XSNS_52 + &Xsns52, +#endif + +#ifdef XSNS_53 + &Xsns53, +#endif + +#ifdef XSNS_54 + &Xsns54, +#endif + +#ifdef XSNS_55 + &Xsns55, +#endif + +#ifdef XSNS_56 + &Xsns56, +#endif + +#ifdef XSNS_57 + &Xsns57, +#endif + +#ifdef XSNS_58 + &Xsns58, +#endif + +#ifdef XSNS_59 + &Xsns59, +#endif + +#ifdef XSNS_60 + &Xsns60, +#endif + +#ifdef XSNS_61 + &Xsns61, +#endif + +#ifdef XSNS_62 + &Xsns62, +#endif + +#ifdef XSNS_63 + &Xsns63, +#endif + +#ifdef XSNS_64 + &Xsns64, +#endif + +#ifdef XSNS_65 + &Xsns65, +#endif + +#ifdef XSNS_66 + &Xsns66, +#endif + +#ifdef XSNS_67 + &Xsns67, +#endif + +#ifdef XSNS_68 + &Xsns68, +#endif + +#ifdef XSNS_69 + &Xsns69, +#endif + +#ifdef XSNS_70 + &Xsns70, +#endif + +#ifdef XSNS_71 + &Xsns71, +#endif + +#ifdef XSNS_72 + &Xsns72, +#endif + +#ifdef XSNS_73 + &Xsns73, +#endif + +#ifdef XSNS_74 + &Xsns74, +#endif + +#ifdef XSNS_75 + &Xsns75, +#endif + +#ifdef XSNS_76 + &Xsns76, +#endif + +#ifdef XSNS_77 + &Xsns77, +#endif + +#ifdef XSNS_78 + &Xsns78, +#endif + +#ifdef XSNS_79 + &Xsns79, +#endif + +#ifdef XSNS_80 + &Xsns80, +#endif + +#ifdef XSNS_81 + &Xsns81, +#endif + +#ifdef XSNS_82 + &Xsns82, +#endif + +#ifdef XSNS_83 + &Xsns83, +#endif + +#ifdef XSNS_84 + &Xsns84, +#endif + +#ifdef XSNS_85 + &Xsns85, +#endif + +#ifdef XSNS_86 + &Xsns86, +#endif + +#ifdef XSNS_87 + &Xsns87, +#endif + +#ifdef XSNS_88 + &Xsns88, +#endif + +#ifdef XSNS_89 + &Xsns89, +#endif + +#ifdef XSNS_90 + &Xsns90, +#endif + +#ifdef XSNS_91 + &Xsns91, +#endif + +#ifdef XSNS_92 + &Xsns92, +#endif + +#ifdef XSNS_93 + &Xsns93, +#endif + +#ifdef XSNS_94 + &Xsns94, +#endif + +#ifdef XSNS_95 + &Xsns95, +#endif + +#ifdef XSNS_96 + &Xsns96, +#endif + +#ifdef XSNS_97 + &Xsns97, +#endif + +#ifdef XSNS_98 + &Xsns98, +#endif + +#ifdef XSNS_99 + &Xsns99 +#endif +}; + +const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXsnsList[] PROGMEM = { +#else +const uint8_t kXsnsList[] = { +#endif + +#ifdef XSNS_01 + XSNS_01, +#endif + +#ifdef XSNS_02 + XSNS_02, +#endif + +#ifdef XSNS_03 + XSNS_03, +#endif + +#ifdef XSNS_04 + XSNS_04, +#endif + +#ifdef XSNS_05 + XSNS_05, +#endif + +#ifdef XSNS_06 + XSNS_06, +#endif + +#ifdef XSNS_07 + XSNS_07, +#endif + +#ifdef XSNS_08 + XSNS_08, +#endif + +#ifdef XSNS_09 + XSNS_09, +#endif + +#ifdef XSNS_10 + XSNS_10, +#endif + +#ifdef XSNS_11 + XSNS_11, +#endif + +#ifdef XSNS_12 + XSNS_12, +#endif + +#ifdef XSNS_13 + XSNS_13, +#endif + +#ifdef XSNS_14 + XSNS_14, +#endif + +#ifdef XSNS_15 + XSNS_15, +#endif + +#ifdef XSNS_16 + XSNS_16, +#endif + +#ifdef XSNS_17 + XSNS_17, +#endif + +#ifdef XSNS_18 + XSNS_18, +#endif + +#ifdef XSNS_19 + XSNS_19, +#endif + +#ifdef XSNS_20 + XSNS_20, +#endif + +#ifdef XSNS_21 + XSNS_21, +#endif + +#ifdef XSNS_22 + XSNS_22, +#endif + +#ifdef XSNS_23 + XSNS_23, +#endif + +#ifdef XSNS_24 + XSNS_24, +#endif + +#ifdef XSNS_25 + XSNS_25, +#endif + +#ifdef XSNS_26 + XSNS_26, +#endif + +#ifdef XSNS_27 + XSNS_27, +#endif + +#ifdef XSNS_28 + XSNS_28, +#endif + +#ifdef XSNS_29 + XSNS_29, +#endif + +#ifdef XSNS_30 + XSNS_30, +#endif + +#ifdef XSNS_31 + XSNS_31, +#endif + +#ifdef XSNS_32 + XSNS_32, +#endif + +#ifdef XSNS_33 + XSNS_33, +#endif + +#ifdef XSNS_34 + XSNS_34, +#endif + +#ifdef XSNS_35 + XSNS_35, +#endif + +#ifdef XSNS_36 + XSNS_36, +#endif + +#ifdef XSNS_37 + XSNS_37, +#endif + +#ifdef XSNS_38 + XSNS_38, +#endif + +#ifdef XSNS_39 + XSNS_39, +#endif + +#ifdef XSNS_40 + XSNS_40, +#endif + +#ifdef XSNS_41 + XSNS_41, +#endif + +#ifdef XSNS_42 + XSNS_42, +#endif + +#ifdef XSNS_43 + XSNS_43, +#endif + +#ifdef XSNS_44 + XSNS_44, +#endif + +#ifdef XSNS_45 + XSNS_45, +#endif + +#ifdef XSNS_46 + XSNS_46, +#endif + +#ifdef XSNS_47 + XSNS_47, +#endif + +#ifdef XSNS_48 + XSNS_48, +#endif + +#ifdef XSNS_49 + XSNS_49, +#endif + +#ifdef XSNS_50 + XSNS_50, +#endif + +#ifdef XSNS_51 + XSNS_51, +#endif + +#ifdef XSNS_52 + XSNS_52, +#endif + +#ifdef XSNS_53 + XSNS_53, +#endif + +#ifdef XSNS_54 + XSNS_54, +#endif + +#ifdef XSNS_55 + XSNS_55, +#endif + +#ifdef XSNS_56 + XSNS_56, +#endif + +#ifdef XSNS_57 + XSNS_57, +#endif + +#ifdef XSNS_58 + XSNS_58, +#endif + +#ifdef XSNS_59 + XSNS_59, +#endif + +#ifdef XSNS_60 + XSNS_60, +#endif + +#ifdef XSNS_61 + XSNS_61, +#endif + +#ifdef XSNS_62 + XSNS_62, +#endif + +#ifdef XSNS_63 + XSNS_63, +#endif + +#ifdef XSNS_64 + XSNS_64, +#endif + +#ifdef XSNS_65 + XSNS_65, +#endif + +#ifdef XSNS_66 + XSNS_66, +#endif + +#ifdef XSNS_67 + XSNS_67, +#endif + +#ifdef XSNS_68 + XSNS_68, +#endif + +#ifdef XSNS_69 + XSNS_69, +#endif + +#ifdef XSNS_70 + XSNS_70, +#endif + +#ifdef XSNS_71 + XSNS_71, +#endif + +#ifdef XSNS_72 + XSNS_72, +#endif + +#ifdef XSNS_73 + XSNS_73, +#endif + +#ifdef XSNS_74 + XSNS_74, +#endif + +#ifdef XSNS_75 + XSNS_75, +#endif + +#ifdef XSNS_76 + XSNS_76, +#endif + +#ifdef XSNS_77 + XSNS_77, +#endif + +#ifdef XSNS_78 + XSNS_78, +#endif + +#ifdef XSNS_79 + XSNS_79, +#endif + +#ifdef XSNS_80 + XSNS_80, +#endif + +#ifdef XSNS_81 + XSNS_81, +#endif + +#ifdef XSNS_82 + XSNS_82, +#endif + +#ifdef XSNS_83 + XSNS_83, +#endif + +#ifdef XSNS_84 + XSNS_84, +#endif + +#ifdef XSNS_85 + XSNS_85, +#endif + +#ifdef XSNS_86 + XSNS_86, +#endif + +#ifdef XSNS_87 + XSNS_87, +#endif + +#ifdef XSNS_88 + XSNS_88, +#endif + +#ifdef XSNS_89 + XSNS_89, +#endif + +#ifdef XSNS_90 + XSNS_90, +#endif + +#ifdef XSNS_91 + XSNS_91, +#endif + +#ifdef XSNS_92 + XSNS_92, +#endif + +#ifdef XSNS_93 + XSNS_93, +#endif + +#ifdef XSNS_94 + XSNS_94, +#endif + +#ifdef XSNS_95 + XSNS_95, +#endif + +#ifdef XSNS_96 + XSNS_96, +#endif + +#ifdef XSNS_97 + XSNS_97, +#endif + +#ifdef XSNS_98 + XSNS_98, +#endif + +#ifdef XSNS_99 + XSNS_99 +#endif +}; + + + +bool XsnsEnabled(uint32_t sns_index) +{ + if (sns_index < sizeof(kXsnsList)) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t index = pgm_read_byte(kXsnsList + sns_index); +#else + uint32_t index = kXsnsList[sns_index]; +#endif + return bitRead(Settings.sensors[index / 32], index % 32); + } + return true; +} + +void XsnsSensorState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t sensorid = pgm_read_byte(kXsnsList + i); +#else + uint32_t sensorid = kXsnsList[i]; +#endif + bool disabled = false; + if (sensorid < MAX_XSNS_DRIVERS) { + disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); + } + ResponseAppend_P(PSTR("\"")); +} + + + + + +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) +{ + if (0 == xsns_present) { + xsns_index = 0; + return false; + } + + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + +#ifndef USE_DEBUG_DRIVER + if (FUNC_WEB_SENSOR == Function) { +#endif + uint32_t max_disabled = xsns_present; + while (!XsnsEnabled(xsns_index) && max_disabled--) { + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + } +#ifndef USE_DEBUG_DRIVER + } +#endif + + return xsns_func_ptr[xsns_index](Function); +} + +bool XsnsCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("SNS: %d"), Function); + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + + for (uint32_t x = 0; x < xsns_present; x++) { +#ifdef USE_DEBUG_DRIVER + if (XsnsEnabled(x)) { +#endif + + if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + result = xsns_func_ptr[x](Function); + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); + } + } +#endif + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_COMMAND_SENSOR == Function) + )) { + break; + } +#ifdef USE_DEBUG_DRIVER + } +#endif + } + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); + } + } +#endif + + return result; +} +# 1 "/workspace/Tasmota/tasmota/xx2c_interface.ino" +# 20 "/workspace/Tasmota/tasmota/xx2c_interface.ino" +#ifdef USE_I2C + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kI2cList[] PROGMEM = { +#else +const uint8_t kI2cList[] = { +#endif + +#ifdef XI2C_01 + XI2C_01, +#endif + +#ifdef XI2C_02 + XI2C_02, +#endif + +#ifdef XI2C_03 + XI2C_03, +#endif + +#ifdef XI2C_04 + XI2C_04, +#endif + +#ifdef XI2C_05 + XI2C_05, +#endif + +#ifdef XI2C_06 + XI2C_06, +#endif + +#ifdef XI2C_07 + XI2C_07, +#endif + +#ifdef XI2C_08 + XI2C_08, +#endif + +#ifdef XI2C_09 + XI2C_09, +#endif + +#ifdef XI2C_10 + XI2C_10, +#endif + +#ifdef XI2C_11 + XI2C_11, +#endif + +#ifdef XI2C_12 + XI2C_12, +#endif + +#ifdef XI2C_13 + XI2C_13, +#endif + +#ifdef XI2C_14 + XI2C_14, +#endif + +#ifdef XI2C_15 + XI2C_15, +#endif + +#ifdef XI2C_16 + XI2C_16, +#endif + +#ifdef XI2C_17 + XI2C_17, +#endif + +#ifdef XI2C_18 + XI2C_18, +#endif + +#ifdef XI2C_19 + XI2C_19, +#endif + +#ifdef XI2C_20 + XI2C_20, +#endif + +#ifdef XI2C_21 + XI2C_21, +#endif + +#ifdef XI2C_22 + XI2C_22, +#endif + +#ifdef XI2C_23 + XI2C_23, +#endif + +#ifdef XI2C_24 + XI2C_24, +#endif + +#ifdef XI2C_25 + XI2C_25, +#endif + +#ifdef XI2C_26 + XI2C_26, +#endif + +#ifdef XI2C_27 + XI2C_27, +#endif + +#ifdef XI2C_28 + XI2C_28, +#endif + +#ifdef XI2C_29 + XI2C_29, +#endif + +#ifdef XI2C_30 + XI2C_30, +#endif + +#ifdef XI2C_31 + XI2C_31, +#endif + +#ifdef XI2C_32 + XI2C_32, +#endif + +#ifdef XI2C_33 + XI2C_33, +#endif + +#ifdef XI2C_34 + XI2C_34, +#endif + +#ifdef XI2C_35 + XI2C_35, +#endif + +#ifdef XI2C_36 + XI2C_36, +#endif + +#ifdef XI2C_37 + XI2C_37, +#endif + +#ifdef XI2C_38 + XI2C_38, +#endif + +#ifdef XI2C_39 + XI2C_39, +#endif + +#ifdef XI2C_40 + XI2C_40, +#endif + +#ifdef XI2C_41 + XI2C_41, +#endif + +#ifdef XI2C_42 + XI2C_42, +#endif + +#ifdef XI2C_43 + XI2C_43, +#endif + +#ifdef XI2C_44 + XI2C_44, +#endif + +#ifdef XI2C_45 + XI2C_45, +#endif + +#ifdef XI2C_46 + XI2C_46, +#endif + +#ifdef XI2C_47 + XI2C_47, +#endif + +#ifdef XI2C_48 + XI2C_48, +#endif + +#ifdef XI2C_49 + XI2C_49, +#endif + +#ifdef XI2C_50 + XI2C_50, +#endif + +#ifdef XI2C_51 + XI2C_51, +#endif + +#ifdef XI2C_52 + XI2C_52, +#endif + +#ifdef XI2C_53 + XI2C_53, +#endif + +#ifdef XI2C_54 + XI2C_54, +#endif + +#ifdef XI2C_55 + XI2C_55, +#endif + +#ifdef XI2C_56 + XI2C_56, +#endif + +#ifdef XI2C_57 + XI2C_57, +#endif + +#ifdef XI2C_58 + XI2C_58, +#endif + +#ifdef XI2C_59 + XI2C_59, +#endif + +#ifdef XI2C_60 + XI2C_60, +#endif + +#ifdef XI2C_61 + XI2C_61, +#endif + +#ifdef XI2C_62 + XI2C_62, +#endif + +#ifdef XI2C_63 + XI2C_63, +#endif + +#ifdef XI2C_64 + XI2C_64, +#endif + +#ifdef XI2C_65 + XI2C_65, +#endif + +#ifdef XI2C_66 + XI2C_66, +#endif + +#ifdef XI2C_67 + XI2C_67, +#endif + +#ifdef XI2C_68 + XI2C_68, +#endif + +#ifdef XI2C_69 + XI2C_69, +#endif + +#ifdef XI2C_70 + XI2C_70, +#endif + +#ifdef XI2C_71 + XI2C_71, +#endif + +#ifdef XI2C_72 + XI2C_72, +#endif + +#ifdef XI2C_73 + XI2C_73, +#endif + +#ifdef XI2C_74 + XI2C_74, +#endif + +#ifdef XI2C_75 + XI2C_75, +#endif + +#ifdef XI2C_76 + XI2C_76, +#endif + +#ifdef XI2C_77 + XI2C_77, +#endif + +#ifdef XI2C_78 + XI2C_78, +#endif + +#ifdef XI2C_79 + XI2C_79, +#endif + +#ifdef XI2C_80 + XI2C_80, +#endif + +#ifdef XI2C_81 + XI2C_81, +#endif + +#ifdef XI2C_82 + XI2C_82, +#endif + +#ifdef XI2C_83 + XI2C_83, +#endif + +#ifdef XI2C_84 + XI2C_84, +#endif + +#ifdef XI2C_85 + XI2C_85, +#endif + +#ifdef XI2C_86 + XI2C_86, +#endif + +#ifdef XI2C_87 + XI2C_87, +#endif + +#ifdef XI2C_88 + XI2C_88, +#endif + +#ifdef XI2C_89 + XI2C_89, +#endif + +#ifdef XI2C_90 + XI2C_90, +#endif + +#ifdef XI2C_91 + XI2C_91, +#endif + +#ifdef XI2C_92 + XI2C_92, +#endif + +#ifdef XI2C_93 + XI2C_93, +#endif + +#ifdef XI2C_94 + XI2C_94, +#endif + +#ifdef XI2C_95 + XI2C_95, +#endif + +#ifdef XI2C_96 + XI2C_96 +#endif +}; + + + +bool I2cEnabled(uint32_t i2c_index) +{ + return (i2c_flg && bitRead(Settings.i2c_drivers[i2c_index / 32], i2c_index % 32)); +} + +void I2cDriverState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kI2cList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t i2c_driver_id = pgm_read_byte(kI2cList + i); +#else + uint32_t i2c_driver_id = kI2cList[i]; +#endif + bool disabled = false; + if (i2c_driver_id < MAX_I2C_DRIVERS) { + disabled = !bitRead(Settings.i2c_drivers[i2c_driver_id / 32], i2c_driver_id % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", i2c_driver_id); + } + ResponseAppend_P(PSTR("\"")); +} + +#endif \ No newline at end of file