Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2018-08-27 22:50:37 +02:00
commit 0bcaf7409a
18 changed files with 752 additions and 171 deletions

View File

@ -1,3 +1,6 @@
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0)
#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // This Library will only work with ARDUINO_ESP8266_RELEASE_2_4_0 and up
#include "Arduino.h"
#include "ESPAsyncUDP.h"
@ -425,3 +428,5 @@ size_t AsyncUDP::broadcast(AsyncUDPMessage &message)
}
return broadcast(message.data(), message.length());
}
#endif

View File

@ -7,14 +7,6 @@
#ifndef ESP_KNX_IP_H
#define ESP_KNX_IP_H
//#define USE_ASYNC_UDP // UDP WIFI Library Selection for Multicast
// If commented out, the esp-knx-ip library will use WIFI_UDP Library that is compatible with ESP8266 Library Version 2.3.0 and up
// If not commented out, the esp-knx-ip library will use ESPAsyncUDP Library that is compatible with ESP8266 Library Version 2.4.0 and up
// The ESPAsyncUDP Library have a more reliable multicast communication
// Please Use it with Patch (https://github.com/me-no-dev/ESPAsyncUDP/pull/21) )
// check line 57 on esp-knx-ip.h file is uncommented: #include <ESPAsyncUDP.h>
// Comment out that line when using UDP WIFI to avoid compiling issues on PlatformIO with ESP8266 Library Version 2.3.0
/**
* CONFIG
* All MAX_ values must not exceed 255 (1 byte, except MAC_CONFIG_SPACE which can go up to 2 bytes, so 0xffff in theory) and must not be negative!
@ -54,8 +46,13 @@
#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0)
#ifndef ARDUINO_ESP8266_RELEASE_2_3_0
#define USE_ASYNC_UDP // UDP WIFI Library Selection for Multicast
#endif
#ifdef USE_ASYNC_UDP
//#include <ESPAsyncUDP.h>
#include <ESPAsyncUDP.h>
#else
#include <WiFiUdp.h>
#endif

View File

@ -52,14 +52,28 @@ framework = arduino
board = esp01_1m
board_build.flash_mode = dout
; set CPU frequency to 80MHz (default) or 160MHz
board_build.f_cpu = 80000000L
;board_build.f_cpu = 160000000L
; *** Fix espressif8266@1.7.0 induced undesired all warnings
build_unflags = -Wall
build_flags =
-Wl,-Tesp8266.flash.1m0.ld
-DVTABLES_IN_FLASH
; -DUSE_CONFIG_OVERRIDE
; lwIP 1.4 (Default)
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; -DUSE_CONFIG_OVERRIDE
; lwIP 2 - Low Memory
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
; lwIP 2 - Higher Bandwitdh
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
; VTABLES in Flash (default)
-DVTABLES_IN_FLASH
; VTABLES in Heap
; -DVTABLES_IN_DRAM
; VTABLES in IRAM
; -DVTABLES_IN_IRAM
; *** Serial Monitor options
monitor_speed = 115200
@ -68,6 +82,7 @@ monitor_speed = 115200
upload_speed = 115200
upload_resetmethod = nodemcu
upload_port = COM5
; *** Fix Esp/Arduino core 2.4.x induced Tasmota unused floating point includes
extra_scripts = pio/strip-floats.py
@ -86,6 +101,7 @@ 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}
monitor_speed = ${common.monitor_speed}
@ -99,6 +115,7 @@ 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} -DBE_MINIMAL
monitor_speed = ${common.monitor_speed}
@ -112,6 +129,7 @@ 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_CLASSIC
monitor_speed = ${common.monitor_speed}
@ -125,6 +143,7 @@ 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_KNX_NO_EMULATION
monitor_speed = ${common.monitor_speed}
@ -138,6 +157,7 @@ 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_ALL_SENSORS
monitor_speed = ${common.monitor_speed}
@ -151,6 +171,7 @@ 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} -DMY_LANGUAGE=bg-BG
monitor_speed = ${common.monitor_speed}
@ -164,6 +185,7 @@ 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} -DMY_LANGUAGE=pt-BR
monitor_speed = ${common.monitor_speed}
@ -177,6 +199,7 @@ 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} -DMY_LANGUAGE=zh-CN
monitor_speed = ${common.monitor_speed}
@ -190,6 +213,7 @@ 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} -DMY_LANGUAGE=cs-CZ
monitor_speed = ${common.monitor_speed}
@ -203,6 +227,7 @@ 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} -DMY_LANGUAGE=de-DE
monitor_speed = ${common.monitor_speed}
@ -216,6 +241,7 @@ 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} -DMY_LANGUAGE=es-AR
monitor_speed = ${common.monitor_speed}
@ -229,6 +255,7 @@ 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} -DMY_LANGUAGE=fr-FR
monitor_speed = ${common.monitor_speed}
@ -242,6 +269,7 @@ 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} -DMY_LANGUAGE=el-GR
monitor_speed = ${common.monitor_speed}
@ -255,6 +283,7 @@ 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} -DMY_LANGUAGE=hu-HU
monitor_speed = ${common.monitor_speed}
@ -268,6 +297,7 @@ 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} -DMY_LANGUAGE=it-IT
monitor_speed = ${common.monitor_speed}
@ -281,6 +311,7 @@ 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} -DMY_LANGUAGE=nl-NL
monitor_speed = ${common.monitor_speed}
@ -294,6 +325,7 @@ 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} -DMY_LANGUAGE=pl-PL
monitor_speed = ${common.monitor_speed}
@ -307,6 +339,7 @@ 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} -DMY_LANGUAGE=pt-PT
monitor_speed = ${common.monitor_speed}
@ -320,6 +353,7 @@ 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} -DMY_LANGUAGE=ru-RU
monitor_speed = ${common.monitor_speed}
@ -333,6 +367,7 @@ 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} -DMY_LANGUAGE=tr-TR
monitor_speed = ${common.monitor_speed}
@ -346,6 +381,7 @@ 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} -DMY_LANGUAGE=zh-TW
monitor_speed = ${common.monitor_speed}
@ -359,6 +395,7 @@ 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} -DMY_LANGUAGE=uk-UK
monitor_speed = ${common.monitor_speed}

View File

@ -1,4 +1,10 @@
/* 6.1.1.11 20180826
/* 6.1.1.12 20180827
* Add commands ButtonDebounce 40..1000 and SwitchDebounce 40..1000 to have user control over debounce timing. Default is 50mS (#3594)
* Add rule variables %sunrise%, %sunset%, %uptime% and %time% (#3608)
* Fix handling use of default names when using names starting with shortcut character (#3392, #3600)
* Fix Sonoff Bridge data reception when using Portisch EFM8 firmware and in data buffer length (#3605)
*
* 6.1.1.11 20180826
* Change scheduler phase 1/3 - Fixed when sleep is enabled: Uptime, Delay, PulseTime and TelePeriod (#3581)
* Change scheduler phase 2/3 - Fixed when sleep is enabled: Blinktime (#3581)
* Change scheduler phase 3/3 - Some sensor update timings: AdcEvery 200 -> 250, Senseair 300 -> 250, SDM120 300 -> 250, SDM630 300 -> 250

View File

@ -192,6 +192,8 @@
#define D_CMND_COUNTER "Counter"
#define D_CMND_COUNTERTYPE "CounterType"
#define D_CMND_COUNTERDEBOUNCE "CounterDebounce"
#define D_CMND_BUTTONDEBOUNCE "ButtonDebounce"
#define D_CMND_SWITCHDEBOUNCE "SwitchDebounce"
#define D_CMND_SLEEP "Sleep"
#define D_CMND_UPLOAD "Upload"
#define D_CMND_UPGRADE "Upgrade"

View File

@ -297,9 +297,7 @@ struct SYSCFG {
char ntp_server[3][33]; // 4CE
byte ina219_mode; // 531
uint16_t pulse_timer[MAX_PULSETIMERS]; // 532
byte free542[2]; // 542
uint16_t button_debounce; // 542
uint32_t ip_address[4]; // 544
unsigned long energy_kWhtotal; // 554
char mqtt_fulltopic[100]; // 558
@ -309,8 +307,9 @@ struct SYSCFG {
uint16_t pulse_counter_debounce; // 5D2
uint8_t rf_code[17][9]; // 5D4
byte free_66d[3]; // 66D
byte free_66d[1]; // 66D
uint16_t switch_debounce; // 66E
Timer timer[MAX_TIMERS]; // 670
int latitude; // 6B0
int longitude; // 6B4
@ -321,7 +320,9 @@ struct SYSCFG {
byte knx_CB_param[MAX_KNX_CB]; // 6EC Type of Output (set relay, toggle relay, reply sensor value)
Mcp230xxCfg mcp230xx_config[16]; // 6F6
uint8_t mcp230xx_int_prio; // 716
byte free_717; // 717
byte free_717[1]; // 717
uint16_t mcp230xx_int_timer; // 718
byte free_71A[180]; // 71A

View File

@ -588,6 +588,9 @@ void SettingsDefaultSet2()
Settings.latitude = (int)((double)LATITUDE * 1000000);
Settings.longitude = (int)((double)LONGITUDE * 1000000);
SettingsDefaultSet_5_13_1c(); // Time STD/DST settings
Settings.button_debounce = KEY_DEBOUNCE_TIME;
Settings.switch_debounce = SWITCH_DEBOUNCE_TIME;
}
/********************************************************************************************/
@ -787,6 +790,10 @@ void SettingsDelta()
if (Settings.version < 0x06010103) {
Settings.flag3.timers_enable = 1;
}
if (Settings.version < 0x0601010C) {
Settings.button_debounce = KEY_DEBOUNCE_TIME;
Settings.switch_debounce = SWITCH_DEBOUNCE_TIME;
}
Settings.version = VERSION;
SettingsSave(1);

View File

@ -83,7 +83,7 @@ typedef unsigned long power_t; // Power (Relay) type
#define SAFE_POWER_WINDOW 30 // Time in MINUTES to disable allow max unit safe power
#define MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow
#define STATES 20 // State loops per second
#define STATES 20 // Number of states per second using 50 mSec interval
#define IMMINENT_RESET_FACTOR 10 // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40
#define SYSLOG_TIMER 600 // Seconds to restore syslog_level
#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog
@ -187,7 +187,9 @@ enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK
enum PowerOnStateOptions { POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON };
enum ButtonStates {PRESSED, NOT_PRESSED};
enum ButtonStates { PRESSED, NOT_PRESSED };
enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER };
enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_MAX_PARAM8};

View File

@ -79,7 +79,7 @@ enum TasmotaCommands {
CMND_BLINKTIME, CMND_BLINKCOUNT, CMND_SENSOR, CMND_SAVEDATA, CMND_SETOPTION, CMND_TEMPERATURE_RESOLUTION, CMND_HUMIDITY_RESOLUTION,
CMND_PRESSURE_RESOLUTION, CMND_POWER_RESOLUTION, CMND_VOLTAGE_RESOLUTION, CMND_CURRENT_RESOLUTION, CMND_ENERGY_RESOLUTION, CMND_MODULE, CMND_MODULES,
CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE,
CMND_COUNTERDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG,
CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG,
CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME,
CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE,
CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE,
@ -89,7 +89,7 @@ const char kTasmotaCommands[] PROGMEM =
D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|"
D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_MODULE "|" D_CMND_MODULES "|"
D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|"
D_CMND_COUNTERDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|"
D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|"
D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|"
D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|"
D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|"
@ -98,52 +98,7 @@ const char kTasmotaCommands[] PROGMEM =
const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}};
// Global variables
unsigned long feature_drv1; // Compiled driver feature map
unsigned long feature_drv2; // Compiled driver feature map
unsigned long feature_sns1; // Compiled sensor feature map
unsigned long feature_sns2; // Compiled sensor feature map
int baudrate = APP_BAUDRATE; // Serial interface baud rate
SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit
byte serial_in_byte; // Received byte
uint8_t serial_local = 0; // Handle serial locally;
unsigned long serial_polling_window = 0; // Serial polling window
int serial_in_byte_counter = 0; // Index in receive buffer
byte dual_hex_code = 0; // Sonoff dual input flag
uint16_t dual_button_code = 0; // Sonoff dual received code
int16_t save_data_counter; // Counter and flag for config save to Flash
uint8_t fallback_topic_flag = 0; // Use Topic or FallbackTopic
unsigned long state_second = 0; // State second timer
unsigned long state_50msecond = 0; // State 50msecond timer
unsigned long state_100msecond = 0; // State 100msecond timer
unsigned long state_250msecond = 0; // State 250msecond timer
uint8_t state_250mS = 0; // State 250msecond per second flag
int ota_state_flag = 0; // OTA state flag
int ota_result = 0; // OTA result
byte ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter
char *ota_url; // OTA url string
int restart_flag = 0; // Sonoff restart flag
int wifi_state_flag = WIFI_RESTART; // Wifi state flag
uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year
boolean latest_uptime_flag = true; // Signal latest uptime
int tele_period = 0; // Tele period timer
byte web_log_index = 1; // Index in Web log buffer (should never be 0)
byte reset_web_log_flag = 0; // Reset web console log
byte devices_present = 0; // Max number of devices supported
int status_update_timer = 0; // Refresh initial status
unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer
unsigned long blink_timer = 0; // Power cycle timer
uint16_t blink_counter = 0; // Number of blink cycles
power_t blink_power; // Blink power state
power_t blink_mask = 0; // Blink relay active mask
power_t blink_powersave; // Blink start power save state
uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command
power_t latching_power = 0; // Power state at latching start
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
uint8_t backlog_index = 0; // Command backlog index
uint8_t backlog_pointer = 0; // Command backlog pointer
uint8_t backlog_mutex = 0; // Command backlog pending
unsigned long backlog_delay = 0; // Command backlog delay
uint8_t interlock_mutex = 0; // Interlock power command pending
#ifdef USE_MQTT_TLS
WiFiClientSecure EspClient; // Wifi Secure Client
@ -153,30 +108,67 @@ uint8_t interlock_mutex = 0; // Interlock power command pending
WiFiUDP PortUdp; // UDP Syslog and Alexa
unsigned long feature_drv1; // Compiled driver feature map
unsigned long feature_drv2; // Compiled driver feature map
unsigned long feature_sns1; // Compiled sensor feature map
unsigned long feature_sns2; // Compiled sensor feature map
unsigned long serial_polling_window = 0; // Serial polling window
unsigned long state_second = 0; // State second timer
unsigned long state_50msecond = 0; // State 50msecond timer
unsigned long state_100msecond = 0; // State 100msecond timer
unsigned long state_250msecond = 0; // State 250msecond timer
unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer
unsigned long blink_timer = 0; // Power cycle timer
unsigned long backlog_delay = 0; // Command backlog delay
unsigned long button_debounce = 0; // Button debounce timer
unsigned long switch_debounce = 0; // Switch debounce timer
power_t power = 0; // Current copy of Settings.power
byte syslog_level; // Current copy of Settings.syslog_level
uint16_t syslog_timer = 0; // Timer to re-enable syslog_level
byte seriallog_level; // Current copy of Settings.seriallog_level
power_t blink_power; // Blink power state
power_t blink_mask = 0; // Blink relay active mask
power_t blink_powersave; // Blink start power save state
power_t latching_power = 0; // Power state at latching start
power_t rel_inverted = 0; // Relay inverted flag (1 = (0 = On, 1 = Off))
int baudrate = APP_BAUDRATE; // Serial interface baud rate
int serial_in_byte_counter = 0; // Index in receive buffer
int ota_state_flag = 0; // OTA state flag
int ota_result = 0; // OTA result
int restart_flag = 0; // Sonoff restart flag
int wifi_state_flag = WIFI_RESTART; // Wifi state flag
int tele_period = 0; // Tele period timer
int status_update_timer = 0; // Refresh initial status
int blinks = 201; // Number of LED blinks
uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year
uint32_t global_update = 0; // Timestamp of last global temperature and humidity update
float global_temperature = 0; // Provide a global temperature to be used by some sensors
float global_humidity = 0; // Provide a global humidity to be used by some sensors
char *ota_url; // OTA url string pointer
uint16_t dual_button_code = 0; // Sonoff dual received code
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
uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold
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;
uint8_t fallback_topic_flag = 0; // Use Topic or FallbackTopic
uint8_t state_250mS = 0; // State 250msecond per second flag
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
uint8_t backlog_index = 0; // Command backlog index
uint8_t backlog_pointer = 0; // Command backlog pointer
uint8_t backlog_mutex = 0; // Command backlog pending
uint8_t interlock_mutex = 0; // Interlock power command pending
uint8_t sleep; // Current copy of Settings.sleep
uint8_t stop_flash_rotate = 0; // Allow flash configuration rotation
int blinks = 201; // Number of LED blinks
uint8_t blinkstate = 0; // LED state
uint8_t blinkspeed = 1; // LED blink rate
uint8_t blockgpio0 = 4; // Block GPIO0 for 4 seconds after poweron to workaround Wemos D1 RTS circuit
uint8_t lastbutton[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states
uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold
uint8_t multiwindow[MAX_KEYS] = { 0 }; // Max time between button presses to record press count
uint8_t multipress[MAX_KEYS] = { 0 }; // Number of button presses within multiwindow
uint8_t lastwallswitch[MAX_SWITCHES]; // Last wall switch states
uint8_t holdwallswitch[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold
uint8_t virtualswitch[MAX_SWITCHES]; // Virtual switch states
mytmplt my_module; // Active copy of Module name and GPIOs
uint8_t pin[GPIO_MAX]; // Possible pin configurations
power_t rel_inverted = 0; // Relay inverted flag (1 = (0 = On, 1 = Off))
uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted)
uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup)
@ -185,16 +177,20 @@ uint8_t energy_flg = 1; // Energy monitor configured
uint8_t i2c_flg = 0; // I2C configured
uint8_t spi_flg = 0; // SPI configured
uint8_t light_type = 0; // Light types
bool pwm_present = false; // Any PWM channel configured with SetOption15 0
boolean mdns_begun = false;
uint8_t ntp_force_sync = 0; // Force NTP sync
StateBitfield global_state;
RulesBitfield rules_flag;
uint32_t global_update = 0;
float global_temperature = 0;
float global_humidity = 0;
byte serial_in_byte; // Received byte
byte dual_hex_code = 0; // Sonoff dual input flag
byte ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter
byte web_log_index = 1; // Index in Web log buffer (should never be 0)
byte reset_web_log_flag = 0; // Reset web console log
byte devices_present = 0; // Max number of devices supported
byte seriallog_level; // Current copy of Settings.seriallog_level
byte syslog_level; // Current copy of Settings.syslog_level
boolean latest_uptime_flag = true; // Signal latest uptime
boolean pwm_present = false; // Any PWM channel configured with SetOption15 0
boolean mdns_begun = false; // mDNS active
mytmplt my_module; // Active copy of Module name and GPIOs (23 x 8 bits)
StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits)
char my_version[33]; // Composed version string
char my_hostname[33]; // Composed Wifi hostname
char mqtt_client[33]; // Composed MQTT Clientname
@ -608,8 +604,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
}
else if (CMND_OTAURL == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.ota_url)))
strlcpy(Settings.ota_url, (1 == payload) ? OTA_URL : dataBuf, sizeof(Settings.ota_url));
if ((data_len > 0) && (data_len < sizeof(Settings.ota_url))) {
strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut(dataBuf)) ? OTA_URL : dataBuf, sizeof(Settings.ota_url));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.ota_url);
}
else if (CMND_SERIALLOG == command_code) {
@ -947,6 +944,18 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce);
}
else if (CMND_BUTTONDEBOUNCE == command_code) {
if ((payload > 39) && (payload < 1001)) {
Settings.button_debounce = payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.button_debounce);
}
else if (CMND_SWITCHDEBOUNCE == command_code) {
if ((payload > 39) && (payload < 1001)) {
Settings.switch_debounce = payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce);
}
else if (CMND_BAUDRATE == command_code) {
if (payload32 > 0) {
payload32 /= 1200; // Make it a valid baudrate
@ -997,12 +1006,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
else if (CMND_LOGHOST == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.syslog_host))) {
strlcpy(Settings.syslog_host, (1 == payload) ? SYS_LOG_HOST : dataBuf, sizeof(Settings.syslog_host));
strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut(dataBuf)) ? SYS_LOG_HOST : dataBuf, sizeof(Settings.syslog_host));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.syslog_host);
}
else if (CMND_LOGPORT == command_code) {
if (payload16 > 0) Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16;
if (payload16 > 0) {
Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.syslog_port);
}
else if ((CMND_IPADDRESS == command_code) && (index > 0) && (index <= 4)) {
@ -1015,7 +1026,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
else if ((CMND_NTPSERVER == command_code) && (index > 0) && (index <= 3)) {
if ((data_len > 0) && (data_len < sizeof(Settings.ntp_server[0]))) {
strlcpy(Settings.ntp_server[index -1], (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? (1==index)?NTP_SERVER1:(2==index)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(Settings.ntp_server[0]));
strlcpy(Settings.ntp_server[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1==index)?NTP_SERVER1:(2==index)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(Settings.ntp_server[0]));
for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) {
if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.';
}
@ -1039,7 +1050,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
else if ((CMND_SSID == command_code) && (index > 0) && (index <= 2)) {
if ((data_len > 0) && (data_len < sizeof(Settings.sta_ssid[0]))) {
strlcpy(Settings.sta_ssid[index -1], (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? (1 == index) ? STA_SSID1 : STA_SSID2 : dataBuf, sizeof(Settings.sta_ssid[0]));
strlcpy(Settings.sta_ssid[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_SSID1 : STA_SSID2 : dataBuf, sizeof(Settings.sta_ssid[0]));
Settings.sta_active = index -1;
restart_flag = 2;
}
@ -1047,7 +1058,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
else if ((CMND_PASSWORD == command_code) && (index > 0) && (index <= 2)) {
if ((data_len > 0) && (data_len < sizeof(Settings.sta_pwd[0]))) {
strlcpy(Settings.sta_pwd[index -1], (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0]));
strlcpy(Settings.sta_pwd[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0]));
Settings.sta_active = index -1;
restart_flag = 2;
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]);
@ -1057,7 +1068,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
else if ((CMND_HOSTNAME == command_code) && !grpflg) {
if ((data_len > 0) && (data_len < sizeof(Settings.hostname))) {
strlcpy(Settings.hostname, (1 == payload) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname));
strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut(dataBuf)) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname));
if (strstr(Settings.hostname,"%")) {
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
}
@ -1087,12 +1098,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
} else {
snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), index);
}
strlcpy(Settings.friendlyname[index -1], (1 == payload) ? stemp1 : dataBuf, sizeof(Settings.friendlyname[index -1]));
strlcpy(Settings.friendlyname[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? stemp1 : dataBuf, sizeof(Settings.friendlyname[index -1]));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]);
}
else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) {
if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) Settings.switchmode[index -1] = payload;
if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) {
Settings.switchmode[index -1] = payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]);
}
else if (CMND_TELEPERIOD == command_code) {
@ -1119,7 +1132,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
}
else if (CMND_TIMEZONE == command_code) {
if ((data_len > 0) && (((payload >= -13) && (payload <= 14)) || (99 == payload))) Settings.timezone = payload;
if ((data_len > 0) && (((payload >= -13) && (payload <= 14)) || (99 == payload))) {
Settings.timezone = payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.timezone);
}
else if ((CMND_TIMESTD == command_code) || (CMND_TIMEDST == command_code)) {
@ -1164,7 +1179,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]);
}
else if (CMND_ALTITUDE == command_code) {
if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) Settings.altitude = payload;
if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) {
Settings.altitude = payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.altitude);
}
else if (CMND_LEDPOWER == command_code) {
@ -1556,8 +1573,6 @@ void PerformEverySecond()
SetDevicePower(power, SRC_RETRY); // Set required power on state
}
if (blockgpio0) blockgpio0--;
if (seriallog_timer) {
seriallog_timer--;
if (!seriallog_timer) {
@ -1631,7 +1646,8 @@ void ButtonHandler()
{
uint8_t button = NOT_PRESSED;
uint8_t button_present = 0;
uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command
uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command
uint16_t loops_per_second = 1000 / Settings.button_debounce;
char scmnd[20];
uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present;
@ -1645,43 +1661,45 @@ void ButtonHandler()
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code);
AddLog(LOG_LEVEL_DEBUG);
button = PRESSED;
if (0xF500 == dual_button_code) { // Button hold
holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1;
if (0xF500 == dual_button_code) { // Button hold
holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1;
hold_time_extent = 1;
}
dual_button_code = 0;
}
} else {
if ((pin[GPIO_KEY1 +button_index] < 99) && !blockgpio0) {
button_present = 1;
button = digitalRead(pin[GPIO_KEY1 +button_index]);
if (pin[GPIO_KEY1 +button_index] < 99) {
if (!((uptime < 4) && (0 == pin[GPIO_KEY1 +button_index]))) { // Block GPIO0 for 4 seconds after poweron to workaround Wemos D1 RTS circuit
button_present = 1;
button = digitalRead(pin[GPIO_KEY1 +button_index]);
}
}
}
if (button_present) {
if (SONOFF_4CHPRO == Settings.module) {
if (holdbutton[button_index]) holdbutton[button_index]--;
if (holdbutton[button_index]) { holdbutton[button_index]--; }
boolean button_pressed = false;
if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1);
AddLog(LOG_LEVEL_DEBUG);
holdbutton[button_index] = STATES;
holdbutton[button_index] = loops_per_second;
button_pressed = true;
}
if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1);
AddLog(LOG_LEVEL_DEBUG);
if (!holdbutton[button_index]) button_pressed = true; // Do not allow within 1 second
if (!holdbutton[button_index]) { button_pressed = true; } // Do not allow within 1 second
}
if (button_pressed) {
if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
}
}
} else {
if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) {
if (Settings.flag.button_single) { // Allow only single button press for immediate action
if (Settings.flag.button_single) { // Allow only single button press for immediate action
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1);
AddLog(LOG_LEVEL_DEBUG);
if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
@ -1691,7 +1709,7 @@ void ButtonHandler()
multipress[button_index] = (multiwindow[button_index]) ? multipress[button_index] +1 : 1;
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, multipress[button_index]);
AddLog(LOG_LEVEL_DEBUG);
multiwindow[button_index] = STATES /2; // 0.5 second multi press window
multiwindow[button_index] = loops_per_second / 2; // 0.5 second multi press window
}
blinks = 201;
}
@ -1700,20 +1718,20 @@ void ButtonHandler()
holdbutton[button_index] = 0;
} else {
holdbutton[button_index]++;
if (Settings.flag.button_single) { // Allow only single button press for immediate action
if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10) * hold_time_extent) { // Button held for factor times longer
if (Settings.flag.button_single) { // Allow only single button press for immediate action
if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer
// Settings.flag.button_single = 0;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only
ExecuteCommand(scmnd, SRC_BUTTON);
}
} else {
if (Settings.flag.button_restrict) { // Button restriction
if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold
if (Settings.flag.button_restrict) { // Button restriction
if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // Button hold
multipress[button_index] = 0;
SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set
SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set
}
} else {
if (holdbutton[button_index] == (Settings.param[P_HOLD_TIME] * (STATES / 10)) * hold_time_extent) { // Button held for factor times longer
if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer
multipress[button_index] = 0;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd, SRC_BUTTON);
@ -1722,13 +1740,13 @@ void ButtonHandler()
}
}
if (!Settings.flag.button_single) { // Allow multi-press
if (!Settings.flag.button_single) { // Allow multi-press
if (multiwindow[button_index]) {
multiwindow[button_index]--;
} else {
if (!restart_flag && !holdbutton[button_index] && (multipress[button_index] > 0) && (multipress[button_index] < MAX_BUTTON_COMMANDS +3)) {
boolean single_press = false;
if (multipress[button_index] < 3) { // Single or Double press
if (multipress[button_index] < 3) { // Single or Double press
if ((SONOFF_DUAL_R2 == Settings.module) || (SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) {
single_press = true;
} else {
@ -1739,13 +1757,13 @@ void ButtonHandler()
if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
// Success
} else {
if (multipress[button_index] < 3) { // Single or Double press
if (WifiState()) { // WPSconfig, Smartconfig or Wifimanager active
if (multipress[button_index] < 3) { // Single or Double press
if (WifiState()) { // WPSconfig, Smartconfig or Wifimanager active
restart_flag = 1;
} else {
ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
}
} else { // 3 - 7 press
} else { // 3 - 7 press
if (!Settings.flag.button_restrict) {
snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]);
ExecuteCommand(scmnd, SRC_BUTTON);
@ -1770,6 +1788,7 @@ void SwitchHandler(byte mode)
{
uint8_t button = NOT_PRESSED;
uint8_t switchflag;
uint16_t loops_per_second = 1000 / Settings.switch_debounce;
for (byte i = 0; i < MAX_SWITCHES; i++) {
if ((pin[GPIO_SWT1 +i] < 99) || (mode)) {
@ -1777,14 +1796,16 @@ void SwitchHandler(byte mode)
if (holdwallswitch[i]) {
holdwallswitch[i]--;
if (0 == holdwallswitch[i]) {
SendKey(1, i +1, 3); // Execute command via MQTT
SendKey(1, i +1, 3); // Execute command via MQTT
}
}
if (mode) {
button = virtualswitch[i];
} else {
button = digitalRead(pin[GPIO_SWT1 +i]);
if (!((uptime < 4) && (0 == pin[GPIO_SWT1 +i]))) { // Block GPIO0 for 4 seconds after poweron to workaround Wemos D1 RTS circuit
button = digitalRead(pin[GPIO_SWT1 +i]);
}
}
if (button != lastwallswitch[i]) {
@ -1816,7 +1837,7 @@ void SwitchHandler(byte mode)
break;
case PUSHBUTTONHOLD:
if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) {
holdwallswitch[i] = Settings.param[P_HOLD_TIME] * (STATES / 10);
holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10;
}
if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) {
holdwallswitch[i] = 0;
@ -1825,18 +1846,18 @@ void SwitchHandler(byte mode)
break;
case PUSHBUTTONHOLD_INV:
if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) {
holdwallswitch[i] = Settings.param[P_HOLD_TIME] * (STATES / 10);
holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10;
}
if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) {
holdwallswitch[i] = 0;
switchflag = 2; // Toggle with pushbutton to Gnd
switchflag = 2; // Toggle with pushbutton to Gnd
}
break;
}
if (switchflag < 3) {
if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT
ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present)
ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present)
}
}
@ -1849,15 +1870,6 @@ void SwitchHandler(byte mode)
/*********************************************************************************************\
* State loops
\*********************************************************************************************/
void Every50mSeconds()
{
// As the max amount of sleep = 250 mSec this loop will shift in time...
ButtonHandler();
SwitchHandler(0);
}
/*-------------------------------------------------------------------------------------------*\
* Every 0.1 second
\*-------------------------------------------------------------------------------------------*/
@ -2607,20 +2619,25 @@ void loop()
OsWatchLoop();
if (TimeReached(button_debounce)) {
SetNextTimeInterval(button_debounce, Settings.button_debounce);
ButtonHandler();
}
if (TimeReached(switch_debounce)) {
SetNextTimeInterval(switch_debounce, Settings.switch_debounce);
SwitchHandler(0);
}
if (TimeReached(state_50msecond)) {
SetNextTimeInterval(state_50msecond, 50);
Every50mSeconds();
XdrvCall(FUNC_EVERY_50_MSECOND);
XsnsCall(FUNC_EVERY_50_MSECOND);
}
if (TimeReached(state_100msecond)) {
SetNextTimeInterval(state_100msecond, 100);
Every100mSeconds();
XdrvCall(FUNC_EVERY_100_MSECOND);
XsnsCall(FUNC_EVERY_100_MSECOND);
}
if (TimeReached(state_250msecond)) {
SetNextTimeInterval(state_250msecond, 250);
Every250mSeconds();

View File

@ -20,7 +20,7 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
#define VERSION 0x0601010B
#define VERSION 0x0601010C
#define D_PROGRAMNAME "Sonoff-Tasmota"
#define D_AUTHOR "Theo Arends"

View File

@ -309,6 +309,31 @@ char* NoAlNumToUnderscore(char* dest, const char* source)
return dest;
}
void SetShortcut(char* str, uint8_t action)
{
if ('\0' != str[0]) { // There must be at least one character in the buffer
str[0] = '0' + action; // SC_CLEAR, SC_DEFAULT, SC_USER
str[1] = '\0';
}
}
uint8_t Shortcut(const char* str)
{
uint8_t result = 10;
if ('\0' == str[1]) { // Only allow single character input for shortcut
if (('"' == str[0]) || ('0' == str[0])) {
result = SC_CLEAR;
} else {
result = atoi(str); // 1 = SC_DEFAULT, 2 = SC_USER
if (0 == result) {
result = 10;
}
}
}
return result;
}
boolean ParseIp(uint32_t* addr, const char* str)
{
uint8_t *part = (uint8_t*)addr;
@ -1186,6 +1211,7 @@ void WiFiSetSleepMode()
* See https://github.com/arendst/Sonoff-Tasmota/issues/2559
*/
//#ifdef ARDUINO_ESP8266_RELEASE_2_4_1
#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2)
#else // Enabled in 2.3.0, 2.4.0 and stage
if (sleep) {

View File

@ -167,7 +167,9 @@
#define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000)
#define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec)
#define 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 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 (the wall switch state)
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)
@ -292,8 +294,8 @@
// #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
// #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+2k2 code)
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+4k7 code)
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code)
// #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code)
// #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
// #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)

View File

@ -555,7 +555,7 @@ bool MqttCommand()
}
else if (CMND_MQTTHOST == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_host))) {
strlcpy(Settings.mqtt_host, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? MQTT_HOST : dataBuf, sizeof(Settings.mqtt_host));
strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_HOST : dataBuf, sizeof(Settings.mqtt_host));
restart_flag = 2;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_host);
@ -587,14 +587,13 @@ bool MqttCommand()
else if ((CMND_MQTTFINGERPRINT == command_code) && (index > 0) && (index <= 2)) {
char fingerprint[60];
if ((data_len > 0) && (data_len < sizeof(fingerprint))) {
strlcpy(fingerprint, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? (1 == index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : dataBuf, sizeof(fingerprint));
strlcpy(fingerprint, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : dataBuf, sizeof(fingerprint));
char *p = fingerprint;
for (byte i = 0; i < 20; i++) {
Settings.mqtt_fingerprint[index -1][i] = strtol(p, &p, 16);
}
restart_flag = 2;
}
fingerprint[0] = '\0';
for (byte i = 0; i < sizeof(Settings.mqtt_fingerprint[index -1]); i++) {
snprintf_P(fingerprint, sizeof(fingerprint), PSTR("%s%s%02X"), fingerprint, (i) ? " " : "", Settings.mqtt_fingerprint[index -1][i]);
@ -604,21 +603,21 @@ bool MqttCommand()
#endif
else if ((CMND_MQTTCLIENT == command_code) && !grpflg) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_client))) {
strlcpy(Settings.mqtt_client, (1 == payload) ? MQTT_CLIENT_ID : dataBuf, sizeof(Settings.mqtt_client));
strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_CLIENT_ID : dataBuf, sizeof(Settings.mqtt_client));
restart_flag = 2;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_client);
}
else if (CMND_MQTTUSER == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_user))) {
strlcpy(Settings.mqtt_user, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? MQTT_USER : dataBuf, sizeof(Settings.mqtt_user));
strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_USER : dataBuf, sizeof(Settings.mqtt_user));
restart_flag = 2;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_user);
}
else if (CMND_MQTTPASSWORD == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_pwd))) {
strlcpy(Settings.mqtt_pwd, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? MQTT_PASS : dataBuf, sizeof(Settings.mqtt_pwd));
strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_PASS : dataBuf, sizeof(Settings.mqtt_pwd));
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_pwd);
restart_flag = 2;
} else {
@ -628,8 +627,8 @@ bool MqttCommand()
else if (CMND_FULLTOPIC == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_fulltopic))) {
MakeValidMqtt(1, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1;
strlcpy(stemp1, (1 == payload) ? MQTT_FULLTOPIC : dataBuf, sizeof(stemp1));
if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_FULLTOPIC : dataBuf, sizeof(stemp1));
if (strcmp(stemp1, Settings.mqtt_fulltopic)) {
snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : "");
MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
@ -642,7 +641,7 @@ bool MqttCommand()
else if ((CMND_PREFIX == command_code) && (index > 0) && (index <= 3)) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_prefix[0]))) {
MakeValidMqtt(0, dataBuf);
strlcpy(Settings.mqtt_prefix[index -1], (1 == payload) ? (1==index)?SUB_PREFIX:(2==index)?PUB_PREFIX:PUB_PREFIX2 : dataBuf, sizeof(Settings.mqtt_prefix[0]));
strlcpy(Settings.mqtt_prefix[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? (1==index)?SUB_PREFIX:(2==index)?PUB_PREFIX:PUB_PREFIX2 : dataBuf, sizeof(Settings.mqtt_prefix[0]));
// if (Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] == '/') Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] = 0;
restart_flag = 2;
}
@ -668,8 +667,8 @@ bool MqttCommand()
else if (CMND_GROUPTOPIC == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_grptopic))) {
MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1;
strlcpy(Settings.mqtt_grptopic, (1 == payload) ? MQTT_GRPTOPIC : dataBuf, sizeof(Settings.mqtt_grptopic));
if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_GRPTOPIC : dataBuf, sizeof(Settings.mqtt_grptopic));
restart_flag = 2;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_grptopic);
@ -677,8 +676,8 @@ bool MqttCommand()
else if ((CMND_TOPIC == command_code) && !grpflg) {
if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) {
MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1;
strlcpy(stemp1, (1 == payload) ? MQTT_TOPIC : dataBuf, sizeof(stemp1));
if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_TOPIC : dataBuf, sizeof(stemp1));
if (strcmp(stemp1, Settings.mqtt_topic)) {
snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : "");
MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic
@ -691,22 +690,26 @@ bool MqttCommand()
else if ((CMND_BUTTONTOPIC == command_code) && !grpflg) {
if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) {
MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1;
if (!strcmp(dataBuf,"0")) strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic));
else if (1 == payload) strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic));
else if (2 == payload) strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic));
else strlcpy(Settings.button_topic, dataBuf, sizeof(Settings.button_topic));
if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
switch (Shortcut(dataBuf)) {
case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break;
case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break;
case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break;
default: strlcpy(Settings.button_topic, dataBuf, sizeof(Settings.button_topic));
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.button_topic);
}
else if (CMND_SWITCHTOPIC == command_code) {
if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) {
MakeValidMqtt(0, dataBuf);
if (!strcmp(dataBuf, mqtt_client)) payload = 1;
if (!strcmp(dataBuf,"0")) strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic));
else if (1 == payload) strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic));
else if (2 == payload) strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic));
else strlcpy(Settings.switch_topic, dataBuf, sizeof(Settings.switch_topic));
if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT);
switch (Shortcut(dataBuf)) {
case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break;
case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break;
case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break;
default: strlcpy(Settings.switch_topic, dataBuf, sizeof(Settings.switch_topic));
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.switch_topic);
}

View File

@ -2019,7 +2019,7 @@ bool WebCommand()
}
else if (CMND_WEBPASSWORD == command_code) {
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) {
strlcpy(Settings.web_password, (!strcmp(XdrvMailbox.data,"0")) ? "" : (1 == XdrvMailbox.payload) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password));
strlcpy(Settings.web_password, (SC_CLEAR == Shortcut(XdrvMailbox.data)) ? "" : (SC_DEFAULT == Shortcut(XdrvMailbox.data)) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password));
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.web_password);
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_ASTERIX, command);

View File

@ -316,13 +316,20 @@ void SonoffBridgeReceived()
boolean SonoffBridgeSerialInput()
{
// iTead Rf Universal Transceiver Module Serial Protocol Version 1.0 (20170420)
int8_t receive_len = 0;
if (sonoff_bridge_receive_flag) {
if (sonoff_bridge_receive_raw_flag) {
if (!serial_in_byte_counter) {
serial_in_buffer[serial_in_byte_counter++] = 0xAA;
}
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
if (0x55 == serial_in_byte) { // 0x55 - End of text
if (serial_in_byte_counter > 2) {
if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { // AA A6 06 023908010155 55 - 06 is receive_len
receive_len = serial_in_buffer[2] + 3 - serial_in_byte_counter; // Get at least receive_len bytes
}
}
if ((0 == receive_len) && (0x55 == serial_in_byte)) { // 0x55 - End of text
SonoffBridgeReceivedRaw();
sonoff_bridge_receive_flag = 0;
return 1;

View File

@ -155,6 +155,24 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule)
break;
}
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%"));
if (rule_param.startsWith(stemp)) {
rule_param = String(GetMinutesPastMidnight());
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%"));
if (rule_param.startsWith(stemp)) {
rule_param = String(GetMinutesUptime());
}
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%"));
if (rule_param.startsWith(stemp)) {
rule_param = String(GetSunMinutes(0));
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%"));
if (rule_param.startsWith(stemp)) {
rule_param = String(GetSunMinutes(1));
}
#endif // USE_TIMERS and USE_SUNRISE
rule_param.toUpperCase();
snprintf(rule_svalue, sizeof(rule_svalue), rule_param.c_str());
@ -522,13 +540,13 @@ boolean RulesCommand()
}
else if ((CMND_VAR == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) {
if (XdrvMailbox.data_len > 0) {
strlcpy(vars[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(vars[index -1]));
strlcpy(vars[index -1], (SC_CLEAR == Shortcut(XdrvMailbox.data)) ? "" : XdrvMailbox.data, sizeof(vars[index -1]));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
}
else if ((CMND_MEM == command_code) && (index > 0) && (index <= MAX_RULE_MEMS)) {
if (XdrvMailbox.data_len > 0) {
strlcpy(Settings.mems[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1]));
strlcpy(Settings.mems[index -1], (SC_CLEAR == Shortcut(XdrvMailbox.data)) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1]));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]);
}

View File

@ -0,0 +1,451 @@
:020000040000FA
:10000000021197ED24F8FEEFD39E4015ED2408FD74
:10001000E433FCC3EF9DEC6480F874809850028058
:1000200001C322021078E4F5A922220215927EFF74
:10003000EFD394004022EF9480501DE4FDED75F065
:1000400005A42486F582E4341BF583E493B5070404
:10005000AE0580040DBD06E5AF06220212AE121AEF
:10006000BB53D87853DBFE121A7CE4900085F02253
:10007000D2DE220215E5D201121883C290C296D2B6
:1000800080E4FBFD7F10121A6312061E74A4F0D2E6
:10009000AFE4F537F538D2960538E5387002053704
:1000A000B410F3E537B427EEC2963001091217C534
:1000B0008E3A8F3B8006753A01753B00E53B700434
:1000C000E53A640170409000CCE07007F537F538F0
:1000D0000202870538E53870020537D39410E537FA
:1000E00094274002D296D3E5389430E53794755082
:1000F00003020287E4F537F5389000CCF09000CB8E
:10010000F0C296020287E4F537F5389000CCE0148F
:10011000602A14700302025914700302021E147044
:100120000302023424046003020287E53B64AA60F0
:10013000030202879000CC04F0020287E53B9000A6
:10014000CBF0A37402F0E53B120BE00206A00170B5
:10015000A10182A501A6A601BAA701C3A801DFA932
:1001600001C6B001CFB10197C00287FF00000215A0
:100170001205D79000A87401F0E4900075F07FA1FB
:10018000806E12005E9000CC7404F0753908E4F5BE
:100190003D753C090202879000CC7404F0E4F53D03
:1001A000753C02020287E49000A8F0900075F07F91
:1001B000A612064F74A6F002028712061E74A4F05F
:1001C0000202877539089000CC7403F00202879010
:1001D00000757401F07FB112064FEFF00202871232
:1001E00005D79000A8E0F536E4F0900075F07FA9FF
:1001F00012174D90007CEFF07D307C757F017E0002
:100200001218D602028712063290007CEFF0E490BA
:1002100000CCF08072E49000CBF0A3F08069E4F5AC
:100220003DE53BF53CE53CD394009000CC40257483
:1002300004F080537404253DF582E43400F583E531
:100240003BF0053DE53D653C6007E53DC3947040EE
:10025000369000CC7402F0802EE53B6455702890F7
:1002600000CCF0C2019000CBE02460601824FC6058
:100270000F24FE600B14600824F66004241070073D
:100280007FA0121A30D2019000CBE0120BE002B036
:10029000A102DAA402ECA503B5A603D1A804F1A932
:1002A000052EB005A1B1037DC003A3FF000000AA85
:1002B000900084E030E70F7DC87C001206077FA322
:1002C00012168102050B121A7440030200AA7DE87F
:1002D0007C031206077FA2020526900084E020E737
:1002E000030200AA7FA41216810203C99000CCE089
:1002F00060030200AA900085E024FC6049240460A9
:10030000030200AA1539900008E0FEA3E0FF1215D1
:100310002C8F46900006E0FEA3E0FF8E47F54812C2
:10032000152C8F49754A18900008E0FAA3E0FB905D
:100330000004E0FCA3E0FDA3E0FEA3E0FF120656EC
:100340007406F00204BD7F0112002EEF12157C60CE
:10035000030200AAE539601D1205B812158CD3946A
:100360000050030204E21205B812158C12063B126B
:100370001AAD0204E21206327FA00205269000CCDC
:10038000E060030200AA900004E0FCA3E0FD7F010E
:100390007E001218D6D296121AADC2967FA0121AFB
:1003A00030800AE4FF121A30E49000CCF0D201123F
:1003B00006460200AA900084E020E7030200AA5447
:1003C0007FF53FFD7FA61214B4E4900084F0020094
:1003D000AA9000CCE060030200AA900085E024FC13
:1003E00070030204C3240460030200AA12157A6099
:1003F000030200AA153912005E900004E0647F70C9
:1004000036A3E0FCA3E0FFAE04900009E0FCA3E00B
:10041000FBAA04A3E0F546A3E0F547A3E0F548A353
:10042000E0F549A3E0F54A900007E0FCA3E0FD12E7
:100430000656740CF0807D900004E0FF12002E8FB1
:100440003EE53EF46069EF121561120A60FFAEF0FE
:10045000C006C00712155F900002120A8BFFAEF0B3
:10046000C006C007900005120A8BFDACF0C004C0A6
:1004700005900009120984FF900007120A8BFDAC59
:10048000F090000A120984FE90000B120984F54ACC
:100490008E498C478D488F46D003D002D005D004BA
:1004A000D007D00612143A9000747401F08005E46D
:1004B0009000CBF09000CBE070030200AA1219AAC2
:1004C0000200AA12157A60030200AAE539601B1225
:1004D000155F12158CFFD394004007EF12063B12F4
:1004E0001AADE4900085F00200AA1206467FA080B3
:1004F00035900084E0FF30E71C547FF53F7DC87CD9
:10050000001205EAAD3F7FAB1214B4E4900084F012
:10051000D2010200AA121A7440030200AA7DE87CEC
:10052000031205EA7FAA121A30D2010200AA900033
:1005300004E025E0F53F9000CCE060030200AA90C3
:100540000085E0700512005E800B900085E0640479
:1005500060030200AAE53F6007E53CC3940450082D
:10056000E49000CBF00200AA7406253FF9E43400C1
:10057000754301F5448945C3E53C953F24FEF546A6
:10058000900005E0F5477B017A0079061213337F6E
:10059000A0121A3090007CE0FF12174DD201020029
:1005A000AA900084E020E7030200AA7FB11213B8EA
:1005B000E4900084F00200AA7F0112002EEF75F093
:1005C00005A4241CF582E4341BF583E493FB740139
:1005D00093FA740293F9227D327C007F017E00122F
:1005E00018D6D296121AADC296227F017E0012183A
:1005F000D6D296121AADC2969000A8E536F09000B9
:100600007CE0FF12174D227F017E001218D6D29691
:10061000121AADC29690007CE0FF12174D22900096
:10062000A87401F0E4900075F07FA412174D9000BB
:100630007C2290007CE0FF12174D22FD7C007F01A0
:100640007E001218D62290007CE0FF12174D221275
:10065000174D90007C2212143A90007422AFE9AE3C
:10066000EA7C007D0A1209C38E088F0920930302D9
:1006700008EE85080A85090BC3E509950DF511E516
:1006800008950CF510900075E01470030208E8045A
:10069000600302090F900085E014606504600302A6
:1006A000090F900084E0600302090F9000A8E0FFAA
:1006B000AB11AA10AD0FAC0E120FCA8F12E5126467
:1006C00080700302090F90007F120FA5900002E5D1
:1006D00010F0A3E511120F0AE4900074F090007777
:1006E000F090007EF0F513F514FE7F70FD7B017A2B
:1006F000007904120C33E490007DF090008504F042
:1007000022E512120F2F700302082A046003020967
:100710000FE512120E3F900004120984FFD39400DB
:10072000400B90007EE09F5004E004F022120F483E
:10073000AE10AF11AB07AA06E50F2BFFE50E3AFE90
:10074000E433FDE433FCC004A905AA06AB07AE0EF2
:10075000AF0F120FADD000120B4E8F22E512120E0A
:100760003F900009120984FD120003401BE512129C
:100770000E3F90000A12097A400EE512120E3F1247
:100780000EE16F60030208A3E512120E3F9000090C
:1007900012097A500CE512120E3F120EE1C39F406F
:1007A00016D3E50F9514E50E9513402EE512120EA3
:1007B0003F120EE16F7023900086120FA5120E58A3
:1007C000C083C082120F407401A806088002C333A0
:1007D000D8FC4FD082D083F0801790007A120FA5FA
:1007E000C290D39514E50E95134006850E13850F20
:1007F00014900088E0700D120E5A120EF0FF1219BC
:10080000C5120F06E512120E3F120EE16F600302D1
:10081000090F121A6C5005E4900083F0120EED706F
:10082000030208E012095C0208E0E512120E3F9094
:10083000000B120984FF7E00900004120A8BFDACAD
:10084000F01209C3120F198E238F24120F48E512DC
:10085000120E3F9000061209105011E512120E3FC1
:100860009000071209365004C2908041E512120E22
:100870003F900008120910502AE512120E3F900016
:1008800009120936501D120E58C083C082120F4043
:100890007401A806088002C333D8FC4FD082D083ED
:1008A000F0800AE4900084F0C290A3F022900088C7
:1008B000E0700D120E5A120EF0FF1219C5120F063B
:1008C000E512120E3F90000A120EE46F7041121AE8
:1008D0006C5005E4900083F0120EED600312095C89
:1008E000C290E4900085F022AF11AE10801E850802
:1008F0000C85090DC3E509950BF50FE508950AF57B
:100900000E900075E0147007AF0FAE0E120C5F2250
:10091000120984FD7C00900004120A8BFFAEF012D5
:1009200009C3C3E50F9FFFE50E9EFE121A24C3EF15
:100930009524EE952322120984FD7C009000041278
:100940000A8BFFAEF01209C3C3E5119FFFE5109EAD
:10095000FE121A24C3EF9524EE952322121A7C7DF1
:10096000207C037F017E001218AD90007DE0900096
:1009700083F0A3E512F04480F022120984FDAF2237
:1009800012000322BB010CE58229F582E5833AF5CA
:1009900083E0225006E92582F8E622BBFE06E9251F
:1009A00082F8E222E58229F582E5833AF583E49331
:1009B00022BB010689828A83F0225002F722BBFE05
:1009C00001F322EF8DF0A4A8F0CF8CF0A428CE8DF7
:1009D000F0A42EFE22BC000BBE0029EF8DF084FF98
:1009E000ADF022E4CCF875F008EF2FFFEE33FEEC0B
:1009F00033FCEE9DEC984005FCEE9DFE0FD5F0E932
:100A0000E4CEFD22EDF8F5F0EE8420D21CFEADF030
:100A100075F008EF2FFFED33FD4007985006D5F035
:100A2000F222C398FD0FD5F0EA22C2D5EC30E709D7
:100A3000B2D5E4C39DFDE49CFCEE30E715B2D5E4ED
:100A4000C39FFFE49EFE1209D5C3E49DFDE49CFC18
:100A500080031209D530D507C3E49FFFE49EFE2230
:100A6000BB010A89828A83E0F5F0A3E02250068761
:100A7000F009E71922BBFE07E3F5F009E319228923
:100A8000828A83E493F5F074019322BB0110E5821E
:100A900029F582E5833AF583E0F5F0A3E0225009D9
:100AA000E92582F886F008E622BBFE0AE92582F8ED
:100AB000E2F5F008E222E5832AF583E993F5F0A355
:100AC000E99322E88FF0A4CC8BF0A42CFCE98EF003
:100AD000A42CFC8AF0EDA42CFCEA8EF0A4CDA8F0A6
:100AE0008BF0A42DCC3825F0FDE98FF0A42CCD356A
:100AF000F0FCEB8EF0A4FEA9F0EB8FF0A4CFC5F0D4
:100B00002ECD39FEE43CFCEAA42DCE35F0FDE43CCC
:100B1000FC2275F008758200EF2FFFEE33FECD3317
:100B2000CDCC33CCC58233C5829BED9AEC99E5825E
:100B300098400CF582EE9BFEED9AFDEC99FC0FD5EA
:100B4000F0D6E4CEFBE4CDFAE4CCF9A88222B800DA
:100B5000C1B90059BA002DEC8BF084CFCECDFCE5A5
:100B6000F0CBF97818EF2FFFEE33FEED33FDEC33C9
:100B7000FCEB33FB10D703994004EB99FB0FD8E54E
:100B8000E4F9FA227818EF2FFFEE33FEED33FDEC97
:100B900033FCC933C910D7059BE99A4007EC9BFC8D
:100BA000E99AF90FD8E0E4C9FAE4CCFB2275F01019
:100BB000EF2FFFEE33FEED33FDCC33CCC833C8103E
:100BC000D7079BEC9AE899400AED9BFDEC9AFCE86C
:100BD00099F80FD5F0DAE4CDFBE4CCFAE4C8F922B9
:100BE000D083D082F8E4937012740193700DA3A3A4
:100BF00093F8740193F5828883E4737402936860B8
:100C0000EFA3A3A380DFEC8EF0A4CCC5F0CCCDF88D
:100C1000EFA4CEC5F02DFDE43CFCE8A42EC8C5F041
:100C20003DFDE43CFCEFA4FFE5F028FEE43DFDE4DF
:100C30003CFC22EF4E6012EF60010EEDBB010B8910
:100C4000828A83F0A3DFFCDEFA2289F05007F709DD
:100C5000DFFCA9F022BBFEFCF309DFFCA9F0228E29
:100C6000258F26900085E0146035147003020D3D39
:100C700024026003020E0EC290900084E060030222
:100C80000E0EAF26AE251219F24003020E0EE490AE
:100C90000001F0120F8E9000857401F0D290229026
:100CA0000078E0FCA3E0FDAE047802CEC313CE13BF
:100CB000D8F92DFFEC3EFED3E5269FE5259E4009A1
:100CC000120F8EE4900001F022AF26AE2512178A93
:100CD000501F900001E094004017E4900074F090E1
:100CE00000887404F0E490007DF09000857402F0B8
:100CF00022C3E5269464E52594005003020DA31257
:100D00000E0F501C120EF8E0FEA3E02526FFE5258D
:100D10003EC313FEEF13FF120EF8EEF0A3EFF02226
:100D2000900001E0120EFA120F91900001E004F021
:100D3000E0D3941E5003020E0EE4020DFFB290AFFA
:100D400026AE2512178A505E121A6C5005E49000E8
:100D500083F0120EED604A121A7C7DF47C017F0153
:100D60007E001218AD90007DE0900083F090008826
:100D7000E07019120E78C083C082E0FF900001E09D
:100D8000540FFEEF4ED082D083F0800E900001E031
:100D9000C454F0440F120E77EFF0900084E04480CA
:100DA000F0C290E48064120E0F5057900088E0B4B7
:100DB0000410E527C454F0120E77EFF0E490008899
:100DC000F022900074E0FF120E7CE0FEE527540F45
:100DD000FDEE4DF074042F120E7E120EF0FF12196C
:100DE000C5EFF0900074E004F09000887404F09077
:100DF0000074E0D394704016121A7CE4900001F065
:100E00008008121A7CE4900001F0900085F0227BAB
:100E1000007A007927AF26AE2512122622E52B751F
:100E2000F005A42488F582E4341BF583E493FB7475
:100E30000193FA740293F9120A60FFAEF0E52B7584
:100E4000F005A42488F582E4341BF583E493FB7455
:100E50000193FA740293F922D290900077E024FF74
:100E6000FFE434FFFE7C007D08120A2A74042FF58B
:100E70008274003EF58322FF900074E02404F58222
:100E8000E43400F58322120B4EAB07AA06E4F9F80E
:100E90007F407E427D0FFC120B4EE47BFFFAF9F897
:100EA000120AC3A804A905AA06AB077F207ED77D36
:100EB000757C01120B4EEFF40422AE28AF297C00A2
:100EC0007D1F1209D58E2C8F2D7C007D051209D532
:100ED000C3E52D9FFDE52C9EFCD3E5279DE5269CD3
:100EE0002290000B120984FF900077E0229000838B
:100EF000E0FF90007DE06F22E52725E0248AF5825F
:100F0000E43400F5832290007DEFF09000887408AF
:100F1000F02275F0FFA4FFAEF07C007D640209D5DD
:100F2000E53524C8FFE43534FEC3ED9FEC9E227501
:100F3000F005A42487F582E4341BF583E49314229E
:100F4000E0FF900088E0FE22900088E014F090001E
:100F500077E004F022E5352438FFE53434FFFED392
:100F6000ED9FEC9E22E52D2FFFE52C3EFEC3E527ED
:100F70009FE5269E2253DAFE53F7DF53F7BF22ABDD
:100F800043AA44A9458548827583000209849000DC
:100F900078E525F0A3E526F022900081E0FF90009F
:100FA00077E0D39F22E50EF0A3E50FF0227C007DD1
:100FB00064020C06540F75F002A4F58285F08322BA
:100FC000D3E5299494E5289411228F258C268D272A
:100FD0008A288B29752A80E5257052E4F52B7F013C
:100FE00012002EEF652B701B120FC04035E52994BF
:100FF00050E5289446502B120EBA4026120F655029
:10100000218054E52B120F2F600D047015120E1D58
:1010100012105D500D8008120E1D12105D500385D8
:101020002B2A052BE52BC3940640B3802DAF251248
:10103000002E8F2B7F0112002EEF652B701C120FDC
:10104000C04017E5299450E5289446500D120EBA79
:101050004008120F655003852B2AAF2A2290000208
:10106000120A8BF53585F034AB07AA06AD29AC28FA
:10107000AF27AE2612163822C0E0C0F0C083C0826F
:10108000C0D075D000C000C001C002C003C004C001
:1010900005C006C007E5985403F54FF45298E54F94
:1010A00030E017121AC49000C0121742EFF09000FF
:1010B000C0E004F0E0B41402E4F0E54F30E12E901B
:1010C00000C2E0D39400401A9000BFE02450F8E63C
:1010D000FF121AC19000BFE004F09000C2E014F0CB
:1010E0008002D2029000BFE0B42002E4F0D007D02A
:1010F00006D005D004D003D002D001D000D0D0D08B
:1011000082D083D0F0D0E0324200C700004200C35A
:1011100000004200C900004200C500004100CC00B0
:101120004100CB0041008400410085004100A8003F
:101130004100750041007C0041007656410089ABBA
:10114000410000004100820042007F000042000296
:101150000000420086000042007A00004100810049
:10116000410088004100770041007E00410074008A
:101170004100830041007D00410001004100C000AA
:101180004100C1004100BE004100BF004100C2005B
:101190004100BD00C1020012002A787FE4F6D8FDAC
:1011A00075816F0211E1020076E493A3F8E493A342
:1011B0004003F68001F208DFF48029E493A3F85499
:1011C00007240CC8C333C4540F4420C8834004F41C
:1011D00056800146F6DFE4800B010204081020402F
:1011E00080901108E47E019360BCA3FF543F30E57A
:1011F00009541FFEE493A360010ECF54C025E060A4
:10120000A840B8E493A3FAE493A3F8E493A3C8C571
:1012100082C8CAC583CAF0A3C8C582C8CAC583CA62
:10122000DFE9DEE780BE8E288F298B2A8A2B892C66
:10123000C200E4F52D900001E0FFE52DC39F506B47
:10124000E529AE287803CEC313CE13D8F9FDAC063A
:10125000E529AE287802CEC313CE13D8F92DF52F89
:10126000EE3CF52EE52D120EFAE0FEA3E0FFC3954D
:101270002FFDEE952EFCC3ED9529EC95285028E521
:101280002F2FFFE52E3EFEC3E5299FE5289E501730
:10129000E52C452B452A600BAB2AAA2BA92CE52D62
:1012A0001209B1D2008004052D808AA20022C0E07C
:1012B000C0F0C083C082C0D075D000C000C001C0E3
:1012C00002C003C004C005C006C007E5D85487F5B6
:1012D00021F452D8E5F730E508E5F730E6031217B8
:1012E000FB53F7DFE52130E708E5D930E003121AB8
:1012F000CAE52130E008E5DA30E0031218FFE52105
:1013000030E108E5DB30E00312065DE52130E2085C
:10131000E5DC30E003121ACBD007D006D005D004AC
:10132000D003D002D001D000D0D0D082D083D0F072
:10133000D0E0328B408A41894253DBFE120F755355
:10134000E2FDE4F548E548C39546504FAB40AA415D
:10135000A942C003C002C001120F7FC4120FB4D053
:1013600001D002D003120A8BF54A85F049D28012CF
:1013700013AAAB40AA41A942C003C002C001120F88
:101380007F120FB4D001D002D003120A8BF54A8528
:10139000F049C2801213AA054880AAB290AF47153F
:1013A00047EF709E43E202C29022FDAC497F0A7E65
:1013B0000012182C121AAD22AE07E4F54012194B98
:1013C000900001E004FF121952121433900001E062
:1013D000FFE540C39F501212173612172912173615
:1013E000F583121730054080E3900078E0FF121972
:1013F00052900078121730121433E4F540900074C4
:10140000E02401FFE433FEC3E5409FEE6480F874FE
:1014100080985017740425401217240540E5405465
:101420001F70DA121ABE121AB780D27F55121952E3
:10143000021ABE121ABE121AB7228E408F418C4277
:101440008D43AE02AF03120FADC007AF46AB07E44A
:10145000FAF9F8D007120E86900000F0AE47AF48B8
:10146000120FADC007AF49AB07E4FAF9F8D0071285
:101470000E86900082F0E546120F12900076EFF093
:10148000E549120F12900089EFF090007FE540F0DF
:10149000A3E541F0900002E542F0A3E543F090009F
:1014A00081E54AF043DA0153F7DF43F74053DBFEAF
:1014B00075F9FF22AC05AB07E4FEFDFA7FAA12190D
:1014C00052AF03121952EC75F005A424F3F582E42F
:1014D000341BF583E49314600B04701212170A9006
:1014E000000B800612170A90000A120984FAEEC354
:1014F0009A500774082EFE0D80F4ED04FF12195265
:10150000EC75F005A424F2F582E4341BF583E49332
:10151000FF121952E4FEEEC39D500974042E1217F7
:10152000240E80F27F55121952021ABE7C007D648F
:10153000120C06A804A905AA06AB07900006E0FE57
:10154000A3E0FFA3E0FCA3E02FFDEC3EAF05FEE42B
:10155000FDC8FCEDC9FDEECAFEEFCBFF020B4EE568
:101560003E75F005A4241CF582E4341BF583E49356
:10157000FB740193FA740293F922E53E75F005A419
:10158000241BF582E4341BF583E4932290000C02C3
:101590000984C0E0C083C082C0D075D000C004C040
:1015A00005C006C00753C87F9000C7E0FEA3E0FF58
:1015B0004E700353C8FB9000C31216FF50099000F1
:1015C000C7E4F0A3F0800DC39000C8E09DF0900048
:1015D000C7E09CF0D007D006D005D004D0D0D08290
:1015E000D083D0E032C0E0C083C082C0D075D000CC
:1015F000C004C005C006C00753917F9000C9E0FE3B
:10160000A3E0FF4E70035391FB9000C51216FF50EC
:10161000099000C9E4F0A3F0800DC39000CAE09DDA
:10162000F09000C9E09CF0D007D006D005D004D0DF
:10163000D0D082D083D0E032C200D3EB9400EA94C1
:1016400000402FEB2438F582EA34FFF583D3EF9581
:1016500082EE95834028EB24C8FBE43AFAC3EF9B63
:10166000EE9A501A120F554015120F205010D2004A
:10167000800C120F554007120F205002D200A2001A
:1016800022AE07E4FDF54012194B900002E0FF1274
:10169000195290000212173090007AE0FF1219528E
:1016A00090007A121730900086E0FF1219529000D5
:1016B0008612173074042D1217240DBD03F67F55C2
:1016C000121952021ABEAB07AA06E4F9F87F407E4F
:1016D000427D0FFC120B4EA804A905AA06AB077F9A
:1016E000207ED77D757C01120B4EC3E49FFFE49EE4
:1016F000FE22AB07AA06E4F9F87FE87E03FD22E0AC
:10170000FCA3E0FDC3EF9DEE9C22EC75F005A42444
:10171000F4F582E4341BF583E493FB740193FA74CB
:101720000293F922F582E43400F583E0FF021952B6
:10173000A3E0FF021952E54025E0248AF582E43453
:101740000022E024A9F582E43400F583228F409042
:10175000007CE0F5417F0B121AC743DB01120F75C5
:101760001200707D0A7C007F017E001218D6121ACA
:10177000AD43E202E4900085F0900084F09000CB4D
:10178000E540F090007CF0AF4122AD07AC06900040
:1017900078E0FEA3E07802CEC313CE13D8F9FFC3DE
:1017A000900079E09FFB900078E09EFAC3EB9DEA01
:1017B0009C5010A3E02FFF900078E03E120F2850BD
:1017C000028001C3229000C0E0FF9000BEE0B50798
:1017D000057E017F00229000BE121742E0FD7C00D2
:1017E0009000BEE004F0E0B41402E4F09000BDE02C
:1017F000FEEE4204E4F0AE04AF0522120F9940035E
:1018000002198C120E78120F40EFA806088002C34E
:1018100013D8FC30E00B900000E0FF121AC7D29002
:1018200022900082E0FF121AC7C290228E4B8F4C8A
:101830008C4D8D4E1216C6121A0CE54E24BF900028
:10184000CAF0E54D34FF9000C9F09000C5E54BF0BB
:10185000A3E54CF043910422D29053E2FDD2809054
:10186000007F121873C290C2809000021218734356
:10187000E20222E0FCA3E0FD7F0A7E0012182C1297
:101880001AAD22121A83121A8A121A91121A3B12D4
:1018900000261219DC121A59121A45121A4F121A7E
:1018A00018121A98121A9F121AB3021AA68E288FAB
:1018B000298C2A8D2B1216F21216D3121A009000C0
:1018C000C7E52AF0A3E52BF09000C3E528F0A3E5D7
:1018D00029F043C804228E428F438C448D45121652
:1018E000F21216D3121A0C9000C9E544F0A3E54594
:1018F000F09000C5E542F0A3E543F0439104229047
:101900000088E07008900074E004120F0A120F4E75
:10191000900088E014F0120F99500302192453E24A
:10192000FDC28022120E78120F40EFA80608800236
:10193000C313D8FC30E0059000768003900089E066
:101940007D00FCE4FF12196E22AE077FAA12195225
:10195000AF06C2029000C1E0B42002E4F09000C1E2
:10196000E02450F8A607E004F0A3E004F022AB075F
:10197000AF04EB14600C14600E2402700E8DFB8F0C
:10198000FC228DE98FEA228DEB8FEC22E4FDFCFF37
:1019900012196E120F75121ABB53D87853E2FDC29A
:1019A00080C2909000857404F022120F0BE014F0B6
:1019B0009000777401F09000857403F012192412DE
:1019C00018580200707E1DE4FDEF30E70625E06E3A
:1019D000FF8004EF25E0FF0DBD08EE22AF8853889D
:1019E000AF758CA0758DCBEF5440FEEF54104E4276
:1019F0008822C3EF94ACEE940D4003D38001C32240
:101A0000AD07AC06ECF5CBAF058FCA22AD07AC062F
:101A1000ECF593AF058F9222C2DE75D90575F9FFFB
:101A200075960122EE30E707C3E49FFFE49EFE2295
:101A30001219497F55121952021ABE75E34075E119
:101A40000175E20222E59154045391FB4291227503
:101A50008E5475892243885022E5C8540453C8FB2C
:101A600042C82253984FEB4F4DF59822E5C8C3204A
:101A7000E201D322E591C320E201D32253C8FB53F4
:101A8000C87F227597DE7597AD2275A41175D4CEE7
:101A90002275A54175D5772253F77F75DA4A22530F
:101AA000F77F75DB302275E69075A8B022E59120AE
:101AB000E2FB22439810223002FD22C2DE22D2999C
:101AC000228F9922AF99228F8C222222019030708E
:101AD00000064001904B19180012C005DC0002BC42
:101AE000012C461E28080BB8232800044C01904BFB
:101AF000191800251C0BB80003840140461E18006D
:101B0000000072D80702BC012C264040000BB81C14
:101B1000520190010303011846000100FF1ACC0294
:101B200000FF1AD90300FF1AE60400FF1AF30500AC
:101B3000FF1B000601FF1B0D0190307000064001E5
:101B4000904B19180012C005DC0002BC012C461E87
:101B500028080BB8232800044C01904B19180025C5
:101B60001C0BB80003840140461E1800000072D808
:101B70000702BC012C264040000BB81C520190010A
:101B80000303011846000100FF1B380200FF1B453C
:101B90000300FF1B520400FF1B5F0500FF1B6C06C8
:101BA00001FF1B790190307000064001904B19181D
:101BB0000012C005DC0002BC012C461E28080BB830
:101BC000232800044C01904B191800251C0BB80069
:101BD00003840140461E1800000072D80702BC01B1
:101BE0002C264040000BB81C520190010303011841
:101BF00046000100FF1BA40200FF1BB10300FF1BF6
:101C0000BE0400FF1BCB0500FF1BD80601FF1BE530
:00000001FF