diff --git a/README.md b/README.md index 3f695f666..498cd2f80 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.
See [Chat](https://discord.gg/Ks2Kzd4) for more user experience. The following devices are supported: -- [iTead Sonoff Basic](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html) +- [iTead Sonoff Basic (R2)](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html) - [iTead Sonoff RF](https://www.itead.cc/smart-home/sonoff-rf.html) - [iTead Sonoff SV](https://www.itead.cc/smart-home/sonoff-sv.html) - [iTead Sonoff TH10/TH16 with temperature sensor](https://www.itead.cc/smart-home/sonoff-th.html) @@ -90,10 +90,15 @@ The following devices are supported: - [MagicHome PWM LED controller](https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-LED-strip-controller) - AriLux AL-LC01, AL-LC06 and AL-LC11 PWM LED controller - [Supla device - Espablo-inCan mod. for electrical Installation box](https://forum.supla.org/viewtopic.php?f=33&t=2188) -- [BlitzWolf BW-SHP2 Smart Socket with Energy Monitoring](https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html) +- [BlitzWolf BW-SHP2 Smart Socket with Energy Monitoring](https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html) - [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/) -- Xiaomi-Phillips Bulbs -- Wemos D1 mini, NodeMcu and Ledunia +- [Wemos D1 mini](https://wiki.wemos.cc/products:d1:d1_mini) +- [HuaFan Smart Socket](HuaFan-Smart-Socket) +- [Hyleton-313 Smart Plug](Hyleton-313-Smart-Plug) +- [Allterco Shelly 1](https://shelly.cloud/shelly1-open-source/) +- [Allterco Shelly 2 with Energy Monitoring](https://shelly.cloud/shelly2/) +- NodeMcu and Ledunia +- [KS-602 based switches like GresaTek, Jesiya, NewRice, Lyasi etc](https://ucexperiment.wordpress.com/2017/11/14/reprogramming-a-lyasi-wifi-wall-switch-with-esp8285/) ### Contribute You can contribute to Sonoff-Tasmota by diff --git a/platformio.ini b/platformio.ini index d333fd187..c01485f5a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,7 @@ env_default = sonoff ;env_default = sonoff-classic ;env_default = sonoff-knx ;env_default = sonoff-sensors +;env_default = sonoff-display ;env_default = sonoff-BG ;env_default = sonoff-BR ;env_default = sonoff-CN @@ -167,6 +168,20 @@ upload_resetmethod = ${common.upload_resetmethod} upload_speed = ${common.upload_speed} extra_scripts = ${common.extra_scripts} +[env:sonoff-display] +platform = ${common.platform} +framework = ${common.framework} +board = ${common.board} +board_build.flash_mode = ${common.board_build.flash_mode} +board_build.f_cpu = ${common.board_build.f_cpu} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} -DUSE_DISPLAYS +monitor_speed = ${common.monitor_speed} +upload_port = ${common.upload_port} +upload_resetmethod = ${common.upload_resetmethod} +upload_speed = ${common.upload_speed} +extra_scripts = ${common.extra_scripts} + [env:sonoff-BG] platform = ${common.platform} framework = ${common.framework} diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 333a524a6..abf4572f6 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,7 +1,19 @@ -/* 6.2.1.7 20180925 +/* 6.2.1.9 20180928 + * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) + * + * 6.2.1.8 20180926 + * Change status JSON message providing more switch and retain information + * Change pinmode for no-pullup defined switches to pullup when configured as switchmode PUSHBUTTON (=3 and up) (#3896) + * Add delay after restart before processing rule sensor data (#3811) + * Fix Home Assistant forced light discovery (#3908) + * Add rule triggers SWITCH1#BOOT and POWER1#BOOT (#3904, #3910) + * Add support for Neo Coolcam Wifi Smart Power Plug + * + * 6.2.1.7 20180925 * Remove restart after ntpserver change and force NTP re-sync (#3890) * Release full Shelly2 support * Released tools/decode-config.py by Norbert Richter to decode configuration data. See file for information + * Add define USE_DISPLAYS for selecting image sonoff-display * * 6.2.1.6 20180922 * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 1c628b7b7..c8d3e219f 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -130,7 +130,7 @@ #define D_JSON_TYPE "Type" #define D_JSON_UPTIME "Uptime" #define D_JSON_UTC_TIME "UTC" -#define D_JSON_UVINDEX "UvIndex" +#define D_JSON_UV_INDEX "UvIndex" #define D_JSON_UV_LEVEL "UvLevel" #define D_JSON_UV_POWER "UvPower" #define D_JSON_VCC "Vcc" diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index ff7801984..d79eb5210 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.0.1 + * Updated until v6.2.1.8 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -82,7 +82,7 @@ #define D_DNS_SERVER "DNS Сървър" #define D_DONE "Изпълнено" #define D_DST_TIME "DST" -#define D_ECO2 "eCO2" +#define D_ECO2 "eCO₂" #define D_EMULATION "Емулация" #define D_ENABLED "Активиран" #define D_ERASE "Изтриване" @@ -163,15 +163,15 @@ #define D_USER "Потребител" #define D_UTC_TIME "UTC" #define D_UV_INDEX "UV индекс" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" -#define D_UV_LEVEL "Ниво на ултравиолетово излъчване" -#define D_UV_POWER "UV Power" +#define D_UV_INDEX_1 "Нисък" +#define D_UV_INDEX_2 "Среден" +#define D_UV_INDEX_3 "Висок" +#define D_UV_INDEX_4 "Много висок" +#define D_UV_INDEX_5 "Изгаряне 1/2 степен" +#define D_UV_INDEX_6 "Изгаряне 3-та степен" +#define D_UV_INDEX_7 "Извън обхват" +#define D_UV_LEVEL "UV ниво" +#define D_UV_POWER "UV мощност" #define D_VERSION "Версия" #define D_VOLTAGE "Напрежение" #define D_WARMLIGHT "Топла" @@ -181,8 +181,8 @@ #define D_WARNING_MINIMAL_VERSION "ПРЕДУПРЕЖДЕНИЕ Тази версия не поддържа постоянни настройки" #define D_LEVEL_10 "ниво 1-0" #define D_LEVEL_01 "ниво 0-1" -#define D_SERIAL_LOGGING_DISABLED "Серийния логинг изключен" -#define D_SYSLOG_LOGGING_REENABLED "Системния логинг активиран" +#define D_SERIAL_LOGGING_DISABLED "Серийният лог изключен" +#define D_SYSLOG_LOGGING_REENABLED "Системният лог активиран" #define D_SET_BAUDRATE_TO "Задаване скорост на предаване (Baudrate)" #define D_RECEIVED_TOPIC "Получен топик" @@ -194,7 +194,7 @@ #define D_BLOCKED_LOOP "Блокиран цикъл" #define D_WPS_FAILED_WITH_STATUS "WPS конфигурацията е НЕУСПЕШНА със статус" #define D_ACTIVE_FOR_3_MINUTES "активно в течение на 3 минути" -#define D_FAILED_TO_START "неуспешно стартиране" +#define D_FAILED_TO_START "Неуспешно стартиране" #define D_PATCH_ISSUE_2186 "Проблем с патч 2186" #define D_CONNECTING_TO_AP "Свързване към точка за достъп" #define D_IN_MODE "в режим" @@ -241,7 +241,7 @@ #define D_CONFIGURE_WIFI "Конфигурация на WiFi" #define D_CONFIGURE_MQTT "Конфигурация на MQTT" #define D_CONFIGURE_DOMOTICZ "Конфигурация на Domoticz" -#define D_CONFIGURE_LOGGING "Конфигурация на логинга" +#define D_CONFIGURE_LOGGING "Конфигурация на лога" #define D_CONFIGURE_OTHER "Драги конфигурации" #define D_CONFIRM_RESET_CONFIGURATION "Потвърдете изчистването" #define D_RESET_CONFIGURATION "Изчистване на конфигурацията" @@ -275,7 +275,7 @@ #define D_CLIENT "Клиент" #define D_FULL_TOPIC "Пълен топик" -#define D_LOGGING_PARAMETERS "Параметри на логинга" +#define D_LOGGING_PARAMETERS "Параметри на лога" #define D_SERIAL_LOG_LEVEL "Степен на серийния лог" #define D_WEB_LOG_LEVEL "Степен на Уеб лога" #define D_SYS_LOG_LEVEL "Степен на системния лог" @@ -379,13 +379,13 @@ #define D_DOMOTICZ_TEMP "Temp" #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" - #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" - #define D_DOMOTICZ_COUNT "Count/PM1" - #define D_DOMOTICZ_VOLTAGE "Voltage/PM2,5" - #define D_DOMOTICZ_CURRENT "Current/PM10" - #define D_DOMOTICZ_AIRQUALITY "AirQuality" -#define D_DOMOTICZ_UPDATE_TIMER "Update timer" + #define D_DOMOTICZ_POWER_ENERGY "Мощност,Енергия" + #define D_DOMOTICZ_ILLUMINANCE "Осветеност" + #define D_DOMOTICZ_COUNT "Брояч/PM1" + #define D_DOMOTICZ_VOLTAGE "Напрежение/PM2,5" + #define D_DOMOTICZ_CURRENT "Ток/PM10" + #define D_DOMOTICZ_AIRQUALITY "Качество на въздуха" +#define D_DOMOTICZ_UPDATE_TIMER "Период на опресняване" // xdrv_09_timers.ino #define D_CONFIGURE_TIMER "Конфигуриране на таймер" @@ -464,7 +464,7 @@ #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" #define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_DFR562 "MP3 плейър" #define D_SENSOR_IRSEND "IRsend" #define D_SENSOR_SWITCH "Ключ" // Suffix "1" #define D_SENSOR_BUTTON "Бутон" // Suffix "1" @@ -506,7 +506,7 @@ #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" -#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m3" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³" #define D_UNIT_MICROMETER "µm" #define D_UNIT_MICROSECOND "µs" #define D_UNIT_MILLIAMPERE "mA" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 6e90ae409..72f63f9c4 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -163,15 +163,15 @@ #define D_USER "Benutzer" #define D_UTC_TIME "UTC" #define D_UV_INDEX "UV-Index" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" +#define D_UV_INDEX_1 "Niedrig" +#define D_UV_INDEX_2 "Mittel" +#define D_UV_INDEX_3 "Hoch" +#define D_UV_INDEX_4 "Intensiv" +#define D_UV_INDEX_5 "Gefährlich" +#define D_UV_INDEX_6 "Schädlich" +#define D_UV_INDEX_7 "Messwert!" #define D_UV_LEVEL "UV-Level" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "UV Intensität" #define D_VERSION "Version" #define D_VOLTAGE "Spannung" #define D_WARMLIGHT "warm" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index b67e6e79f..713e166ab 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -163,13 +163,13 @@ #define D_USER "Usuario" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Índice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" +#define D_UV_INDEX_1 "Bajo" +#define D_UV_INDEX_2 "Medio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Peligroso" +#define D_UV_INDEX_5 "Quemaduras 1 a 2 grad" +#define D_UV_INDEX_6 "Quemaduras 3 grad" +#define D_UV_INDEX_7 "Fuera de Rango" #define D_UV_LEVEL "Nivel UV" #define D_UV_POWER "UV Power" #define D_VERSION "Versión" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index c4c913fec..a7adaea39 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.1.1.7 + * Updated until v6.2.1.7 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -65,7 +65,7 @@ #define D_BY "par" // Written by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Canal" #define D_CO2 "Dioxyde de carbone" #define D_CODE "code" // Button code #define D_COLDLIGHT "Froid" @@ -163,15 +163,15 @@ #define D_USER "Utilisateur" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Indice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" +#define D_UV_INDEX_1 "Faible" +#define D_UV_INDEX_2 "Modéré" +#define D_UV_INDEX_3 "Élevé" +#define D_UV_INDEX_4 "Très élevé" +#define D_UV_INDEX_5 "Brûlure niv.1/2" +#define D_UV_INDEX_6 "Brûlure niv.3" +#define D_UV_INDEX_7 "Hors échelle" #define D_UV_LEVEL "Niveau UV" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "Puissance UV" #define D_VERSION "Version" #define D_VOLTAGE "Tension" #define D_WARMLIGHT "Chaud" @@ -184,7 +184,7 @@ #define D_SERIAL_LOGGING_DISABLED "Journalisation série désactivée" #define D_SYSLOG_LOGGING_REENABLED "Jounalisation syslog réactivée" -#define D_SET_BAUDRATE_TO "Définir baudrate à" +#define D_SET_BAUDRATE_TO "Définir le débit à" #define D_RECEIVED_TOPIC "Topic reçu" // Terme MQTT #define D_DATA_SIZE "Taille données" #define D_ANALOG_INPUT "Analogique" diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 0d8fa451a..95e6ce367 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -86,20 +86,12 @@ void RtcSettingsSave() RtcSettings.valid = RTC_MEM_VALID; ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); rtc_settings_crc = GetRtcSettingsCrc(); -#ifdef DEBUG_THEO - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Save")); - RtcSettingsDump(); -#endif // DEBUG_THEO } } void RtcSettingsLoad() { ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); // 0x290 -#ifdef DEBUG_THEO - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Load")); - RtcSettingsDump(); -#endif // DEBUG_THEO if (RtcSettings.valid != RTC_MEM_VALID) { memset(&RtcSettings, 0, sizeof(RTCMEM)); RtcSettings.valid = RTC_MEM_VALID; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index f0619f362..6b354bb24 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -147,6 +147,7 @@ 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 uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold +uint16_t switch_no_pullup = 0; // Switch pull-up bitmask flags int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) uint8_t serial_local = 0; // Handle serial locally; @@ -1119,6 +1120,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { Settings.switchmode[index -1] = payload; + GpioSwitchPinMode(index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); } @@ -1412,6 +1414,7 @@ void PublishStatus(uint8_t payload) { uint8_t option = STAT; char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; + char stemp2[MAX_SWITCHES * 3]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; // TELE @@ -1426,8 +1429,12 @@ void PublishStatus(uint8_t payload) for (byte i = 0; i < maxfn; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_power_retain); + stemp2[0] = '\0'; + for (byte i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); } @@ -2291,11 +2298,23 @@ void SerialInput() /********************************************************************************************/ +void GpioSwitchPinMode(uint8_t index) +{ + if (pin[GPIO_SWT1 +index] < 99) { +// pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : bitRead(switch_no_pullup, index) ? INPUT : INPUT_PULLUP); + + uint8_t no_pullup = 0; + if (bitRead(switch_no_pullup, index)) { + no_pullup = (Settings.switchmode[index] < PUSHBUTTON); + } + pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : (no_pullup) ? INPUT : INPUT_PULLUP); + } +} + void GpioInit() { uint8_t mpin; uint8_t key_no_pullup = 0; - uint16_t switch_no_pullup = 0; mytmplt def_module; if (!Settings.module || (Settings.module >= MAXMODULE)) { @@ -2452,7 +2471,7 @@ void GpioInit() for (byte i = 0; i < MAX_SWITCHES; i++) { lastwallswitch[i] = 1; // Init global to virtual switch state; if (pin[GPIO_SWT1 +i] < 99) { - pinMode(pin[GPIO_SWT1 +i], (16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : bitRead(switch_no_pullup, i) ? INPUT : INPUT_PULLUP); + GpioSwitchPinMode(i); lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check } virtualswitch[i] = lastwallswitch[i]; diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 8622d0818..423a75f3c 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -51,7 +51,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #define USE_DHT // Default DHT11 sensor needs no external library -#define USE_ENERGY_SENSOR // Use energy sensors +#define USE_ENERGY_SENSOR // Use energy sensors (+14k code) #define USE_HLW8012 // Use energy sensor for Sonoff Pow and WolfBlitz #define USE_CSE7766 // Use energy sensor for Sonoff S31 and Pow R2 @@ -167,6 +167,30 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #endif // USE_KNX_NO_EMULATION +/*********************************************************************************************\ + * [sonoff-display.bin] + * Provide an image with display drivers enabled +\*********************************************************************************************/ + +#ifdef USE_DISPLAYS + +#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) +#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) + +#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) + #define USE_DISPLAY // Add I2C Display Support (+2k code) + #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 + #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code) + #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code) + #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code) + +#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) + #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) + +#undef USE_ARILUX_RF // Remove support for Arilux RF remote controller (-0k8 code, 252 iram (non 2.3.0)) +#undef USE_RF_FLASH // Remove support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (-3k code) +#endif // USE_DISPLAYS + /*********************************************************************************************\ * Mandatory define for DS18x20 if changed by above image selections \*********************************************************************************************/ diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 781b1004d..8374fa09d 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -231,6 +231,7 @@ enum SupportedModules { SHELLY1, SHELLY2, PHILIPS, + NEO_COOLCAM, MAXMODULE }; /********************************************************************************************/ @@ -390,6 +391,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { SHELLY1, SHELLY2, BLITZWOLF_BWSHP2, + NEO_COOLCAM, H801, MAGICHOME, ARILUX_LC01, @@ -399,9 +401,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { HUAFAN_SS, KMC_70011, AILIGHT, - WEMOS, + PHILIPS, WITTY, - PHILIPS + WEMOS }; // Default module settings @@ -1054,12 +1056,40 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, GPIO_PWM1, // GPIO15 light intensity 0, 0 + }, + { "Neo Coolcam", // Neo Coolcam (ESP8266) + // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN + 0, 0, 0, 0, + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) + 0, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_KEY1, // GPIO13 Button + 0, 0, 0, 0 } }; /* Optionals + { "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF + // https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285 + // https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html + // https://www.aliexpress.com/item/Wifi-LED-RGB-Controler-DC12V-MIni-Wifi-RGB-RGBW-LED-Controller-for-RGB-RGBW-LED-Strip/32673444047.html + GPIO_USER, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor0 + GPIO_ARIRFRCV, // GPIO04 RF receiver input + GPIO_PWM2, // GPIO05 RGB LED Green + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_PWM3, // GPIO12 RGB LED Blue + GPIO_PWM4, // GPIO13 RGBW LED White + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_LED2_INV, // GPIO15 RF receiver control + 0, 0 + } + { "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128) 0, 0, 0, GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 16c554783..4f2b5c0ec 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020107 +#define VERSION 0x06020109 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support.ino b/sonoff/support.ino index fb2b5eae3..fc73e726e 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -494,6 +494,32 @@ double FastPrecisePow(double a, double b) return r * u.d; } +uint32_t SqrtInt(uint32_t num) +{ + if (num <= 1) { + return num; + } + + uint32_t x = num / 2; + uint32_t y; + do { + y = (x + num / x) / 2; + if (y >= x) { + return x; + } + x = y; + } while (true); +} + +uint32_t RoundSqrtInt(uint32_t num) +{ + uint32_t s = SqrtInt(4 * num); + if (s & 1) { + s++; + } + return s / 2; +} + char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack) { // Returns empty string if not found diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 846c86097..eceadc68d 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -282,9 +282,9 @@ #define USE_BMP // Enable BMP085/BMP180/BMP280/BME280 sensor (I2C address 0x76 or 0x77) (+4k code) // #define USE_BME680 // Enable support for BME680 sensor using Bosch BME680 library (+4k code) #define USE_BH1750 // Enable BH1750 sensor (I2C address 0x23 or 0x5C) (+0k5 code) -// #define USE_VEML6070 // Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+0k5 code) -// #define USE_VEML6070_RSET 270000 // VEML6070, Rset in Ohm used on PCB board, default 270K = 270000ohm, range for this sensor: 220K ... 1Meg -// #define USE_VEML6070_SHOW_RAW // VEML6070, shows the raw value of UV-A +// #define USE_VEML6070 // Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+1k5 code) + #define USE_VEML6070_RSET 270000 // VEML6070, Rset in Ohm used on PCB board, default 270K = 270000ohm, range for this sensor: 220K ... 1Meg + #define USE_VEML6070_SHOW_RAW // VEML6070, shows the raw value of UV-A // #define USE_ADS1115 // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) based on Adafruit ADS1x15 library (no library needed) (+0k7 code) // #define USE_ADS1115_I2CDEV // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) // #define USE_INA219 // Enable INA219 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+1k code) @@ -323,7 +323,7 @@ #endif // USE_I2C // -- SPI sensors --------------------------------- -//#define USE_SPI // SPI using library TasmotaTFT +//#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) #ifdef USE_SPI #ifndef USE_DISPLAY @@ -386,6 +386,7 @@ //#define USE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager //#define USE_SENSORS // Create sonoff-sensors with useful sensors enabled //#define USE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation +//#define USE_DISPLAYS // Create sonoff-display with display drivers enabled //#define BE_MINIMAL // Create sonoff-minimal as intermediate firmware for OTA-MAGIC /*********************************************************************************************\ diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 3ccad7df4..341fc71b5 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -41,23 +41,25 @@ const char kEnergyCommands[] PROGMEM = D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; -float energy_voltage = 0; // 123.1 V -float energy_current = 0; // 123.123 A -float energy_power = 0; // 123.1 W -float energy_power_factor = NAN; // 0.12 -int energy_calc_power_factor = 0; // Do not calculate power factor from data -float energy_frequency = NAN; // 123.1 Hz -float energy_start = 0; // 12345.12345 kWh total previous +float energy_voltage = 0; // 123.1 V +float energy_current = 0; // 123.123 A +float energy_active_power = 0; // 123.1 W +float energy_apparent_power = NAN; // 123.1 VA +float energy_reactive_power = NAN; // 123.1 VAr +float energy_power_factor = NAN; // 0.12 +float energy_frequency = NAN; // 123.1 Hz +float energy_start = 0; // 12345.12345 kWh total previous -float energy_daily = 0; // 123.123 kWh -float energy_total = 0; // 12345.12345 kWh +float energy_daily = 0; // 123.123 kWh +float energy_total = 0; // 12345.12345 kWh unsigned long energy_kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to energy_kWhtoday (HLW and CSE only) -unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily -unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily +unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily +unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily float energy_power_last[3] = { 0 }; uint8_t energy_power_delta = 0; +bool energy_type_dc = false; bool energy_power_on = true; byte energy_min_power_flag = 0; @@ -124,15 +126,6 @@ void Energy200ms() } XnrgCall(FUNC_EVERY_200_MSECOND); - - if (energy_calc_power_factor) { - float power_factor = 0; - if (energy_voltage && energy_current && energy_power) { - power_factor = energy_power / (energy_voltage * energy_current); - if (power_factor > 1) power_factor = 1; - } - energy_power_factor = power_factor; - } } void EnergySaveState() @@ -178,21 +171,21 @@ void EnergyMarginCheck() } if (Settings.energy_power_delta) { - float delta = abs(energy_power_last[0] - energy_power); + float delta = abs(energy_power_last[0] - energy_active_power); // Any delta compared to minimal delta - float min_power = (energy_power_last[0] > energy_power) ? energy_power : energy_power_last[0]; + float min_power = (energy_power_last[0] > energy_active_power) ? energy_active_power : energy_power_last[0]; if (((delta / min_power) * 100) > Settings.energy_power_delta) { energy_power_delta = 1; - energy_power_last[1] = energy_power; // We only want one report so reset history - energy_power_last[2] = energy_power; + energy_power_last[1] = energy_active_power; // We only want one report so reset history + energy_power_last[2] = energy_active_power; } } energy_power_last[0] = energy_power_last[1]; // Shift in history every second allowing power changes to settle for up to three seconds energy_power_last[1] = energy_power_last[2]; - energy_power_last[2] = energy_power; + energy_power_last[2] = energy_active_power; if (energy_power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - energy_power_u = (uint16_t)(energy_power); + energy_power_u = (uint16_t)(energy_active_power); energy_voltage_u = (uint16_t)(energy_voltage); energy_current_u = (uint16_t)(energy_current * 1000); @@ -235,7 +228,7 @@ void EnergyMarginCheck() #if FEATURE_POWER_LIMIT // Max Power if (Settings.energy_max_power_limit) { - if (energy_power > Settings.energy_max_power_limit) { + if (energy_active_power > Settings.energy_max_power_limit) { if (!energy_mplh_counter) { energy_mplh_counter = Settings.energy_max_power_limit_hold; } else { @@ -535,6 +528,8 @@ const char HTTP_ENERGY_SNS1[] PROGMEM = "%s" "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; const char HTTP_ENERGY_SNS2[] PROGMEM = "%s" + "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" + "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" "{s}" D_POWER_FACTOR "{m}%s{e}"; const char HTTP_ENERGY_SNS3[] PROGMEM = "%s" @@ -548,27 +543,65 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = "%s" void EnergyShow(boolean json) { - char energy_total_chr[10]; + char voltage_chr[10]; + char current_chr[10]; + char active_power_chr[10]; + char apparent_power_chr[10]; + char reactive_power_chr[10]; + char power_factor_chr[10]; + char frequency_chr[10]; char energy_daily_chr[10]; char energy_period_chr[10]; - char energy_power_chr[10]; - char energy_voltage_chr[10]; - char energy_current_chr[10]; - char energy_frequency_chr[10]; - char energy_power_factor_chr[10]; char energy_yesterday_chr[10]; + char energy_total_chr[10]; + char speriod[20]; - char spfactor[20]; char sfrequency[20]; bool show_energy_period = (0 == tele_period); - dtostrfd(energy_power, Settings.flag2.wattage_resolution, energy_power_chr); - dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, energy_voltage_chr); - dtostrfd(energy_current, Settings.flag2.current_resolution, energy_current_chr); - dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); + float power_factor = energy_power_factor; + + if (!energy_type_dc) { + float apparent_power = energy_apparent_power; + if (isnan(apparent_power)) { + apparent_power = energy_voltage * energy_current; + } + if (apparent_power < energy_active_power) { // Should be impossible + energy_active_power = apparent_power; + } + + if (isnan(power_factor)) { + power_factor = (energy_active_power && apparent_power) ? energy_active_power / apparent_power : 0; + if (power_factor > 1) power_factor = 1; + } + + float reactive_power = energy_reactive_power; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(energy_active_power * 100)) / 10; + if ((energy_current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + // calculating reactive power only if current is greater than 0.005A and + // difference between active and apparent power is greater than 1.5W or 1% + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(energy_active_power * energy_active_power * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr); + dtostrfd(power_factor, 2, power_factor_chr); + if (!isnan(energy_frequency)) { + dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, frequency_chr); + snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr); + } + } + + dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, voltage_chr); + dtostrfd(energy_current, Settings.flag2.current_resolution, current_chr); + dtostrfd(energy_active_power, Settings.flag2.wattage_resolution, active_power_chr); dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); float energy = 0; if (show_energy_period) { @@ -577,34 +610,30 @@ void EnergyShow(boolean json) dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); snprintf_P(speriod, sizeof(speriod), PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); } - if (!isnan(energy_frequency)) { - dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, energy_frequency_chr); - snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), energy_frequency_chr); - } - if (!isnan(energy_power_factor)) { - dtostrfd(energy_power_factor, 2, energy_power_factor_chr); - snprintf_P(spfactor, sizeof(spfactor), PSTR(",\"" D_JSON_POWERFACTOR "\":%s"), energy_power_factor_chr); - } if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" - D_JSON_POWERUSAGE "\":%s%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s%s}"), - mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", - energy_power_chr, (!isnan(energy_power_factor)) ? spfactor : "", energy_voltage_chr, energy_current_chr, (!isnan(energy_frequency)) ? sfrequency : ""); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" D_JSON_POWERUSAGE "\":%s"), + mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", active_power_chr); + if (!energy_type_dc) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s%s"), + mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr, (!isnan(energy_frequency)) ? sfrequency : ""); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), mqtt_data, voltage_chr, current_chr); + #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry dtostrfd(energy_total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)energy_power, energy_total_chr); // PowerUsage, EnergyToday - DomoticzSensor(DZ_VOLTAGE, energy_voltage_chr); // Voltage - DomoticzSensor(DZ_CURRENT, energy_current_chr); // Current + DomoticzSensorPowerEnergy((int)energy_active_power, energy_total_chr); // PowerUsage, EnergyToday + DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage + DomoticzSensor(DZ_CURRENT, current_chr); // Current } #endif // USE_DOMOTICZ #ifdef USE_KNX if (show_energy_period) { KnxSensor(KNX_ENERGY_VOLTAGE, energy_voltage); KnxSensor(KNX_ENERGY_CURRENT, energy_current); - KnxSensor(KNX_ENERGY_POWER, energy_power); - if (!isnan(energy_power_factor)) { KnxSensor(KNX_ENERGY_POWERFACTOR, energy_power_factor); } + KnxSensor(KNX_ENERGY_POWER, energy_active_power); + if (!energy_type_dc) { KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor); } KnxSensor(KNX_ENERGY_DAILY, energy_daily); KnxSensor(KNX_ENERGY_TOTAL, energy_total); KnxSensor(KNX_ENERGY_START, energy_start); @@ -612,9 +641,11 @@ void EnergyShow(boolean json) #endif // USE_KNX #ifdef USE_WEBSERVER } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, energy_voltage_chr, energy_current_chr, energy_power_chr); - if (!isnan(energy_power_factor)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, energy_power_factor_chr); } - if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, energy_frequency_chr); } + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, voltage_chr, current_chr, active_power_chr); + if (!energy_type_dc) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr); + if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, frequency_chr); } + } snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS4, mqtt_data, energy_daily_chr, energy_yesterday_chr, energy_total_chr); #endif // USE_WEBSERVER } diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 303207c10..b13dfc771 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -376,6 +376,25 @@ void RulesEvery50ms() RulesProcessEvent(json_event); } } + } else { + // Boot time POWER OUTPUTS (Relays) Status + for (byte i = 0; i < devices_present; i++) { + uint8_t new_state = (rules_new_power >> i) &1; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + // Boot time SWITCHES Status + for (byte i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { +#else + if (pin[GPIO_SWT1 +i] < 99) { +#endif // USE_TM1638 + boolean swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (swm ^ lastwallswitch[i])); + RulesProcessEvent(json_event); + } + } } rules_old_power = rules_new_power; } @@ -425,7 +444,7 @@ void RulesEvery50ms() void RulesEvery100ms() { - if (Settings.rule_enabled) { // Any rule enabled + if (Settings.rule_enabled && (uptime > 4)) { // Any rule enabled and allow 4 seconds start-up time for sensors (#3811) mqtt_data[0] = '\0'; int tele_period_save = tele_period; tele_period = 2; // Do not allow HA updates during next function call diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index b085947ae..c4e5d833b 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -76,7 +76,7 @@ void HAssDiscoverRelay() for (int i = 1; i <= MAX_RELAYS; i++) { is_light = ((i == devices_present) && (light_type)); - is_topic_light = Settings.flag.hass_light; + is_topic_light = Settings.flag.hass_light || is_light; mqtt_data[0] = '\0'; // Clear retained message diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 5140d6596..025ccff44 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -111,9 +111,9 @@ void HlwEvery200ms() if (hlw_cf_pulse_length && energy_power_on && !hlw_load_off) { hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_pulse_length; - energy_power = (float)hlw_w / 10; + energy_active_power = (float)hlw_w / 10; } else { - energy_power = 0; + energy_active_power = 0; } hlw_cf1_timer++; @@ -142,7 +142,7 @@ void HlwEvery200ms() hlw_cf1_current_pulse_length = hlw_cf1_pulse_length; hlw_cf1_current_max_pulse_counter = hlw_cf1_pulse_counter; - if (hlw_cf1_current_pulse_length && energy_power) { // No current if no power being consumed + if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length; energy_current = (float)hlw_i / 1000; } else { @@ -217,7 +217,6 @@ void HlwDrvInit() { if (!energy_flg) { if ((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) { // Sonoff Pow or any HLW8012 based device - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_01; } } diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index 8ba1b146e..1be231192 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -95,14 +95,14 @@ void CseReceived() if (adjustement & 0x10) { // Power valid cse_power_invalid = 0; if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range - energy_power = 0; + energy_active_power = 0; } else { if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle if (power_cycle_first != power_cycle) { power_cycle_first = -1; - energy_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; + energy_active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; } else { - energy_power = 0; + energy_active_power = 0; } } } else { @@ -110,11 +110,11 @@ void CseReceived() cse_power_invalid++; } else { power_cycle_first = 0; - energy_power = 0; // Powered on but no load + energy_active_power = 0; // Powered on but no load } } if (adjustement & 0x20) { // Current valid - if (0 == energy_power) { + if (0 == energy_active_power) { energy_current = 0; } else { energy_current = (float)Settings.energy_current_calibration / (float)current_cycle; @@ -123,7 +123,7 @@ void CseReceived() } else { // Powered off power_cycle_first = 0; energy_voltage = 0; - energy_power = 0; + energy_active_power = 0; energy_current = 0; } } @@ -180,7 +180,7 @@ void CseEverySecond() } else { cf_frequency = cf_pulses - cf_pulses_last_time; } - if (cf_frequency && energy_power) { + if (cf_frequency && energy_active_power) { cf_pulses_last_time = cf_pulses; energy_kWhtoday_delta += (cf_frequency * Settings.energy_power_calibration) / 36; EnergyUpdateToday(); @@ -194,7 +194,6 @@ void CseDrvInit() if ((SONOFF_S31 == Settings.module) || (SONOFF_POW_R2 == Settings.module)) { // Sonoff S31 or Sonoff Pow R2 baudrate = 4800; serial_config = SERIAL_8E1; - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_02; } } diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 03581862c..3dfbc3879 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -177,7 +177,7 @@ void PzemEvery200ms() energy_current = value; break; case 3: // Power as 20W - energy_power = value; + energy_active_power = value; break; case 4: // Total energy as 99999Wh if (!energy_start || (value < energy_start)) energy_start = value; // Init after restart and hanlde roll-over if any @@ -215,7 +215,6 @@ void PzemDrvInit() { if (!energy_flg) { if ((pin[GPIO_PZEM_RX] < 99) && (pin[GPIO_PZEM_TX] < 99)) { // Any device with a Pzem004T - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_03; } } diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index f2ce321bc..d962ef204 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -448,8 +448,8 @@ void McpParseData(void) if (energy_power_on) { // Powered on energy_frequency = (float)mcp_line_frequency / 1000; energy_voltage = (float)mcp_voltage_rms / 10; - energy_power = (float)mcp_active_power / 100; - if (0 == energy_power) { + energy_active_power = (float)mcp_active_power / 100; + if (0 == energy_active_power) { energy_current = 0; } else { energy_current = (float)mcp_current_rms / 10000; @@ -457,7 +457,7 @@ void McpParseData(void) } else { // Powered off energy_frequency = 0; energy_voltage = 0; - energy_power = 0; + energy_active_power = 0; energy_current = 0; } } @@ -557,7 +557,6 @@ void McpDrvInit(void) mcp_calibrate = 0; mcp_timeout = 2; // Initial wait mcp_init = 2; // Initial setup steps - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_04; } } diff --git a/sonoff/xnrg_05_pzem2.ino b/sonoff/xnrg_05_pzem2.ino index b7e1eab42..bb0f4864d 100644 --- a/sonoff/xnrg_05_pzem2.ino +++ b/sonoff/xnrg_05_pzem2.ino @@ -141,12 +141,13 @@ void Pzem2Every200ms() float energy = 0; if (PZEM2_TYPES_003_017 == pzem2_type) { + energy_type_dc = true; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // FE 04 10 27 10 00 64 03 E8 00 00 00 00 00 00 00 00 00 00 HH LL = PZEM-017 // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V energy_current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - energy_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W + energy_active_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W energy = (float)((uint32_t)buffer[13] << 24 + (uint32_t)buffer[14] << 16 + (uint32_t)buffer[11] << 8 + buffer[12]); // 4294967295 Wh if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and hanlde roll-over if any energy_kWhtoday += (energy - energy_start) * 100; @@ -154,12 +155,13 @@ void Pzem2Every200ms() EnergyUpdateToday(); } else if (PZEM2_TYPES_014_016 == pzem2_type) { // PZEM-014,016 + energy_type_dc = false; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // FE 04 14 08 98 03 E8 00 00 08 98 00 00 00 00 00 00 01 F4 00 64 00 00 HH LL = PZEM-014 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V energy_current = (float)((uint32_t)buffer[7] << 24 + (uint32_t)buffer[8] << 16 + (uint32_t)buffer[5] << 8 + buffer[6]) / 1000.0; // 4294967.000 A - energy_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W + energy_active_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W energy_frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz energy_power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 energy = (float)((uint32_t)buffer[15] << 24 + (uint32_t)buffer[16] << 16 + (uint32_t)buffer[13] << 8 + buffer[14]); // 4294967295 Wh diff --git a/sonoff/xsns_11_veml6070.ino b/sonoff/xsns_11_veml6070.ino index be7d3daa0..f60c0f00f 100644 --- a/sonoff/xsns_11_veml6070.ino +++ b/sonoff/xsns_11_veml6070.ino @@ -30,7 +30,7 @@ -------------------------------------------------------------------------------------------- Version Date Action Description -------------------------------------------------------------------------------------------- - + 1.0.0.1 20180925 tests - all tests are done with 1x sonoff sv, 2x Wemos D1 (not the mini) - 3 different VEMl6070 sensors from 3 different online shops - all the last three test where good and all looks working so far @@ -43,7 +43,7 @@ cleaned - source code a little bit added - missing void in function calls: void name(void) added - UV Risk level now defined as UV Index, 0.00 based on NASA standard with text behind the value - added - UV Power level now named as UV Power, used W/m2 because official standards + added - UV Power level now named as UV Power, used W/m2 because official standards added - automatic fill of the uv-risk compare table based on the coefficient calculation added - suspend and wakeup mode for the uv seonsor - current drain in wake-up-ed mode was around 180uA incl. I2C bus @@ -51,7 +51,7 @@ changed - 2x the power calculation about some incorrent data sheet values changed - float to double calculation because a rare effect on uv compare map filling - in that case @andrethomas was a big help too (while(work){output=lot_of_fun};) - added - USE_VEML6070_RSET + added - USE_VEML6070_RSET - in user_config as possible input, different resistor values depending on PCB types added - USE_VEML6070_SHOW_RAW - in user_config, show or show-NOT the uv raw value @@ -62,12 +62,12 @@ safety - personal, please read this: http://www.segurancaetrabalho.com.br/download/uv_index_karel_vanicek.pdf next - possible i will add the calculation for LAT and LONG coordinates for much more precission - show not only the UV Power value in W/m2, possible a @define value to show it as joule value - - add a #define to select how many characters are shown benhind the decimal point for the UV Index + - add a #define to select how many characters are shown benhind the decimal point for the UV Index --- 1.0.0.0 20180912 started - further development by mike2nl - https://github.com/mike2nl/Sonoff-Tasmota forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota base - code base from arendst too - + */ #ifdef USE_I2C @@ -87,8 +87,8 @@ #define VEML6070_RSET_DEFAULT 270000 // 270K default resistor value 270000 ohm, range from 220K..1Meg #define VEML6070_UV_MAX_INDEX 15 // normal 11, internal on weather laboratories and NASA it's 15 so far the sensor is linear #define VEML6070_UV_MAX_DEFAULT 11 // 11 = public default table values -#define VEML6070_POWER_COEFFCIENT 0.025 // based on calculations from Karel Vanicek and reorder by hand -#define VEML6070_TABLE_COEFFCIENT 32.86270591 // calculated by hand with help from a friend of mine, a professor which works in aero space things +#define VEML6070_POWER_COEFFCIENT 0.025 // based on calculations from Karel Vanicek and reorder by hand +#define VEML6070_TABLE_COEFFCIENT 32.86270591 // calculated by hand with help from a friend of mine, a professor which works in aero space things // (resistor, differences, power coefficients and official UV index calculations (LAT & LONG will be added later) /********************************************************************************************/ @@ -110,7 +110,7 @@ void Veml6070Detect(void) Wire.beginTransmission(veml6070_address); Wire.write((itime << 2) | 0x02); uint8_t status = Wire.endTransmission(); - + if (!status) { veml6070_type = 1; snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070", veml6070_address); @@ -130,7 +130,7 @@ void Veml6070ModeCmd(boolean mode_cmd) } else { opmode = VEML6070_DISABLE; } - + veml6070_address = VEML6070_ADDR_L; Wire.beginTransmission(veml6070_address); Wire.write((opmode << 0) | 0x02 | (itime << 2)); @@ -165,7 +165,7 @@ double Veml6070UvRiskLevel(uint16_t uv_level) { double risk = 0; double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - + // fill the uv-risk compare table based on the coefficient calculation for (uint8_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { #ifdef USE_VEML6070_RSET @@ -179,7 +179,7 @@ double Veml6070UvRiskLevel(uint16_t uv_level) #else uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT) * (i+1); #endif - } + } // get the uv-risk level if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { @@ -223,8 +223,8 @@ void Veml6070Show(boolean json) { if (veml6070_type) { // wakeup the sensor - Veml6070ModeCmd(1); - + Veml6070ModeCmd(1); + // get values from functions uint16_t uvlevel = Veml6070ReadUv(); double uvrisk = Veml6070UvRiskLevel(uvlevel); @@ -243,7 +243,7 @@ void Veml6070Show(boolean json) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"VEML6070\":{\"" D_JSON_UV_INDEX "\":%s}"), mqtt_data, str_uvrisk); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"VEML6070\":{\"" D_JSON_UV_POWER "\":%s}"), mqtt_data, str_uvpower); #ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel) }; + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); }; #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { @@ -270,7 +270,7 @@ void Veml6070Show(boolean json) } else { // else for Unknown or Out Of Range error = 99 snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_UV_INDEX7, mqtt_data, str_uvrisk); - } + } snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_UV_POWER, mqtt_data, str_uvpower); #endif // USE_WEBSERVER } diff --git a/tools/decode-config.py b/tools/decode-config.py index 299f152c4..dc51e0f42 100644 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -21,7 +21,7 @@ Requirements: - Python - - pip json pycurl urllib2 configargparse + - pip install json pycurl urllib2 configargparse Instructions: Execute command with option -d to retrieve config data from device or @@ -31,10 +31,11 @@ Instructions: Usage: - decode-config.py [-h] [-f ] [-d ] - [-u ] [-p ] [--format ] - [--sort ] [--raw] [--unhide-pw] [-o ] - [-c ] [-V] + decode-config.py [-h] [-f ] [-d ] [-u ] + [-p ] [--format ] + [--json-indent ] [--json-compact] + [--sort ] [--raw] [--unhide-pw] [-o ] + [-c ] [-V] Decode configuration of Sonoff-Tasmota device. Args that start with '--' (eg. -f) can also be set in a config file (specified via -c). Config file syntax @@ -46,21 +47,27 @@ Usage: -h, --help show this help message and exit -c , --config Config file, can be used instead of command parameter - (defaults to None) + (default: None) source: -f , --file file to retrieve Tasmota configuration from (default: None) - -d , --device - device to retrieve configuration from (default: None) + -d , --device + hostname or IP address to retrieve Tasmota + configuration from (default: None) -u , --username - for -d usage: http access username (default: admin) + host http access username (default: admin) -p , --password - for -d usage: http access password (default: None) + host http access password (default: None) output: --format output format ("json" or "text", default: "json") + --json-indent + pretty-printed JSON output using indent level + (default: "None") + --json-compact compact JSON output by eliminate whitespace (default: + "not compact") --sort sort result - can be "none" or "name" (default: "name") --raw output raw values (default: processed) @@ -72,7 +79,7 @@ Usage: info: -V, --version show program's version number and exit - Note: Either argument -d or -f must be given. + Either argument -d or -f must be given. Examples: @@ -113,7 +120,7 @@ except ImportError: sys.exit(9) -VER = '1.5.0008' +VER = '1.5.0009' PROG='{} v{} by Norbert Richter'.format(os.path.basename(sys.argv[0]),VER) CONFIG_FILE_XOR = 0x5A @@ -135,6 +142,8 @@ DEFAULTS = { 'output': { 'format': 'json', + 'jsonindent': None, + 'jsoncompact': False, 'sort': 'name', 'raw': False, 'unhide-pw': False, @@ -940,7 +949,7 @@ Setting_5_14_0 = { 'knx_CB_addr': ('3: + if not raw and len(fielddef)>3: if isinstance(fielddef[3],str): # use a format string return fielddef[3].format(value) elif callable(fielddef[3]): # use a format function @@ -1685,7 +1696,7 @@ def ConvertFieldValue(value, fielddef): return value -def GetField(dobj, fieldname, fielddef): +def GetField(dobj, fieldname, fielddef, raw=False): """ Get field value from definition @@ -1695,6 +1706,8 @@ def GetField(dobj, fieldname, fielddef): name of the field @param fielddef: see Settings desc above + @param raw + return raw values (True) or converted values (False) @return: read field value """ @@ -1715,13 +1728,13 @@ def GetField(dobj, fieldname, fielddef): subfielddef = (fielddef[0], addr, None, None if len(fielddef)<4 else fielddef[3]) length = GetFieldLength(subfielddef) if length != 0: - result.append(GetField(dobj, fieldname, subfielddef)) + result.append(GetField(dobj, fieldname, subfielddef, raw)) addr += length # tuple 2 contains a list with dict elif isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], dict): d = {} value = struct.unpack_from(fielddef[0], dobj, fielddef[1])[0] - d['base'] = ConvertFieldValue(value, fielddef); + d['base'] = ConvertFieldValue(value, fielddef, raw); union = fielddef[2] i = 0 for l in union: @@ -1738,8 +1751,8 @@ def GetField(dobj, fieldname, fielddef): if ord(result[:1])==0x00 or ord(result[:1])==0xff: result = '' s = str(result).split('\0')[0] - result = s #unicode(s, errors='replace') - result = ConvertFieldValue(result, fielddef) + result = unicode(s, errors='replace') + result = ConvertFieldValue(result, fielddef, raw) return result @@ -1762,40 +1775,52 @@ def DeEncrypt(obj): def Decode(obj): """ - Decodes (already decrypted) binary data stream + Decodes binary data stream @param obj: - binary config data + binary config data (decrypted) """ # get header data - cfg_size = GetField(obj, 'cfg_size', Setting_6_2_1['cfg_size']) - version = GetField(obj, 'version', Setting_6_2_1['version']) + version = GetField(obj, 'version', Setting_6_2_1['version'], raw=True) # search setting definition - setting = None + template = None for cfg in Settings: - if version >= cfg[0] and cfg_size == cfg[1]: + if version >= cfg[0]: template = cfg break - setting = template[2] # if we did not found a mathching setting - if setting is None: - exit(2, "Can't handle Tasmota configuration data for version 0x{:x} with {} bytes".format(version, cfg_size) ) + if template is None: + exit(2, "Can't handle Tasmota configuration data for version 0x{:x}".format(version) ) - if GetField(obj, 'cfg_crc', setting['cfg_crc']) != GetSettingsCrc(obj): + setting = template[2] + + # check size if exists + if 'cfg_size' in setting: + cfg_size = GetField(obj, 'cfg_size', setting['cfg_size'], raw=True) + # if we did not found a mathching setting + if cfg_size != template[1]: + exit(2, "Data size does not match. Expected {} bytes, read {} bytes.".format(template[1], cfg_size) ) + + # check crc if exists + if 'cfg_crc' in setting: + cfg_crc = GetField(obj, 'cfg_crc', setting['cfg_crc'], raw=True) + else: + cfg_crc = GetSettingsCrc(obj) + if cfg_crc != GetSettingsCrc(obj): exit(3, 'Data crc error' ) config = {} config['version_template'] = '0x{:x}'.format(template[0]) for name in setting: - config[name] = GetField(obj, name, setting[name]) + config[name] = GetField(obj, name, setting[name], args.raw) if args.sort == 'name': config = collections.OrderedDict(sorted(config.items())) if args.format == 'json': - print json.dumps(config, sort_keys=args.sort=='name') + print json.dumps(config, sort_keys=args.sort=='name', indent=args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') ) else: for key,value in config.items(): print '{} = {}'.format(key, repr(value)) @@ -1804,7 +1829,7 @@ def Decode(obj): if __name__ == "__main__": parser = configargparse.ArgumentParser(description='Decode configuration of Sonoff-Tasmota device.', - epilog='Note: Either argument -d or -f must be given.') + epilog='Either argument -d or -f must be given.') source = parser.add_argument_group('source') source.add_argument('-f', '--file', @@ -1813,20 +1838,20 @@ if __name__ == "__main__": default=DEFAULTS['source']['tasmotafile'], help='file to retrieve Tasmota configuration from (default: {})'.format(DEFAULTS['source']['tasmotafile'])) source.add_argument('-d', '--device', - metavar='', + metavar='', dest='device', default=DEFAULTS['source']['device'], - help='device to retrieve configuration from (default: {})'.format(DEFAULTS['source']['device']) ) + help='hostname or IP address to retrieve Tasmota configuration from (default: {})'.format(DEFAULTS['source']['device']) ) source.add_argument('-u', '--username', metavar='', dest='username', default=DEFAULTS['source']['username'], - help='for -d usage: http access username (default: {})'.format(DEFAULTS['source']['username'])) + help='host http access username (default: {})'.format(DEFAULTS['source']['username'])) source.add_argument('-p', '--password', metavar='', dest='password', default=DEFAULTS['source']['password'], - help='for -d usage: http access password (default: {})'.format(DEFAULTS['source']['password'])) + help='host http access password (default: {})'.format(DEFAULTS['source']['password'])) output = parser.add_argument_group('output') output.add_argument('--format', @@ -1835,6 +1860,17 @@ if __name__ == "__main__": choices=['json', 'text'], default=DEFAULTS['output']['format'], help='output format ("json" or "text", default: "{}")'.format(DEFAULTS['output']['format']) ) + output.add_argument('--json-indent', + metavar='', + dest='jsonindent', + type=int, + default=DEFAULTS['output']['jsonindent'], + help='pretty-printed JSON output using indent level (default: "{}")'.format(DEFAULTS['output']['jsonindent']) ) + output.add_argument('--json-compact', + dest='jsoncompact', + action='store_true', + default=DEFAULTS['output']['jsoncompact'], + help='compact JSON output by eliminate whitespace (default: "{}")'.format('compact' if DEFAULTS['output']['jsoncompact'] else 'not compact') ) output.add_argument('--sort', metavar='', dest='sort', @@ -1862,7 +1898,7 @@ if __name__ == "__main__": dest='configfile', default=DEFAULTS['DEFAULT']['configfile'], is_config_file=True, - help='Config file, can be used instead of command parameter (defaults to {})'.format(DEFAULTS['DEFAULT']['configfile']) ) + help='Config file, can be used instead of command parameter (default: {})'.format(DEFAULTS['DEFAULT']['configfile']) ) info = parser.add_argument_group('info') info.add_argument('-V', '--version', action='version', version=PROG) @@ -1921,4 +1957,7 @@ if __name__ == "__main__": Decode(cfg) else: - exit(4, "Could not read configuration data from {} '{}'".format('device' if args.device is not None else 'file', args.device if args.device is not None else args.tasmotafile) ) \ No newline at end of file + exit(4, "Could not read configuration data from {} '{}'".format('device' if args.device is not None else 'file', \ + args.device if args.device is not None else args.tasmotafile) ) + + sys.exit(0)