mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' into release
This commit is contained in:
commit
1b315134da
|
@ -47,10 +47,28 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
|
||||
## Changelog
|
||||
|
||||
### Version 7.1.2 Betty
|
||||
### Version 7.2.0 Constance
|
||||
|
||||
- Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080)
|
||||
- Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071)
|
||||
- Fix WS2812 power control (#7090)
|
||||
- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control
|
||||
- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110)
|
||||
- Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available
|
||||
- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors <cors_domain>`` allowing user control of specific CORS domain by Shantur Rathore (#7066)
|
||||
- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166)
|
||||
- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167)
|
||||
- Change some Settings locations freeing up space for future single char allowing variable length text
|
||||
- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649)
|
||||
- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147)
|
||||
- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174)
|
||||
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108)
|
||||
- Add command ``Sensor34 9 <weight code>`` to set minimum delta to trigger JSON message by @tobox (#7188)
|
||||
- Add rule var ``%topic%`` by Adrian Scillato (#5522)
|
||||
- Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093)
|
||||
- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112)
|
||||
- Add experimental support for stepper motor shutter control by Stefan Bode
|
||||
- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115)
|
||||
- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger
|
||||
- Add Home Assistant force update by Frederico Leoni (#7140, #7074)
|
||||
- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145)
|
||||
- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058)
|
||||
- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171)
|
||||
- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor by Stefan Hadinger
|
||||
- Add Shutter functions ramp up/down and MQTT reporting by Stefan Bode
|
||||
- Add fallback functionality from version 8.x
|
||||
|
|
|
@ -32,8 +32,18 @@
|
|||
#define DISABLE_RESTORE_BUTTON 1 // [Default 0] Set to 1 to disable the "restore defaults" button in the web ui.
|
||||
|
||||
// These values normally don't need adjustment
|
||||
#define MULTICAST_PORT 3671 // [Default 3671]
|
||||
#ifndef MULTICAST_IP
|
||||
#define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)]
|
||||
#else
|
||||
#warning USING CUSTOM MULTICAST_IP
|
||||
#endif
|
||||
|
||||
#ifndef MULTICAST_PORT
|
||||
#define MULTICAST_PORT 3671 // [Default 3671]
|
||||
#else
|
||||
#warning USING CUSTOM MULTICAST_PORT
|
||||
#endif
|
||||
|
||||
#define SEND_CHECKSUM 0
|
||||
|
||||
// Uncomment to enable printing out debug messages.
|
||||
|
|
|
@ -2,6 +2,56 @@
|
|||
|
||||
## Released
|
||||
|
||||
### 7.2.0 20191221
|
||||
|
||||
- Release
|
||||
|
||||
### 7.1.2.6 20191214
|
||||
|
||||
- Change some more Settings locations freeing up space for future single char allowing variable length text
|
||||
- Add Zigbee send automatic ZigbeeRead after sending a command
|
||||
- Add Zigbee improving Occupancy:false detection for Aqara sensor
|
||||
- Add fallback functionality from version 8.x
|
||||
|
||||
### 7.1.2.5 20191213
|
||||
|
||||
- Change some Settings locations freeing up space for future single char allowing variable length text
|
||||
- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor by Stefan Hadinger
|
||||
- Add Shutter functions ramp up/down and MQTT reporting by Stefan Bode
|
||||
|
||||
### 7.1.2.4 20191209
|
||||
|
||||
- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors <cors_domain>`` allowing user control of specific CORS domain by Shantur Rathore (#7066)
|
||||
- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166)
|
||||
- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167)
|
||||
- Revert removal of exception details from MQTT info on restart
|
||||
- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145)
|
||||
- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058)
|
||||
- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171)
|
||||
- Add command ``Sensor34 9 <weight code>`` to set minimum delta to trigger JSON message by @tobox (#7188)
|
||||
- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649)
|
||||
- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147)
|
||||
- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174)
|
||||
|
||||
### 7.1.2.3 20191208
|
||||
|
||||
- Change Exception reporting removing exception details from both MQTT info and ``Status 1``. Now consolidated in ``Status 12`` if available.
|
||||
|
||||
### 7.1.2.2 20191206
|
||||
|
||||
- Remove rule trigger ``tele_power1#state`` due to compatibility
|
||||
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108)
|
||||
- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger
|
||||
- Add Home Assistant force update by Frederico Leoni (#7140, #7074)
|
||||
|
||||
### 7.1.2.1 20191206
|
||||
|
||||
- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112)
|
||||
- Add rule var ``%topic%`` by Adrian Scillato (#5522)
|
||||
- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` by Adrian Scillato (#7093)
|
||||
- Add experimental support for stepper motor shutter control by Stefan Bode
|
||||
- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115)
|
||||
|
||||
### 7.1.2 20191206
|
||||
|
||||
- Maintenance Release
|
||||
|
|
|
@ -134,6 +134,7 @@
|
|||
#define D_JSON_SELECTED "selected"
|
||||
#define D_JSON_SERIALRECEIVED "SerialReceived"
|
||||
#define D_JSON_SET "Set"
|
||||
#define D_JSON_SIGNAL "Signal"
|
||||
#define D_JSON_SSID "SSId"
|
||||
#define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time
|
||||
#define D_JSON_STARTED "Started"
|
||||
|
@ -208,6 +209,7 @@
|
|||
#define D_STATUS9_MARGIN "PTH"
|
||||
#define D_STATUS10_SENSOR "SNS"
|
||||
#define D_STATUS11_STATUS "STS"
|
||||
#define D_STATUS12_STATUS "STK"
|
||||
#define D_CMND_STATE "State"
|
||||
#define D_CMND_POWER "Power"
|
||||
#define D_CMND_FANSPEED "FanSpeed"
|
||||
|
@ -285,6 +287,7 @@
|
|||
#define D_CMND_SERIALSEND "SerialSend"
|
||||
#define D_CMND_SERIALDELIMITER "SerialDelimiter"
|
||||
#define D_CMND_BAUDRATE "Baudrate"
|
||||
#define D_CMND_SERIALCONFIG "SerialConfig"
|
||||
#define D_CMND_TEMPLATE "Template"
|
||||
#define D_JSON_NAME "NAME"
|
||||
#define D_JSON_GPIO "GPIO"
|
||||
|
@ -332,6 +335,7 @@
|
|||
#define D_CMND_WEBSENSOR "WebSensor"
|
||||
#define D_CMND_EMULATION "Emulation"
|
||||
#define D_CMND_SENDMAIL "Sendmail"
|
||||
#define D_CMND_CORS "CORS"
|
||||
|
||||
// Commands xdrv_03_energy.ino
|
||||
#define D_CMND_POWERLOW "PowerLow"
|
||||
|
@ -490,6 +494,24 @@
|
|||
#define D_JSON_MOTOR_MIS "setMIS"
|
||||
#endif
|
||||
|
||||
// Commands xdrv_27_Shutter.ino
|
||||
#ifdef USE_SHUTTER
|
||||
#define D_PRFX_SHUTTER "Shutter"
|
||||
#define D_CMND_SHUTTER_OPEN "Open"
|
||||
#define D_CMND_SHUTTER_CLOSE "Close"
|
||||
#define D_CMND_SHUTTER_STOP "Stop"
|
||||
#define D_CMND_SHUTTER_POSITION "Position"
|
||||
#define D_CMND_SHUTTER_OPENTIME "OpenDuration"
|
||||
#define D_CMND_SHUTTER_CLOSETIME "CloseDuration"
|
||||
#define D_CMND_SHUTTER_RELAY "Relay"
|
||||
#define D_CMND_SHUTTER_SETHALFWAY "SetHalfway"
|
||||
#define D_CMND_SHUTTER_SETCLOSE "SetClose"
|
||||
#define D_CMND_SHUTTER_INVERT "Invert"
|
||||
#define D_CMND_SHUTTER_CLIBRATION "Calibration"
|
||||
#define D_CMND_SHUTTER_MOTORDELAY "MotorDelay"
|
||||
#define D_CMND_SHUTTER_FREQUENCY "Frequency"
|
||||
#endif
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Log message prefix
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
|
||||
* Use online command Prefix to translate cmnd, stat and tele.
|
||||
*
|
||||
* Updated until v6.5.0.8
|
||||
* Updated until v7.1.2.4
|
||||
\*********************************************************************/
|
||||
|
||||
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
|
||||
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Хладна"
|
||||
#define D_COMMAND "Команда"
|
||||
#define D_CONNECTED "Свързан"
|
||||
#define D_CORS_DOMAIN "CORS домейн"
|
||||
#define D_COUNT "Брой"
|
||||
#define D_COUNTER "Брояч"
|
||||
#define D_CURRENT "Ток" // As in Voltage and Current
|
||||
|
@ -113,7 +114,7 @@
|
|||
#define D_LWT "LWT"
|
||||
#define D_MODULE "Модул"
|
||||
#define D_MQTT "MQTT"
|
||||
#define D_MULTI_PRESS "множествено натискане"
|
||||
#define D_MULTI_PRESS "неколкократно натискане"
|
||||
#define D_NOISE "Шум"
|
||||
#define D_NONE "Няма"
|
||||
#define D_OFF "Изкл."
|
||||
|
@ -284,10 +285,10 @@
|
|||
|
||||
#define D_LOGGING_PARAMETERS "Параметри на лога"
|
||||
#define D_SERIAL_LOG_LEVEL "Степен на серийния лог"
|
||||
#define D_MQTT_LOG_LEVEL "Mqtt log level"
|
||||
#define D_MQTT_LOG_LEVEL "Степен на MQTT лога"
|
||||
#define D_WEB_LOG_LEVEL "Степен на уеб лога"
|
||||
#define D_SYS_LOG_LEVEL "Степен на системния лог"
|
||||
#define D_MORE_DEBUG "Още дебъгване"
|
||||
#define D_MORE_DEBUG "Допълнителна debug информация"
|
||||
#define D_SYSLOG_HOST "Хост на системния лог"
|
||||
#define D_SYSLOG_PORT "Порт на системния лог"
|
||||
#define D_TELEMETRY_PERIOD "Период на телеметрия"
|
||||
|
@ -381,7 +382,7 @@
|
|||
|
||||
#define D_HUE "Hue"
|
||||
#define D_HUE_BRIDGE_SETUP "Настройка на Hue bridge"
|
||||
#define D_HUE_API_NOT_IMPLEMENTED "Hue API не е внедрено"
|
||||
#define D_HUE_API_NOT_IMPLEMENTED "Hue API не е внедрен"
|
||||
#define D_HUE_API "Hue API"
|
||||
#define D_HUE_POST_ARGS "Hue POST аргументи"
|
||||
#define D_3_RESPONSE_PACKETS_SENT "Изпратени са 3 пакета за отговор"
|
||||
|
@ -443,17 +444,17 @@
|
|||
#define D_ENERGY_TOTAL "Използвана енергия общо"
|
||||
|
||||
// xdrv_27_shutter.ino
|
||||
#define D_OPEN "Open"
|
||||
#define D_CLOSE "Close"
|
||||
#define D_DOMOTICZ_SHUTTER "Shutter"
|
||||
#define D_OPEN "Отворена"
|
||||
#define D_CLOSE "Затворена"
|
||||
#define D_DOMOTICZ_SHUTTER "Щора"
|
||||
|
||||
// xdrv_28_pcf8574.ino
|
||||
#define D_CONFIGURE_PCF8574 "Configure PCF8574"
|
||||
#define D_PCF8574_PARAMETERS "PCF8574 parameters"
|
||||
#define D_INVERT_PORTS "Invert Ports"
|
||||
#define D_DEVICE "Device"
|
||||
#define D_DEVICE_INPUT "Input"
|
||||
#define D_DEVICE_OUTPUT "Output"
|
||||
#define D_CONFIGURE_PCF8574 "Конфигуриране на PCF8574"
|
||||
#define D_PCF8574_PARAMETERS "PCF8574 параметри"
|
||||
#define D_INVERT_PORTS "Обърни портовете"
|
||||
#define D_DEVICE "Устройство"
|
||||
#define D_DEVICE_INPUT "Вход"
|
||||
#define D_DEVICE_OUTPUT "Изход"
|
||||
|
||||
// xsns_05_ds18b20.ino
|
||||
#define D_SENSOR_BUSY "Датчикът DS18x20 е зает"
|
||||
|
@ -674,27 +675,27 @@
|
|||
#define D_UNIT_ANGLE "°"
|
||||
|
||||
//SOLAXX1
|
||||
#define D_PV1_VOLTAGE "PV1 Voltage"
|
||||
#define D_PV1_CURRENT "PV1 Current"
|
||||
#define D_PV1_POWER "PV1 Power"
|
||||
#define D_PV2_VOLTAGE "PV2 Voltage"
|
||||
#define D_PV2_CURRENT "PV2 Current"
|
||||
#define D_PV2_POWER "PV2 Power"
|
||||
#define D_SOLAR_POWER "Solar Power"
|
||||
#define D_INVERTER_POWER "Inverter Power"
|
||||
#define D_STATUS "Status"
|
||||
#define D_WAITING "Waiting"
|
||||
#define D_CHECKING "Checking"
|
||||
#define D_WORKING "Working"
|
||||
#define D_FAILURE "Failure"
|
||||
#define D_SOLAX_ERROR_0 "No Error Code"
|
||||
#define D_SOLAX_ERROR_1 "Grid Lost Fault"
|
||||
#define D_SOLAX_ERROR_2 "Grid Voltage Fault"
|
||||
#define D_SOLAX_ERROR_3 "Grid Frequency Fault"
|
||||
#define D_SOLAX_ERROR_4 "Pv Voltage Fault"
|
||||
#define D_SOLAX_ERROR_5 "Isolation Fault"
|
||||
#define D_SOLAX_ERROR_6 "Over Temperature Fault"
|
||||
#define D_SOLAX_ERROR_7 "Fan Fault"
|
||||
#define D_SOLAX_ERROR_8 "Other Device Fault"
|
||||
#define D_PV1_VOLTAGE "Напрежение на PV1"
|
||||
#define D_PV1_CURRENT "Ток на PV1"
|
||||
#define D_PV1_POWER "Мощност на PV1"
|
||||
#define D_PV2_VOLTAGE "Напрежение на PV2"
|
||||
#define D_PV2_CURRENT "Ток на PV2"
|
||||
#define D_PV2_POWER "Мощност на PV2"
|
||||
#define D_SOLAR_POWER "Слънчева мощност"
|
||||
#define D_INVERTER_POWER "Мощност на инвертера"
|
||||
#define D_STATUS "Състояние"
|
||||
#define D_WAITING "Очакване"
|
||||
#define D_CHECKING "Проверка"
|
||||
#define D_WORKING "Работи"
|
||||
#define D_FAILURE "Грешка"
|
||||
#define D_SOLAX_ERROR_0 "Грешка - няма код"
|
||||
#define D_SOLAX_ERROR_1 "Грешка - загуба на мрежата"
|
||||
#define D_SOLAX_ERROR_2 "Грешка - мрежово напрежение"
|
||||
#define D_SOLAX_ERROR_3 "Грешка - мрежова честота"
|
||||
#define D_SOLAX_ERROR_4 "Грешка - напрежение на Pv"
|
||||
#define D_SOLAX_ERROR_5 "Грешка - проблем с изолацията"
|
||||
#define D_SOLAX_ERROR_6 "Грешка - прегряване"
|
||||
#define D_SOLAX_ERROR_7 "Грешка - вентилатор"
|
||||
#define D_SOLAX_ERROR_8 "Грешка - друго оборудване"
|
||||
|
||||
#endif // _LANGUAGE_BG_BG_H_
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Studené světlo"
|
||||
#define D_COMMAND "Příkaz"
|
||||
#define D_CONNECTED "...připojeno"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Počítej"
|
||||
#define D_COUNTER "Počítadlo"
|
||||
#define D_CURRENT "Proud" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "kalt"
|
||||
#define D_COMMAND "Befehl"
|
||||
#define D_CONNECTED "verbunden"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "zählen"
|
||||
#define D_COUNTER "Zähler"
|
||||
#define D_CURRENT "Strom" // As in Voltage and Current
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#define D_COMMAND "Εντολή"
|
||||
#define D_CONNECTED "Συνδεδεμένο"
|
||||
#define D_COUNT "Μέτρηση"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNTER "Μετρητής"
|
||||
#define D_CURRENT "Ένταση" // As in Voltage and Current
|
||||
#define D_DATA "Δεδομένα"
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Cold"
|
||||
#define D_COMMAND "Command"
|
||||
#define D_CONNECTED "Connected"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Count"
|
||||
#define D_COUNTER "Counter"
|
||||
#define D_CURRENT "Current" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Fría"
|
||||
#define D_COMMAND "Comando"
|
||||
#define D_CONNECTED "Conectado"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Conteo"
|
||||
#define D_COUNTER "Contador"
|
||||
#define D_CURRENT "Corriente" // As in Voltage and Current
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
|
||||
* Use online command Prefix to translate cmnd, stat and tele.
|
||||
*
|
||||
* Updated until v6.6.0.15
|
||||
* Updated until v7.1.2.4
|
||||
\*********************************************************************/
|
||||
|
||||
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
|
||||
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Froid"
|
||||
#define D_COMMAND "Commande"
|
||||
#define D_CONNECTED "Connecté"
|
||||
#define D_CORS_DOMAIN "Domaine CORS"
|
||||
#define D_COUNT "Compte"
|
||||
#define D_COUNTER "Compteur"
|
||||
#define D_CURRENT "Courant" // As in Voltage and Current
|
||||
|
@ -280,7 +281,7 @@
|
|||
|
||||
#define D_MQTT_PARAMETERS "Paramètres MQTT"
|
||||
#define D_CLIENT "Client"
|
||||
#define D_FULL_TOPIC "topic complet"
|
||||
#define D_FULL_TOPIC "Topic complet"
|
||||
|
||||
#define D_LOGGING_PARAMETERS "Paramètres du journal"
|
||||
#define D_SERIAL_LOG_LEVEL "Niveau de journalisation série"
|
||||
|
@ -687,7 +688,7 @@
|
|||
#define D_CHECKING "En test"
|
||||
#define D_WORKING "En marche"
|
||||
#define D_FAILURE "Défault"
|
||||
#define D_SOLAX_ERROR_0 "Aucun Code d'erreur"
|
||||
#define D_SOLAX_ERROR_0 "Aucun code d'erreur"
|
||||
#define D_SOLAX_ERROR_1 "Défaut Perte de réseau"
|
||||
#define D_SOLAX_ERROR_2 "Défaut Tension réseau"
|
||||
#define D_SOLAX_ERROR_3 "Défaut Fréquence réseau"
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "אור קר"
|
||||
#define D_COMMAND "פקודה"
|
||||
#define D_CONNECTED "מחובר"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "סופר"
|
||||
#define D_COUNTER "מונה"
|
||||
#define D_CURRENT "נוכחי" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Hideg fény"
|
||||
#define D_COMMAND "Parancs"
|
||||
#define D_CONNECTED "Csatlakoztatva"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Szám"
|
||||
#define D_COUNTER "Számláló"
|
||||
#define D_CURRENT "Áramerősség" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Fredda"
|
||||
#define D_COMMAND "Comando"
|
||||
#define D_CONNECTED "Connesso"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Conteggio"
|
||||
#define D_COUNTER "Contatore"
|
||||
#define D_CURRENT "Corrente" // As in Voltage and Current
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#define D_COMMAND "커맨드"
|
||||
#define D_CONNECTED "연결됨"
|
||||
#define D_COUNT "횟수"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNTER "Counter"
|
||||
#define D_CURRENT "전류" // As in Voltage and Current
|
||||
#define D_DATA "Data"
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#define D_COMMAND "Opdracht"
|
||||
#define D_CONNECTED "Verbonden"
|
||||
#define D_COUNT "Aantal"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNTER "Teller"
|
||||
#define D_CURRENT "Stroom" // As in Voltage and Current
|
||||
#define D_DATA "Data"
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Zimny"
|
||||
#define D_COMMAND "Komenda"
|
||||
#define D_CONNECTED "Połączony"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Licz"
|
||||
#define D_COUNTER "Licznik"
|
||||
#define D_CURRENT "Prąd" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Luz fria"
|
||||
#define D_COMMAND "Comando"
|
||||
#define D_CONNECTED "Ligado"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Contagem"
|
||||
#define D_COUNTER "Contador"
|
||||
#define D_CURRENT "Corrente" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Luz Fria"
|
||||
#define D_COMMAND "Comando"
|
||||
#define D_CONNECTED "Ligado"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Contagem"
|
||||
#define D_COUNTER "Contador"
|
||||
#define D_CURRENT "Corrente" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Холодный"
|
||||
#define D_COMMAND "Команда"
|
||||
#define D_CONNECTED "Соединен"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Подсчет"
|
||||
#define D_COUNTER "Счетчик"
|
||||
#define D_CURRENT "Ток" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Studené svetlo"
|
||||
#define D_COMMAND "Príkaz"
|
||||
#define D_CONNECTED "...pripojené"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Počítaj"
|
||||
#define D_COUNTER "Počítadlo"
|
||||
#define D_CURRENT "Prúd" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Kallt"
|
||||
#define D_COMMAND "Kommando"
|
||||
#define D_CONNECTED "Ansluten"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Räkna"
|
||||
#define D_COUNTER "Räknare"
|
||||
#define D_CURRENT "Ström" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Soğuk"
|
||||
#define D_COMMAND "Komut"
|
||||
#define D_CONNECTED "Bağlandı"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Sayı"
|
||||
#define D_COUNTER "Sayaç"
|
||||
#define D_CURRENT "Current" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "Холодний"
|
||||
#define D_COMMAND "Команда"
|
||||
#define D_CONNECTED "Під'єднано"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "Розмір"
|
||||
#define D_COUNTER "Лічильник"
|
||||
#define D_CURRENT "Струм" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "冷"
|
||||
#define D_COMMAND "命令:"
|
||||
#define D_CONNECTED "已连接"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "数量:"
|
||||
#define D_COUNTER "计数器"
|
||||
#define D_CURRENT "电流" // As in Voltage and Current
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#define D_COLDLIGHT "冷"
|
||||
#define D_COMMAND "命令:"
|
||||
#define D_CONNECTED "已連接"
|
||||
#define D_CORS_DOMAIN "CORS Domain"
|
||||
#define D_COUNT "數量:"
|
||||
#define D_COUNTER "Counter"
|
||||
#define D_CURRENT "電流" // As in Voltage and Current
|
||||
|
|
|
@ -132,6 +132,7 @@
|
|||
#define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable)
|
||||
#define FRIENDLY_NAME "Tasmota" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa
|
||||
#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE)
|
||||
#define CORS_DOMAIN "" // [Cors] CORS Domain for preflight requests
|
||||
|
||||
// -- HTTP GUI Colors -----------------------------
|
||||
// HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp
|
||||
|
@ -219,7 +220,7 @@
|
|||
#define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1
|
||||
#define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000)
|
||||
#define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec),
|
||||
#define PWM_MAX_SLEEP 10 // Sleep will be lowered to this value when light is on, to avoid flickering
|
||||
#define PWM_MAX_SLEEP 10 // Sleep will be lowered to this value when light is on, to avoid flickering
|
||||
|
||||
#define KEY_DEBOUNCE_TIME 50 // [ButtonDebounce] Number of mSeconds button press debounce time
|
||||
#define KEY_HOLD_TIME 40 // [SetOption32] Number of 0.1 seconds to hold Button or external Pushbutton before sending HOLD message
|
||||
|
@ -284,14 +285,14 @@
|
|||
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // Domoticz Output Topic
|
||||
|
||||
// -- MQTT - Home Assistant Discovery -------------
|
||||
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code)
|
||||
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+4.1k code, +6 bytes mem)
|
||||
#define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix
|
||||
|
||||
// -- MQTT - TLS - AWS IoT ------------------------
|
||||
// Using TLS starting with version v6.5.0.16 compilation will only work using Core 2.4.2 and 2.5.2. No longer supported: 2.3.0
|
||||
//#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
// #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake)
|
||||
// This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
|
||||
// This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
|
||||
// #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
|
||||
// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
|
||||
// Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp'
|
||||
|
|
|
@ -86,7 +86,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
|||
uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF
|
||||
uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531)
|
||||
uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable hardware energy total counter as reference (#6561)
|
||||
uint32_t cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS
|
||||
uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS
|
||||
uint32_t ds18x20_internal_pullup : 1; // bit 24 (v7.0.0.1) - SetOption74 - Enable internal pullup for single DS18x20 sensor
|
||||
uint32_t grouptopic_mode : 1; // bit 25 (v7.0.0.1) - SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
|
||||
uint32_t bootcount_update : 1; // bit 26 (v7.0.0.4) - SetOption76 - Enable incrementing bootcount when deepsleep is enabled
|
||||
|
@ -248,42 +248,54 @@ struct SYSCFG {
|
|||
SysBitfield flag; // 010
|
||||
int16_t save_data; // 014
|
||||
int8_t timezone; // 016
|
||||
|
||||
// Start of single char array Settings.text
|
||||
|
||||
char ota_url[101]; // 017
|
||||
char mqtt_prefix[3][11]; // 07C
|
||||
|
||||
uint8_t ex_baudrate; // 09D - Free since 6.6.0.9
|
||||
uint8_t seriallog_level; // 09E
|
||||
uint8_t sta_config; // 09F
|
||||
uint8_t sta_active; // 0A0
|
||||
uint8_t ex_seriallog_level; // 09E
|
||||
uint8_t ex_sta_config; // 09F
|
||||
uint8_t ex_sta_active; // 0A0
|
||||
|
||||
char sta_ssid[2][33]; // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 5
|
||||
char sta_pwd[2][65]; // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 5
|
||||
char hostname[33]; // 165
|
||||
char syslog_host[33]; // 186
|
||||
uint8_t rule_stop; // 1A7
|
||||
uint16_t syslog_port; // 1A8
|
||||
uint8_t syslog_level; // 1AA
|
||||
uint8_t webserver; // 1AB
|
||||
uint8_t weblog_level; // 1AC
|
||||
uint8_t mqtt_fingerprint[2][20]; // 1AD
|
||||
uint8_t adc_param_type; // 1D5
|
||||
|
||||
uint8_t free_1d6[10]; // 1D6
|
||||
uint8_t ex_rule_stop; // 1A7
|
||||
uint16_t ex_syslog_port; // 1A8
|
||||
uint8_t ex_syslog_level; // 1AA
|
||||
uint8_t ex_webserver; // 1AB
|
||||
uint8_t ex_weblog_level; // 1AC
|
||||
uint8_t ex_mqtt_fingerprint[2][20]; // 1AD
|
||||
uint8_t ex_adc_param_type; // 1D5
|
||||
|
||||
SysBitfield4 flag4; // 1E0
|
||||
uint8_t ex_free_1d6[10]; // 1D6
|
||||
|
||||
uint8_t free_1e4; // 1E4
|
||||
// End of single char array of 456 chars max (phase 3)
|
||||
|
||||
SysBitfield4 ex_flag4; // 1E0
|
||||
uint8_t ex_serial_config; // 1E4
|
||||
uint8_t ex_wifi_output_power; // 1E5
|
||||
uint8_t ex_shutter_accuracy; // 1E6
|
||||
uint8_t ex_mqttlog_level; // 1E7
|
||||
uint8_t ex_sps30_inuse_hours; // 1E8
|
||||
|
||||
uint8_t wifi_output_power; // 1E5
|
||||
uint8_t shutter_accuracy; // 1E6
|
||||
uint8_t mqttlog_level; // 1E7
|
||||
uint8_t sps30_inuse_hours; // 1E8
|
||||
char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6
|
||||
uint16_t mqtt_port; // 20A - Keep together
|
||||
|
||||
uint16_t ex_mqtt_port; // 20A - Keep together
|
||||
|
||||
char mqtt_client[33]; // 20C - Keep together
|
||||
char mqtt_user[33]; // 22D - Keep together
|
||||
char mqtt_pwd[33]; // 24E - Keep together
|
||||
char mqtt_topic[33]; // 26F - Keep together with above items as being copied as one chunck with reset 6
|
||||
char button_topic[33]; // 290
|
||||
char mqtt_grptopic[33]; // 2B1
|
||||
|
||||
// Optional end of single char array of 698 chars max (phase 5)
|
||||
|
||||
uint8_t display_model; // 2D2
|
||||
uint8_t display_mode; // 2D3
|
||||
uint8_t display_refresh; // 2D4
|
||||
|
@ -304,7 +316,9 @@ struct SYSCFG {
|
|||
int16_t toffset[2]; // 30E
|
||||
uint8_t display_font; // 312
|
||||
char state_text[4][11]; // 313
|
||||
|
||||
uint8_t ex_energy_power_delta; // 33F - Free since 6.6.0.20
|
||||
|
||||
uint16_t domoticz_update_timer; // 340
|
||||
uint16_t pwm_range; // 342
|
||||
unsigned long domoticz_relay_idx[MAX_DOMOTICZ_IDX]; // 344
|
||||
|
@ -340,7 +354,7 @@ struct SYSCFG {
|
|||
char friendlyname[MAX_FRIENDLYNAMES][33]; // 3AC
|
||||
char switch_topic[33]; // 430
|
||||
char serial_delimiter; // 451
|
||||
uint8_t ex_sbaudrate; // 452 - Free since 6.6.0.9
|
||||
uint8_t seriallog_level; // 452
|
||||
uint8_t sleep; // 453
|
||||
uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454
|
||||
uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C
|
||||
|
@ -400,7 +414,6 @@ struct SYSCFG {
|
|||
uint16_t baudrate; // 778
|
||||
uint16_t sbaudrate; // 77A
|
||||
EnergyUsage energy_usage; // 77C
|
||||
// uint32_t drivers[3]; // 794 - 6.5.0.12 replaced by below three entries
|
||||
uint32_t adc_param1; // 794
|
||||
uint32_t adc_param2; // 798
|
||||
int adc_param3; // 79C
|
||||
|
@ -416,7 +429,9 @@ struct SYSCFG {
|
|||
unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight
|
||||
uint16_t web_refresh; // 7CC
|
||||
char mems[MAX_RULE_MEMS][10]; // 7CE
|
||||
|
||||
char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b
|
||||
|
||||
TuyaFnidDpidMap tuya_fnid_map[MAX_TUYA_FUNCTIONS]; // E00 32 bytes
|
||||
uint16_t ina226_r_shunt[4]; // E20
|
||||
uint16_t ina226_i_fs[4]; // E28
|
||||
|
@ -435,12 +450,27 @@ struct SYSCFG {
|
|||
uint16_t energy_power_delta; // E98
|
||||
uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A
|
||||
int8_t temp_comp; // E9E
|
||||
|
||||
uint8_t free_e9f[1]; // E9F
|
||||
|
||||
uint8_t weight_change; // E9F
|
||||
uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color
|
||||
char cors_domain[33]; // EA6
|
||||
uint8_t sta_config; // EC7
|
||||
uint8_t sta_active; // EC8
|
||||
uint8_t rule_stop; // EC9
|
||||
uint16_t syslog_port; // ECA
|
||||
uint8_t syslog_level; // ECC
|
||||
uint8_t webserver; // ECD
|
||||
uint8_t weblog_level; // ECE
|
||||
uint8_t mqtt_fingerprint[2][20]; // ECF
|
||||
uint8_t adc_param_type; // EF7
|
||||
SysBitfield4 flag4; // EF8
|
||||
uint16_t mqtt_port; // EFC
|
||||
uint8_t serial_config; // EFE
|
||||
uint8_t wifi_output_power; // EFF
|
||||
uint8_t shutter_accuracy; // F00
|
||||
uint8_t mqttlog_level; // F01
|
||||
uint8_t sps30_inuse_hours; // F02
|
||||
|
||||
uint8_t free_ea4[326]; // EA6
|
||||
uint8_t free_f03[233]; // F03
|
||||
|
||||
uint32_t i2c_drivers[3]; // FEC I2cDriver
|
||||
uint32_t cfg_timestamp; // FF8
|
||||
|
|
|
@ -140,6 +140,10 @@
|
|||
#ifndef DEFAULT_LIGHT_COMPONENT
|
||||
#define DEFAULT_LIGHT_COMPONENT 255
|
||||
#endif
|
||||
#ifndef CORS_ENABLED_ALL
|
||||
#define CORS_ENABLED_ALL "*"
|
||||
#endif
|
||||
|
||||
|
||||
enum WebColors {
|
||||
COL_TEXT, COL_BACKGROUND, COL_FORM,
|
||||
|
@ -156,6 +160,23 @@ const char kWebColors[] PROGMEM =
|
|||
COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|"
|
||||
COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND "|" COLOR_TITLE_TEXT;
|
||||
|
||||
enum TasmotaSerialConfig {
|
||||
TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1,
|
||||
TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2,
|
||||
TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1,
|
||||
TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2,
|
||||
TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1,
|
||||
TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 };
|
||||
|
||||
const uint8_t kTasmotaSerialConfig[] PROGMEM = {
|
||||
SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1,
|
||||
SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2,
|
||||
SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1,
|
||||
SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2,
|
||||
SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1,
|
||||
SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2
|
||||
};
|
||||
|
||||
/*********************************************************************************************\
|
||||
* RTC memory
|
||||
\*********************************************************************************************/
|
||||
|
@ -421,6 +442,197 @@ void UpdateQuickPowerCycle(bool update)
|
|||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Config Settings.text char array support
|
||||
\*********************************************************************************************/
|
||||
|
||||
char aws_mqtt_host[66];
|
||||
char aws_mqtt_user[1] { 0 };
|
||||
|
||||
const uint32_t settings_text_size = 457; // Settings.flag4 (1E0) - Settings.ota_url (017)
|
||||
|
||||
uint32_t GetSettingsTextLen(void)
|
||||
{
|
||||
char* position = Settings.ota_url;
|
||||
for (uint32_t size = 0; size < SET_MAX; size++) {
|
||||
while (*position++ != '\0') { }
|
||||
}
|
||||
return position - Settings.ota_url;
|
||||
}
|
||||
|
||||
bool SettingsUpdateText(uint32_t index, const char* replace_me)
|
||||
{
|
||||
if (index >= SET_MAX) {
|
||||
return false; // Setting not supported - internal error
|
||||
}
|
||||
|
||||
// Make a copy first in case we use source from Settings.text
|
||||
uint32_t replace_len = strlen(replace_me);
|
||||
char replace[replace_len +1];
|
||||
memcpy(replace, replace_me, sizeof(replace));
|
||||
|
||||
if (Settings.version < 0x08000000) {
|
||||
uint32_t idx = 0;
|
||||
switch (index) {
|
||||
case SET_OTAURL: strlcpy(Settings.ota_url, replace, sizeof(Settings.ota_url)); break;
|
||||
case SET_MQTTPREFIX3: idx++;
|
||||
case SET_MQTTPREFIX2: idx++;
|
||||
case SET_MQTTPREFIX1: strlcpy(Settings.mqtt_prefix[idx], replace, sizeof(Settings.mqtt_prefix[idx])); break;
|
||||
case SET_STASSID2: idx++;
|
||||
case SET_STASSID1: strlcpy(Settings.sta_ssid[idx], replace, sizeof(Settings.sta_ssid[idx])); break;
|
||||
case SET_STAPWD2: idx++;
|
||||
case SET_STAPWD1: strlcpy(Settings.sta_pwd[idx], replace, sizeof(Settings.sta_pwd[idx])); break;
|
||||
case SET_HOSTNAME: strlcpy(Settings.hostname, replace, sizeof(Settings.hostname)); break;
|
||||
case SET_SYSLOG_HOST: strlcpy(Settings.syslog_host, replace, sizeof(Settings.syslog_host)); break;
|
||||
case SET_WEBPWD: strlcpy(Settings.web_password, replace, sizeof(Settings.web_password)); break;
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
case SET_MQTT_HOST:
|
||||
if (strlen(replace) <= sizeof(Settings.mqtt_host)) {
|
||||
strlcpy(Settings.mqtt_host, replace, sizeof(Settings.mqtt_host));
|
||||
Settings.mqtt_user[0] = 0;
|
||||
} else {
|
||||
// need to split in mqtt_user first then mqtt_host
|
||||
strlcpy(Settings.mqtt_user, replace, sizeof(Settings.mqtt_user));
|
||||
strlcpy(Settings.mqtt_host, &replace[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host));
|
||||
}
|
||||
break;
|
||||
case SET_MQTT_USER: break;
|
||||
#else
|
||||
case SET_MQTT_HOST: strlcpy(Settings.mqtt_host, replace, sizeof(Settings.mqtt_host)); break;
|
||||
case SET_MQTT_USER: strlcpy(Settings.mqtt_user, replace, sizeof(Settings.mqtt_user)); break;
|
||||
#endif
|
||||
case SET_MQTT_CLIENT: strlcpy(Settings.mqtt_client, replace, sizeof(Settings.mqtt_client)); break;
|
||||
case SET_MQTT_PWD: strlcpy(Settings.mqtt_pwd, replace, sizeof(Settings.mqtt_pwd)); break;
|
||||
case SET_MQTT_FULLTOPIC: strlcpy(Settings.mqtt_fulltopic, replace, sizeof(Settings.mqtt_fulltopic)); break;
|
||||
case SET_MQTT_TOPIC: strlcpy(Settings.mqtt_topic, replace, sizeof(Settings.mqtt_topic)); break;
|
||||
case SET_MQTT_BUTTON_TOPIC: strlcpy(Settings.button_topic, replace, sizeof(Settings.button_topic)); break;
|
||||
case SET_MQTT_SWITCH_TOPIC: strlcpy(Settings.switch_topic, replace, sizeof(Settings.switch_topic)); break;
|
||||
case SET_MQTT_GRP_TOPIC: strlcpy(Settings.mqtt_grptopic, replace, sizeof(Settings.mqtt_grptopic)); break;
|
||||
case SET_STATE_TXT4: idx++;
|
||||
case SET_STATE_TXT3: idx++;
|
||||
case SET_STATE_TXT2: idx++;
|
||||
case SET_STATE_TXT1: strlcpy(Settings.state_text[idx], replace, sizeof(Settings.state_text[idx])); break;
|
||||
case SET_NTPSERVER3: idx++;
|
||||
case SET_NTPSERVER2: idx++;
|
||||
case SET_NTPSERVER1: strlcpy(Settings.ntp_server[idx], replace, sizeof(Settings.ntp_server[idx])); break;
|
||||
case SET_MEM5: idx++;
|
||||
case SET_MEM4: idx++;
|
||||
case SET_MEM3: idx++;
|
||||
case SET_MEM2: idx++;
|
||||
case SET_MEM1: strlcpy(Settings.mems[idx], replace, sizeof(Settings.mems[idx])); break;
|
||||
case SET_CORS: strlcpy(Settings.cors_domain, replace, sizeof(Settings.cors_domain)); break;
|
||||
case SET_FRIENDLYNAME4: idx++;
|
||||
case SET_FRIENDLYNAME3: idx++;
|
||||
case SET_FRIENDLYNAME2: idx++;
|
||||
case SET_FRIENDLYNAME1: strlcpy(Settings.friendlyname[idx], replace, sizeof(Settings.friendlyname[idx])); break;
|
||||
}
|
||||
} else {
|
||||
uint32_t start_pos = 0;
|
||||
uint32_t end_pos = 0;
|
||||
char* position = Settings.ota_url;
|
||||
for (uint32_t size = 0; size < SET_MAX; size++) {
|
||||
while (*position++ != '\0') { }
|
||||
if (1 == index) {
|
||||
start_pos = position - Settings.ota_url;
|
||||
}
|
||||
else if (0 == index) {
|
||||
end_pos = position - Settings.ota_url -1;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
uint32_t char_len = position - Settings.ota_url;
|
||||
|
||||
uint32_t current_len = end_pos - start_pos;
|
||||
int diff = replace_len - current_len;
|
||||
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"),
|
||||
// start_pos, end_pos, char_len, current_len, replace_len, diff);
|
||||
|
||||
int too_long = (char_len + diff) - settings_text_size;
|
||||
if (too_long > 0) {
|
||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR("CFG: Text too long by %d char(s)"), too_long);
|
||||
return false; // Replace text too long
|
||||
}
|
||||
|
||||
if (diff != 0) {
|
||||
// Shift Settings.text up or down
|
||||
memmove_P(Settings.ota_url + start_pos + replace_len, Settings.ota_url + end_pos, char_len - end_pos);
|
||||
}
|
||||
// Replace text
|
||||
memmove_P(Settings.ota_url + start_pos, replace, replace_len);
|
||||
// Fill for future use
|
||||
memset(Settings.ota_url + char_len + diff, 0x00, settings_text_size - char_len - diff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char* SettingsText(uint32_t index)
|
||||
{
|
||||
if (index >= SET_MAX) {
|
||||
return nullptr; // Setting not supported - internal error
|
||||
}
|
||||
|
||||
char* position = Settings.ota_url;
|
||||
|
||||
if (Settings.version < 0x08000000) {
|
||||
uint32_t idx = 0;
|
||||
switch (index) {
|
||||
case SET_MQTTPREFIX3: idx++;
|
||||
case SET_MQTTPREFIX2: idx++;
|
||||
case SET_MQTTPREFIX1: position = Settings.mqtt_prefix[idx]; break;
|
||||
case SET_STASSID2: idx++;
|
||||
case SET_STASSID1: position = Settings.sta_ssid[idx]; break;
|
||||
case SET_STAPWD2: idx++;
|
||||
case SET_STAPWD1: position = Settings.sta_pwd[idx]; break;
|
||||
case SET_HOSTNAME: position = Settings.hostname; break;
|
||||
case SET_SYSLOG_HOST: position = Settings.syslog_host; break;
|
||||
case SET_WEBPWD: position = Settings.web_password; break;
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
case SET_MQTT_HOST:
|
||||
strlcpy(aws_mqtt_host, Settings.mqtt_user, strlen(Settings.mqtt_user));
|
||||
strlcpy(&aws_mqtt_host[strlen(Settings.mqtt_user)], Settings.mqtt_host, sizeof(Settings.mqtt_host));
|
||||
position = aws_mqtt_host; break;
|
||||
case SET_MQTT_USER: position = aws_mqtt_user; break;
|
||||
#else
|
||||
case SET_MQTT_HOST: position = Settings.mqtt_host; break;
|
||||
case SET_MQTT_USER: position = Settings.mqtt_user; break;
|
||||
#endif
|
||||
case SET_MQTT_CLIENT: position = Settings.mqtt_client; break;
|
||||
case SET_MQTT_PWD: position = Settings.mqtt_pwd; break;
|
||||
case SET_MQTT_FULLTOPIC: position = Settings.mqtt_fulltopic; break;
|
||||
case SET_MQTT_TOPIC: position = Settings.mqtt_topic; break;
|
||||
case SET_MQTT_BUTTON_TOPIC: position = Settings.button_topic; break;
|
||||
case SET_MQTT_SWITCH_TOPIC: position = Settings.switch_topic; break;
|
||||
case SET_MQTT_GRP_TOPIC: position = Settings.mqtt_grptopic; break;
|
||||
case SET_STATE_TXT4: idx++;
|
||||
case SET_STATE_TXT3: idx++;
|
||||
case SET_STATE_TXT2: idx++;
|
||||
case SET_STATE_TXT1: position = Settings.state_text[idx]; break;
|
||||
case SET_NTPSERVER3: idx++;
|
||||
case SET_NTPSERVER2: idx++;
|
||||
case SET_NTPSERVER1: position = Settings.ntp_server[idx]; break;
|
||||
case SET_MEM5: idx++;
|
||||
case SET_MEM4: idx++;
|
||||
case SET_MEM3: idx++;
|
||||
case SET_MEM2: idx++;
|
||||
case SET_MEM1: position = Settings.mems[idx]; break;
|
||||
case SET_CORS: position = Settings.cors_domain; break;
|
||||
case SET_FRIENDLYNAME4: idx++;
|
||||
case SET_FRIENDLYNAME3: idx++;
|
||||
case SET_FRIENDLYNAME2: idx++;
|
||||
case SET_FRIENDLYNAME1: position = Settings.friendlyname[idx]; break;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (;index > 0; index--) {
|
||||
while (*position++ != '\0') { }
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Config Save - Save parameters to Flash ONLY if any parameter has changed
|
||||
\*********************************************************************************************/
|
||||
|
@ -652,11 +864,11 @@ void SettingsDefaultSet2(void)
|
|||
Settings.module = MODULE;
|
||||
ModuleDefault(WEMOS);
|
||||
// for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; }
|
||||
strlcpy(Settings.friendlyname[0], FRIENDLY_NAME, sizeof(Settings.friendlyname[0]));
|
||||
strlcpy(Settings.friendlyname[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1]));
|
||||
strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2]));
|
||||
strlcpy(Settings.friendlyname[3], FRIENDLY_NAME"4", sizeof(Settings.friendlyname[3]));
|
||||
strlcpy(Settings.ota_url, OTA_URL, sizeof(Settings.ota_url));
|
||||
SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME);
|
||||
SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2");
|
||||
SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3");
|
||||
SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4");
|
||||
SettingsUpdateText(SET_OTAURL, OTA_URL);
|
||||
|
||||
// Power
|
||||
Settings.flag.save_state = SAVE_STATE;
|
||||
|
@ -670,6 +882,7 @@ void SettingsDefaultSet2(void)
|
|||
// for (uint32_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; }
|
||||
|
||||
// Serial
|
||||
Settings.serial_config = TS_SERIAL_8N1;
|
||||
Settings.baudrate = APP_BAUDRATE / 300;
|
||||
Settings.sbaudrate = SOFT_BAUDRATE / 300;
|
||||
Settings.serial_delimiter = 0xff;
|
||||
|
@ -683,14 +896,14 @@ void SettingsDefaultSet2(void)
|
|||
ParseIp(&Settings.ip_address[3], WIFI_DNS);
|
||||
Settings.sta_config = WIFI_CONFIG_TOOL;
|
||||
// Settings.sta_active = 0;
|
||||
strlcpy(Settings.sta_ssid[0], STA_SSID1, sizeof(Settings.sta_ssid[0]));
|
||||
strlcpy(Settings.sta_pwd[0], STA_PASS1, sizeof(Settings.sta_pwd[0]));
|
||||
strlcpy(Settings.sta_ssid[1], STA_SSID2, sizeof(Settings.sta_ssid[1]));
|
||||
strlcpy(Settings.sta_pwd[1], STA_PASS2, sizeof(Settings.sta_pwd[1]));
|
||||
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
|
||||
SettingsUpdateText(SET_STASSID1, STA_SSID1);
|
||||
SettingsUpdateText(SET_STASSID2, STA_SSID2);
|
||||
SettingsUpdateText(SET_STAPWD1, STA_PASS1);
|
||||
SettingsUpdateText(SET_STAPWD2, STA_PASS2);
|
||||
SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME);
|
||||
|
||||
// Syslog
|
||||
strlcpy(Settings.syslog_host, SYS_LOG_HOST, sizeof(Settings.syslog_host));
|
||||
SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST);
|
||||
Settings.syslog_port = SYS_LOG_PORT;
|
||||
Settings.syslog_level = SYS_LOG_LEVEL;
|
||||
|
||||
|
@ -698,8 +911,9 @@ void SettingsDefaultSet2(void)
|
|||
Settings.flag2.emulation = EMULATION;
|
||||
Settings.webserver = WEB_SERVER;
|
||||
Settings.weblog_level = WEB_LOG_LEVEL;
|
||||
strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password));
|
||||
SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD);
|
||||
Settings.flag3.mdns_enabled = MDNS_ENABLED;
|
||||
SettingsUpdateText(SET_CORS, CORS_DOMAIN);
|
||||
|
||||
// Button
|
||||
// Settings.flag.button_restrict = 0;
|
||||
|
@ -722,24 +936,24 @@ void SettingsDefaultSet2(void)
|
|||
// Settings.flag.mqtt_offline = 0;
|
||||
// Settings.flag.mqtt_serial = 0;
|
||||
// Settings.flag.device_index_enable = 0;
|
||||
strlcpy(Settings.mqtt_host, MQTT_HOST, sizeof(Settings.mqtt_host));
|
||||
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
|
||||
Settings.mqtt_port = MQTT_PORT;
|
||||
strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client));
|
||||
strlcpy(Settings.mqtt_user, MQTT_USER, sizeof(Settings.mqtt_user));
|
||||
strlcpy(Settings.mqtt_pwd, MQTT_PASS, sizeof(Settings.mqtt_pwd));
|
||||
strlcpy(Settings.mqtt_topic, MQTT_TOPIC, sizeof(Settings.mqtt_topic));
|
||||
strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic));
|
||||
strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic));
|
||||
strlcpy(Settings.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(Settings.mqtt_grptopic));
|
||||
strlcpy(Settings.mqtt_fulltopic, MQTT_FULLTOPIC, sizeof(Settings.mqtt_fulltopic));
|
||||
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;
|
||||
strlcpy(Settings.mqtt_prefix[0], SUB_PREFIX, sizeof(Settings.mqtt_prefix[0]));
|
||||
strlcpy(Settings.mqtt_prefix[1], PUB_PREFIX, sizeof(Settings.mqtt_prefix[1]));
|
||||
strlcpy(Settings.mqtt_prefix[2], PUB_PREFIX2, sizeof(Settings.mqtt_prefix[2]));
|
||||
strlcpy(Settings.state_text[0], MQTT_STATUS_OFF, sizeof(Settings.state_text[0]));
|
||||
strlcpy(Settings.state_text[1], MQTT_STATUS_ON, sizeof(Settings.state_text[1]));
|
||||
strlcpy(Settings.state_text[2], MQTT_CMND_TOGGLE, sizeof(Settings.state_text[2]));
|
||||
strlcpy(Settings.state_text[3], MQTT_CMND_HOLD, sizeof(Settings.state_text[3]));
|
||||
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);
|
||||
char fingerprint[60];
|
||||
strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint));
|
||||
char *p = fingerprint;
|
||||
|
@ -891,15 +1105,11 @@ void SettingsDefaultSet2(void)
|
|||
Settings.timezone = APP_TIMEZONE / 60;
|
||||
Settings.timezone_minutes = abs(APP_TIMEZONE % 60);
|
||||
}
|
||||
strlcpy(Settings.ntp_server[0], NTP_SERVER1, sizeof(Settings.ntp_server[0]));
|
||||
strlcpy(Settings.ntp_server[1], NTP_SERVER2, sizeof(Settings.ntp_server[1]));
|
||||
strlcpy(Settings.ntp_server[2], NTP_SERVER3, sizeof(Settings.ntp_server[2]));
|
||||
for (uint32_t j = 0; j < 3; j++) {
|
||||
for (uint32_t i = 0; i < strlen(Settings.ntp_server[j]); i++) {
|
||||
if (Settings.ntp_server[j][i] == ',') {
|
||||
Settings.ntp_server[j][i] = '.';
|
||||
}
|
||||
}
|
||||
SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1);
|
||||
SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2);
|
||||
SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3);
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i)));
|
||||
}
|
||||
Settings.latitude = (int)((double)LATITUDE * 1000000);
|
||||
Settings.longitude = (int)((double)LONGITUDE * 1000000);
|
||||
|
@ -1059,8 +1269,8 @@ void SettingsDelta(void)
|
|||
}
|
||||
}
|
||||
if (Settings.version < 0x06060009) {
|
||||
Settings.baudrate = Settings.ex_baudrate * 4;
|
||||
Settings.sbaudrate = Settings.ex_sbaudrate * 4;
|
||||
Settings.baudrate = APP_BAUDRATE / 300;
|
||||
Settings.sbaudrate = SOFT_BAUDRATE / 300;
|
||||
}
|
||||
if (Settings.version < 0x0606000A) {
|
||||
uint8_t tuyaindex = 0;
|
||||
|
@ -1155,6 +1365,54 @@ void SettingsDelta(void)
|
|||
if (Settings.version < 0x07000004) {
|
||||
Settings.wifi_output_power = 170;
|
||||
}
|
||||
if (Settings.version < 0x07010202) {
|
||||
Settings.serial_config = TS_SERIAL_8N1;
|
||||
}
|
||||
if (Settings.version < 0x07010204) {
|
||||
if (Settings.flag3.ex_cors_enabled == 1) {
|
||||
strlcpy(Settings.cors_domain, CORS_ENABLED_ALL, sizeof(Settings.cors_domain));
|
||||
} else {
|
||||
Settings.cors_domain[0] = 0;
|
||||
}
|
||||
}
|
||||
if (Settings.version < 0x07010205) {
|
||||
Settings.seriallog_level = Settings.ex_seriallog_level; // 09E -> 452
|
||||
Settings.sta_config = Settings.ex_sta_config; // 09F -> EC7
|
||||
Settings.sta_active = Settings.ex_sta_active; // 0A0 -> EC8
|
||||
memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); // 1A7 -> EC9
|
||||
}
|
||||
if (Settings.version < 0x07010206) {
|
||||
Settings.flag4 = Settings.ex_flag4; // 1E0 -> EF8
|
||||
Settings.mqtt_port = Settings.ex_mqtt_port; // 20A -> EFC
|
||||
memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE
|
||||
}
|
||||
|
||||
if ((VERSION < 0x08000000) && (Settings.version > VERSION)) {
|
||||
char temp[strlen(SettingsText(SET_OTAURL)) +1]; strncpy(temp, SettingsText(SET_OTAURL), sizeof(temp));
|
||||
char temp21[strlen(SettingsText(SET_MQTTPREFIX1)) +1]; strncpy(temp21, SettingsText(SET_MQTTPREFIX1), sizeof(temp21));
|
||||
char temp22[strlen(SettingsText(SET_MQTTPREFIX2)) +1]; strncpy(temp22, SettingsText(SET_MQTTPREFIX2), sizeof(temp22));
|
||||
char temp23[strlen(SettingsText(SET_MQTTPREFIX3)) +1]; strncpy(temp23, SettingsText(SET_MQTTPREFIX3), sizeof(temp23));
|
||||
char temp31[strlen(SettingsText(SET_STASSID1)) +1]; strncpy(temp31, SettingsText(SET_STASSID1), sizeof(temp31));
|
||||
char temp32[strlen(SettingsText(SET_STASSID2)) +1]; strncpy(temp32, SettingsText(SET_STASSID2), sizeof(temp32));
|
||||
char temp41[strlen(SettingsText(SET_STAPWD1)) +1]; strncpy(temp41, SettingsText(SET_STAPWD1), sizeof(temp41));
|
||||
char temp42[strlen(SettingsText(SET_STAPWD2)) +1]; strncpy(temp42, SettingsText(SET_STAPWD2), sizeof(temp42));
|
||||
char temp5[strlen(SettingsText(SET_HOSTNAME)) +1]; strncpy(temp5, SettingsText(SET_HOSTNAME), sizeof(temp5));
|
||||
char temp6[strlen(SettingsText(SET_SYSLOG_HOST)) +1]; strncpy(temp6, SettingsText(SET_SYSLOG_HOST), sizeof(temp5));
|
||||
|
||||
uint32_t version = Settings.version;
|
||||
Settings.version = VERSION;
|
||||
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);
|
||||
Settings.version = version;
|
||||
}
|
||||
|
||||
Settings.version = VERSION;
|
||||
SettingsSave(1);
|
||||
|
|
|
@ -51,7 +51,7 @@ void OsWatchTicker(void)
|
|||
uint32_t last_run = abs(t - oswatch_last_loop_time);
|
||||
|
||||
#ifdef DEBUG_THEO
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), last_run);
|
||||
#endif // DEBUG_THEO
|
||||
if (last_run >= (OSWATCH_RESET_TIME * 1000)) {
|
||||
// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space
|
||||
|
@ -107,12 +107,6 @@ String GetResetReason(void)
|
|||
}
|
||||
}
|
||||
|
||||
String GetResetReasonInfo(void)
|
||||
{
|
||||
// "Fatal exception:0 flag:2 (EXCEPTION) epc1:0x704022a7 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000000 depc:0x00000000"
|
||||
return (ResetReason() == REASON_EXCEPTION_RST) ? ESP.getResetInfo() : GetResetReason();
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Miscellaneous
|
||||
\*********************************************************************************************/
|
||||
|
@ -334,6 +328,22 @@ char* RemoveSpace(char* p)
|
|||
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;
|
||||
|
@ -383,6 +393,23 @@ char* Trim(char* p)
|
|||
return p;
|
||||
}
|
||||
|
||||
char* RemoveAllSpaces(char* p)
|
||||
{
|
||||
// remove any white space from the base64
|
||||
char *cursor = p;
|
||||
uint32_t offset = 0;
|
||||
while (1) {
|
||||
*cursor = *(cursor + offset);
|
||||
if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string
|
||||
offset++;
|
||||
} else {
|
||||
if (0 == *cursor) { break; }
|
||||
cursor++;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char* NoAlNumToUnderscore(char* dest, const char* source)
|
||||
{
|
||||
char* write = dest;
|
||||
|
@ -737,19 +764,50 @@ int GetStateNumber(char *state_text)
|
|||
return state_number;
|
||||
}
|
||||
|
||||
String GetSerialConfig(void)
|
||||
{
|
||||
// Settings.serial_config layout
|
||||
// b000000xx - 5, 6, 7 or 8 data bits
|
||||
// b00000x00 - 1 or 2 stop bits
|
||||
// b000xx000 - None, Even or Odd parity
|
||||
|
||||
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(uint32_t baudrate)
|
||||
{
|
||||
if (seriallog_level) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Set Serial to %s %d bit/s"), GetSerialConfig().c_str(), baudrate);
|
||||
delay(100);
|
||||
}
|
||||
Serial.flush();
|
||||
Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config));
|
||||
delay(10);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void SetSerialConfig(uint32_t serial_config)
|
||||
{
|
||||
if (serial_config == Settings.serial_config) { return; }
|
||||
if (serial_config > TS_SERIAL_8O2) { return; }
|
||||
|
||||
Settings.serial_config = serial_config;
|
||||
SetSerialBegin(Serial.baudRate());
|
||||
}
|
||||
|
||||
void SetSerialBaudrate(int baudrate)
|
||||
{
|
||||
Settings.baudrate = baudrate / 300;
|
||||
if (Serial.baudRate() != baudrate) {
|
||||
if (seriallog_level) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate);
|
||||
}
|
||||
delay(100);
|
||||
Serial.flush();
|
||||
Serial.begin(baudrate, serial_config);
|
||||
delay(10);
|
||||
Serial.println();
|
||||
}
|
||||
if (Serial.baudRate() == baudrate) { return; }
|
||||
|
||||
SetSerialBegin(baudrate);
|
||||
}
|
||||
|
||||
void ClaimSerial(void)
|
||||
|
@ -1529,10 +1587,10 @@ void Syslog(void)
|
|||
{
|
||||
// Destroys log_data
|
||||
|
||||
uint32_t current_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host));
|
||||
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(Settings.syslog_host, syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash
|
||||
WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash
|
||||
}
|
||||
if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) {
|
||||
char syslog_preamble[64]; // Hostname + Id
|
||||
|
|
|
@ -23,7 +23,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
|
|||
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_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_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_WIFIPOWER "|" D_CMND_TEMPOFFSET "|"
|
||||
|
@ -38,7 +38,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
|
|||
&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,
|
||||
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
|
||||
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
|
||||
&CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
|
||||
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset,
|
||||
|
@ -147,7 +147,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len)
|
|||
data_len--;
|
||||
}
|
||||
|
||||
bool grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr);
|
||||
bool grpflg = (strstr(topicBuf, SettingsText(SET_MQTT_GRP_TOPIC)) != nullptr);
|
||||
|
||||
char stemp1[TOPSZ];
|
||||
GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/
|
||||
|
@ -325,15 +325,18 @@ void CmndStatus(void)
|
|||
uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload;
|
||||
|
||||
uint32_t option = STAT;
|
||||
char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)];
|
||||
char stemp[200];
|
||||
char stemp2[100];
|
||||
|
||||
// Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX
|
||||
if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2)) && (!payload)) { option++; } // TELE
|
||||
|
||||
if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT
|
||||
if (!energy_flg && (9 == payload)) { payload = 99; }
|
||||
|
||||
bool exception_flg = (ResetReason() == REASON_EXCEPTION_RST);
|
||||
if (!exception_flg && (12 == payload)) { payload = 99; }
|
||||
|
||||
if ((0 == payload) || (99 == payload)) {
|
||||
uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
|
||||
#ifdef USE_SONOFF_IFAN
|
||||
|
@ -341,7 +344,7 @@ void CmndStatus(void)
|
|||
#endif // USE_SONOFF_IFAN
|
||||
stemp[0] = '\0';
|
||||
for (uint32_t i = 0; i < maxfn; i++) {
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]);
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), SettingsText(SET_FRIENDLYNAME1 +i));
|
||||
}
|
||||
stemp2[0] = '\0';
|
||||
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
|
||||
|
@ -352,10 +355,10 @@ void CmndStatus(void)
|
|||
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(), stemp, mqtt_topic,
|
||||
Settings.button_topic, power, Settings.poweronstate, Settings.ledstate,
|
||||
SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate,
|
||||
Settings.ledmask, Settings.save_data,
|
||||
Settings.flag.save_state, // SetOption0 - Save power state and use after restart
|
||||
Settings.switch_topic,
|
||||
SettingsText(SET_MQTT_SWITCH_TOPIC),
|
||||
stemp2,
|
||||
Settings.flag.mqtt_button_retain, // CMND_BUTTONRETAIN
|
||||
Settings.flag.mqtt_switch_retain, // CMND_SWITCHRETAIN
|
||||
|
@ -368,8 +371,8 @@ void CmndStatus(void)
|
|||
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" 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,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"),
|
||||
baudrate, Settings.mqtt_grptopic, Settings.ota_url,
|
||||
GetResetReasonInfo().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep,
|
||||
baudrate, 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, Settings.save_flag, GetSettingsAddress());
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1"));
|
||||
}
|
||||
|
@ -377,9 +380,12 @@ void CmndStatus(void)
|
|||
if ((0 == payload) || (2 == payload)) {
|
||||
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\""
|
||||
D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
|
||||
"\"Hardware\":\"%s\"}}"),
|
||||
"\"Hardware\":\"%s\""
|
||||
"%s}}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str(),
|
||||
ESP.getBootVersion(), ESP.getSdkVersion(), GetDeviceHardware().c_str());
|
||||
ESP.getBootVersion(), ESP.getSdkVersion(),
|
||||
GetDeviceHardware().c_str(),
|
||||
GetStatistics().c_str());
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2"));
|
||||
}
|
||||
|
||||
|
@ -388,7 +394,7 @@ void CmndStatus(void)
|
|||
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\"]}}"),
|
||||
Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level,
|
||||
Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period,
|
||||
SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), 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);
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3"));
|
||||
|
@ -419,17 +425,10 @@ void CmndStatus(void)
|
|||
}
|
||||
|
||||
if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\""
|
||||
D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"),
|
||||
Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client,
|
||||
mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
|
||||
#else
|
||||
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}}"),
|
||||
Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client,
|
||||
mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
|
||||
#endif
|
||||
SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT),
|
||||
mqtt_client, SettingsText(SET_MQTT_USER), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6"));
|
||||
}
|
||||
|
||||
|
@ -483,6 +482,15 @@ void CmndStatus(void)
|
|||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11"));
|
||||
}
|
||||
|
||||
if (exception_flg) {
|
||||
if ((0 == payload) || (12 == payload)) {
|
||||
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":"));
|
||||
CrashDump();
|
||||
ResponseJsonEnd();
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12"));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_SCRIPT_STATUS
|
||||
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data);
|
||||
#endif
|
||||
|
@ -542,10 +550,10 @@ void CmndUpgrade(void)
|
|||
|
||||
void CmndOtaUrl(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ota_url))) {
|
||||
strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data, sizeof(Settings.ota_url));
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndChar(Settings.ota_url);
|
||||
ResponseCmndChar(SettingsText(SET_OTAURL));
|
||||
}
|
||||
|
||||
void CmndSeriallog(void)
|
||||
|
@ -564,6 +572,9 @@ void CmndRestart(void)
|
|||
restart_flag = 2;
|
||||
ResponseCmndChar(D_JSON_RESTARTING);
|
||||
break;
|
||||
case -1:
|
||||
CmndCrash(); // force a crash
|
||||
break;
|
||||
case 99:
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
|
||||
EspRestart();
|
||||
|
@ -1082,6 +1093,48 @@ void CmndBaudrate(void)
|
|||
ResponseCmndNumber(Settings.baudrate * 300);
|
||||
}
|
||||
|
||||
void CmndSerialConfig(void)
|
||||
{
|
||||
// See TasmotaSerialConfig for possible options
|
||||
// SerialConfig 0..23 where 3 equals 8N1
|
||||
// SerialConfig 8N1
|
||||
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.data_len < 3) { // Use 0..23 as serial config option
|
||||
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; // Data bits 5, 6, 7 or 8, No parity and 1 stop bit
|
||||
|
||||
bool valid = true;
|
||||
char parity = (XdrvMailbox.data[1] & 0xdf);
|
||||
if ('E' == parity) {
|
||||
serial_config += 0x08; // Even parity
|
||||
}
|
||||
else if ('O' == parity) {
|
||||
serial_config += 0x10; // Odd parity
|
||||
}
|
||||
else if ('N' != parity) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if ('2' == XdrvMailbox.data[2]) {
|
||||
serial_config += 0x04; // Stop bits 2
|
||||
}
|
||||
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 <= 5)) {
|
||||
|
@ -1133,10 +1186,10 @@ void CmndSyslog(void)
|
|||
|
||||
void CmndLoghost(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.syslog_host))) {
|
||||
strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data, sizeof(Settings.syslog_host));
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndChar(Settings.syslog_host);
|
||||
ResponseCmndChar(SettingsText(SET_SYSLOG_HOST));
|
||||
}
|
||||
|
||||
void CmndLogport(void)
|
||||
|
@ -1164,17 +1217,15 @@ void CmndIpAddress(void)
|
|||
void CmndNtpServer(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ntp_server[0]))) {
|
||||
strlcpy(Settings.ntp_server[XdrvMailbox.index -1],
|
||||
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?NTP_SERVER1:(2==XdrvMailbox.index)?NTP_SERVER2:NTP_SERVER3 : XdrvMailbox.data,
|
||||
sizeof(Settings.ntp_server[0]));
|
||||
for (uint32_t i = 0; i < strlen(Settings.ntp_server[XdrvMailbox.index -1]); i++) {
|
||||
if (Settings.ntp_server[XdrvMailbox.index -1][i] == ',') Settings.ntp_server[XdrvMailbox.index -1][i] = '.';
|
||||
}
|
||||
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) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data);
|
||||
SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server)));
|
||||
// restart_flag = 2; // Issue #3890
|
||||
ntp_force_sync = true;
|
||||
}
|
||||
ResponseCmndIdxChar(Settings.ntp_server[XdrvMailbox.index -1]);
|
||||
ResponseCmndIdxChar(SettingsText(ntp_server));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1191,33 +1242,31 @@ void CmndAp(void)
|
|||
}
|
||||
restart_flag = 2;
|
||||
}
|
||||
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]);
|
||||
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active));
|
||||
}
|
||||
|
||||
void CmndSsid(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.sta_ssid[0]))) {
|
||||
strlcpy(Settings.sta_ssid[XdrvMailbox.index -1],
|
||||
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data,
|
||||
sizeof(Settings.sta_ssid[0]));
|
||||
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(Settings.sta_ssid[XdrvMailbox.index -1]);
|
||||
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()) && (XdrvMailbox.data_len < sizeof(Settings.sta_pwd[0]))) {
|
||||
strlcpy(Settings.sta_pwd[XdrvMailbox.index -1],
|
||||
(SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data,
|
||||
sizeof(Settings.sta_pwd[0]));
|
||||
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(Settings.sta_pwd[XdrvMailbox.index -1]);
|
||||
ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1));
|
||||
} else {
|
||||
Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index);
|
||||
}
|
||||
|
@ -1226,14 +1275,14 @@ void CmndPassword(void)
|
|||
|
||||
void CmndHostname(void)
|
||||
{
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.hostname))) {
|
||||
strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data, sizeof(Settings.hostname));
|
||||
if (strstr(Settings.hostname, "%") != nullptr) {
|
||||
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
|
||||
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(Settings.hostname);
|
||||
ResponseCmndChar(SettingsText(SET_HOSTNAME));
|
||||
}
|
||||
|
||||
void CmndWifiConfig(void)
|
||||
|
@ -1255,16 +1304,16 @@ void CmndWifiConfig(void)
|
|||
void CmndFriendlyname(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.friendlyname[0]))) {
|
||||
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);
|
||||
}
|
||||
strlcpy(Settings.friendlyname[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data, sizeof(Settings.friendlyname[XdrvMailbox.index -1]));
|
||||
SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndIdxChar(Settings.friendlyname[XdrvMailbox.index -1]);
|
||||
ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
support_crash_recorder.ino - record the call stack in RTC in case of crash
|
||||
|
||||
Copyright (C) 2019 Stephan Hadinger, Theo Arends,
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const uint32_t crash_magic = 0x53415400; // Stack trace magic number (TASx)
|
||||
const uint32_t crash_rtc_offset = 32; // Offset in RTC memory skipping OTA used block
|
||||
const uint32_t crash_dump_max_len = 31; // Dump only 31 call addresses to satisfy max JSON length of about 600 characters
|
||||
|
||||
/**
|
||||
* Save crash information in RTC memory
|
||||
* This function is called automatically if ESP8266 suffers an exception
|
||||
* It should be kept quick / consise to be able to execute before hardware wdt may kick in
|
||||
*/
|
||||
extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end )
|
||||
{
|
||||
uint32_t addr_written = 0; // how many addresses have we already written in RTC
|
||||
uint32_t value; // 4 bytes buffer to write to RTC
|
||||
|
||||
for (uint32_t i = stack; i < stack_end; i += 4) {
|
||||
value = *((uint32_t*) i); // load value from stack
|
||||
if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area
|
||||
ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value));
|
||||
addr_written++;
|
||||
if (addr_written >= crash_dump_max_len) { break; } // we store only 31 addresses
|
||||
}
|
||||
}
|
||||
value = crash_magic + addr_written;
|
||||
ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
// Generate a crash to test the crash recorder
|
||||
void CmndCrash(void)
|
||||
{
|
||||
volatile uint32_t dummy;
|
||||
dummy = *((uint32_t*) 0x00000000);
|
||||
}
|
||||
|
||||
// Clear the RTC dump counter when we do a normal reboot, this avoids garbage data to stay in RTC
|
||||
void CrashDumpClear(void)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* CmndCrashDump - dump the crash history - called by `Status 12`
|
||||
\*********************************************************************************************/
|
||||
|
||||
void CrashDump(void)
|
||||
{
|
||||
ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""),
|
||||
resetInfo.exccause, // Exception Cause
|
||||
GetResetReason().c_str(), // Reset Reason
|
||||
resetInfo.epc1, // Exception Progam Counter
|
||||
resetInfo.epc2, // Exception Progam Counter - High-Priority Interrupt 1
|
||||
resetInfo.epc3, // Exception Progam Counter - High-Priority Interrupt 2
|
||||
resetInfo.excvaddr, // Exception Virtual Address Register - Virtual address that caused last fetch, load, or store exception
|
||||
resetInfo.depc); // Double Exception Program Counter
|
||||
|
||||
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();
|
||||
}
|
|
@ -172,6 +172,7 @@ double const f_sixthpi = f_pi / 6.0; // f_pi/6.0, used in
|
|||
double const f_tansixthpi = tan(f_sixthpi); // tan(f_pi/6), used in atan routines
|
||||
double const f_twelfthpi = f_pi / 12.0; // f_pi/12.0, used in atan routines
|
||||
double const f_tantwelfthpi = tan(f_twelfthpi); // tan(f_pi/12), used in atan routines
|
||||
float const f_180pi = 180 / f_pi; // 180 / pi for angles in degrees
|
||||
|
||||
// *******************************************************************
|
||||
// ***
|
||||
|
|
|
@ -379,7 +379,9 @@ void RtcSecond(void)
|
|||
Rtc.ntp_sync_minute = 1; // If sync prepare for a new cycle
|
||||
}
|
||||
uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id
|
||||
if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || (Rtc.ntp_sync_minute == uptime_minute))) || ntp_force_sync ) ) {
|
||||
if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || // Never synced
|
||||
(Rtc.ntp_sync_minute == uptime_minute))) || // Re-sync every hour
|
||||
ntp_force_sync ) ) { // Forced sync
|
||||
Rtc.ntp_time = sntp_get_current_timestamp();
|
||||
if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on)
|
||||
ntp_force_sync = false;
|
||||
|
@ -467,9 +469,9 @@ void RtcSetTime(uint32_t epoch)
|
|||
|
||||
void RtcInit(void)
|
||||
{
|
||||
sntp_setservername(0, Settings.ntp_server[0]);
|
||||
sntp_setservername(1, Settings.ntp_server[1]);
|
||||
sntp_setservername(2, Settings.ntp_server[2]);
|
||||
sntp_setservername(0, SettingsText(SET_NTPSERVER1));
|
||||
sntp_setservername(1, SettingsText(SET_NTPSERVER2));
|
||||
sntp_setservername(2, SettingsText(SET_NTPSERVER3));
|
||||
sntp_stop();
|
||||
sntp_set_timezone(0); // UTC time
|
||||
sntp_init();
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
support_statistics.ino - gather statistics for Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define USE_STATS_CODE
|
||||
|
||||
#ifdef USE_STATS_CODE
|
||||
/*********************************************************************************************\
|
||||
* Gather statistics
|
||||
\*********************************************************************************************/
|
||||
|
||||
String GetStatistics(void)
|
||||
{
|
||||
char data[40];
|
||||
|
||||
if (Settings.version < 0x08000000) {
|
||||
uint32_t str_len = 0;
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
str_len += strlen(Settings.sta_ssid[i]);
|
||||
str_len += strlen(Settings.sta_pwd[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
str_len += strlen(Settings.mqtt_prefix[i]);
|
||||
str_len += strlen(Settings.ntp_server[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
str_len += strlen(Settings.state_text[i]);
|
||||
str_len += strlen(Settings.friendlyname[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) {
|
||||
str_len += strlen(Settings.mems[i]);
|
||||
}
|
||||
str_len += strlen(Settings.ota_url);
|
||||
str_len += strlen(Settings.hostname);
|
||||
str_len += strlen(Settings.syslog_host);
|
||||
str_len += strlen(Settings.mqtt_host);
|
||||
str_len += strlen(Settings.mqtt_client);
|
||||
str_len += strlen(Settings.mqtt_user);
|
||||
str_len += strlen(Settings.mqtt_pwd);
|
||||
str_len += strlen(Settings.mqtt_topic);
|
||||
str_len += strlen(Settings.button_topic);
|
||||
str_len += strlen(Settings.switch_topic);
|
||||
str_len += strlen(Settings.mqtt_grptopic);
|
||||
str_len += strlen(Settings.web_password);
|
||||
str_len += strlen(Settings.mqtt_fulltopic);
|
||||
str_len += strlen(Settings.cors_domain);
|
||||
snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), 37 + str_len); // Char Usage Ratio
|
||||
} else {
|
||||
snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); // Char Usage Ratio
|
||||
}
|
||||
return String(data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
String GetStatistics(void)
|
||||
{
|
||||
return String("");
|
||||
}
|
||||
|
||||
#endif // USE_STATS_CODE
|
|
@ -60,15 +60,16 @@ char* Format(char* output, const char* input, int size)
|
|||
|
||||
char* GetOtaUrl(char *otaurl, size_t otaurl_size)
|
||||
{
|
||||
if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID
|
||||
snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff);
|
||||
if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { // OTA url contains placeholder for chip ID
|
||||
snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId() & 0x1fff);
|
||||
}
|
||||
else if (strstr(Settings.ota_url, "%d") != nullptr) { // OTA url contains placeholder for chip ID
|
||||
snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId());
|
||||
else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { // OTA url contains placeholder for chip ID
|
||||
snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId());
|
||||
}
|
||||
else {
|
||||
strlcpy(otaurl, Settings.ota_url, otaurl_size);
|
||||
strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size);
|
||||
}
|
||||
|
||||
return otaurl;
|
||||
}
|
||||
|
||||
|
@ -101,17 +102,19 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi
|
|||
fulltopic += topic; // cmnd/<grouptopic>
|
||||
}
|
||||
} else {
|
||||
fulltopic = Settings.mqtt_fulltopic;
|
||||
fulltopic = SettingsText(SET_MQTT_FULLTOPIC);
|
||||
if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) {
|
||||
fulltopic += F("/");
|
||||
fulltopic += FPSTR(MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops
|
||||
}
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
if ('\0' == Settings.mqtt_prefix[i][0]) {
|
||||
GetTextIndexed(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), i, kPrefixes);
|
||||
if ('\0' == SettingsText(SET_MQTTPREFIX1 + i)) {
|
||||
char temp[TOPSZ];
|
||||
SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes));
|
||||
}
|
||||
}
|
||||
fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]);
|
||||
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();
|
||||
|
@ -131,7 +134,7 @@ char* GetGroupTopic_P(char *stopic, const char* subtopic)
|
|||
{
|
||||
// SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing/<grouptopic>/#
|
||||
// SetOption75 1: cmnd/<grouptopic>
|
||||
return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, Settings.mqtt_grptopic, subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
|
||||
return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
|
||||
}
|
||||
|
||||
char* GetFallbackTopic_P(char *stopic, const char* subtopic)
|
||||
|
@ -144,7 +147,7 @@ char* GetStateText(uint32_t state)
|
|||
if (state > 3) {
|
||||
state = 1;
|
||||
}
|
||||
return Settings.state_text[state];
|
||||
return SettingsText(SET_STATE_TXT1 + state);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
@ -358,10 +361,10 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
|
|||
|
||||
char stopic[TOPSZ];
|
||||
char scommand[CMDSZ];
|
||||
char key_topic[sizeof(Settings.button_topic)];
|
||||
char key_topic[TOPSZ];
|
||||
bool result = false;
|
||||
|
||||
char *tmp = (key) ? Settings.switch_topic : Settings.button_topic;
|
||||
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")) { // SetOption3 - Enable MQTT
|
||||
if (!key && (device > devices_present)) {
|
||||
|
@ -374,7 +377,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
|
|||
} else {
|
||||
if ((Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set
|
||||
!strcmp(mqtt_topic, key_topic) ||
|
||||
!strcmp(Settings.mqtt_grptopic, key_topic)) &&
|
||||
!strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) &&
|
||||
(POWER_TOGGLE == state)) {
|
||||
state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON
|
||||
}
|
||||
|
@ -583,8 +586,9 @@ void MqttShowState(void)
|
|||
MqttShowPWMState();
|
||||
}
|
||||
|
||||
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_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"),
|
||||
Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str());
|
||||
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, SettingsText(SET_STASSID1 + Settings.sta_active), WiFi.BSSIDstr().c_str(), WiFi.channel(),
|
||||
WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str());
|
||||
}
|
||||
|
||||
void MqttPublishTeleState(void)
|
||||
|
@ -592,7 +596,7 @@ void MqttPublishTeleState(void)
|
|||
mqtt_data[0] = '\0';
|
||||
MqttShowState();
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN);
|
||||
#ifdef USE_SCRIPT
|
||||
#if defined(USE_RULES) || defined(USE_SCRIPT)
|
||||
RulesTeleperiod(); // Allow rule based HA messages
|
||||
#endif // USE_SCRIPT
|
||||
}
|
||||
|
@ -812,7 +816,6 @@ void Every250mSeconds(void)
|
|||
if (ota_state_flag && BACKLOG_EMPTY) {
|
||||
ota_state_flag--;
|
||||
if (2 == ota_state_flag) {
|
||||
ota_url = Settings.ota_url;
|
||||
RtcSettings.ota_loader = 0; // Try requested image first
|
||||
ota_retry_counter = OTA_ATTEMPTS;
|
||||
ESPhttpUpdate.rebootOnUpdate(false);
|
||||
|
@ -838,7 +841,7 @@ void Every250mSeconds(void)
|
|||
if (!pch) { pch = ech; }
|
||||
if (pch) {
|
||||
mqtt_data[pch - mqtt_data] = '\0';
|
||||
char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin
|
||||
char *ech = strrchr(SettingsText(SET_OTAURL), '.'); // Change from filename.bin into filename-minimal.bin
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ech); // Minimal filename must be filename-minimal
|
||||
}
|
||||
}
|
||||
|
@ -902,26 +905,45 @@ void Every250mSeconds(void)
|
|||
}
|
||||
if (restart_flag && BACKLOG_EMPTY) {
|
||||
if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
|
||||
char storage_wifi[sizeof(Settings.sta_ssid) +
|
||||
sizeof(Settings.sta_pwd)];
|
||||
char storage_mqtt[sizeof(Settings.mqtt_host) +
|
||||
sizeof(Settings.mqtt_port) +
|
||||
sizeof(Settings.mqtt_client) +
|
||||
sizeof(Settings.mqtt_user) +
|
||||
sizeof(Settings.mqtt_pwd) +
|
||||
sizeof(Settings.mqtt_topic)];
|
||||
memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords
|
||||
if (216 == restart_flag) {
|
||||
memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password
|
||||
}
|
||||
// Backup current SSIDs and Passwords
|
||||
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 (216 == restart_flag) {
|
||||
// Backup mqtt host, port, client, username and password
|
||||
// }
|
||||
if ((215 == restart_flag) || (216 == restart_flag)) {
|
||||
SettingsErase(0); // Erase all flash from program end to end of physical flash
|
||||
}
|
||||
SettingsDefault();
|
||||
memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi)); // Restore current SSIDs and Passwords
|
||||
// Restore current SSIDs and Passwords
|
||||
SettingsUpdateText(SET_STASSID1, storage_ssid1);
|
||||
SettingsUpdateText(SET_STASSID2, storage_ssid2);
|
||||
SettingsUpdateText(SET_STAPWD1, storage_pass1);
|
||||
SettingsUpdateText(SET_STAPWD2, storage_pass2);
|
||||
if (216 == restart_flag) {
|
||||
memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password
|
||||
strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default
|
||||
// Restore the mqtt host, port, client, username and password
|
||||
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;
|
||||
}
|
||||
|
@ -972,7 +994,7 @@ void ArduinoOTAInit(void)
|
|||
{
|
||||
ArduinoOTA.setPort(8266);
|
||||
ArduinoOTA.setHostname(my_hostname);
|
||||
if (Settings.web_password[0] !=0) { ArduinoOTA.setPassword(Settings.web_password); }
|
||||
if (strlen(SettingsText(SET_WEBPWD))) { ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); }
|
||||
|
||||
ArduinoOTA.onStart([]()
|
||||
{
|
||||
|
@ -1285,15 +1307,19 @@ void GpioInit(void)
|
|||
}
|
||||
#endif // USE_SONOFF_SC
|
||||
|
||||
if (!light_type) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
|
||||
if (pin[GPIO_PWM1 +i] < 99) {
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
if (light_type) {
|
||||
// force PWM GPIOs to low or high mode, see #7165
|
||||
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0);
|
||||
} else {
|
||||
pwm_present = true;
|
||||
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
|
||||
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 (pin[GPIO_REL1 +i] < 99) {
|
||||
pinMode(pin[GPIO_REL1 +i], OUTPUT);
|
||||
|
|
|
@ -191,18 +191,20 @@ void WifiBegin(uint8_t flag, uint8_t channel)
|
|||
case 2: // Toggle
|
||||
Settings.sta_active ^= 1;
|
||||
} // 3: Current AP
|
||||
if ('\0' == Settings.sta_ssid[Settings.sta_active][0]) { Settings.sta_active ^= 1; } // Skip empty SSID
|
||||
if ('\0' == SettingsText(SET_STASSID1 + Settings.sta_active)) {
|
||||
Settings.sta_active ^= 1; // Skip empty SSID
|
||||
}
|
||||
if (Settings.ip_address[0]) {
|
||||
WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP
|
||||
}
|
||||
WiFi.hostname(my_hostname);
|
||||
if (channel) {
|
||||
WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active], channel, Wifi.bssid);
|
||||
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid);
|
||||
} else {
|
||||
WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]);
|
||||
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 " D_IN_MODE " 11%c " D_AS " %s..."),
|
||||
Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
|
||||
Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
|
||||
|
||||
#if LWIP_IPV6
|
||||
for (bool configured = false; !configured;) {
|
||||
|
@ -278,10 +280,10 @@ void WifiBeginAfterScan(void)
|
|||
bool known = false;
|
||||
uint32_t j;
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (ssid_scan == Settings.sta_ssid[j]) { // SSID match
|
||||
if (ssid_scan == SettingsText(SET_STASSID1 + j)) { // SSID match
|
||||
known = true;
|
||||
if (rssi_scan > best_network_db) { // Best network
|
||||
if (sec_scan == ENC_TYPE_NONE || Settings.sta_pwd[j]) { // Check for passphrase if not open wlan
|
||||
if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { // Check for passphrase if not open wlan
|
||||
best_network_db = (int8_t)rssi_scan;
|
||||
channel = chan_scan;
|
||||
ap = j; // AP1 or AP2
|
||||
|
@ -352,6 +354,14 @@ bool WifiCheckIPv6(void)
|
|||
return ipv6_global;
|
||||
}
|
||||
|
||||
String WifiGetIPv6(void)
|
||||
{
|
||||
for (auto a : addrList) {
|
||||
if(!a.isLocal() && a.isV6()) return a.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool WifiCheckIPAddrStatus(void) // Return false for 169.254.x.x or fe80::/64
|
||||
{
|
||||
bool ip_global=false;
|
||||
|
@ -426,7 +436,7 @@ void WifiCheckIp(void)
|
|||
if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) {
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT));
|
||||
} else {
|
||||
if (('\0' == Settings.sta_ssid[0][0]) && ('\0' == Settings.sta_ssid[1][0])) {
|
||||
if (('\0' == SettingsText(SET_STASSID1)) && ('\0' == SettingsText(SET_STASSID2))) {
|
||||
wifi_config_tool = WIFI_MANAGER; // Skip empty SSIDs and start Wifi config tool
|
||||
Wifi.retry = 0;
|
||||
} else {
|
||||
|
@ -472,13 +482,13 @@ void WifiCheck(uint8_t param)
|
|||
if (Wifi.config_counter) {
|
||||
if (!Wifi.config_counter) {
|
||||
if (strlen(WiFi.SSID().c_str())) {
|
||||
strlcpy(Settings.sta_ssid[0], WiFi.SSID().c_str(), sizeof(Settings.sta_ssid[0]));
|
||||
SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str());
|
||||
}
|
||||
if (strlen(WiFi.psk().c_str())) {
|
||||
strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0]));
|
||||
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"), Settings.sta_ssid[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) {
|
||||
|
@ -617,6 +627,7 @@ void WifiShutdown(void)
|
|||
void EspRestart(void)
|
||||
{
|
||||
WifiShutdown();
|
||||
CrashDumpClear(); // Clear the stack dump in RTC
|
||||
// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0
|
||||
ESP.reset();
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate
|
|||
const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate
|
||||
const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms
|
||||
const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms
|
||||
const uint8_t MAX_STATUS = 11; // Max number of status lines
|
||||
const uint8_t MAX_STATUS = 12; // Max number of status lines
|
||||
|
||||
const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01
|
||||
|
||||
|
@ -272,6 +272,30 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU
|
|||
|
||||
enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND };
|
||||
|
||||
enum SettingsTextIndex { SET_OTAURL,
|
||||
SET_MQTTPREFIX1, SET_MQTTPREFIX2, SET_MQTTPREFIX3,
|
||||
SET_STASSID1, SET_STASSID2,
|
||||
SET_STAPWD1, SET_STAPWD2,
|
||||
SET_HOSTNAME, SET_SYSLOG_HOST,
|
||||
SET_WEBPWD,
|
||||
SET_MQTT_HOST, SET_MQTT_CLIENT,
|
||||
SET_MQTT_USER, SET_MQTT_PWD,
|
||||
SET_MQTT_FULLTOPIC, SET_MQTT_TOPIC,
|
||||
SET_MQTT_BUTTON_TOPIC, SET_MQTT_SWITCH_TOPIC, SET_MQTT_GRP_TOPIC,
|
||||
SET_STATE_TXT1, SET_STATE_TXT2, SET_STATE_TXT3, SET_STATE_TXT4,
|
||||
SET_NTPSERVER1, SET_NTPSERVER2, SET_NTPSERVER3,
|
||||
SET_MEM1, SET_MEM2, SET_MEM3, SET_MEM4, SET_MEM5,
|
||||
SET_CORS,
|
||||
SET_FRIENDLYNAME1, SET_FRIENDLYNAME2, SET_FRIENDLYNAME3, SET_FRIENDLYNAME4,
|
||||
|
||||
// SET_FRIENDLYNAME5, SET_FRIENDLYNAME6, SET_FRIENDLYNAME7, SET_FRIENDLYNAME8, // Future extension
|
||||
// SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, // Future extension
|
||||
// SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, // Future extension
|
||||
// SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, // Future extension
|
||||
// SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, // Future extension
|
||||
|
||||
SET_MAX };
|
||||
|
||||
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
|
||||
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER,
|
||||
SRC_MAX };
|
||||
|
|
|
@ -74,8 +74,6 @@ const char kCodeImage[] PROGMEM = "tasmota|minimal|sensors|knx|basic|display|ir"
|
|||
* Global variables
|
||||
\*********************************************************************************************/
|
||||
|
||||
SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit
|
||||
|
||||
WiFiUDP PortUdp; // UDP Syslog and Alexa
|
||||
|
||||
unsigned long feature_drv1; // Compiled driver feature map
|
||||
|
@ -112,7 +110,6 @@ uint32_t web_log_index = 1; // Index in Web log buffer (should n
|
|||
float global_temperature = 9999; // Provide a global temperature to be used by some sensors
|
||||
float global_humidity = 0; // Provide a global humidity to be used by some sensors
|
||||
float global_pressure = 0; // Provide a global pressure to be used by some sensors
|
||||
char *ota_url; // OTA url string pointer
|
||||
uint16_t tele_period = 9999; // Tele period timer
|
||||
uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command
|
||||
uint16_t blink_counter = 0; // Number of blink cycles
|
||||
|
@ -267,13 +264,13 @@ void setup(void)
|
|||
}
|
||||
}
|
||||
|
||||
Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client));
|
||||
Format(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic));
|
||||
if (strstr(Settings.hostname, "%") != nullptr) {
|
||||
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
|
||||
snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname, mqtt_topic, ESP.getChipId() & 0x1FFF);
|
||||
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, Settings.hostname);
|
||||
snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME));
|
||||
}
|
||||
|
||||
GetEspHardwareType();
|
||||
|
@ -332,7 +329,7 @@ void setup(void)
|
|||
}
|
||||
blink_powersave = power;
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, Settings.friendlyname[0], my_version, my_image);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image);
|
||||
#ifdef FIRMWARE_MINIMAL
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
|
||||
#endif // FIRMWARE_MINIMAL
|
||||
|
@ -351,15 +348,18 @@ void BacklogLoop(void)
|
|||
{
|
||||
if (TimeReached(backlog_delay)) {
|
||||
if (!BACKLOG_EMPTY && !backlog_mutex) {
|
||||
backlog_mutex = true;
|
||||
#ifdef SUPPORT_IF_STATEMENT
|
||||
ExecuteCommand((char*)backlog.shift().c_str(), SRC_BACKLOG);
|
||||
backlog_mutex = true;
|
||||
String cmd = backlog.shift();
|
||||
backlog_mutex = false;
|
||||
ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG);
|
||||
#else
|
||||
backlog_mutex = true;
|
||||
ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG);
|
||||
backlog_pointer++;
|
||||
if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; }
|
||||
#endif
|
||||
backlog_mutex = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -512,7 +512,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_ARDUINO_OTA // Disable support for Arduino OTA
|
||||
#undef USE_DOMOTICZ // Disable Domoticz
|
||||
#undef USE_HOME_ASSISTANT // Disable Home Assistant
|
||||
#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set
|
||||
//#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set
|
||||
#undef USE_KNX // Disable KNX IP Protocol Support
|
||||
//#undef USE_WEBSERVER // Disable Webserver
|
||||
#undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code)
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
#ifndef _TASMOTA_VERSION_H_
|
||||
#define _TASMOTA_VERSION_H_
|
||||
|
||||
const uint32_t VERSION = 0x07010200;
|
||||
const uint32_t VERSION = 0x07020000;
|
||||
|
||||
#endif // _TASMOTA_VERSION_H_
|
||||
|
|
|
@ -414,7 +414,8 @@ const char HTTP_FORM_WIFI[] PROGMEM =
|
|||
"<p><b>" D_AP1_PASSWORD "</b><input type='checkbox' onclick='sp(\"p1\")'><br><input id='p1' type='password' placeholder='" D_AP1_PASSWORD "' value='" D_ASTERISK_PWD "'></p>"
|
||||
"<p><b>" D_AP2_SSID "</b> (" STA_SSID2 ")<br><input id='s2' placeholder='" STA_SSID2 "' value='%s'></p>"
|
||||
"<p><b>" D_AP2_PASSWORD "</b><input type='checkbox' onclick='sp(\"p2\")'><br><input id='p2' type='password' placeholder='" D_AP2_PASSWORD "' value='" D_ASTERISK_PWD "'></p>"
|
||||
"<p><b>" D_HOSTNAME "</b> (%s)<br><input id='h' placeholder='%s' value='%s'></p>";
|
||||
"<p><b>" D_HOSTNAME "</b> (%s)<br><input id='h' placeholder='%s' value='%s'></p>"
|
||||
"<p><b>" D_CORS_DOMAIN "</b><input id='c' placeholder='" CORS_DOMAIN "' value='%s'></p>";
|
||||
|
||||
const char HTTP_FORM_LOG1[] PROGMEM =
|
||||
"<fieldset><legend><b> " D_LOGGING_PARAMETERS " </b>"
|
||||
|
@ -597,7 +598,13 @@ void StartWebserver(int type, IPAddress ipweb)
|
|||
WebServer->begin(); // Web server start
|
||||
}
|
||||
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 "), my_hostname, (Wifi.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"), my_hostname, (Wifi.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"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str());
|
||||
#endif // LWIP_IPV6 = 1
|
||||
rules_flag.http_init = 1;
|
||||
}
|
||||
if (type) { Web.state = type; }
|
||||
|
@ -651,8 +658,8 @@ void PollDnsWebserver(void)
|
|||
|
||||
bool WebAuthenticate(void)
|
||||
{
|
||||
if (Settings.web_password[0] != 0 && HTTP_MANAGER_RESET_ONLY != Web.state) {
|
||||
return WebServer->authenticate(WEB_USERNAME, Settings.web_password);
|
||||
if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) {
|
||||
return WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
@ -673,8 +680,8 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
|
|||
|
||||
void HttpHeaderCors(void)
|
||||
{
|
||||
if (Settings.flag3.cors_enabled) { // SetOption73 - Enable HTTP CORS
|
||||
WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*"));
|
||||
if (strlen(SettingsText(SET_CORS))) {
|
||||
WebServer->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -809,7 +816,7 @@ void WSContentSend_PD(const char* formatP, ...) // Content send snprintf_P ch
|
|||
|
||||
void WSContentStart_P(const char* title, bool auth)
|
||||
{
|
||||
if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) {
|
||||
if (auth && strlen(SettingsText(SET_WEBPWD)) && !WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) {
|
||||
return WebServer->requestAuthentication();
|
||||
}
|
||||
|
||||
|
@ -818,7 +825,7 @@ void WSContentStart_P(const char* title, bool auth)
|
|||
if (title != nullptr) {
|
||||
char ctitle[strlen_P(title) +1];
|
||||
strcpy_P(ctitle, title); // Get title from flash to RAM
|
||||
WSContentSend_P(HTTP_HEADER, Settings.friendlyname[0], ctitle);
|
||||
WSContentSend_P(HTTP_HEADER, SettingsText(SET_FRIENDLYNAME1), ctitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -862,7 +869,7 @@ void WSContentSendStyle_P(const char* formatP, ...)
|
|||
WebColor(COL_TEXT_WARNING),
|
||||
#endif
|
||||
WebColor(COL_TITLE),
|
||||
ModuleName().c_str(), Settings.friendlyname[0]);
|
||||
ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1));
|
||||
if (Settings.flag3.gui_hostname_ip) { // SetOption53 - Show hostanme and IP address in GUI main menu
|
||||
bool lip = (static_cast<uint32_t>(WiFi.localIP()) != 0);
|
||||
bool sip = (static_cast<uint32_t>(WiFi.softAPIP()) != 0);
|
||||
|
@ -986,10 +993,10 @@ void HandleRoot(void)
|
|||
|
||||
if (WifiIsInManagerMode()) {
|
||||
#ifndef FIRMWARE_MINIMAL
|
||||
if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
|
||||
if (strlen(SettingsText(SET_WEBPWD)) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
|
||||
HandleWifiLogin();
|
||||
} else {
|
||||
if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
|
||||
if (!strlen(SettingsText(SET_WEBPWD)) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
|
||||
HandleWifiConfiguration();
|
||||
} else {
|
||||
// wrong user and pass
|
||||
|
@ -1106,6 +1113,21 @@ void HandleRoot(void)
|
|||
} else {
|
||||
#endif // USE_SONOFF_IFAN
|
||||
for (uint32_t idx = 1; idx <= devices_present; idx++) {
|
||||
#ifdef USE_SHUTTER
|
||||
if (Settings.flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
||||
bool shutter_used = false;
|
||||
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
|
||||
if (Settings.shutter_startrelay[i] == (((idx -1) & 0xFFFFFFFE) +1)) {
|
||||
shutter_used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shutter_used) {
|
||||
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (idx % 2) ? "▲" : "▼" , "");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif // USE_SHUTTER
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
|
||||
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : "");
|
||||
}
|
||||
|
@ -1638,11 +1660,11 @@ void HandleWifiConfiguration(void)
|
|||
int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i]));
|
||||
int auth = WiFi.encryptionType(indices[i]);
|
||||
char encryption[20];
|
||||
WSContentSend_P(PSTR("<div><a href='#p' onclick='c(this)'>%s</a> (%d) <span class='q'>%s %d%%</span></div>"),
|
||||
WSContentSend_P(PSTR("<div><a href='#p' onclick='c(this)'>%s</a> (%d) <span class='q'>%s %d%% (%d dBm)</span></div>"),
|
||||
HtmlEscape(WiFi.SSID(indices[i])).c_str(),
|
||||
WiFi.channel(indices[i]),
|
||||
GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType),
|
||||
quality
|
||||
quality, WiFi.RSSI()
|
||||
);
|
||||
delay(0);
|
||||
|
||||
|
@ -1654,7 +1676,7 @@ void HandleWifiConfiguration(void)
|
|||
}
|
||||
|
||||
// As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception
|
||||
WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1671,22 +1693,25 @@ void HandleWifiConfiguration(void)
|
|||
|
||||
void WifiSaveSettings(void)
|
||||
{
|
||||
char tmp[sizeof(Settings.sta_pwd[0])]; // Max length is currently 65
|
||||
char tmp[100]; // Max length is currently 65
|
||||
|
||||
WebGetArg("h", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.hostname, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp, sizeof(Settings.hostname));
|
||||
if (strstr(Settings.hostname, "%") != nullptr) {
|
||||
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
|
||||
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));
|
||||
strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? STA_SSID1 : tmp, sizeof(Settings.sta_ssid[0]));
|
||||
SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp);
|
||||
WebGetArg("s2", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.sta_ssid[1], (!strlen(tmp)) ? STA_SSID2 : tmp, sizeof(Settings.sta_ssid[1]));
|
||||
SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp);
|
||||
WebGetArg("p1", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0]));
|
||||
SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp);
|
||||
WebGetArg("p2", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1]));
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1]);
|
||||
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));
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
@ -1723,7 +1748,7 @@ void HandleLoggingConfiguration(void)
|
|||
}
|
||||
WSContentSend_P(PSTR("</select></p>"));
|
||||
}
|
||||
WSContentSend_P(HTTP_FORM_LOG2, Settings.syslog_host, Settings.syslog_port, Settings.tele_period);
|
||||
WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period);
|
||||
WSContentSend_P(HTTP_FORM_END);
|
||||
WSContentSpaceButton(BUTTON_CONFIGURATION);
|
||||
WSContentStop();
|
||||
|
@ -1731,7 +1756,7 @@ void HandleLoggingConfiguration(void)
|
|||
|
||||
void LoggingSaveSettings(void)
|
||||
{
|
||||
char tmp[sizeof(Settings.syslog_host)]; // Max length is currently 33
|
||||
char tmp[100]; // Max length is currently 33
|
||||
|
||||
WebGetArg("l0", tmp, sizeof(tmp));
|
||||
SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp));
|
||||
|
@ -1742,7 +1767,7 @@ void LoggingSaveSettings(void)
|
|||
WebGetArg("l3", tmp, sizeof(tmp));
|
||||
SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp));
|
||||
WebGetArg("lh", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.syslog_host, (!strlen(tmp)) ? SYS_LOG_HOST : tmp, sizeof(Settings.syslog_host));
|
||||
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));
|
||||
|
@ -1751,7 +1776,7 @@ void LoggingSaveSettings(void)
|
|||
Settings.tele_period = 10; // Do not allow periods < 10 seconds
|
||||
}
|
||||
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, Settings.syslog_host, Settings.syslog_port, Settings.tele_period);
|
||||
Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
@ -1787,7 +1812,7 @@ void HandleOtherConfiguration(void)
|
|||
(i) ? stemp : "",
|
||||
i,
|
||||
(i) ? stemp : "",
|
||||
Settings.friendlyname[i]);
|
||||
SettingsText(SET_FRIENDLYNAME1 + i));
|
||||
}
|
||||
|
||||
#ifdef USE_EMULATION
|
||||
|
@ -1819,10 +1844,10 @@ void OtherSaveSettings(void)
|
|||
{
|
||||
char tmp[128];
|
||||
char webindex[5];
|
||||
char friendlyname[sizeof(Settings.friendlyname[0])];
|
||||
char friendlyname[TOPSZ];
|
||||
|
||||
WebGetArg("wp", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.web_password, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.web_password : tmp, sizeof(Settings.web_password));
|
||||
SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp);
|
||||
Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); // SetOption3 - Enable MQTT
|
||||
#ifdef USE_EMULATION
|
||||
WebGetArg("b2", tmp, sizeof(tmp));
|
||||
|
@ -1833,8 +1858,8 @@ void OtherSaveSettings(void)
|
|||
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);
|
||||
strlcpy(Settings.friendlyname[i], (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp, sizeof(Settings.friendlyname[i]));
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s%s %s"), log_data, (i) ? "," : "", Settings.friendlyname[i]);
|
||||
SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp);
|
||||
snprintf_P(log_data, sizeof(log_data), PSTR("%s%s %s"), log_data, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i));
|
||||
}
|
||||
AddLog(LOG_LEVEL_INFO);
|
||||
WebGetArg("t1", tmp, sizeof(tmp));
|
||||
|
@ -1866,8 +1891,8 @@ void HandleBackupConfiguration(void)
|
|||
|
||||
char attachment[100];
|
||||
|
||||
// char friendlyname[sizeof(Settings.friendlyname[0])];
|
||||
// snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version);
|
||||
// char friendlyname[TOPSZ];
|
||||
// snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, SettingsText(SET_FRIENDLYNAME1)), my_version);
|
||||
|
||||
char hostname[sizeof(my_hostname)];
|
||||
snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version);
|
||||
|
@ -1967,11 +1992,17 @@ void HandleInformation(void)
|
|||
if (IsModuleIfan()) { maxfn = 1; }
|
||||
#endif // USE_SONOFF_IFAN
|
||||
for (uint32_t i = 0; i < maxfn; i++) {
|
||||
WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]);
|
||||
WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i));
|
||||
}
|
||||
WSContentSend_P(PSTR("}1}2 ")); // Empty line
|
||||
WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()));
|
||||
WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI());
|
||||
WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.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<uint32_t>(WiFi.localIP()) != 0) {
|
||||
WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str());
|
||||
WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str());
|
||||
|
@ -1986,17 +2017,12 @@ void HandleInformation(void)
|
|||
}
|
||||
WSContentSend_P(PSTR("}1}2 ")); // Empty line
|
||||
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s%s"), Settings.mqtt_user, Settings.mqtt_host);
|
||||
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);
|
||||
#else
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), Settings.mqtt_host);
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port);
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), Settings.mqtt_user);
|
||||
#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"), Settings.mqtt_topic);
|
||||
// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), Settings.mqtt_grptopic);
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC));
|
||||
// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), SettingsText(SET_MQTT_GRP_TOPIC));
|
||||
WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, ""));
|
||||
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, ""));
|
||||
|
@ -2055,7 +2081,7 @@ void HandleUpgradeFirmware(void)
|
|||
|
||||
WSContentStart_P(S_FIRMWARE_UPGRADE);
|
||||
WSContentSendStyle();
|
||||
WSContentSend_P(HTTP_FORM_UPG, Settings.ota_url);
|
||||
WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL));
|
||||
WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE);
|
||||
WSContentSpaceButton(BUTTON_MAIN);
|
||||
WSContentStop();
|
||||
|
@ -2068,12 +2094,12 @@ void HandleUpgradeFirmwareStart(void)
|
|||
{
|
||||
if (!HttpCheckPriviledgedAccess()) { return; }
|
||||
|
||||
char command[sizeof(Settings.ota_url) + 10]; // OtaUrl
|
||||
char command[128]; // OtaUrl
|
||||
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
|
||||
WifiConfigCounter();
|
||||
|
||||
char otaurl[sizeof(Settings.ota_url)];
|
||||
char otaurl[101];
|
||||
WebGetArg("o", otaurl, sizeof(otaurl));
|
||||
if (strlen(otaurl)) {
|
||||
snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl);
|
||||
|
@ -2382,12 +2408,12 @@ void HandleHttpCommand(void)
|
|||
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
|
||||
|
||||
bool valid = true;
|
||||
if (Settings.web_password[0] != 0) {
|
||||
char tmp1[sizeof(Settings.web_password)];
|
||||
if (strlen(SettingsText(SET_WEBPWD))) {
|
||||
char tmp1[33];
|
||||
WebGetArg("user", tmp1, sizeof(tmp1));
|
||||
char tmp2[sizeof(Settings.web_password)];
|
||||
char tmp2[strlen(SettingsText(SET_WEBPWD)) +1];
|
||||
WebGetArg("password", tmp2, sizeof(tmp2));
|
||||
if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = false; }
|
||||
if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { valid = false; }
|
||||
}
|
||||
|
||||
WSContentBegin(200, CT_JSON);
|
||||
|
@ -2698,7 +2724,7 @@ const char kWebCommands[] PROGMEM = "|" // No prefix
|
|||
#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_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR "|" D_CMND_CORS;
|
||||
|
||||
void (* const WebCommand[])(void) PROGMEM = {
|
||||
#ifdef USE_EMULATION
|
||||
|
@ -2707,7 +2733,7 @@ void (* const WebCommand[])(void) PROGMEM = {
|
|||
#ifdef USE_SENDMAIL
|
||||
&CmndSendmail,
|
||||
#endif
|
||||
&CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor };
|
||||
&CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor, &CmndCors };
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
|
@ -2760,9 +2786,9 @@ void CmndWebServer(void)
|
|||
|
||||
void CmndWebPassword(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) {
|
||||
strlcpy(Settings.web_password, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password));
|
||||
ResponseCmndChar(Settings.web_password);
|
||||
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);
|
||||
}
|
||||
|
@ -2828,6 +2854,14 @@ void CmndWebSensor(void)
|
|||
ResponseJsonEnd();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
|
|
@ -91,10 +91,6 @@ tls_dir_t tls_dir; // memory copy of tls_dir from flash
|
|||
|
||||
#endif // USE_MQTT_AWS_IOT
|
||||
|
||||
// A typical AWS IoT endpoint is 50 characters long, it does not fit
|
||||
// in MqttHost field (32 chars). We need to concatenate both MqttUser and MqttHost
|
||||
char AWS_endpoint[65]; // aWS IOT endpoint, concatenation of user and host
|
||||
|
||||
// check whether the fingerprint is filled with a single value
|
||||
// Filled with 0x00 = accept any fingerprint and learn it for next time
|
||||
// Filled with 0xFF = accept any fingerpring forever
|
||||
|
@ -106,21 +102,6 @@ bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
void setLongMqttHost(const char *mqtt_host) {
|
||||
if (strlen(mqtt_host) <= sizeof(Settings.mqtt_host)) {
|
||||
strlcpy(Settings.mqtt_host, mqtt_host, sizeof(Settings.mqtt_host));
|
||||
Settings.mqtt_user[0] = 0;
|
||||
} else {
|
||||
// need to split in mqtt_user first then mqtt_host
|
||||
strlcpy(Settings.mqtt_user, mqtt_host, sizeof(Settings.mqtt_user));
|
||||
strlcpy(Settings.mqtt_host, &mqtt_host[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host));
|
||||
}
|
||||
strlcpy(AWS_endpoint, mqtt_host, sizeof(AWS_endpoint));
|
||||
}
|
||||
#endif // USE_MQTT_AWS_IOT
|
||||
|
||||
#endif // USE_MQTT_TLS
|
||||
|
||||
void MakeValidMqtt(uint32_t option, char* str)
|
||||
|
@ -165,10 +146,10 @@ void MqttDiscoverServer(void)
|
|||
}
|
||||
}
|
||||
#endif // MDNS_HOSTNAME
|
||||
snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(i).toString().c_str());
|
||||
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(), Settings.mqtt_host, Settings.mqtt_port);
|
||||
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 // MQTT_HOST_DISCOVERY
|
||||
|
@ -202,8 +183,6 @@ void MqttInit(void)
|
|||
tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024);
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
snprintf_P(AWS_endpoint, sizeof(AWS_endpoint), PSTR("%s%s"), Settings.mqtt_user, Settings.mqtt_host);
|
||||
|
||||
loadTlsDir(); // load key and certificate data from Flash
|
||||
tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
|
||||
AWS_IoT_Private_Key,
|
||||
|
@ -261,8 +240,8 @@ void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len
|
|||
if (data_len >= MQTT_MAX_PACKET_SIZE) { return; }
|
||||
|
||||
// Do not execute multiple times if Prefix1 equals Prefix2
|
||||
if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) {
|
||||
char *str = strstr(mqtt_topic, Settings.mqtt_prefix[0]);
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1));
|
||||
if ((str == mqtt_topic) && mqtt_cmnd_publish) {
|
||||
if (mqtt_cmnd_publish > 3) {
|
||||
mqtt_cmnd_publish -= 3;
|
||||
|
@ -330,8 +309,8 @@ void MqttPublishLogging(const char *mxtime)
|
|||
GetTopic_P(stopic, STAT, mqtt_topic, romram);
|
||||
|
||||
char *me;
|
||||
if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) {
|
||||
me = strstr(stopic, Settings.mqtt_prefix[0]);
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
me = strstr(stopic, SettingsText(SET_MQTTPREFIX1));
|
||||
if (me == stopic) {
|
||||
mqtt_cmnd_publish += 3;
|
||||
}
|
||||
|
@ -389,8 +368,8 @@ void MqttPublish(const char* topic, bool retained)
|
|||
retained = false; // AWS IoT does not support retained, it will disconnect if received
|
||||
#endif
|
||||
|
||||
if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) {
|
||||
me = strstr(topic,Settings.mqtt_prefix[0]);
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
me = strstr(topic, SettingsText(SET_MQTTPREFIX1));
|
||||
if (me == topic) {
|
||||
mqtt_cmnd_publish += 3;
|
||||
}
|
||||
|
@ -505,11 +484,7 @@ void MqttDisconnected(int state)
|
|||
|
||||
MqttClient.disconnect();
|
||||
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), AWS_endpoint, Settings.mqtt_port, state, Mqtt.retry_counter);
|
||||
#else
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, Mqtt.retry_counter);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -533,7 +508,7 @@ void MqttConnected(void)
|
|||
|
||||
GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#"));
|
||||
MqttSubscribe(stopic);
|
||||
if (strstr_P(Settings.mqtt_fulltopic, MQTT_TOKEN_TOPIC) != nullptr) {
|
||||
if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) {
|
||||
GetGroupTopic_P(stopic, PSTR("#")); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing/<grouptopic>/# or SetOption75 1: cmnd/<grouptopic>
|
||||
MqttSubscribe(stopic);
|
||||
GetFallbackTopic_P(stopic, PSTR("#"));
|
||||
|
@ -550,12 +525,23 @@ void MqttConnected(void)
|
|||
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, my_hostname, WiFi.localIP().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, my_hostname, WiFi.localIP().toString().c_str());
|
||||
#endif // LWIP_IPV6 = 1
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2"));
|
||||
}
|
||||
#endif // USE_WEBSERVER
|
||||
Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), GetResetReasonInfo().c_str());
|
||||
Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":"));
|
||||
if (ResetReason() == REASON_EXCEPTION_RST) {
|
||||
CrashDump();
|
||||
} else {
|
||||
ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str());
|
||||
}
|
||||
ResponseJsonEnd();
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3"));
|
||||
MqttPublishAllPowerState();
|
||||
if (Settings.tele_period) {
|
||||
|
@ -583,7 +569,7 @@ void MqttReconnect(void)
|
|||
MqttDiscoverServer();
|
||||
#endif // MQTT_HOST_DISCOVERY
|
||||
#endif // USE_DISCOVERY
|
||||
if (!strlen(Settings.mqtt_host) || !Settings.mqtt_port) {
|
||||
if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) {
|
||||
Mqtt.allowed = false;
|
||||
}
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
|
@ -610,8 +596,12 @@ void MqttReconnect(void)
|
|||
|
||||
char *mqtt_user = nullptr;
|
||||
char *mqtt_pwd = nullptr;
|
||||
if (strlen(Settings.mqtt_user) > 0) mqtt_user = Settings.mqtt_user;
|
||||
if (strlen(Settings.mqtt_pwd) > 0) mqtt_pwd = Settings.mqtt_pwd;
|
||||
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_OFFLINE);
|
||||
|
@ -634,10 +624,8 @@ void MqttReconnect(void)
|
|||
tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
|
||||
AWS_IoT_Private_Key,
|
||||
0xFFFF /* all usages, don't care */, 0);
|
||||
MqttClient.setServer(AWS_endpoint, Settings.mqtt_port);
|
||||
#else
|
||||
MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);
|
||||
#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)
|
||||
|
@ -651,7 +639,7 @@ void MqttReconnect(void)
|
|||
tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints);
|
||||
#endif
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), AWS_endpoint);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST));
|
||||
//if (MqttClient.connect(mqtt_client, nullptr, nullptr, nullptr, 0, false, nullptr)) {
|
||||
if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) {
|
||||
#else
|
||||
|
@ -711,7 +699,7 @@ void MqttCheck(void)
|
|||
if (!Mqtt.retry_counter) {
|
||||
#ifdef USE_DISCOVERY
|
||||
#ifdef MQTT_HOST_DISCOVERY
|
||||
if (!strlen(Settings.mqtt_host) && !Wifi.mdns_begun) { return; }
|
||||
if (!strlen(SettingsText(SET_MQTT_HOST)) && !Wifi.mdns_begun) { return; }
|
||||
#endif // MQTT_HOST_DISCOVERY
|
||||
#endif // USE_DISCOVERY
|
||||
MqttReconnect();
|
||||
|
@ -752,18 +740,18 @@ void CmndMqttFingerprint(void)
|
|||
#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT
|
||||
void CmndMqttUser(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_user))) {
|
||||
strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data, sizeof(Settings.mqtt_user));
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data);
|
||||
restart_flag = 2;
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_user);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_USER));
|
||||
}
|
||||
|
||||
void CmndMqttPassword(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_pwd))) {
|
||||
strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data, sizeof(Settings.mqtt_pwd));
|
||||
ResponseCmndChar(Settings.mqtt_pwd);
|
||||
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);
|
||||
|
@ -781,19 +769,11 @@ void CmndMqttlog(void)
|
|||
|
||||
void CmndMqttHost(void)
|
||||
{
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len <= sizeof(Settings.mqtt_host) + sizeof(Settings.mqtt_user) - 2)) {
|
||||
setLongMqttHost((SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data);
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data);
|
||||
restart_flag = 2;
|
||||
}
|
||||
ResponseCmndChar(AWS_endpoint);
|
||||
#else
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_host))) {
|
||||
strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data, sizeof(Settings.mqtt_host));
|
||||
restart_flag = 2;
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_host);
|
||||
#endif
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_HOST));
|
||||
}
|
||||
|
||||
void CmndMqttPort(void)
|
||||
|
@ -817,11 +797,11 @@ void CmndMqttRetry(void)
|
|||
void CmndStateText(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.state_text[0]))) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) {
|
||||
if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_';
|
||||
}
|
||||
strlcpy(Settings.state_text[XdrvMailbox.index -1], XdrvMailbox.data, sizeof(Settings.state_text[0]));
|
||||
SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1));
|
||||
}
|
||||
|
@ -829,40 +809,41 @@ void CmndStateText(void)
|
|||
|
||||
void CmndMqttClient(void)
|
||||
{
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_client))) {
|
||||
strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data, sizeof(Settings.mqtt_client));
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
||||
SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data);
|
||||
restart_flag = 2;
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_client);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_CLIENT));
|
||||
}
|
||||
|
||||
void CmndFullTopic(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_fulltopic))) {
|
||||
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, Settings.mqtt_fulltopic)) {
|
||||
if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) {
|
||||
Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
|
||||
strlcpy(Settings.mqtt_fulltopic, stemp1, sizeof(Settings.mqtt_fulltopic));
|
||||
SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1);
|
||||
restart_flag = 2;
|
||||
}
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_fulltopic);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC));
|
||||
}
|
||||
|
||||
void CmndPrefix(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_prefix[0]))) {
|
||||
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
MakeValidMqtt(0, XdrvMailbox.data);
|
||||
strlcpy(Settings.mqtt_prefix[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?SUB_PREFIX:(2==XdrvMailbox.index)?PUB_PREFIX:PUB_PREFIX2 : XdrvMailbox.data, sizeof(Settings.mqtt_prefix[0]));
|
||||
// if (Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] == '/') Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] = 0;
|
||||
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(Settings.mqtt_prefix[XdrvMailbox.index -1]);
|
||||
ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -888,60 +869,60 @@ void CmndPublish(void)
|
|||
|
||||
void CmndGroupTopic(void)
|
||||
{
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_grptopic))) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
MakeValidMqtt(0, XdrvMailbox.data);
|
||||
if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); }
|
||||
strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data, sizeof(Settings.mqtt_grptopic));
|
||||
SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data);
|
||||
restart_flag = 2;
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_grptopic);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC));
|
||||
}
|
||||
|
||||
void CmndTopic(void)
|
||||
{
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_topic))) {
|
||||
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, Settings.mqtt_topic)) {
|
||||
if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) {
|
||||
Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
|
||||
strlcpy(Settings.mqtt_topic, stemp1, sizeof(Settings.mqtt_topic));
|
||||
SettingsUpdateText(SET_MQTT_TOPIC, stemp1);
|
||||
restart_flag = 2;
|
||||
}
|
||||
}
|
||||
ResponseCmndChar(Settings.mqtt_topic);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_TOPIC));
|
||||
}
|
||||
|
||||
void CmndButtonTopic(void)
|
||||
{
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.button_topic))) {
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
||||
MakeValidMqtt(0, XdrvMailbox.data);
|
||||
if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); }
|
||||
switch (Shortcut()) {
|
||||
case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break;
|
||||
case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break;
|
||||
case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break;
|
||||
default: strlcpy(Settings.button_topic, XdrvMailbox.data, sizeof(Settings.button_topic));
|
||||
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(Settings.button_topic);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC));
|
||||
}
|
||||
|
||||
void CmndSwitchTopic(void)
|
||||
{
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.switch_topic))) {
|
||||
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
||||
MakeValidMqtt(0, XdrvMailbox.data);
|
||||
if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); }
|
||||
switch (Shortcut()) {
|
||||
case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break;
|
||||
case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break;
|
||||
case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break;
|
||||
default: strlcpy(Settings.switch_topic, XdrvMailbox.data, sizeof(Settings.switch_topic));
|
||||
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(Settings.switch_topic);
|
||||
ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC));
|
||||
}
|
||||
|
||||
void CmndButtonRetain(void)
|
||||
|
@ -1071,6 +1052,9 @@ void CmndTlsKey(void) {
|
|||
}
|
||||
memcpy_P(spi_buffer, tls_spi_start, tls_spi_len);
|
||||
|
||||
// remove any white space from the base64
|
||||
RemoveAllSpaces(XdrvMailbox.data);
|
||||
|
||||
// allocate buffer for decoded base64
|
||||
uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data);
|
||||
uint8_t *bin_buf = nullptr;
|
||||
|
@ -1206,22 +1190,18 @@ void HandleMqttConfiguration(void)
|
|||
return;
|
||||
}
|
||||
|
||||
char str[sizeof(Settings.mqtt_client)];
|
||||
char str[33];
|
||||
|
||||
WSContentStart_P(S_CONFIGURE_MQTT);
|
||||
WSContentSendStyle();
|
||||
WSContentSend_P(HTTP_FORM_MQTT1,
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
AWS_endpoint,
|
||||
#else
|
||||
Settings.mqtt_host,
|
||||
#endif
|
||||
SettingsText(SET_MQTT_HOST),
|
||||
Settings.mqtt_port,
|
||||
Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, Settings.mqtt_client);
|
||||
Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT));
|
||||
WSContentSend_P(HTTP_FORM_MQTT2,
|
||||
(Settings.mqtt_user[0] == '\0') ? "0" : Settings.mqtt_user,
|
||||
Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, Settings.mqtt_topic,
|
||||
MQTT_FULLTOPIC, MQTT_FULLTOPIC, Settings.mqtt_fulltopic);
|
||||
(!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();
|
||||
|
@ -1239,32 +1219,28 @@ void MqttSaveSettings(void)
|
|||
WebGetArg("mf", tmp, sizeof(tmp));
|
||||
strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2));
|
||||
MakeValidMqtt(1, stemp2);
|
||||
if ((strcmp(stemp, Settings.mqtt_topic)) || (strcmp(stemp2, Settings.mqtt_fulltopic))) {
|
||||
if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) {
|
||||
Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format
|
||||
MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic
|
||||
}
|
||||
strlcpy(Settings.mqtt_topic, stemp, sizeof(Settings.mqtt_topic));
|
||||
strlcpy(Settings.mqtt_fulltopic, stemp2, sizeof(Settings.mqtt_fulltopic));
|
||||
SettingsUpdateText(SET_MQTT_TOPIC, stemp);
|
||||
SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2);
|
||||
WebGetArg("mh", tmp, sizeof(tmp));
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
setLongMqttHost((!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp);
|
||||
#else
|
||||
strlcpy(Settings.mqtt_host, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_host));
|
||||
#endif
|
||||
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);
|
||||
WebGetArg("mc", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.mqtt_client, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp, sizeof(Settings.mqtt_client));
|
||||
SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
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"),
|
||||
AWS_endpoint, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_topic, Settings.mqtt_fulltopic);
|
||||
SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC));
|
||||
#else // USE_MQTT_AWS_IOT
|
||||
WebGetArg("mu", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user));
|
||||
SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp);
|
||||
WebGetArg("mp", tmp, sizeof(tmp));
|
||||
strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd));
|
||||
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"),
|
||||
Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_topic, Settings.mqtt_fulltopic);
|
||||
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 // USE_WEBSERVER
|
||||
|
|
|
@ -554,10 +554,10 @@ const char HTTP_TIMER_SCRIPT2[] PROGMEM =
|
|||
"o=qs('#ho');"
|
||||
"e=o.childElementCount;"
|
||||
"if(b==1){"
|
||||
"qs('#dr').disabled='';"
|
||||
"qs('#dr').style.visibility='';"
|
||||
"if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" // Create offset hours select options
|
||||
"}else{"
|
||||
"qs('#dr').disabled='disabled';"
|
||||
"qs('#dr').style.visibility='hidden';"
|
||||
"if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" // Create hours select options
|
||||
"}"
|
||||
"}";
|
||||
|
@ -583,7 +583,7 @@ const char HTTP_TIMER_SCRIPT3[] PROGMEM =
|
|||
"if(m==0){s|=l;}" // Get time
|
||||
#ifdef USE_SUNRISE
|
||||
"if((m==1)||(m==2)){"
|
||||
"if(qs('#dr').selectedIndex>0){l+=720;}" // If negative offset, add 12h to given offset time
|
||||
"if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" // If negative offset and delta-time > 0, add 12h to given offset time
|
||||
"s|=l&0x7FF;" // Save offset instead of time
|
||||
"}"
|
||||
#endif
|
||||
|
|
|
@ -217,7 +217,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
|||
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 = Settings.mems[i];
|
||||
rule_param = SettingsText(SET_MEM1 + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
|||
String ucommand = commands;
|
||||
ucommand.toUpperCase();
|
||||
// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception
|
||||
if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception
|
||||
if ((ucommand.indexOf("EVENT ") != -1) && (ucommand.indexOf("BACKLOG ") == -1)) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception
|
||||
|
||||
RulesVarReplace(commands, F("%VALUE%"), Rules.event_value);
|
||||
for (uint32_t i = 0; i < MAX_RULE_VARS; i++) {
|
||||
|
@ -447,11 +447,12 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
|||
}
|
||||
for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) {
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1);
|
||||
RulesVarReplace(commands, stemp, Settings.mems[i]);
|
||||
RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i));
|
||||
}
|
||||
RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight()));
|
||||
RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime()));
|
||||
RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL));
|
||||
RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC));
|
||||
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
|
||||
RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0)));
|
||||
RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1)));
|
||||
|
@ -607,7 +608,7 @@ void RulesEvery50ms(void)
|
|||
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, Settings.mems[i]);
|
||||
snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i));
|
||||
RulesProcessEvent(json_event);
|
||||
break;
|
||||
}
|
||||
|
@ -1001,7 +1002,7 @@ bool findNextVariableValue(char * &pVarname, float &value)
|
|||
} else if (sVarName.startsWith(F("MEM"))) {
|
||||
int index = sVarName.substring(3).toInt();
|
||||
if (index > 0 && index <= MAX_RULE_MEMS) {
|
||||
value = CharToFloat(Settings.mems[index -1]);
|
||||
value = CharToFloat(SettingsText(SET_MEM1 + index -1));
|
||||
}
|
||||
} else if (sVarName.equals(F("TIME"))) {
|
||||
value = MinutesPastMidnight();
|
||||
|
@ -1809,23 +1810,23 @@ void CmndMemory(void)
|
|||
if (!XdrvMailbox.usridx) {
|
||||
mqtt_data[0] = '\0';
|
||||
for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) {
|
||||
ResponseAppend_P(PSTR("%c\"Mem%d\":\"%s\""), (i) ? ',' : '{', i +1, Settings.mems[i]);
|
||||
ResponseAppend_P(PSTR("%c\"Mem%d\":\"%s\""), (i) ? ',' : '{', i +1, SettingsText(SET_MEM1 +i));
|
||||
}
|
||||
ResponseJsonEnd();
|
||||
} else {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
#ifdef USE_EXPRESSION
|
||||
if (XdrvMailbox.data[0] == '=') { // Spaces already been skipped in data
|
||||
dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, Settings.mems[XdrvMailbox.index -1]);
|
||||
dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1));
|
||||
} else {
|
||||
strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1]));
|
||||
SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
|
||||
}
|
||||
#else
|
||||
strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1]));
|
||||
SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
|
||||
#endif // USE_EXPRESSION
|
||||
bitSet(Rules.mems_event, XdrvMailbox.index -1);
|
||||
}
|
||||
ResponseCmndIdxChar(Settings.mems[XdrvMailbox.index -1]);
|
||||
ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1030,8 +1030,10 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
|
|||
if ((*jo).is<char*>(vn)) {
|
||||
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;
|
||||
|
@ -1039,6 +1041,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
|
|||
if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE);
|
||||
return lp+len;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (fp) {
|
||||
if (!strncmp(vn.c_str(),"Epoch",5)) {
|
||||
|
@ -1047,6 +1050,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
|
|||
*fp=CharToFloat((char*)str_value);
|
||||
}
|
||||
}
|
||||
nexit:
|
||||
*vtype=NUM_RES;
|
||||
tind->bits.constant=1;
|
||||
tind->bits.is_string=0;
|
||||
|
@ -1366,7 +1370,7 @@ chknext:
|
|||
goto exit;
|
||||
}
|
||||
if (!strncmp(vname,"gtopic",6)) {
|
||||
if (sp) strlcpy(sp,Settings.mqtt_grptopic,glob_script_mem.max_ssize);
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
}
|
||||
break;
|
||||
|
@ -1523,15 +1527,15 @@ chknext:
|
|||
goto exit;
|
||||
}
|
||||
if (!strncmp(vname,"prefix1",7)) {
|
||||
if (sp) strlcpy(sp,Settings.mqtt_prefix[0],glob_script_mem.max_ssize);
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX1),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
}
|
||||
if (!strncmp(vname,"prefix2",7)) {
|
||||
if (sp) strlcpy(sp,Settings.mqtt_prefix[1],glob_script_mem.max_ssize);
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX2),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
}
|
||||
if (!strncmp(vname,"prefix3",7)) {
|
||||
if (sp) strlcpy(sp,Settings.mqtt_prefix[2],glob_script_mem.max_ssize);
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX3),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
}
|
||||
if (!strncmp(vname,"pow(",4)) {
|
||||
|
@ -1740,7 +1744,7 @@ chknext:
|
|||
goto strexit;
|
||||
}
|
||||
if (!strncmp(vname,"topic",5)) {
|
||||
if (sp) strlcpy(sp,Settings.mqtt_topic,glob_script_mem.max_ssize);
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
}
|
||||
#ifdef USE_DISPLAY
|
||||
|
@ -4843,6 +4847,12 @@ bool Xdrv10(uint8_t function)
|
|||
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);
|
||||
#endif
|
||||
break;
|
||||
case FUNC_RULES_PROCESS:
|
||||
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data);
|
||||
break;
|
||||
|
|
|
@ -499,6 +499,7 @@ void KNX_INIT(void)
|
|||
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)
|
||||
// Any device with a Power Monitoring
|
||||
if ( energy_flg != ENERGY_NONE ) {
|
||||
device_param[KNX_ENERGY_POWER-1].show = true;
|
||||
|
@ -509,6 +510,7 @@ void KNX_INIT(void)
|
|||
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;
|
||||
|
|
|
@ -46,7 +46,7 @@ const char HASS_DISCOVER_BUTTON_TOGGLE[] PROGMEM =
|
|||
|
||||
const char HASS_DISCOVER_SWITCH_TOGGLE[] PROGMEM =
|
||||
",\"value_template\":\"{%%if is_state(entity_id,\\\"on\\\")-%%}OFF{%%-else-%%}ON{%%-endif%%}\""; // A switch must maintain his state until the next update
|
||||
|
||||
|
||||
const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM =
|
||||
",\"value_template\":\"{{value_json.%s}}\"," // STATE
|
||||
"\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work
|
||||
|
@ -86,6 +86,7 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM =
|
|||
"{\"name\":\"%s\"," // dualr2 1 BTN
|
||||
"\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",")
|
||||
"\"avty_t\":\"%s\"," // tele/dualr2/LWT
|
||||
"\"frc_upd\":true," // force update for better graph representation
|
||||
"\"pl_avail\":\"" D_ONLINE "\"," // Online
|
||||
"\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline
|
||||
|
||||
|
@ -238,9 +239,9 @@ void HAssAnnounceRelayLight(void)
|
|||
char *availability_topic = stemp3;
|
||||
|
||||
if (i > MAX_FRIENDLYNAMES) {
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), Settings.friendlyname[0], i);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i);
|
||||
} else {
|
||||
snprintf_P(name, sizeof(name), Settings.friendlyname[i -1]);
|
||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 +i -1));
|
||||
}
|
||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
|
||||
|
@ -252,7 +253,7 @@ void HAssAnnounceRelayLight(void)
|
|||
Shorten(&state_topic, prefix);
|
||||
Shorten(&availability_topic, prefix);
|
||||
|
||||
Response_P(HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
|
||||
Response_P(HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2), availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str());
|
||||
TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix);
|
||||
|
||||
|
@ -323,7 +324,7 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint
|
|||
char *availability_topic = stemp2;
|
||||
char jsoname[8];
|
||||
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s%d"), Settings.friendlyname[0], key?"Switch":"Button", device+1);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s%d"), SettingsText(SET_FRIENDLYNAME1), key?"Switch":"Button", device+1);
|
||||
snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key?"SWITCH":"BUTTON", device+1);
|
||||
GetPowerDevice(value_template, device+1, sizeof(value_template),
|
||||
key + Settings.flag.device_index_enable); // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1
|
||||
|
@ -338,11 +339,11 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint
|
|||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str());
|
||||
if (strlen(prefix) > 0 ) TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix);
|
||||
if (toggle) {
|
||||
if (!key) {
|
||||
TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE, PSTR(D_RSLT_STATE), Settings.state_text[toggle?2:1]);
|
||||
if (!key) {
|
||||
TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT1 + toggle?2:1));
|
||||
} else {TryResponseAppend_P(HASS_DISCOVER_SWITCH_TOGGLE);}
|
||||
}
|
||||
else TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), Settings.state_text[toggle?2:1], Settings.state_text[0]);
|
||||
else TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT1 + toggle?2:1), SettingsText(SET_STATE_TXT1));
|
||||
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
@ -351,10 +352,10 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint
|
|||
|
||||
void HAssAnnounceSwitches(void)
|
||||
{
|
||||
char sw_topic[sizeof(Settings.switch_topic)];
|
||||
char sw_topic[TOPSZ];
|
||||
|
||||
// Send info about buttons
|
||||
char *tmp = Settings.switch_topic;
|
||||
char *tmp = SettingsText(SET_MQTT_SWITCH_TOPIC);
|
||||
Format(sw_topic, tmp, sizeof(sw_topic));
|
||||
if (!strcmp_P(sw_topic, "0") || strlen(sw_topic) == 0 ) {
|
||||
for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) {
|
||||
|
@ -368,7 +369,7 @@ void HAssAnnounceSwitches(void)
|
|||
// Check if MQTT message will be ON/OFF or TOGGLE
|
||||
if (Settings.switchmode[switch_index] == FOLLOW || Settings.switchmode[switch_index] == FOLLOW_INV ||
|
||||
Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set
|
||||
!strcmp(mqtt_topic, sw_topic) || !strcmp(Settings.mqtt_grptopic, sw_topic))
|
||||
!strcmp(mqtt_topic, sw_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), sw_topic))
|
||||
{
|
||||
toggle = 0; // MQTT message will be ON/OFF
|
||||
}
|
||||
|
@ -380,10 +381,10 @@ void HAssAnnounceSwitches(void)
|
|||
|
||||
void HAssAnnounceButtons(void)
|
||||
{
|
||||
char key_topic[sizeof(Settings.button_topic)];
|
||||
char key_topic[TOPSZ];
|
||||
|
||||
// Send info about buttons
|
||||
char *tmp = Settings.button_topic;
|
||||
char *tmp = SettingsText(SET_MQTT_BUTTON_TOPIC);
|
||||
Format(key_topic, tmp, sizeof(key_topic));
|
||||
if (!strcmp_P(key_topic, "0") || strlen(key_topic) == 0 ) {
|
||||
for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) {
|
||||
|
@ -400,7 +401,7 @@ void HAssAnnounceButtons(void)
|
|||
|
||||
// Check if MQTT message will be ON/OFF or TOGGLE
|
||||
if (Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set
|
||||
!strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic))
|
||||
!strcmp(mqtt_topic, key_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic))
|
||||
{
|
||||
toggle = 0; // MQTT message will be ON/OFF
|
||||
}
|
||||
|
@ -444,7 +445,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype)
|
|||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR));
|
||||
}
|
||||
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), Settings.friendlyname[0], sensorname, subsensortype);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, subsensortype);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
FindPrefix(state_topic, availability_topic, prefix);
|
||||
Shorten(&state_topic, prefix);
|
||||
|
@ -457,7 +458,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype)
|
|||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_TEMP, TempUnit(), sensorname);
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_HUMIDITY))) {
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_HUM, sensorname);
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE))
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE))
|
||||
|| !strcmp_P(subsensortype, PSTR(D_JSON_PRESSUREATSEALEVEL))){
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_PRESS, PressureUnit().c_str(), sensorname, subsensortype);
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_TOTAL))
|
||||
|
@ -547,7 +548,7 @@ void HAssAnnounceStatusSensor(void)
|
|||
char *state_topic = stemp1;
|
||||
char *availability_topic = stemp2;
|
||||
|
||||
snprintf_P(name, sizeof(name), PSTR("%s status"), Settings.friendlyname[0]);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1));
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE));
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
FindPrefix(state_topic, availability_topic, prefix);
|
||||
|
@ -557,7 +558,7 @@ void HAssAnnounceStatusSensor(void)
|
|||
Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), WiFi.macAddress().c_str(),
|
||||
Settings.friendlyname[0], ModuleName().c_str(), my_version, my_image);
|
||||
SettingsText(SET_FRIENDLYNAME1), ModuleName().c_str(), my_version, my_image);
|
||||
TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix);
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
@ -587,8 +588,8 @@ void HAssDiscovery(void)
|
|||
Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values
|
||||
Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT
|
||||
// Settings.light_scheme = 0; // To just control color it needs to be Scheme 0
|
||||
if (strcmp_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"))) {
|
||||
strncpy_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"), sizeof(Settings.mqtt_fulltopic));
|
||||
if (strcmp_P(SettingsText(SET_MQTT_FULLTOPIC), PSTR("%topic%/%prefix%/"))) {
|
||||
SettingsUpdateText(SET_MQTT_FULLTOPIC, "%topic%/%prefix%/");
|
||||
restart_flag = 2;
|
||||
return; // As full topic has changed do restart first before sending discovery data
|
||||
}
|
||||
|
|
|
@ -1008,7 +1008,7 @@ void DisplayLogBufferInit(void)
|
|||
|
||||
snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname);
|
||||
DisplayLogBufferAdd(buffer);
|
||||
snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), Settings.sta_ssid[Settings.sta_active]);
|
||||
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_MAC " %s"), WiFi.macAddress().c_str());
|
||||
DisplayLogBufferAdd(buffer);
|
||||
|
@ -1196,7 +1196,7 @@ void DisplayMqttSubscribe(void)
|
|||
char ntopic[TOPSZ];
|
||||
|
||||
ntopic[0] = '\0';
|
||||
strlcpy(stopic, Settings.mqtt_fulltopic, sizeof(stopic));
|
||||
strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic));
|
||||
char *tp = strtok(stopic, "/");
|
||||
while (tp != nullptr) {
|
||||
if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) {
|
||||
|
@ -1205,7 +1205,7 @@ void DisplayMqttSubscribe(void)
|
|||
strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); // Add single-level wildcards
|
||||
tp = strtok(nullptr, "/");
|
||||
}
|
||||
strncat(ntopic, Settings.mqtt_prefix[2], sizeof(ntopic) - strlen(ntopic) -1); // Subscribe to tele messages
|
||||
strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); // Subscribe to tele messages
|
||||
strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); // Add multi-level wildcard
|
||||
MqttSubscribe(ntopic);
|
||||
disp_subscribed = true;
|
||||
|
@ -1219,7 +1219,7 @@ bool DisplayMqttData(void)
|
|||
if (disp_subscribed) {
|
||||
char stopic[TOPSZ];
|
||||
|
||||
snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), Settings.mqtt_prefix[2]); // tele/
|
||||
snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); // tele/
|
||||
char *tp = strstr(XdrvMailbox.topic, stopic);
|
||||
if (tp) { // tele/tasmota/SENSOR
|
||||
if (Settings.display_mode &0x04) {
|
||||
|
|
|
@ -85,6 +85,7 @@ void RfInit(void)
|
|||
mySwitch.enableTransmit(pin[GPIO_RFSEND]);
|
||||
}
|
||||
if (pin[GPIO_RFRECV] < 99) {
|
||||
pinMode( pin[GPIO_RFRECV], INPUT);
|
||||
mySwitch.enableReceive(pin[GPIO_RFRECV]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -365,17 +365,18 @@ void HueLightStatus1(uint8_t device, String *response)
|
|||
// Any device whose friendly name start with "$" is considered hidden
|
||||
bool HueActive(uint8_t device) {
|
||||
if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; }
|
||||
return '$' != Settings.friendlyname[device-1][0];
|
||||
// return '$' != Settings.friendlyname[device-1][0];
|
||||
return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1);
|
||||
}
|
||||
|
||||
void HueLightStatus2(uint8_t device, String *response)
|
||||
{
|
||||
*response += FPSTR(HUE_LIGHTS_STATUS_JSON2);
|
||||
if (device <= MAX_FRIENDLYNAMES) {
|
||||
response->replace("{j1", Settings.friendlyname[device-1]);
|
||||
response->replace("{j1", SettingsText(SET_FRIENDLYNAME1 +device -1));
|
||||
} else {
|
||||
char fname[33];
|
||||
strcpy(fname, Settings.friendlyname[MAX_FRIENDLYNAMES-1]);
|
||||
strcpy(fname, SettingsText(SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1));
|
||||
uint32_t fname_len = strlen(fname);
|
||||
if (fname_len > 30) { fname_len = 30; }
|
||||
fname[fname_len++] = '-';
|
||||
|
|
|
@ -241,7 +241,7 @@ void HandleUpnpSetupWemo(void)
|
|||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_SETUP));
|
||||
|
||||
String setup_xml = FPSTR(WEMO_SETUP_XML);
|
||||
setup_xml.replace("{x1", Settings.friendlyname[0]);
|
||||
setup_xml.replace("{x1", SettingsText(SET_FRIENDLYNAME1));
|
||||
setup_xml.replace("{x2", WemoUuid());
|
||||
setup_xml.replace("{x3", WemoSerialnumber());
|
||||
WSSend(200, CT_XML, setup_xml);
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
#define ZIGBEE_VERBOSE // output versbose MQTT Zigbee logs. Will remain active for now
|
||||
|
||||
typedef uint64_t Z_IEEEAddress;
|
||||
typedef uint16_t Z_ShortAddress;
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
xdrv_23_zigbee_1_headers.ino - zigbee support for Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends and Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
// contains some definitions for functions used before their declarations
|
||||
|
||||
void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1);
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -22,6 +22,9 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value);
|
||||
|
||||
typedef struct Z_Device {
|
||||
uint16_t shortaddr; // unique key if not null, or unspecified if null
|
||||
uint64_t longaddr; // 0x00 means unspecified
|
||||
|
@ -33,6 +36,12 @@ typedef struct Z_Device {
|
|||
std::vector<uint32_t> endpoints; // encoded as high 16 bits is endpoint, low 16 bits is ProfileId
|
||||
std::vector<uint32_t> clusters_in; // encoded as high 16 bits is endpoint, low 16 bits is cluster number
|
||||
std::vector<uint32_t> clusters_out; // encoded as high 16 bits is endpoint, low 16 bits is cluster number
|
||||
// below are per device timers, used for example to query the new state of the device
|
||||
uint32_t timer; // millis() when to fire the timer, 0 if no timer
|
||||
uint16_t cluster; // cluster to use for the timer
|
||||
uint16_t endpoint; // endpoint to use for timer
|
||||
uint32_t value; // any raw value to use for the timer
|
||||
Z_DeviceTimer func; // function to call when timer occurs
|
||||
} Z_Device;
|
||||
|
||||
// All devices are stored in a Vector
|
||||
|
@ -70,6 +79,11 @@ public:
|
|||
// Dump json
|
||||
String dump(uint32_t dump_mode, int32_t device_num = 0) const;
|
||||
|
||||
// Timers
|
||||
void resetTimer(uint32_t shortaddr);
|
||||
void setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func);
|
||||
void runTimer(void);
|
||||
|
||||
private:
|
||||
std::vector<Z_Device> _devices = {};
|
||||
|
||||
|
@ -157,7 +171,9 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
|||
String(), // FriendlyName
|
||||
std::vector<uint32_t>(),
|
||||
std::vector<uint32_t>(),
|
||||
std::vector<uint32_t>() };
|
||||
std::vector<uint32_t>(),
|
||||
0,0,0,0,
|
||||
nullptr };
|
||||
_devices.push_back(device);
|
||||
return _devices.back();
|
||||
}
|
||||
|
@ -346,6 +362,47 @@ void Z_Devices::updateLastSeen(uint16_t shortaddr) {
|
|||
_updateLastSeen(device);
|
||||
}
|
||||
|
||||
// Per device timers
|
||||
//
|
||||
// Reset the timer for a specific device
|
||||
void Z_Devices::resetTimer(uint32_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
device.timer = 0;
|
||||
device.func = nullptr;
|
||||
}
|
||||
|
||||
// Set timer for a specific device
|
||||
void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
|
||||
device.cluster = cluster;
|
||||
device.endpoint = endpoint;
|
||||
device.value = value;
|
||||
device.func = func;
|
||||
device.timer = wait_ms + millis();
|
||||
}
|
||||
|
||||
// Run timer at each tick
|
||||
void Z_Devices::runTimer(void) {
|
||||
uint32_t now = millis();
|
||||
|
||||
for (std::vector<Z_Device>::iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
||||
Z_Device &device = *it;
|
||||
uint16_t shortaddr = device.shortaddr;
|
||||
|
||||
uint32_t timer = device.timer;
|
||||
if ((timer) && (timer <= now)) {
|
||||
// trigger the timer
|
||||
(*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value);
|
||||
|
||||
device.timer = 0; // cancel the timer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Dump the internal memory of Zigbee devices
|
||||
// Mode = 1: simple dump of devices addresses and names
|
||||
// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters
|
||||
|
|
|
@ -39,45 +39,44 @@ 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 = 0, uint16_t groupid = 0):
|
||||
const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0,
|
||||
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
|
||||
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
|
||||
uint32_t timestamp = 0):
|
||||
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
|
||||
_payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough
|
||||
_cluster_id(clusterid), _group_id(groupid)
|
||||
_cluster_id(clusterid), _group_id(groupid),
|
||||
_srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
|
||||
_linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber),
|
||||
_timestamp(timestamp)
|
||||
{
|
||||
_frame_control.d8 = frame_control;
|
||||
_payload.addBuffer(buf, buf_len);
|
||||
};
|
||||
|
||||
|
||||
void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, Z_ShortAddress srcaddr,
|
||||
uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
|
||||
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
|
||||
uint32_t timestamp) {
|
||||
#ifdef ZIGBEE_VERBOSE
|
||||
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 "\":{"
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, 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,"
|
||||
"\"timestamp\":%d,"
|
||||
"\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d,"
|
||||
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\""),
|
||||
groupid, clusterid, srcaddr,
|
||||
srcendpoint, dstendpoint, wasbroadcast,
|
||||
linkquality, securityuse, seqnumber,
|
||||
timestamp,
|
||||
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"),
|
||||
_group_id, _cluster_id, _srcaddr,
|
||||
_srcendpoint, _dstendpoint, _wasbroadcast,
|
||||
_linkquality, _securityuse, _seqnumber,
|
||||
_timestamp,
|
||||
_frame_control, _manuf_code, _transact_seq, _cmd_id,
|
||||
hex_char);
|
||||
|
||||
ResponseJsonEnd(); // append '}'
|
||||
ResponseJsonEnd(); // append '}'
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
XdrvRulesProcess();
|
||||
#endif
|
||||
}
|
||||
|
||||
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object
|
||||
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid,
|
||||
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
|
||||
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
|
||||
uint32_t timestamp = 0) { // parse a raw frame and build the ZCL frame object
|
||||
uint32_t i = offset;
|
||||
ZCLHeaderFrameControl_t frame_control;
|
||||
uint16_t manuf_code = 0;
|
||||
|
@ -122,10 +121,18 @@ public:
|
|||
return _cluster_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; // optional
|
||||
|
@ -134,6 +141,15 @@ private:
|
|||
uint16_t _cluster_id = 0;
|
||||
uint16_t _group_id = 0;
|
||||
SBuffer _payload;
|
||||
// information from decoded ZCL frame
|
||||
uint16_t _srcaddr;
|
||||
uint8_t _srcendpoint;
|
||||
uint8_t _dstendpoint;
|
||||
uint8_t _wasbroadcast;
|
||||
uint8_t _linkquality;
|
||||
uint8_t _securityuse;
|
||||
uint8_t _seqnumber;
|
||||
uint32_t _timestamp;
|
||||
};
|
||||
|
||||
// Zigbee ZCL converters
|
||||
|
@ -456,7 +472,7 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
|
|||
// return value:
|
||||
// 0 = keep initial value
|
||||
// 1 = remove initial value
|
||||
typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name);
|
||||
typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name, uint16_t cluster, uint16_t attr);
|
||||
typedef struct Z_AttributeConverter {
|
||||
uint16_t cluster;
|
||||
uint16_t attribute;
|
||||
|
@ -464,6 +480,8 @@ typedef struct Z_AttributeConverter {
|
|||
Z_AttrConverter func;
|
||||
} Z_AttributeConverter;
|
||||
|
||||
#define OCCUPANCY "Occupancy" // global define for Aqara
|
||||
|
||||
// list of post-processing directives
|
||||
const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
||||
{ 0x0000, 0x0000, "ZCLVersion", &Z_Copy },
|
||||
|
@ -498,7 +516,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0007, 0x0000, "SwitchType", &Z_Copy },
|
||||
|
||||
// Level Control cluster
|
||||
{ 0x0008, 0x0000, "CurrentLevel", &Z_Copy },
|
||||
{ 0x0008, 0x0000, "Dimmer", &Z_Copy },
|
||||
// { 0x0008, 0x0001, "RemainingTime", &Z_Copy },
|
||||
// { 0x0008, 0x0010, "OnOffTransitionTime", &Z_Copy },
|
||||
// { 0x0008, 0x0011, "OnLevel", &Z_Copy },
|
||||
|
@ -625,6 +643,11 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy },
|
||||
{ 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy },
|
||||
{ 0x0101, 0x0006, "OpenPeriod", &Z_Copy },
|
||||
// Aqara Lumi Vibration Sensor
|
||||
{ 0x0101, 0x0055, "AqaraVibrationMode", &Z_AqaraVibration },
|
||||
{ 0x0101, 0x0503, "AqaraVibrationsOrAngle", &Z_Copy },
|
||||
{ 0x0101, 0x0505, "AqaraVibration505", &Z_Copy },
|
||||
{ 0x0101, 0x0508, "AqaraAccelerometer", &Z_AqaraVibration },
|
||||
// Window Covering cluster
|
||||
{ 0x0102, 0x0000, "WindowCoveringType", &Z_Copy },
|
||||
{ 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy },
|
||||
|
@ -648,14 +671,14 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy },
|
||||
|
||||
// Color Control cluster
|
||||
{ 0x0300, 0x0000, "CurrentHue", &Z_Copy },
|
||||
{ 0x0300, 0x0001, "CurrentSaturation", &Z_Copy },
|
||||
{ 0x0300, 0x0000, "Hue", &Z_Copy },
|
||||
{ 0x0300, 0x0001, "Sat", &Z_Copy },
|
||||
{ 0x0300, 0x0002, "RemainingTime", &Z_Copy },
|
||||
{ 0x0300, 0x0003, "CurrentX", &Z_Copy },
|
||||
{ 0x0300, 0x0004, "CurrentY", &Z_Copy },
|
||||
{ 0x0300, 0x0003, "X", &Z_Copy },
|
||||
{ 0x0300, 0x0004, "Y", &Z_Copy },
|
||||
{ 0x0300, 0x0005, "DriftCompensation", &Z_Copy },
|
||||
{ 0x0300, 0x0006, "CompensationText", &Z_Copy },
|
||||
{ 0x0300, 0x0007, "ColorTemperatureMireds",&Z_Copy },
|
||||
{ 0x0300, 0x0007, "CT", &Z_Copy },
|
||||
{ 0x0300, 0x0008, "ColorMode", &Z_Copy },
|
||||
{ 0x0300, 0x0010, "NumberOfPrimaries", &Z_Copy },
|
||||
{ 0x0300, 0x0011, "Primary1X", &Z_Copy },
|
||||
|
@ -727,7 +750,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Occupancy Sensing cluster
|
||||
{ 0x0406, 0x0000, "Occupancy", &Z_Copy }, // Occupancy (map8)
|
||||
{ 0x0406, 0x0000, OCCUPANCY, &Z_AqaraOccupancy }, // Occupancy (map8)
|
||||
{ 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType
|
||||
{ 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
|
@ -753,13 +776,13 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
|
||||
// ======================================================================
|
||||
// Record Manuf
|
||||
int32_t Z_ManufKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = value;
|
||||
zigbee_devices.setManufId(shortaddr, value.as<const char*>());
|
||||
return 1;
|
||||
}
|
||||
//
|
||||
int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = value;
|
||||
zigbee_devices.setModelId(shortaddr, value.as<const char*>());
|
||||
return 1;
|
||||
|
@ -767,34 +790,113 @@ int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, Json
|
|||
|
||||
// ======================================================================
|
||||
// Remove attribute
|
||||
int32_t Z_Remove(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Copy value as-is
|
||||
int32_t Z_Copy(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = value;
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Add pressure unit
|
||||
int32_t Z_AddPressureUnit(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = F(D_UNIT_PRESSURE);
|
||||
return 0; // keep original key
|
||||
}
|
||||
|
||||
// Convert int to float and divide by 100
|
||||
int32_t Z_FloatDiv100(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = ((float)value) / 100.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
// Convert int to float and divide by 10
|
||||
int32_t Z_FloatDiv10(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = ((float)value) / 10.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
|
||||
// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds.
|
||||
// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false
|
||||
const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
|
||||
|
||||
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
|
||||
// send Occupancy:false message
|
||||
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":{\"" OCCUPANCY "\":0}}}"), shortaddr);
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
|
||||
int32_t Z_AqaraOccupancy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
json[new_name] = value;
|
||||
uint32_t occupancy = value;
|
||||
|
||||
if (occupancy) {
|
||||
zigbee_devices.setTimer(shortaddr, OCCUPANCY_TIMEOUT, cluster, zcl->getSrcEndpoint(), 0, &Z_OccupancyCallback);
|
||||
} else {
|
||||
zigbee_devices.resetTimer(shortaddr);
|
||||
}
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Aqara Vibration Sensor - special proprietary attributes
|
||||
int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
//json[new_name] = value;
|
||||
switch (attr) {
|
||||
case 0x0055:
|
||||
{
|
||||
int32_t ivalue = value;
|
||||
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;
|
||||
}
|
||||
json[new_name] = svalue;
|
||||
}
|
||||
break;
|
||||
// case 0x0503:
|
||||
// break;
|
||||
// case 0x0505:
|
||||
// break;
|
||||
case 0x0508:
|
||||
{
|
||||
// see https://github.com/Koenkk/zigbee2mqtt/issues/295 and http://faire-ca-soi-meme.fr/domotique/2018/09/03/test-xiaomi-aqara-vibration-sensor/
|
||||
// report accelerometer measures
|
||||
String hex = value;
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
int16_t x, y, z;
|
||||
z = buf2.get16(0);
|
||||
y = buf2.get16(2);
|
||||
x = buf2.get16(4);
|
||||
JsonArray& xyz = json.createNestedArray(new_name);
|
||||
xyz.add(x);
|
||||
xyz.add(y);
|
||||
xyz.add(z);
|
||||
// calculate angles
|
||||
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;
|
||||
// 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;
|
||||
JsonArray& angles = json.createNestedArray(F("AqaraAngles"));
|
||||
angles.add(Angle_X);
|
||||
angles.add(Angle_Y);
|
||||
angles.add(Angle_Z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
|
||||
String hex = value;
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint32_t i = 0;
|
||||
|
@ -809,36 +911,31 @@ int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, Js
|
|||
i += parseSingleAttribute(json, tmp, buf2, i, len);
|
||||
float val = json[tmp];
|
||||
json.remove(tmp);
|
||||
if (0x64 == attrid) {
|
||||
json[F(D_JSON_TEMPERATURE)] = val / 100.0f;
|
||||
} else if (0x65 == attrid) {
|
||||
json[F(D_JSON_HUMIDITY)] = val / 100.0f;
|
||||
} else if (0x66 == attrid) {
|
||||
json[F(D_JSON_PRESSURE)] = val / 100.0f;
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
} else if (0x01 == attrid) {
|
||||
if (0x01 == attrid) {
|
||||
json[F(D_JSON_VOLTAGE)] = val / 1000.0f;
|
||||
json[F("Battery")] = toPercentageCR2032(val);
|
||||
} else if (0 == zcl->getManufCode()) {
|
||||
// onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
|
||||
if (0x64 == attrid) {
|
||||
json[F(D_JSON_TEMPERATURE)] = val / 100.0f;
|
||||
} else if (0x65 == attrid) {
|
||||
json[F(D_JSON_HUMIDITY)] = val / 100.0f;
|
||||
} else if (0x66 == attrid) {
|
||||
json[F(D_JSON_PRESSURE)] = val / 100.0f;
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
} else if (0x01 == attrid) {
|
||||
json[F(D_JSON_VOLTAGE)] = val / 1000.0f;
|
||||
json[F("Battery")] = toPercentageCR2032(val);
|
||||
}
|
||||
} else if (0x115F == zcl->getManufCode()) {
|
||||
// Aqara Motion Sensor, still unknown field
|
||||
json[F("AqaraUnknown")] = val;
|
||||
}
|
||||
}
|
||||
return 1; // remove original key
|
||||
}
|
||||
// ======================================================================
|
||||
|
||||
// Cluster Specific commands
|
||||
// #define ZCL_OO_OFF "s_0006_00" // Cluster 0x0006, cmd 0x00 - On/Off - Off
|
||||
// #define ZCL_OO_ON "s_0006_01" // Cluster 0x0006, cmd 0x01 - On/Off - On
|
||||
// #define ZCL_COLORTEMP_MOVE "s_0300_0A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp
|
||||
// #define ZCL_LC_MOVE "s_0008_00" // Cluster 0x0008, cmd 0x00, Level Control Move to Level
|
||||
// #define ZCL_LC_MOVE_1 "s_0008_01" // Cluster 0x0008, cmd 0x01, Level Control Move
|
||||
// #define ZCL_LC_STEP "s_0008_02" // Cluster 0x0008, cmd 0x02, Level Control Step
|
||||
// #define ZCL_LC_STOP "s_0008_03" // Cluster 0x0008, cmd 0x03, Level Control Stop
|
||||
// #define ZCL_LC_MOVE_WOO "s_0008_04" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off
|
||||
// #define ZCL_LC_MOVE_1_WOO "s_0008_05" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off
|
||||
// #define ZCL_LC_STEP_WOO "s_0008_06" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off
|
||||
// #define ZCL_LC_STOP_WOO "s_0008_07" // Cluster 0x0008, cmd 0x07, Level Control Stop
|
||||
|
||||
|
||||
void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
|
||||
// iterate on json elements
|
||||
for (auto kv : json) {
|
||||
|
@ -859,7 +956,7 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
|
|||
|
||||
if ((conv_cluster == cluster) &&
|
||||
((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) {
|
||||
int32_t drop = (*converter->func)(shortaddr, json, key, value, (const __FlashStringHelper*) converter->name);
|
||||
int32_t drop = (*converter->func)(this, shortaddr, json, key, value, (const __FlashStringHelper*) converter->name, conv_cluster, conv_attribute);
|
||||
if (drop) {
|
||||
json.remove(key);
|
||||
}
|
||||
|
@ -870,157 +967,4 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
|
|||
}
|
||||
}
|
||||
|
||||
//void ZCLFrame::postProcessAttributes2(JsonObject& json) {
|
||||
// void postProcessAttributes2(JsonObject& json) {
|
||||
// const __FlashStringHelper *key;
|
||||
//
|
||||
// // Osram Mini Switch
|
||||
// key = F(ZCL_OO_OFF);
|
||||
// if (json.containsKey(key)) {
|
||||
// json.remove(key);
|
||||
// json[F(D_CMND_POWER)] = F("Off");
|
||||
// }
|
||||
// key = F(ZCL_OO_ON);
|
||||
// if (json.containsKey(key)) {
|
||||
// json.remove(key);
|
||||
// json[F(D_CMND_POWER)] = F("On");
|
||||
// }
|
||||
// key = F(ZCL_COLORTEMP_MOVE);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint16_t color_temp = buf2.get16(0);
|
||||
// uint16_t transition_time = buf2.get16(2);
|
||||
// json.remove(key);
|
||||
// json[F("ColorTemp")] = color_temp;
|
||||
// json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
// }
|
||||
// key = F(ZCL_LC_MOVE_WOO);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t level = buf2.get8(0);
|
||||
// uint16_t transition_time = buf2.get16(1);
|
||||
// json.remove(key);
|
||||
// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage
|
||||
// json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
// if (0 == level) {
|
||||
// json[F(D_CMND_POWER)] = F("Off");
|
||||
// } else {
|
||||
// json[F(D_CMND_POWER)] = F("On");
|
||||
// }
|
||||
// }
|
||||
// key = F(ZCL_LC_MOVE);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t level = buf2.get8(0);
|
||||
// uint16_t transition_time = buf2.get16(1);
|
||||
// json.remove(key);
|
||||
// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage
|
||||
// json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
// }
|
||||
// key = F(ZCL_LC_MOVE_1);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t move_mode = buf2.get8(0);
|
||||
// uint8_t move_rate = buf2.get8(1);
|
||||
// json.remove(key);
|
||||
// json[F("Move")] = move_mode ? F("Down") : F("Up");
|
||||
// json[F("Rate")] = move_rate;
|
||||
// }
|
||||
// key = F(ZCL_LC_MOVE_1_WOO);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t move_mode = buf2.get8(0);
|
||||
// uint8_t move_rate = buf2.get8(1);
|
||||
// json.remove(key);
|
||||
// json[F("Move")] = move_mode ? F("Down") : F("Up");
|
||||
// json[F("Rate")] = move_rate;
|
||||
// if (0 == move_mode) {
|
||||
// json[F(D_CMND_POWER)] = F("On");
|
||||
// }
|
||||
// }
|
||||
// key = F(ZCL_LC_STEP);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t step_mode = buf2.get8(0);
|
||||
// uint8_t step_size = buf2.get8(1);
|
||||
// uint16_t transition_time = buf2.get16(2);
|
||||
// json.remove(key);
|
||||
// json[F("Step")] = step_mode ? F("Down") : F("Up");
|
||||
// json[F("StepSize")] = step_size;
|
||||
// json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
// }
|
||||
// key = F(ZCL_LC_STEP_WOO);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// uint8_t step_mode = buf2.get8(0);
|
||||
// uint8_t step_size = buf2.get8(1);
|
||||
// uint16_t transition_time = buf2.get16(2);
|
||||
// json.remove(key);
|
||||
// json[F("Step")] = step_mode ? F("Down") : F("Up");
|
||||
// json[F("StepSize")] = step_size;
|
||||
// json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
// if (0 == step_mode) {
|
||||
// json[F(D_CMND_POWER)] = F("On");
|
||||
// }
|
||||
// }
|
||||
// key = F(ZCL_LC_STOP);
|
||||
// if (json.containsKey(key)) {
|
||||
// json.remove(key);
|
||||
// json[F("Stop")] = 1;
|
||||
// }
|
||||
// key = F(ZCL_LC_STOP_WOO);
|
||||
// if (json.containsKey(key)) {
|
||||
// json.remove(key);
|
||||
// json[F("Stop")] = 1;
|
||||
// }
|
||||
//
|
||||
// // Lumi.weather proprietary field
|
||||
// key = F(ZCL_LUMI_WEATHER);
|
||||
// if (json.containsKey(key)) {
|
||||
// String hex = json[key];
|
||||
// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
// DynamicJsonBuffer jsonBuffer;
|
||||
// JsonObject& json_lumi = jsonBuffer.createObject();
|
||||
// uint32_t i = 0;
|
||||
// uint32_t len = buf2.len();
|
||||
// char shortaddr[8];
|
||||
//
|
||||
// while (len - i >= 2) {
|
||||
// uint8_t attrid = buf2.get8(i++);
|
||||
//
|
||||
// snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%02X"), attrid);
|
||||
//
|
||||
// //json[shortaddr] = parseSingleAttribute(json_lumi, buf2, i, len, nullptr, 0);
|
||||
// }
|
||||
// // parse output
|
||||
// if (json_lumi.containsKey("0x64")) { // Temperature
|
||||
// int32_t temperature = json_lumi["0x64"];
|
||||
// json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f;
|
||||
// }
|
||||
// if (json_lumi.containsKey("0x65")) { // Humidity
|
||||
// uint32_t humidity = json_lumi["0x65"];
|
||||
// json[F(D_JSON_HUMIDITY)] = humidity / 100.0f;
|
||||
// }
|
||||
// if (json_lumi.containsKey("0x66")) { // Pressure
|
||||
// int32_t pressure = json_lumi["0x66"];
|
||||
// json[F(D_JSON_PRESSURE)] = pressure / 100.0f;
|
||||
// json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
// }
|
||||
// if (json_lumi.containsKey("0x01")) { // Battery Voltage
|
||||
// uint32_t voltage = json_lumi["0x01"];
|
||||
// json[F(D_JSON_VOLTAGE)] = voltage / 1000.0f;
|
||||
// json[F("Battery")] = toPercentageCR2032(voltage);
|
||||
// }
|
||||
// json.remove(key);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
#endif // USE_ZIGBEE
|
||||
|
|
|
@ -47,6 +47,64 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "ShutterTilt", "0102!08xx"}, // Tilt percentage
|
||||
};
|
||||
|
||||
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
|
||||
|
||||
// Below are the attributes we wand to read from each cluster
|
||||
const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; // Power
|
||||
const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel
|
||||
const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount
|
||||
const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007) }; // Hue, Sat, X, Y, CT
|
||||
|
||||
int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
|
||||
size_t attrs_len = 0;
|
||||
const uint8_t* attrs = nullptr;
|
||||
|
||||
switch (cluster) {
|
||||
case 0x0006: // for On/Off
|
||||
attrs = CLUSTER_0006;
|
||||
attrs_len = sizeof(CLUSTER_0006);
|
||||
break;
|
||||
case 0x0008: // for Dimmer
|
||||
attrs = CLUSTER_0008;
|
||||
attrs_len = sizeof(CLUSTER_0008);
|
||||
break;
|
||||
case 0x0009: // for Alarms
|
||||
attrs = CLUSTER_0009;
|
||||
attrs_len = sizeof(CLUSTER_0009);
|
||||
break;
|
||||
case 0x0300: // for Lights
|
||||
attrs = CLUSTER_0300;
|
||||
attrs_len = sizeof(CLUSTER_0300);
|
||||
break;
|
||||
}
|
||||
if (attrs) {
|
||||
ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set a timer to read back the value in the future
|
||||
void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint) {
|
||||
uint32_t wait_ms = 0;
|
||||
|
||||
switch (cluster) {
|
||||
case 0x0006: // for On/Off
|
||||
case 0x0009: // for Alamrs
|
||||
wait_ms = 200; // wait 0.2 s
|
||||
break;
|
||||
case 0x0008: // for Dimmer
|
||||
case 0x0300: // for Color
|
||||
wait_ms = 1050; // wait 1.0 s
|
||||
break;
|
||||
case 0x0102: // for Shutters
|
||||
wait_ms = 10000; // wait 10.0 s
|
||||
break;
|
||||
}
|
||||
if (wait_ms) {
|
||||
zigbee_devices.setTimer(shortaddr, wait_ms, cluster, endpoint, 0 /* value */, &Z_ReadAttrCallback);
|
||||
}
|
||||
}
|
||||
|
||||
const __FlashStringHelper* zigbeeFindCommand(const char *command) {
|
||||
char parm_uc[16]; // used to convert JSON keys to uppercase
|
||||
for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) {
|
||||
|
@ -114,6 +172,7 @@ const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0,
|
|||
2,2,
|
||||
255 };
|
||||
|
||||
// Convert an alias like "On" to the corresponding number
|
||||
uint32_t ZigbeeAliasOrNumber(const char *state_text) {
|
||||
char command[16];
|
||||
int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias);
|
||||
|
|
|
@ -370,15 +370,12 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
uint8_t seqnumber = buf.get8(17);
|
||||
|
||||
zigbee_devices.updateLastSeen(srcaddr);
|
||||
ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid);
|
||||
|
||||
#ifdef ZIGBEE_VERBOSE
|
||||
zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr,
|
||||
srcendpoint, dstendpoint, wasbroadcast,
|
||||
linkquality, securityuse, seqnumber,
|
||||
timestamp);
|
||||
#endif
|
||||
|
||||
ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid,
|
||||
srcaddr,
|
||||
srcendpoint, dstendpoint, wasbroadcast,
|
||||
linkquality, securityuse, seqnumber,
|
||||
timestamp);
|
||||
zcl_received.log();
|
||||
char shortaddr[8];
|
||||
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
|
||||
|
||||
|
@ -398,7 +395,7 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
String msg("");
|
||||
msg.reserve(100);
|
||||
json_root.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add linkquality
|
||||
|
|
|
@ -191,14 +191,9 @@ void ZigbeeInput(void)
|
|||
|
||||
SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS
|
||||
|
||||
#ifdef ZIGBEE_VERBOSE
|
||||
ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char));
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPRECEIVED " %s"),
|
||||
hex_char);
|
||||
// Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char);
|
||||
// MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPRECEIVED));
|
||||
// XdrvRulesProcess();
|
||||
#endif
|
||||
|
||||
// now process the message
|
||||
ZigbeeProcessInput(znp_buffer);
|
||||
|
@ -235,13 +230,14 @@ void ZigbeeInit(void)
|
|||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
uint32_t strToUInt(const JsonVariant val) {
|
||||
uint32_t strToUInt(const JsonVariant &val) {
|
||||
// if the string starts with 0x, it is considered Hex, otherwise it is an int
|
||||
if (val.is<unsigned int>()) {
|
||||
return val.as<unsigned int>();
|
||||
} else {
|
||||
if (val.is<char*>()) {
|
||||
return strtoull(val.as<char*>(), nullptr, 0);
|
||||
if (val.is<const char*>()) {
|
||||
String sval = val.as<String>();
|
||||
return strtoull(sval.c_str(), nullptr, 0);
|
||||
}
|
||||
}
|
||||
return 0; // couldn't parse anything
|
||||
|
@ -268,7 +264,7 @@ void CmndZigbeeReset(void) {
|
|||
void CmndZigbeeStatus(void) {
|
||||
if (ZigbeeSerial) {
|
||||
String dump = zigbee_devices.dump(XdrvMailbox.index, XdrvMailbox.payload);
|
||||
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.payload, dump.c_str());
|
||||
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,15 +315,13 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|||
ZigbeeSerial->write(fcs); // finally send fcs checksum byte
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
|
||||
}
|
||||
#ifdef ZIGBEE_VERBOSE
|
||||
// Now send a MQTT message to report the sent message
|
||||
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)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1) {
|
||||
void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId) {
|
||||
SBuffer buf(25+len);
|
||||
buf.add8(Z_SREQ | Z_AF); // 24
|
||||
buf.add8(AF_DATA_REQUEST); // 01
|
||||
|
@ -422,6 +416,10 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
|
|||
|
||||
// everything is good, we can send the command
|
||||
ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len());
|
||||
// now set the timer, if any, to read back the state later
|
||||
if (clusterSpecific) {
|
||||
zigbeeSetCommandTimer(dstAddr, cluster, endpoint);
|
||||
}
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
|
@ -539,7 +537,7 @@ void CmndZigbeeSend(void) {
|
|||
// we have an unsupported command type, just ignore it and fallback to missing command
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
|
||||
device, endpoint, cmd_str.c_str());
|
||||
zigbeeZCLSendStr(device, endpoint, cmd_str.c_str());
|
||||
} else {
|
||||
|
@ -593,6 +591,7 @@ void CmndZigbeeRead(void) {
|
|||
|
||||
const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read"));
|
||||
if (nullptr != &val_attr) {
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
if (val_attr.is<JsonArray>()) {
|
||||
JsonArray& attr_arr = val_attr;
|
||||
attrs_len = attr_arr.size() * 2;
|
||||
|
@ -604,19 +603,18 @@ void CmndZigbeeRead(void) {
|
|||
attrs[i++] = val & 0xFF;
|
||||
attrs[i++] = val >> 8;
|
||||
}
|
||||
|
||||
} else {
|
||||
attrs_len = 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
attrs[0] = val & 0xFF; // little endian
|
||||
attrs[1] = val >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||
|
||||
if (attrs) { delete[] attrs; }
|
||||
if (attrs) { delete[] attrs; }
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// Allow or Deny pairing of new Zigbee devices
|
||||
|
@ -647,6 +645,11 @@ bool Xdrv23(uint8_t function)
|
|||
|
||||
if (zigbee.active) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_50_MSECOND:
|
||||
if (!zigbee.init_phase) {
|
||||
zigbee_devices.runTimer();
|
||||
}
|
||||
break;
|
||||
case FUNC_LOOP:
|
||||
if (ZigbeeSerial) { ZigbeeInput(); }
|
||||
if (zigbee.state_machine) {
|
||||
|
|
|
@ -24,23 +24,9 @@
|
|||
|
||||
#define XDRV_27 27
|
||||
|
||||
#define D_PRFX_SHUTTER "Shutter"
|
||||
#define D_CMND_SHUTTER_OPEN "Open"
|
||||
#define D_CMND_SHUTTER_CLOSE "Close"
|
||||
#define D_CMND_SHUTTER_STOP "Stop"
|
||||
#define D_CMND_SHUTTER_POSITION "Position"
|
||||
#define D_CMND_SHUTTER_OPENTIME "OpenDuration"
|
||||
#define D_CMND_SHUTTER_CLOSETIME "CloseDuration"
|
||||
#define D_CMND_SHUTTER_RELAY "Relay"
|
||||
#define D_CMND_SHUTTER_SETHALFWAY "SetHalfway"
|
||||
#define D_CMND_SHUTTER_SETCLOSE "SetClose"
|
||||
#define D_CMND_SHUTTER_INVERT "Invert"
|
||||
#define D_CMND_SHUTTER_CLIBRATION "Calibration"
|
||||
#define D_CMND_SHUTTER_MOTORDELAY "MotorDelay"
|
||||
|
||||
#define D_SHUTTER "SHUTTER"
|
||||
|
||||
const uint16_t MOTOR_STOP_TIME = 500; // in mS
|
||||
const uint16_t MOTOR_STOP_TIME = 500; // in mS
|
||||
|
||||
uint8_t calibrate_pos[6] = {0,30,50,70,90,100};
|
||||
uint16_t messwerte[5] = {30,50,70,90,100};
|
||||
|
@ -51,14 +37,16 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
|
|||
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
|
||||
D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|"
|
||||
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
|
||||
D_CMND_SHUTTER_MOTORDELAY;
|
||||
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY;
|
||||
|
||||
void (* const ShutterCommand[])(void) PROGMEM = {
|
||||
&CmndShutterOpen, &CmndShutterClose, &CmndShutterStop, &CmndShutterPosition,
|
||||
&CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay,
|
||||
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay};
|
||||
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
|
||||
&CmndShutterFrequency};
|
||||
|
||||
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"direction\":%d}";
|
||||
const char MSG_SHUTTER_POS[] PROGMEM = "SHT: " D_PRFX_SHUTTER " %d: Real. %d, Start: %d, Stop: %d, dir %d, motordelay %d, rtc: %s [s], freq %d";
|
||||
|
||||
#include <Ticker.h>
|
||||
|
||||
|
@ -68,23 +56,25 @@ struct SHUTTER {
|
|||
power_t mask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
|
||||
power_t old_power = 0; // preserve old bitmask for power to extract the relay that changes.
|
||||
power_t switched_relay = 0; // bitmatrix that contain the relays that was lastly changed.
|
||||
uint32_t time[MAX_SHUTTERS];
|
||||
uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec
|
||||
int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated
|
||||
int32_t target_position[MAX_SHUTTERS]; // position to go to
|
||||
int32_t start_position[MAX_SHUTTERS];
|
||||
int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start
|
||||
int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max
|
||||
uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter
|
||||
uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter
|
||||
uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec
|
||||
uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec
|
||||
uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster
|
||||
uint16_t operations[MAX_SHUTTERS];
|
||||
int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down
|
||||
uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE
|
||||
uint8_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec.
|
||||
int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec.
|
||||
int16_t pwm_frequency; // frequency of PWN for stepper motors
|
||||
uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency that can be used. depend on the motor and drivers
|
||||
uint8_t skip_relay_change; // avoid overrun at endstops
|
||||
} Shutter;
|
||||
|
||||
void ShutterRtc50mS(void)
|
||||
{
|
||||
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
Shutter.time[i]++;
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +84,7 @@ int32_t ShutterPercentToRealPosition(uint8_t percent,uint8_t index)
|
|||
if (Settings.shutter_set50percent[index] != 50) {
|
||||
return percent <= 5 ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index];
|
||||
} else {
|
||||
uint32_t realpos;
|
||||
int32_t realpos;
|
||||
// check against DIV 0
|
||||
for (uint8_t j=0 ; j < 5 ; j++) {
|
||||
if (Settings.shuttercoeff[j][index] == 0) {
|
||||
|
@ -128,7 +118,7 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint8_t index)
|
|||
if (Settings.shutter_set50percent[index] != 50) {
|
||||
return Settings.shuttercoeff[2][index] * 5 > realpos ? realpos / Settings.shuttercoeff[2][index] : (realpos-Settings.shuttercoeff[0][index]) / Settings.shuttercoeff[1][index];
|
||||
} else {
|
||||
uint16_t realpercent;
|
||||
int16_t realpercent;
|
||||
|
||||
for (uint8_t i=0 ; i < 5 ; i++) {
|
||||
if (realpos > Shutter.open_max[index] * calibrate_pos[i+1] / 100) {
|
||||
|
@ -146,7 +136,7 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint8_t index)
|
|||
break;
|
||||
}
|
||||
}
|
||||
return realpercent;
|
||||
return (realpercent < 0 ? 0 : (realpercent > 100 ? 0 : realpercent));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,8 +148,6 @@ void ShutterInit(void)
|
|||
Shutter.old_power = power;
|
||||
bool relay_in_interlock = false;
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Accuracy digits: %d"), Settings.shutter_accuracy);
|
||||
|
||||
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
|
||||
// upgrade to 0.1sec calculation base.
|
||||
if ( Settings.shutter_accuracy == 0) {
|
||||
|
@ -189,6 +177,11 @@ void ShutterInit(void)
|
|||
}
|
||||
} else {
|
||||
Shutter.mode = SHT_OFF_ON__OPEN_CLOSE;
|
||||
if (pin[GPIO_PWM1+i] < 99) {
|
||||
Shutter.pwm_frequency = 0;
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
analogWrite(pin[GPIO_PWM1+i], 50);
|
||||
}
|
||||
}
|
||||
|
||||
TickerShutter.attach_ms(50, ShutterRtc50mS );
|
||||
|
@ -220,8 +213,8 @@ void ShutterInit(void)
|
|||
dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr);
|
||||
char shutter_close_chr[10];
|
||||
dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoedffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"),
|
||||
i, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr,
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"),
|
||||
i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr,
|
||||
Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i],
|
||||
Shutter.mask, Settings.shutter_invert[i], Shutter.mode, Shutter.motordelay[i]);
|
||||
|
||||
|
@ -229,6 +222,9 @@ void ShutterInit(void)
|
|||
// terminate loop at first INVALID shutter.
|
||||
break;
|
||||
}
|
||||
if (shutters_present < 4) {
|
||||
Shutter.max_pwm_frequency = Settings.shuttercoeff[4][4] > 0 ? Settings.shuttercoeff[4][4] : Shutter.max_pwm_frequency;
|
||||
}
|
||||
Settings.shutter_accuracy = 1;
|
||||
}
|
||||
}
|
||||
|
@ -237,45 +233,32 @@ void ShutterUpdatePosition(void)
|
|||
{
|
||||
char scommand[CMDSZ];
|
||||
char stopic[TOPSZ];
|
||||
char stemp2[10];
|
||||
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
if (Shutter.direction[i] != 0) {
|
||||
//char stemp1[20];
|
||||
Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
|
||||
// avoid real position leaving the boundaries.
|
||||
Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ;
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) {
|
||||
// Calculate position with counter. Much more accurate and no need for motordelay workaround
|
||||
// adding some steps to stop early
|
||||
Shutter.real_position[i] = Shutter.direction[i] * 20 + ShutterCounterBasedPosition(i);;
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_frequency , Shutter.direction[i] ,Shutter.max_pwm_frequency );
|
||||
} else {
|
||||
Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
|
||||
}
|
||||
|
||||
// Add additional runtime, if shutter did not reach the endstop for some time.
|
||||
if (Shutter.target_position[i] == Shutter.real_position[i] && Shutter.target_position[i] == 0) {
|
||||
// for every operation add 5x50ms = 250ms to stop position
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Adding additional runtime"));
|
||||
Shutter.real_position[i] += 500 * Shutter.operations[i] ;
|
||||
Shutter.operations[i] = 0;
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99) {
|
||||
uint16_t freq_change = Shutter.max_pwm_frequency/(Shutter.motordelay[i]+1);
|
||||
// ramp up phase. calculate frequency
|
||||
Shutter.pwm_frequency = tmin(freq_change * Shutter.time[i],Shutter.max_pwm_frequency);
|
||||
// ramp down at the end of the movement time will not be exactly motordelay
|
||||
Shutter.pwm_frequency = tmax(tmin(freq_change * (Shutter.target_position[i]-Shutter.real_position[i])*Shutter.direction[i]/30, Shutter.pwm_frequency),10);
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
analogWrite(pin[GPIO_PWM1+i], 50);
|
||||
}
|
||||
if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) {
|
||||
// calculate relay number responsible for current movement.
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]);
|
||||
uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ;
|
||||
char stemp2[10];
|
||||
|
||||
Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i);
|
||||
//Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? (Shutter.real_position[i] * 10 / Settings.shuttercoeff[2][i] + 4)/10 : ((Shutter.real_position[i]-Settings.shuttercoeff[0,i]) *10 / Settings.shuttercoeff[1][i] +4) / 10;
|
||||
|
||||
if (0 < Settings.shutter_position[i] && Settings.shutter_position[i] < 100) {
|
||||
Shutter.operations[i]++;
|
||||
} else {
|
||||
Shutter.operations[i] = 0;
|
||||
}
|
||||
|
||||
dtostrfd((float)Shutter.time[i] / 20, 1, stemp2);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, rtcshutter: %s [s], operationtime %d"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], stemp2, Shutter.operations[i]);
|
||||
Shutter.start_position[i] = Shutter.real_position[i];
|
||||
|
||||
// sending MQTT result to broker
|
||||
snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1);
|
||||
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
|
||||
Response_P("%d", Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]);
|
||||
MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN
|
||||
|
||||
switch (Shutter.mode) {
|
||||
case SHT_PULSE_OPEN__PULSE_CLOSE:
|
||||
|
@ -288,8 +271,25 @@ void ShutterUpdatePosition(void)
|
|||
break;
|
||||
case SHT_OFF_ON__OPEN_CLOSE:
|
||||
// This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction
|
||||
// Only allow PWM microstepping if PWM and COUNTER are defined.
|
||||
// see wiki to connect PWM and COUNTER
|
||||
if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) {
|
||||
int16_t missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i];
|
||||
//prepare for stop PWM
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency);
|
||||
Shutter.pwm_frequency = 0;
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) {
|
||||
delay(1);
|
||||
}
|
||||
analogWrite(pin[GPIO_PWM1+i], 0);
|
||||
Shutter.real_position[i] = ShutterCounterBasedPosition(i);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]);
|
||||
|
||||
}
|
||||
if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER);
|
||||
}
|
||||
break;
|
||||
case SHT_OFF_OPEN__OFF_CLOSE:
|
||||
|
@ -300,6 +300,18 @@ void ShutterUpdatePosition(void)
|
|||
}
|
||||
break;
|
||||
}
|
||||
Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i);
|
||||
|
||||
dtostrfd((float)Shutter.time[i] / 20, 1, stemp2);
|
||||
AddLog_P2(LOG_LEVEL_INFO, MSG_SHUTTER_POS, i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency);
|
||||
Shutter.start_position[i] = Shutter.real_position[i];
|
||||
|
||||
// sending MQTT result to broker
|
||||
snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1);
|
||||
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
|
||||
Response_P("%d", Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]);
|
||||
MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN
|
||||
|
||||
Shutter.direction[i] = 0;
|
||||
uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i];
|
||||
Response_P(PSTR("{"));
|
||||
|
@ -320,32 +332,73 @@ bool ShutterState(uint8_t device)
|
|||
(Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) );
|
||||
}
|
||||
|
||||
void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos)
|
||||
void ShutterStartInit(uint8_t index, int8_t direction, int32_t target_pos)
|
||||
{
|
||||
Shutter.direction[index] = direction;
|
||||
Shutter.target_position[index] = target_pos;
|
||||
Shutter.start_position[index] = Shutter.real_position[index];
|
||||
Shutter.time[index] = 0;
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: dir %d, delta1 %d, delta2 %d, grant %d"),direction, (Shutter.open_max[index] - Shutter.real_position[index]) / Shutter.close_velocity[index], Shutter.real_position[index] / Shutter.close_velocity[index], 2+Shutter.motordelay[index]);
|
||||
if ( ( direction == 1 && (Shutter.open_max[index] - Shutter.real_position[index]) / 100 <= 2 )
|
||||
|| ( direction == -1 && Shutter.real_position[index] / Shutter.close_velocity[index] <= 2)) {
|
||||
Shutter.skip_relay_change = 1 ;
|
||||
} else {
|
||||
if (pin[GPIO_PWM1+index] < 99) {
|
||||
Shutter.pwm_frequency = 0;
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
analogWrite(pin[GPIO_PWM1+index], 0);
|
||||
// can be operated without counter, but then not that acurate.
|
||||
if (pin[GPIO_CNTR1+index] < 99) {
|
||||
RtcSettings.pulse_counter[index] = 0;
|
||||
}
|
||||
}
|
||||
Shutter.target_position[index] = target_pos;
|
||||
Shutter.start_position[index] = Shutter.real_position[index];
|
||||
Shutter.time[index] = 0;
|
||||
Shutter.skip_relay_change = 0;
|
||||
Shutter.direction[index] = direction;
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[index], Shutter.start_position[index] ,RtcSettings.pulse_counter[index],Shutter.max_pwm_frequency , Shutter.direction[index] ,Shutter.max_pwm_frequency );
|
||||
}
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]);
|
||||
}
|
||||
|
||||
void ShutterDelayForMotorStop(void)
|
||||
void ShutterWaitForMotorStop(uint8_t index)
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop %d"), MOTOR_STOP_TIME);
|
||||
delay(MOTOR_STOP_TIME);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop.."));
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) {
|
||||
if (pin[GPIO_PWM1+index] < 99 && pin[GPIO_CNTR1+index] < 99 ) {
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency);
|
||||
while (Shutter.pwm_frequency > 100) {
|
||||
Shutter.pwm_frequency = tmax(Shutter.pwm_frequency-(Shutter.max_pwm_frequency/(Shutter.motordelay[index]+1)) , 0);
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
analogWrite(pin[GPIO_PWM1+index], 50);
|
||||
delay(50);
|
||||
}
|
||||
Shutter.pwm_frequency = 0;
|
||||
analogWriteFreq(Shutter.pwm_frequency);
|
||||
analogWrite(pin[GPIO_PWM1+index], 0);
|
||||
Shutter.real_position[index] = ShutterCounterBasedPosition(index);
|
||||
} else {
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
|
||||
delay(MOTOR_STOP_TIME);
|
||||
}
|
||||
} else {
|
||||
delay(MOTOR_STOP_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void ShutterReportPosition(void)
|
||||
{
|
||||
uint16_t shutter_moving = 0;
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
for (uint8_t i = 0; i < shutters_present; i++) {
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]);
|
||||
if (Shutter.direction[i] != 0) {
|
||||
char stemp1[20];
|
||||
char stemp2[10];
|
||||
dtostrfd((float)Shutter.time[i] / 20, 1, stemp2);
|
||||
uint8_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i);
|
||||
dtostrfd((float)Shutter.time[i] / 20, 2, stemp2);
|
||||
shutter_moving = 1;
|
||||
//Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i];
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, rtcshutter: %s [s]"), i,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], stemp2 );
|
||||
AddLog_P2(LOG_LEVEL_INFO, MSG_SHUTTER_POS, i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency);
|
||||
Response_P(PSTR("{"));
|
||||
ResponseAppend_P(JSON_SHUTTER_POS, i+1, Settings.shutter_invert[i] ? 100-position : position, Shutter.direction[i]);
|
||||
ResponseJsonEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data);
|
||||
}
|
||||
}
|
||||
if (rules_flag.shutter_moving > shutter_moving) {
|
||||
|
@ -357,6 +410,11 @@ void ShutterReportPosition(void)
|
|||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved);
|
||||
}
|
||||
|
||||
int32_t ShutterCounterBasedPosition(uint8 i)
|
||||
{
|
||||
return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i];
|
||||
}
|
||||
|
||||
void ShutterRelayChanged(void)
|
||||
{
|
||||
|
||||
|
@ -369,49 +427,44 @@ void ShutterRelayChanged(void)
|
|||
power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3;
|
||||
//uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
|
||||
uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
|
||||
if (manual_relays_changed) {
|
||||
//Shutter.skip_relay_change = true;
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) {
|
||||
ShutterWaitForMotorStop(i);
|
||||
switch (powerstate_local) {
|
||||
case 1:
|
||||
ShutterDelayForMotorStop();
|
||||
ShutterStartInit(i, 1, Shutter.open_max[i]);
|
||||
ShutterStartInit(i, 1, Shutter.open_max[i]);
|
||||
break;
|
||||
case 3:
|
||||
ShutterDelayForMotorStop();
|
||||
ShutterStartInit(i, -1, 0);
|
||||
break;
|
||||
default:
|
||||
Shutter.direction[i] = 0;
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
|
||||
Shutter.target_position[i] = Shutter.real_position[i];
|
||||
}
|
||||
} else {
|
||||
if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) {
|
||||
Shutter.target_position[i] = Shutter.real_position[i];
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
|
||||
} else {
|
||||
last_source = SRC_SHUTTER; // avoid switch off in the next loop
|
||||
if (powerstate_local == 2) { // testing on CLOSE relay, if ON
|
||||
// close with relay two
|
||||
ShutterDelayForMotorStop();
|
||||
ShutterWaitForMotorStop(i);
|
||||
ShutterStartInit(i, -1, 0);
|
||||
} else {
|
||||
// opens with relay one
|
||||
ShutterDelayForMotorStop();
|
||||
ShutterWaitForMotorStop(i);
|
||||
ShutterStartInit(i, 1, Shutter.open_max[i]);
|
||||
}
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i, Shutter.target_position[i], powerstate_local);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Shutter specific functions
|
||||
// TODO: move to shutter driver and make them accessible in a generic way
|
||||
|
||||
// device: 1..<numberOfShutters>
|
||||
// position: 0-100
|
||||
void ShutterSetPosition(uint8_t device, uint8_t position)
|
||||
{
|
||||
char svalue[32]; // Command and number parameter
|
||||
|
@ -425,16 +478,23 @@ void ShutterSetPosition(uint8_t device, uint8_t position)
|
|||
|
||||
void CmndShutterOpen(void)
|
||||
{
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload close: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index);
|
||||
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
|
||||
XdrvMailbox.index = XdrvMailbox.payload;
|
||||
}
|
||||
XdrvMailbox.payload = 100;
|
||||
XdrvMailbox.data_len = 3;
|
||||
last_source = SRC_WEBGUI;
|
||||
CmndShutterPosition();
|
||||
}
|
||||
|
||||
void CmndShutterClose(void)
|
||||
{
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload open: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index);
|
||||
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
|
||||
XdrvMailbox.index = XdrvMailbox.payload;
|
||||
}
|
||||
XdrvMailbox.payload = 0;
|
||||
XdrvMailbox.data_len = 1;
|
||||
XdrvMailbox.data_len = 0;
|
||||
last_source = SRC_WEBGUI;
|
||||
CmndShutterPosition();
|
||||
}
|
||||
|
@ -442,11 +502,14 @@ void CmndShutterClose(void)
|
|||
void CmndShutterStop(void)
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
|
||||
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
|
||||
XdrvMailbox.index = XdrvMailbox.payload;
|
||||
}
|
||||
uint32_t index = XdrvMailbox.index -1;
|
||||
if (Shutter.direction[index] != 0) {
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]);
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[index]);
|
||||
// set stop position 10 steps ahead (0.5sec to allow normal stop)
|
||||
int32_t temp_realpos = Shutter.start_position[index] + ( (Shutter.time[index]+10) * (Shutter.direction[index] > 0 ? 100 : -Shutter.close_velocity[index]));
|
||||
XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, index);
|
||||
//XdrvMailbox.payload = Settings.shuttercoeff[2][index] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][index] : (temp_realpos-Settings.shuttercoeff[0,index]) / Settings.shuttercoeff[1][index];
|
||||
|
@ -463,7 +526,7 @@ void CmndShutterPosition(void)
|
|||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
|
||||
uint32_t index = XdrvMailbox.index -1;
|
||||
//limit the payload
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Position in: payload %s (%d), payload %d, index %d, source %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source );
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, 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) {
|
||||
UpperCase(XdrvMailbox.data, XdrvMailbox.data);
|
||||
|
@ -472,7 +535,7 @@ void CmndShutterPosition(void)
|
|||
if (!strcmp(XdrvMailbox.data,"STOP")) { CmndShutterStop(); }
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int8_t target_pos_percent = XdrvMailbox.payload < 0 ? 0 : (XdrvMailbox.payload > 100 ? 100 : XdrvMailbox.payload);
|
||||
// webgui still send also on inverted shutter the native position.
|
||||
target_pos_percent = Settings.shutter_invert[index] && SRC_WEBGUI != last_source ? 100 - target_pos_percent : target_pos_percent;
|
||||
|
@ -480,7 +543,7 @@ void CmndShutterPosition(void)
|
|||
//target_pos_percent = Settings.shutter_invert[index] ? 100 - target_pos_percent : target_pos_percent;
|
||||
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
|
||||
//Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, realpos %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
|
||||
}
|
||||
if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) {
|
||||
int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
|
||||
|
@ -491,24 +554,26 @@ void CmndShutterPosition(void)
|
|||
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
|
||||
delay(100);
|
||||
} else {
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), 0, SRC_SHUTTER);
|
||||
ShutterDelayForMotorStop();
|
||||
if (Shutter.mode == SHT_OFF_OPEN__OFF_CLOSE) {
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), 0, SRC_SHUTTER);
|
||||
ShutterWaitForMotorStop(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Shutter.direction[index] != new_shutterdirection ) {
|
||||
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
|
||||
Shutter.operations[index]++;
|
||||
if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) {
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload);
|
||||
ShutterDelayForMotorStop();
|
||||
ShutterWaitForMotorStop(index);
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
|
||||
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
|
||||
// Code for shutters with circuit safe configuration, switch the direction Relay
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
|
||||
// power on
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
|
||||
} else {
|
||||
// now start the motor for the right direction, work for momentary and normal shutters.
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter in direction %d"), Shutter.direction[index]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]);
|
||||
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
|
||||
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload);
|
||||
}
|
||||
|
@ -556,7 +621,7 @@ void CmndShutterMotorDelay(void)
|
|||
ShutterInit();
|
||||
}
|
||||
char time_chr[10];
|
||||
dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / 20, 1, time_chr);
|
||||
dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / 20, 2, time_chr);
|
||||
ResponseCmndIdxChar(time_chr);
|
||||
}
|
||||
}
|
||||
|
@ -571,7 +636,6 @@ void CmndShutterRelay(void)
|
|||
} else {
|
||||
Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1);
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Relay %d is %d"), XdrvMailbox.index, XdrvMailbox.payload);
|
||||
Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
|
||||
ShutterInit();
|
||||
// if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work
|
||||
|
@ -586,10 +650,21 @@ void CmndShutterSetHalfway(void)
|
|||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
||||
Settings.shutter_set50percent[XdrvMailbox.index -1] = Settings.shutter_invert[XdrvMailbox.index -1] ? 100 - XdrvMailbox.payload : XdrvMailbox.payload;
|
||||
ShutterInit();
|
||||
ResponseCmndIdxNumber(XdrvMailbox.payload); // ????
|
||||
} else {
|
||||
ResponseCmndIdxNumber(Settings.shutter_set50percent[XdrvMailbox.index -1]);
|
||||
}
|
||||
ResponseCmndIdxNumber(Settings.shutter_invert[XdrvMailbox.index -1] ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]);
|
||||
}
|
||||
}
|
||||
|
||||
void CmndShutterFrequency(void)
|
||||
{
|
||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) {
|
||||
Shutter.max_pwm_frequency = XdrvMailbox.payload;
|
||||
if (shutters_present < 4) {
|
||||
Settings.shuttercoeff[4][4] = Shutter.max_pwm_frequency;
|
||||
}
|
||||
ResponseCmndNumber(XdrvMailbox.payload); // ????
|
||||
} else {
|
||||
ResponseCmndNumber(Shutter.max_pwm_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,7 +674,7 @@ void CmndShutterSetClose(void)
|
|||
Shutter.real_position[XdrvMailbox.index -1] = 0;
|
||||
ShutterStartInit(XdrvMailbox.index -1, 0, 0);
|
||||
Settings.shutter_position[XdrvMailbox.index -1] = 0;
|
||||
ResponseCmndChar(D_CONFIGURATION_RESET);
|
||||
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,7 +690,7 @@ void CmndShutterInvert(void)
|
|||
|
||||
void CmndShutterCalibration(void) // ????
|
||||
{
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) {
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
uint32_t i = 0;
|
||||
char *str_ptr;
|
||||
|
@ -658,13 +733,15 @@ bool Xdrv27(uint8_t function)
|
|||
ShutterUpdatePosition();
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
//case FUNC_EVERY_250_MSECOND:
|
||||
ShutterReportPosition();
|
||||
break;
|
||||
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kShutterCommands, ShutterCommand);
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
for (uint32_t i = 0; i < shutters_present; i++) {
|
||||
for (uint8_t i = 0; i < shutters_present; i++) {
|
||||
uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i];
|
||||
ResponseAppend_P(",");
|
||||
ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]);
|
||||
|
@ -678,10 +755,26 @@ bool Xdrv27(uint8_t function)
|
|||
case FUNC_SET_POWER:
|
||||
char stemp1[10];
|
||||
// extract the number of the relay that was switched and save for later in Update Position.
|
||||
Shutter.switched_relay = power ^ Shutter.old_power;
|
||||
Shutter.old_power = power;
|
||||
Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource));
|
||||
ShutterRelayChanged();
|
||||
Shutter.old_power = XdrvMailbox.index;
|
||||
break;
|
||||
case FUNC_SET_DEVICE_POWER:
|
||||
if (Shutter.skip_relay_change ) {
|
||||
uint8_t i;
|
||||
for (i = 0; i < devices_present; i++) {
|
||||
if (Shutter.switched_relay &1) {
|
||||
break;
|
||||
}
|
||||
Shutter.switched_relay >>= 1;
|
||||
}
|
||||
//AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1);
|
||||
result = true;
|
||||
Shutter.skip_relay_change = 0;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i);
|
||||
ExecuteCommandPower(i+1, 0, SRC_SHUTTER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#endif // USE_DEBUG_DRIVER
|
||||
#endif // DEBUG_THEO
|
||||
|
||||
//#define USE_DEBUG_SETTING_NAMES
|
||||
|
||||
#ifdef USE_DEBUG_DRIVER
|
||||
/*********************************************************************************************\
|
||||
* Virtual debugging support - Part1
|
||||
|
@ -64,9 +62,6 @@
|
|||
|
||||
const char kDebugCommands[] PROGMEM = "|" // No prefix
|
||||
D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|"
|
||||
#ifdef USE_DEBUG_SETTING_NAMES
|
||||
D_CMND_CFGSHOW "|"
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER
|
||||
D_CMND_CFGXOR "|"
|
||||
#endif
|
||||
|
@ -82,9 +77,6 @@ const char kDebugCommands[] PROGMEM = "|" // No prefix
|
|||
|
||||
void (* const DebugCommand[])(void) PROGMEM = {
|
||||
&CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke,
|
||||
#ifdef USE_DEBUG_SETTING_NAMES
|
||||
&CmndCfgShow,
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER
|
||||
&CmndCfgXor,
|
||||
#endif
|
||||
|
@ -391,36 +383,6 @@ void DebugCfgPoke(char* parms)
|
|||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32);
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUG_SETTING_NAMES
|
||||
void DebugCfgShow(uint8_t more)
|
||||
{
|
||||
uint8_t *SetAddr;
|
||||
SetAddr = (uint8_t *)&Settings;
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]);
|
||||
if (17 == more) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password);
|
||||
}
|
||||
}
|
||||
#endif // USE_DEBUG_SETTING_NAMES
|
||||
|
||||
void SetFlashMode(uint8_t mode)
|
||||
{
|
||||
uint8_t *_buffer;
|
||||
|
@ -474,14 +436,6 @@ void CmndCfgPoke(void)
|
|||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUG_SETTING_NAMES
|
||||
void CmndCfgShow(void)
|
||||
{
|
||||
DebugCfgShow(XdrvMailbox.payload);
|
||||
ResponseCmndDone();
|
||||
}
|
||||
#endif // USE_DEBUG_SETTING_NAMES
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
void CmndCfgXor(void)
|
||||
{
|
||||
|
|
|
@ -211,7 +211,7 @@ void CseDrvInit(void)
|
|||
{
|
||||
if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported
|
||||
baudrate = 4800;
|
||||
serial_config = SERIAL_8E1;
|
||||
Settings.serial_config = TS_SERIAL_8E1;
|
||||
if (0 == Settings.param[P_CSE7766_INVALID_POWER]) {
|
||||
Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#define XSNS_06 6
|
||||
|
||||
#define DHT_MAX_SENSORS 3
|
||||
#define DHT_MAX_SENSORS 4
|
||||
#define DHT_MAX_RETRY 8
|
||||
|
||||
uint32_t dht_max_cycles;
|
||||
|
|
|
@ -57,6 +57,7 @@ int16_t MPU_6050_temperature = 0;
|
|||
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
|
||||
VectorFloat gravity; // [x, y, z] gravity vector
|
||||
float euler[3]; // [psi, theta, phi] Euler angle container
|
||||
float yawPitchRoll[3]; // [yaw, pitch roll] Yaw-pitch-roll container
|
||||
} MPU6050_DMP;
|
||||
|
||||
MPU6050_DMP MPU6050_dmp;
|
||||
|
@ -68,7 +69,7 @@ MPU6050 mpu6050;
|
|||
void MPU_6050PerformReading(void)
|
||||
{
|
||||
#ifdef USE_MPU6050_DMP
|
||||
mpu6050.resetFIFO(); // with a default dampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle
|
||||
mpu6050.resetFIFO(); // with a default sampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle
|
||||
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);
|
||||
|
@ -79,6 +80,7 @@ void MPU_6050PerformReading(void)
|
|||
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;
|
||||
|
@ -145,6 +147,10 @@ void MPU_6050Detect(void)
|
|||
}
|
||||
}
|
||||
|
||||
#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} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
|
@ -153,6 +159,12 @@ const char HTTP_SNS_AXIS[] PROGMEM =
|
|||
"{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
#ifdef USE_MPU6050_DMP
|
||||
const char HTTP_SNS_YPR[] PROGMEM =
|
||||
"{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
#endif // USE_MPU6050_DMP
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
#define D_JSON_AXIS_AX "AccelXAxis"
|
||||
|
@ -161,6 +173,9 @@ const char HTTP_SNS_AXIS[] PROGMEM =
|
|||
#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)
|
||||
{
|
||||
|
@ -181,6 +196,14 @@ void MPU_6050Show(bool json)
|
|||
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 // USE_MPU6050_DMP
|
||||
|
||||
if (json) {
|
||||
char json_axis_ax[25];
|
||||
|
@ -195,8 +218,20 @@ void MPU_6050Show(bool json)
|
|||
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 // USE_MPU6050_DMP
|
||||
#ifdef USE_DOMOTICZ
|
||||
DomoticzSensor(DZ_TEMP, temperature);
|
||||
#endif // USE_DOMOTICZ
|
||||
|
@ -204,6 +239,9 @@ void MPU_6050Show(bool json)
|
|||
} 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 // USE_MPU6050_DMP
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
#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 };
|
||||
|
||||
|
@ -64,8 +66,10 @@ const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|"
|
|||
|
||||
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;
|
||||
|
@ -78,6 +82,7 @@ struct HX {
|
|||
uint8_t pin_dout;
|
||||
bool tare_flg = false;
|
||||
bool weight_changed = false;
|
||||
uint16_t weight_delta = 4;
|
||||
} Hx;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
@ -146,6 +151,24 @@ void HxCalibrationStateTextJson(uint8_t msg_id)
|
|||
if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); }
|
||||
}
|
||||
|
||||
void SetWeightDelta()
|
||||
{
|
||||
// backwards compatible: restore old default value of 4 grams
|
||||
if (Settings.weight_change == 0) {
|
||||
Hx.weight_delta = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// map upper values 101-255 to
|
||||
if (Settings.weight_change > 100) {
|
||||
Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100;
|
||||
return;
|
||||
}
|
||||
|
||||
// map 1..100 to 0..99 grams
|
||||
Hx.weight_delta = Settings.weight_change - 1;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Supported commands for Sensor34:
|
||||
*
|
||||
|
@ -163,6 +186,7 @@ void HxCalibrationStateTextJson(uint8_t msg_id)
|
|||
* Sensor34 7 - Save current weight to be used as start weight on restart
|
||||
* Sensor34 8 0 - Disable JSON weight change message
|
||||
* Sensor34 8 1 - Enable JSON weight change message
|
||||
* Sensor34 9 <weight code> - Set minimum delta to trigger JSON message
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool HxCommand(void)
|
||||
|
@ -225,6 +249,13 @@ bool HxCommand(void)
|
|||
}
|
||||
show_parms = true;
|
||||
break;
|
||||
case 9: // WeightDelta
|
||||
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;
|
||||
}
|
||||
|
@ -232,8 +263,10 @@ bool HxCommand(void)
|
|||
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\"}}"),
|
||||
Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item, GetStateText(Settings.SensorBits1.hx711_json_weight_change));
|
||||
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;
|
||||
|
@ -258,6 +291,8 @@ void HxInit(void)
|
|||
|
||||
digitalWrite(Hx.pin_sck, LOW);
|
||||
|
||||
SetWeightDelta();
|
||||
|
||||
if (HxIsReady(8 * HX_TIMEOUT)) { // Can take 600 milliseconds after power on
|
||||
if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; }
|
||||
if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; }
|
||||
|
@ -272,13 +307,17 @@ void HxInit(void)
|
|||
|
||||
void HxEvery100mSecond(void)
|
||||
{
|
||||
Hx.sum_weight += HxRead();
|
||||
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; // grams
|
||||
long raw_average = Hx.sum_raw / Hx.sample_count; // grams
|
||||
long value = average - Hx.offset; // grams
|
||||
Hx.weight = value / Hx.scale; // grams
|
||||
Hx.raw = raw_average / Hx.scale;
|
||||
if (Hx.weight < 0) {
|
||||
if (Settings.energy_frequency_calibration) {
|
||||
long difference = Settings.energy_frequency_calibration + Hx.weight;
|
||||
|
@ -351,7 +390,7 @@ void HxEvery100mSecond(void)
|
|||
Hx.weight += Hx.last_weight; // grams
|
||||
|
||||
if (Settings.SensorBits1.hx711_json_weight_change) {
|
||||
if (abs(Hx.weight - Hx.weight_diff) > 4) { // Use 4 gram threshold to decrease "ghost" weights
|
||||
if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { // Use weight_delta threshold to decrease "ghost" weights
|
||||
Hx.weight_diff = Hx.weight;
|
||||
Hx.weight_changed = true;
|
||||
}
|
||||
|
@ -367,6 +406,7 @@ void HxEvery100mSecond(void)
|
|||
}
|
||||
|
||||
Hx.sum_weight = 0;
|
||||
Hx.sum_raw = 0;
|
||||
Hx.sample_count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +445,7 @@ void HxShow(bool json)
|
|||
dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr);
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s}"), weight_chr, scount);
|
||||
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);
|
||||
|
@ -565,4 +605,4 @@ bool Xsns34(uint8_t function)
|
|||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_HX711
|
||||
#endif // USE_HX711
|
||||
|
|
|
@ -499,7 +499,10 @@ const uint8_t meter[]=
|
|||
#define USE_SML_MEDIAN_FILTER
|
||||
|
||||
// max number of vars , may be adjusted
|
||||
#ifndef MAX_VARS
|
||||
#define MAX_VARS 20
|
||||
#endif
|
||||
|
||||
// max number of meters , may be adjusted
|
||||
#define MAX_METERS 5
|
||||
double meter_vars[MAX_VARS];
|
||||
|
@ -1427,14 +1430,25 @@ void SML_Decode(uint8_t index) {
|
|||
//ignore
|
||||
mp+=2;
|
||||
cp++;
|
||||
} else if (!strncmp(mp,"uuuuuuuu",8)) {
|
||||
} 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'){
|
||||
} 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;
|
||||
|
@ -1442,17 +1456,25 @@ void SML_Decode(uint8_t index) {
|
|||
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') {
|
||||
} 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;
|
||||
}
|
||||
|
|
|
@ -1,407 +1,3 @@
|
|||
# decode-config.py
|
||||
_decode-config.py_ is able to backup and restore Tasmota configuration.
|
||||
A tool to backup and restore the configuration of [Tasmota](http://tasmota.com/)-devices.
|
||||
|
||||
In comparison with the Tasmota build-in "Backup/Restore Configuration" function _decode-config.py_
|
||||
* uses human readable and editable [JSON](http://www.json.org/)-format for backup/restore,
|
||||
* can restore previously backup and changed [JSON](http://www.json.org/)-format files,
|
||||
* is able to create Tasmota compatible command list with related config parameter
|
||||
|
||||
Comparing backup files created by *decode-config.py* and *.dmp files created by Tasmota "Backup/Restore Configuration":
|
||||
|
||||
| | decode-config.py<br />*.json file | Tasmota<br />*.dmp file |
|
||||
|-------------------------|:-------------------------------:|:-----------------------------------:|
|
||||
| Encrypted | No | Yes |
|
||||
| Readable | Yes | No |
|
||||
| Simply editable | Yes | No |
|
||||
| Simply batch processing | Yes | No |
|
||||
|
||||
_decode-config.py_ is compatible with Tasmota version from v5.10.0 up to now.
|
||||
|
||||
# Content
|
||||
* [Prerequisite](decode-config.md#prerequisite)
|
||||
* [File Types](decode-config.md#file-types)
|
||||
* [.dmp File Format](decode-config.md#-dmp-format)
|
||||
* [.json File Format](decode-config.md#-json-format)
|
||||
* [.bin File Format](decode-config.md#-bin-format)
|
||||
* [File extensions](decode-config.md#file-extensions)
|
||||
* [Usage](decode-config.md#usage)
|
||||
* [Basics](decode-config.md#basics)
|
||||
* [Save backup file](decode-config.md#save-backup-file)
|
||||
* [Restore backup file](decode-config.md#restore-backup-file)
|
||||
* [Output to screen](decode-config.md#output-to-screen)
|
||||
* [JSON output](decode-config.md#json-output)
|
||||
* [Tasmota command output](decode-config.md#tasmota-command-output)
|
||||
* [Filter data](decode-config.md#filter-data)
|
||||
* [Configuration file](decode-config.md#configuration-file)
|
||||
* [More program arguments](decode-config.md#more-program-arguments)
|
||||
* [Examples](decode-config.md#examples)
|
||||
* [Config file](decode-config.md#config-file)
|
||||
* [Using Tasmota binary configuration files](decode-config.md#using-tasmota-binary-configuration-files)
|
||||
* [Use batch processing](decode-config.md#use-batch-processing)
|
||||
* [Notes](decode-config.md#notes)
|
||||
|
||||
## Prerequisite
|
||||
* This program is written in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) so you need to install a working python environment for your operating system.
|
||||
|
||||
### Linux
|
||||
```
|
||||
sudo apt-get install python python-pip libcurl4-openssl-dev libssl-dev
|
||||
```
|
||||
```
|
||||
pip install pycurl configargparse
|
||||
```
|
||||
|
||||
### Windows 10
|
||||
|
||||
Install [Python 2.7](https://www.python.org/download/releases/2.7/) then install dependencies. For PyCurl you need to [download pycurl‑7.43.0.3‑cp27‑cp27m‑win_amd64.whl](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl) for Windows 10 64bit.
|
||||
```
|
||||
pip install pycurl-7.43.0.3-cp27-cp27m-win_amd64.whl
|
||||
// run the command from the folder where you downloaded the file
|
||||
|
||||
pip install configargparse
|
||||
```
|
||||
|
||||
* [Tasmota](https://github.com/arendst/Tasmota) [Firmware](https://github.com/arendst/Tasmota/releases) with Web-Server enabled:
|
||||
* To backup or restore configurations from or to a Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://tasmota.github.io/docs/#/Commands#wifi)). This is the Tasmota default.
|
||||
* If using your own compiled firmware be aware to enable the web-server (`#define USE_WEBSERVER` and `#define WEB_SERVER 2`).
|
||||
|
||||
## File Types
|
||||
_decode-config.py_ can handle the following backup file types:
|
||||
### .dmp Format
|
||||
Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.
|
||||
This format is binary and encrypted.
|
||||
### .json Format
|
||||
Configuration data in [JSON](http://www.json.org/)-format.
|
||||
This format is decrypted, human readable and editable and can also be used for the `--restore-file` parameter.
|
||||
This file will be created by _decode-config.py_ using the `--backup-file` with `--backup-type json` parameter, this is the default.
|
||||
### .bin Format
|
||||
Configuration data in binary format.
|
||||
This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for `--restore-file` command.
|
||||
It will be created by _decode-config.py_ using `--backup-file` with `--backup-type bin`.
|
||||
Note:
|
||||
The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and 4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the **struct SYSCFG** from Tasmota [settings.h](https://github.com/arendst/Tasmota/blob/master/tasmota/settings.h) in decrypted format.
|
||||
|
||||
#### File extensions
|
||||
You don't need to append exensions for your file name as _decode-config.py_ uses auto extension as default. The extension will be choose based on file contents and `--backup-type` parameter.
|
||||
If you do not want using auto extensions use the `--no-extension` parameter.
|
||||
|
||||
## Usage
|
||||
After download don't forget to set the executable flag under linux with `chmod +x decode-config.py` or call the program using `python decode-config.py...`.
|
||||
|
||||
### Basics
|
||||
At least pass a source where you want to read the configuration data from using `-f <filename>` or `-d <host>`:
|
||||
|
||||
The source can be either
|
||||
* a Tasmota device hostname or IP using the `-d <host>` parameter
|
||||
* a Tasmota `*.dmp` configuration file using `-f <filename>` parameter
|
||||
|
||||
Example:
|
||||
|
||||
decode-config.py -d tasmota-4281
|
||||
|
||||
will output a human readable configuration in [JSON](http://www.json.org/)-format:
|
||||
|
||||
{
|
||||
"altitude": 112,
|
||||
"baudrate": 115200,
|
||||
"blinkcount": 10,
|
||||
"blinktime": 10,
|
||||
...
|
||||
"ws_width": [
|
||||
1,
|
||||
3,
|
||||
5
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
### Save backup file
|
||||
To save the output as backup file use `--backup-file <filename>`, you can use placeholder for Version, Friendlyname and Hostname:
|
||||
|
||||
decode-config.py -d tasmota-4281 --backup-file Config_@f_@v
|
||||
|
||||
If you have setup a WebPassword within Tasmota, use
|
||||
|
||||
decode-config.py -d tasmota-4281 -p <yourpassword> --backup-file Config_@f_@v
|
||||
|
||||
will create a file like `Config_Tasmota_6.4.0.json` (the part `Tasmota` and `6.4.0` will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor.
|
||||
|
||||
### Restore backup file
|
||||
Reading back a saved (and possible changed) backup file use the `--restore-file <filename>` parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.
|
||||
|
||||
To restore the previously save backup file `Config_Tasmota_6.2.1.json` to device `tasmota-4281` use:
|
||||
|
||||
decode-config.py -d tasmota-4281 --restore-file Config_Tasmota_6.2.1.json
|
||||
|
||||
with password set by WebPassword:
|
||||
|
||||
decode-config.py -d tasmota-4281 -p <yourpassword> --restore-file Config_Tasmota_6.2.1.json
|
||||
|
||||
### Output to screen
|
||||
To force screen output use the `--output` parameter.
|
||||
|
||||
Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter.
|
||||
|
||||
#### JSON output
|
||||
The default output format is [JSON](decode-config.md#-json-format). You can force JSON output using the `--output-format json` parameter.
|
||||
|
||||
Example:
|
||||
|
||||
decode-config.py -d tasmota-4281 -c my.conf -x Wifi --output-format json
|
||||
|
||||
{
|
||||
...
|
||||
"hostname": "%s-%04d",
|
||||
"ip_address": [
|
||||
"0.0.0.0",
|
||||
"192.168.12.1",
|
||||
"255.255.255.0",
|
||||
"192.168.12.1"
|
||||
],
|
||||
"ntp_server": [
|
||||
"ntp.localnet.home",
|
||||
"ntp2.localnet.home",
|
||||
"192.168.12.1"
|
||||
],
|
||||
"sta_active": 0,
|
||||
"sta_config": 5,
|
||||
"sta_pwd": [
|
||||
"myWlAnPaszxwo!z",
|
||||
"myWlAnPaszxwo!z2"
|
||||
],
|
||||
"sta_ssid": [
|
||||
"wlan.1",
|
||||
"my-wlan"
|
||||
],
|
||||
"web_password": "myPaszxwo!z",
|
||||
"webserver": 2
|
||||
...
|
||||
}
|
||||
|
||||
Note: JSON output always contains all configuration data like the backup file except you are using `--group` arg.
|
||||
|
||||
|
||||
#### Tasmota command output
|
||||
_decode-config.py_ is able to translate the configuration data to (most all) Tasmota commands. To output your configuration as Tasmota commands use `--output-format cmnd` or `--output-format command`.
|
||||
|
||||
Example:
|
||||
|
||||
decode-config.py -d tasmota-4281 -c my.conf -g Wifi --output-format cmnd
|
||||
|
||||
# Wifi:
|
||||
AP 0
|
||||
Hostname %s-%04d
|
||||
IPAddress1 0.0.0.0
|
||||
IPAddress2 192.168.12.1
|
||||
IPAddress3 255.255.255.0
|
||||
IPAddress4 192.168.12.1
|
||||
NtpServer1 ntp.localnet.home
|
||||
NtpServer2 ntp2.localnet.home
|
||||
NtpServer3 192.168.12.1
|
||||
Password1 myWlAnPaszxwo!z
|
||||
Password2 myWlAnPaszxwo!z2
|
||||
SSId1 wlan.1
|
||||
SSId2 wlan.1
|
||||
WebPassword myPaszxwo!z
|
||||
WebServer 2
|
||||
WifiConfig 5
|
||||
|
||||
Note: A few very specific module commands like MPC230xx, KNX and some Display commands are not supported. These are still available by JSON output.
|
||||
|
||||
### Filter data
|
||||
The huge number of Tasmota configuration data can be overstrained and confusing, so the most of the configuration data are grouped into categories.
|
||||
|
||||
With _decode-config.py_ the following categories are available: `Display`, `Domoticz`, `Internal`, `KNX`, `Led`, `Logging`, `MCP230xx`, `MQTT`, `Main`, `Management`, `Pow`, `Sensor`, `Serial`, `SetOption`, `RF`, `System`, `Timers`, `Wifi`
|
||||
|
||||
These are similary to the categories on [https://tasmota.github.io/docs/#/Commands](Tasmota Command Wiki).
|
||||
|
||||
To filter outputs to a subset of groups use the `-g` or `--group` arg concatenating the grooup you want, e. g.
|
||||
|
||||
decode-config.py -d tasmota-4281 -c my.conf --output-format cmnd --group Main MQTT Management Wifi
|
||||
|
||||
|
||||
### Configuration file
|
||||
Each argument that start with `--` (eg. `--file`) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/)).
|
||||
|
||||
If an argument is specified in more than one place, then commandline values override config file values which override defaults. This is usefull if you always use the same argument or a basic set of arguments.
|
||||
|
||||
The http authentication credentials `--username` and `--password` is predestinated to store it in a file instead using it on your command line as argument:
|
||||
|
||||
e.g. my.conf:
|
||||
|
||||
[source]
|
||||
username = admin
|
||||
password = myPaszxwo!z
|
||||
|
||||
To make a backup file from example above you can now pass the config file instead using the password on command line:
|
||||
|
||||
decode-config.py -d tasmota-4281 -c my.conf --backup-file Config_@f_@v
|
||||
|
||||
|
||||
|
||||
### More program arguments
|
||||
For better reading each short written arg (minus sign `-`) has a corresponding long version (two minus signs `--`), eg. `--device` for `-d` or `--file` for `-f` (note: not even all `--` arg has a corresponding `-` one).
|
||||
|
||||
A short list of possible program args is displayed using `-h` or `--help`.
|
||||
|
||||
For advanced help use `-H` or `--full-help`:
|
||||
|
||||
usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
|
||||
[-u <username>] [-p <password>] [-i <filename>]
|
||||
[-o <filename>] [-t json|bin|dmp] [-E] [-e] [-F]
|
||||
[--json-indent <indent>] [--json-compact]
|
||||
[--json-hide-pw] [--json-show-pw]
|
||||
[--cmnd-indent <indent>] [--cmnd-groups]
|
||||
[--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
|
||||
[-c <filename>] [-S] [-T json|cmnd|command]
|
||||
[-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} ...]]
|
||||
[--ignore-warnings] [-h] [-H] [-v] [-V]
|
||||
|
||||
Backup/Restore Tasmota configuration data. Args that start with '--'
|
||||
(eg. -f) can also be set in a config file (specified via -c). Config file
|
||||
syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at
|
||||
https://goo.gl/R74nmi). If an arg is specified in more than one place, then
|
||||
commandline values override config file values which override defaults.
|
||||
|
||||
Source:
|
||||
Read/Write Tasmota configuration from/to
|
||||
|
||||
-f, --file, --tasmota-file <filename>
|
||||
file to retrieve/write Tasmota configuration from/to
|
||||
(default: None)'
|
||||
-d, --device, --host <host>
|
||||
hostname or IP address to retrieve/send Tasmota
|
||||
configuration from/to (default: None)
|
||||
-P, --port <port> TCP/IP port number to use for the host connection
|
||||
(default: 80)
|
||||
-u, --username <username>
|
||||
host HTTP access username (default: admin)
|
||||
-p, --password <password>
|
||||
host HTTP access password (default: None)
|
||||
|
||||
Backup/Restore:
|
||||
Backup & restore specification
|
||||
|
||||
-i, --restore-file <filename>
|
||||
file to restore configuration from (default: None).
|
||||
Replacements: @v=firmware version from config,
|
||||
@f=device friendly name from config, @h=device
|
||||
hostname from config, @H=device hostname from device
|
||||
(-d arg only)
|
||||
-o, --backup-file <filename>
|
||||
file to backup configuration to (default: None).
|
||||
Replacements: @v=firmware version from config,
|
||||
@f=device friendly name from config, @h=device
|
||||
hostname from config, @H=device hostname from device
|
||||
(-d arg only)
|
||||
-t, --backup-type json|bin|dmp
|
||||
backup filetype (default: 'json')
|
||||
-E, --extension append filetype extension for -i and -o filename
|
||||
(default)
|
||||
-e, --no-extension do not append filetype extension, use -i and -o
|
||||
filename as passed
|
||||
-F, --force-restore force restore even configuration is identical
|
||||
|
||||
JSON output:
|
||||
JSON format specification
|
||||
|
||||
--json-indent <indent>
|
||||
pretty-printed JSON output using indent level
|
||||
(default: 'None'). -1 disables indent.
|
||||
--json-compact compact JSON output by eliminate whitespace
|
||||
--json-hide-pw hide passwords
|
||||
--json-show-pw, --json-unhide-pw
|
||||
unhide passwords (default)
|
||||
|
||||
Tasmota command output:
|
||||
Tasmota command output format specification
|
||||
|
||||
--cmnd-indent <indent>
|
||||
Tasmota command grouping indent level (default: '2').
|
||||
0 disables indent
|
||||
--cmnd-groups group Tasmota commands (default)
|
||||
--cmnd-nogroups leave Tasmota commands ungrouped
|
||||
--cmnd-sort sort Tasmota commands (default)
|
||||
--cmnd-unsort leave Tasmota commands unsorted
|
||||
|
||||
Common:
|
||||
Optional arguments
|
||||
|
||||
-c, --config <filename>
|
||||
program config file - can be used to set default
|
||||
command args (default: None)
|
||||
-S, --output display output regardsless of backup/restore usage
|
||||
(default do not output on backup or restore usage)
|
||||
-T, --output-format json|cmnd|command
|
||||
display output format (default: 'json')
|
||||
-g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi}
|
||||
limit data processing to command groups (default no
|
||||
filter)
|
||||
--ignore-warnings do not exit on warnings. Not recommended, used by your
|
||||
own responsibility!
|
||||
|
||||
Info:
|
||||
Extra information
|
||||
|
||||
-h, --help show usage help message and exit
|
||||
-H, --full-help show full help message and exit
|
||||
-v, --verbose produce more output about what the program does
|
||||
-V, --version show program's version number and exit
|
||||
|
||||
Either argument -d <host> or -f <filename> must be given.
|
||||
|
||||
### Program parameter notes
|
||||
|
||||
_decode-config.py_
|
||||
|
||||
|
||||
### Examples
|
||||
The most of the examples are for linux command line. Under Windows call the program using `python decode-config.py ...`.
|
||||
|
||||
#### Config file
|
||||
Note: The example contains .ini style sections `[...]`. Sections are always treated as comment and serves as clarity only.
|
||||
For further details of config file syntax see [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/).
|
||||
|
||||
*my.conf*
|
||||
|
||||
[Source]
|
||||
username = admin
|
||||
password = myPaszxwo!z
|
||||
|
||||
[JSON]
|
||||
json-indent 2
|
||||
|
||||
#### Using Tasmota binary configuration files
|
||||
|
||||
1. Restore a Tasmota configuration file
|
||||
|
||||
`decode-config.py -c my.conf -d tasmota --restore-file Config_Tasmota_6.2.1.dmp`
|
||||
|
||||
2. Backup device using Tasmota configuration compatible format
|
||||
|
||||
a) use file extension to choice the file format
|
||||
|
||||
`decode-config.py -c my.conf -d tasmota --backup-file Config_@f_@v.dmp`
|
||||
|
||||
b) use args to choice the file format
|
||||
|
||||
`decode-config.py -c my.conf -d tasmota --backup-type dmp --backup-file Config_@f_@v`
|
||||
|
||||
#### Use batch processing
|
||||
|
||||
for device in tasmota1 tasmota2 tasmota3; do ./decode-config.py -c my.conf -d $device -o Config_@f_@v
|
||||
|
||||
or under windows
|
||||
|
||||
for device in (tasmota1 tasmota2 tasmota3) do python decode-config.py -c my.conf -d %device -o Config_@f_@v
|
||||
|
||||
will produce JSON configuration files for host tasmota1, tasmota2 and tasmota3 using friendly name and Tasmota firmware version for backup filenames.
|
||||
|
||||
## Notes
|
||||
Some general notes:
|
||||
* Filename replacement macros **@h** and **@H**:
|
||||
* **@h**
|
||||
The **@h** replacement macro uses the hostname configured with the Tasomta Wifi `Hostname <host>` command (defaults to `%s-%04d`). It will not use the network hostname of your device because this is not available when working with files only (e.g. `--file <filename>` as source).
|
||||
To prevent having a useless % in your filename, **@h** will not replaced by configuration data hostname if this contains '%' characters.
|
||||
* **@H**
|
||||
If you want to use the network hostname within your filename, use the **@H** replacement macro instead - but be aware this will only replaced if you are using a network device as source (`-d`, `--device`, `--host`); it will not work when using a file as source (`-f`, `--file`)
|
||||
## decode-config has moved to [https://github.com/tasmota/decode-config](https://github.com/tasmota/decode-config)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue