mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' of https://github.com/arendst/Sonoff-Tasmota into hm10
This commit is contained in:
commit
3e43c4c551
|
@ -128,6 +128,7 @@
|
|||
| | | | | | | | |
|
||||
| USE_IR_REMOTE | - | - | x | x | x | x | x |
|
||||
| USE_IR_RECEIVE | - | - | x | x | x | x | x |
|
||||
| USE_IR_REMOTE_FULL | - | - | - | - | - | x | - | Enable ALL protocols
|
||||
| | | | | | | | |
|
||||
| USE_SR04 | - | - | x | x | x | - | x |
|
||||
| USE_TM1638 | - | - | - | - | x | - | - |
|
||||
|
|
|
@ -52,11 +52,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
|
||||
## Changelog
|
||||
|
||||
### Version 8.1.0.4
|
||||
### Version 8.1.0.5
|
||||
|
||||
- Change Lights: simplified gamma correction and 10 bits internal computation
|
||||
- Change commands ``Prefix``, ``Ssid``, ``StateText``, ``NTPServer``, and ``FriendlyName`` displaying all items
|
||||
- Change IRremoteESP8266 library updated to v2.7.2
|
||||
- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*``
|
||||
- Change wifi connectivity stability (#7602)
|
||||
- Fix Sonoff Bridge, Sc, L1, iFan03 and CSE7766 serial interface to forced speed, config and disable logging
|
||||
- Fix commands ``Display`` and ``Counter`` from overruling command processing (#7322)
|
||||
- Fix ``White`` added to light status (#7142)
|
||||
|
@ -66,6 +68,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
- Fix ``WakeUp <x>`` ignores provided value (#7473)
|
||||
- Fix exception 9 restart on log message in Ticker interrupt service routines NTP, Wemos and Hue emulation (#7496)
|
||||
- Fix ``PowerDelta`` zero power detection (#7515)
|
||||
- Fix ``RGBWWTable`` ignored (#7572)
|
||||
- Add command ``SetOption79 0/1`` to enable reset of counters at teleperiod time by Andre Thomas (#7355)
|
||||
- Add command ``SetOption82 0/1`` to limit the CT range for Alexa to 200..380
|
||||
- Add command ``ShutterButton <parameters>`` to control shutter(s) by to-scho (#7403)
|
||||
|
@ -80,3 +83,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
- Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota
|
||||
- Add support for gzipped binaries
|
||||
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
|
||||
- Add most SetOptions as defines to my_user_config.h
|
||||
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
|
||||
- Add optional parameter <startcolor> to command ``Scheme <scheme>, <startcolor>`` to control initial start color
|
||||
- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zigbeereceived#vibration_sensor#aqaracubeside=0 do ...``
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
## Unreleased (development)
|
||||
|
||||
### 8.1.0.5 20200126
|
||||
|
||||
- Change wifi connectivity stability (#7602)
|
||||
- Add ``SetOption84 1`` sends AWS IoT device shadow updates (alternative to retained)
|
||||
|
||||
### 8.1.0.4 20200116
|
||||
|
||||
- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*``
|
||||
- Fix ``PowerDelta`` zero power detection (#7515)
|
||||
- Fix OTA minimal gzipped detection regression from 8.1.0.3
|
||||
- Fix ``RGBWWTable`` ignored (#7572)
|
||||
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
|
||||
- Add Zigbee persistence and friendly names
|
||||
- Add most SetOptions as defines to my_user_config.h
|
||||
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
|
||||
- Add optional parameter <startcolor> to command ``Scheme <scheme>, <startcolor>`` to control initial start color
|
||||
- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zigbeereceived#vibration_sensor#aqaracubeside=0 do ...``
|
||||
|
||||
### 8.1.0.3 20200106
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
#define D_JSON_ACTIVE_POWERUSAGE "ActivePower"
|
||||
#define D_JSON_APPARENT_POWERUSAGE "ApparentPower"
|
||||
#define D_JSON_REACTIVE_POWERUSAGE "ReactivePower"
|
||||
#define D_JSON_RANGE "Range"
|
||||
#define D_JSON_PRESSURE "Pressure"
|
||||
#define D_JSON_PRESSUREATSEALEVEL "SeaPressure"
|
||||
#define D_JSON_PRESSURE_UNIT "PressureUnit"
|
||||
|
@ -466,26 +467,32 @@
|
|||
#define D_JSON_TUYA_MCU_RECEIVED "TuyaReceived"
|
||||
|
||||
// Commands xdrv_23_zigbee.ino
|
||||
#define D_PRFX_ZB "Zb"
|
||||
#define D_PRFX_ZIGBEE "Zigbee"
|
||||
#define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)"
|
||||
#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin"
|
||||
#define D_CMND_ZIGBEE_STATUS "ZigbeeStatus"
|
||||
#define D_CMND_ZIGBEE_RESET "ZigbeeReset"
|
||||
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
|
||||
#define D_CMND_ZIGBEE_STATUS "Status"
|
||||
#define D_CMND_ZIGBEE_RESET "Reset"
|
||||
#define D_JSON_ZIGBEE_CC2530 "CC2530"
|
||||
#define D_CMND_ZIGBEEZNPRECEIVE "ZigbeeZNPReceive" // only for debug
|
||||
#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend"
|
||||
#define D_JSON_ZIGBEE_STATE "ZigbeeState"
|
||||
#define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived"
|
||||
#define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent"
|
||||
#define D_JSON_ZIGBEEZCL_RECEIVED "ZigbeeZCLReceived"
|
||||
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived"
|
||||
#define D_CMND_ZIGBEEZNPRECEIVE "ZNPReceive" // only for debug
|
||||
#define D_CMND_ZIGBEEZNPSEND "ZNPSend"
|
||||
#define D_JSON_ZIGBEE_STATE "ZbState"
|
||||
#define D_JSON_ZIGBEEZNPRECEIVED "ZbZNPReceived"
|
||||
#define D_JSON_ZIGBEEZNPSENT "ZbZNPSent"
|
||||
#define D_JSON_ZIGBEEZCL_RECEIVED "ZbZCLReceived"
|
||||
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZbZCLRawReceived"
|
||||
#define D_JSON_ZIGBEE_DEVICE "Device"
|
||||
#define D_JSON_ZIGBEE_NAME "Name"
|
||||
#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe"
|
||||
#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived"
|
||||
#define D_CMND_ZIGBEE_NAME "Name"
|
||||
#define D_CMND_ZIGBEE_PROBE "Probe"
|
||||
#define D_CMND_ZIGBEE_FORGET "Forget"
|
||||
#define D_CMND_ZIGBEE_SAVE "Save"
|
||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||
#define D_CMND_ZIGBEE_READ "ZigbeeRead"
|
||||
#define D_CMND_ZIGBEE_SEND "ZigbeeSend"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZigbeeZCLSent"
|
||||
#define D_CMND_ZIGBEE_READ "Read"
|
||||
#define D_CMND_ZIGBEE_SEND "Send"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
||||
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
||||
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
|
||||
|
||||
// Commands xdrv_25_A4988_Stepper.ino
|
||||
#define D_CMND_MOTOR "MOTOR"
|
||||
|
@ -602,6 +609,7 @@ const char JSON_SNS_TEMPHUM[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,
|
|||
|
||||
const char JSON_SNS_ILLUMINANCE[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d}";
|
||||
const char JSON_SNS_MOISTURE[] PROGMEM = ",\"%s\":{\"" D_JSON_MOISTURE "\":%d}";
|
||||
const char JSON_SNS_RANGE[] PROGMEM = ",\"%s\":{\"" D_JSON_RANGE "\":%d}";
|
||||
|
||||
const char JSON_SNS_GNGPM[] PROGMEM = ",\"%s\":{\"" D_JSON_TOTAL_USAGE "\":%s,\"" D_JSON_FLOWRATE "\":%s}";
|
||||
|
||||
|
@ -633,6 +641,7 @@ const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PE
|
|||
const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}";
|
||||
const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN" {e}";
|
||||
const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d %%{e}";
|
||||
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d{e}";
|
||||
|
||||
const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}";
|
||||
const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}";
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Размер на програмата"
|
||||
#define D_PROJECT "Проект"
|
||||
#define D_RAIN "Дъжд"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Получено"
|
||||
#define D_RESTART "Рестарт"
|
||||
#define D_RESTARTING "Рестартиране"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Velikost programu"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Přijatý"
|
||||
#define D_RESTART "Restart"
|
||||
#define D_RESTARTING "Restartování"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Ben. Flash Speicher"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Regen"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "erhalten"
|
||||
#define D_RESTART "Neustart"
|
||||
#define D_RESTARTING "starte neu"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Μέγεθος προγράμματος"
|
||||
#define D_PROJECT "Έργο"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Ελήφθη"
|
||||
#define D_RESTART "Επανεκκίνηση"
|
||||
#define D_RESTARTING "Επανεκκινεί"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Program Size"
|
||||
#define D_PROJECT "Project"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Received"
|
||||
#define D_RESTART "Restart"
|
||||
#define D_RESTARTING "Restarting"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Tamaño Programa"
|
||||
#define D_PROJECT "Proyecto"
|
||||
#define D_RAIN "Lluvia"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Recibido"
|
||||
#define D_RESTART "Reiniciar"
|
||||
#define D_RESTARTING "Reiniciando"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Taille programme"
|
||||
#define D_PROJECT "Projet"
|
||||
#define D_RAIN "Pluie"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Reçu"
|
||||
#define D_RESTART "Redémarrage"
|
||||
#define D_RESTARTING "Redémarre"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "גודל תוכנית"
|
||||
#define D_PROJECT "פרויקט"
|
||||
#define D_RAIN "גשם"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "התקבל"
|
||||
#define D_RESTART "איתחול"
|
||||
#define D_RESTARTING "הפעלה מחדש"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Program méret"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Eső"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Érkezett"
|
||||
#define D_RESTART "Újraindítás"
|
||||
#define D_RESTARTING "Újraindítás"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Dimensione Programma"
|
||||
#define D_PROJECT "Progetto"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Ricevuto"
|
||||
#define D_RESTART "Riavvio"
|
||||
#define D_RESTARTING "Riavviando"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "프로그램 용량"
|
||||
#define D_PROJECT "프로젝트"
|
||||
#define D_RAIN "비"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "받음"
|
||||
#define D_RESTART "재시작"
|
||||
#define D_RESTARTING "재시작 중.."
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Programma Grootte"
|
||||
#define D_PROJECT "Project"
|
||||
#define D_RAIN "Regen"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Ontvangen"
|
||||
#define D_RESTART "Herstart"
|
||||
#define D_RESTARTING "Herstarten"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Rozmiar programu"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Deszcz"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Otrzymany"
|
||||
#define D_RESTART "Restart"
|
||||
#define D_RESTARTING "Restartowanie"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Tamanho do programa"
|
||||
#define D_PROJECT "Projeto"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Recebido"
|
||||
#define D_RESTART "Reiniciar"
|
||||
#define D_RESTARTING "Reiniciando"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Tamanho do Programa"
|
||||
#define D_PROJECT "Projeto"
|
||||
#define D_RAIN "Chuva"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Recebido"
|
||||
#define D_RESTART "Reiniciar"
|
||||
#define D_RESTARTING "A reiniciar"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Размер программы "
|
||||
#define D_PROJECT "Проект"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Получено"
|
||||
#define D_RESTART "Перезапуск"
|
||||
#define D_RESTARTING "Перезапуск"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Veľkosť programu"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Dážď"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Prijatý"
|
||||
#define D_RESTART "Reštart"
|
||||
#define D_RESTARTING "Reštartuje sa"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Programstorlek"
|
||||
#define D_PROJECT "Projekt"
|
||||
#define D_RAIN "Regn"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Mottagen"
|
||||
#define D_RESTART "Omstart"
|
||||
#define D_RESTARTING "Startar om"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Yazılım Boyutu"
|
||||
#define D_PROJECT "Proje"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Alınan"
|
||||
#define D_RESTART "Yeniden Başlat"
|
||||
#define D_RESTARTING "Yeniden Başlatılıyor"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "Розмір програми"
|
||||
#define D_PROJECT "Проект"
|
||||
#define D_RAIN "Дощ"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "Отримано"
|
||||
#define D_RESTART "Перезавантаження"
|
||||
#define D_RESTARTING "Перезавантаження"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "固件大小"
|
||||
#define D_PROJECT "项目:"
|
||||
#define D_RAIN "降水量"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "已接收"
|
||||
#define D_RESTART "重启"
|
||||
#define D_RESTARTING "正在重启"
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
#define D_PROGRAM_SIZE "韌體大小"
|
||||
#define D_PROJECT "項目:"
|
||||
#define D_RAIN "Rain"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RECEIVED "已接收"
|
||||
#define D_RESTART "重啟"
|
||||
#define D_RESTARTING "正在重啟"
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
//#define MODULE SONOFF_BASIC // [Module] Select default model from tasmota_template.h
|
||||
|
||||
#define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds)
|
||||
#define SAVE_STATE 1 // [SetOption0] Save changed power state to Flash (0 = disable, 1 = enable)
|
||||
#define SAVE_STATE true // [SetOption0] Save changed power state to Flash (false = disable, true = enable)
|
||||
#define BOOT_LOOP_OFFSET 1 // [SetOption36] Number of boot loops before starting restoring defaults (0 = disable, 1..200 = boot loops offset)
|
||||
|
||||
// -- Wifi ----------------------------------------
|
||||
|
@ -69,6 +69,8 @@
|
|||
#define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if wifi fails to connect (default option: 4 - WIFI_RETRY)
|
||||
// (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY)
|
||||
// The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7.
|
||||
#define WIFI_SCAN_AT_RESTART false // [SetOption56] Scan wifi network at restart for configured AP's
|
||||
#define WIFI_SCAN_REGULARLY false // [SetOption57] Scan wifi network every 44 minutes for configured AP's
|
||||
|
||||
// -- Syslog --------------------------------------
|
||||
#define SYS_LOG_HOST "" // [LogHost] (Linux) syslog host
|
||||
|
@ -80,9 +82,10 @@
|
|||
|
||||
// -- Ota -----------------------------------------
|
||||
#define OTA_URL "http://thehackbox.org/tasmota/release/tasmota.bin" // [OtaUrl]
|
||||
#define OTA_COMPATIBILITY false // [SetOption78] Disable OTA compatibility check
|
||||
|
||||
// -- MQTT ----------------------------------------
|
||||
#define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On)
|
||||
#define MQTT_USE true // [SetOption3] Select default MQTT use (false = Off, true = On)
|
||||
|
||||
#define MQTT_HOST "" // [MqttHost]
|
||||
#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" // [MqttFingerprint1]
|
||||
|
@ -91,10 +94,11 @@
|
|||
#define MQTT_USER "DVES_USER" // [MqttUser] MQTT user
|
||||
#define MQTT_PASS "DVES_PASS" // [MqttPassword] MQTT password
|
||||
|
||||
#define MQTT_BUTTON_RETAIN 0 // [ButtonRetain] Button may send retain flag (0 = off, 1 = on)
|
||||
#define MQTT_POWER_RETAIN 0 // [PowerRetain] Power status message may send retain flag (0 = off, 1 = on)
|
||||
#define MQTT_SWITCH_RETAIN 0 // [SwitchRetain] Switch may send retain flag (0 = off, 1 = on)
|
||||
#define MQTT_BUTTON_SWITCH_FORCE_LOCAL 0 // [SetOption61] Force local operation when button/switch topic is set (0 = off, 1 = on)
|
||||
#define MQTT_BUTTON_RETAIN false // [ButtonRetain] Button may send retain flag (false = off, true = on)
|
||||
#define MQTT_POWER_RETAIN false // [PowerRetain] Power status message may send retain flag (false = off, true = on)
|
||||
#define MQTT_SWITCH_RETAIN false // [SwitchRetain] Switch may send retain flag (false = off, true = on)
|
||||
#define MQTT_SENSOR_RETAIN false // [SensorRetain] Sensor may send retain flag (false = off, true = on)
|
||||
#define MQTT_BUTTON_SWITCH_FORCE_LOCAL false // [SetOption61] Force local operation when button/switch topic is set (false = off, true = on)
|
||||
|
||||
#define MQTT_STATUS_OFF "OFF" // [StateText1] Command or Status result when turned off (needs to be a string like "0" or "Off")
|
||||
#define MQTT_STATUS_ON "ON" // [StateText2] Command or Status result when turned on (needs to be a string like "1" or "On")
|
||||
|
@ -113,19 +117,30 @@
|
|||
// %topic% token options (also ButtonTopic and SwitchTopic)
|
||||
#define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic, set to 'PROJECT "_%06X"' for unique topic including device MAC address
|
||||
#define MQTT_GRPTOPIC "tasmotas" // [GroupTopic] MQTT Group topic
|
||||
#define MQTT_GROUPTOPIC_FORMAT false // [SetOption75] GroupTopic replaces %topic% (false) or fixed topic cmnd/grouptopic (true)
|
||||
#define MQTT_BUTTON_TOPIC "0" // [ButtonTopic] MQTT button topic, "0" = same as MQTT_TOPIC, set to 'PROJECT "_BTN_%06X"' for unique topic including device MAC address
|
||||
#define MQTT_SWITCH_TOPIC "0" // [SwitchTopic] MQTT button topic, "0" = same as MQTT_TOPIC, set to 'PROJECT "_SW_%06X"' for unique topic including device MAC address
|
||||
#define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address
|
||||
|
||||
// -- MQTT - Telemetry ----------------------------
|
||||
#define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds)
|
||||
#define TELE_ON_POWER 0 // [SetOption59] send tele/STATE together with stat/RESULT (0 = Disable, 1 = Enable)
|
||||
#define TELE_ON_POWER false // [SetOption59] send tele/STATE together with stat/RESULT (false = Disable, true = Enable)
|
||||
|
||||
// -- MQTT - Domoticz -----------------------------
|
||||
#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds)
|
||||
|
||||
// -- MQTT - Home Assistant Discovery -------------
|
||||
#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 // [SetOption19] Home Assistant Discovery (0 = Disable, 1 = Enable)
|
||||
#define HOME_ASSISTANT_DISCOVERY_ENABLE false // [SetOption19] Home Assistant Discovery (false = Disable, true = Enable)
|
||||
#define HASS_AS_LIGHT false // [SetOption30] Enforce HAss autodiscovery as light
|
||||
|
||||
// -- MQTT - Options ------------------------------
|
||||
#define MQTT_RESULT_COMMAND false // [SetOption4] Switch between MQTT RESULT or COMMAND
|
||||
#define MQTT_LWT_MESSAGE false // [SetOption10] Switch between MQTT LWT OFFLINE or empty message
|
||||
#define MQTT_POWER_FORMAT false // [SetOption26] Switch between POWER or POWER1 for single power devices
|
||||
#define MQTT_APPEND_TIMEZONE false // [SetOption52] Append timezone to JSON time
|
||||
#define MQTT_NO_HOLD_RETAIN false // [SetOption62] Disable retain flag on HOLD messages
|
||||
#define MQTT_INDEX_SEPARATOR false // [SetOption64] Enable "_" instead of "-" as sensor index separator
|
||||
#define MQTT_TUYA_RECEIVED false // [SetOption66] Enable TuyaMcuReceived messages over Mqtt
|
||||
|
||||
// -- HTTP ----------------------------------------
|
||||
#define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin)
|
||||
|
@ -134,6 +149,9 @@
|
|||
#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 Options --------------------------------
|
||||
#define GUI_SHOW_HOSTNAME false // [SetOption53] Show hostname and IP address in GUI main menu
|
||||
|
||||
// -- 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
|
||||
// Light theme - pre v7
|
||||
|
@ -181,8 +199,12 @@
|
|||
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
|
||||
#define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray
|
||||
|
||||
// -- KNX -----------------------------------------
|
||||
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
|
||||
#define KNX_ENHANCED false // [Knx_Enhanced] Enable KNX Enhanced Mode
|
||||
|
||||
// -- mDNS ----------------------------------------
|
||||
#define MDNS_ENABLED 0 // [SetOption55] Use mDNS (0 = Disable, 1 = Enable)
|
||||
#define MDNS_ENABLED false // [SetOption55] Use mDNS (false = Disable, true = Enable)
|
||||
|
||||
// -- Time - Up to three NTP servers in your region
|
||||
#define NTP_SERVER1 "pool.ntp.org" // [NtpServer1] Select first NTP server by name or IP address (129.250.35.250)
|
||||
|
@ -214,28 +236,69 @@
|
|||
#define APP_LEDSTATE LED_POWER // [LedState] Function of led
|
||||
// (LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT)
|
||||
#define APP_LEDMASK 0xFFFF // [LedMask] Assign Relay to Power led (0xFFFF is default)
|
||||
#define APP_ENABLE_LEDLINK false // [SetOption31] Enable link led blinking
|
||||
|
||||
#define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled)
|
||||
#define APP_POWERON_STATE POWER_ALL_SAVED // [PowerOnState] Power On Relay state
|
||||
// (POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON)
|
||||
#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_NORMAL_SLEEP false // [SetOption60] Enable normal sleep instead of dynamic sleep
|
||||
#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 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
|
||||
#define KEY_DISABLE_MULTIPRESS false // [SetOption1] Disable button multipress
|
||||
#define KEY_SWAP_DOUBLE_PRESS false // [SetOption11] Swap button single and double press functionality
|
||||
#define KEY_ONLY_SINGLE_PRESS false // [SetOption13] Enable only single press to speed up button press recognition
|
||||
|
||||
#define SWITCH_DEBOUNCE_TIME 50 // [SwitchDebounce] Number of mSeconds switch press debounce time
|
||||
#define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, TOGGLEMULTI, FOLLOWMULTI, FOLLOWMULTI_INV (the wall switch state)
|
||||
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)
|
||||
|
||||
#define TEMP_CONVERSION 0 // [SetOption8] Return temperature in (0 = Celsius or 1 = Fahrenheit)
|
||||
#define PRESSURE_CONVERSION 0 // [SetOption24] Return pressure in (0 = hPa or 1 = mmHg)
|
||||
#define TEMP_CONVERSION false // [SetOption8] Return temperature in (false = Celsius or true = Fahrenheit)
|
||||
#define PRESSURE_CONVERSION false // [SetOption24] Return pressure in (false = hPa or true = mmHg)
|
||||
#define TEMP_RESOLUTION 1 // [TempRes] Maximum number of decimals (0 - 3) showing sensor Temperature
|
||||
#define HUMIDITY_RESOLUTION 1 // [HumRes] Maximum number of decimals (0 - 3) showing sensor Humidity
|
||||
#define PRESSURE_RESOLUTION 1 // [PressRes] Maximum number of decimals (0 - 3) showing sensor Pressure
|
||||
#define ENERGY_RESOLUTION 3 // [EnergyRes] Maximum number of decimals (0 - 5) showing energy usage in kWh
|
||||
#define CALC_RESOLUTION 3 // [CalcRes] Maximum number of decimals (0 - 7) used in commands ADD, SUB, MULT and SCALE
|
||||
|
||||
#define APP_FLASH_CYCLE false // [SetOption12] Switch between dynamic or fixed slot flash save location
|
||||
#define APP_NO_RELAY_SCAN false // [SetOption63] Don't scan relay power state at restart
|
||||
#define APP_DISABLE_POWERCYCLE false // [SetOption65] Disable fast power cycle detection for device reset
|
||||
#define DEEPSLEEP_BOOTCOUNT false // [SetOption76] Enable incrementing bootcount when deepsleep is enabled
|
||||
|
||||
// -- Lights --------------------------------------
|
||||
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)
|
||||
#define LIGHT_MODE true // [SetOption15] Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL
|
||||
#define LIGHT_CLOCK_DIRECTION false // [SetOption16] Switch WS2812 clock between clockwise or counter-clockwise
|
||||
#define LIGHT_COLOR_RADIX false // [SetOption17] Switch between decimal or hexadecimal color output (false = hexadecimal, true = decimal)
|
||||
#define LIGHT_PAIRS_CO2 false // [SetOption18] Enable Pair light signal with CO2 sensor
|
||||
#define LIGHT_POWER_CONTROL false // [SetOption20] Enable power control in relation to Dimmer/Color/Ct changes
|
||||
#define LIGHT_CHANNEL_MODE false // [SetOption68] Enable multi-channels PWM instead of Color PWM
|
||||
#define LIGHT_SLIDER_POWER false // [SetOption77] Do not power off if slider moved to far left
|
||||
#define LIGHT_ALEXA_CT_RANGE false // [SetOption82] Reduced CT range for Alexa
|
||||
|
||||
// -- Energy --------------------------------------
|
||||
#define ENERGY_VOLTAGE_ALWAYS false // [SetOption21] Enable show voltage even if powered off
|
||||
#define ENERGY_DDS2382_MODE false // [SetOption71] Enable DDS2382 different Modbus registers for Active Energy (#6531)
|
||||
#define ENERGY_HARDWARE_TOTALS false // [SetOption72] Enable hardware energy total counter as reference (#6561)
|
||||
|
||||
// -- Other Options -------------------------------
|
||||
#define TIMERS_ENABLED false // [Timers] Enable Timers
|
||||
#define RF_DATA_RADIX false // [SetOption28] RF receive data format (false = hexadecimal, true = decimal)
|
||||
#define IR_DATA_RADIX false // [SetOption29] IR receive data format (false = hexadecimal, true = decimal)
|
||||
#define TUYA_SETOPTION_20 false // [SetOption54] Apply SetOption20 settings to Tuya device
|
||||
#define IR_ADD_RAW_DATA false // [SetOption58] Add IR Raw data to JSON message
|
||||
#define BUZZER_ENABLE false // [SetOption67] Enable buzzer when available
|
||||
#define DS18X20_PULL_UP false // [SetOption74] Enable internal pullup for single DS18x20 sensor
|
||||
#define COUNTER_RESET false // [SetOption79] Enable resetting of counters after telemetry was sent
|
||||
#define SHUTTER_SUPPORT false // [SetOption80] Enable shutter support
|
||||
#define PCF8574_INVERT_PORTS false // [SetOption81] Invert all ports on PCF8574 devices
|
||||
#define ZIGBEE_FRIENDLY_NAMES false // [SetOption83] Enable Zigbee FriendlyNames instead of ShortAddresses when possible
|
||||
|
||||
/*********************************************************************************************\
|
||||
* END OF SECTION 1
|
||||
*
|
||||
|
@ -531,7 +594,7 @@
|
|||
#define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255)
|
||||
|
||||
// -- Zigbee interface ----------------------------
|
||||
//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP
|
||||
//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP (+35k code, +3.2k mem)
|
||||
#define USE_ZIGBEE_PANID 0x1A63 // arbitrary PAN ID for Zigbee network, must be unique in the home
|
||||
#define USE_ZIGBEE_EXTPANID 0xCCCCCCCCCCCCCCCCL // arbitrary extended PAN ID
|
||||
#define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26)
|
||||
|
|
|
@ -101,9 +101,9 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
|||
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
|
||||
uint32_t data; // Allow bit manipulation using SetOption
|
||||
struct { // SetOption82 .. SetOption113
|
||||
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
|
||||
uint32_t spare01 : 1;
|
||||
uint32_t spare02 : 1;
|
||||
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
|
||||
uint32_t zigbee_use_names : 1; // bit 1 (v8.1.0.4) - SetOption83 - Use FriendlyNames instead of ShortAddresses when possible
|
||||
uint32_t awsiot_shadow : 1; // bit 2 (v8.1.0.5) - SetOption84 - (AWS IoT) publish MQTT state to a device shadow
|
||||
uint32_t spare03 : 1;
|
||||
uint32_t spare04 : 1;
|
||||
uint32_t spare05 : 1;
|
||||
|
@ -467,8 +467,9 @@ struct SYSCFG {
|
|||
uint8_t hotplug_scan; // F03
|
||||
uint8_t reserved1; // F04 - reserved for s-hadinger
|
||||
|
||||
uint8_t free_f05[215]; // F05
|
||||
uint8_t free_f05[211]; // F05
|
||||
|
||||
int adc_param4; // FD8
|
||||
uint32_t shutter_button[MAX_KEYS]; // FDC
|
||||
uint32_t i2c_drivers[3]; // FEC I2cDriver
|
||||
uint32_t cfg_timestamp; // FF8
|
||||
|
|
|
@ -700,7 +700,11 @@ void EspErase(uint32_t start_sector, uint32_t end_sector)
|
|||
// bool result = EsptoolEraseSector(sector); // Esptool - erases flash completely (slow)
|
||||
|
||||
if (serial_output) {
|
||||
#ifdef ARDUINO_ESP8266_RELEASE_2_3_0
|
||||
Serial.printf(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n", sector, (result) ? D_OK : D_ERROR);
|
||||
#else
|
||||
Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR);
|
||||
#endif
|
||||
delay(10);
|
||||
} else {
|
||||
yield();
|
||||
|
@ -782,8 +786,13 @@ void SettingsDefaultSet2(void)
|
|||
{
|
||||
memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16);
|
||||
|
||||
// Settings.flag.value_units = 0;
|
||||
// Settings.flag.stop_flash_rotate = 0;
|
||||
Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE;
|
||||
Settings.flag.global_state = APP_ENABLE_LEDLINK;
|
||||
Settings.flag3.sleep_normal = APP_NORMAL_SLEEP;
|
||||
Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN;
|
||||
Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE;
|
||||
Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT;
|
||||
Settings.flag3.compatibility_check = OTA_COMPATIBILITY;
|
||||
Settings.save_data = SAVE_DATA;
|
||||
Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY;
|
||||
Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; // SetOption36
|
||||
|
@ -824,6 +833,8 @@ void SettingsDefaultSet2(void)
|
|||
Settings.seriallog_level = SERIAL_LOG_LEVEL;
|
||||
|
||||
// Wifi
|
||||
Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART;
|
||||
Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY;
|
||||
Settings.wifi_output_power = 170;
|
||||
ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS);
|
||||
ParseIp(&Settings.ip_address[1], WIFI_GATEWAY);
|
||||
|
@ -844,16 +855,17 @@ void SettingsDefaultSet2(void)
|
|||
|
||||
// Webserver
|
||||
Settings.flag2.emulation = EMULATION;
|
||||
Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME;
|
||||
Settings.flag3.mdns_enabled = MDNS_ENABLED;
|
||||
Settings.webserver = WEB_SERVER;
|
||||
Settings.weblog_level = WEB_LOG_LEVEL;
|
||||
SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD);
|
||||
Settings.flag3.mdns_enabled = MDNS_ENABLED;
|
||||
SettingsUpdateText(SET_CORS, CORS_DOMAIN);
|
||||
|
||||
// Button
|
||||
// Settings.flag.button_restrict = 0;
|
||||
// Settings.flag.button_swap = 0;
|
||||
// Settings.flag.button_single = 0;
|
||||
Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS;
|
||||
Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS;
|
||||
Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS;
|
||||
Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time
|
||||
|
||||
// Switch
|
||||
|
@ -861,16 +873,19 @@ void SettingsDefaultSet2(void)
|
|||
|
||||
// MQTT
|
||||
Settings.flag.mqtt_enabled = MQTT_USE;
|
||||
// Settings.flag.mqtt_response = 0;
|
||||
Settings.flag.mqtt_response = MQTT_RESULT_COMMAND;
|
||||
Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE;
|
||||
Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN;
|
||||
Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN;
|
||||
Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN;
|
||||
Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL;
|
||||
Settings.flag3.hass_tele_on_power = TELE_ON_POWER;
|
||||
// Settings.flag.mqtt_sensor_retain = 0;
|
||||
// Settings.flag.mqtt_offline = 0;
|
||||
Settings.flag.mqtt_sensor_retain = MQTT_SENSOR_RETAIN;
|
||||
// Settings.flag.mqtt_serial = 0;
|
||||
// Settings.flag.device_index_enable = 0;
|
||||
Settings.flag.device_index_enable = MQTT_POWER_FORMAT;
|
||||
Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE;
|
||||
Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL;
|
||||
Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN;
|
||||
Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR;
|
||||
Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT;
|
||||
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
|
||||
Settings.mqtt_port = MQTT_PORT;
|
||||
SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID);
|
||||
|
@ -904,10 +919,13 @@ void SettingsDefaultSet2(void)
|
|||
Settings.mqttlog_level = MQTT_LOG_LEVEL;
|
||||
|
||||
// Energy
|
||||
Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS;
|
||||
Settings.flag2.current_resolution = 3;
|
||||
// Settings.flag2.voltage_resolution = 0;
|
||||
// Settings.flag2.wattage_resolution = 0;
|
||||
Settings.flag2.energy_resolution = ENERGY_RESOLUTION;
|
||||
Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE;
|
||||
Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS;
|
||||
Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY;
|
||||
// Settings.energy_power_delta = 0;
|
||||
Settings.energy_power_calibration = HLW_PREF_PULSE;
|
||||
|
@ -937,9 +955,12 @@ void SettingsDefaultSet2(void)
|
|||
Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP;
|
||||
|
||||
// IRRemote
|
||||
Settings.flag.ir_receive_decimal = IR_DATA_RADIX;
|
||||
Settings.flag3.receive_raw = IR_ADD_RAW_DATA;
|
||||
Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE;
|
||||
|
||||
// RF Bridge
|
||||
Settings.flag.rf_receive_decimal = RF_DATA_RADIX;
|
||||
// for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; }
|
||||
memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9);
|
||||
|
||||
|
@ -960,6 +981,8 @@ void SettingsDefaultSet2(void)
|
|||
Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION;
|
||||
Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION;
|
||||
Settings.flag2.temperature_resolution = TEMP_RESOLUTION;
|
||||
Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP;
|
||||
Settings.flag3.counter_reset_on_tele = COUNTER_RESET;
|
||||
// Settings.altitude = 0;
|
||||
|
||||
// Rules
|
||||
|
@ -968,19 +991,28 @@ void SettingsDefaultSet2(void)
|
|||
// for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; }
|
||||
Settings.flag2.calc_resolution = CALC_RESOLUTION;
|
||||
|
||||
// Timer
|
||||
Settings.flag3.timers_enable = TIMERS_ENABLED;
|
||||
|
||||
// Home Assistant
|
||||
Settings.flag.hass_light = HASS_AS_LIGHT;
|
||||
Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE;
|
||||
Settings.flag3.hass_tele_on_power = TELE_ON_POWER;
|
||||
|
||||
// Knx
|
||||
// Settings.flag.knx_enabled = 0;
|
||||
// Settings.flag.knx_enable_enhancement = 0;
|
||||
Settings.flag.knx_enabled = KNX_ENABLED;
|
||||
Settings.flag.knx_enable_enhancement = KNX_ENHANCED;
|
||||
|
||||
// Light
|
||||
Settings.flag.pwm_control = 1;
|
||||
//Settings.flag.ws_clock_reverse = 0;
|
||||
//Settings.flag.light_signal = 0;
|
||||
//Settings.flag.not_power_linked = 0;
|
||||
//Settings.flag.decimal_text = 0;
|
||||
Settings.flag.pwm_control = LIGHT_MODE;
|
||||
Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION;
|
||||
Settings.flag.light_signal = LIGHT_PAIRS_CO2;
|
||||
Settings.flag.not_power_linked = LIGHT_POWER_CONTROL;
|
||||
Settings.flag.decimal_text = LIGHT_COLOR_RADIX;
|
||||
Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE;
|
||||
Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER;
|
||||
Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE;
|
||||
|
||||
Settings.pwm_frequency = PWM_FREQ;
|
||||
Settings.pwm_range = PWM_RANGE;
|
||||
for (uint32_t i = 0; i < MAX_PWMS; i++) {
|
||||
|
@ -1064,6 +1096,15 @@ void SettingsDefaultSet2(void)
|
|||
|
||||
memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors
|
||||
SettingsEnableAllI2cDrivers();
|
||||
|
||||
// Tuya
|
||||
Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20;
|
||||
Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED;
|
||||
|
||||
Settings.flag3.buzzer_enable = BUZZER_ENABLE;
|
||||
Settings.flag3.shutter_mode = SHUTTER_SUPPORT;
|
||||
Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS;
|
||||
Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
|
|
@ -493,6 +493,17 @@ bool ParseIp(uint32_t* addr, const char* str)
|
|||
return (3 == i);
|
||||
}
|
||||
|
||||
uint32_t ParseParameters(uint32_t count, uint32_t *params)
|
||||
{
|
||||
char *p;
|
||||
uint32_t i = 0;
|
||||
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p)) {
|
||||
params[i] = strtoul(str, nullptr, 0);
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// Function to parse & check if version_str is newer than our currently installed version.
|
||||
bool NewerVersion(char* version_str)
|
||||
{
|
||||
|
@ -1521,11 +1532,7 @@ bool I2cSetDevice(uint32_t addr)
|
|||
return false; // If already active report as not present;
|
||||
}
|
||||
Wire.beginTransmission((uint8_t)addr);
|
||||
bool result = (0 == Wire.endTransmission());
|
||||
if (result) {
|
||||
I2cSetActive(addr, 1);
|
||||
}
|
||||
return result;
|
||||
return (0 == Wire.endTransmission());
|
||||
}
|
||||
#endif // USE_I2C
|
||||
|
||||
|
@ -1631,8 +1638,12 @@ void AddLog(uint32_t loglevel)
|
|||
if (!web_log_index) web_log_index++; // Index 0 is not allowed as it is the end of char string
|
||||
}
|
||||
#endif // USE_WEBSERVER
|
||||
if (!global_state.mqtt_down && (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); }
|
||||
if (!global_state.wifi_down && (loglevel <= syslog_level)) { Syslog(); }
|
||||
if (Settings.flag.mqtt_enabled && // SetOption3 - Enable MQTT
|
||||
!global_state.mqtt_down &&
|
||||
(loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); }
|
||||
|
||||
if (!global_state.wifi_down &&
|
||||
(loglevel <= syslog_level)) { Syslog(); }
|
||||
}
|
||||
|
||||
void AddLog_P(uint32_t loglevel, const char *formatP)
|
||||
|
|
|
@ -338,7 +338,8 @@ void CmndStatus(void)
|
|||
char stemp2[TOPSZ];
|
||||
|
||||
// Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2)) && (!payload)) { option++; } // TELE
|
||||
// Commented on 20200118 as it seems to be no longer needed
|
||||
// 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; }
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
return _buf->len;
|
||||
}
|
||||
size_t add32(const uint32_t data) { // append 32 bits value
|
||||
if (_buf->len < _buf->size - 3) { // do we have room for 2 bytes
|
||||
if (_buf->len < _buf->size - 3) { // do we have room for 4 bytes
|
||||
_buf->buf[_buf->len++] = data;
|
||||
_buf->buf[_buf->len++] = data >> 8;
|
||||
_buf->buf[_buf->len++] = data >> 16;
|
||||
|
@ -88,6 +88,19 @@ public:
|
|||
}
|
||||
return _buf->len;
|
||||
}
|
||||
size_t add64(const uint64_t data) { // append 64 bits value
|
||||
if (_buf->len < _buf->size - 7) { // do we have room for 8 bytes
|
||||
_buf->buf[_buf->len++] = data;
|
||||
_buf->buf[_buf->len++] = data >> 8;
|
||||
_buf->buf[_buf->len++] = data >> 16;
|
||||
_buf->buf[_buf->len++] = data >> 24;
|
||||
_buf->buf[_buf->len++] = data >> 32;
|
||||
_buf->buf[_buf->len++] = data >> 40;
|
||||
_buf->buf[_buf->len++] = data >> 48;
|
||||
_buf->buf[_buf->len++] = data >> 56;
|
||||
}
|
||||
return _buf->len;
|
||||
}
|
||||
|
||||
size_t addBuffer(const SBuffer &buf2) {
|
||||
if (len() + buf2.len() <= size()) {
|
||||
|
@ -152,6 +165,20 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
// if no NULL is found, returns length until the end of the buffer
|
||||
inline size_t strlen(const size_t offset) const {
|
||||
return strnlen((const char*) &_buf->buf[offset], len() - offset);
|
||||
}
|
||||
|
||||
size_t strlen_s(const size_t offset) const {
|
||||
size_t slen = this->strlen(offset);
|
||||
if (slen == len() - offset) {
|
||||
return 0; // we didn't find a NULL char
|
||||
} else {
|
||||
return slen;
|
||||
}
|
||||
}
|
||||
|
||||
SBuffer subBuffer(const size_t start, size_t len) const {
|
||||
if (start >= _buf->len) {
|
||||
len = 0;
|
||||
|
|
|
@ -438,9 +438,9 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
|
|||
#ifdef USE_DOMOTICZ
|
||||
if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) {
|
||||
#endif // USE_DOMOTICZ
|
||||
MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
|
||||
: Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
|
||||
(state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
|
||||
MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
|
||||
: Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
|
||||
(state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
|
||||
#ifdef USE_DOMOTICZ
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
|
@ -692,7 +692,12 @@ void MqttPublishSensor(void)
|
|||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
/*********************************************************************************************\
|
||||
* State loops
|
||||
\*********************************************************************************************/
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
* Every second
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
void PerformEverySecond(void)
|
||||
{
|
||||
|
@ -715,6 +720,13 @@ void PerformEverySecond(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
if (mqtt_cmnd_blocked_reset) {
|
||||
mqtt_cmnd_blocked_reset--;
|
||||
if (!mqtt_cmnd_blocked_reset) {
|
||||
mqtt_cmnd_blocked = 0; // Clean up MQTT cmnd loop block
|
||||
}
|
||||
}
|
||||
|
||||
if (seriallog_timer) {
|
||||
seriallog_timer--;
|
||||
if (!seriallog_timer) {
|
||||
|
@ -763,9 +775,6 @@ void PerformEverySecond(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* State loops
|
||||
\*********************************************************************************************/
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
* Every 0.1 second
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
|
@ -822,8 +831,6 @@ void Every250mSeconds(void)
|
|||
state_250mS++;
|
||||
state_250mS &= 0x3;
|
||||
|
||||
if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up
|
||||
|
||||
if (!Settings.flag.global_state) { // Problem blinkyblinky enabled - SetOption31 - Control link led blinking
|
||||
if (global_state.data) { // Any problem
|
||||
if (global_state.mqtt_down) { blinkinterval = 7; } // MQTT problem so blink every 2 seconds (slowest)
|
||||
|
|
|
@ -22,14 +22,17 @@
|
|||
\*********************************************************************************************/
|
||||
|
||||
#ifndef WIFI_RSSI_THRESHOLD
|
||||
#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network
|
||||
// Decrease the roam threshold from 10 to 5 to address devices connecting at very low RSSI and being close to inoperative
|
||||
#define WIFI_RSSI_THRESHOLD 5 // Difference in dB between current network and scanned network
|
||||
#endif
|
||||
#ifndef WIFI_RESCAN_MINUTES
|
||||
#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan
|
||||
// Increase rescan interval from 44 to 5 minutes to improve ability for devices to reach network harmony
|
||||
#define WIFI_RESCAN_MINUTES 5 // Number of minutes between wifi network rescan
|
||||
#endif
|
||||
|
||||
const uint8_t WIFI_CONFIG_SEC = 180; // seconds before restart
|
||||
const uint8_t WIFI_CHECK_SEC = 20; // seconds
|
||||
// Drop from 20 seconds to 5 seconds since we control the reconnections, not the Arduino SDK
|
||||
const uint8_t WIFI_CHECK_SEC = 5; // seconds
|
||||
const uint8_t WIFI_RETRY_OFFSET_SEC = 20; // seconds
|
||||
|
||||
#include <ESP8266WiFi.h> // Wifi, MQTT, Ota, WifiManager
|
||||
|
@ -49,7 +52,9 @@ struct WIFI {
|
|||
uint8_t config_counter = 0;
|
||||
uint8_t mdns_begun = 0; // mDNS active
|
||||
uint8_t scan_state;
|
||||
uint8_t bssid[6];
|
||||
uint8_t bssid[6] = {0};
|
||||
uint8_t bssid_last[6] = {0}; // store the last connect bssid
|
||||
int8_t best_network_db;
|
||||
} Wifi;
|
||||
|
||||
int WifiGetRssiAsQuality(int rssi)
|
||||
|
@ -182,7 +187,9 @@ void WifiBegin(uint8_t flag, uint8_t channel)
|
|||
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } // B/G/N
|
||||
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11G) { WiFi.setPhyMode(WIFI_PHY_MODE_11G); } // B/G
|
||||
if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); }
|
||||
// WiFi.setAutoReconnect(true);
|
||||
// Handle the reconnection in WifiCheckIp() since the autoreconnect keeps sending deauthentication messages which causes the AP to block traffic as it looks like an DoS attack
|
||||
// This needs to be explicitly called as "false" otherwise the default is enabled
|
||||
WiFi.setAutoReconnect(false);
|
||||
switch (flag) {
|
||||
case 0: // AP1
|
||||
case 1: // AP2
|
||||
|
@ -198,13 +205,18 @@ void WifiBegin(uint8_t flag, uint8_t channel)
|
|||
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);
|
||||
|
||||
char stemp[40] = { 0 };
|
||||
if (channel) {
|
||||
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid);
|
||||
// Add connected BSSID and channel for multi-AP installations
|
||||
char hex_char[18];
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':'));
|
||||
} else {
|
||||
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active));
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."),
|
||||
Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."),
|
||||
Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
|
||||
|
||||
#if LWIP_IPV6
|
||||
for (bool configured = false; !configured;) {
|
||||
|
@ -223,22 +235,22 @@ void WifiBegin(uint8_t flag, uint8_t channel)
|
|||
|
||||
void WifiBeginAfterScan(void)
|
||||
{
|
||||
static int8_t best_network_db;
|
||||
|
||||
// Not active
|
||||
if (0 == Wifi.scan_state) { return; }
|
||||
// Init scan when not connected
|
||||
if (1 == Wifi.scan_state) {
|
||||
memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid));
|
||||
best_network_db = -127;
|
||||
Wifi.best_network_db = -127;
|
||||
Wifi.scan_state = 3;
|
||||
}
|
||||
// Init scan when connected
|
||||
if (2 == Wifi.scan_state) {
|
||||
uint8_t* bssid = WiFi.BSSID(); // Get current bssid
|
||||
memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid));
|
||||
best_network_db = WiFi.RSSI(); // Get current rssi and add threshold
|
||||
if (best_network_db < -WIFI_RSSI_THRESHOLD) { best_network_db += WIFI_RSSI_THRESHOLD; }
|
||||
Wifi.best_network_db = WiFi.RSSI(); // Get current rssi and add threshold
|
||||
if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) {
|
||||
Wifi.best_network_db += WIFI_RSSI_THRESHOLD;
|
||||
}
|
||||
Wifi.scan_state = 3;
|
||||
}
|
||||
// Init scan
|
||||
|
@ -259,6 +271,12 @@ void WifiBeginAfterScan(void)
|
|||
}
|
||||
// Scan done
|
||||
if (5 == Wifi.scan_state) {
|
||||
uint32_t number_known = 0; // count the number of known AP's so we can clear the Wifi.bssid_last if there is only one
|
||||
int32_t channel_max = 0; // No scan result
|
||||
int8_t ap_max = 3; // AP default if not found
|
||||
uint8_t bssid_max[6]; // Save last bssid
|
||||
memcpy((void*) &bssid_max, (void*) &Wifi.bssid, sizeof(bssid_max)); // store the strongest bssid
|
||||
|
||||
int32_t channel = 0; // No scan result
|
||||
int8_t ap = 3; // AP default if not found
|
||||
uint8_t last_bssid[6]; // Save last bssid
|
||||
|
@ -282,12 +300,25 @@ void WifiBeginAfterScan(void)
|
|||
for (j = 0; j < MAX_SSIDS; j++) {
|
||||
if (ssid_scan == SettingsText(SET_STASSID1 + j)) { // SSID match
|
||||
known = true;
|
||||
if (rssi_scan > best_network_db) { // Best network
|
||||
number_known++;
|
||||
if (rssi_scan > Wifi.best_network_db) { // Best network
|
||||
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
|
||||
memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid));
|
||||
// store the max values in case there is only one AP and we need to try to reconnect
|
||||
memcpy((void*) &bssid_max, (void*) bssid_scan, sizeof(bssid_max));
|
||||
channel_max = chan_scan;
|
||||
ap_max = j;
|
||||
// if the bssid is not the same as the last failed attempt, force picking the next strongest AP to prevent getting stuck on a strong RSSI, but poor channel health
|
||||
for (uint32_t i = 0; i < sizeof(Wifi.bssid_last); i++) {
|
||||
if (bssid_scan[i] != Wifi.bssid_last[i]) {
|
||||
Wifi.best_network_db = (int8_t)rssi_scan;
|
||||
channel = chan_scan;
|
||||
ap = j; // AP1 or AP2
|
||||
memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid));
|
||||
// save the last bssid used
|
||||
memcpy((void*) &Wifi.bssid_last, (void*) bssid_scan, sizeof(Wifi.bssid_last));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -307,6 +338,16 @@ void WifiBeginAfterScan(void)
|
|||
WiFi.scanDelete(); // Clean up Ram
|
||||
delay(0);
|
||||
}
|
||||
|
||||
// reset the last bssid if there is only one AP to allow the reconnect of the same AP on the next cycle
|
||||
if (number_known == 1) {
|
||||
// clear the last value
|
||||
memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last));
|
||||
memcpy((void*) &Wifi.bssid, (void*) bssid_max, sizeof(Wifi.bssid));
|
||||
channel = channel_max;
|
||||
ap = ap_max;
|
||||
}
|
||||
|
||||
Wifi.scan_state = 0;
|
||||
// If bssid changed then (re)connect wifi
|
||||
for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) {
|
||||
|
@ -381,17 +422,11 @@ void WifiCheckIp(void)
|
|||
#else
|
||||
if ((WL_CONNECTED == WiFi.status()) && (static_cast<uint32_t>(WiFi.localIP()) != 0)) {
|
||||
#endif // LWIP_IPV6=1
|
||||
// initialize the last connect bssid since we had a successful connection
|
||||
memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last));
|
||||
WifiSetState(1);
|
||||
Wifi.counter = WIFI_CHECK_SEC;
|
||||
Wifi.retry = Wifi.retry_init;
|
||||
AddLog_P((Wifi.status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED));
|
||||
if (Wifi.status != WL_CONNECTED) {
|
||||
// AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses"));
|
||||
Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP();
|
||||
Settings.ip_address[2] = (uint32_t)WiFi.subnetMask();
|
||||
Settings.ip_address[3] = (uint32_t)WiFi.dnsIP();
|
||||
}
|
||||
Wifi.status = WL_CONNECTED;
|
||||
#ifdef USE_DISCOVERY
|
||||
#ifdef WEBSERVER_ADVERTISE
|
||||
if (2 == Wifi.mdns_begun) {
|
||||
|
@ -407,32 +442,18 @@ void WifiCheckIp(void)
|
|||
switch (Wifi.status) {
|
||||
case WL_CONNECTED:
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS));
|
||||
Wifi.status = 0;
|
||||
Wifi.retry = Wifi.retry_init;
|
||||
// if poor channel health prevents DHCP broadcast from succeeding, restart the request
|
||||
// The code will eventually do a recoonect when the 1/2 interval is hit to try another access point if this remains unsuccessful
|
||||
wifi_station_dhcpc_start();
|
||||
break;
|
||||
case WL_NO_SSID_AVAIL:
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED));
|
||||
if (WIFI_WAIT == Settings.sta_config) {
|
||||
Wifi.retry = Wifi.retry_init;
|
||||
} else {
|
||||
if (Wifi.retry > (Wifi.retry_init / 2)) {
|
||||
Wifi.retry = Wifi.retry_init / 2;
|
||||
}
|
||||
else if (Wifi.retry) {
|
||||
Wifi.retry = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD));
|
||||
if (Wifi.retry > (Wifi.retry_init / 2)) {
|
||||
Wifi.retry = Wifi.retry_init / 2;
|
||||
}
|
||||
else if (Wifi.retry) {
|
||||
Wifi.retry = 0;
|
||||
}
|
||||
break;
|
||||
default: // WL_IDLE_STATUS and WL_DISCONNECTED
|
||||
// log on the 1/2 or full interval
|
||||
if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) {
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT));
|
||||
} else {
|
||||
|
@ -444,9 +465,11 @@ void WifiCheckIp(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Wifi.retry) {
|
||||
if (Settings.flag3.use_wifi_scan) { // SetOption56 - Scan wifi network at restart for configured AP's
|
||||
if (Wifi.retry_init == Wifi.retry) {
|
||||
// check the 1/2 interval as well when rescanning - scan state machine takes 4 seconds
|
||||
if ((Wifi.retry_init == Wifi.retry) || ((Wifi.retry_init / 2) == Wifi.retry)){
|
||||
Wifi.scan_state = 1; // Select scanned SSID
|
||||
}
|
||||
} else {
|
||||
|
@ -593,7 +616,6 @@ String WifiGetOutputPower(void)
|
|||
dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1);
|
||||
return String(stemp1);
|
||||
}
|
||||
|
||||
void WifiSetOutputPower(void)
|
||||
{
|
||||
WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10);
|
||||
|
@ -605,7 +627,9 @@ void WifiConnect(void)
|
|||
WifiSetOutputPower();
|
||||
WiFi.persistent(false); // Solve possible wifi init errors
|
||||
Wifi.status = 0;
|
||||
Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2);
|
||||
// lower the rety times now Tasmota control the reconnections, not the Arduino SDK
|
||||
// Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2);
|
||||
Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP.getChipId() & 0xF);
|
||||
Wifi.retry = Wifi.retry_init;
|
||||
Wifi.counter = 1;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
\*********************************************************************************************/
|
||||
|
||||
#define CODE_IMAGE 0
|
||||
#define CODE_IMAGE_STR "tasmota"
|
||||
|
||||
#define USE_LIGHT // Enable light control
|
||||
#define USE_ENERGY_SENSOR // Use energy sensors (+14k code)
|
||||
|
|
|
@ -68,8 +68,6 @@
|
|||
// Structs
|
||||
#include "settings.h"
|
||||
|
||||
const char kCodeImage[] PROGMEM = "tasmota|minimal|sensors|knx|lite|display|ir";
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Global variables
|
||||
\*********************************************************************************************/
|
||||
|
@ -110,12 +108,13 @@ float global_temperature = 9999; // Provide a global temperature to b
|
|||
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
|
||||
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
|
||||
uint16_t seriallog_timer = 0; // Timer to disable Seriallog
|
||||
uint16_t syslog_timer = 0; // Timer to re-enable syslog_level
|
||||
int16_t save_data_counter; // Counter and flag for config save to Flash
|
||||
RulesBitfield rules_flag; // Rule state flags (16 bits)
|
||||
uint8_t mqtt_cmnd_blocked = 0; // Ignore flag for publish command
|
||||
uint8_t mqtt_cmnd_blocked_reset = 0; // Count down to reset if needed
|
||||
uint8_t state_250mS = 0; // State 250msecond per second flag
|
||||
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
|
||||
uint8_t sleep; // Current copy of Settings.sleep
|
||||
|
@ -199,8 +198,8 @@ void setup(void)
|
|||
if (VERSION & 0xff) { // Development or patched version 6.3.0.10
|
||||
snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff);
|
||||
}
|
||||
char code_image[20];
|
||||
snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), GetTextIndexed(code_image, sizeof(code_image), CODE_IMAGE, kCodeImage));
|
||||
// Thehackbox inserts "release" or "commit number" before compiling using sed -i -e 's/PSTR("(%s)")/PSTR("(85cff52-%s)")/g' tasmota.ino
|
||||
snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); // Results in (85cff52-tasmota) or (release-tasmota)
|
||||
|
||||
SettingsLoad();
|
||||
SettingsDelta();
|
||||
|
|
|
@ -79,6 +79,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 2
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "sensors"
|
||||
|
||||
#undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem)
|
||||
|
||||
|
@ -228,6 +230,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 3
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "knx"
|
||||
|
||||
#ifndef USE_KNX
|
||||
#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem)
|
||||
|
@ -248,6 +252,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 5
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "display"
|
||||
|
||||
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
|
||||
#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
|
||||
|
@ -313,6 +319,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 6
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "ir"
|
||||
|
||||
#undef USE_EMULATION
|
||||
#undef USE_EMULATION_HUE // Disable Hue emulation - only for lights and relays
|
||||
|
@ -406,6 +414,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 4
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "lite"
|
||||
|
||||
#undef APP_SLEEP
|
||||
#define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_LITE
|
||||
|
@ -511,6 +521,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
|
||||
#undef CODE_IMAGE
|
||||
#define CODE_IMAGE 1
|
||||
#undef CODE_IMAGE_STR
|
||||
#define CODE_IMAGE_STR "minimal"
|
||||
|
||||
#undef FIRMWARE_LITE // Disable tasmota-lite with no sensors
|
||||
#undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled
|
||||
|
|
|
@ -169,7 +169,7 @@ enum UserSelectablePins {
|
|||
GPIO_SM16716_SEL, // SM16716 SELECT
|
||||
GPIO_DI, // my92x1 PWM input
|
||||
GPIO_DCKI, // my92x1 CLK input
|
||||
GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2)
|
||||
GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - Not used anymore 20200121
|
||||
GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2)
|
||||
GPIO_ARIRFRCV, // AriLux RF Receive input
|
||||
GPIO_TXD, // Serial interface
|
||||
|
@ -311,7 +311,7 @@ enum UserSelectableAdc0 {
|
|||
ADC0_LIGHT, // Light sensor
|
||||
ADC0_BUTTON, // Button
|
||||
ADC0_BUTTON_INV,
|
||||
ADC0_MOIST, // Moisture
|
||||
ADC0_RANGE, // Range
|
||||
ADC0_CT_POWER, // Current
|
||||
// ADC0_SWITCH, // Switch
|
||||
// ADC0_SWITCH_INV,
|
||||
|
@ -328,7 +328,7 @@ const char kAdc0Names[] PROGMEM =
|
|||
D_SENSOR_NONE "|" D_ANALOG_INPUT "|"
|
||||
D_TEMPERATURE "|" D_LIGHT "|"
|
||||
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|"
|
||||
D_MOISTURE "|"
|
||||
D_RANGE "|"
|
||||
D_CT_POWER "|"
|
||||
// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|"
|
||||
;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef _TASMOTA_VERSION_H_
|
||||
#define _TASMOTA_VERSION_H_
|
||||
|
||||
const uint32_t VERSION = 0x08010004;
|
||||
const uint32_t VERSION = 0x08010005;
|
||||
|
||||
// Lowest compatible version
|
||||
const uint32_t VERSION_COMPATIBLE = 0x07010006;
|
||||
|
|
|
@ -221,6 +221,15 @@ void MqttUnsubscribeLib(const char *topic)
|
|||
|
||||
bool MqttPublishLib(const char* topic, bool retained)
|
||||
{
|
||||
// If Prefix1 equals Prefix2 disable next MQTT subscription to prevent loop
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1));
|
||||
if (str == topic) {
|
||||
mqtt_cmnd_blocked_reset = 4; // Allow up to four seconds before resetting residual cmnd blocks
|
||||
mqtt_cmnd_blocked++;
|
||||
}
|
||||
}
|
||||
|
||||
bool result = MqttClient.publish(topic, mqtt_data, retained);
|
||||
yield(); // #3313
|
||||
return result;
|
||||
|
@ -238,12 +247,8 @@ void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len
|
|||
// Do not execute multiple times if Prefix1 equals Prefix2
|
||||
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;
|
||||
} else {
|
||||
mqtt_cmnd_publish = 0;
|
||||
}
|
||||
if ((str == mqtt_topic) && mqtt_cmnd_blocked) {
|
||||
mqtt_cmnd_blocked--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -291,52 +296,41 @@ void MqttUnsubscribe(const char *topic)
|
|||
|
||||
void MqttPublishLogging(const char *mxtime)
|
||||
{
|
||||
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
if (MqttIsConnected()) {
|
||||
char saved_mqtt_data[strlen(mqtt_data) +1];
|
||||
memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data));
|
||||
|
||||
char saved_mqtt_data[MESSZ];
|
||||
memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data));
|
||||
// ResponseTime_P(PSTR(",\"Log\":{\"%s\"}}"), log_data); // Will fail as some messages contain JSON
|
||||
Response_P(PSTR("%s%s"), mxtime, log_data); // No JSON and ugly!!
|
||||
// ResponseTime_P(PSTR(",\"Log\":{\"%s\"}}"), log_data); // Will fail as some messages contain JSON
|
||||
Response_P(PSTR("%s%s"), mxtime, log_data); // No JSON and ugly!!
|
||||
char stopic[TOPSZ];
|
||||
GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING"));
|
||||
MqttPublishLib(stopic, false);
|
||||
|
||||
char romram[33];
|
||||
char stopic[TOPSZ];
|
||||
snprintf_P(romram, sizeof(romram), PSTR("LOGGING"));
|
||||
GetTopic_P(stopic, STAT, mqtt_topic, romram);
|
||||
|
||||
char *me;
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
me = strstr(stopic, SettingsText(SET_MQTTPREFIX1));
|
||||
if (me == stopic) {
|
||||
mqtt_cmnd_publish += 3;
|
||||
}
|
||||
}
|
||||
MqttPublishLib(stopic, false);
|
||||
|
||||
memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data));
|
||||
}
|
||||
}
|
||||
memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data));
|
||||
}
|
||||
|
||||
void MqttPublishDirect(const char* topic, bool retained)
|
||||
void MqttPublish(const char* topic, bool retained)
|
||||
{
|
||||
char sretained[CMDSZ];
|
||||
char slog_type[20];
|
||||
|
||||
#ifdef USE_DEBUG_DRIVER
|
||||
ShowFreeMem(PSTR("MqttPublishDirect"));
|
||||
ShowFreeMem(PSTR("MqttPublish"));
|
||||
#endif
|
||||
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
// if (retained) {
|
||||
// AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained are not supported by AWS IoT, using retained = false."));
|
||||
// }
|
||||
retained = false; // AWS IoT does not support retained, it will disconnect if received
|
||||
#endif
|
||||
|
||||
char sretained[CMDSZ];
|
||||
sretained[0] = '\0';
|
||||
char slog_type[20];
|
||||
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT));
|
||||
|
||||
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
if (MqttIsConnected()) {
|
||||
if (MqttPublishLib(topic, retained)) {
|
||||
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT));
|
||||
if (retained) {
|
||||
snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")"));
|
||||
}
|
||||
if (MqttPublishLib(topic, retained)) {
|
||||
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT));
|
||||
if (retained) {
|
||||
snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,25 +348,6 @@ void MqttPublishDirect(const char* topic, bool retained)
|
|||
}
|
||||
}
|
||||
|
||||
void MqttPublish(const char* topic, bool retained)
|
||||
{
|
||||
char *me;
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
if (retained) {
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained are not supported by AWS IoT, using retained = false."));
|
||||
}
|
||||
retained = false; // AWS IoT does not support retained, it will disconnect if received
|
||||
#endif
|
||||
|
||||
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
|
||||
me = strstr(topic, SettingsText(SET_MQTTPREFIX1));
|
||||
if (me == topic) {
|
||||
mqtt_cmnd_publish += 3;
|
||||
}
|
||||
}
|
||||
MqttPublishDirect(topic, retained);
|
||||
}
|
||||
|
||||
void MqttPublish(const char* topic)
|
||||
{
|
||||
MqttPublish(topic, false);
|
||||
|
@ -387,7 +362,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
|
|||
* prefix 5 = stat using subtopic or RESULT
|
||||
* prefix 6 = tele using subtopic or RESULT
|
||||
*/
|
||||
char romram[33];
|
||||
char romram[64];
|
||||
char stopic[TOPSZ];
|
||||
|
||||
snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); // SetOption4 - Switch between MQTT RESULT or COMMAND
|
||||
|
@ -397,6 +372,36 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
|
|||
prefix &= 3;
|
||||
GetTopic_P(stopic, prefix, mqtt_topic, romram);
|
||||
MqttPublish(stopic, retained);
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
if ((prefix > 0) && (Settings.flag4.awsiot_shadow)) { // placeholder for SetOptionXX
|
||||
// compute the target topic
|
||||
char *topic = SettingsText(SET_MQTT_TOPIC);
|
||||
char topic2[strlen(topic)+1]; // save buffer onto stack
|
||||
strcpy(topic2, topic);
|
||||
// replace any '/' with '_'
|
||||
char *s = topic2;
|
||||
while (*s) {
|
||||
if ('/' == *s) {
|
||||
*s = '_';
|
||||
}
|
||||
s++;
|
||||
}
|
||||
// update topic is "$aws/things/<topic>/shadow/update"
|
||||
snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2);
|
||||
|
||||
// copy buffer
|
||||
char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1);
|
||||
if (!mqtt_save) { return; } // abort
|
||||
strcpy(mqtt_save, mqtt_data);
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save);
|
||||
free(mqtt_save);
|
||||
|
||||
bool result = MqttClient.publish(romram, mqtt_data, false);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram);
|
||||
yield(); // #3313
|
||||
}
|
||||
#endif // USE_MQTT_AWS_IOT
|
||||
}
|
||||
|
||||
void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic)
|
||||
|
@ -692,11 +697,6 @@ void MqttCheck(void)
|
|||
if (!MqttIsConnected()) {
|
||||
global_state.mqtt_down = 1;
|
||||
if (!Mqtt.retry_counter) {
|
||||
#ifdef USE_DISCOVERY
|
||||
#ifdef MQTT_HOST_DISCOVERY
|
||||
if (!strlen(SettingsText(SET_MQTT_HOST)) && !Wifi.mdns_begun) { return; }
|
||||
#endif // MQTT_HOST_DISCOVERY
|
||||
#endif // USE_DISCOVERY
|
||||
MqttReconnect();
|
||||
} else {
|
||||
Mqtt.retry_counter--;
|
||||
|
@ -706,7 +706,9 @@ void MqttCheck(void)
|
|||
}
|
||||
} else {
|
||||
global_state.mqtt_down = 0;
|
||||
if (Mqtt.initial_connection_state) MqttReconnect();
|
||||
if (Mqtt.initial_connection_state) {
|
||||
MqttReconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -850,17 +852,17 @@ void CmndPrefix(void)
|
|||
void CmndPublish(void)
|
||||
{
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
char *mqtt_part = strtok(XdrvMailbox.data, " ");
|
||||
char *payload_part;
|
||||
char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part);
|
||||
if (mqtt_part) {
|
||||
char stemp1[TOPSZ];
|
||||
strlcpy(stemp1, mqtt_part, sizeof(stemp1));
|
||||
mqtt_part = strtok(nullptr, " ");
|
||||
if (mqtt_part) {
|
||||
strlcpy(mqtt_data, mqtt_part, sizeof(mqtt_data));
|
||||
if ((payload_part != nullptr) && strlen(payload_part)) {
|
||||
strlcpy(mqtt_data, payload_part, sizeof(mqtt_data));
|
||||
} else {
|
||||
mqtt_data[0] = '\0';
|
||||
}
|
||||
MqttPublishDirect(stemp1, (XdrvMailbox.index == 2));
|
||||
MqttPublish(stemp1, (XdrvMailbox.index == 2));
|
||||
// ResponseCmndDone();
|
||||
mqtt_data[0] = '\0';
|
||||
}
|
||||
|
@ -986,8 +988,10 @@ void CmndSensorRetain(void)
|
|||
\*********************************************************************************************/
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
|
||||
const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
|
||||
const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
|
||||
// const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
|
||||
// const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
|
||||
const static uint16_t tls_spi_start_sector = 0xFF; // Force last bank of first MB
|
||||
const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
|
||||
const static size_t tls_spi_len = 0x1000; // 4kb blocs
|
||||
const static size_t tls_block_offset = 0x0400;
|
||||
const static size_t tls_block_len = 0x0400; // 1kb
|
||||
|
@ -1144,8 +1148,12 @@ void CmndTlsDump(void) {
|
|||
uint32_t start = (uint32_t)tls_spi_start + tls_block_offset;
|
||||
uint32_t end = start + tls_block_len -1;
|
||||
for (uint32_t pos = start; pos < end; pos += 0x10) {
|
||||
uint32_t* values = (uint32_t*)(pos);
|
||||
Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
|
||||
uint32_t* values = (uint32_t*)(pos);
|
||||
#ifdef ARDUINO_ESP8266_RELEASE_2_3_0
|
||||
Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
|
||||
#else
|
||||
Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_DUMP_TLS
|
||||
|
|
|
@ -522,26 +522,19 @@ void CmndEnergyReset(void)
|
|||
}
|
||||
}
|
||||
else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) {
|
||||
char *p;
|
||||
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
||||
int32_t position = -1;
|
||||
uint32_t values[2];
|
||||
|
||||
while ((str != nullptr) && (position < 1)) {
|
||||
uint32_t value = strtoul(str, nullptr, 10);
|
||||
position++;
|
||||
values[position] = value *100;
|
||||
str = strtok_r(nullptr, ", ", &p);
|
||||
}
|
||||
uint32_t values[2] = { 0 };
|
||||
uint32_t position = ParseParameters(2, values);
|
||||
values[0] *= 100;
|
||||
values[1] *= 100;
|
||||
|
||||
switch (XdrvMailbox.index)
|
||||
{
|
||||
case 4:
|
||||
// Reset energy_usage.usage totals
|
||||
if (position > -1) {
|
||||
if (position > 0) {
|
||||
RtcSettings.energy_usage.usage1_kWhtotal = values[0];
|
||||
}
|
||||
if (position > 0) {
|
||||
if (position > 1) {
|
||||
RtcSettings.energy_usage.usage2_kWhtotal = values[1];
|
||||
}
|
||||
Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal;
|
||||
|
@ -549,10 +542,10 @@ void CmndEnergyReset(void)
|
|||
break;
|
||||
case 5:
|
||||
// Reset energy_usage.return totals
|
||||
if (position > -1) {
|
||||
if (position > 0) {
|
||||
RtcSettings.energy_usage.return1_kWhtotal = values[0];
|
||||
}
|
||||
if (position > 0) {
|
||||
if (position > 1) {
|
||||
RtcSettings.energy_usage.return2_kWhtotal = values[1];
|
||||
}
|
||||
Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal;
|
||||
|
|
|
@ -1723,8 +1723,16 @@ void LightAnimate(void)
|
|||
Light.new_color[i] = Light.current_color[i];
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"}"));
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_CMND_WAKEUP));
|
||||
*/
|
||||
Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\""));
|
||||
LightState(1);
|
||||
ResponseJsonEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP));
|
||||
XdrvRulesProcess();
|
||||
|
||||
Light.wakeup_active = 0;
|
||||
Settings.light_scheme = LS_POWER;
|
||||
}
|
||||
|
@ -1799,6 +1807,14 @@ void LightAnimate(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Apply RGBWWTable only if Settings.rgbwwTable[4] != 0
|
||||
if (0 != Settings.rgbwwTable[4]) {
|
||||
for (uint32_t i = 0; i<Light.subtype; i++) {
|
||||
uint32_t adjust = change8to10(Settings.rgbwwTable[i]);
|
||||
cur_col_10[i] = changeUIntScale(cur_col_10[i], 0, 1023, 0, adjust);
|
||||
}
|
||||
}
|
||||
|
||||
// final adjusments for PMW, post-gamma correction
|
||||
for (uint32_t i = 0; i < LST_MAX; i++) {
|
||||
// scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1
|
||||
|
@ -2185,6 +2201,13 @@ void CmndSupportColor(void)
|
|||
|
||||
void CmndColor(void)
|
||||
{
|
||||
// Color - Show current RGBWW color state
|
||||
// Color1 - Change color to RGBWW
|
||||
// Color2 - Change color to RGBWW but retain brightness (=dimmer)
|
||||
// Color3 - Change color to RGB of WS2812 Clock Second
|
||||
// Color4 - Change color to RGB of WS2812 Clock Minute
|
||||
// Color5 - Change color to RGB of WS2812 Clock Hour
|
||||
// Color6 - Change color to RGB of WS2812 Clock Marker
|
||||
if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) {
|
||||
CmndSupportColor();
|
||||
}
|
||||
|
@ -2192,6 +2215,8 @@ void CmndColor(void)
|
|||
|
||||
void CmndWhite(void)
|
||||
{
|
||||
// White - Show current White (=Dimmer2) state
|
||||
// White 0..100 - Set White colors dimmer state
|
||||
if (Light.pwm_multi_channels) { return; }
|
||||
if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
||||
|
@ -2205,6 +2230,10 @@ void CmndWhite(void)
|
|||
|
||||
void CmndChannel(void)
|
||||
{
|
||||
// Channel<x> - Show current Channel state
|
||||
// Channel<x> 0..100 - Set Channel dimmer state
|
||||
// Channel<x> + - Incerement Channel in steps of 10
|
||||
// Channel<x> - - Decrement Channel in steps of 10
|
||||
if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) {
|
||||
uint32_t light_index = XdrvMailbox.index - Light.device;
|
||||
power_t coldim = 0; // bit flag to update
|
||||
|
@ -2249,48 +2278,35 @@ void CmndChannel(void)
|
|||
|
||||
void CmndHsbColor(void)
|
||||
{
|
||||
// HsbColor - Show current HSB
|
||||
// HsbColor 360,100,100 - Set Hue, Saturation and Brighthness
|
||||
// HsbColor 360,100 - Set Hue and Saturation
|
||||
// HsbColor 360 - Set Hue
|
||||
// HsbColor1 360 - Set Hue
|
||||
// HsbColor2 100 - Set Saturation
|
||||
// HsbColor3 100 - Set Brightness
|
||||
if (Light.subtype >= LST_RGB) {
|
||||
bool validHSB = (XdrvMailbox.data_len > 0);
|
||||
if (validHSB) {
|
||||
uint16_t HSB[3];
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
uint16_t c_hue;
|
||||
uint8_t c_sat;
|
||||
|
||||
light_state.getHSB(&c_hue, &c_sat, nullptr);
|
||||
uint32_t HSB[3];
|
||||
HSB[0] = c_hue;
|
||||
HSB[1] = c_sat;
|
||||
HSB[2] = light_state.getBriRGB();
|
||||
|
||||
char *substr = strstr(XdrvMailbox.data, ",");
|
||||
if (substr != nullptr) { // Command with comma separated parameters, Hue (0<H<360), Saturation (0<S<100) AND optional Brightness (0<B<100)
|
||||
HSB[0] = atoi(XdrvMailbox.data);
|
||||
|
||||
for (uint32_t i = 1; i < 3; i++) {
|
||||
substr++;
|
||||
HSB[i] = atoi(substr);
|
||||
if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) {
|
||||
if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; }
|
||||
HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255);
|
||||
} else {
|
||||
uint32_t paramcount = ParseParameters(3, HSB);
|
||||
if (HSB[0] > 360) { HSB[0] = 360; }
|
||||
for (uint32_t i = 1; i < paramcount; i++) {
|
||||
if (HSB[i] > 100) { HSB[i] == 100; }
|
||||
HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); // change sat and bri to 0..255
|
||||
substr = strstr(substr, ",");
|
||||
if (substr == nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (substr != nullptr) {
|
||||
validHSB = false;
|
||||
}
|
||||
} else { // Command with only 1 parameter, Hue (0<H<360), Saturation (0<S<100) OR Brightness (0<B<100)
|
||||
|
||||
if (1 == XdrvMailbox.index) {
|
||||
HSB[0] = XdrvMailbox.payload;
|
||||
} else if ((XdrvMailbox.index > 1) && (XdrvMailbox.index < 4)) {
|
||||
HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
|
||||
} else {
|
||||
validHSB = false;
|
||||
}
|
||||
}
|
||||
if (validHSB) {
|
||||
light_controller.changeHSB(HSB[0], HSB[1], HSB[2]);
|
||||
LightPreparePower(1);
|
||||
}
|
||||
light_controller.changeHSB(HSB[0], HSB[1], HSB[2]);
|
||||
LightPreparePower(1);
|
||||
} else {
|
||||
LightState(0);
|
||||
}
|
||||
|
@ -2299,6 +2315,11 @@ void CmndHsbColor(void)
|
|||
|
||||
void CmndScheme(void)
|
||||
{
|
||||
// Scheme 0..12 - Select one of schemes 0 to 12
|
||||
// Scheme 2 - Select scheme 2
|
||||
// Scheme 2,0 - Select scheme 2 with color wheel set to 0 (HSB Red)
|
||||
// Scheme + - Select next scheme
|
||||
// Scheme - - Select previous scheme
|
||||
if (Light.subtype >= LST_RGB) {
|
||||
uint32_t max_scheme = Light.max_scheme;
|
||||
|
||||
|
@ -2311,6 +2332,10 @@ void CmndScheme(void)
|
|||
}
|
||||
}
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) {
|
||||
uint32_t parm[2];
|
||||
if (ParseParameters(2, parm) > 1) {
|
||||
Light.wheel = parm[1];
|
||||
}
|
||||
Settings.light_scheme = XdrvMailbox.payload;
|
||||
if (LS_WAKEUP == Settings.light_scheme) {
|
||||
Light.wakeup_active = 3;
|
||||
|
@ -2328,6 +2353,8 @@ void CmndScheme(void)
|
|||
|
||||
void CmndWakeup(void)
|
||||
{
|
||||
// Wakeup - Start wakeup light
|
||||
// Wakeup 0..100 - Start wakeup light to dimmer value 0..100
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
||||
light_controller.changeDimmer(XdrvMailbox.payload);
|
||||
}
|
||||
|
@ -2339,6 +2366,10 @@ void CmndWakeup(void)
|
|||
|
||||
void CmndColorTemperature(void)
|
||||
{
|
||||
// CT - Show current color temperature
|
||||
// CT 153..500 - Set color temperature
|
||||
// CT + - Incerement color temperature in steps of 34
|
||||
// CT - - Decrement color temperature in steps of 34
|
||||
if (Light.pwm_multi_channels) { return; }
|
||||
if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { // ColorTemp
|
||||
uint32_t ct = light_state.getCT();
|
||||
|
@ -2361,6 +2392,12 @@ void CmndColorTemperature(void)
|
|||
|
||||
void CmndDimmer(void)
|
||||
{
|
||||
// Dimmer - Show current Dimmer state
|
||||
// Dimmer0 0..100 - Change both RGB and W(W) Dimmers
|
||||
// Dimmer1 0..100 - Change RGB Dimmer
|
||||
// Dimmer2 0..100 - Change W(W) Dimmer
|
||||
// Dimmer<x> + - Incerement Dimmer in steps of 10
|
||||
// Dimmer<x> - - Decrement Dimmer in steps of 10
|
||||
uint32_t dimmer;
|
||||
if (XdrvMailbox.index > 2) { XdrvMailbox.index = 1; }
|
||||
|
||||
|
@ -2402,17 +2439,13 @@ void CmndDimmer(void)
|
|||
|
||||
void CmndDimmerRange(void)
|
||||
{
|
||||
// DimmerRange - Show current dimmer range as used by Tuya and PS16DZ Dimmers
|
||||
// DimmerRange 0,100 - Set dimmer hardware range from 0 to 100 and restart
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
char *p;
|
||||
uint8_t i = 0;
|
||||
uint16_t parm[2];
|
||||
uint32_t parm[2];
|
||||
parm[0] = Settings.dimmer_hw_min;
|
||||
parm[1] = Settings.dimmer_hw_max;
|
||||
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) {
|
||||
parm[i] = strtoul(str, nullptr, 0);
|
||||
i++;
|
||||
}
|
||||
|
||||
ParseParameters(2, parm);
|
||||
if (parm[0] < parm[1]) {
|
||||
Settings.dimmer_hw_min = parm[0];
|
||||
Settings.dimmer_hw_max = parm[1];
|
||||
|
@ -2427,6 +2460,10 @@ void CmndDimmerRange(void)
|
|||
|
||||
void CmndLedTable(void)
|
||||
{
|
||||
// LedTable - Show current LedTable state
|
||||
// LedTable 0 - Turn LedTable Off
|
||||
// LedTable On - Turn LedTable On
|
||||
// LedTable Toggle - Toggle LedTable state
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 0: // Off
|
||||
|
@ -2444,20 +2481,13 @@ void CmndLedTable(void)
|
|||
|
||||
void CmndRgbwwTable(void)
|
||||
{
|
||||
// RgbWwTable - Show current RGBWW State
|
||||
// RgbWwTable 255,255,255,255,255 - Set RGBWW state to maximum
|
||||
if ((XdrvMailbox.data_len > 0)) {
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Command with up to 5 comma separated parameters
|
||||
for (uint32_t i = 0; i < LST_RGBCW; i++) {
|
||||
char *substr;
|
||||
|
||||
if (0 == i) {
|
||||
substr = strtok(XdrvMailbox.data, ",");
|
||||
} else {
|
||||
substr = strtok(nullptr, ",");
|
||||
}
|
||||
if (substr != nullptr) {
|
||||
Settings.rgbwwTable[i] = atoi(substr);
|
||||
}
|
||||
}
|
||||
uint32_t parm[LST_RGBCW -1];
|
||||
uint32_t parmcount = ParseParameters(LST_RGBCW, parm);
|
||||
for (uint32_t i = 0; i < parmcount; i++) {
|
||||
Settings.rgbwwTable[i] = parm[i];
|
||||
}
|
||||
Light.update = true;
|
||||
}
|
||||
|
@ -2466,11 +2496,15 @@ void CmndRgbwwTable(void)
|
|||
for (uint32_t i = 0; i < LST_RGBCW; i++) {
|
||||
snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]);
|
||||
}
|
||||
ResponseCmndIdxChar(scolor);
|
||||
ResponseCmndChar(scolor);
|
||||
}
|
||||
|
||||
void CmndFade(void)
|
||||
{
|
||||
// Fade - Show current Fade state
|
||||
// Fade 0 - Turn Fade Off
|
||||
// Fade On - Turn Fade On
|
||||
// Fade Toggle - Toggle Fade state
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 0: // Off
|
||||
case 1: // On
|
||||
|
@ -2485,7 +2519,11 @@ void CmndFade(void)
|
|||
}
|
||||
|
||||
void CmndSpeed(void)
|
||||
{ // 1 - fast, 40 - very slow
|
||||
{
|
||||
// Speed 1 - Fast
|
||||
// Speed 40 - Very slow
|
||||
// Speed + - Increment Speed
|
||||
// Speed - - Decrement Speed
|
||||
if (1 == XdrvMailbox.data_len) {
|
||||
if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) {
|
||||
XdrvMailbox.payload = Settings.light_speed - 1;
|
||||
|
@ -2502,6 +2540,8 @@ void CmndSpeed(void)
|
|||
|
||||
void CmndWakeupDuration(void)
|
||||
{
|
||||
// WakeUpDuration - Show current Wake Up duration in seconds
|
||||
// WakeUpDuration 60 - Set Wake Up duration to 60 seconds
|
||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) {
|
||||
Settings.light_wakeup = XdrvMailbox.payload;
|
||||
Light.wakeup_active = 0;
|
||||
|
@ -2510,7 +2550,8 @@ void CmndWakeupDuration(void)
|
|||
}
|
||||
|
||||
void CmndUndocA(void)
|
||||
{ // Theos legacy status
|
||||
{
|
||||
// Theos legacy status
|
||||
char scolor[LIGHT_COLOR_SIZE];
|
||||
LightGetColor(scolor, true); // force hex whatever Option 17
|
||||
scolor[6] = '\0'; // RGB only
|
||||
|
|
|
@ -255,27 +255,42 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
|||
}
|
||||
|
||||
// Step2: Search rule_task and rule_name
|
||||
StaticJsonBuffer<1024> jsonBuf;
|
||||
JsonObject &root = jsonBuf.parseObject(event);
|
||||
if (!root.success()) { return false; } // No valid JSON data
|
||||
|
||||
const char* str_value;
|
||||
if ((pos = rule_name.indexOf("[")) > 0) { // "CURRENT[1]"
|
||||
int rule_name_idx = rule_name.substring(pos +1).toInt();
|
||||
int rule_name_idx = 0;
|
||||
if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]"
|
||||
rule_name_idx = rule_name.substring(pos +1).toInt();
|
||||
if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6
|
||||
rule_name_idx = 1;
|
||||
}
|
||||
rule_name = rule_name.substring(0, pos); // "CURRENT"
|
||||
str_value = root[rule_task][rule_name][rule_name_idx -1]; // "ENERGY" and "CURRENT" and 0
|
||||
} else {
|
||||
str_value = root[rule_task][rule_name]; // "INA219" and "CURRENT"
|
||||
rule_name = rule_name.substring(0, pos); // "SUBTYPE1#CURRENT"
|
||||
}
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
|
||||
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
|
||||
StaticJsonBuffer<1024> jsonBuf;
|
||||
JsonObject &root = jsonBuf.parseObject(event);
|
||||
if (!root.success()) { return false; } // No valid JSON data
|
||||
if (!root[rule_task].success()) { return false; } // No rule_task in JSON data
|
||||
|
||||
if (!root[rule_task][rule_name].success()) { return false; }
|
||||
// No value but rule_name is ok
|
||||
JsonObject &obj1 = root[rule_task];
|
||||
JsonObject *obj = &obj1;
|
||||
String subtype;
|
||||
uint32_t i = 0;
|
||||
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
|
||||
subtype = rule_name.substring(0, pos);
|
||||
if (!(*obj)[subtype].success()) { return false; } // No subtype in JSON data
|
||||
JsonObject &obj2 = (*obj)[subtype];
|
||||
obj = &obj2;
|
||||
rule_name = rule_name.substring(pos +1);
|
||||
if (i++ > 10) { return false; } // Abandon possible loop
|
||||
}
|
||||
if (!(*obj)[rule_name].success()) { return false; } // No name in JSON data
|
||||
const char* str_value;
|
||||
if (rule_name_idx) {
|
||||
str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]"
|
||||
} else {
|
||||
str_value = (*obj)[rule_name]; // "CURRENT"
|
||||
}
|
||||
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
|
||||
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
|
||||
|
||||
Rules.event_value = str_value; // Prepare %value%
|
||||
|
||||
|
|
|
@ -4775,6 +4775,7 @@ bool Xdrv10(uint8_t function)
|
|||
glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE;
|
||||
|
||||
glob_script_mem.flags=1;
|
||||
I2cSetActiveFound(EEPROM_ADDRESS, "EEPROM");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#ifndef ZIGBEE_SAVE_DELAY_SECONDS
|
||||
#define ZIGBEE_SAVE_DELAY_SECONDS 10; // wait for 10s before saving Zigbee info
|
||||
#endif
|
||||
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
||||
|
||||
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value);
|
||||
|
||||
|
@ -57,6 +61,16 @@ class Z_Devices {
|
|||
public:
|
||||
Z_Devices() {};
|
||||
|
||||
// Probe the existence of device keys
|
||||
// Results:
|
||||
// - 0x0000 = not found
|
||||
// - 0xFFFF = bad parameter
|
||||
// - 0x<shortaddr> = the device's short address
|
||||
uint16_t isKnownShortAddr(uint16_t shortaddr) const;
|
||||
uint16_t isKnownLongAddr(uint64_t longaddr) const;
|
||||
uint16_t isKnownIndex(uint32_t index) const;
|
||||
uint16_t isKnownFriendlyName(const char * name) const;
|
||||
|
||||
// Add new device, provide ShortAddr and optional longAddr
|
||||
// If it is already registered, update information, otherwise create the entry
|
||||
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
|
||||
|
@ -74,13 +88,14 @@ public:
|
|||
|
||||
void setManufId(uint16_t shortaddr, const char * str);
|
||||
void setModelId(uint16_t shortaddr, const char * str);
|
||||
void setFriendlyNameId(uint16_t shortaddr, const char * str);
|
||||
void setFriendlyName(uint16_t shortaddr, const char * str);
|
||||
const String * getFriendlyName(uint16_t) const;
|
||||
|
||||
// device just seen on the network, update the lastSeen field
|
||||
void updateLastSeen(uint16_t shortaddr);
|
||||
|
||||
// Dump json
|
||||
String dump(uint32_t dump_mode, int32_t device_num = 0) const;
|
||||
String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const;
|
||||
|
||||
// Timers
|
||||
void resetTimer(uint32_t shortaddr);
|
||||
|
@ -89,13 +104,33 @@ public:
|
|||
|
||||
// Append or clear attributes Json structure
|
||||
void jsonClear(uint16_t shortaddr);
|
||||
void jsonAppend(uint16_t shortaddr, JsonObject &values);
|
||||
void jsonAppend(uint16_t shortaddr, const JsonObject &values);
|
||||
const JsonObject *jsonGet(uint16_t shortaddr);
|
||||
const void jsonPublish(uint16_t shortaddr); // publish the json message and clear buffer
|
||||
void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer
|
||||
bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
|
||||
void jsonPublishNow(uint16_t shortaddr, JsonObject &values);
|
||||
|
||||
// Iterator
|
||||
size_t devicesSize(void) const {
|
||||
return _devices.size();
|
||||
}
|
||||
const Z_Device &devicesAt(size_t i) const {
|
||||
return _devices.at(i);
|
||||
}
|
||||
|
||||
// Remove device from list
|
||||
bool removeDevice(uint16_t shortaddr);
|
||||
|
||||
// Mark data as 'dirty' and requiring to save in Flash
|
||||
void dirty(void);
|
||||
void clean(void); // avoid writing to flash the last changes
|
||||
|
||||
// Find device by name, can be short_addr, long_addr, number_in_array or name
|
||||
uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const;
|
||||
|
||||
private:
|
||||
std::vector<Z_Device> _devices = {};
|
||||
uint32_t _saveTimer = 0;
|
||||
|
||||
template < typename T>
|
||||
static bool findInVector(const std::vector<T> & vecOfElements, const T & element);
|
||||
|
@ -109,8 +144,9 @@ private:
|
|||
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
|
||||
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
|
||||
|
||||
int32_t findShortAddr(uint16_t shortaddr);
|
||||
int32_t findLongAddr(uint64_t longaddr);
|
||||
int32_t findShortAddr(uint16_t shortaddr) const;
|
||||
int32_t findLongAddr(uint64_t longaddr) const;
|
||||
int32_t findFriendlyName(const char * name) const;
|
||||
|
||||
void _updateLastSeen(Z_Device &device) {
|
||||
if (&device != nullptr) {
|
||||
|
@ -187,6 +223,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
|||
nullptr, nullptr };
|
||||
device.json_buffer = new DynamicJsonBuffer();
|
||||
_devices.push_back(device);
|
||||
dirty();
|
||||
return _devices.back();
|
||||
}
|
||||
|
||||
|
@ -198,7 +235,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
|||
// Out:
|
||||
// index in _devices of entry, -1 if not found
|
||||
//
|
||||
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
|
||||
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
||||
if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost)
|
||||
int32_t found = 0;
|
||||
if (shortaddr) {
|
||||
|
@ -217,7 +254,7 @@ int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
|
|||
// Out:
|
||||
// index in _devices of entry, -1 if not found
|
||||
//
|
||||
int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
|
||||
int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
|
||||
if (!longaddr) { return -1; }
|
||||
int32_t found = 0;
|
||||
if (longaddr) {
|
||||
|
@ -228,6 +265,66 @@ int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
//
|
||||
// Scan all devices to find a corresponding friendlyNme
|
||||
// Looks info device.friendlyName entry
|
||||
// In:
|
||||
// friendlyName (null terminated, should not be empty)
|
||||
// Out:
|
||||
// index in _devices of entry, -1 if not found
|
||||
//
|
||||
int32_t Z_Devices::findFriendlyName(const char * name) const {
|
||||
if (!name) { return -1; } // if pointer is null
|
||||
size_t name_len = strlen(name);
|
||||
int32_t found = 0;
|
||||
if (name_len) {
|
||||
for (auto &elem : _devices) {
|
||||
if (elem.friendlyName == name) { return found; }
|
||||
found++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Probe if device is already known but don't create any entry
|
||||
uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const {
|
||||
int32_t found = findShortAddr(shortaddr);
|
||||
if (found >= 0) {
|
||||
return shortaddr;
|
||||
} else {
|
||||
return 0; // unknown
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
|
||||
int32_t found = findLongAddr(longaddr);
|
||||
if (found >= 0) {
|
||||
const Z_Device & device = devicesAt(found);
|
||||
return device.shortaddr; // can be zero, if not yet registered
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
|
||||
if (index < devicesSize()) {
|
||||
const Z_Device & device = devicesAt(index);
|
||||
return device.shortaddr;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
||||
if ((!name) || (0 == strlen(name))) { return 0xFFFF; } // Error
|
||||
int32_t found = findFriendlyName(name);
|
||||
if (found >= 0) {
|
||||
const Z_Device & device = devicesAt(found);
|
||||
return device.shortaddr; // can be zero, if not yet registered
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// We have a seen a shortaddr on the network, get the corresponding
|
||||
|
@ -252,6 +349,17 @@ Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
|
|||
return createDeviceEntry(0, longaddr);
|
||||
}
|
||||
|
||||
// Remove device from list, return true if it was known, false if it was not recorded
|
||||
bool Z_Devices::removeDevice(uint16_t shortaddr) {
|
||||
int32_t found = findShortAddr(shortaddr);
|
||||
if (found >= 0) {
|
||||
_devices.erase(_devices.begin() + found);
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// We have just seen a device on the network, update the info based on short/long addr
|
||||
// In:
|
||||
|
@ -270,15 +378,18 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
|||
// erase the previous shortaddr
|
||||
_devices.erase(_devices.begin() + s_found);
|
||||
updateLastSeen(shortaddr);
|
||||
dirty();
|
||||
}
|
||||
} else if (s_found >= 0) {
|
||||
// shortaddr already exists but longaddr not
|
||||
// add the longaddr to the entry
|
||||
_devices[s_found].longaddr = longaddr;
|
||||
updateLastSeen(shortaddr);
|
||||
dirty();
|
||||
} else if (l_found >= 0) {
|
||||
// longaddr entry exists, update shortaddr
|
||||
_devices[l_found].shortaddr = shortaddr;
|
||||
dirty();
|
||||
} else {
|
||||
// neither short/lonf addr are found.
|
||||
if (shortaddr || longaddr) {
|
||||
|
@ -298,6 +409,7 @@ void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) {
|
|||
_updateLastSeen(device);
|
||||
if (findEndpointInVector(device.endpoints, ep_profile) < 0) {
|
||||
device.endpoints.push_back(ep_profile);
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,8 +422,12 @@ void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t
|
|||
int32_t found = findEndpointInVector(device.endpoints, ep_profile);
|
||||
if (found < 0) {
|
||||
device.endpoints.push_back(ep_profile);
|
||||
dirty();
|
||||
} else {
|
||||
device.endpoints[found] = ep_profile;
|
||||
if (device.endpoints[found] != ep_profile) {
|
||||
device.endpoints[found] = ep_profile;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,10 +440,12 @@ void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluste
|
|||
if (!out) {
|
||||
if (!findInVector(device.clusters_in, ep_cluster)) {
|
||||
device.clusters_in.push_back(ep_cluster);
|
||||
dirty();
|
||||
}
|
||||
} else { // out
|
||||
if (!findInVector(device.clusters_out, ep_cluster)) {
|
||||
device.clusters_out.push_back(ep_cluster);
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,21 +470,41 @@ void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
|
|||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
_updateLastSeen(device);
|
||||
if (!device.manufacturerId.equals(str)) {
|
||||
dirty();
|
||||
}
|
||||
device.manufacturerId = str;
|
||||
}
|
||||
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
_updateLastSeen(device);
|
||||
if (!device.modelId.equals(str)) {
|
||||
dirty();
|
||||
}
|
||||
device.modelId = str;
|
||||
}
|
||||
void Z_Devices::setFriendlyNameId(uint16_t shortaddr, const char * str) {
|
||||
void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
_updateLastSeen(device);
|
||||
if (!device.friendlyName.equals(str)) {
|
||||
dirty();
|
||||
}
|
||||
device.friendlyName = str;
|
||||
}
|
||||
|
||||
const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const {
|
||||
int32_t found = findShortAddr(shortaddr);
|
||||
if (found >= 0) {
|
||||
const Z_Device & device = devicesAt(found);
|
||||
if (device.friendlyName.length() > 0) {
|
||||
return &device.friendlyName;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// device just seen on the network, update the lastSeen field
|
||||
void Z_Devices::updateLastSeen(uint16_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
|
@ -398,19 +536,22 @@ void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster,
|
|||
|
||||
// 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)) {
|
||||
if ((timer) && TimeReached(timer)) {
|
||||
device.timer = 0; // cancel the timer before calling, so the callback can set another timer
|
||||
// trigger the timer
|
||||
(*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value);
|
||||
}
|
||||
}
|
||||
// save timer
|
||||
if ((_saveTimer) && TimeReached(_saveTimer)) {
|
||||
saveZigbeeDevices();
|
||||
_saveTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Z_Devices::jsonClear(uint16_t shortaddr) {
|
||||
|
@ -482,7 +623,7 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
|
||||
void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
if (&values == nullptr) { return; }
|
||||
|
@ -490,6 +631,16 @@ void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
|
|||
if (nullptr == device.json) {
|
||||
device.json = &(device.json_buffer->createObject());
|
||||
}
|
||||
// Prepend Device, will be removed later if redundant
|
||||
char sa[8];
|
||||
snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
|
||||
device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
|
||||
// Prepend Friendly Name if it has one
|
||||
const String * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||
if (fname) {
|
||||
device.json->set(F(D_JSON_ZIGBEE_NAME), (char*)fname->c_str()); // (char*) forces ArduinoJson to make a copy of the cstring
|
||||
}
|
||||
|
||||
// copy all values from 'values' to 'json'
|
||||
CopyJsonObject(*device.json, values);
|
||||
}
|
||||
|
@ -500,40 +651,125 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
|
|||
return device.json;
|
||||
}
|
||||
|
||||
const void Z_Devices::jsonPublish(uint16_t shortaddr) {
|
||||
const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
|
||||
if (json == nullptr) { return; } // don't crash if not found
|
||||
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (&device == nullptr) { return; } // don't crash if not found
|
||||
JsonObject * json = device.json;
|
||||
if (json == nullptr) { return; } // abort if nothing in buffer
|
||||
|
||||
const String * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||
|
||||
// if (use_fname) {
|
||||
// // we need to add the Device short_addr inside the JSON
|
||||
// char sa[8];
|
||||
// snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
|
||||
// json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
|
||||
// } else if (fname) {
|
||||
// json->set(F(D_JSON_NAME), (char*) fname);
|
||||
// }
|
||||
|
||||
// Remove redundant "Name" or "Device"
|
||||
if (use_fname) {
|
||||
json->remove(F(D_JSON_ZIGBEE_NAME));
|
||||
} else {
|
||||
json->remove(F(D_JSON_ZIGBEE_DEVICE));
|
||||
}
|
||||
|
||||
String msg = "";
|
||||
json->printTo(msg);
|
||||
zigbee_devices.jsonClear(shortaddr);
|
||||
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
|
||||
if (use_fname) {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
// DEPRECATED TODO
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
} else {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
// DEPRECATED TODO
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
// MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
// XdrvRulesProcess();
|
||||
}
|
||||
|
||||
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
||||
jsonPublishFlush(shortaddr); // flush any previous buffer
|
||||
jsonAppend(shortaddr, values);
|
||||
jsonPublishFlush(shortaddr); // publish now
|
||||
}
|
||||
|
||||
void Z_Devices::dirty(void) {
|
||||
_saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis();
|
||||
}
|
||||
void Z_Devices::clean(void) {
|
||||
_saveTimer = 0;
|
||||
}
|
||||
|
||||
// Parse the command parameters for either:
|
||||
// - a short address starting with "0x", example: 0x1234
|
||||
// - a long address starting with "0x", example: 0x7CB03EBB0A0292DD
|
||||
// - a number 0..99, the index number in ZigbeeStatus
|
||||
// - a friendly name, between quotes, example: "Room_Temp"
|
||||
uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const {
|
||||
if (nullptr == param) { return 0; }
|
||||
size_t param_len = strlen(param);
|
||||
char dataBuf[param_len + 1];
|
||||
strcpy(dataBuf, param);
|
||||
RemoveSpace(dataBuf);
|
||||
uint16_t shortaddr = 0;
|
||||
|
||||
if (strlen(dataBuf) < 4) {
|
||||
// simple number 0..99
|
||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
|
||||
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
|
||||
}
|
||||
} else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) {
|
||||
// starts with 0x
|
||||
if (strlen(dataBuf) < 18) {
|
||||
// expect a short address
|
||||
shortaddr = strtoull(dataBuf, nullptr, 0);
|
||||
if (short_must_be_known) {
|
||||
shortaddr = zigbee_devices.isKnownShortAddr(shortaddr);
|
||||
}
|
||||
// else we don't check if it's already registered to force unregistered devices
|
||||
} else {
|
||||
// expect a long address
|
||||
uint64_t longaddr = strtoull(dataBuf, nullptr, 0);
|
||||
shortaddr = zigbee_devices.isKnownLongAddr(longaddr);
|
||||
}
|
||||
} else {
|
||||
// expect a Friendly Name
|
||||
shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf);
|
||||
}
|
||||
|
||||
return shortaddr;
|
||||
}
|
||||
|
||||
// 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
|
||||
String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
|
||||
// Mode = 1: simple dump of devices addresses
|
||||
// Mode = 2: simple dump of devices addresses and names
|
||||
// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters
|
||||
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonArray& json = jsonBuffer.createArray();
|
||||
JsonArray& devices = json;
|
||||
//JsonArray& devices = json.createNestedArray(F("ZigbeeDevices"));
|
||||
|
||||
// if device_num == 0, then we show all devices.
|
||||
// When no payload, the default is -99. In this case change it to 0.
|
||||
if (device_num < 0) { device_num = 0; }
|
||||
|
||||
uint32_t device_current = 1;
|
||||
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it, ++device_current) {
|
||||
// ignore non-current device, if specified device is non-zero
|
||||
if ((device_num > 0) && (device_num != device_current)) { continue; }
|
||||
|
||||
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
||||
const Z_Device& device = *it;
|
||||
uint16_t shortaddr = device.shortaddr;
|
||||
char hex[20];
|
||||
char hex[22];
|
||||
|
||||
// ignore non-current device, if specified device is non-zero
|
||||
if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
|
||||
|
||||
JsonObject& dev = devices.createNestedObject();
|
||||
|
||||
|
@ -545,7 +781,9 @@ String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
|
|||
}
|
||||
|
||||
if (2 <= dump_mode) {
|
||||
Uint64toHex(device.longaddr, hex, 64);
|
||||
hex[0] = '0'; // prefix with '0x'
|
||||
hex[1] = 'x';
|
||||
Uint64toHex(device.longaddr, &hex[2], 64);
|
||||
dev[F("IEEEAddr")] = hex;
|
||||
if (device.modelId.length() > 0) {
|
||||
dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId;
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
xdrv_23_zigbee.ino - zigbee support for Tasmota
|
||||
|
||||
Copyright (C) 2020 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
|
||||
|
||||
// Ensure persistence of devices into Flash
|
||||
//
|
||||
// Structure:
|
||||
// (from file info):
|
||||
// uint16 - start address in Flash (offset)
|
||||
// uint16 - length in bytes (makes sure parsing stops)
|
||||
//
|
||||
// File structure:
|
||||
// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased)
|
||||
//
|
||||
// [Array of devices]
|
||||
// [Offset = 2]
|
||||
// uint8 - length of revice record
|
||||
// uint16 - short address
|
||||
// uint64 - long IEEE address
|
||||
// uint8 - number of endpoints
|
||||
// [Array of endpoints]
|
||||
// uint8 - endpoint number
|
||||
// uint16 - profileID of the endpoint
|
||||
// Array of uint8 - clusters In codes, 0xFF end marker
|
||||
// Array of uint8 - clusters Out codes, 0xFF end marker
|
||||
//
|
||||
// str - ModelID (null terminated C string, 32 chars max)
|
||||
// str - Manuf (null terminated C string, 32 chars max)
|
||||
// reserved for extensions
|
||||
|
||||
// Memory footprint
|
||||
const static uint16_t z_spi_start_sector = 0xFF; // Force last bank of first MB
|
||||
const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
|
||||
const static uint8_t* z_dev_start = z_spi_start + 0x0800; // 0x402FF800 - 2KB
|
||||
const static size_t z_spi_len = 0x1000; // 4kb blocs
|
||||
const static size_t z_block_offset = 0x0800;
|
||||
const static size_t z_block_len = 0x0800; // 2kb
|
||||
|
||||
class z_flashdata_t {
|
||||
public:
|
||||
uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2'
|
||||
uint16_t len; // len of object
|
||||
uint16_t reserved; // align on 4 bytes boundary
|
||||
};
|
||||
|
||||
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
|
||||
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
|
||||
|
||||
// encoding for the most commonly 32 clusters, used for binary encoding
|
||||
const uint16_t Z_ClusterNumber[] PROGMEM = {
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0100, 0x0101, 0x0102,
|
||||
0x0201, 0x0202, 0x0203, 0x0204,
|
||||
0x0300, 0x0301,
|
||||
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
|
||||
0x0500, 0x0501, 0x0502,
|
||||
0x0700, 0x0701, 0x0702,
|
||||
0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05,
|
||||
0x1000,
|
||||
0xFC0F,
|
||||
};
|
||||
|
||||
// convert a 1 byte cluster code to the actual cluster number
|
||||
uint16_t fromClusterCode(uint8_t c) {
|
||||
if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) {
|
||||
return 0xFFFF; // invalid
|
||||
}
|
||||
return pgm_read_word(&Z_ClusterNumber[c]);
|
||||
}
|
||||
|
||||
// convert a cluster number to 1 byte, or 0xFF if not in table
|
||||
uint8_t toClusterCode(uint16_t c) {
|
||||
for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) {
|
||||
if (c == pgm_read_word(&Z_ClusterNumber[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xFF; // not found
|
||||
}
|
||||
|
||||
class SBuffer hibernateDevice(const struct Z_Device &device) {
|
||||
SBuffer buf(128);
|
||||
|
||||
buf.add8(0x00); // overall length, will be updated later
|
||||
buf.add16(device.shortaddr);
|
||||
buf.add64(device.longaddr);
|
||||
uint32_t endpoints = device.endpoints.size();
|
||||
if (endpoints > 254) { endpoints = 254; }
|
||||
buf.add8(endpoints);
|
||||
// iterate on endpoints
|
||||
for (std::vector<uint32_t>::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) {
|
||||
uint32_t ep_profile = *ite;
|
||||
uint8_t endpoint = (ep_profile >> 16) & 0xFF;
|
||||
uint16_t profileId = ep_profile & 0xFFFF;
|
||||
|
||||
buf.add8(endpoint);
|
||||
buf.add16(profileId);
|
||||
for (std::vector<uint32_t>::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) {
|
||||
uint16_t cluster = *itc & 0xFFFF;
|
||||
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
|
||||
|
||||
if (endpoint == c_endpoint) {
|
||||
uint8_t clusterCode = toClusterCode(cluster);
|
||||
if (0xFF != clusterCode) { buf.add8(clusterCode); }
|
||||
}
|
||||
}
|
||||
buf.add8(0xFF); // end of endpoint marker
|
||||
|
||||
for (std::vector<uint32_t>::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) {
|
||||
uint16_t cluster = *itc & 0xFFFF;
|
||||
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
|
||||
|
||||
if (endpoint == c_endpoint) {
|
||||
uint8_t clusterCode = toClusterCode(cluster);
|
||||
if (0xFF != clusterCode) { buf.add8(clusterCode); }
|
||||
}
|
||||
}
|
||||
buf.add8(0xFF); // end of endpoint marker
|
||||
}
|
||||
|
||||
// ModelID
|
||||
size_t model_len = device.modelId.length();
|
||||
if (model_len > 32) { model_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.modelId.c_str(), model_len);
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// ManufID
|
||||
size_t manuf_len = device.manufacturerId.length();
|
||||
if (manuf_len > 32) {manuf_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.manufacturerId.c_str(), manuf_len);
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// FriendlyName
|
||||
size_t frname_len = device.friendlyName.length();
|
||||
if (frname_len > 32) {frname_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.friendlyName.c_str(), frname_len);
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// update overall length
|
||||
buf.set8(0, buf.len());
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
class SBuffer hibernateDevices(void) {
|
||||
SBuffer buf(2048);
|
||||
|
||||
size_t devices_size = zigbee_devices.devicesSize();
|
||||
if (devices_size > 32) { devices_size = 32; } // arbitrarily limit to 32 devices, for now
|
||||
buf.add8(devices_size); // number of devices
|
||||
|
||||
for (uint32_t i = 0; i < devices_size; i++) {
|
||||
const Z_Device & device = zigbee_devices.devicesAt(i);
|
||||
const SBuffer buf_device = hibernateDevice(device);
|
||||
buf.addBuffer(buf_device);
|
||||
}
|
||||
|
||||
size_t buf_len = buf.len();
|
||||
if (buf_len > 2040) {
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len);
|
||||
}
|
||||
|
||||
// Log
|
||||
char *hex_char = (char*) malloc((buf_len * 2) + 2);
|
||||
if (hex_char) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"),
|
||||
ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2));
|
||||
free(hex_char);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void hidrateDevices(const SBuffer &buf) {
|
||||
uint32_t buf_len = buf.len();
|
||||
if (buf_len <= 10) { return; }
|
||||
|
||||
uint32_t k = 0;
|
||||
uint32_t num_devices = buf.get8(k++);
|
||||
|
||||
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||
uint32_t dev_record_len = buf.get8(k);
|
||||
|
||||
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
||||
|
||||
uint32_t d = 1; // index in device buffer
|
||||
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
||||
uint64_t longaddr = buf_d.get64(d); d += 8;
|
||||
zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses
|
||||
|
||||
uint32_t endpoints = buf_d.get8(d++);
|
||||
for (uint32_t j = 0; j < endpoints; j++) {
|
||||
uint8_t ep = buf_d.get8(d++);
|
||||
uint16_t ep_profile = buf_d.get16(d); d += 2;
|
||||
zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile);
|
||||
|
||||
// in clusters
|
||||
while (d < dev_record_len) { // safe guard against overflow
|
||||
uint8_t ep_cluster = buf_d.get8(d++);
|
||||
if (0xFF == ep_cluster) { break; } // end of block
|
||||
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false);
|
||||
}
|
||||
// out clusters
|
||||
while (d < dev_record_len) { // safe guard against overflow
|
||||
uint8_t ep_cluster = buf_d.get8(d++);
|
||||
if (0xFF == ep_cluster) { break; } // end of block
|
||||
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true);
|
||||
}
|
||||
}
|
||||
|
||||
// parse 3 strings
|
||||
char empty[] = "";
|
||||
|
||||
// ManufID
|
||||
uint32_t s_len = buf_d.strlen_s(d);
|
||||
char *ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setModelId(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
|
||||
// ManufID
|
||||
s_len = buf_d.strlen_s(d);
|
||||
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setManufId(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
|
||||
// FriendlyName
|
||||
s_len = buf_d.strlen_s(d);
|
||||
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setFriendlyName(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
|
||||
// next iteration
|
||||
k += dev_record_len;
|
||||
}
|
||||
}
|
||||
|
||||
void loadZigbeeDevices(void) {
|
||||
z_flashdata_t flashdata;
|
||||
memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t));
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
|
||||
|
||||
// Check the signature
|
||||
if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) {
|
||||
uint16_t buf_len = flashdata.len;
|
||||
// parse what seems to be a valid entry
|
||||
SBuffer buf(buf_len);
|
||||
buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len);
|
||||
hidrateDevices(buf);
|
||||
zigbee_devices.clean(); // don't write back to Flash what we just loaded
|
||||
} else {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash"));
|
||||
}
|
||||
}
|
||||
|
||||
void saveZigbeeDevices(void) {
|
||||
SBuffer buf = hibernateDevices();
|
||||
size_t buf_len = buf.len();
|
||||
if (buf_len > Z_MAX_FLASH) {
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// first copy SPI buffer into ram
|
||||
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
|
||||
if (!spi_buffer) {
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
|
||||
return;
|
||||
}
|
||||
// copy the flash into RAM to make local change, and write back the whole buffer
|
||||
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||
|
||||
z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset);
|
||||
flashdata->name = ZIGB_NAME;
|
||||
flashdata->len = buf_len;
|
||||
flashdata->reserved = 0;
|
||||
|
||||
memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len);
|
||||
|
||||
// buffer is now ready, write it back
|
||||
if (ESP.flashEraseSector(z_spi_start_sector)) {
|
||||
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||
}
|
||||
|
||||
free(spi_buffer);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len);
|
||||
}
|
||||
|
||||
// Erase the flash area containing the ZigbeeData
|
||||
void eraseZigbeeDevices(void) {
|
||||
zigbee_devices.clean(); // avoid writing data to flash after erase
|
||||
// first copy SPI buffer into ram
|
||||
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
|
||||
if (!spi_buffer) {
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
|
||||
return;
|
||||
}
|
||||
// copy the flash into RAM to make local change, and write back the whole buffer
|
||||
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||
|
||||
// Fill the Zigbee area with 0xFF
|
||||
memset(spi_buffer + z_block_offset, 0xFF, z_block_len);
|
||||
|
||||
// buffer is now ready, write it back
|
||||
if (ESP.flashEraseSector(z_spi_start_sector)) {
|
||||
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||
}
|
||||
|
||||
free(spi_buffer);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len);
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -356,7 +356,12 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
|
|||
|
||||
// TODO
|
||||
case 0x39: // float
|
||||
i += 4;
|
||||
{
|
||||
uint32_t uint32_val = buf.get32(i);
|
||||
float * float_val = (float*) &uint32_val;
|
||||
i += 4;
|
||||
json[attrid_str] = *float_val;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE0: // ToD
|
||||
|
@ -401,7 +406,12 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
|
|||
i += 2;
|
||||
break;
|
||||
case 0x3A: // double precision
|
||||
i += 8;
|
||||
{
|
||||
uint64_t uint64_val = buf.get64(i);
|
||||
double * double_val = (double*) &uint64_val;
|
||||
i += 8;
|
||||
json[attrid_str] = *double_val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -473,7 +483,7 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
|
|||
uint32_t len = _payload.len();
|
||||
|
||||
char attrid_str[12];
|
||||
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cmd_id, _cluster_id);
|
||||
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cluster_id, _cmd_id);
|
||||
|
||||
char hex_char[_payload.len()*2+2];
|
||||
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
|
||||
|
@ -561,7 +571,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x000C, 0x0041, "MaxPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0045, "MinPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x000C, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0055, "AqaraRotate", &Z_Copy },
|
||||
{ 0x000C, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x000C, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x000C, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
|
@ -569,6 +579,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x000C, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x000C, 0x0075, "EngineeringUnits", &Z_Copy },
|
||||
{ 0x000C, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy },
|
||||
// Binary Output cluster
|
||||
{ 0x0010, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x0010, 0x001C, "Description", &Z_Copy },
|
||||
|
@ -830,10 +841,10 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
|
|||
|
||||
// Publish a message for `"Occupancy":0` when the timer expired
|
||||
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();
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json = jsonBuffer.createObject();
|
||||
json[F(OCCUPANCY)] = 0;
|
||||
zigbee_devices.jsonPublishNow(shortaddr, json);
|
||||
}
|
||||
|
||||
// Aqara Cube
|
||||
|
|
|
@ -40,11 +40,11 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "Color", "0300!07/xxxxyyyy0A00" }, // x, y (uint16)
|
||||
{ "CT", "0300!0A/xxxx0A00" }, // Color Temperature Mireds (uint16)
|
||||
{ "Shutter", "0102!xx" },
|
||||
{ "ShutterOpen", "0102!00"},
|
||||
{ "ShutterClose", "0102!01"},
|
||||
{ "ShutterStop", "0102!02"},
|
||||
{ "ShutterLift", "0102!05xx"}, // Lift percentage, 0%=open, 100%=closed
|
||||
{ "ShutterTilt", "0102!08xx"}, // Tilt percentage
|
||||
{ "ShutterOpen", "0102!00" },
|
||||
{ "ShutterClose", "0102!01" },
|
||||
{ "ShutterStop", "0102!02" },
|
||||
{ "ShutterLift", "0102!05xx" }, // Lift percentage, 0%=open, 100%=closed
|
||||
{ "ShutterTilt", "0102!08xx" }, // Tilt percentage
|
||||
};
|
||||
|
||||
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
|
||||
|
|
|
@ -32,7 +32,7 @@ const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; // Device announces its
|
|||
const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor
|
||||
const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor
|
||||
const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters)
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
|
||||
const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version
|
||||
const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration
|
||||
const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
|
||||
|
@ -354,7 +354,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
|
||||
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
||||
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
||||
ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
||||
ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
||||
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
||||
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
|
||||
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // memorize info
|
||||
|
@ -386,6 +386,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
|||
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started")
|
||||
ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started")
|
||||
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
|
||||
ZI_CALL(&Z_Load_Devices, 0)
|
||||
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
||||
ZI_WAIT_FOREVER()
|
||||
ZI_GOTO(ZIGBEE_LABEL_READY)
|
||||
|
@ -544,7 +545,7 @@ void ZigbeeStateMachine_Run(void) {
|
|||
}
|
||||
|
||||
// load current instruction details
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc);
|
||||
const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc];
|
||||
cur_instr = pgm_read_byte(&cur_instr_line->i.i);
|
||||
cur_d8 = pgm_read_byte(&cur_instr_line->i.d8);
|
||||
|
|
|
@ -403,7 +403,7 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpo
|
|||
// Post-provess for Aqara Presence Senson
|
||||
Z_AqaraOccupancy(shortaddr, cluster, endpoint, json);
|
||||
|
||||
zigbee_devices.jsonPublish(shortaddr);
|
||||
zigbee_devices.jsonPublishFlush(shortaddr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -432,9 +432,7 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
|
||||
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json_root = jsonBuffer.createObject();
|
||||
JsonObject& json1 = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED));
|
||||
JsonObject& json = json1.createNestedObject(shortaddr);
|
||||
JsonObject& json = jsonBuffer.createObject();
|
||||
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
|
@ -446,8 +444,8 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
}
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json_root.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
||||
json.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add linkquality
|
||||
|
@ -457,18 +455,14 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
// Prepare for publish
|
||||
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
|
||||
// there is conflicting values, force a publish of the previous message now and don't coalesce
|
||||
zigbee_devices.jsonPublish(srcaddr);
|
||||
zigbee_devices.jsonPublishFlush(srcaddr);
|
||||
} else {
|
||||
zigbee_devices.jsonAppend(srcaddr, json);
|
||||
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
|
||||
}
|
||||
} else {
|
||||
// Publish immediately
|
||||
msg = "";
|
||||
json_root.printTo(msg);
|
||||
Response_P(PSTR("%s"), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
zigbee_devices.jsonPublishNow(srcaddr, json);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -511,6 +505,12 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
|
|||
}
|
||||
}
|
||||
|
||||
int32_t Z_Load_Devices(uint8_t value) {
|
||||
// try to hidrate from known devices
|
||||
loadZigbeeDevices();
|
||||
return 0; // continue
|
||||
}
|
||||
|
||||
int32_t Z_State_Ready(uint8_t value) {
|
||||
zigbee.init_phase = false; // initialization phase complete
|
||||
return 0; // continue
|
||||
|
|
|
@ -28,16 +28,24 @@ const uint8_t ZIGBEE_SOF_ALT = 0xFF;
|
|||
#include <TasmotaSerial.h>
|
||||
TasmotaSerial *ZigbeeSerial = nullptr;
|
||||
|
||||
const char kZigbeeCommands[] PROGMEM = "|"
|
||||
|
||||
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE
|
||||
;
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
|
||||
|
||||
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
|
||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
|
||||
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive
|
||||
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive,
|
||||
&CmndZigbeeForget, &CmndZigbeeSave, &CmndZigbeeName
|
||||
};
|
||||
|
||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||
|
@ -62,7 +70,7 @@ int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
|||
}
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZigbeeProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match);
|
||||
}
|
||||
|
||||
// if there is a recv_callback, call it now
|
||||
|
@ -101,7 +109,7 @@ int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
|||
res = (*zigbee.recv_unexpected)(res, buf);
|
||||
}
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZigbeeProcessInput: res = %d"), res);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res);
|
||||
|
||||
// change state accordingly
|
||||
if (0 == res) {
|
||||
|
@ -134,7 +142,7 @@ void ZigbeeInput(void)
|
|||
while (ZigbeeSerial->available()) {
|
||||
yield();
|
||||
uint8_t zigbee_in_byte = ZigbeeSerial->read();
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZigbeeInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len());
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZbInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len());
|
||||
|
||||
if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized
|
||||
zigbee_frame_len = 5;
|
||||
|
@ -143,14 +151,14 @@ void ZigbeeInput(void)
|
|||
// in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE
|
||||
// We forgive this mistake, and next bytes are automatically resynchronized
|
||||
if (ZIGBEE_SOF_ALT == zigbee_in_byte) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
|
||||
zigbee_in_byte = ZIGBEE_SOF;
|
||||
}
|
||||
}
|
||||
|
||||
if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) {
|
||||
// waiting for SOF (Start Of Frame) byte, discard anything else
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte);
|
||||
continue; // discard
|
||||
}
|
||||
|
||||
|
@ -210,7 +218,7 @@ void ZigbeeInit(void)
|
|||
{
|
||||
zigbee.active = false;
|
||||
if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
|
||||
// if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial
|
||||
ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes
|
||||
ZigbeeSerial->begin(115200);
|
||||
|
@ -254,6 +262,7 @@ void CmndZigbeeReset(void) {
|
|||
switch (XdrvMailbox.payload) {
|
||||
case 1:
|
||||
ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET));
|
||||
eraseZigbeeDevices();
|
||||
restart_flag = 2;
|
||||
ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING);
|
||||
break;
|
||||
|
@ -263,13 +272,6 @@ 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.index, dump.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CmndZigbeeZNPSendOrReceive(bool send)
|
||||
{
|
||||
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
||||
|
@ -320,17 +322,17 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|||
uint8_t fcs = data_len;
|
||||
|
||||
ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
|
||||
ZigbeeSerial->write(data_len);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint8_t b = pgm_read_byte(msg + i);
|
||||
ZigbeeSerial->write(b);
|
||||
fcs ^= b;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
|
||||
}
|
||||
ZigbeeSerial->write(fcs); // finally send fcs checksum byte
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
|
||||
}
|
||||
// Now send a MQTT message to report the sent message
|
||||
char hex_char[(len * 2) + 2];
|
||||
|
@ -421,13 +423,13 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
|
|||
if (0 == endpoint) {
|
||||
// endpoint is not specified, let's try to find it from shortAddr
|
||||
endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, cluster);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: guessing endpoint 0x%02X"), endpoint);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
||||
dstAddr, cluster, endpoint, cmd, data);
|
||||
|
||||
if (0 == endpoint) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeSend: unspecified endpoint"));
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -467,7 +469,7 @@ void CmndZigbeeSend(void) {
|
|||
if (nullptr != &val_device) { device = strToUInt(val_device); }
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
||||
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
||||
if (nullptr != &val_cmd) {
|
||||
// probe the type of the argument
|
||||
// If JSON object, it's high level commands
|
||||
|
@ -522,9 +524,9 @@ void CmndZigbeeSend(void) {
|
|||
}
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_template = %s"), cmd_str.c_str());
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str());
|
||||
cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_final = %s"), cmd_str.c_str());
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str());
|
||||
} else {
|
||||
// we have zero command, pass through until last error for missing command
|
||||
}
|
||||
|
@ -535,7 +537,7 @@ void CmndZigbeeSend(void) {
|
|||
// we have an unsupported command type, just ignore it and fallback to missing command
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
|
||||
device, endpoint, cmd_str.c_str());
|
||||
zigbeeZCLSendStr(device, endpoint, cmd_str.c_str());
|
||||
} else {
|
||||
|
@ -548,20 +550,68 @@ void CmndZigbeeSend(void) {
|
|||
// Probe a specific device to get its endpoints and supported clusters
|
||||
void CmndZigbeeProbe(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
char dataBufUc[XdrvMailbox.data_len + 1];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
if (strlen(dataBufUc) < 3) { ResponseCmndChar("Invalid destination"); return; }
|
||||
|
||||
// TODO, for now ignore friendly names
|
||||
uint16_t shortaddr = strtoull(dataBufUc, nullptr, 0);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeScan: short addr 0x%04X"), shortaddr);
|
||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
|
||||
// everything is good, we can send the command
|
||||
Z_SendActiveEpReq(shortaddr);
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// Specify, read or erase a Friendly Name
|
||||
void CmndZigbeeName(void) {
|
||||
// Syntax is:
|
||||
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
|
||||
// ZigbeeName <device_id> - display the current friendly name
|
||||
// ZigbeeName <device_id>, - remove friendly name
|
||||
//
|
||||
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
|
||||
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
|
||||
// check if parameters contain a comma ','
|
||||
char *p;
|
||||
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
||||
|
||||
// parse first part, <device_id>
|
||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
|
||||
if (p == nullptr) {
|
||||
const String * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
|
||||
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName->c_str() : "");
|
||||
} else {
|
||||
zigbee_devices.setFriendlyName(shortaddr, p);
|
||||
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
|
||||
void CmndZigbeeForget(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
|
||||
// everything is good, we can send the command
|
||||
if (zigbee_devices.removeDevice(shortaddr)) {
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
ResponseCmndChar("Unknown device");
|
||||
}
|
||||
}
|
||||
|
||||
// Save Zigbee information to flash
|
||||
void CmndZigbeeSave(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
|
||||
saveZigbeeDevices();
|
||||
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
||||
void CmndZigbeeRead(void) {
|
||||
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
||||
|
@ -633,6 +683,20 @@ void CmndZigbeePermitJoin(void)
|
|||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
void CmndZigbeeStatus(void) {
|
||||
if (ZigbeeSerial) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
if (XdrvMailbox.payload > 0) {
|
||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||
}
|
||||
|
||||
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
|
||||
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
@ -659,7 +723,8 @@ bool Xdrv23(uint8_t function)
|
|||
ZigbeeInit();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kZigbeeCommands, ZigbeeCommand);
|
||||
result = DecodeCommand(kZbCommands, ZigbeeCommand);
|
||||
result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); // deprecated
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,13 +155,8 @@ void CmndBuzzer(void)
|
|||
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (XdrvMailbox.payload > 0) {
|
||||
char *p;
|
||||
uint32_t i = 0;
|
||||
uint32_t parm[4] = { 0 };
|
||||
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) {
|
||||
parm[i] = strtoul(str, nullptr, 0);
|
||||
i++;
|
||||
}
|
||||
ParseParameters(4, parm);
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
if (parm[i] < 1) { parm[i] = 1; } // Default Count, On time, Off time
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ uint16_t messwerte[5] = {30,50,70,90,100};
|
|||
uint16_t last_execute_step;
|
||||
|
||||
enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,};
|
||||
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
|
||||
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
|
||||
|
||||
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
|
||||
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
|
||||
|
@ -48,7 +48,8 @@ void (* const ShutterCommand[])(void) PROGMEM = {
|
|||
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
|
||||
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime};
|
||||
|
||||
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}";
|
||||
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}";
|
||||
const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}";
|
||||
|
||||
#include <Ticker.h>
|
||||
|
||||
|
@ -101,6 +102,8 @@ void ShutterRtc50mS(void)
|
|||
|
||||
int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
|
||||
{
|
||||
if (0 == percent) return 0;
|
||||
if (100 == percent) return Shutter.open_max[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 {
|
||||
|
@ -135,6 +138,8 @@ int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
|
|||
|
||||
uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
|
||||
{
|
||||
if (0 >= realpos) return 0;
|
||||
if (Shutter.open_max[index] <= realpos) return 100;
|
||||
if (Settings.shutter_set50percent[index] != 50) {
|
||||
return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]);
|
||||
} else {
|
||||
|
@ -228,7 +233,7 @@ void ShutterInit(void)
|
|||
|
||||
Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
|
||||
//Shutter.real_position[i] = Settings.shutter_position[i] <= 5 ? Settings.shuttercoeff[2][i] * Settings.shutter_position[i] : Settings.shuttercoeff[1][i] * Settings.shutter_position[i] + Settings.shuttercoeff[0,i];
|
||||
Shutter.start_position[i] = Shutter.real_position[i];
|
||||
Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i];
|
||||
Shutter.motordelay[i] = Settings.shutter_motordelay[i];
|
||||
|
||||
char shutter_open_chr[10];
|
||||
|
@ -244,7 +249,7 @@ void ShutterInit(void)
|
|||
// terminate loop at first INVALID shutter.
|
||||
break;
|
||||
}
|
||||
|
||||
ShutterLimitRealAndTargetPositions(i);
|
||||
Settings.shutter_accuracy = 1;
|
||||
}
|
||||
}
|
||||
|
@ -556,28 +561,27 @@ void ShutterButtonHandler(void)
|
|||
}
|
||||
}
|
||||
if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { // press still valid && SetOption32 (40) - Button hold
|
||||
if (!Settings.flag.button_restrict) { // no SetOption1 (0)
|
||||
// check for simultaneous shutter button hold
|
||||
if (ShutterButtonIsSimultaneousHold(button_index)) {
|
||||
// simultaneous shutter button hold detected
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++)
|
||||
if (Settings.shutter_button[i] & (1<<31))
|
||||
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
|
||||
}
|
||||
// check for simultaneous shutter button hold
|
||||
if (ShutterButtonIsSimultaneousHold(button_index)) {
|
||||
// simultaneous shutter button hold detected
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++)
|
||||
if (Settings.shutter_button[i] & (1<<31))
|
||||
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
|
||||
press_index = 0;
|
||||
buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS;
|
||||
}
|
||||
if (Button.press_counter[button_index]<99)
|
||||
if (Button.press_counter[button_index]<99) {
|
||||
press_index = 0;
|
||||
buttonState = SHT_PRESSED_HOLD;
|
||||
}
|
||||
Button.press_counter[button_index] = 0;
|
||||
}
|
||||
if ((!Settings.flag.button_restrict) && (Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { // no SetOption1 (0) && SetOption32 (40) - Button held for factor times longer
|
||||
if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
|
||||
// check for simultaneous shutter button extend hold
|
||||
if (ShutterButtonIsSimultaneousHold(button_index)) {
|
||||
// simultaneous shutter button extend hold detected
|
||||
char scmnd[20];
|
||||
press_index = 0;
|
||||
buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS;
|
||||
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
|
||||
ExecuteCommand(scmnd, SRC_BUTTON);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -589,29 +593,23 @@ void ShutterButtonHandler(void)
|
|||
} else {
|
||||
if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) {
|
||||
if (Button.press_counter[button_index]<99) {
|
||||
if ((!Settings.flag.button_restrict) && (Button.press_counter[button_index]>=5)) { // no SetOption1 (0) && 5x or more presses
|
||||
// check for simultaneous shutter button press >3
|
||||
uint32 min_shutterbutton_press_counter = -1;
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++) {
|
||||
if ((Settings.shutter_button[i] & (1<<31)) && (Button.press_counter[i] < min_shutterbutton_press_counter))
|
||||
min_shutterbutton_press_counter = Button.press_counter[i];
|
||||
}
|
||||
if (min_shutterbutton_press_counter >= Button.press_counter[button_index]-2) {
|
||||
char scmnd[20];
|
||||
// simultaneous shutter button press >3 detected
|
||||
press_index = Button.press_counter[button_index];
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++)
|
||||
if (Settings.shutter_button[i] & (1<<31))
|
||||
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
|
||||
buttonState = SHT_SHT_PRESSED_MULTI_SIMULTANEOUS;
|
||||
GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands);
|
||||
ExecuteCommand(scmnd, SRC_BUTTON);
|
||||
return;
|
||||
}
|
||||
// check for simultaneous shutter button press
|
||||
uint32 min_shutterbutton_press_counter = -1;
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++) {
|
||||
if ((Settings.shutter_button[i] & (1<<31)) && (Button.press_counter[i] < min_shutterbutton_press_counter))
|
||||
min_shutterbutton_press_counter = Button.press_counter[i];
|
||||
}
|
||||
press_index = Button.press_counter[button_index];
|
||||
if ((buttonState == SHT_NOT_PRESSED) && (Button.press_counter[button_index]<99)) {
|
||||
if (min_shutterbutton_press_counter == Button.press_counter[button_index]) {
|
||||
// simultaneous shutter button press detected
|
||||
press_index = Button.press_counter[button_index];
|
||||
for (uint32_t i = 0; i < MAX_KEYS; i++)
|
||||
if (Settings.shutter_button[i] & (1<<31))
|
||||
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
|
||||
buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS;
|
||||
}
|
||||
if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) {
|
||||
// no simultaneous shutter button press >3 detected
|
||||
press_index = Button.press_counter[button_index];
|
||||
buttonState = SHT_PRESSED_MULTI;
|
||||
}
|
||||
}
|
||||
|
@ -620,40 +618,58 @@ void ShutterButtonHandler(void)
|
|||
}
|
||||
}
|
||||
|
||||
if ((buttonState != SHT_NOT_PRESSED) && (buttonState != SHT_SHT_PRESSED_MULTI_SIMULTANEOUS) && (buttonState != SHT_PRESSED_EXT_HOLD_SIMULTANEOUS)) {
|
||||
if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) {
|
||||
if (press_index>3) press_index=3;
|
||||
press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, press_index+1);
|
||||
XdrvMailbox.index = shutter_index +1;
|
||||
last_source = SRC_BUTTON;
|
||||
XdrvMailbox.data_len = 0;
|
||||
char databuf[1] = "";
|
||||
XdrvMailbox.data = databuf;
|
||||
XdrvMailbox.command = NULL;
|
||||
if (buttonState == SHT_PRESSED_IMMEDIATE) {
|
||||
XdrvMailbox.payload = XdrvMailbox.index;
|
||||
CmndShutterStop();
|
||||
}
|
||||
else {
|
||||
uint8_t position = (Settings.shutter_button[button_index]>>(6*press_index + 2)) & 0x03f;
|
||||
if (position) {
|
||||
if (Shutter.direction[shutter_index]) {
|
||||
XdrvMailbox.payload = XdrvMailbox.index;
|
||||
CmndShutterStop();
|
||||
} else {
|
||||
XdrvMailbox.payload = position = (position-1)<<1;
|
||||
CmndShutterPosition();
|
||||
if (Settings.shutter_button[button_index] & ((0x01<<26)<<press_index)) {
|
||||
// MQTT broadcast to grouptopic
|
||||
char scommand[CMDSZ];
|
||||
char stopic[TOPSZ];
|
||||
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
|
||||
if ((i==shutter_index) || (Settings.shutter_button[button_index] & (0x01<<30))) {
|
||||
snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1);
|
||||
GetGroupTopic_P(stopic, scommand);
|
||||
Response_P("%d", position);
|
||||
MqttPublish(stopic, false);
|
||||
if (buttonState != SHT_NOT_PRESSED) {
|
||||
if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) {
|
||||
if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { // 5x..7x && no SetOption1 (0)
|
||||
// simultaneous shutter button press 5x, 6x, 7x detected
|
||||
char scmnd[20];
|
||||
GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands);
|
||||
ExecuteCommand(scmnd, SRC_BUTTON);
|
||||
return;
|
||||
}
|
||||
} else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) {
|
||||
// simultaneous shutter button extend hold detected
|
||||
if (!Settings.flag.button_restrict) { // no SetOption1 (0)
|
||||
char scmnd[20];
|
||||
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
|
||||
ExecuteCommand(scmnd, SRC_BUTTON);
|
||||
return;
|
||||
}
|
||||
} else if (buttonState <= SHT_PRESSED_IMMEDIATE) {
|
||||
if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) {
|
||||
uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1);
|
||||
if (pos_press_index>3) pos_press_index=3;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1);
|
||||
XdrvMailbox.index = shutter_index +1;
|
||||
last_source = SRC_BUTTON;
|
||||
XdrvMailbox.data_len = 0;
|
||||
char databuf[1] = "";
|
||||
XdrvMailbox.data = databuf;
|
||||
XdrvMailbox.command = NULL;
|
||||
if (buttonState == SHT_PRESSED_IMMEDIATE) {
|
||||
XdrvMailbox.payload = XdrvMailbox.index;
|
||||
CmndShutterStop();
|
||||
}
|
||||
else {
|
||||
uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f;
|
||||
if (position) {
|
||||
if (Shutter.direction[shutter_index]) {
|
||||
XdrvMailbox.payload = XdrvMailbox.index;
|
||||
CmndShutterStop();
|
||||
} else {
|
||||
XdrvMailbox.payload = position = (position-1)<<1;
|
||||
CmndShutterPosition();
|
||||
if (Settings.shutter_button[button_index] & ((0x01<<26)<<pos_press_index)) {
|
||||
// MQTT broadcast to grouptopic
|
||||
char scommand[CMDSZ];
|
||||
char stopic[TOPSZ];
|
||||
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
|
||||
if ((i==shutter_index) || (Settings.shutter_button[button_index] & (0x01<<30))) {
|
||||
snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1);
|
||||
GetGroupTopic_P(stopic, scommand);
|
||||
Response_P("%d", position);
|
||||
MqttPublish(stopic, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -661,6 +677,11 @@ void ShutterButtonHandler(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
Response_P(PSTR("{"));
|
||||
ResponseAppend_P(JSON_SHUTTER_BUTTON, shutter_index+1, (buttonState <= SHT_PRESSED_IMMEDIATE) ? (button_index+1) : 0, press_index);
|
||||
ResponseJsonEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -757,18 +778,16 @@ void CmndShutterPosition(void)
|
|||
target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent;
|
||||
if (XdrvMailbox.payload != -99) {
|
||||
//target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
|
||||
if (0 == target_pos_percent) {
|
||||
Shutter.target_position[index] = (Settings.shutter_options[index] & 4) ? (-1 * 2000) : 0;
|
||||
} else if (100 == target_pos_percent) {
|
||||
Shutter.target_position[index] = (Settings.shutter_options[index] & 4) ? (Shutter.open_max[index] + 1 * 2000) : Shutter.open_max[index];
|
||||
} else {
|
||||
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
|
||||
}
|
||||
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
|
||||
Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
|
||||
//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:, 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) {
|
||||
if (Settings.shutter_options[index] & 4) {
|
||||
if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000;
|
||||
if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000;
|
||||
}
|
||||
int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
|
||||
if (Shutter.direction[index] == -new_shutterdirection) {
|
||||
// direction need to be changed. on momentary switches first stop the Shutter
|
||||
|
@ -789,26 +808,32 @@ void CmndShutterPosition(void)
|
|||
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);
|
||||
if (Shutter.skip_relay_change == 0) {
|
||||
// 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 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);
|
||||
if (Shutter.skip_relay_change == 0) {
|
||||
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);
|
||||
}
|
||||
Shutter.switched_relay = 0;
|
||||
}
|
||||
} else {
|
||||
target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
|
||||
ShutterReportPosition(true);
|
||||
}
|
||||
XdrvMailbox.index = index +1; // Fix random index for ShutterClose
|
||||
if (XdrvMailbox.command)
|
||||
ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent);
|
||||
} else {
|
||||
ShutterReportPosition(true);
|
||||
if (XdrvMailbox.command)
|
||||
ResponseCmndIdxChar("Locked");
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ void ILI9488_InitDriver()
|
|||
if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) {
|
||||
FT6236begin(FT6236_address);
|
||||
FT6236_found=1;
|
||||
I2cSetActiveFound(FT6236_address, "FT6236");
|
||||
} else {
|
||||
FT6236_found=0;
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ void RA8876_InitDriver()
|
|||
if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) {
|
||||
FT6236begin(FT5316_address);
|
||||
FT5316_found=1;
|
||||
I2cSetActiveFound(FT5316_address, "FT5316");
|
||||
} else {
|
||||
FT5316_found=0;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_CSE7766
|
||||
/*********************************************************************************************\
|
||||
* CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2)
|
||||
* CSE7759 and CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2)
|
||||
* HLW8032 - Energy (Blitzwolf SHP5)
|
||||
*
|
||||
* Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf
|
||||
|
@ -37,6 +37,12 @@
|
|||
#define CSE_PREF 1000
|
||||
#define CSE_UREF 100
|
||||
|
||||
#define CSE_BUFFER_SIZE 25
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
TasmotaSerial *CseSerial = nullptr;
|
||||
|
||||
struct CSE {
|
||||
long voltage_cycle = 0;
|
||||
long current_cycle = 0;
|
||||
|
@ -45,6 +51,8 @@ struct CSE {
|
|||
long cf_pulses = 0;
|
||||
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
|
||||
|
||||
int byte_counter = 0;
|
||||
uint8_t *rx_buffer = nullptr;
|
||||
uint8_t power_invalid = 0;
|
||||
bool received = false;
|
||||
} Cse;
|
||||
|
@ -57,7 +65,7 @@ void CseReceived(void)
|
|||
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok
|
||||
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
|
||||
|
||||
uint8_t header = serial_in_buffer[0];
|
||||
uint8_t header = Cse.rx_buffer[0];
|
||||
if ((header & 0xFC) == 0xFC) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware"));
|
||||
return;
|
||||
|
@ -67,30 +75,30 @@ void CseReceived(void)
|
|||
if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) {
|
||||
long voltage_coefficient = 191200; // uSec
|
||||
if (CSE_NOT_CALIBRATED != header) {
|
||||
voltage_coefficient = serial_in_buffer[2] << 16 | serial_in_buffer[3] << 8 | serial_in_buffer[4];
|
||||
voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4];
|
||||
}
|
||||
Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF;
|
||||
}
|
||||
if (HLW_IREF_PULSE == Settings.energy_current_calibration) {
|
||||
long current_coefficient = 16140; // uSec
|
||||
if (CSE_NOT_CALIBRATED != header) {
|
||||
current_coefficient = serial_in_buffer[8] << 16 | serial_in_buffer[9] << 8 | serial_in_buffer[10];
|
||||
current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10];
|
||||
}
|
||||
Settings.energy_current_calibration = current_coefficient;
|
||||
}
|
||||
if (HLW_PREF_PULSE == Settings.energy_power_calibration) {
|
||||
long power_coefficient = 5364000; // uSec
|
||||
if (CSE_NOT_CALIBRATED != header) {
|
||||
power_coefficient = serial_in_buffer[14] << 16 | serial_in_buffer[15] << 8 | serial_in_buffer[16];
|
||||
power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16];
|
||||
}
|
||||
Settings.energy_power_calibration = power_coefficient / CSE_PREF;
|
||||
}
|
||||
|
||||
uint8_t adjustement = serial_in_buffer[20];
|
||||
Cse.voltage_cycle = serial_in_buffer[5] << 16 | serial_in_buffer[6] << 8 | serial_in_buffer[7];
|
||||
Cse.current_cycle = serial_in_buffer[11] << 16 | serial_in_buffer[12] << 8 | serial_in_buffer[13];
|
||||
Cse.power_cycle = serial_in_buffer[17] << 16 | serial_in_buffer[18] << 8 | serial_in_buffer[19];
|
||||
Cse.cf_pulses = serial_in_buffer[21] << 8 | serial_in_buffer[22];
|
||||
uint8_t adjustement = Cse.rx_buffer[20];
|
||||
Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7];
|
||||
Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13];
|
||||
Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19];
|
||||
Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22];
|
||||
|
||||
if (Energy.power_on) { // Powered on
|
||||
if (adjustement & 0x40) { // Voltage valid
|
||||
|
@ -134,41 +142,44 @@ void CseReceived(void)
|
|||
|
||||
bool CseSerialInput(void)
|
||||
{
|
||||
if (Cse.received) {
|
||||
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
|
||||
if (24 == serial_in_byte_counter) {
|
||||
while (CseSerial->available()) {
|
||||
yield();
|
||||
uint8_t serial_in_byte = CseSerial->read();
|
||||
|
||||
AddLogSerial(LOG_LEVEL_DEBUG_MORE);
|
||||
if (Cse.received) {
|
||||
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
|
||||
if (24 == Cse.byte_counter) {
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (uint32_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; }
|
||||
if (checksum == serial_in_buffer[23]) {
|
||||
Energy.data_valid[0] = 0;
|
||||
CseReceived();
|
||||
Cse.received = false;
|
||||
return true;
|
||||
} else {
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
|
||||
do { // Sync buffer with data (issue #1907 and #3425)
|
||||
memmove(serial_in_buffer, serial_in_buffer +1, 24);
|
||||
serial_in_byte_counter--;
|
||||
} while ((serial_in_byte_counter > 2) && (0x5A != serial_in_buffer[1]));
|
||||
if (0x5A != serial_in_buffer[1]) {
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24);
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; }
|
||||
if (checksum == Cse.rx_buffer[23]) {
|
||||
Energy.data_valid[0] = 0;
|
||||
CseReceived();
|
||||
Cse.received = false;
|
||||
serial_in_byte_counter = 0;
|
||||
return true;
|
||||
} else {
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
|
||||
do { // Sync buffer with data (issue #1907 and #3425)
|
||||
memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24);
|
||||
Cse.byte_counter--;
|
||||
} while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1]));
|
||||
if (0x5A != Cse.rx_buffer[1]) {
|
||||
Cse.received = false;
|
||||
Cse.byte_counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((0x5A == serial_in_byte) && (1 == serial_in_byte_counter)) { // 0x5A - Packet header 2
|
||||
Cse.received = true;
|
||||
} else {
|
||||
serial_in_byte_counter = 0;
|
||||
if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { // 0x5A - Packet header 2
|
||||
Cse.received = true;
|
||||
} else {
|
||||
Cse.byte_counter = 0;
|
||||
}
|
||||
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
|
||||
}
|
||||
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
|
||||
}
|
||||
serial_in_byte = 0; // Discard
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
@ -209,15 +220,33 @@ void CseEverySecond(void)
|
|||
}
|
||||
}
|
||||
|
||||
void CseDrvInit(void)
|
||||
void CseSnsInit(void)
|
||||
{
|
||||
if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported
|
||||
SetSerial(4800, TS_SERIAL_8E1);
|
||||
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
|
||||
// CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], pin[GPIO_CSE7766_TX], 1);
|
||||
CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1);
|
||||
if (CseSerial->begin(4800, 2)) { // Fake Software Serial 8E1 by using two stop bits
|
||||
if (CseSerial->hardwareSerial()) {
|
||||
SetSerial(4800, TS_SERIAL_8E1);
|
||||
ClaimSerial();
|
||||
}
|
||||
if (0 == Settings.param[P_CSE7766_INVALID_POWER]) {
|
||||
Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255
|
||||
}
|
||||
Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER];
|
||||
energy_flg = XNRG_02;
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void CseDrvInit(void)
|
||||
{
|
||||
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
|
||||
if (Cse.rx_buffer != nullptr) {
|
||||
// if ((pin[GPIO_CSE7766_RX] < 99) && (pin[GPIO_CSE7766_TX] < 99)) {
|
||||
if (pin[GPIO_CSE7766_RX] < 99) {
|
||||
energy_flg = XNRG_02;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,8 +283,8 @@ bool Xnrg02(uint8_t function)
|
|||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_SERIAL:
|
||||
result = CseSerialInput();
|
||||
case FUNC_LOOP:
|
||||
if (CseSerial) { CseSerialInput(); }
|
||||
break;
|
||||
case FUNC_ENERGY_EVERY_SECOND:
|
||||
CseEverySecond();
|
||||
|
@ -263,6 +292,9 @@ bool Xnrg02(uint8_t function)
|
|||
case FUNC_COMMAND:
|
||||
result = CseCommand();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
CseSnsInit();
|
||||
break;
|
||||
case FUNC_PRE_INIT:
|
||||
CseDrvInit();
|
||||
break;
|
||||
|
|
|
@ -71,7 +71,7 @@ struct {
|
|||
|
||||
void AdcInit(void)
|
||||
{
|
||||
if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000) || (Settings.adc_param1 < 100)) {
|
||||
if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) {
|
||||
if (ADC0_TEMP == my_adc0) {
|
||||
// Default Shelly 2.5 and 1PM parameters
|
||||
Settings.adc_param_type = ADC0_TEMP;
|
||||
|
@ -85,11 +85,12 @@ void AdcInit(void)
|
|||
Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR;
|
||||
Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000;
|
||||
}
|
||||
else if (ADC0_MOIST == my_adc0) {
|
||||
Settings.adc_param_type = ADC0_MOIST;
|
||||
else if (ADC0_RANGE == my_adc0) {
|
||||
Settings.adc_param_type = ADC0_RANGE;
|
||||
Settings.adc_param1 = 0;
|
||||
Settings.adc_param2 = 1023;
|
||||
Settings.adc_param3 = 0;
|
||||
Settings.adc_param4 = 100;
|
||||
}
|
||||
else if (ADC0_CT_POWER == my_adc0) {
|
||||
Settings.adc_param_type = ADC0_CT_POWER;
|
||||
|
@ -144,17 +145,14 @@ uint16_t AdcGetLux(void)
|
|||
return (uint16_t)ldrLux;
|
||||
}
|
||||
|
||||
uint16_t AdcGetMoist(void)
|
||||
uint16_t AdcGetRange(void)
|
||||
{
|
||||
// formula for calibration: value, fromLow, fromHigh, toHigh, toLow
|
||||
// Example: 632, 0, 1023, 100, 0
|
||||
// int( ( ( (<param2> - <analogue-value>) / ( <param2> - <param1> ) ) * ( <param3> - <param4> ) ) + <param4> )
|
||||
// double amoist = ((Settings.adc_param2 - (double)adc) / (Settings.adc_param2 - Settings.adc_param1) * 100;
|
||||
// int((((1023 - <analog-reading>) / ( 1023 - 0 )) * ( 100 - 0 )) + 0 )
|
||||
// formula for calibration: value, fromLow, fromHigh, toLow, toHigh
|
||||
// Example: 514, 632, 236, 0, 100
|
||||
// int( ((<param2> - <analog-value>) / (<param2> - <param1>) ) * (<param3> - <param4>) ) + <param4> )
|
||||
int adc = AdcRead(2);
|
||||
double amoist = ((double)Settings.adc_param2 - (double)adc) / ((double)Settings.adc_param2 - (double)Settings.adc_param1) * 100;
|
||||
//double amoist = ((1023 - (double)adc) / 1023) * 100;
|
||||
return (uint16_t)amoist;
|
||||
double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 );
|
||||
return (uint16_t)adcrange;
|
||||
}
|
||||
|
||||
void AdcGetCurrentPower(uint8_t factor)
|
||||
|
@ -255,14 +253,14 @@ void AdcShow(bool json)
|
|||
}
|
||||
}
|
||||
|
||||
else if (ADC0_MOIST == my_adc0) {
|
||||
uint16_t adc_moist = AdcGetMoist();
|
||||
else if (ADC0_RANGE == my_adc0) {
|
||||
uint16_t adc_range = AdcGetRange();
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(JSON_SNS_MOISTURE, "ANALOG", adc_moist);
|
||||
ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_MOISTURE, "", adc_moist);
|
||||
WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
@ -342,16 +340,20 @@ void CmndAdcParam(void)
|
|||
if (XdrvMailbox.data_len) {
|
||||
if ((ADC0_TEMP == XdrvMailbox.payload) ||
|
||||
(ADC0_LIGHT == XdrvMailbox.payload) ||
|
||||
(ADC0_MOIST == XdrvMailbox.payload) ||
|
||||
(ADC0_RANGE == XdrvMailbox.payload) ||
|
||||
(ADC0_CT_POWER == XdrvMailbox.payload)) {
|
||||
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry
|
||||
char sub_string[XdrvMailbox.data_len +1];
|
||||
// AdcParam 2, 32000, 10000, 3350
|
||||
// AdcParam 3, 10000, 12518931, -1.405
|
||||
// AdcParam 6, 0, 1023, 0, 100
|
||||
Settings.adc_param_type = XdrvMailbox.payload;
|
||||
Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
|
||||
Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10);
|
||||
if (!ADC0_MOIST == XdrvMailbox.payload) {
|
||||
if (ADC0_RANGE == XdrvMailbox.payload) {
|
||||
Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10));
|
||||
Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10));
|
||||
} else {
|
||||
Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000);
|
||||
}
|
||||
if (ADC0_CT_POWER == XdrvMailbox.payload) {
|
||||
|
@ -373,7 +375,9 @@ void CmndAdcParam(void)
|
|||
|
||||
// AdcParam
|
||||
Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2);
|
||||
if (ADC0_MOIST != my_adc0) {
|
||||
if (ADC0_RANGE == my_adc0) {
|
||||
ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4);
|
||||
} else {
|
||||
int value = Settings.adc_param3;
|
||||
uint8_t precision;
|
||||
for (precision = 4; precision > 0; precision--) {
|
||||
|
@ -403,7 +407,7 @@ bool Xsns02(uint8_t function)
|
|||
if ((ADC0_INPUT == my_adc0) ||
|
||||
(ADC0_TEMP == my_adc0) ||
|
||||
(ADC0_LIGHT == my_adc0) ||
|
||||
(ADC0_MOIST == my_adc0) ||
|
||||
(ADC0_RANGE == my_adc0) ||
|
||||
(ADC0_CT_POWER == my_adc0)) {
|
||||
switch (function) {
|
||||
#ifdef USE_RULES
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
#define DS1621_COUNTER_REGISTER 0xA8 //exists on 1621 and 1624(undocumented)
|
||||
#define DS1621_SLOPE_REGISTER 0xA9 //exists on 1624 and 1624(undocumented)
|
||||
|
||||
#define DS1621_CFG_1SHOT (1<<0)
|
||||
#define DS1621_CFG_DONE (1<<7)
|
||||
|
||||
enum {
|
||||
DS1624_TYPE_DS1624,
|
||||
DS1624_TYPE_DS1621
|
||||
|
@ -50,6 +53,8 @@ bool ds1624_init = false;
|
|||
struct {
|
||||
float value;
|
||||
uint8_t type;
|
||||
int errcnt;
|
||||
int misscnt;
|
||||
bool valid;
|
||||
char name[9];
|
||||
} ds1624_sns[DS1624_MAX_SENSORS];
|
||||
|
@ -58,6 +63,17 @@ uint32_t DS1624_Idx2Addr(uint32_t idx) {
|
|||
return 0x48 + idx;
|
||||
}
|
||||
|
||||
int DS1624_Restart(uint8_t config, uint32_t idx) {
|
||||
uint32_t addr = DS1624_Idx2Addr(idx);
|
||||
if ((config & 1) == 1) {
|
||||
config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT);
|
||||
I2cWrite8(addr, DS1624_CONF_REGISTER, config); // 1shot off
|
||||
delay(10); // by spec after writing
|
||||
AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config);
|
||||
}
|
||||
I2cValidRead(addr, DS1624_START_REGISTER, 1);
|
||||
}
|
||||
|
||||
void DS1624_HotPlugUp(uint32_t idx)
|
||||
{
|
||||
uint32_t addr = DS1624_Idx2Addr(idx);
|
||||
|
@ -75,12 +91,9 @@ void DS1624_HotPlugUp(uint32_t idx)
|
|||
I2cSetActiveFound(addr, ds1624_sns[idx].name);
|
||||
|
||||
ds1624_sns[idx].valid = true;
|
||||
if ((config & 1) == 1) {
|
||||
config &= 0xfe;
|
||||
I2cWrite8(addr, DS1624_CONF_REGISTER, config); // 1show off
|
||||
delay(10); // by spec after writing
|
||||
}
|
||||
I2cValidRead(addr, DS1624_START_REGISTER, 1); // FIXME 0 must read, but 0 isn't work for tasmota
|
||||
ds1624_sns[idx].errcnt = 0;
|
||||
ds1624_sns[idx].misscnt = 0;
|
||||
DS1624_Restart(config,idx);
|
||||
AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config);
|
||||
}
|
||||
}
|
||||
|
@ -98,16 +111,37 @@ bool DS1624GetTemp(float *value, int idx)
|
|||
{
|
||||
uint32_t addr = DS1624_Idx2Addr(idx);
|
||||
|
||||
uint8_t config;
|
||||
if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) {
|
||||
ds1624_sns[idx].misscnt++;
|
||||
AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt);
|
||||
return false;
|
||||
}
|
||||
ds1624_sns[idx].misscnt=0;
|
||||
if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) {
|
||||
ds1624_sns[idx].errcnt++;
|
||||
AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt);
|
||||
DS1624_Restart(config, idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t t;
|
||||
if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; }
|
||||
*value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625;
|
||||
if (ds1624_sns[idx].type == DS1624_TYPE_DS1621) { // Higher resolution
|
||||
if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) {
|
||||
*value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625;
|
||||
} else { //type == DS1624_TYPE_DS1621
|
||||
// Datasheet for ds1621 is wrong for high resolution, real is:
|
||||
*value = ((float)(int8_t)(t>>8));
|
||||
uint8_t remain;
|
||||
if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; }
|
||||
uint8_t perc;
|
||||
if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; }
|
||||
*value += ((float)perc - (float)remain)/((float)perc) - 0.25;
|
||||
float fix=(float)(perc - remain)/(float)perc;
|
||||
*value+=fix;
|
||||
}
|
||||
ds1624_sns[idx].errcnt=0;
|
||||
config &= ~(DS1621_CFG_DONE);
|
||||
I2cWrite8(addr, DS1624_CONF_REGISTER, config);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -120,9 +154,11 @@ void DS1624HotPlugScan(void)
|
|||
if (I2cActive(addr) && !ds1624_sns[idx].valid) {
|
||||
continue; // is busy by another driver
|
||||
}
|
||||
if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) {
|
||||
DS1624_HotPlugDown(idx);
|
||||
continue;
|
||||
if (ds1624_sns[idx].valid) {
|
||||
if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) {
|
||||
DS1624_HotPlugDown(idx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
DS1624_HotPlugUp(idx);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue