From 2c160a8a2567cec4cbe9f73f88e4f40b9a700164 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 May 2023 09:38:21 +1200 Subject: [PATCH 01/67] Bump version to 2023.5.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 46e5545078..56034bfbf6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0-dev" +__version__ = "2023.5.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d80885c7a8868b8bd4b25cb7d2a262282d82d622 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Thu, 11 May 2023 00:00:28 +0200 Subject: [PATCH 02/67] Fixed access point for ESP32 IDF platform (#4784) --- esphome/components/wifi/wifi_component_esp_idf.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 1c70f33040..e18d3cc043 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -767,11 +767,10 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); } - esp_netif_dhcp_status_t dhcp_status; - esp_netif_dhcps_get_status(s_sta_netif, &dhcp_status); - err = esp_netif_dhcps_stop(s_sta_netif); - if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_netif_dhcps_stop failed! %d", err); + + err = esp_netif_dhcpc_stop(s_sta_netif); + if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { + ESP_LOGV(TAG, "esp_netif_dhcpc_stop failed: %s", esp_err_to_name(err)); return false; } From d88358be8e9a18994c3c37a9be4397b2910ddcea Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 May 2023 11:33:59 +1200 Subject: [PATCH 03/67] Remove AUTO_LOAD from apds9960 (#4746) --- esphome/components/apds9960/__init__.py | 1 - esphome/components/apds9960/apds9960.cpp | 71 ++++++++++++++------ esphome/components/apds9960/apds9960.h | 41 +++++------ esphome/components/apds9960/binary_sensor.py | 11 +-- esphome/components/apds9960/sensor.py | 12 +--- 5 files changed, 79 insertions(+), 57 deletions(-) diff --git a/esphome/components/apds9960/__init__.py b/esphome/components/apds9960/__init__.py index 37dc4c0b28..06b3c85aee 100644 --- a/esphome/components/apds9960/__init__.py +++ b/esphome/components/apds9960/__init__.py @@ -4,7 +4,6 @@ from esphome.components import i2c from esphome.const import CONF_ID DEPENDENCIES = ["i2c"] -AUTO_LOAD = ["sensor", "binary_sensor"] MULTI_CONF = True CONF_APDS9960_ID = "apds9960_id" diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index 1c6ec5c14a..d91378afee 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -116,8 +116,12 @@ void APDS9960::setup() { APDS9960_WRITE_BYTE(0x80, val); } bool APDS9960::is_color_enabled_() const { - return this->red_channel_ != nullptr || this->green_channel_ != nullptr || this->blue_channel_ != nullptr || - this->clear_channel_ != nullptr; +#ifdef USE_SENSOR + return this->red_sensor_ != nullptr || this->green_sensor_ != nullptr || this->blue_sensor_ != nullptr || + this->clear_sensor_ != nullptr; +#else + return false; +#endif } void APDS9960::dump_config() { @@ -125,6 +129,15 @@ void APDS9960::dump_config() { LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); + +#ifdef USE_SENSOR + LOG_SENSOR(" ", "Red channel", this->red_sensor_); + LOG_SENSOR(" ", "Green channel", this->green_sensor_); + LOG_SENSOR(" ", "Blue channel", this->blue_sensor_); + LOG_SENSOR(" ", "Clear channel", this->clear_sensor_); + LOG_SENSOR(" ", "Proximity", this->proximity_sensor_); +#endif + if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: @@ -181,17 +194,22 @@ void APDS9960::read_color_data_(uint8_t status) { float blue_perc = (uint_blue / float(UINT16_MAX)) * 100.0f; ESP_LOGD(TAG, "Got clear=%.1f%% red=%.1f%% green=%.1f%% blue=%.1f%%", clear_perc, red_perc, green_perc, blue_perc); - if (this->clear_channel_ != nullptr) - this->clear_channel_->publish_state(clear_perc); - if (this->red_channel_ != nullptr) - this->red_channel_->publish_state(red_perc); - if (this->green_channel_ != nullptr) - this->green_channel_->publish_state(green_perc); - if (this->blue_channel_ != nullptr) - this->blue_channel_->publish_state(blue_perc); +#ifdef USE_SENSOR + if (this->clear_sensor_ != nullptr) + this->clear_sensor_->publish_state(clear_perc); + if (this->red_sensor_ != nullptr) + this->red_sensor_->publish_state(red_perc); + if (this->green_sensor_ != nullptr) + this->green_sensor_->publish_state(green_perc); + if (this->blue_sensor_ != nullptr) + this->blue_sensor_->publish_state(blue_perc); +#endif } void APDS9960::read_proximity_data_(uint8_t status) { - if (this->proximity_ == nullptr) +#ifndef USE_SENSOR + return; +#else + if (this->proximity_sensor_ == nullptr) return; if ((status & 0b10) == 0x00) { @@ -204,7 +222,8 @@ void APDS9960::read_proximity_data_(uint8_t status) { float prox_perc = (prox / float(UINT8_MAX)) * 100.0f; ESP_LOGD(TAG, "Got proximity=%.1f%%", prox_perc); - this->proximity_->publish_state(prox_perc); + this->proximity_sensor_->publish_state(prox_perc); +#endif } void APDS9960::read_gesture_data_() { if (!this->is_gesture_enabled_()) @@ -256,28 +275,29 @@ void APDS9960::read_gesture_data_() { } } void APDS9960::report_gesture_(int gesture) { +#ifdef USE_BINARY_SENSOR binary_sensor::BinarySensor *bin; switch (gesture) { case 1: - bin = this->up_direction_; + bin = this->up_direction_binary_sensor_; this->gesture_up_started_ = false; this->gesture_down_started_ = false; ESP_LOGD(TAG, "Got gesture UP"); break; case 2: - bin = this->down_direction_; + bin = this->down_direction_binary_sensor_; this->gesture_up_started_ = false; this->gesture_down_started_ = false; ESP_LOGD(TAG, "Got gesture DOWN"); break; case 3: - bin = this->left_direction_; + bin = this->left_direction_binary_sensor_; this->gesture_left_started_ = false; this->gesture_right_started_ = false; ESP_LOGD(TAG, "Got gesture LEFT"); break; case 4: - bin = this->right_direction_; + bin = this->right_direction_binary_sensor_; this->gesture_left_started_ = false; this->gesture_right_started_ = false; ESP_LOGD(TAG, "Got gesture RIGHT"); @@ -290,6 +310,7 @@ void APDS9960::report_gesture_(int gesture) { bin->publish_state(true); bin->publish_state(false); } +#endif } void APDS9960::process_dataset_(int up, int down, int left, int right) { /* Algorithm: (see Figure 11 in datasheet) @@ -365,10 +386,22 @@ void APDS9960::process_dataset_(int up, int down, int left, int right) { } } float APDS9960::get_setup_priority() const { return setup_priority::DATA; } -bool APDS9960::is_proximity_enabled_() const { return this->proximity_ != nullptr || this->is_gesture_enabled_(); } +bool APDS9960::is_proximity_enabled_() const { + return +#ifdef USE_SENSOR + this->proximity_sensor_ != nullptr +#else + false +#endif + || this->is_gesture_enabled_(); +} bool APDS9960::is_gesture_enabled_() const { - return this->up_direction_ != nullptr || this->left_direction_ != nullptr || this->down_direction_ != nullptr || - this->right_direction_ != nullptr; +#ifdef USE_BINARY_SENSOR + return this->up_direction_binary_sensor_ != nullptr || this->left_direction_binary_sensor_ != nullptr || + this->down_direction_binary_sensor_ != nullptr || this->right_direction_binary_sensor_ != nullptr; +#else + return false; +#endif } } // namespace apds9960 diff --git a/esphome/components/apds9960/apds9960.h b/esphome/components/apds9960/apds9960.h index 23d9835640..2a0fbb5c19 100644 --- a/esphome/components/apds9960/apds9960.h +++ b/esphome/components/apds9960/apds9960.h @@ -1,14 +1,34 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" +#endif +#ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" +#endif namespace esphome { namespace apds9960 { class APDS9960 : public PollingComponent, public i2c::I2CDevice { +#ifdef USE_SENSOR + SUB_SENSOR(red) + SUB_SENSOR(green) + SUB_SENSOR(blue) + SUB_SENSOR(clear) + SUB_SENSOR(proximity) +#endif + +#ifdef USE_BINARY_SENSOR + SUB_BINARY_SENSOR(up_direction) + SUB_BINARY_SENSOR(right_direction) + SUB_BINARY_SENSOR(down_direction) + SUB_BINARY_SENSOR(left_direction) +#endif + public: void setup() override; void dump_config() override; @@ -23,16 +43,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice { void set_gesture_gain(uint8_t gain) { this->gesture_gain_ = gain; } void set_gesture_wait_time(uint8_t wait_time) { this->gesture_wait_time_ = wait_time; } - void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; } - void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; } - void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; } - void set_clear_channel(sensor::Sensor *clear_channel) { clear_channel_ = clear_channel; } - void set_up_direction(binary_sensor::BinarySensor *up_direction) { up_direction_ = up_direction; } - void set_right_direction(binary_sensor::BinarySensor *right_direction) { right_direction_ = right_direction; } - void set_down_direction(binary_sensor::BinarySensor *down_direction) { down_direction_ = down_direction; } - void set_left_direction(binary_sensor::BinarySensor *left_direction) { left_direction_ = left_direction; } - void set_proximity(sensor::Sensor *proximity) { proximity_ = proximity; } - protected: bool is_color_enabled_() const; bool is_proximity_enabled_() const; @@ -50,15 +60,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice { uint8_t gesture_gain_; uint8_t gesture_wait_time_; - sensor::Sensor *red_channel_{nullptr}; - sensor::Sensor *green_channel_{nullptr}; - sensor::Sensor *blue_channel_{nullptr}; - sensor::Sensor *clear_channel_{nullptr}; - binary_sensor::BinarySensor *up_direction_{nullptr}; - binary_sensor::BinarySensor *right_direction_{nullptr}; - binary_sensor::BinarySensor *down_direction_{nullptr}; - binary_sensor::BinarySensor *left_direction_{nullptr}; - sensor::Sensor *proximity_{nullptr}; enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, diff --git a/esphome/components/apds9960/binary_sensor.py b/esphome/components/apds9960/binary_sensor.py index 04dc6f4d5d..46b08d8d69 100644 --- a/esphome/components/apds9960/binary_sensor.py +++ b/esphome/components/apds9960/binary_sensor.py @@ -6,19 +6,14 @@ from . import APDS9960, CONF_APDS9960_ID DEPENDENCIES = ["apds9960"] -DIRECTIONS = { - "UP": "set_up_direction", - "DOWN": "set_down_direction", - "LEFT": "set_left_direction", - "RIGHT": "set_right_direction", -} +DIRECTIONS = ["up", "down", "left", "right"] CONFIG_SCHEMA = binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_MOVING ).extend( { cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), - cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), + cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, lower=True), } ) @@ -26,5 +21,5 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema( async def to_code(config): hub = await cg.get_variable(config[CONF_APDS9960_ID]) var = await binary_sensor.new_binary_sensor(config) - func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]]) + func = getattr(hub, f"set_{config[CONF_DIRECTION]}_direction_binary_sensor") cg.add(func(var)) diff --git a/esphome/components/apds9960/sensor.py b/esphome/components/apds9960/sensor.py index e1990ec26e..c9865f8687 100644 --- a/esphome/components/apds9960/sensor.py +++ b/esphome/components/apds9960/sensor.py @@ -11,13 +11,7 @@ from . import APDS9960, CONF_APDS9960_ID DEPENDENCIES = ["apds9960"] -TYPES = { - "CLEAR": "set_clear_channel", - "RED": "set_red_channel", - "GREEN": "set_green_channel", - "BLUE": "set_blue_channel", - "PROXIMITY": "set_proximity", -} +TYPES = ["clear", "red", "green", "blue", "proximity"] CONFIG_SCHEMA = sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, @@ -26,7 +20,7 @@ CONFIG_SCHEMA = sensor.sensor_schema( state_class=STATE_CLASS_MEASUREMENT, ).extend( { - cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), + cv.Required(CONF_TYPE): cv.one_of(*TYPES, lower=True), cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), } ) @@ -35,5 +29,5 @@ CONFIG_SCHEMA = sensor.sensor_schema( async def to_code(config): hub = await cg.get_variable(config[CONF_APDS9960_ID]) var = await sensor.new_sensor(config) - func = getattr(hub, TYPES[config[CONF_TYPE]]) + func = getattr(hub, f"set_{config[CONF_TYPE]}_sensor") cg.add(func(var)) From 2fd2e5ceb2044e2faadb56fddd67351b0ed23840 Mon Sep 17 00:00:00 2001 From: Alex Dekker Date: Thu, 11 May 2023 23:25:34 +0200 Subject: [PATCH 04/67] Supposed to fix #4069, by changing the default value to 0s (timeunit instead of int) to pass validation (#4806) Co-authored-by: Alex1602 --- esphome/components/sprinkler/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index 6aa76dcd2f..5097abc7e7 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -275,7 +275,7 @@ SPRINKLER_ACTION_SET_RUN_DURATION_SCHEMA = cv.Schema( SPRINKLER_ACTION_QUEUE_VALVE_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.use_id(Sprinkler), - cv.Optional(CONF_RUN_DURATION, default=0): cv.templatable( + cv.Optional(CONF_RUN_DURATION, default="0s"): cv.templatable( cv.positive_time_period_seconds ), cv.Required(CONF_VALVE_NUMBER): cv.templatable(cv.positive_int), From e2f3e7c3a60f1617d4ed05a73764975de20b7b4a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 12 May 2023 10:32:59 +1200 Subject: [PATCH 05/67] Bump version to 2023.5.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 56034bfbf6..48009df20a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0b1" +__version__ = "2023.5.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From af95e781f519e0c6230d94d4412b58f08cc14f23 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Fri, 12 May 2023 02:46:47 +0100 Subject: [PATCH 06/67] Wording (#4805) --- esphome/components/sen5x/sen5x.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index 865fae373b..ddce568c97 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -252,7 +252,7 @@ void SEN5XComponent::dump_config() { ESP_LOGCONFIG(TAG, " Firmware version: %d", this->firmware_version_); ESP_LOGCONFIG(TAG, " Serial number %02d.%02d.%02d", serial_number_[0], serial_number_[1], serial_number_[2]); if (this->auto_cleaning_interval_.has_value()) { - ESP_LOGCONFIG(TAG, " Auto auto cleaning interval %d seconds", auto_cleaning_interval_.value()); + ESP_LOGCONFIG(TAG, " Auto cleaning interval %d seconds", auto_cleaning_interval_.value()); } if (this->acceleration_mode_.has_value()) { switch (this->acceleration_mode_.value()) { From e0ee8ca17ca810b55d50d36fc524ca51a1aae71e Mon Sep 17 00:00:00 2001 From: richardhopton Date: Fri, 12 May 2023 20:16:28 -0700 Subject: [PATCH 07/67] Tuya: Prevent loop when setting colors on case-sensitive dps (#4809) Co-authored-by: Samuel Sieb --- esphome/components/tuya/light/tuya_light.cpp | 46 +++++++++++--------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 869e20871d..7b7a974de2 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -57,37 +57,43 @@ void TuyaLight::setup() { return; } + float red, green, blue; switch (*this->color_type_) { case TuyaColorType::RGBHSV: case TuyaColorType::RGB: { - auto red = parse_hex(datapoint.value_string.substr(0, 2)); - auto green = parse_hex(datapoint.value_string.substr(2, 2)); - auto blue = parse_hex(datapoint.value_string.substr(4, 2)); - if (red.has_value() && green.has_value() && blue.has_value()) { - auto rgb_call = this->state_->make_call(); - rgb_call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255); - rgb_call.perform(); - } + auto rgb = parse_hex(datapoint.value_string.substr(0, 6)); + if (!rgb.has_value()) + return; + + red = (*rgb >> 16) / 255.0f; + green = ((*rgb >> 8) & 0xff) / 255.0f; + blue = (*rgb & 0xff) / 255.0f; break; } case TuyaColorType::HSV: { auto hue = parse_hex(datapoint.value_string.substr(0, 4)); auto saturation = parse_hex(datapoint.value_string.substr(4, 4)); auto value = parse_hex(datapoint.value_string.substr(8, 4)); - if (hue.has_value() && saturation.has_value() && value.has_value()) { - float red, green, blue; - hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue); - auto rgb_call = this->state_->make_call(); - rgb_call.set_rgb(red, green, blue); - rgb_call.perform(); - } + if (!hue.has_value() || !saturation.has_value() || !value.has_value()) + return; + + hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue); break; } } + + float current_red, current_green, current_blue; + this->state_->current_values_as_rgb(¤t_red, ¤t_green, ¤t_blue); + if (red == current_red && green == current_green && blue == current_blue) + return; + auto rgb_call = this->state_->make_call(); + rgb_call.set_rgb(red, green, blue); + rgb_call.perform(); }); } + if (min_value_datapoint_id_.has_value()) { - parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); + this->parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); } } @@ -156,7 +162,7 @@ void TuyaLight::write_state(light::LightState *state) { } if (!state->current_values.is_on() && this->switch_id_.has_value()) { - parent_->set_boolean_datapoint_value(*this->switch_id_, false); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, false); return; } @@ -166,14 +172,14 @@ void TuyaLight::write_state(light::LightState *state) { if (this->color_temperature_invert_) { color_temp_int = this->color_temperature_max_value_ - color_temp_int; } - parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); + this->parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); } if (this->dimmer_id_.has_value()) { auto brightness_int = static_cast(brightness * this->max_value_); brightness_int = std::max(brightness_int, this->min_value_); - parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int); } } @@ -210,7 +216,7 @@ void TuyaLight::write_state(light::LightState *state) { } if (this->switch_id_.has_value()) { - parent_->set_boolean_datapoint_value(*this->switch_id_, true); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, true); } } From 625126df686a782a54aaf79dbeced59911b3b953 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 13 May 2023 17:19:06 +1200 Subject: [PATCH 08/67] Fix i2s media player volume control (#4813) --- .../media_player/i2s_audio_media_player.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index 2e9ded601d..4de1136987 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -22,14 +22,14 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { this->start(); } } - if (this->i2s_state_ != I2S_STATE_RUNNING) { - return; - } if (call.get_volume().has_value()) { this->volume = call.get_volume().value(); this->set_volume_(volume); this->unmute_(); } + if (this->i2s_state_ != I2S_STATE_RUNNING) { + return; + } if (call.get_command().has_value()) { switch (call.get_command().value()) { case media_player::MEDIA_PLAYER_COMMAND_PLAY: @@ -97,7 +97,8 @@ void I2SAudioMediaPlayer::unmute_() { this->muted_ = false; } void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) { - this->audio_->setVolume(remap(volume, 0.0f, 1.0f, 0, 21)); + if (this->audio_ != nullptr) + this->audio_->setVolume(remap(volume, 0.0f, 1.0f, 0, 21)); if (publish) this->volume = volume; } @@ -157,13 +158,23 @@ void I2SAudioMediaPlayer::start_() { #endif this->i2s_state_ = I2S_STATE_RUNNING; this->high_freq_.start(); + this->audio_->setVolume(remap(this->volume, 0.0f, 1.0f, 0, 21)); if (this->current_url_.has_value()) { this->audio_->connecttohost(this->current_url_.value().c_str()); this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->publish_state(); } } -void I2SAudioMediaPlayer::stop() { this->i2s_state_ = I2S_STATE_STOPPING; } +void I2SAudioMediaPlayer::stop() { + if (this->i2s_state_ == I2S_STATE_STOPPED) { + return; + } + if (this->i2s_state_ == I2S_STATE_STARTING) { + this->i2s_state_ = I2S_STATE_STOPPED; + return; + } + this->i2s_state_ = I2S_STATE_STOPPING; +} void I2SAudioMediaPlayer::stop_() { if (this->audio_->isRunning()) { this->audio_->stopSong(); From 65cda1088400b257c68fca0f87e73b49abf9f50e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 May 2023 10:11:48 +1200 Subject: [PATCH 09/67] Dontr try stop if not actually started (#4814) --- .../components/i2s_audio/microphone/i2s_audio_microphone.cpp | 4 ++++ esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 2b38853528..0f45cf95c6 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -89,6 +89,10 @@ void I2SAudioMicrophone::start_() { void I2SAudioMicrophone::stop() { if (this->state_ == microphone::STATE_STOPPED || this->is_failed()) return; + if (this->state_ == microphone::STATE_STARTING) { + this->state_ = microphone::STATE_STOPPED; + return; + } this->state_ = microphone::STATE_STOPPING; } diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index fa41a70277..5ae597dc7c 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -136,6 +136,10 @@ void I2SAudioSpeaker::player_task(void *params) { void I2SAudioSpeaker::stop() { if (this->state_ == speaker::STATE_STOPPED) return; + if (this->state_ == speaker::STATE_STARTING) { + this->state_ = speaker::STATE_STOPPED; + return; + } this->state_ = speaker::STATE_STOPPING; DataEvent data; data.stop = true; From e25d92e1f5d35049faeb331d8eebaea2c716a2a7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 May 2023 11:37:55 +1200 Subject: [PATCH 10/67] Bump version to 2023.5.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 48009df20a..9c2ab5e2b8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0b2" +__version__ = "2023.5.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 6ccea59f71a58559fc48e631e1b221bfb6e9fce1 Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Mon, 15 May 2023 22:24:03 +0200 Subject: [PATCH 11/67] Fix missing stop trait in send_cover_info (#4826) --- esphome/components/api/api_connection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 013b46695d..c350197e68 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -223,6 +223,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) { msg.assumed_state = traits.get_is_assumed_state(); msg.supports_position = traits.get_supports_position(); msg.supports_tilt = traits.get_supports_tilt(); + msg.supports_stop = traits.get_supports_stop(); msg.device_class = cover->get_device_class(); msg.disabled_by_default = cover->is_disabled_by_default(); msg.icon = cover->get_icon(); From e13e754bc46ecdfc67f76fe164ab00c1d982c7bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 08:24:58 +1200 Subject: [PATCH 12/67] Bump aioesphomeapi from 13.7.2 to 13.7.5 (#4830) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a62c48e235..8a2281b96f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==6.1.6 # When updating platformio, also update Dockerfile esptool==4.5.1 click==8.1.3 esphome-dashboard==20230214.0 -aioesphomeapi==13.7.2 +aioesphomeapi==13.7.5 zeroconf==0.60.0 # esp-idf requires this, but doesn't bundle it by default From c16ca7be133d292eed6f0a7b52197101453bffce Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 15 May 2023 21:29:00 +0100 Subject: [PATCH 13/67] Update PulseLightEffect with range brightness (#4820) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/light/base_light_effects.h | 9 ++++++++- esphome/components/light/effects.py | 9 +++++++++ esphome/components/shelly_dimmer/light.py | 5 +++-- esphome/const.py | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index 6291aa0610..9211bba7c9 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -25,7 +25,7 @@ class PulseLightEffect : public LightEffect { return; } auto call = this->state_->turn_on(); - float out = this->on_ ? 1.0 : 0.0; + float out = this->on_ ? this->max_brightness : this->min_brightness; call.set_brightness_if_supported(out); this->on_ = !this->on_; call.set_transition_length_if_supported(this->transition_length_); @@ -41,11 +41,18 @@ class PulseLightEffect : public LightEffect { void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } + void set_min_max_brightness(float min, float max) { + this->min_brightness = min; + this->max_brightness = max; + } + protected: bool on_ = false; uint32_t last_color_change_{0}; uint32_t transition_length_{}; uint32_t update_interval_{}; + float min_brightness{0.0}; + float max_brightness{1.0}; }; /// Random effect. Sets random colors every 10 seconds and slowly transitions between them. diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index cef7cd7f3a..c694d6f50c 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -28,6 +28,8 @@ from esphome.const import ( CONF_NUM_LEDS, CONF_RANDOM, CONF_SEQUENCE, + CONF_MAX_BRIGHTNESS, + CONF_MIN_BRIGHTNESS, ) from esphome.util import Registry from .types import ( @@ -174,12 +176,19 @@ async def automation_effect_to_code(config, effect_id): cv.Optional( CONF_UPDATE_INTERVAL, default="1s" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_MIN_BRIGHTNESS, default="0%"): cv.percentage, + cv.Optional(CONF_MAX_BRIGHTNESS, default="100%"): cv.percentage, }, ) async def pulse_effect_to_code(config, effect_id): effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH])) cg.add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL])) + cg.add( + effect.set_min_max_brightness( + config[CONF_MIN_BRIGHTNESS], config[CONF_MAX_BRIGHTNESS] + ) + ) return effect diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py index 20e0e8156b..c49193d135 100644 --- a/esphome/components/shelly_dimmer/light.py +++ b/esphome/components/shelly_dimmer/light.py @@ -23,6 +23,8 @@ from esphome.const import ( DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, + CONF_MIN_BRIGHTNESS, + CONF_MAX_BRIGHTNESS, ) from esphome.core import HexInt, CORE @@ -41,8 +43,7 @@ CONF_UPDATE = "update" CONF_LEADING_EDGE = "leading_edge" CONF_WARMUP_BRIGHTNESS = "warmup_brightness" # CONF_WARMUP_TIME = "warmup_time" -CONF_MIN_BRIGHTNESS = "min_brightness" -CONF_MAX_BRIGHTNESS = "max_brightness" + CONF_NRST_PIN = "nrst_pin" CONF_BOOT0_PIN = "boot0_pin" diff --git a/esphome/const.py b/esphome/const.py index 9c2ab5e2b8..8cc689fa58 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -377,6 +377,7 @@ CONF_MAKE_ID = "make_id" CONF_MANUAL_IP = "manual_ip" CONF_MANUFACTURER_ID = "manufacturer_id" CONF_MASK_DISTURBER = "mask_disturber" +CONF_MAX_BRIGHTNESS = "max_brightness" CONF_MAX_COOLING_RUN_TIME = "max_cooling_run_time" CONF_MAX_CURRENT = "max_current" CONF_MAX_DURATION = "max_duration" @@ -396,6 +397,7 @@ CONF_MEDIUM = "medium" CONF_MEMORY_BLOCKS = "memory_blocks" CONF_METHOD = "method" CONF_MICROPHONE = "microphone" +CONF_MIN_BRIGHTNESS = "min_brightness" CONF_MIN_COOLING_OFF_TIME = "min_cooling_off_time" CONF_MIN_COOLING_RUN_TIME = "min_cooling_run_time" CONF_MIN_FAN_MODE_SWITCHING_TIME = "min_fan_mode_switching_time" From f66024b37c975f418dc4bbe8298b1f1961c53170 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 May 2023 10:25:49 +1200 Subject: [PATCH 14/67] Bump esphome-dashboard to 20230516.0 (#4831) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8a2281b96f..b791311152 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.1.6 # When updating platformio, also update Dockerfile esptool==4.5.1 click==8.1.3 -esphome-dashboard==20230214.0 +esphome-dashboard==20230516.0 aioesphomeapi==13.7.5 zeroconf==0.60.0 From 2e5757a3f0de22dfc851a2333ea19c901c36ec51 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Mon, 15 May 2023 23:28:01 +0100 Subject: [PATCH 15/67] Fix time period validation for the auto cleaning interval (#4811) --- esphome/components/sen5x/sensor.py | 2 +- tests/test5.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/sen5x/sensor.py b/esphome/components/sen5x/sensor.py index 489fda8335..392510e417 100644 --- a/esphome/components/sen5x/sensor.py +++ b/esphome/components/sen5x/sensor.py @@ -119,7 +119,7 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_PM10, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.time_period_in_seconds_, + cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval, cv.Optional(CONF_VOC): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, diff --git a/tests/test5.yaml b/tests/test5.yaml index 6b64ef2d15..cb4b559b06 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -489,6 +489,7 @@ sensor: offset: 0 normalized_offset_slope: 0 time_constant: 0 + auto_cleaning_interval: 604800s acceleration_mode: low store_baseline: true address: 0x69 From daa966975e2660d0ddbec98141506b59d04fd091 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 10:30:08 +1200 Subject: [PATCH 16/67] Bump tzlocal from 4.2 to 5.0.1 (#4829) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b791311152..0da1d8a812 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ PyYAML==6.0 paho-mqtt==1.6.1 colorama==0.4.6 tornado==6.3.1 -tzlocal==4.2 # from time +tzlocal==5.0.1 # from time tzdata>=2021.1 # from time pyserial==3.5 platformio==6.1.6 # When updating platformio, also update Dockerfile From c71e7d0132c9f0a6de02ebc9c85f15b8f75834ba Mon Sep 17 00:00:00 2001 From: Justin Gerace Date: Mon, 15 May 2023 16:00:05 -0700 Subject: [PATCH 17/67] Start UART assignment at UART0 if the logger is not enabled or is not configured for hardware logging on ESP32 (#4762) --- .../uart/uart_component_esp32_arduino.cpp | 20 +++++++++++++++++-- .../uart/uart_component_esp32_arduino.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 8bbbc1a650..7306dd2f31 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -86,10 +86,26 @@ void ESP32ArduinoUARTComponent::setup() { is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1; is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3; #endif - if (is_default_tx && is_default_rx) { + static uint8_t next_uart_num = 0; + if (is_default_tx && is_default_rx && next_uart_num == 0) { this->hw_serial_ = &Serial; + next_uart_num++; } else { - static uint8_t next_uart_num = 1; +#ifdef USE_LOGGER + // The logger doesn't use this UART component, instead it targets the UARTs + // directly (i.e. Serial/Serial0, Serial1, and Serial2). If the logger is + // enabled, skip the UART that it is configured to use. + if (logger::global_logger->get_baud_rate() > 0 && logger::global_logger->get_uart() == next_uart_num) { + next_uart_num++; + } +#endif // USE_LOGGER + + if (next_uart_num >= UART_NUM_MAX) { + ESP_LOGW(TAG, "Maximum number of UART components created already."); + this->mark_failed(); + return; + } + this->number_ = next_uart_num; this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory) } diff --git a/esphome/components/uart/uart_component_esp32_arduino.h b/esphome/components/uart/uart_component_esp32_arduino.h index f85c709097..02dfd0531e 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.h +++ b/esphome/components/uart/uart_component_esp32_arduino.h @@ -2,6 +2,7 @@ #ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include #include #include #include "esphome/core/component.h" From a4e63c5f86f3e007d20f7eff4bdcf4cd5549b74a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 23:13:17 +0000 Subject: [PATCH 18/67] Synchronise Device Classes from Home Assistant (#4825) Co-authored-by: esphomebot --- esphome/components/number/__init__.py | 2 ++ esphome/components/sensor/__init__.py | 2 ++ esphome/const.py | 1 + 3 files changed, 5 insertions(+) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index f532f4e405..73fbfd6e90 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -57,6 +57,7 @@ from esphome.const import ( DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME_STORAGE, @@ -109,6 +110,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME_STORAGE, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index f0a58d908c..06b96171a7 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -73,6 +73,7 @@ from esphome.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME_STORAGE, @@ -129,6 +130,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME_STORAGE, diff --git a/esphome/const.py b/esphome/const.py index 8cc689fa58..692325603c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1004,6 +1004,7 @@ DEVICE_CLASS_TIMESTAMP = "timestamp" DEVICE_CLASS_UPDATE = "update" DEVICE_CLASS_VIBRATION = "vibration" DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" +DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" DEVICE_CLASS_VOLTAGE = "voltage" DEVICE_CLASS_VOLUME = "volume" DEVICE_CLASS_VOLUME_STORAGE = "volume_storage" From 71c4714a6e5bfddc0d9584af921eeb901fd170f9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 May 2023 11:16:14 +1200 Subject: [PATCH 19/67] Bump version to 2023.5.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 692325603c..83b3425e7d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0b3" +__version__ = "2023.5.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From cc76e5353c360bcd63d798c77921e014a644c3d6 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 16 May 2023 04:36:02 -0700 Subject: [PATCH 20/67] support sending keys to the collector (#4838) Co-authored-by: Samuel Sieb --- esphome/components/key_collector/__init__.py | 7 ++++--- esphome/components/key_collector/key_collector.cpp | 2 ++ esphome/components/key_collector/key_collector.h | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/key_collector/__init__.py b/esphome/components/key_collector/__init__.py index 2099e28109..fd142b3cd7 100644 --- a/esphome/components/key_collector/__init__.py +++ b/esphome/components/key_collector/__init__.py @@ -33,7 +33,7 @@ CONFIG_SCHEMA = cv.All( cv.COMPONENT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(KeyCollector), - cv.GenerateID(CONF_SOURCE_ID): cv.use_id(key_provider.KeyProvider), + cv.Optional(CONF_SOURCE_ID): cv.use_id(key_provider.KeyProvider), cv.Optional(CONF_MIN_LENGTH): cv.int_, cv.Optional(CONF_MAX_LENGTH): cv.int_, cv.Optional(CONF_START_KEYS): cv.string, @@ -55,8 +55,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - source = await cg.get_variable(config[CONF_SOURCE_ID]) - cg.add(var.set_provider(source)) + if CONF_SOURCE_ID in config: + source = await cg.get_variable(config[CONF_SOURCE_ID]) + cg.add(var.set_provider(source)) if CONF_MIN_LENGTH in config: cg.add(var.set_min_length(config[CONF_MIN_LENGTH])) if CONF_MAX_LENGTH in config: diff --git a/esphome/components/key_collector/key_collector.cpp b/esphome/components/key_collector/key_collector.cpp index a9213890ee..bf2333d97d 100644 --- a/esphome/components/key_collector/key_collector.cpp +++ b/esphome/components/key_collector/key_collector.cpp @@ -52,6 +52,8 @@ void KeyCollector::clear(bool progress_update) { this->progress_trigger_->trigger(this->result_, 0); } +void KeyCollector::send_key(uint8_t key) { this->key_pressed_(key); } + void KeyCollector::key_pressed_(uint8_t key) { this->last_key_time_ = millis(); if (!this->start_keys_.empty() && !this->start_key_) { diff --git a/esphome/components/key_collector/key_collector.h b/esphome/components/key_collector/key_collector.h index 5e63397839..7ef53929ef 100644 --- a/esphome/components/key_collector/key_collector.h +++ b/esphome/components/key_collector/key_collector.h @@ -27,6 +27,7 @@ class KeyCollector : public Component { void set_timeout(int timeout) { this->timeout_ = timeout; }; void clear(bool progress_update = true); + void send_key(uint8_t key); protected: void key_pressed_(uint8_t key); From c941bc4109a4fc7c1ae9553e48907c295ef7491a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 16 May 2023 14:30:14 -0700 Subject: [PATCH 21/67] handle Wiegand 8-bit keys (#4837) Co-authored-by: Samuel Sieb --- esphome/components/wiegand/wiegand.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/esphome/components/wiegand/wiegand.cpp b/esphome/components/wiegand/wiegand.cpp index c4e834c85a..10c77a8aa2 100644 --- a/esphome/components/wiegand/wiegand.cpp +++ b/esphome/components/wiegand/wiegand.cpp @@ -102,6 +102,16 @@ void Wiegand::loop() { uint8_t key = KEYS[value]; this->send_key_(key); } + } else if (count == 8) { + if ((value ^ 0xf0) >> 4 == (value & 0xf)) { + value &= 0xf; + for (auto *trigger : this->key_triggers_) + trigger->trigger(value); + if (value < 12) { + uint8_t key = KEYS[value]; + this->send_key_(key); + } + } } else { ESP_LOGD(TAG, "received unknown %d-bit value: %llx", count, value); } From 3c371a0c59c08f5e2774711e3c0f3fc627271fbe Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 May 2023 09:57:36 +1200 Subject: [PATCH 22/67] Bump version to 2023.5.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 83b3425e7d..c1b35aafff 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0b4" +__version__ = "2023.5.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 726bdd7be2ed38cf9536f1caf4d61615778c8c36 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 May 2023 12:45:42 +1200 Subject: [PATCH 23/67] Bump version to 2023.5.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c1b35aafff..c22cf9acca 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0b5" +__version__ = "2023.5.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From bfaad1f28de907922fbc3cef43f44f417744383a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 May 2023 11:34:18 +1200 Subject: [PATCH 24/67] Remove i2c dependency from ttp229_bsf (#4851) --- esphome/components/ttp229_bsf/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/ttp229_bsf/__init__.py b/esphome/components/ttp229_bsf/__init__.py index f1f86c929e..9c8208df83 100644 --- a/esphome/components/ttp229_bsf/__init__.py +++ b/esphome/components/ttp229_bsf/__init__.py @@ -3,7 +3,6 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import CONF_ID, CONF_SDO_PIN, CONF_SCL_PIN -DEPENDENCIES = ["i2c"] AUTO_LOAD = ["binary_sensor"] CONF_TTP229_ID = "ttp229_id" From 9bf946e1962bbb7e43bcf809d1e8584a988bb7aa Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 17 May 2023 18:36:52 -0500 Subject: [PATCH 25/67] Sprinkler fixes (#4816) --- esphome/components/sprinkler/__init__.py | 140 ++++++++++----------- esphome/components/sprinkler/sprinkler.cpp | 126 +++++++++++++------ esphome/components/sprinkler/sprinkler.h | 12 +- 3 files changed, 162 insertions(+), 116 deletions(-) diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index 5097abc7e7..e1d855778a 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -599,15 +599,6 @@ async def to_code(config): ) cg.add(var.set_controller_auto_adv_switch(sw_aa_var)) - if CONF_QUEUE_ENABLE_SWITCH in sprinkler_controller: - sw_qen_var = await switch.new_switch( - sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH] - ) - await cg.register_component( - sw_qen_var, sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH] - ) - cg.add(var.set_controller_queue_enable_switch(sw_qen_var)) - if CONF_REVERSE_SWITCH in sprinkler_controller: sw_rev_var = await switch.new_switch( sprinkler_controller[CONF_REVERSE_SWITCH] @@ -617,78 +608,83 @@ async def to_code(config): ) cg.add(var.set_controller_reverse_switch(sw_rev_var)) - if CONF_STANDBY_SWITCH in sprinkler_controller: - sw_stb_var = await switch.new_switch( - sprinkler_controller[CONF_STANDBY_SWITCH] - ) - await cg.register_component( - sw_stb_var, sprinkler_controller[CONF_STANDBY_SWITCH] - ) - cg.add(var.set_controller_standby_switch(sw_stb_var)) + if CONF_STANDBY_SWITCH in sprinkler_controller: + sw_stb_var = await switch.new_switch( + sprinkler_controller[CONF_STANDBY_SWITCH] + ) + await cg.register_component( + sw_stb_var, sprinkler_controller[CONF_STANDBY_SWITCH] + ) + cg.add(var.set_controller_standby_switch(sw_stb_var)) - if CONF_MULTIPLIER_NUMBER in sprinkler_controller: - num_mult_var = await number.new_number( - sprinkler_controller[CONF_MULTIPLIER_NUMBER], - min_value=sprinkler_controller[CONF_MULTIPLIER_NUMBER][ - CONF_MIN_VALUE - ], - max_value=sprinkler_controller[CONF_MULTIPLIER_NUMBER][ - CONF_MAX_VALUE - ], - step=sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_STEP], + if CONF_QUEUE_ENABLE_SWITCH in sprinkler_controller: + sw_qen_var = await switch.new_switch( + sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH] + ) + await cg.register_component( + sw_qen_var, sprinkler_controller[CONF_QUEUE_ENABLE_SWITCH] + ) + cg.add(var.set_controller_queue_enable_switch(sw_qen_var)) + + if CONF_MULTIPLIER_NUMBER in sprinkler_controller: + num_mult_var = await number.new_number( + sprinkler_controller[CONF_MULTIPLIER_NUMBER], + min_value=sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_MIN_VALUE], + max_value=sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_MAX_VALUE], + step=sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_STEP], + ) + await cg.register_component( + num_mult_var, sprinkler_controller[CONF_MULTIPLIER_NUMBER] + ) + cg.add( + num_mult_var.set_initial_value( + sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_INITIAL_VALUE] ) - await cg.register_component( - num_mult_var, sprinkler_controller[CONF_MULTIPLIER_NUMBER] + ) + cg.add( + num_mult_var.set_restore_value( + sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_RESTORE_VALUE] ) - cg.add( - num_mult_var.set_initial_value( - sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_INITIAL_VALUE] - ) - ) - cg.add( - num_mult_var.set_restore_value( - sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_RESTORE_VALUE] - ) + ) + + if CONF_SET_ACTION in sprinkler_controller[CONF_MULTIPLIER_NUMBER]: + await automation.build_automation( + num_mult_var.get_set_trigger(), + [(float, "x")], + sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_SET_ACTION], ) - if CONF_SET_ACTION in sprinkler_controller[CONF_MULTIPLIER_NUMBER]: - await automation.build_automation( - num_mult_var.get_set_trigger(), - [(float, "x")], - sprinkler_controller[CONF_MULTIPLIER_NUMBER][CONF_SET_ACTION], - ) + cg.add(var.set_controller_multiplier_number(num_mult_var)) - cg.add(var.set_controller_multiplier_number(num_mult_var)) + if CONF_REPEAT_NUMBER in sprinkler_controller: + num_repeat_var = await number.new_number( + sprinkler_controller[CONF_REPEAT_NUMBER], + min_value=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_MIN_VALUE], + max_value=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_MAX_VALUE], + step=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_STEP], + ) + await cg.register_component( + num_repeat_var, sprinkler_controller[CONF_REPEAT_NUMBER] + ) + cg.add( + num_repeat_var.set_initial_value( + sprinkler_controller[CONF_REPEAT_NUMBER][CONF_INITIAL_VALUE] + ) + ) + cg.add( + num_repeat_var.set_restore_value( + sprinkler_controller[CONF_REPEAT_NUMBER][CONF_RESTORE_VALUE] + ) + ) - if CONF_REPEAT_NUMBER in sprinkler_controller: - num_repeat_var = await number.new_number( - sprinkler_controller[CONF_REPEAT_NUMBER], - min_value=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_MIN_VALUE], - max_value=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_MAX_VALUE], - step=sprinkler_controller[CONF_REPEAT_NUMBER][CONF_STEP], - ) - await cg.register_component( - num_repeat_var, sprinkler_controller[CONF_REPEAT_NUMBER] - ) - cg.add( - num_repeat_var.set_initial_value( - sprinkler_controller[CONF_REPEAT_NUMBER][CONF_INITIAL_VALUE] - ) - ) - cg.add( - num_repeat_var.set_restore_value( - sprinkler_controller[CONF_REPEAT_NUMBER][CONF_RESTORE_VALUE] - ) + if CONF_SET_ACTION in sprinkler_controller[CONF_REPEAT_NUMBER]: + await automation.build_automation( + num_repeat_var.get_set_trigger(), + [(float, "x")], + sprinkler_controller[CONF_REPEAT_NUMBER][CONF_SET_ACTION], ) - if CONF_SET_ACTION in sprinkler_controller[CONF_REPEAT_NUMBER]: - await automation.build_automation( - num_repeat_var.get_set_trigger(), - [(float, "x")], - sprinkler_controller[CONF_REPEAT_NUMBER][CONF_SET_ACTION], - ) - - cg.add(var.set_controller_repeat_number(num_repeat_var)) + cg.add(var.set_controller_repeat_number(num_repeat_var)) for valve in sprinkler_controller[CONF_VALVES]: sw_valve_var = await switch.new_switch(valve[CONF_VALVE_SWITCH]) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 52a6cd2af4..095884997c 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -147,22 +147,22 @@ SprinklerValveOperator::SprinklerValveOperator(SprinklerValve *valve, Sprinkler : controller_(controller), valve_(valve) {} void SprinklerValveOperator::loop() { - if (millis() >= this->pinned_millis_) { // dummy check + if (millis() >= this->start_millis_) { // dummy check switch (this->state_) { case STARTING: - if (millis() > (this->pinned_millis_ + this->start_delay_)) { + if (millis() > (this->start_millis_ + this->start_delay_)) { this->run_(); // start_delay_ has been exceeded, so ensure both valves are on and update the state } break; case ACTIVE: - if (millis() > (this->pinned_millis_ + this->start_delay_ + this->run_duration_)) { + if (millis() > (this->start_millis_ + this->start_delay_ + this->run_duration_)) { this->stop(); // start_delay_ + run_duration_ has been exceeded, start shutting down } break; case STOPPING: - if (millis() > (this->pinned_millis_ + this->stop_delay_)) { + if (millis() > (this->stop_millis_ + this->stop_delay_)) { this->kill_(); // stop_delay_has been exceeded, ensure all valves are off } break; @@ -183,11 +183,12 @@ void SprinklerValveOperator::set_controller(Sprinkler *controller) { void SprinklerValveOperator::set_valve(SprinklerValve *valve) { if (valve != nullptr) { - this->state_ = IDLE; // reset state - this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it - this->pinned_millis_ = 0; // reset because (new) valve has not been started yet - this->kill_(); // ensure everything is off before we let go! - this->valve_ = valve; // finally, set the pointer to the new valve + this->state_ = IDLE; // reset state + this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it + this->start_millis_ = 0; // reset because (new) valve has not been started yet + this->stop_millis_ = 0; // reset because (new) valve has not been started yet + this->kill_(); // ensure everything is off before we let go! + this->valve_ = valve; // finally, set the pointer to the new valve } } @@ -221,7 +222,8 @@ void SprinklerValveOperator::start() { } else { this->run_(); // there is no start_delay_, so just start the pump and valve } - this->pinned_millis_ = millis(); // save the time the start request was made + this->stop_millis_ = 0; + this->start_millis_ = millis(); // save the time the start request was made } void SprinklerValveOperator::stop() { @@ -238,19 +240,33 @@ void SprinklerValveOperator::stop() { if (this->pump_switch()->state()) { // if the pump is still on at this point, it may be in use... this->valve_off_(); // ...so just switch the valve off now to ensure consistent run time } - this->pinned_millis_ = millis(); // save the time the stop request was made } else { this->kill_(); // there is no stop_delay_, so just stop the pump and valve } + this->stop_millis_ = millis(); // save the time the stop request was made } -uint32_t SprinklerValveOperator::run_duration() { return this->run_duration_; } +uint32_t SprinklerValveOperator::run_duration() { return this->run_duration_ / 1000; } uint32_t SprinklerValveOperator::time_remaining() { - if ((this->state_ == STARTING) || (this->state_ == ACTIVE)) { - return (this->pinned_millis_ + this->start_delay_ + this->run_duration_ - millis()) / 1000; + if (this->start_millis_ == 0) { + return this->run_duration(); // hasn't been started yet } - return 0; + + if (this->stop_millis_) { + if (this->stop_millis_ - this->start_millis_ >= this->start_delay_ + this->run_duration_) { + return 0; // valve was active for more than its configured duration, so we are done + } else { + // we're stopped; return time remaining + return (this->run_duration_ - (this->stop_millis_ - this->start_millis_)) / 1000; + } + } + + auto completed_millis = this->start_millis_ + this->start_delay_ + this->run_duration_; + if (completed_millis > millis()) { + return (completed_millis - millis()) / 1000; // running now + } + return 0; // run completed } SprinklerState SprinklerValveOperator::state() { return this->state_; } @@ -386,6 +402,9 @@ void Sprinkler::loop() { for (auto &vo : this->valve_op_) { vo.loop(); } + if (this->prev_req_.has_request() && this->prev_req_.valve_operator()->state() == IDLE) { + this->prev_req_.reset(); + } } void Sprinkler::add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw) { @@ -732,7 +751,7 @@ bool Sprinkler::auto_advance() { if (this->auto_adv_sw_ != nullptr) { return this->auto_adv_sw_->state; } - return false; + return true; } float Sprinkler::multiplier() { @@ -972,7 +991,14 @@ optional Sprinkler::active_valve_request_is_from return nullopt; } -optional Sprinkler::active_valve() { return this->active_req_.valve_as_opt(); } +optional Sprinkler::active_valve() { + if (!this->valve_overlap_ && this->prev_req_.has_request() && + (this->prev_req_.valve_operator()->state() == STARTING || this->prev_req_.valve_operator()->state() == ACTIVE)) { + return this->prev_req_.valve_as_opt(); + } + return this->active_req_.valve_as_opt(); +} + optional Sprinkler::paused_valve() { return this->paused_valve_; } optional Sprinkler::queued_valve() { @@ -1097,22 +1123,35 @@ uint32_t Sprinkler::total_cycle_time_enabled_valves() { uint32_t Sprinkler::total_cycle_time_enabled_incomplete_valves() { uint32_t total_time_remaining = 0; - uint32_t valve_count = 0; + uint32_t enabled_valve_count = 0; + uint32_t incomplete_valve_count = 0; for (size_t valve = 0; valve < this->number_of_valves(); valve++) { - if (this->valve_is_enabled_(valve) && !this->valve_cycle_complete_(valve)) { - if (!this->active_valve().has_value() || (valve != this->active_valve().value())) { - total_time_remaining += this->valve_run_duration_adjusted(valve); - valve_count++; + if (this->valve_is_enabled_(valve)) { + enabled_valve_count++; + if (!this->valve_cycle_complete_(valve)) { + if (!this->active_valve().has_value() || (valve != this->active_valve().value())) { + total_time_remaining += this->valve_run_duration_adjusted(valve); + incomplete_valve_count++; + } else { + // to get here, there must be an active valve and this valve must be equal to 'valve' + if (this->active_req_.valve_operator() == nullptr) { // no SVO has been assigned yet so it hasn't started + total_time_remaining += this->valve_run_duration_adjusted(valve); + incomplete_valve_count++; + } + } } } } - if (valve_count) { + if (incomplete_valve_count >= enabled_valve_count) { + incomplete_valve_count--; + } + if (incomplete_valve_count) { if (this->valve_overlap_) { - total_time_remaining -= this->switching_delay_.value_or(0) * (valve_count - 1); + total_time_remaining -= this->switching_delay_.value_or(0) * incomplete_valve_count; } else { - total_time_remaining += this->switching_delay_.value_or(0) * (valve_count - 1); + total_time_remaining += this->switching_delay_.value_or(0) * incomplete_valve_count; } } @@ -1149,31 +1188,32 @@ optional Sprinkler::time_remaining_active_valve() { return this->active_req_.valve_operator()->time_remaining(); } } - for (auto &vo : this->valve_op_) { // ...else return the value from the first non-IDLE SprinklerValveOperator - if (vo.state() != IDLE) { - return vo.time_remaining(); + if (this->prev_req_.has_request()) { // try to return the value based on prev_req_... + if (this->prev_req_.valve_operator() != nullptr) { + return this->prev_req_.valve_operator()->time_remaining(); } } return nullopt; } optional Sprinkler::time_remaining_current_operation() { - auto total_time_remaining = this->time_remaining_active_valve(); + if (!this->time_remaining_active_valve().has_value() && this->state_ == IDLE) { + return nullopt; + } - if (total_time_remaining.has_value()) { - if (this->auto_advance()) { - total_time_remaining = total_time_remaining.value() + this->total_cycle_time_enabled_incomplete_valves(); - total_time_remaining = - total_time_remaining.value() + + auto total_time_remaining = this->time_remaining_active_valve().value_or(0); + if (this->auto_advance()) { + total_time_remaining += this->total_cycle_time_enabled_incomplete_valves(); + if (this->repeat().value_or(0) > 0) { + total_time_remaining += (this->total_cycle_time_enabled_valves() * (this->repeat().value_or(0) - this->repeat_count().value_or(0))); } - - if (this->queue_enabled()) { - total_time_remaining = total_time_remaining.value() + this->total_queue_time(); - } - return total_time_remaining; } - return nullopt; + + if (this->queue_enabled()) { + total_time_remaining += this->total_queue_time(); + } + return total_time_remaining; } bool Sprinkler::any_controller_is_active() { @@ -1305,6 +1345,12 @@ optional Sprinkler::next_valve_number_in_cycle_(const optional f } void Sprinkler::load_next_valve_run_request_(const optional first_valve) { + if (this->active_req_.has_request()) { + this->prev_req_ = this->active_req_; + } else { + this->prev_req_.reset(); + } + if (this->next_req_.has_request()) { if (!this->next_req_.run_duration()) { // ensure the run duration is set correctly for consumption later on this->next_req_.set_run_duration(this->valve_run_duration_adjusted(this->next_req_.valve())); diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index 7a8285ae73..ae7554d3af 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -170,7 +170,8 @@ class SprinklerValveOperator { uint32_t start_delay_{0}; uint32_t stop_delay_{0}; uint32_t run_duration_{0}; - uint64_t pinned_millis_{0}; + uint64_t start_millis_{0}; + uint64_t stop_millis_{0}; Sprinkler *controller_{nullptr}; SprinklerValve *valve_{nullptr}; SprinklerState state_{IDLE}; @@ -538,15 +539,18 @@ class Sprinkler : public Component { /// The valve run request that is currently active SprinklerValveRunRequest active_req_; + /// The next run request for the controller to consume after active_req_ is complete + SprinklerValveRunRequest next_req_; + + /// The previous run request the controller processed + SprinklerValveRunRequest prev_req_; + /// The number of the manually selected valve currently selected optional manual_valve_; /// The number of the valve to resume from (if paused) optional paused_valve_; - /// The next run request for the controller to consume after active_req_ is complete - SprinklerValveRunRequest next_req_; - /// Set the number of times to repeat a full cycle optional target_repeats_; From f30f20db8645e0dc75a52b3b836e8c307e2408d0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 May 2023 13:00:37 +1200 Subject: [PATCH 26/67] Bump version to 2023.5.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c22cf9acca..93c0961561 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.0" +__version__ = "2023.5.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 2d3b48f86f98f5de828495c415281e62d832e7d3 Mon Sep 17 00:00:00 2001 From: Stefan Rado Date: Sun, 21 May 2023 22:10:23 +0200 Subject: [PATCH 27/67] Fix i2s_audio media_player mutex acquisition (#4867) Co-authored-by: Rajan Patel --- .../i2s_audio/media_player/i2s_audio_media_player.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index 4de1136987..6eaa32c23c 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -133,7 +133,7 @@ void I2SAudioMediaPlayer::play_() { void I2SAudioMediaPlayer::start() { this->i2s_state_ = I2S_STATE_STARTING; } void I2SAudioMediaPlayer::start_() { - if (this->parent_->try_lock()) { + if (!this->parent_->try_lock()) { return; // Waiting for another i2s to return lock } @@ -156,6 +156,7 @@ void I2SAudioMediaPlayer::start_() { #if SOC_I2S_SUPPORTS_DAC } #endif + this->i2s_state_ = I2S_STATE_RUNNING; this->high_freq_.start(); this->audio_->setVolume(remap(this->volume, 0.0f, 1.0f, 0, 21)); @@ -218,6 +219,12 @@ void I2SAudioMediaPlayer::dump_config() { default: break; } + } else { +#endif + ESP_LOGCONFIG(TAG, " External DAC channels: %d", this->external_dac_channels_); + ESP_LOGCONFIG(TAG, " I2S DOUT Pin: %d", this->dout_pin_); + LOG_PIN(" Mute Pin: ", this->mute_pin_); +#if SOC_I2S_SUPPORTS_DAC } #endif } From 8fcec8e2cbaff5a225170dd9ffcd0193b3f00435 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 May 2023 11:31:30 +1200 Subject: [PATCH 28/67] Bump version to 2023.5.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 93c0961561..1e2218b0da 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.1" +__version__ = "2023.5.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 148eb03d1336d6aad46d52464a7280e86ca4c3b7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 23 May 2023 07:02:16 +1200 Subject: [PATCH 29/67] Allow microphone channel to be specified in config (#4871) --- esphome/components/i2s_audio/microphone/__init__.py | 11 ++++++++++- .../i2s_audio/microphone/i2s_audio_microphone.cpp | 2 +- .../i2s_audio/microphone/i2s_audio_microphone.h | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index 48d4d28f8e..089e796ae0 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -2,7 +2,7 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome import pins -from esphome.const import CONF_ID, CONF_NUMBER +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_NUMBER from esphome.components import microphone, esp32 from esphome.components.adc import ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, validate_adc_pin @@ -25,6 +25,12 @@ I2SAudioMicrophone = i2s_audio_ns.class_( "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component ) +i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t") +CHANNELS = { + "left": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, + "right": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, +} + INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32] PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3] @@ -47,6 +53,7 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(I2SAudioMicrophone), cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), + cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS), } ).extend(cv.COMPONENT_SCHEMA) @@ -86,4 +93,6 @@ async def to_code(config): cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) + cg.add(var.set_channel(CHANNELS[config[CONF_CHANNEL]])) + await microphone.register_microphone(var, config) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 0f45cf95c6..9452762e94 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -49,7 +49,7 @@ void I2SAudioMicrophone::start_() { .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX), .sample_rate = 16000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, + .channel_format = this->channel_, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 4, diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index e704ed2915..acc7d2b45a 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -28,6 +28,8 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub } #endif + void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } + protected: void start_(); void stop_(); @@ -40,6 +42,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub #endif bool pdm_{false}; std::vector buffer_; + i2s_channel_fmt_t channel_; HighFrequencyLoopRequester high_freq_; }; From d2480d31946b5fa3e713ea583cd683003b6bf473 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 22 May 2023 22:43:03 +0200 Subject: [PATCH 30/67] [PSRam] Change log unit to KB to minimize rounding error. (#4872) Co-authored-by: Your Name --- esphome/components/psram/psram.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/psram/psram.cpp b/esphome/components/psram/psram.cpp index 8325709632..68d8dfd697 100644 --- a/esphome/components/psram/psram.cpp +++ b/esphome/components/psram/psram.cpp @@ -21,7 +21,7 @@ void PsramComponent::dump_config() { ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) if (available) { - ESP_LOGCONFIG(TAG, " Size: %d MB", heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024 / 1024); + ESP_LOGCONFIG(TAG, " Size: %d KB", heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024); } #endif } From 9d2467cf62fc6ada43d0dc80cd9693f48e79de50 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 22 May 2023 16:53:10 -0500 Subject: [PATCH 31/67] Bump version to 2023.5.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 1e2218b0da..5304e9b65a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.2" +__version__ = "2023.5.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 19f91a7debafbb6bc54a80baef6edb0121d92664 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 23 May 2023 21:51:12 +0200 Subject: [PATCH 32/67] [internal_temperature] ESP32-S3 needs ESP IDF V4.4.3 or higher (#4873) Co-authored-by: Your Name --- .../internal_temperature.cpp | 4 +++ .../components/internal_temperature/sensor.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index 9a22a77f63..a387708263 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -33,6 +33,10 @@ void InternalTemperatureSensor::update() { temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); temp_sensor_set_config(tsens); temp_sensor_start(); +#if defined(USE_ESP32_VARIANT_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3)) +#error \ + "ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271" +#endif esp_err_t result = temp_sensor_read_celsius(&temperature); temp_sensor_stop(); success = (result == ESP_OK); diff --git a/esphome/components/internal_temperature/sensor.py b/esphome/components/internal_temperature/sensor.py index 2655711bb5..8d462bd801 100644 --- a/esphome/components/internal_temperature/sensor.py +++ b/esphome/components/internal_temperature/sensor.py @@ -1,18 +1,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import ( + VARIANT_ESP32S3, +) from esphome.const import ( STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, DEVICE_CLASS_TEMPERATURE, ENTITY_CATEGORY_DIAGNOSTIC, + KEY_CORE, + KEY_FRAMEWORK_VERSION, ) +from esphome.core import CORE internal_temperature_ns = cg.esphome_ns.namespace("internal_temperature") InternalTemperatureSensor = internal_temperature_ns.class_( "InternalTemperatureSensor", sensor.Sensor, cg.PollingComponent ) + +def validate_config(config): + if CORE.is_esp32: + variant = get_esp32_variant() + if variant == VARIANT_ESP32S3: + if CORE.using_arduino and CORE.data[KEY_CORE][ + KEY_FRAMEWORK_VERSION + ] < cv.Version(2, 0, 6): + raise cv.Invalid( + "ESP32-S3 Internal Temperature Sensor requires framework version 2.0.6 or higher. See ." + ) + if CORE.using_esp_idf and CORE.data[KEY_CORE][ + KEY_FRAMEWORK_VERSION + ] < cv.Version(4, 4, 3): + raise cv.Invalid( + "ESP32-S3 Internal Temperature Sensor requires framework version 4.4.3 or higher. See ." + ) + return config + + CONFIG_SCHEMA = cv.All( sensor.sensor_schema( InternalTemperatureSensor, @@ -23,6 +50,7 @@ CONFIG_SCHEMA = cv.All( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ).extend(cv.polling_component_schema("60s")), cv.only_on(["esp32", "rp2040"]), + validate_config, ) From fb4cb07c6fe20adce221bf991748df477dd9421c Mon Sep 17 00:00:00 2001 From: Davrosx <75336029+Davrosx@users.noreply.github.com> Date: Tue, 23 May 2023 20:52:34 +0100 Subject: [PATCH 33/67] Update cover.h for compile errors with stop() (#4879) --- esphome/components/cover/cover.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index d21fbe02be..89598a9636 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -140,8 +140,9 @@ class Cover : public EntityBase, public EntityBase_DeviceClass { /** Stop the cover. * * This is a legacy method and may be removed later, please use `.make_call()` instead. + * As per solution from issue #2885 the call should include perform() */ - ESPDEPRECATED("stop() is deprecated, use make_call().set_command_stop() instead.", "2021.9") + ESPDEPRECATED("stop() is deprecated, use make_call().set_command_stop().perform() instead.", "2021.9") void stop(); void add_on_state_callback(std::function &&f); From 7d2ae4e25248a2d11888281e81dd4372ba11d077 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 24 May 2023 09:56:15 +1200 Subject: [PATCH 34/67] Print ESPHome version when running commands (#4883) --- esphome/__main__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/__main__.py b/esphome/__main__.py index 78320a05f0..85f7106e3f 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -932,6 +932,8 @@ def run_esphome(argv): _LOGGER.error(e, exc_info=args.verbose) return 1 + safe_print(f"ESPHome {const.__version__}") + for conf_path in args.configuration: if any(os.path.basename(conf_path) == x for x in SECRETS_FILES): _LOGGER.warning("Skipping secrets file %s", conf_path) From 6e414180e0d411ec88211650ec19914b85b76ec8 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 23 May 2023 15:00:33 -0700 Subject: [PATCH 35/67] fix modbus sending FP32_R values (#4882) --- esphome/components/modbus_controller/modbus_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 57f714f233..79c13e3f68 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -506,12 +506,12 @@ void number_to_payload(std::vector &data, int64_t value, SensorValueTy case SensorValueType::U_DWORD: case SensorValueType::S_DWORD: case SensorValueType::FP32: - case SensorValueType::FP32_R: data.push_back((value & 0xFFFF0000) >> 16); data.push_back(value & 0xFFFF); break; case SensorValueType::U_DWORD_R: case SensorValueType::S_DWORD_R: + case SensorValueType::FP32_R: data.push_back(value & 0xFFFF); data.push_back((value & 0xFFFF0000) >> 16); break; From 91ff502872bd86e3dd5d19733c54fb1236fe53b7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 24 May 2023 19:20:06 +1200 Subject: [PATCH 36/67] Fix esp32_rmt_led_strip color modes (#4886) --- esphome/components/esp32_rmt_led_strip/led_strip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index 508f784ec8..11d61b07e1 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -34,7 +34,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { light::LightTraits get_traits() override { auto traits = light::LightTraits(); if (this->is_rgbw_) { - traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::RGB_WHITE}); + traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE}); } else { traits.set_supported_color_modes({light::ColorMode::RGB}); } From 316171491f78842dc342cd3da07c6226ccbc7608 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 24 May 2023 15:58:54 -0500 Subject: [PATCH 37/67] Bump version to 2023.5.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5304e9b65a..0fe9ce18a4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.3" +__version__ = "2023.5.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 69f5674d9e3c9b34e7ed818232ade99fc1b79fde Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 May 2023 09:18:01 +1200 Subject: [PATCH 38/67] Fix version printing not breaking yaml parsing (#4904) --- esphome/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 85f7106e3f..11a363691f 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -932,7 +932,7 @@ def run_esphome(argv): _LOGGER.error(e, exc_info=args.verbose) return 1 - safe_print(f"ESPHome {const.__version__}") + _LOGGER.info("ESPHome %s", const.__version__) for conf_path in args.configuration: if any(os.path.basename(conf_path) == x for x in SECRETS_FILES): From 1057ac4db75a96bd6b7e194e200d520d84e6c583 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 May 2023 09:42:12 +1200 Subject: [PATCH 39/67] Bump version to 2023.5.5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 0fe9ce18a4..fce8ab9f1c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.5.4" +__version__ = "2023.5.5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 5ebb468ccfab535672e8e09e0ee0bc858f9a7957 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:25:36 +1200 Subject: [PATCH 40/67] Bump version to 2023.6.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 470f8a46e5..8820c39d17 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0-dev" +__version__ = "2023.6.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 2ffd430b0ba66b2450f7c8845a2252b7a987a2f1 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:51:44 +1000 Subject: [PATCH 41/67] Add support in vbus component for Deltasol BS 2009 (#4943) --- esphome/components/vbus/__init__.py | 1 + .../components/vbus/binary_sensor/__init__.py | 52 ++++++++ .../vbus/binary_sensor/vbus_binary_sensor.cpp | 22 ++++ .../vbus/binary_sensor/vbus_binary_sensor.h | 21 +++ esphome/components/vbus/sensor/__init__.py | 121 ++++++++++++++++++ .../components/vbus/sensor/vbus_sensor.cpp | 41 ++++++ esphome/components/vbus/sensor/vbus_sensor.h | 31 +++++ 7 files changed, 289 insertions(+) diff --git a/esphome/components/vbus/__init__.py b/esphome/components/vbus/__init__.py index 70f130e23b..99a473a3dc 100644 --- a/esphome/components/vbus/__init__.py +++ b/esphome/components/vbus/__init__.py @@ -15,6 +15,7 @@ VBus = vbus_ns.class_("VBus", uart.UARTDevice, cg.Component) CONF_VBUS_ID = "vbus_id" CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus" +CONF_DELTASOL_BS_2009 = "deltasol_bs_2009" CONF_DELTASOL_C = "deltasol_c" CONF_DELTASOL_CS2 = "deltasol_cs2" CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus" diff --git a/esphome/components/vbus/binary_sensor/__init__.py b/esphome/components/vbus/binary_sensor/__init__.py index 9901fb2724..70fbda2d1f 100644 --- a/esphome/components/vbus/binary_sensor/__init__.py +++ b/esphome/components/vbus/binary_sensor/__init__.py @@ -18,12 +18,14 @@ from .. import ( VBus, CONF_VBUS_ID, CONF_DELTASOL_BS_PLUS, + CONF_DELTASOL_BS_2009, CONF_DELTASOL_C, CONF_DELTASOL_CS2, CONF_DELTASOL_CS_PLUS, ) DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component) +DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009BSensor", cg.Component) DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component) DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component) DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component) @@ -42,6 +44,7 @@ CONF_COLLECTOR_FROST = "collector_frost" CONF_TUBE_COLLECTOR = "tube_collector" CONF_RECOOLING = "recooling" CONF_HQM = "hqm" +CONF_FROST_PROTECTION_ACTIVE = "frost_protection_active" CONFIG_SCHEMA = cv.typed_schema( { @@ -87,6 +90,33 @@ CONFIG_SCHEMA = cv.typed_schema( ), } ), + CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional( + CONF_FROST_PROTECTION_ACTIVE + ): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(DeltaSol_C), @@ -222,6 +252,28 @@ async def to_code(config): sens = await binary_sensor.new_binary_sensor(config[CONF_HQM]) cg.add(var.set_hqm_bsensor(sens)) + elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x427B)) + cg.add(var.set_dest(0x0010)) + if CONF_SENSOR1_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR]) + cg.add(var.set_s1_error_bsensor(sens)) + if CONF_SENSOR2_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR]) + cg.add(var.set_s2_error_bsensor(sens)) + if CONF_SENSOR3_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR]) + cg.add(var.set_s3_error_bsensor(sens)) + if CONF_SENSOR4_ERROR in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR]) + cg.add(var.set_s4_error_bsensor(sens)) + if CONF_FROST_PROTECTION_ACTIVE in config: + sens = await binary_sensor.new_binary_sensor( + config[CONF_FROST_PROTECTION_ACTIVE] + ) + cg.add(var.set_frost_protection_active_bsensor(sens)) + elif config[CONF_MODEL] == CONF_DELTASOL_C: cg.add(var.set_command(0x0100)) cg.add(var.set_source(0x4212)) diff --git a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp index 6edbae22ba..087d049a57 100644 --- a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp +++ b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp @@ -50,6 +50,28 @@ void DeltaSolBSPlusBSensor::handle_message(std::vector &message) { this->hqm_bsensor_->publish_state(message[15] & 0x20); } +void DeltaSolBS2009BSensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol BS 2009:"); + LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_); + LOG_BINARY_SENSOR(" ", "Frost Protection Active", this->frost_protection_active_bsensor_); +} + +void DeltaSolBS2009BSensor::handle_message(std::vector &message) { + if (this->s1_error_bsensor_ != nullptr) + this->s1_error_bsensor_->publish_state(message[20] & 1); + if (this->s2_error_bsensor_ != nullptr) + this->s2_error_bsensor_->publish_state(message[20] & 2); + if (this->s3_error_bsensor_ != nullptr) + this->s3_error_bsensor_->publish_state(message[20] & 4); + if (this->s4_error_bsensor_ != nullptr) + this->s4_error_bsensor_->publish_state(message[20] & 8); + if (this->frost_protection_active_bsensor_ != nullptr) + this->frost_protection_active_bsensor_->publish_state(message[25] & 1); +} + void DeltaSolCBSensor::dump_config() { ESP_LOGCONFIG(TAG, "Deltasol C:"); LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_); diff --git a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h index c0a823a0ab..146aa1b673 100644 --- a/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h +++ b/esphome/components/vbus/binary_sensor/vbus_binary_sensor.h @@ -39,6 +39,27 @@ class DeltaSolBSPlusBSensor : public VBusListener, public Component { void handle_message(std::vector &message) override; }; +class DeltaSolBS2009BSensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; } + void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; } + void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; } + void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; } + void set_frost_protection_active_bsensor(binary_sensor::BinarySensor *bsensor) { + this->frost_protection_active_bsensor_ = bsensor; + } + + protected: + binary_sensor::BinarySensor *s1_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s2_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s3_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *s4_error_bsensor_{nullptr}; + binary_sensor::BinarySensor *frost_protection_active_bsensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + class DeltaSolCBSensor : public VBusListener, public Component { public: void dump_config() override; diff --git a/esphome/components/vbus/sensor/__init__.py b/esphome/components/vbus/sensor/__init__.py index bce28758ce..2ad9da424e 100644 --- a/esphome/components/vbus/sensor/__init__.py +++ b/esphome/components/vbus/sensor/__init__.py @@ -33,12 +33,14 @@ from .. import ( VBus, CONF_VBUS_ID, CONF_DELTASOL_BS_PLUS, + CONF_DELTASOL_BS_2009, CONF_DELTASOL_C, CONF_DELTASOL_CS2, CONF_DELTASOL_CS_PLUS, ) DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component) +DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009Sensor", cg.Component) DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component) DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component) DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component) @@ -142,6 +144,87 @@ CONFIG_SCHEMA = cv.typed_schema( ), } ), + CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009), + cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema( + unit_of_measurement=UNIT_HOUR, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT_HOURS, + icon=ICON_RADIATOR, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TIME): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DURATION, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_VERSION): sensor.sensor_schema( + accuracy_decimals=2, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ), CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(DeltaSol_C), @@ -437,6 +520,44 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_VERSION]) cg.add(var.set_version_sensor(sens)) + elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009: + cg.add(var.set_command(0x0100)) + cg.add(var.set_source(0x427B)) + cg.add(var.set_dest(0x0010)) + if CONF_TEMPERATURE_1 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1]) + cg.add(var.set_temperature1_sensor(sens)) + if CONF_TEMPERATURE_2 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2]) + cg.add(var.set_temperature2_sensor(sens)) + if CONF_TEMPERATURE_3 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3]) + cg.add(var.set_temperature3_sensor(sens)) + if CONF_TEMPERATURE_4 in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4]) + cg.add(var.set_temperature4_sensor(sens)) + if CONF_PUMP_SPEED_1 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1]) + cg.add(var.set_pump_speed1_sensor(sens)) + if CONF_PUMP_SPEED_2 in config: + sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2]) + cg.add(var.set_pump_speed2_sensor(sens)) + if CONF_OPERATING_HOURS_1 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1]) + cg.add(var.set_operating_hours1_sensor(sens)) + if CONF_OPERATING_HOURS_2 in config: + sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2]) + cg.add(var.set_operating_hours2_sensor(sens)) + if CONF_HEAT_QUANTITY in config: + sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY]) + cg.add(var.set_heat_quantity_sensor(sens)) + if CONF_TIME in config: + sens = await sensor.new_sensor(config[CONF_TIME]) + cg.add(var.set_time_sensor(sens)) + if CONF_VERSION in config: + sens = await sensor.new_sensor(config[CONF_VERSION]) + cg.add(var.set_version_sensor(sens)) + elif config[CONF_MODEL] == CONF_DELTASOL_C: cg.add(var.set_command(0x0100)) cg.add(var.set_source(0x4212)) diff --git a/esphome/components/vbus/sensor/vbus_sensor.cpp b/esphome/components/vbus/sensor/vbus_sensor.cpp index 8261773431..5644f707d0 100644 --- a/esphome/components/vbus/sensor/vbus_sensor.cpp +++ b/esphome/components/vbus/sensor/vbus_sensor.cpp @@ -57,6 +57,47 @@ void DeltaSolBSPlusSensor::handle_message(std::vector &message) { this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f); } +void DeltaSolBS2009Sensor::dump_config() { + ESP_LOGCONFIG(TAG, "Deltasol BS 2009:"); + LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); + LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_); + LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_); + LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_); + LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_); + LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_); + LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_); + LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_); + LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_); + LOG_SENSOR(" ", "System Time", this->time_sensor_); + LOG_SENSOR(" ", "FW Version", this->version_sensor_); +} + +void DeltaSolBS2009Sensor::handle_message(std::vector &message) { + if (this->temperature1_sensor_ != nullptr) + this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f); + if (this->temperature2_sensor_ != nullptr) + this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f); + if (this->temperature3_sensor_ != nullptr) + this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f); + if (this->temperature4_sensor_ != nullptr) + this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f); + if (this->pump_speed1_sensor_ != nullptr) + this->pump_speed1_sensor_->publish_state(message[8]); + if (this->pump_speed2_sensor_ != nullptr) + this->pump_speed2_sensor_->publish_state(message[12]); + if (this->operating_hours1_sensor_ != nullptr) + this->operating_hours1_sensor_->publish_state(get_u16(message, 10)); + if (this->operating_hours2_sensor_ != nullptr) + this->operating_hours2_sensor_->publish_state(get_u16(message, 18)); + if (this->heat_quantity_sensor_ != nullptr) { + this->heat_quantity_sensor_->publish_state(get_u16(message, 28) + get_u16(message, 30) * 1000); + } + if (this->time_sensor_ != nullptr) + this->time_sensor_->publish_state(get_u16(message, 22)); + if (this->version_sensor_ != nullptr) + this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f); +} + void DeltaSolCSensor::dump_config() { ESP_LOGCONFIG(TAG, "Deltasol C:"); LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_); diff --git a/esphome/components/vbus/sensor/vbus_sensor.h b/esphome/components/vbus/sensor/vbus_sensor.h index 6ba752b68c..d5535b2019 100644 --- a/esphome/components/vbus/sensor/vbus_sensor.h +++ b/esphome/components/vbus/sensor/vbus_sensor.h @@ -37,6 +37,37 @@ class DeltaSolBSPlusSensor : public VBusListener, public Component { void handle_message(std::vector &message) override; }; +class DeltaSolBS2009Sensor : public VBusListener, public Component { + public: + void dump_config() override; + void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; } + void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; } + void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; } + void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; } + void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; } + void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; } + void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; } + void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; } + void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; } + void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; } + void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; } + + protected: + sensor::Sensor *temperature1_sensor_{nullptr}; + sensor::Sensor *temperature2_sensor_{nullptr}; + sensor::Sensor *temperature3_sensor_{nullptr}; + sensor::Sensor *temperature4_sensor_{nullptr}; + sensor::Sensor *pump_speed1_sensor_{nullptr}; + sensor::Sensor *pump_speed2_sensor_{nullptr}; + sensor::Sensor *operating_hours1_sensor_{nullptr}; + sensor::Sensor *operating_hours2_sensor_{nullptr}; + sensor::Sensor *heat_quantity_sensor_{nullptr}; + sensor::Sensor *time_sensor_{nullptr}; + sensor::Sensor *version_sensor_{nullptr}; + + void handle_message(std::vector &message) override; +}; + class DeltaSolCSensor : public VBusListener, public Component { public: void dump_config() override; From 407b5e199e40d4b66e60ad4ec6454f38b87112bd Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Thu, 15 Jun 2023 01:05:28 -0700 Subject: [PATCH 42/67] fix vbus sensor offsets (#4952) --- esphome/components/vbus/sensor/vbus_sensor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/vbus/sensor/vbus_sensor.cpp b/esphome/components/vbus/sensor/vbus_sensor.cpp index 5644f707d0..5b4f57f73d 100644 --- a/esphome/components/vbus/sensor/vbus_sensor.cpp +++ b/esphome/components/vbus/sensor/vbus_sensor.cpp @@ -207,9 +207,9 @@ void DeltaSolCSPlusSensor::handle_message(std::vector &message) { if (this->heat_quantity_sensor_ != nullptr) this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28)); if (this->time_sensor_ != nullptr) - this->time_sensor_->publish_state(get_u16(message, 12)); + this->time_sensor_->publish_state(get_u16(message, 22)); if (this->version_sensor_ != nullptr) - this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f); + this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f); if (this->flow_rate_sensor_ != nullptr) this->flow_rate_sensor_->publish_state(get_u16(message, 38)); } From abca47f36f9ed9f08dc4c67ac20b038c6b5e0110 Mon Sep 17 00:00:00 2001 From: guillempages Date: Fri, 16 Jun 2023 01:39:50 +0200 Subject: [PATCH 43/67] Add support for ESP32-S3-BOX-Lite displays (#4941) --- esphome/components/ili9xxx/display.py | 1 + .../components/ili9xxx/ili9xxx_display.cpp | 12 ++++++++ esphome/components/ili9xxx/ili9xxx_display.h | 5 ++++ esphome/components/ili9xxx/ili9xxx_init.h | 30 +++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 780c64ec70..98edadb6a5 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -44,6 +44,7 @@ MODELS = { "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), "ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI), + "S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI), } COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 67d643fe31..ad70bd6e48 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -421,5 +421,17 @@ void ILI9XXXST7796::initialize() { } } +// 24_TFT rotated display +void ILI9XXXS3BoxLite::initialize() { + this->init_lcd_(INITCMD_S3BOXLITE); + if (this->width_ == 0) { + this->width_ = 320; + } + if (this->height_ == 0) { + this->height_ = 240; + } + this->invert_display_(true); +} + } // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 8a8cd4bb44..dbdf023bc0 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -134,5 +134,10 @@ class ILI9XXXST7796 : public ILI9XXXDisplay { void initialize() override; }; +class ILI9XXXS3BoxLite : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + } // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 593b9a79ce..36cc30ec2e 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -169,6 +169,36 @@ static const uint8_t PROGMEM INITCMD_ST7796[] = { 0x00 // End of list }; +static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = { + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0] + ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control + ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2 + ILI9XXX_MADCTL , 1, 0x40, // Memory Access Control + ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9XXX_PIXFMT , 1, 0x55, + ILI9XXX_FRMCTR1 , 2, 0x00, 0x18, + ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected + ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, + 0x0E, 0x09, 0x00, + ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, + 0x31, 0x36, 0x0F, + ILI9XXX_SLPOUT , 0x80, // Exit Sleep + ILI9XXX_DISPON , 0x80, // Display on + 0x00 // End of list +}; + // clang-format on } // namespace ili9xxx } // namespace esphome From 6aa3092be075a4d0886fe0f8827751a41ce18931 Mon Sep 17 00:00:00 2001 From: guillempages Date: Sat, 17 Jun 2023 10:32:07 +0200 Subject: [PATCH 44/67] Split display_buffer sub-components into own files (#4950) * Split display_buffer sub-components into own files Move the Image, Animation and Font classes to their own h/cpp pairs, instead of having everything into the display_buffer h/cpp files. * Fixed COLOR_ON duplicate definition --- esphome/components/display/animation.cpp | 69 ++++ esphome/components/display/animation.h | 37 +++ esphome/components/display/display_buffer.cpp | 303 +----------------- esphome/components/display/display_buffer.h | 136 +------- esphome/components/display/font.cpp | 105 ++++++ esphome/components/display/font.h | 66 ++++ esphome/components/display/image.cpp | 135 ++++++++ esphome/components/display/image.h | 75 +++++ tests/test2.yaml | 7 + 9 files changed, 502 insertions(+), 431 deletions(-) create mode 100644 esphome/components/display/animation.cpp create mode 100644 esphome/components/display/animation.h create mode 100644 esphome/components/display/font.cpp create mode 100644 esphome/components/display/font.h create mode 100644 esphome/components/display/image.cpp create mode 100644 esphome/components/display/image.h diff --git a/esphome/components/display/animation.cpp b/esphome/components/display/animation.cpp new file mode 100644 index 0000000000..d68084b68d --- /dev/null +++ b/esphome/components/display/animation.cpp @@ -0,0 +1,69 @@ +#include "animation.h" + +#include "esphome/core/hal.h" + +namespace esphome { +namespace display { + +Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) + : Image(data_start, width, height, type), + animation_data_start_(data_start), + current_frame_(0), + animation_frame_count_(animation_frame_count), + loop_start_frame_(0), + loop_end_frame_(animation_frame_count_), + loop_count_(0), + loop_current_iteration_(1) {} +void Animation::set_loop(uint32_t start_frame, uint32_t end_frame, int count) { + loop_start_frame_ = std::min(start_frame, animation_frame_count_); + loop_end_frame_ = std::min(end_frame, animation_frame_count_); + loop_count_ = count; + loop_current_iteration_ = 1; +} + +uint32_t Animation::get_animation_frame_count() const { return this->animation_frame_count_; } +int Animation::get_current_frame() const { return this->current_frame_; } +void Animation::next_frame() { + this->current_frame_++; + if (loop_count_ && this->current_frame_ == loop_end_frame_ && + (this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) { + this->current_frame_ = loop_start_frame_; + this->loop_current_iteration_++; + } + if (this->current_frame_ >= animation_frame_count_) { + this->loop_current_iteration_ = 1; + this->current_frame_ = 0; + } + + this->update_data_start_(); +} +void Animation::prev_frame() { + this->current_frame_--; + if (this->current_frame_ < 0) { + this->current_frame_ = this->animation_frame_count_ - 1; + } + + this->update_data_start_(); +} + +void Animation::set_frame(int frame) { + unsigned abs_frame = abs(frame); + + if (abs_frame < this->animation_frame_count_) { + if (frame >= 0) { + this->current_frame_ = frame; + } else { + this->current_frame_ = this->animation_frame_count_ - abs_frame; + } + } + + this->update_data_start_(); +} + +void Animation::update_data_start_() { + const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_; + this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_; +} + +} // namespace display +} // namespace esphome diff --git a/esphome/components/display/animation.h b/esphome/components/display/animation.h new file mode 100644 index 0000000000..38e632ccf0 --- /dev/null +++ b/esphome/components/display/animation.h @@ -0,0 +1,37 @@ +#pragma once +#include "image.h" + +namespace esphome { +namespace display { + +class Animation : public Image { + public: + Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type); + + uint32_t get_animation_frame_count() const; + int get_current_frame() const; + void next_frame(); + void prev_frame(); + + /** Selects a specific frame within the animation. + * + * @param frame If possitive, advance to the frame. If negative, recede to that frame from the end frame. + */ + void set_frame(int frame); + + void set_loop(uint32_t start_frame, uint32_t end_frame, int count); + + protected: + void update_data_start_(); + + const uint8_t *animation_data_start_; + int current_frame_; + uint32_t animation_frame_count_; + uint32_t loop_start_frame_; + uint32_t loop_end_frame_; + int loop_count_; + int loop_current_iteration_; +}; + +} // namespace display +} // namespace esphome diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index c8dc7b62e2..8092b9f12f 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -7,6 +7,10 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "animation.h" +#include "image.h" +#include "font.h" + namespace esphome { namespace display { @@ -15,25 +19,6 @@ static const char *const TAG = "display"; const Color COLOR_OFF(0, 0, 0, 255); const Color COLOR_ON(255, 255, 255, 255); -static int image_type_to_bpp(ImageType type) { - switch (type) { - case IMAGE_TYPE_BINARY: - return 1; - case IMAGE_TYPE_GRAYSCALE: - return 8; - case IMAGE_TYPE_RGB565: - return 16; - case IMAGE_TYPE_RGB24: - return 24; - case IMAGE_TYPE_RGBA: - return 32; - default: - return 0; - } -} - -static int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; } - void Rect::expand(int16_t horizontal, int16_t vertical) { if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) { this->x = this->x - horizontal; @@ -505,286 +490,6 @@ Rect DisplayBuffer::get_clipping() { return this->clipping_rectangle_.back(); } } -bool Glyph::get_pixel(int x, int y) const { - const int x_data = x - this->glyph_data_->offset_x; - const int y_data = y - this->glyph_data_->offset_y; - if (x_data < 0 || x_data >= this->glyph_data_->width || y_data < 0 || y_data >= this->glyph_data_->height) - return false; - const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u; - const uint32_t pos = x_data + y_data * width_8; - return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); -} -const char *Glyph::get_char() const { return this->glyph_data_->a_char; } -bool Glyph::compare_to(const char *str) const { - // 1 -> this->char_ - // 2 -> str - for (uint32_t i = 0;; i++) { - if (this->glyph_data_->a_char[i] == '\0') - return true; - if (str[i] == '\0') - return false; - if (this->glyph_data_->a_char[i] > str[i]) - return false; - if (this->glyph_data_->a_char[i] < str[i]) - return true; - } - // this should not happen - return false; -} -int Glyph::match_length(const char *str) const { - for (uint32_t i = 0;; i++) { - if (this->glyph_data_->a_char[i] == '\0') - return i; - if (str[i] != this->glyph_data_->a_char[i]) - return 0; - } - // this should not happen - return 0; -} -void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const { - *x1 = this->glyph_data_->offset_x; - *y1 = this->glyph_data_->offset_y; - *width = this->glyph_data_->width; - *height = this->glyph_data_->height; -} -int Font::match_next_glyph(const char *str, int *match_length) { - int lo = 0; - int hi = this->glyphs_.size() - 1; - while (lo != hi) { - int mid = (lo + hi + 1) / 2; - if (this->glyphs_[mid].compare_to(str)) { - lo = mid; - } else { - hi = mid - 1; - } - } - *match_length = this->glyphs_[lo].match_length(str); - if (*match_length <= 0) - return -1; - return lo; -} -void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) { - *baseline = this->baseline_; - *height = this->height_; - int i = 0; - int min_x = 0; - bool has_char = false; - int x = 0; - while (str[i] != '\0') { - int match_length; - int glyph_n = this->match_next_glyph(str + i, &match_length); - if (glyph_n < 0) { - // Unknown char, skip - if (!this->get_glyphs().empty()) - x += this->get_glyphs()[0].glyph_data_->width; - i++; - continue; - } - - const Glyph &glyph = this->glyphs_[glyph_n]; - if (!has_char) { - min_x = glyph.glyph_data_->offset_x; - } else { - min_x = std::min(min_x, x + glyph.glyph_data_->offset_x); - } - x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x; - - i += match_length; - has_char = true; - } - *x_offset = min_x; - *width = x - min_x; -} -Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) { - glyphs_.reserve(data_nr); - for (int i = 0; i < data_nr; ++i) - glyphs_.emplace_back(&data[i]); -} - -void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) { - switch (type_) { - case IMAGE_TYPE_BINARY: { - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - if (this->get_binary_pixel_(img_x, img_y)) { - display->draw_pixel_at(x + img_x, y + img_y, color_on); - } else if (!this->transparent_) { - display->draw_pixel_at(x + img_x, y + img_y, color_off); - } - } - } - break; - } - case IMAGE_TYPE_GRAYSCALE: - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_grayscale_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGB565: - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_rgb565_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGB24: - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_rgb24_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGBA: - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_rgba_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - } -} -Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const { - if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) - return color_off; - switch (this->type_) { - case IMAGE_TYPE_BINARY: - return this->get_binary_pixel_(x, y) ? color_on : color_off; - case IMAGE_TYPE_GRAYSCALE: - return this->get_grayscale_pixel_(x, y); - case IMAGE_TYPE_RGB565: - return this->get_rgb565_pixel_(x, y); - case IMAGE_TYPE_RGB24: - return this->get_rgb24_pixel_(x, y); - case IMAGE_TYPE_RGBA: - return this->get_rgba_pixel_(x, y); - default: - return color_off; - } -} -bool Image::get_binary_pixel_(int x, int y) const { - const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; - const uint32_t pos = x + y * width_8; - return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); -} -Color Image::get_rgba_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 4; - return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), - progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3)); -} -Color Image::get_rgb24_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 3; - Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), - progmem_read_byte(this->data_start_ + pos + 2)); - if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) { - // (0, 0, 1) has been defined as transparent color for non-alpha images. - // putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if) - color.w = 0; - } else { - color.w = 0xFF; - } - return color; -} -Color Image::get_rgb565_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 2; - uint16_t rgb565 = - progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1); - auto r = (rgb565 & 0xF800) >> 11; - auto g = (rgb565 & 0x07E0) >> 5; - auto b = rgb565 & 0x001F; - Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2)); - if (rgb565 == 0x0020 && transparent_) { - // darkest green has been defined as transparent color for transparent RGB565 images. - color.w = 0; - } else { - color.w = 0xFF; - } - return color; -} -Color Image::get_grayscale_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_); - const uint8_t gray = progmem_read_byte(this->data_start_ + pos); - uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF; - return Color(gray, gray, gray, alpha); -} -int Image::get_width() const { return this->width_; } -int Image::get_height() const { return this->height_; } -ImageType Image::get_type() const { return this->type_; } -Image::Image(const uint8_t *data_start, int width, int height, ImageType type) - : width_(width), height_(height), type_(type), data_start_(data_start) {} - -Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) - : Image(data_start, width, height, type), - animation_data_start_(data_start), - current_frame_(0), - animation_frame_count_(animation_frame_count), - loop_start_frame_(0), - loop_end_frame_(animation_frame_count_), - loop_count_(0), - loop_current_iteration_(1) {} -void Animation::set_loop(uint32_t start_frame, uint32_t end_frame, int count) { - loop_start_frame_ = std::min(start_frame, animation_frame_count_); - loop_end_frame_ = std::min(end_frame, animation_frame_count_); - loop_count_ = count; - loop_current_iteration_ = 1; -} - -uint32_t Animation::get_animation_frame_count() const { return this->animation_frame_count_; } -int Animation::get_current_frame() const { return this->current_frame_; } -void Animation::next_frame() { - this->current_frame_++; - if (loop_count_ && this->current_frame_ == loop_end_frame_ && - (this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) { - this->current_frame_ = loop_start_frame_; - this->loop_current_iteration_++; - } - if (this->current_frame_ >= animation_frame_count_) { - this->loop_current_iteration_ = 1; - this->current_frame_ = 0; - } - - this->update_data_start_(); -} -void Animation::prev_frame() { - this->current_frame_--; - if (this->current_frame_ < 0) { - this->current_frame_ = this->animation_frame_count_ - 1; - } - - this->update_data_start_(); -} - -void Animation::set_frame(int frame) { - unsigned abs_frame = abs(frame); - - if (abs_frame < this->animation_frame_count_) { - if (frame >= 0) { - this->current_frame_ = frame; - } else { - this->current_frame_ = this->animation_frame_count_ - abs_frame; - } - } - - this->update_data_start_(); -} - -void Animation::update_data_start_() { - const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_; - this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_; -} DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {} void DisplayPage::show() { this->parent_->show_page(this); } diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 0c31ac24d9..b66ec529f7 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -16,6 +16,10 @@ #include "esphome/components/qr_code/qr_code.h" #endif +#include "animation.h" +#include "font.h" +#include "image.h" + namespace esphome { namespace display { @@ -70,19 +74,6 @@ enum class TextAlign { BOTTOM_RIGHT = BOTTOM | RIGHT, }; -/// Turn the pixel OFF. -extern const Color COLOR_OFF; -/// Turn the pixel ON. -extern const Color COLOR_ON; - -enum ImageType { - IMAGE_TYPE_BINARY = 0, - IMAGE_TYPE_GRAYSCALE = 1, - IMAGE_TYPE_RGB24 = 2, - IMAGE_TYPE_RGB565 = 3, - IMAGE_TYPE_RGBA = 4, -}; - enum DisplayType { DISPLAY_TYPE_BINARY = 1, DISPLAY_TYPE_GRAYSCALE = 2, @@ -123,8 +114,6 @@ class Rect { void info(const std::string &prefix = "rect info:"); }; -class BaseImage; -class Font; class DisplayBuffer; class DisplayPage; class DisplayOnPageChangeTrigger; @@ -475,123 +464,6 @@ class DisplayPage { DisplayPage *next_{nullptr}; }; -struct GlyphData { - const char *a_char; - const uint8_t *data; - int offset_x; - int offset_y; - int width; - int height; -}; - -class Glyph { - public: - Glyph(const GlyphData *data) : glyph_data_(data) {} - - bool get_pixel(int x, int y) const; - - const char *get_char() const; - - bool compare_to(const char *str) const; - - int match_length(const char *str) const; - - void scan_area(int *x1, int *y1, int *width, int *height) const; - - protected: - friend Font; - friend DisplayBuffer; - - const GlyphData *glyph_data_; -}; - -class Font { - public: - /** Construct the font with the given glyphs. - * - * @param glyphs A vector of glyphs, must be sorted lexicographically. - * @param baseline The y-offset from the top of the text to the baseline. - * @param bottom The y-offset from the top of the text to the bottom (i.e. height). - */ - Font(const GlyphData *data, int data_nr, int baseline, int height); - - int match_next_glyph(const char *str, int *match_length); - - void measure(const char *str, int *width, int *x_offset, int *baseline, int *height); - inline int get_baseline() { return this->baseline_; } - inline int get_height() { return this->height_; } - - const std::vector> &get_glyphs() const { return glyphs_; } - - protected: - std::vector> glyphs_; - int baseline_; - int height_; -}; - -class BaseImage { - public: - virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0; - virtual int get_width() const = 0; - virtual int get_height() const = 0; -}; - -class Image : public BaseImage { - public: - Image(const uint8_t *data_start, int width, int height, ImageType type); - Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const; - int get_width() const override; - int get_height() const override; - ImageType get_type() const; - - void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override; - - void set_transparency(bool transparent) { transparent_ = transparent; } - bool has_transparency() const { return transparent_; } - - protected: - bool get_binary_pixel_(int x, int y) const; - Color get_rgb24_pixel_(int x, int y) const; - Color get_rgba_pixel_(int x, int y) const; - Color get_rgb565_pixel_(int x, int y) const; - Color get_grayscale_pixel_(int x, int y) const; - - int width_; - int height_; - ImageType type_; - const uint8_t *data_start_; - bool transparent_; -}; - -class Animation : public Image { - public: - Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type); - - uint32_t get_animation_frame_count() const; - int get_current_frame() const; - void next_frame(); - void prev_frame(); - - /** Selects a specific frame within the animation. - * - * @param frame If possitive, advance to the frame. If negative, recede to that frame from the end frame. - */ - void set_frame(int frame); - - void set_loop(uint32_t start_frame, uint32_t end_frame, int count); - - protected: - void update_data_start_(); - - const uint8_t *animation_data_start_; - int current_frame_; - uint32_t animation_frame_count_; - uint32_t loop_start_frame_; - uint32_t loop_end_frame_; - int loop_count_; - int loop_current_iteration_; -}; - template class DisplayPageShowAction : public Action { public: TEMPLATABLE_VALUE(DisplayPage *, page) diff --git a/esphome/components/display/font.cpp b/esphome/components/display/font.cpp new file mode 100644 index 0000000000..1833ef5023 --- /dev/null +++ b/esphome/components/display/font.cpp @@ -0,0 +1,105 @@ +#include "font.h" + +#include "esphome/core/hal.h" + +namespace esphome { +namespace display { + +bool Glyph::get_pixel(int x, int y) const { + const int x_data = x - this->glyph_data_->offset_x; + const int y_data = y - this->glyph_data_->offset_y; + if (x_data < 0 || x_data >= this->glyph_data_->width || y_data < 0 || y_data >= this->glyph_data_->height) + return false; + const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u; + const uint32_t pos = x_data + y_data * width_8; + return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); +} +const char *Glyph::get_char() const { return this->glyph_data_->a_char; } +bool Glyph::compare_to(const char *str) const { + // 1 -> this->char_ + // 2 -> str + for (uint32_t i = 0;; i++) { + if (this->glyph_data_->a_char[i] == '\0') + return true; + if (str[i] == '\0') + return false; + if (this->glyph_data_->a_char[i] > str[i]) + return false; + if (this->glyph_data_->a_char[i] < str[i]) + return true; + } + // this should not happen + return false; +} +int Glyph::match_length(const char *str) const { + for (uint32_t i = 0;; i++) { + if (this->glyph_data_->a_char[i] == '\0') + return i; + if (str[i] != this->glyph_data_->a_char[i]) + return 0; + } + // this should not happen + return 0; +} +void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const { + *x1 = this->glyph_data_->offset_x; + *y1 = this->glyph_data_->offset_y; + *width = this->glyph_data_->width; + *height = this->glyph_data_->height; +} +int Font::match_next_glyph(const char *str, int *match_length) { + int lo = 0; + int hi = this->glyphs_.size() - 1; + while (lo != hi) { + int mid = (lo + hi + 1) / 2; + if (this->glyphs_[mid].compare_to(str)) { + lo = mid; + } else { + hi = mid - 1; + } + } + *match_length = this->glyphs_[lo].match_length(str); + if (*match_length <= 0) + return -1; + return lo; +} +void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) { + *baseline = this->baseline_; + *height = this->height_; + int i = 0; + int min_x = 0; + bool has_char = false; + int x = 0; + while (str[i] != '\0') { + int match_length; + int glyph_n = this->match_next_glyph(str + i, &match_length); + if (glyph_n < 0) { + // Unknown char, skip + if (!this->get_glyphs().empty()) + x += this->get_glyphs()[0].glyph_data_->width; + i++; + continue; + } + + const Glyph &glyph = this->glyphs_[glyph_n]; + if (!has_char) { + min_x = glyph.glyph_data_->offset_x; + } else { + min_x = std::min(min_x, x + glyph.glyph_data_->offset_x); + } + x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x; + + i += match_length; + has_char = true; + } + *x_offset = min_x; + *width = x - min_x; +} +Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) { + glyphs_.reserve(data_nr); + for (int i = 0; i < data_nr; ++i) + glyphs_.emplace_back(&data[i]); +} + +} // namespace display +} // namespace esphome diff --git a/esphome/components/display/font.h b/esphome/components/display/font.h new file mode 100644 index 0000000000..08b457116e --- /dev/null +++ b/esphome/components/display/font.h @@ -0,0 +1,66 @@ +#pragma once + +#include "esphome/core/datatypes.h" + +namespace esphome { +namespace display { + +class DisplayBuffer; +class Font; + +struct GlyphData { + const char *a_char; + const uint8_t *data; + int offset_x; + int offset_y; + int width; + int height; +}; + +class Glyph { + public: + Glyph(const GlyphData *data) : glyph_data_(data) {} + + bool get_pixel(int x, int y) const; + + const char *get_char() const; + + bool compare_to(const char *str) const; + + int match_length(const char *str) const; + + void scan_area(int *x1, int *y1, int *width, int *height) const; + + protected: + friend Font; + friend DisplayBuffer; + + const GlyphData *glyph_data_; +}; + +class Font { + public: + /** Construct the font with the given glyphs. + * + * @param glyphs A vector of glyphs, must be sorted lexicographically. + * @param baseline The y-offset from the top of the text to the baseline. + * @param bottom The y-offset from the top of the text to the bottom (i.e. height). + */ + Font(const GlyphData *data, int data_nr, int baseline, int height); + + int match_next_glyph(const char *str, int *match_length); + + void measure(const char *str, int *width, int *x_offset, int *baseline, int *height); + inline int get_baseline() { return this->baseline_; } + inline int get_height() { return this->height_; } + + const std::vector> &get_glyphs() const { return glyphs_; } + + protected: + std::vector> glyphs_; + int baseline_; + int height_; +}; + +} // namespace display +} // namespace esphome diff --git a/esphome/components/display/image.cpp b/esphome/components/display/image.cpp new file mode 100644 index 0000000000..b3cab3ff7f --- /dev/null +++ b/esphome/components/display/image.cpp @@ -0,0 +1,135 @@ +#include "image.h" + +#include "esphome/core/hal.h" +#include "display_buffer.h" + +namespace esphome { +namespace display { + +void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) { + switch (type_) { + case IMAGE_TYPE_BINARY: { + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + if (this->get_binary_pixel_(img_x, img_y)) { + display->draw_pixel_at(x + img_x, y + img_y, color_on); + } else if (!this->transparent_) { + display->draw_pixel_at(x + img_x, y + img_y, color_off); + } + } + } + break; + } + case IMAGE_TYPE_GRAYSCALE: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_grayscale_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGB565: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgb565_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGB24: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgb24_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + case IMAGE_TYPE_RGBA: + for (int img_x = 0; img_x < width_; img_x++) { + for (int img_y = 0; img_y < height_; img_y++) { + auto color = this->get_rgba_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + } + } + break; + } +} +Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const { + if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) + return color_off; + switch (this->type_) { + case IMAGE_TYPE_BINARY: + return this->get_binary_pixel_(x, y) ? color_on : color_off; + case IMAGE_TYPE_GRAYSCALE: + return this->get_grayscale_pixel_(x, y); + case IMAGE_TYPE_RGB565: + return this->get_rgb565_pixel_(x, y); + case IMAGE_TYPE_RGB24: + return this->get_rgb24_pixel_(x, y); + case IMAGE_TYPE_RGBA: + return this->get_rgba_pixel_(x, y); + default: + return color_off; + } +} +bool Image::get_binary_pixel_(int x, int y) const { + const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; + const uint32_t pos = x + y * width_8; + return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); +} +Color Image::get_rgba_pixel_(int x, int y) const { + const uint32_t pos = (x + y * this->width_) * 4; + return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), + progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3)); +} +Color Image::get_rgb24_pixel_(int x, int y) const { + const uint32_t pos = (x + y * this->width_) * 3; + Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), + progmem_read_byte(this->data_start_ + pos + 2)); + if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) { + // (0, 0, 1) has been defined as transparent color for non-alpha images. + // putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if) + color.w = 0; + } else { + color.w = 0xFF; + } + return color; +} +Color Image::get_rgb565_pixel_(int x, int y) const { + const uint32_t pos = (x + y * this->width_) * 2; + uint16_t rgb565 = + progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1); + auto r = (rgb565 & 0xF800) >> 11; + auto g = (rgb565 & 0x07E0) >> 5; + auto b = rgb565 & 0x001F; + Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2)); + if (rgb565 == 0x0020 && transparent_) { + // darkest green has been defined as transparent color for transparent RGB565 images. + color.w = 0; + } else { + color.w = 0xFF; + } + return color; +} +Color Image::get_grayscale_pixel_(int x, int y) const { + const uint32_t pos = (x + y * this->width_); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); + uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF; + return Color(gray, gray, gray, alpha); +} +int Image::get_width() const { return this->width_; } +int Image::get_height() const { return this->height_; } +ImageType Image::get_type() const { return this->type_; } +Image::Image(const uint8_t *data_start, int width, int height, ImageType type) + : width_(width), height_(height), type_(type), data_start_(data_start) {} + +} // namespace display +} // namespace esphome diff --git a/esphome/components/display/image.h b/esphome/components/display/image.h new file mode 100644 index 0000000000..ac2d5a3421 --- /dev/null +++ b/esphome/components/display/image.h @@ -0,0 +1,75 @@ +#pragma once +#include "esphome/core/color.h" + +namespace esphome { +namespace display { + +enum ImageType { + IMAGE_TYPE_BINARY = 0, + IMAGE_TYPE_GRAYSCALE = 1, + IMAGE_TYPE_RGB24 = 2, + IMAGE_TYPE_RGB565 = 3, + IMAGE_TYPE_RGBA = 4, +}; + +inline int image_type_to_bpp(ImageType type) { + switch (type) { + case IMAGE_TYPE_BINARY: + return 1; + case IMAGE_TYPE_GRAYSCALE: + return 8; + case IMAGE_TYPE_RGB565: + return 16; + case IMAGE_TYPE_RGB24: + return 24; + case IMAGE_TYPE_RGBA: + return 32; + } + return 0; +} + +inline int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; } + +/// Turn the pixel OFF. +extern const Color COLOR_OFF; +/// Turn the pixel ON. +extern const Color COLOR_ON; + +class DisplayBuffer; + +class BaseImage { + public: + virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0; + virtual int get_width() const = 0; + virtual int get_height() const = 0; +}; + +class Image : public BaseImage { + public: + Image(const uint8_t *data_start, int width, int height, ImageType type); + Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const; + int get_width() const override; + int get_height() const override; + ImageType get_type() const; + + void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override; + + void set_transparency(bool transparent) { transparent_ = transparent; } + bool has_transparency() const { return transparent_; } + + protected: + bool get_binary_pixel_(int x, int y) const; + Color get_rgb24_pixel_(int x, int y) const; + Color get_rgba_pixel_(int x, int y) const; + Color get_rgb565_pixel_(int x, int y) const; + Color get_grayscale_pixel_(int x, int y) const; + + int width_; + int height_; + ImageType type_; + const uint8_t *data_start_; + bool transparent_; +}; + +} // namespace display +} // namespace esphome diff --git a/tests/test2.yaml b/tests/test2.yaml index 2873d0e8e0..fa4b97c7c1 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -695,6 +695,13 @@ image: file: mdi:alert-outline type: BINARY +graph: + - id: my_graph + sensor: ha_hello_world_temperature + duration: 1h + width: 100 + height: 100 + cap1188: id: cap1188_component address: 0x29 From a4ef26749b391daed1543950d4aeab78b45654e1 Mon Sep 17 00:00:00 2001 From: guillempages Date: Sat, 17 Jun 2023 10:38:44 +0200 Subject: [PATCH 45/67] Add support for ESP32-S3-BOX displays (#4942) The ESP32-S3-BOX display has an ILI9xxx driver Add the needed configuration so that it works. --- esphome/components/ili9xxx/display.py | 1 + .../components/ili9xxx/ili9xxx_display.cpp | 11 +++++++ esphome/components/ili9xxx/ili9xxx_display.h | 5 ++++ esphome/components/ili9xxx/ili9xxx_init.h | 30 +++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 98edadb6a5..29603eb30f 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -44,6 +44,7 @@ MODELS = { "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), "ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI), + "S3BOX": ili9XXX_ns.class_("ILI9XXXS3Box", ili9XXXSPI), "S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI), } diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index ad70bd6e48..6fc6da3cdb 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -421,6 +421,17 @@ void ILI9XXXST7796::initialize() { } } +// 24_TFT rotated display +void ILI9XXXS3Box::initialize() { + this->init_lcd_(INITCMD_S3BOX); + if (this->width_ == 0) { + this->width_ = 320; + } + if (this->height_ == 0) { + this->height_ = 240; + } +} + // 24_TFT rotated display void ILI9XXXS3BoxLite::initialize() { this->init_lcd_(INITCMD_S3BOXLITE); diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index dbdf023bc0..dc7bfdc6eb 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -134,6 +134,11 @@ class ILI9XXXST7796 : public ILI9XXXDisplay { void initialize() override; }; +class ILI9XXXS3Box : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + class ILI9XXXS3BoxLite : public ILI9XXXDisplay { protected: void initialize() override; diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 36cc30ec2e..a17e6b127c 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -169,6 +169,36 @@ static const uint8_t PROGMEM INITCMD_ST7796[] = { 0x00 // End of list }; +static const uint8_t PROGMEM INITCMD_S3BOX[] = { + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0] + ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control + ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2 + ILI9XXX_MADCTL , 1, 0xC8, // Memory Access Control + ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9XXX_PIXFMT , 1, 0x55, + ILI9XXX_FRMCTR1 , 2, 0x00, 0x18, + ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected + ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, + 0x0E, 0x09, 0x00, + ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, + 0x31, 0x36, 0x0F, + ILI9XXX_SLPOUT , 0x80, // Exit Sleep + ILI9XXX_DISPON , 0x80, // Display on + 0x00 // End of list +}; + static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = { 0xEF, 3, 0x03, 0x80, 0x02, 0xCF, 3, 0x00, 0xC1, 0x30, From b558a1c9ddb5d9d1fba2944a0836f3ccb6365d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Sun, 18 Jun 2023 21:24:23 +0200 Subject: [PATCH 46/67] display: allow to align image with `ImageAlign` (#4933) --- esphome/components/display/display_buffer.cpp | 31 ++++++++++ esphome/components/display/display_buffer.h | 61 ++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 8092b9f12f..672c6a22b0 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -311,6 +311,37 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al } void DisplayBuffer::image(int x, int y, BaseImage *image, Color color_on, Color color_off) { + this->image(x, y, image, ImageAlign::TOP_LEFT, color_on, color_off); +} + +void DisplayBuffer::image(int x, int y, BaseImage *image, ImageAlign align, Color color_on, Color color_off) { + auto x_align = ImageAlign(int(align) & (int(ImageAlign::HORIZONTAL_ALIGNMENT))); + auto y_align = ImageAlign(int(align) & (int(ImageAlign::VERTICAL_ALIGNMENT))); + + switch (x_align) { + case ImageAlign::RIGHT: + x -= image->get_width(); + break; + case ImageAlign::CENTER_HORIZONTAL: + x -= image->get_width() / 2; + break; + case ImageAlign::LEFT: + default: + break; + } + + switch (y_align) { + case ImageAlign::BOTTOM: + y -= image->get_height(); + break; + case ImageAlign::CENTER_VERTICAL: + y -= image->get_height() / 2; + break; + case ImageAlign::TOP: + default: + break; + } + image->draw(x, y, this, color_on, color_off); } diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index b66ec529f7..652039517f 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -74,6 +74,54 @@ enum class TextAlign { BOTTOM_RIGHT = BOTTOM | RIGHT, }; +/** ImageAlign is used to tell the display class how to position a image. By default + * the coordinates you enter for the image() functions take the upper left corner of the image + * as the "anchor" point. You can customize this behavior to, for example, make the coordinates + * refer to the *center* of the image. + * + * All image alignments consist of an X and Y-coordinate alignment. For the alignment along the X-axis + * these options are allowed: + * + * - LEFT (x-coordinate of anchor point is on left) + * - CENTER_HORIZONTAL (x-coordinate of anchor point is in the horizontal center of the image) + * - RIGHT (x-coordinate of anchor point is on right) + * + * For the Y-Axis alignment these options are allowed: + * + * - TOP (y-coordinate of anchor is on the top of the image) + * - CENTER_VERTICAL (y-coordinate of anchor is in the vertical center of the image) + * - BOTTOM (y-coordinate of anchor is on the bottom of the image) + * + * These options are then combined to create combined TextAlignment options like: + * - TOP_LEFT (default) + * - CENTER (anchor point is in the middle of the image bounds) + * - ... + */ +enum class ImageAlign { + TOP = 0x00, + CENTER_VERTICAL = 0x01, + BOTTOM = 0x02, + + LEFT = 0x00, + CENTER_HORIZONTAL = 0x04, + RIGHT = 0x08, + + TOP_LEFT = TOP | LEFT, + TOP_CENTER = TOP | CENTER_HORIZONTAL, + TOP_RIGHT = TOP | RIGHT, + + CENTER_LEFT = CENTER_VERTICAL | LEFT, + CENTER = CENTER_VERTICAL | CENTER_HORIZONTAL, + CENTER_RIGHT = CENTER_VERTICAL | RIGHT, + + BOTTOM_LEFT = BOTTOM | LEFT, + BOTTOM_CENTER = BOTTOM | CENTER_HORIZONTAL, + BOTTOM_RIGHT = BOTTOM | RIGHT, + + HORIZONTAL_ALIGNMENT = LEFT | CENTER_HORIZONTAL | RIGHT, + VERTICAL_ALIGNMENT = TOP | CENTER_VERTICAL | BOTTOM +}; + enum DisplayType { DISPLAY_TYPE_BINARY = 1, DISPLAY_TYPE_GRAYSCALE = 2, @@ -300,12 +348,23 @@ class DisplayBuffer { * * @param x The x coordinate of the upper left corner. * @param y The y coordinate of the upper left corner. - * @param image The image to draw + * @param image The image to draw. * @param color_on The color to replace in binary images for the on bits. * @param color_off The color to replace in binary images for the off bits. */ void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); + /** Draw the `image` at [x,y] to the screen. + * + * @param x The x coordinate of the upper left corner. + * @param y The y coordinate of the upper left corner. + * @param image The image to draw. + * @param align The alignment of the image. + * @param color_on The color to replace in binary images for the on bits. + * @param color_off The color to replace in binary images for the off bits. + */ + void image(int x, int y, BaseImage *image, ImageAlign align, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); + #ifdef USE_GRAPH /** Draw the `graph` with the top-left corner at [x,y] to the screen. * From 29f44306589978c084b3f276dd3db8b9a5eaa7b5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 19 Jun 2023 07:24:44 +1200 Subject: [PATCH 47/67] Use HW SPI for rp2040 (#4955) --- esphome/components/spi/spi.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 141bfb9448..c9bb075fb5 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -87,6 +87,30 @@ void SPIComponent::setup() { return; } #endif // USE_ESP32 +#ifdef USE_RP2040 + static uint8_t spi_bus_num = 0; + if (spi_bus_num >= 2) { + use_hw_spi = false; + } + if (use_hw_spi) { + SPIClassRP2040 *spi; + if (spi_bus_num == 0) { + spi = &SPI; + } else { + spi = &SPI1; + } + spi_bus_num++; + + if (miso_pin != -1) + spi->setRX(miso_pin); + if (mosi_pin != -1) + spi->setTX(mosi_pin); + spi->setSCK(clk_pin); + this->hw_spi_ = spi; + this->hw_spi_->begin(); + return; + } +#endif // USE_RP2040 #endif // USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { From 88c13768e352608dc3bab8b9079f6acb5979512d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 19 Jun 2023 07:35:03 +1200 Subject: [PATCH 48/67] Bump version to 2023.6.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8820c39d17..d4cfd30c32 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b1" +__version__ = "2023.6.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 5ba04eb620aebfa76d50e34fb4f1949493daf813 Mon Sep 17 00:00:00 2001 From: Hawawa McTaru <86658622+TaruDesigns@users.noreply.github.com> Date: Mon, 19 Jun 2023 01:20:32 +0200 Subject: [PATCH 49/67] Fix for Fujitsu AC not having Quiet Fan Mode (#4962) --- esphome/components/fujitsu_general/fujitsu_general.cpp | 7 +++++-- esphome/components/fujitsu_general/fujitsu_general.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/fujitsu_general/fujitsu_general.cpp b/esphome/components/fujitsu_general/fujitsu_general.cpp index 291af8c8cd..6c7adebfea 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.cpp +++ b/esphome/components/fujitsu_general/fujitsu_general.cpp @@ -151,11 +151,13 @@ void FujitsuGeneralClimate::transmit_state() { case climate::CLIMATE_FAN_LOW: SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_LOW); break; + case climate::CLIMATE_FAN_QUIET: + SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_SILENT); + break; case climate::CLIMATE_FAN_AUTO: default: SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_AUTO); break; - // TODO Quiet / Silent } // Set swing @@ -345,8 +347,9 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) { const uint8_t recv_fan_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_FAN_NIBBLE); ESP_LOGV(TAG, "Received fan mode %X", recv_fan_mode); switch (recv_fan_mode) { - // TODO No Quiet / Silent in ESPH case FUJITSU_GENERAL_FAN_SILENT: + this->fan_mode = climate::CLIMATE_FAN_QUIET; + break; case FUJITSU_GENERAL_FAN_LOW: this->fan_mode = climate::CLIMATE_FAN_LOW; break; diff --git a/esphome/components/fujitsu_general/fujitsu_general.h b/esphome/components/fujitsu_general/fujitsu_general.h index ee83ae9d19..d7d01bf6f3 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.h +++ b/esphome/components/fujitsu_general/fujitsu_general.h @@ -52,7 +52,7 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR { FujitsuGeneralClimate() : ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true, {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, - climate::CLIMATE_FAN_HIGH}, + climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_QUIET}, {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {} From 76e947651dc54f2e884103de6b7f47f29f287bb5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 18 Jun 2023 18:35:47 -0500 Subject: [PATCH 50/67] Store app comment and compilation_time in flash (#4945) --- esphome/core/application.cpp | 2 +- esphome/core/application.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index c012195f34..d82a7a5d37 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -99,7 +99,7 @@ void Application::loop() { if (this->dump_config_at_ < this->components_.size()) { if (this->dump_config_at_ == 0) { - ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str()); + ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_); #ifdef ESPHOME_PROJECT_NAME ESP_LOGI(TAG, "Project " ESPHOME_PROJECT_NAME " version " ESPHOME_PROJECT_VERSION); #endif diff --git a/esphome/core/application.h b/esphome/core/application.h index 0501d1a56a..054f2ea648 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -56,7 +56,7 @@ namespace esphome { class Application { public: - void pre_setup(const std::string &name, const std::string &friendly_name, const std::string &comment, + void pre_setup(const std::string &name, const std::string &friendly_name, const char *comment, const char *compilation_time, bool name_add_mac_suffix) { arch_init(); this->name_add_mac_suffix_ = name_add_mac_suffix; @@ -154,11 +154,11 @@ class Application { /// Get the friendly name of this Application set by pre_setup(). const std::string &get_friendly_name() const { return this->friendly_name_; } /// Get the comment of this Application set by pre_setup(). - const std::string &get_comment() const { return this->comment_; } + std::string get_comment() const { return this->comment_; } bool is_name_add_mac_suffix_enabled() const { return this->name_add_mac_suffix_; } - const std::string &get_compilation_time() const { return this->compilation_time_; } + std::string get_compilation_time() const { return this->compilation_time_; } /** Set the target interval with which to run the loop() calls. * If the loop() method takes longer than the target interval, ESPHome won't @@ -376,8 +376,8 @@ class Application { std::string name_; std::string friendly_name_; - std::string comment_; - std::string compilation_time_; + const char *comment_{nullptr}; + const char *compilation_time_{nullptr}; bool name_add_mac_suffix_; uint32_t last_loop_{0}; uint32_t loop_interval_{16}; From 959e7745a6809310f9ac16464e6ff45efd8521a0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 18 Jun 2023 20:51:19 -0500 Subject: [PATCH 51/67] Construct web_server assets at build time instead of run time (#4944) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/web_server/__init__.py | 49 ++++++++++++++--- esphome/components/web_server/web_server.cpp | 53 ++++++++----------- esphome/components/web_server/web_server.h | 42 ++++++++++++--- .../web_server_base/web_server_base.h | 1 + 4 files changed, 100 insertions(+), 45 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index d8343c6c39..130c082277 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -81,6 +81,37 @@ CONFIG_SCHEMA = cv.All( ) +def build_index_html(config) -> str: + html = "" + css_include = config.get(CONF_CSS_INCLUDE) + js_include = config.get(CONF_JS_INCLUDE) + if css_include: + html += "" + if config[CONF_CSS_URL]: + html += f'' + html += "" + if js_include: + html += "" + html += "" + if config[CONF_JS_URL]: + html += f'' + html += "" + return html + + +def add_resource_as_progmem(resource_name: str, content: str) -> None: + """Add a resource to progmem.""" + content_encoded = content.encode("utf-8") + content_encoded_size = len(content_encoded) + bytes_as_int = ", ".join(str(x) for x in content_encoded) + uint8_t = f"const uint8_t ESPHOME_WEBSERVER_{resource_name}[{content_encoded_size}] PROGMEM = {{{bytes_as_int}}}" + size_t = ( + f"const size_t ESPHOME_WEBSERVER_{resource_name}_SIZE = {content_encoded_size}" + ) + cg.add_global(cg.RawExpression(uint8_t)) + cg.add_global(cg.RawExpression(size_t)) + + @coroutine_with_priority(40.0) async def to_code(config): paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) @@ -89,13 +120,17 @@ async def to_code(config): await cg.register_component(var, config) cg.add_define("USE_WEBSERVER") + version = config[CONF_VERSION] cg.add(paren.set_port(config[CONF_PORT])) cg.add_define("USE_WEBSERVER") cg.add_define("USE_WEBSERVER_PORT", config[CONF_PORT]) - cg.add_define("USE_WEBSERVER_VERSION", config[CONF_VERSION]) - cg.add(var.set_css_url(config[CONF_CSS_URL])) - cg.add(var.set_js_url(config[CONF_JS_URL])) + cg.add_define("USE_WEBSERVER_VERSION", version) + if version == 2: + add_resource_as_progmem("INDEX_HTML", build_index_html(config)) + else: + cg.add(var.set_css_url(config[CONF_CSS_URL])) + cg.add(var.set_js_url(config[CONF_JS_URL])) cg.add(var.set_allow_ota(config[CONF_OTA])) if CONF_AUTH in config: cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME])) @@ -103,13 +138,13 @@ async def to_code(config): if CONF_CSS_INCLUDE in config: cg.add_define("USE_WEBSERVER_CSS_INCLUDE") path = CORE.relative_config_path(config[CONF_CSS_INCLUDE]) - with open(file=path, encoding="utf-8") as myfile: - cg.add(var.set_css_include(myfile.read())) + with open(file=path, encoding="utf-8") as css_file: + add_resource_as_progmem("CSS_INCLUDE", css_file.read()) if CONF_JS_INCLUDE in config: cg.add_define("USE_WEBSERVER_JS_INCLUDE") path = CORE.relative_config_path(config[CONF_JS_INCLUDE]) - with open(file=path, encoding="utf-8") as myfile: - cg.add(var.set_js_include(myfile.read())) + with open(file=path, encoding="utf-8") as js_file: + add_resource_as_progmem("JS_INCLUDE", js_file.read()) cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL])) if CONF_LOCAL in config and config[CONF_LOCAL]: cg.add_define("USE_WEBSERVER_LOCAL") diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1ac94375c2..b3a2dfdb66 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -90,10 +90,17 @@ WebServer::WebServer(web_server_base::WebServerBase *base) #endif } +#if USE_WEBSERVER_VERSION == 1 void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; } -void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; } void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; } +#endif + +#ifdef USE_WEBSERVER_CSS_INCLUDE +void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; } +#endif +#ifdef USE_WEBSERVER_JS_INCLUDE void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; } +#endif void WebServer::setup() { ESP_LOGCONFIG(TAG, "Setting up web server..."); @@ -159,20 +166,14 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { response->addHeader("Content-Encoding", "gzip"); request->send(response); } -#else +#elif USE_WEBSERVER_VERSION == 1 void WebServer::handle_index_request(AsyncWebServerRequest *request) { AsyncResponseStream *stream = request->beginResponseStream("text/html"); - // All content is controlled and created by user - so allowing all origins is fine here. - stream->addHeader("Access-Control-Allow-Origin", "*"); -#if USE_WEBSERVER_VERSION == 1 const std::string &title = App.get_name(); stream->print(F("")); stream->print(title.c_str()); stream->print(F("")); -#else - stream->print(F("")); -#endif #ifdef USE_WEBSERVER_CSS_INCLUDE stream->print(F("")); #endif @@ -182,7 +183,6 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { stream->print(F("\">")); } stream->print(F("")); -#if USE_WEBSERVER_VERSION == 1 stream->print(F("

")); stream->print(title.c_str()); stream->print(F("

")); @@ -308,49 +308,40 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { "type=\"file\" name=\"update\">")); } stream->print(F("

Debug Log

"));
-#endif
 #ifdef USE_WEBSERVER_JS_INCLUDE
   if (this->js_include_ != nullptr) {
     stream->print(F(""));
   }
-#endif
-#if USE_WEBSERVER_VERSION == 2
-  stream->print(F(""));
 #endif
   if (strlen(this->js_url_) > 0) {
     stream->print(F(""));
   }
-#if USE_WEBSERVER_VERSION == 1
   stream->print(F("
")); -#else - stream->print(F("")); -#endif - request->send(stream); } +#elif USE_WEBSERVER_VERSION == 2 +void WebServer::handle_index_request(AsyncWebServerRequest *request) { + AsyncWebServerResponse *response = + request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); + request->send(response); +} #endif + #ifdef USE_WEBSERVER_CSS_INCLUDE void WebServer::handle_css_request(AsyncWebServerRequest *request) { - AsyncResponseStream *stream = request->beginResponseStream("text/css"); - if (this->css_include_ != nullptr) { - stream->print(this->css_include_); - } - - request->send(stream); + AsyncWebServerResponse *response = + request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE); + request->send(response); } #endif #ifdef USE_WEBSERVER_JS_INCLUDE void WebServer::handle_js_request(AsyncWebServerRequest *request) { - AsyncResponseStream *stream = request->beginResponseStream("text/javascript"); - if (this->js_include_ != nullptr) { - stream->addHeader("Access-Control-Allow-Origin", "*"); - stream->print(this->js_include_); - } - - request->send(stream); + AsyncWebServerResponse *response = + request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE); + request->send(response); } #endif diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 45d0bc03a4..a664bb5a12 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -14,6 +14,22 @@ #include #include #endif + +#if USE_WEBSERVER_VERSION == 2 +extern const uint8_t ESPHOME_WEBSERVER_INDEX_HTML[] PROGMEM; +extern const size_t ESPHOME_WEBSERVER_INDEX_HTML_SIZE; +#endif + +#ifdef USE_WEBSERVER_CSS_INCLUDE +extern const uint8_t ESPHOME_WEBSERVER_CSS_INCLUDE[] PROGMEM; +extern const size_t ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE; +#endif + +#ifdef USE_WEBSERVER_JS_INCLUDE +extern const uint8_t ESPHOME_WEBSERVER_JS_INCLUDE[] PROGMEM; +extern const size_t ESPHOME_WEBSERVER_JS_INCLUDE_SIZE; +#endif + namespace esphome { namespace web_server { @@ -40,6 +56,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { public: WebServer(web_server_base::WebServerBase *base); +#if USE_WEBSERVER_VERSION == 1 /** Set the URL to the CSS that's sent to each client. Defaults to * https://esphome.io/_static/webserver-v1.min.css * @@ -47,24 +64,29 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { */ void set_css_url(const char *css_url); - /** Set local path to the script that's embedded in the index page. Defaults to - * - * @param css_include Local path to web server script. - */ - void set_css_include(const char *css_include); - /** Set the URL to the script that's embedded in the index page. Defaults to * https://esphome.io/_static/webserver-v1.min.js * * @param js_url The url to the web server script. */ void set_js_url(const char *js_url); +#endif +#ifdef USE_WEBSERVER_CSS_INCLUDE + /** Set local path to the script that's embedded in the index page. Defaults to + * + * @param css_include Local path to web server script. + */ + void set_css_include(const char *css_include); +#endif + +#ifdef USE_WEBSERVER_JS_INCLUDE /** Set local path to the script that's embedded in the index page. Defaults to * * @param js_include Local path to web server script. */ void set_js_include(const char *js_include); +#endif /** Determine whether internal components should be displayed on the web server. * Defaults to false. @@ -240,10 +262,16 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { web_server_base::WebServerBase *base_; AsyncEventSource events_{"/events"}; ListEntitiesIterator entities_iterator_; +#if USE_WEBSERVER_VERSION == 1 const char *css_url_{nullptr}; - const char *css_include_{nullptr}; const char *js_url_{nullptr}; +#endif +#ifdef USE_WEBSERVER_CSS_INCLUDE + const char *css_include_{nullptr}; +#endif +#ifdef USE_WEBSERVER_JS_INCLUDE const char *js_include_{nullptr}; +#endif bool include_internal_{false}; bool allow_ota_{true}; #ifdef USE_ESP32 diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index f6d3a84f89..ae286b1e22 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -83,6 +83,7 @@ class WebServerBase : public Component { return; } this->server_ = std::make_shared(this->port_); + // All content is controlled and created by user - so allowing all origins is fine here. DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); this->server_->begin(); From 5513b0e121776616485527d67e4d47f75a7e66a5 Mon Sep 17 00:00:00 2001 From: Stanislav Habich Date: Mon, 19 Jun 2023 03:56:12 +0200 Subject: [PATCH 52/67] Update pca9685_output.cpp (#4929) --- esphome/components/pca9685/pca9685_output.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index c61251b66f..d92312355a 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -29,13 +29,10 @@ void PCA9685Output::setup() { ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent..."); ESP_LOGV(TAG, " Resetting devices..."); - uint8_t address_tmp = this->address_; - this->set_i2c_address(0x00); if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) { this->mark_failed(); return; } - this->set_i2c_address(address_tmp); if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) { this->mark_failed(); From 501fe837104b0ecee6f3733ad6257907ff86cb9b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 20 Jun 2023 11:10:48 +1200 Subject: [PATCH 53/67] Bump version to 2023.6.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d4cfd30c32..8d15ede755 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b2" +__version__ = "2023.6.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 867b4719d1d87bf8db5f408a460264b694cbe8f7 Mon Sep 17 00:00:00 2001 From: Martin Murray Date: Tue, 20 Jun 2023 19:53:21 -0400 Subject: [PATCH 54/67] Apply configured IIR filter setting in generated BMP280 code (#4975) Co-authored-by: Martin Murray --- esphome/components/bmp280/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/bmp280/sensor.py b/esphome/components/bmp280/sensor.py index 95a9577f7e..e80511a509 100644 --- a/esphome/components/bmp280/sensor.py +++ b/esphome/components/bmp280/sensor.py @@ -94,3 +94,5 @@ async def to_code(config): sens = await sensor.new_sensor(conf) cg.add(var.set_pressure_sensor(sens)) cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) + + cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) From 0671287b8012d18c36775915a0cfb4a19455d9e8 Mon Sep 17 00:00:00 2001 From: Onne Date: Wed, 21 Jun 2023 02:22:32 +0200 Subject: [PATCH 55/67] Make growatt play nicer with other modbus components. (#4947) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../growatt_solar/growatt_solar.cpp | 31 +++++++++++++++++++ .../components/growatt_solar/growatt_solar.h | 4 +++ 2 files changed, 35 insertions(+) diff --git a/esphome/components/growatt_solar/growatt_solar.cpp b/esphome/components/growatt_solar/growatt_solar.cpp index ed753c4d3f..c4ed5ab841 100644 --- a/esphome/components/growatt_solar/growatt_solar.cpp +++ b/esphome/components/growatt_solar/growatt_solar.cpp @@ -9,11 +9,42 @@ static const char *const TAG = "growatt_solar"; static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion +void GrowattSolar::loop() { + // If update() was unable to send we retry until we can send. + if (!this->waiting_to_update_) + return; + update(); +} + void GrowattSolar::update() { + // If our last send has had no reply yet, and it wasn't that long ago, do nothing. + uint32_t now = millis(); + if (now - this->last_send_ < this->get_update_interval() / 2) { + return; + } + + // The bus might be slow, or there might be other devices, or other components might be talking to our device. + if (this->waiting_for_response()) { + this->waiting_to_update_ = true; + return; + } + + this->waiting_to_update_ = false; this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]); + this->last_send_ = millis(); } void GrowattSolar::on_modbus_data(const std::vector &data) { + // Other components might be sending commands to our device. But we don't get called with enough + // context to know what is what. So if we didn't do a send, we ignore the data. + if (!this->last_send_) + return; + this->last_send_ = 0; + + // Also ignore the data if the message is too short. Otherwise we will publish invalid values. + if (data.size() < MODBUS_REGISTER_COUNT[this->protocol_version_] * 2) + return; + auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void { if (sensor == nullptr) return; diff --git a/esphome/components/growatt_solar/growatt_solar.h b/esphome/components/growatt_solar/growatt_solar.h index d1b08b534a..b0ddd4b99d 100644 --- a/esphome/components/growatt_solar/growatt_solar.h +++ b/esphome/components/growatt_solar/growatt_solar.h @@ -19,6 +19,7 @@ enum GrowattProtocolVersion { class GrowattSolar : public PollingComponent, public modbus::ModbusDevice { public: + void loop() override; void update() override; void on_modbus_data(const std::vector &data) override; void dump_config() override; @@ -55,6 +56,9 @@ class GrowattSolar : public PollingComponent, public modbus::ModbusDevice { } protected: + bool waiting_to_update_; + uint32_t last_send_; + struct GrowattPhase { sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; From 6c00be0a638eaa0ebb6c0ff3385dc8d7f9be44ee Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:30:19 +1200 Subject: [PATCH 56/67] Bump esphome-dashboard to 20230621.0 (#4980) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b994de1932..934e0b46a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.1.7 # When updating platformio, also update Dockerfile esptool==4.6 click==8.1.3 -esphome-dashboard==20230516.0 +esphome-dashboard==20230621.0 aioesphomeapi==14.0.0 zeroconf==0.63.0 From d919373853abcfa107dbd8d39e61cc71f0e81426 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:32:53 +1200 Subject: [PATCH 57/67] Bump version to 2023.6.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8d15ede755..6a3286abd7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b3" +__version__ = "2023.6.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a064ab5c2b05573f1ca91c25f0f587cb5bb8a6dc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Jun 2023 16:36:54 +1200 Subject: [PATCH 58/67] Fix pypi release (#4983) --- .github/workflows/release.yml | 4 +++- script/setup | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ebd04e793..74ff4d87f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,9 +49,11 @@ jobs: with: python-version: "3.x" - name: Set up python environment + env: + ESPHOME_NO_VENV: 1 run: | script/setup - pip install setuptools wheel twine + pip install twine - name: Build run: python setup.py sdist bdist_wheel - name: Upload diff --git a/script/setup b/script/setup index 656e95eba6..e1b5941d0e 100755 --- a/script/setup +++ b/script/setup @@ -5,12 +5,13 @@ set -e cd "$(dirname "$0")/.." -if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ]; then +if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV" ]; then python3 -m venv venv source venv/bin/activate fi pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt +pip3 install setuptools wheel pip3 install --no-use-pep517 -e . pre-commit install From 2047bba4f7a3e294efab85dffe77d97d22fc5c83 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Jun 2023 16:39:52 +1200 Subject: [PATCH 59/67] Bump version to 2023.6.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6a3286abd7..32231d5c83 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b4" +__version__ = "2023.6.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 74daca668ec141069914ee526060966ebd3646c6 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Thu, 22 Jun 2023 07:58:49 +1000 Subject: [PATCH 60/67] Add configuration option to disable the log UI. (#4419) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/web_server/__init__.py | 3 +++ esphome/components/web_server/web_server.cpp | 21 +++++++++++--------- esphome/components/web_server/web_server.h | 9 +++++++++ esphome/const.py | 1 + 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 130c082277..25c0254f90 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -14,6 +14,7 @@ from esphome.const import ( CONF_PASSWORD, CONF_INCLUDE_INTERNAL, CONF_OTA, + CONF_LOG, CONF_VERSION, CONF_LOCAL, ) @@ -71,6 +72,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean, cv.Optional(CONF_OTA, default=True): cv.boolean, + cv.Optional(CONF_LOG, default=True): cv.boolean, cv.Optional(CONF_LOCAL): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), @@ -132,6 +134,7 @@ async def to_code(config): cg.add(var.set_css_url(config[CONF_CSS_URL])) cg.add(var.set_js_url(config[CONF_JS_URL])) cg.add(var.set_allow_ota(config[CONF_OTA])) + cg.add(var.set_expose_log(config[CONF_LOG])) if CONF_AUTH in config: cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME])) cg.add(paren.set_auth_password(config[CONF_AUTH][CONF_PASSWORD])) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index b3a2dfdb66..eb1858a09c 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -102,6 +102,16 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ = void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; } #endif +std::string WebServer::get_config_json() { + return json::build_json([this](JsonObject root) { + root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name(); + root["comment"] = App.get_comment(); + root["ota"] = this->allow_ota_; + root["log"] = this->expose_log_; + root["lang"] = "en"; + }); +} + void WebServer::setup() { ESP_LOGCONFIG(TAG, "Setting up web server..."); this->setup_controller(this->include_internal_); @@ -109,20 +119,13 @@ void WebServer::setup() { this->events_.onConnect([this](AsyncEventSourceClient *client) { // Configure reconnect timeout and send config - - client->send(json::build_json([this](JsonObject root) { - root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name(); - root["comment"] = App.get_comment(); - root["ota"] = this->allow_ota_; - root["lang"] = "en"; - }).c_str(), - "ping", millis(), 30000); + client->send(this->get_config_json().c_str(), "ping", millis(), 30000); this->entities_iterator_.begin(this->include_internal_); }); #ifdef USE_LOGGER - if (logger::global_logger != nullptr) { + if (logger::global_logger != nullptr && this->expose_log_) { logger::global_logger->add_on_log_callback( [this](int level, const char *tag, const char *message) { this->events_.send(message, "log", millis()); }); } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index a664bb5a12..83ebba205f 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -99,6 +99,11 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { * @param allow_ota. */ void set_allow_ota(bool allow_ota) { this->allow_ota_ = allow_ota; } + /** Set whether or not the webserver should expose the Log. + * + * @param expose_log. + */ + void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; } // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) @@ -114,6 +119,9 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { /// Handle an index request under '/'. void handle_index_request(AsyncWebServerRequest *request); + /// Return the webserver configuration as JSON. + std::string get_config_json(); + #ifdef USE_WEBSERVER_CSS_INCLUDE /// Handle included css request under '/0.css'. void handle_css_request(AsyncWebServerRequest *request); @@ -274,6 +282,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif bool include_internal_{false}; bool allow_ota_{true}; + bool expose_log_{true}; #ifdef USE_ESP32 std::deque> to_schedule_; SemaphoreHandle_t to_schedule_lock_; diff --git a/esphome/const.py b/esphome/const.py index 32231d5c83..e698ffcc64 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -368,6 +368,7 @@ CONF_LINE_TYPE = "line_type" CONF_LOADED_INTEGRATIONS = "loaded_integrations" CONF_LOCAL = "local" CONF_LOCK_ACTION = "lock_action" +CONF_LOG = "log" CONF_LOG_TOPIC = "log_topic" CONF_LOGGER = "logger" CONF_LOGS = "logs" From c455a5dd6a7e87503ffb5a65a762ac912278a3ff Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:12:25 +1200 Subject: [PATCH 61/67] Update webserver and captive portal pages to 67c48ee9 (#4986) --- .../components/captive_portal/captive_index.h | 190 ++- esphome/components/web_server/server_index.h | 1169 +++++++++-------- 2 files changed, 684 insertions(+), 675 deletions(-) diff --git a/esphome/components/captive_portal/captive_index.h b/esphome/components/captive_portal/captive_index.h index bf2e6e6e8b..56071f3d2a 100644 --- a/esphome/components/captive_portal/captive_index.h +++ b/esphome/components/captive_portal/captive_index.h @@ -6,102 +6,100 @@ namespace esphome { namespace captive_portal { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xdd, 0x58, 0x09, 0x6f, 0xdc, 0x36, 0x16, 0xfe, 0x2b, - 0xac, 0x92, 0x74, 0x34, 0x8d, 0xc5, 0xd1, 0x31, 0x97, 0x35, 0xd2, 0x14, 0x89, 0x37, 0x45, 0x0b, 0x24, 0x69, 0x00, - 0xbb, 0x5d, 0x14, 0x69, 0x00, 0x73, 0x24, 0x6a, 0xc4, 0x58, 0xa2, 0x54, 0x91, 0x9a, 0x23, 0x83, 0xd9, 0xdf, 0xde, - 0x47, 0x52, 0x73, 0x38, 0x6b, 0x2f, 0x90, 0x62, 0x8b, 0xa2, 0x4d, 0x6c, 0x9a, 0xc7, 0x3b, 0x3f, 0xf2, 0xf1, 0x3d, - 0x2a, 0xfa, 0x2a, 0xad, 0x12, 0xb9, 0xad, 0x29, 0xca, 0x65, 0x59, 0xcc, 0x23, 0xd5, 0xa2, 0x82, 0xf0, 0x65, 0x4c, - 0x39, 0x8c, 0x28, 0x49, 0xe7, 0x51, 0x49, 0x25, 0x41, 0x49, 0x4e, 0x1a, 0x41, 0x65, 0xfc, 0xd3, 0xcd, 0x77, 0xce, - 0x14, 0x0d, 0xe6, 0x51, 0xc1, 0xf8, 0x1d, 0x6a, 0x68, 0x11, 0xb3, 0xa4, 0xe2, 0x28, 0x6f, 0x68, 0x16, 0xa7, 0x44, - 0x92, 0x90, 0x95, 0x64, 0x49, 0x15, 0x81, 0x66, 0xe3, 0xa4, 0xa4, 0xf1, 0x8a, 0xd1, 0x75, 0x5d, 0x35, 0x12, 0x01, - 0xa5, 0xa4, 0x5c, 0xc6, 0xd6, 0x9a, 0xa5, 0x32, 0x8f, 0x53, 0xba, 0x62, 0x09, 0x75, 0xf4, 0xe0, 0x82, 0x71, 0x26, - 0x19, 0x29, 0x1c, 0x91, 0x90, 0x82, 0xc6, 0xde, 0x45, 0x2b, 0x68, 0xa3, 0x07, 0x64, 0x01, 0x63, 0x5e, 0x59, 0x20, - 0x52, 0x24, 0x0d, 0xab, 0x25, 0x52, 0xf6, 0xc6, 0x65, 0x95, 0xb6, 0x05, 0x9d, 0x67, 0x2d, 0x4f, 0x24, 0x03, 0x0b, - 0x84, 0xcd, 0xfb, 0xbb, 0x82, 0x4a, 0x44, 0xe3, 0x37, 0x44, 0xe6, 0xb8, 0x24, 0x1b, 0xdb, 0x74, 0x18, 0xb7, 0xfd, - 0x6f, 0x6c, 0xfe, 0xdc, 0x73, 0xdd, 0xfe, 0x85, 0x6e, 0xdc, 0xfe, 0x00, 0xfe, 0xce, 0x1a, 0x2a, 0xdb, 0x86, 0x23, - 0x62, 0xdf, 0x46, 0x35, 0x50, 0xa2, 0x34, 0xb6, 0x4a, 0xcf, 0xc7, 0xae, 0x3b, 0x45, 0xde, 0x25, 0xf6, 0x47, 0x8e, - 0xe7, 0xe1, 0xc0, 0xf1, 0x46, 0xc9, 0xc4, 0x19, 0x21, 0x6f, 0x08, 0x8d, 0xef, 0xe3, 0x11, 0x72, 0x3f, 0x59, 0x28, - 0x63, 0x45, 0x11, 0x5b, 0xbc, 0xe2, 0xd4, 0x42, 0x42, 0x36, 0xd5, 0x1d, 0x8d, 0xad, 0xa4, 0x6d, 0x1a, 0xf0, 0xee, - 0xaa, 0x2a, 0xaa, 0x06, 0xac, 0xfd, 0x95, 0xa3, 0x7b, 0xff, 0xbe, 0x58, 0x87, 0x6c, 0x08, 0x17, 0x59, 0xd5, 0x94, - 0xb1, 0xa5, 0x41, 0xb1, 0x9f, 0xee, 0xe8, 0x1e, 0xa9, 0xa6, 0x7f, 0xb6, 0xe8, 0x54, 0x0d, 0x5b, 0x32, 0x1e, 0x5b, - 0x9e, 0x8f, 0xbc, 0x29, 0xe8, 0xbd, 0xed, 0xef, 0x8f, 0xa0, 0x10, 0x05, 0x4a, 0xe7, 0x66, 0x65, 0xbf, 0xbf, 0x8d, - 0xc4, 0x6a, 0x89, 0x36, 0x65, 0xc1, 0x45, 0x6c, 0xe5, 0x52, 0xd6, 0xe1, 0x60, 0xb0, 0x5e, 0xaf, 0xf1, 0x3a, 0xc0, - 0x55, 0xb3, 0x1c, 0xf8, 0xae, 0xeb, 0x0e, 0x80, 0xc2, 0x42, 0x66, 0x7f, 0x2c, 0x7f, 0x68, 0xa1, 0x9c, 0xb2, 0x65, - 0x2e, 0x75, 0x7f, 0xfe, 0x74, 0xc7, 0xf7, 0x91, 0xa2, 0x98, 0xdf, 0x7e, 0x38, 0xd3, 0xd2, 0x9c, 0x69, 0xe1, 0xdf, - 0x12, 0xdb, 0x3a, 0xb8, 0xda, 0x7b, 0xa3, 0x8c, 0x9a, 0x10, 0x1f, 0xf9, 0xc8, 0xd5, 0xff, 0x7d, 0x47, 0xf5, 0xbb, - 0x91, 0xf3, 0xd9, 0x08, 0x9d, 0x8d, 0xe0, 0xaf, 0x02, 0xd0, 0x2f, 0xc7, 0xce, 0xe5, 0x91, 0xdf, 0x53, 0xeb, 0x2b, - 0xcf, 0x3d, 0x4d, 0x28, 0xa6, 0xef, 0xc7, 0xe7, 0x63, 0xc7, 0xff, 0x59, 0x11, 0x68, 0xf4, 0x8f, 0x5c, 0x8e, 0x9f, - 0x7b, 0x3f, 0x8f, 0xc9, 0x08, 0x8d, 0xba, 0x99, 0x91, 0xa3, 0xfa, 0xc7, 0x91, 0xd6, 0x85, 0x46, 0x2b, 0x20, 0x2b, - 0x9d, 0xb1, 0x33, 0x22, 0x01, 0x0a, 0x3a, 0xab, 0xa0, 0x07, 0xd3, 0x63, 0xe0, 0x3e, 0x9b, 0x73, 0x82, 0x4f, 0xbd, - 0xc1, 0xdc, 0xea, 0x87, 0x96, 0x75, 0x82, 0xa1, 0x3a, 0x87, 0x01, 0x7f, 0xac, 0xe0, 0xdc, 0x59, 0x56, 0x7f, 0x6f, - 0x7d, 0x2b, 0xc8, 0x8a, 0x5a, 0x71, 0x1c, 0x43, 0xa8, 0xb5, 0x25, 0x9c, 0x10, 0x5c, 0x54, 0x09, 0x51, 0x2c, 0x58, - 0x50, 0xd2, 0x24, 0xf9, 0xd7, 0x5f, 0xdb, 0xc7, 0xa5, 0x25, 0x95, 0xaf, 0x0a, 0xaa, 0xba, 0xe2, 0xe5, 0xf6, 0x86, - 0x2c, 0xdf, 0x42, 0x00, 0xd9, 0x16, 0x11, 0x2c, 0xa5, 0x56, 0xff, 0xbd, 0xfb, 0x01, 0x0b, 0xb9, 0x2d, 0x28, 0x4e, - 0x99, 0xa8, 0x0b, 0xb2, 0x8d, 0xad, 0x05, 0xc8, 0xba, 0xb3, 0xfa, 0x17, 0x19, 0x95, 0x49, 0x6e, 0x5b, 0x03, 0x08, - 0xb1, 0x8c, 0x2d, 0xf1, 0x47, 0x51, 0x71, 0xab, 0x8f, 0x65, 0x4e, 0xb9, 0x6d, 0x1f, 0x2c, 0x54, 0xf6, 0x71, 0xbd, - 0x64, 0x3f, 0xb4, 0x74, 0xb4, 0x41, 0x32, 0xa9, 0x42, 0x0e, 0xab, 0xe0, 0xbd, 0x38, 0xce, 0x2e, 0xaa, 0x74, 0xfb, - 0x88, 0x79, 0xb9, 0x67, 0x6c, 0x63, 0x9c, 0xd3, 0xe6, 0x86, 0x6e, 0xe0, 0xb8, 0xfc, 0x9b, 0x7d, 0xc7, 0xd0, 0x5b, - 0x2a, 0xd7, 0x55, 0x73, 0x27, 0x42, 0x64, 0x3d, 0x37, 0xe2, 0x66, 0x26, 0x42, 0x39, 0x26, 0xb5, 0xc0, 0xa2, 0x80, - 0xf0, 0xb7, 0xbd, 0x3e, 0xc4, 0x6a, 0x7d, 0xdf, 0x14, 0x83, 0xe2, 0x6d, 0x94, 0xb2, 0x15, 0x4a, 0x0a, 0x22, 0xe0, - 0xb8, 0x72, 0x23, 0xcb, 0x42, 0x87, 0xb8, 0xaa, 0x78, 0x02, 0xfc, 0x77, 0xb1, 0xf5, 0x00, 0x76, 0x2f, 0xb7, 0x3f, - 0xa4, 0x76, 0x4f, 0x00, 0x6a, 0xbd, 0x3e, 0x5e, 0x91, 0xa2, 0xa5, 0x28, 0x46, 0x32, 0x67, 0xe2, 0x64, 0xe2, 0xec, - 0x51, 0xb6, 0x5a, 0xdc, 0x01, 0x57, 0x06, 0xcb, 0xc2, 0xee, 0x5b, 0xc7, 0x38, 0x8e, 0x88, 0xb9, 0xe5, 0xac, 0x27, - 0xd6, 0x67, 0x36, 0x39, 0x05, 0xcd, 0xa4, 0x75, 0x16, 0xf0, 0x4f, 0x77, 0x70, 0x1b, 0xe1, 0x06, 0xf4, 0xf7, 0xf7, - 0xa7, 0xd9, 0x48, 0xd4, 0x84, 0x7f, 0xce, 0xaa, 0x6c, 0xd4, 0x81, 0x85, 0x55, 0x4f, 0x45, 0x17, 0x10, 0x9d, 0x74, - 0x0e, 0xc8, 0xb1, 0xff, 0x74, 0x07, 0x71, 0xa6, 0x8e, 0xce, 0xdd, 0x49, 0x68, 0x34, 0x00, 0x84, 0xe6, 0xb7, 0xfb, - 0x7e, 0xff, 0xe4, 0xce, 0x6f, 0x2d, 0x6d, 0xb6, 0xd7, 0xb4, 0xa0, 0x89, 0xac, 0x1a, 0xdb, 0x7a, 0x02, 0x9a, 0xe0, - 0x24, 0x68, 0xbf, 0xbf, 0xbf, 0x79, 0xf3, 0x3a, 0xae, 0x6c, 0xda, 0xbf, 0x78, 0x8c, 0x5a, 0xdd, 0xea, 0xef, 0xe1, - 0x56, 0xff, 0x4f, 0xdc, 0x53, 0xf7, 0x7a, 0xef, 0x03, 0xb0, 0x1a, 0xaf, 0x4f, 0x97, 0xbb, 0xba, 0x00, 0x9e, 0xc3, - 0x25, 0x72, 0x61, 0x3d, 0x17, 0xb6, 0x33, 0x1e, 0xf5, 0x41, 0x3d, 0xfc, 0x80, 0xe9, 0xfa, 0x7a, 0x86, 0x6b, 0x5a, - 0x1d, 0xd1, 0xf9, 0x37, 0xbb, 0x45, 0xb5, 0x71, 0x04, 0xfb, 0xc4, 0xf8, 0x32, 0x64, 0x3c, 0xa7, 0x0d, 0x93, 0x7b, - 0x30, 0x17, 0x6e, 0xfa, 0xba, 0x95, 0xbb, 0x9a, 0xa4, 0xa9, 0x5a, 0x19, 0xd5, 0x9b, 0x59, 0x06, 0x79, 0x41, 0x51, - 0xd2, 0xd0, 0xa3, 0xe5, 0xde, 0xac, 0xeb, 0x2b, 0x28, 0xbc, 0x1c, 0x3d, 0xdb, 0xab, 0x83, 0xb7, 0x93, 0xb0, 0x65, - 0x0e, 0x29, 0xd8, 0x92, 0x87, 0x09, 0xd8, 0x4d, 0x1b, 0xc3, 0x94, 0x91, 0x92, 0x15, 0xdb, 0x50, 0xc0, 0x65, 0xe8, - 0x40, 0xc2, 0x60, 0xd9, 0x7e, 0xd1, 0x4a, 0x59, 0x71, 0xd0, 0xdd, 0xa4, 0xb4, 0x09, 0xdd, 0x99, 0xe9, 0x38, 0x0d, - 0x49, 0x59, 0x2b, 0x42, 0x1c, 0x34, 0xb4, 0x9c, 0x2d, 0x48, 0x72, 0xb7, 0x6c, 0xaa, 0x96, 0xa7, 0x4e, 0xa2, 0x6e, - 0xeb, 0xf0, 0x89, 0x97, 0x91, 0x80, 0x26, 0xb3, 0x6e, 0x94, 0x65, 0xd9, 0x0c, 0x90, 0xa0, 0x8e, 0xb9, 0xfc, 0x42, - 0x1f, 0x0f, 0x15, 0xdb, 0x99, 0x99, 0xd8, 0x57, 0x13, 0xc6, 0x46, 0x48, 0x25, 0xcf, 0x66, 0x07, 0x77, 0xdc, 0x19, - 0xa4, 0x01, 0x01, 0x42, 0x6a, 0x88, 0x7f, 0x30, 0x73, 0x5f, 0x12, 0xc6, 0xcf, 0xad, 0x57, 0x67, 0x65, 0xd6, 0x85, - 0x2f, 0xc0, 0xa2, 0xd5, 0xe8, 0x20, 0x9e, 0x41, 0xa2, 0x32, 0xb9, 0x30, 0xf4, 0xc7, 0x6e, 0xbd, 0xd9, 0xe3, 0xee, - 0x8c, 0xec, 0x0e, 0xd4, 0x59, 0x41, 0x37, 0xb3, 0x8f, 0xad, 0x90, 0x2c, 0xdb, 0x3a, 0x5d, 0x2e, 0x0d, 0xe1, 0xbc, - 0x40, 0x0e, 0x5d, 0x00, 0x29, 0xa5, 0x7c, 0xa6, 0x75, 0x38, 0x4c, 0xd2, 0x52, 0x74, 0x38, 0x1d, 0xc5, 0xe8, 0x53, - 0x7a, 0x5f, 0xd6, 0xff, 0xa2, 0x56, 0xc7, 0x71, 0x57, 0x92, 0x06, 0x72, 0x8b, 0xb3, 0xa8, 0x00, 0xd3, 0x32, 0x74, - 0x26, 0xb0, 0x57, 0xdd, 0x94, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xfa, 0x6e, 0x3a, 0xe0, 0xed, 0xd5, 0x1b, 0x24, 0xaa, - 0x82, 0xa5, 0x1d, 0x9d, 0x26, 0x41, 0xee, 0x11, 0x1e, 0x0f, 0xb6, 0x1b, 0xa9, 0xb9, 0x03, 0xd4, 0xc3, 0x6c, 0x4a, - 0x3c, 0xf7, 0x81, 0x1d, 0x49, 0xb3, 0xcc, 0x5f, 0x64, 0x47, 0xa4, 0x54, 0xaa, 0xdd, 0xb3, 0xee, 0x54, 0xf8, 0x43, - 0x10, 0x70, 0xd8, 0x1b, 0xe8, 0xef, 0x99, 0x8e, 0x8b, 0xdd, 0x99, 0x14, 0x7d, 0x52, 0xc3, 0xb6, 0x29, 0xec, 0x87, - 0x4e, 0xee, 0xb3, 0xe0, 0xea, 0x94, 0x09, 0x7b, 0x8f, 0x67, 0xc2, 0x1e, 0x52, 0xb5, 0xcb, 0xcb, 0x6a, 0x13, 0xf7, - 0x74, 0x4e, 0x1a, 0xc2, 0x4f, 0xef, 0x59, 0xf0, 0x0a, 0xf8, 0xff, 0x2f, 0x29, 0xee, 0x0f, 0xa7, 0xb7, 0x2f, 0x48, - 0x6d, 0x5f, 0x98, 0xd5, 0x8c, 0x77, 0xca, 0x79, 0xe8, 0x41, 0xfa, 0x62, 0x58, 0xb0, 0xa5, 0xf7, 0x67, 0x40, 0xfb, - 0xdf, 0x38, 0x06, 0x2f, 0xbc, 0x29, 0xbe, 0x44, 0xba, 0x31, 0x10, 0xe1, 0x60, 0x8a, 0x26, 0x57, 0x43, 0x3c, 0xf4, - 0x90, 0xaa, 0x9a, 0xc6, 0x68, 0x82, 0xa7, 0x40, 0x30, 0xc6, 0xc1, 0x04, 0x26, 0x90, 0xef, 0xe1, 0xd1, 0x6b, 0x3f, - 0xc0, 0xe3, 0x11, 0x50, 0xf9, 0x2e, 0x0e, 0x7c, 0x64, 0x68, 0xc7, 0xd8, 0x07, 0x71, 0x8a, 0x24, 0x28, 0x01, 0xe8, - 0x24, 0xc0, 0xee, 0x04, 0xc4, 0x8d, 0xb1, 0x7b, 0x89, 0xa7, 0x63, 0x34, 0xc5, 0x13, 0x80, 0x0e, 0x0f, 0x47, 0x85, - 0x33, 0xc2, 0x1e, 0x4c, 0x07, 0x63, 0x32, 0xc5, 0xc3, 0x00, 0xe9, 0xc6, 0xc0, 0x31, 0x01, 0x11, 0x0e, 0x76, 0xbd, - 0xd7, 0x01, 0xf6, 0x27, 0xa0, 0x77, 0x38, 0x7c, 0x01, 0x62, 0x2f, 0x87, 0xc8, 0xb4, 0x06, 0x5e, 0x50, 0x30, 0x7a, - 0x0c, 0x34, 0xff, 0x9f, 0x0b, 0x1a, 0x40, 0xe2, 0xa1, 0x00, 0x5f, 0x42, 0xec, 0x7a, 0x8a, 0xdf, 0xb4, 0x06, 0x37, - 0xcf, 0x43, 0xee, 0x1f, 0xc6, 0x2c, 0xf8, 0xe7, 0x62, 0xe6, 0x29, 0x04, 0xa0, 0x0b, 0xba, 0x41, 0x0e, 0xd2, 0x8d, - 0xd1, 0x0d, 0xcc, 0xd3, 0xab, 0x4b, 0x34, 0x05, 0xae, 0xf1, 0x14, 0x5d, 0xa2, 0x91, 0x42, 0x17, 0xd8, 0x87, 0x86, - 0xc9, 0x01, 0xa6, 0x2f, 0x84, 0x71, 0xf8, 0x37, 0x86, 0xf1, 0x31, 0x9f, 0xfe, 0xc6, 0x2e, 0xfd, 0x15, 0x57, 0x10, - 0x94, 0x63, 0xba, 0x0c, 0x8b, 0x06, 0xe6, 0x15, 0xaf, 0xaa, 0x28, 0x78, 0x94, 0x43, 0x35, 0x02, 0xef, 0x7a, 0x0f, - 0xb1, 0x34, 0xce, 0xbd, 0xf9, 0xbd, 0x2a, 0x1d, 0x28, 0xbd, 0x79, 0xa4, 0xd3, 0xf9, 0xfc, 0x26, 0xa7, 0xe8, 0xd5, - 0xf5, 0x3b, 0x78, 0x08, 0x16, 0x05, 0xe2, 0xd5, 0x1a, 0xde, 0x9b, 0x5b, 0x24, 0x2b, 0xf5, 0x82, 0xe7, 0x50, 0x2a, - 0xaa, 0x2e, 0x3c, 0x20, 0x50, 0x57, 0x2c, 0x60, 0x8c, 0xa3, 0x45, 0x33, 0x7f, 0x57, 0x50, 0x22, 0x28, 0x5a, 0xb2, - 0x15, 0x45, 0x4c, 0x42, 0x1d, 0x50, 0x52, 0x24, 0x99, 0x6a, 0x8e, 0x8c, 0x9a, 0xee, 0x6d, 0x25, 0x69, 0x88, 0xae, - 0xaa, 0x7a, 0xab, 0x85, 0x24, 0x39, 0xe1, 0x4b, 0x9a, 0x1e, 0x84, 0x29, 0xea, 0x6d, 0xd5, 0x36, 0xe8, 0x97, 0x17, - 0x6f, 0x5e, 0xab, 0x87, 0x36, 0x45, 0x4e, 0xa7, 0x6c, 0x23, 0xd1, 0x8f, 0x37, 0x2f, 0x50, 0x5b, 0xc3, 0xa6, 0x53, - 0x63, 0x5b, 0xb5, 0xa2, 0xcd, 0x1a, 0x2a, 0x4b, 0xaa, 0x48, 0x40, 0xb9, 0xa0, 0x52, 0x42, 0xa1, 0x21, 0x30, 0x94, - 0xce, 0xda, 0x13, 0x53, 0x75, 0x83, 0xbb, 0x20, 0x7e, 0xde, 0x95, 0xd7, 0x51, 0x1e, 0x18, 0xd7, 0xaf, 0x3b, 0x6a, - 0x70, 0x3d, 0x98, 0x47, 0xea, 0x39, 0x8d, 0x88, 0x7e, 0x84, 0xc4, 0x83, 0x35, 0xcb, 0x98, 0x7a, 0xb8, 0xcd, 0x23, - 0x5d, 0x8f, 0x2a, 0x09, 0xaa, 0x24, 0x32, 0x5f, 0x34, 0x74, 0xaf, 0xa0, 0x7c, 0x09, 0xaf, 0x64, 0xd8, 0x70, 0xa8, - 0x50, 0x12, 0x9a, 0x57, 0x05, 0x54, 0x40, 0xf1, 0xf5, 0xf5, 0x0f, 0xff, 0x52, 0x9f, 0x3f, 0xc0, 0xcf, 0x13, 0x27, - 0x3c, 0x29, 0x0c, 0xa3, 0xea, 0x74, 0x7c, 0xe3, 0xa1, 0xf9, 0x90, 0x51, 0xc3, 0x7b, 0x00, 0xfc, 0x4e, 0xef, 0x49, - 0x79, 0x77, 0x98, 0xec, 0x24, 0xe9, 0x5f, 0x5d, 0xd9, 0x1a, 0x26, 0xd1, 0x2e, 0x4a, 0x26, 0xe7, 0xd7, 0x60, 0x60, - 0x34, 0x30, 0x0b, 0xe0, 0x9c, 0x72, 0xc0, 0xd0, 0xe6, 0x1d, 0x0f, 0xec, 0xa8, 0x42, 0xec, 0x27, 0x8d, 0x98, 0xd9, - 0x60, 0xed, 0x65, 0x49, 0x65, 0x5e, 0xa5, 0xf1, 0xbb, 0x1f, 0xaf, 0x6f, 0x8e, 0x1e, 0x77, 0xb0, 0x52, 0x9e, 0x98, - 0x0f, 0x2c, 0x6d, 0x21, 0x59, 0x4d, 0x1a, 0xa9, 0xc5, 0x3a, 0x2a, 0xce, 0x0e, 0x1e, 0xe9, 0x75, 0xbd, 0x33, 0xda, - 0xa9, 0x8e, 0x71, 0x30, 0x47, 0x0f, 0xd9, 0x78, 0xd0, 0xfd, 0x99, 0x95, 0x03, 0x73, 0x14, 0x07, 0xe6, 0x5c, 0x0e, - 0xf4, 0xe7, 0xa7, 0xdf, 0x01, 0xf1, 0x69, 0xfc, 0xac, 0x8e, 0x12, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x5b, 0x8f, 0xdb, 0x36, 0x16, 0x7e, 0xef, + 0xaf, 0xe0, 0x2a, 0x49, 0x2d, 0x37, 0x23, 0xea, 0x66, 0xf9, 0x2a, 0xa9, 0x48, 0xb2, 0x29, 0x5a, 0x20, 0x69, 0x03, + 0xcc, 0xb4, 0xfb, 0x10, 0x04, 0x18, 0x5a, 0xa2, 0x2c, 0x66, 0x24, 0x4a, 0x15, 0xe9, 0x5b, 0x0c, 0xef, 0x6f, 0xdf, + 0x43, 0x52, 0xf6, 0x38, 0xb3, 0x99, 0x05, 0x52, 0xec, 0x62, 0xd1, 0x4e, 0x26, 0x1c, 0x92, 0x3a, 0xd7, 0x4f, 0x3c, + 0x17, 0x2a, 0xfe, 0x5b, 0xde, 0x64, 0x72, 0xdf, 0x52, 0x54, 0xca, 0xba, 0x4a, 0x63, 0x35, 0xa2, 0x8a, 0xf0, 0x55, + 0x42, 0x39, 0xac, 0x28, 0xc9, 0xd3, 0xb8, 0xa6, 0x92, 0xa0, 0xac, 0x24, 0x9d, 0xa0, 0x32, 0xf9, 0xf5, 0xe6, 0x07, + 0x67, 0x8a, 0xdc, 0x34, 0xae, 0x18, 0xbf, 0x43, 0x1d, 0xad, 0x12, 0x96, 0x35, 0x1c, 0x95, 0x1d, 0x2d, 0x92, 0x9c, + 0x48, 0x32, 0x67, 0x35, 0x59, 0x51, 0x45, 0xa0, 0xd9, 0x38, 0xa9, 0x69, 0xb2, 0x61, 0x74, 0xdb, 0x36, 0x9d, 0x44, + 0x40, 0x29, 0x29, 0x97, 0x89, 0xb5, 0x65, 0xb9, 0x2c, 0x93, 0x9c, 0x6e, 0x58, 0x46, 0x1d, 0xbd, 0xb8, 0x62, 0x9c, + 0x49, 0x46, 0x2a, 0x47, 0x64, 0xa4, 0xa2, 0x89, 0x7f, 0xb5, 0x16, 0xb4, 0xd3, 0x0b, 0xb2, 0x84, 0x35, 0x6f, 0x2c, + 0x10, 0x29, 0xb2, 0x8e, 0xb5, 0x12, 0x29, 0x7b, 0x93, 0xba, 0xc9, 0xd7, 0x15, 0x4d, 0x5d, 0x97, 0x08, 0xb0, 0x4b, + 0xb8, 0x8c, 0xe7, 0x74, 0x87, 0xa7, 0xb3, 0x68, 0x32, 0x9e, 0xe6, 0x13, 0xfc, 0x51, 0x7c, 0x03, 0x9e, 0xad, 0x6b, + 0x50, 0x87, 0xab, 0x26, 0x23, 0x92, 0x35, 0x1c, 0x0b, 0x4a, 0xba, 0xac, 0x4c, 0x92, 0xc4, 0xfa, 0x5e, 0x90, 0x0d, + 0xb5, 0xbe, 0xfd, 0xd6, 0x3e, 0x13, 0xad, 0xa8, 0x7c, 0x5d, 0x51, 0x35, 0x15, 0x2f, 0xf7, 0x37, 0x64, 0xf5, 0x33, + 0x58, 0x6e, 0x5b, 0x44, 0xb0, 0x9c, 0x5a, 0xc3, 0xf7, 0xde, 0x07, 0x2c, 0xe4, 0xbe, 0xa2, 0x38, 0x67, 0xa2, 0xad, + 0xc8, 0x3e, 0xb1, 0x96, 0x20, 0xf5, 0xce, 0x1a, 0x2e, 0x8a, 0x35, 0xcf, 0x94, 0x70, 0x24, 0x6c, 0x3a, 0x3c, 0x54, + 0x14, 0xcc, 0x4b, 0xde, 0x12, 0x59, 0xe2, 0x9a, 0xec, 0x6c, 0x33, 0x61, 0xdc, 0x0e, 0xbe, 0xb3, 0xe9, 0x73, 0xdf, + 0xf3, 0x86, 0x57, 0x7a, 0xf0, 0x86, 0x2e, 0xfc, 0x5d, 0x74, 0x54, 0xae, 0x3b, 0x8e, 0x88, 0x7d, 0x1b, 0xb7, 0x40, + 0x89, 0xf2, 0xc4, 0xaa, 0xfd, 0x00, 0x7b, 0xde, 0x14, 0xf9, 0x33, 0x1c, 0x44, 0x8e, 0xef, 0xe3, 0xd0, 0xf1, 0xa3, + 0x6c, 0xe2, 0x44, 0xc8, 0x1f, 0xc1, 0x10, 0x04, 0x38, 0x42, 0xde, 0x27, 0x0b, 0x15, 0xac, 0xaa, 0x12, 0x8b, 0x37, + 0x9c, 0x5a, 0x48, 0xc8, 0xae, 0xb9, 0xa3, 0x89, 0x95, 0xad, 0xbb, 0x0e, 0xec, 0x7f, 0xd5, 0x54, 0x4d, 0x07, 0x70, + 0x7d, 0x83, 0x3e, 0xfb, 0xf9, 0x6a, 0x15, 0xb2, 0x23, 0x5c, 0x14, 0x4d, 0x57, 0x27, 0x96, 0x7e, 0x29, 0xf6, 0xd3, + 0x83, 0x3c, 0x22, 0x35, 0x0c, 0x2f, 0x1e, 0x3a, 0x4d, 0xc7, 0x56, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0a, 0x6a, + 0x6f, 0x87, 0xc7, 0x33, 0x26, 0x44, 0x61, 0xd2, 0x7b, 0xd9, 0xd8, 0xef, 0x6f, 0x63, 0xb1, 0x59, 0xa1, 0x5d, 0x5d, + 0x71, 0x91, 0x58, 0xa5, 0x94, 0xed, 0xdc, 0x75, 0xb7, 0xdb, 0x2d, 0xde, 0x86, 0xb8, 0xe9, 0x56, 0x6e, 0xe0, 0x79, + 0x9e, 0x0b, 0x14, 0x16, 0x32, 0xe7, 0xc3, 0x0a, 0x46, 0x16, 0x2a, 0x29, 0x5b, 0x95, 0x52, 0xcf, 0xd3, 0xa7, 0x07, + 0x7a, 0x8c, 0x15, 0x45, 0x7a, 0xfb, 0xe1, 0x42, 0x4b, 0x77, 0xa1, 0x85, 0x7e, 0x7f, 0x81, 0xe6, 0xe0, 0xad, 0x32, + 0x6a, 0x42, 0x02, 0x14, 0x20, 0x4f, 0xff, 0x0b, 0x1c, 0x35, 0xef, 0x57, 0xce, 0x83, 0x15, 0xba, 0x58, 0xc1, 0x5f, + 0xc0, 0x2f, 0xa8, 0xc7, 0xce, 0xec, 0xcc, 0xee, 0xab, 0xc7, 0x1b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x8e, 0x2f, + 0xd7, 0x4e, 0xf0, 0x9b, 0x22, 0x50, 0xd8, 0x9f, 0x99, 0x9c, 0xa0, 0xf4, 0x7f, 0x1b, 0x93, 0x08, 0x45, 0xfd, 0x4e, + 0xe4, 0xa8, 0xf9, 0x79, 0xa5, 0x34, 0xa1, 0x68, 0x03, 0x54, 0xb5, 0x33, 0x76, 0x22, 0x12, 0xa2, 0xb0, 0x37, 0x09, + 0x66, 0xb0, 0x3d, 0x06, 0xe6, 0x8b, 0x3d, 0x27, 0xfc, 0x34, 0x50, 0x30, 0xcf, 0x2d, 0xeb, 0x1e, 0x83, 0xe6, 0x12, + 0x03, 0xfc, 0xb1, 0x81, 0x33, 0x67, 0x59, 0x80, 0x11, 0x95, 0x59, 0x69, 0x5b, 0x2e, 0x44, 0x5e, 0xc1, 0x56, 0x10, + 0x15, 0x0d, 0xb7, 0x86, 0x58, 0x96, 0x94, 0xdb, 0x27, 0x56, 0xc5, 0x48, 0xf5, 0x13, 0xfb, 0xe1, 0x13, 0x39, 0x3c, + 0x9c, 0xe3, 0x43, 0x32, 0x09, 0x71, 0x28, 0xb1, 0x8a, 0xe8, 0xab, 0xf3, 0xee, 0xb2, 0xc9, 0xf7, 0x8f, 0x84, 0x4e, + 0xe9, 0x9b, 0xb8, 0x61, 0x9c, 0xd3, 0xee, 0x86, 0xee, 0xe0, 0x1d, 0xfe, 0x83, 0xfd, 0xc0, 0xd0, 0xcf, 0x54, 0x6e, + 0x9b, 0xee, 0x4e, 0xcc, 0x91, 0xf5, 0xdc, 0x88, 0x5b, 0xa8, 0xa8, 0x61, 0x20, 0x9b, 0xb4, 0x02, 0x8b, 0x0a, 0x72, + 0x82, 0xed, 0x0f, 0x21, 0x7e, 0xda, 0x7b, 0x4b, 0xf8, 0xc9, 0xb9, 0xdb, 0x38, 0x67, 0x1b, 0x94, 0x55, 0x10, 0xf5, + 0x70, 0xfc, 0x8d, 0x28, 0x0b, 0xf5, 0x47, 0xbd, 0xe1, 0x19, 0x70, 0xdf, 0x25, 0xd6, 0x17, 0xa2, 0xfa, 0xe5, 0xfe, + 0xa7, 0xdc, 0x1e, 0x08, 0x88, 0xe7, 0xc1, 0x10, 0x6f, 0x48, 0xb5, 0xa6, 0x28, 0x41, 0xb2, 0x64, 0xe2, 0xde, 0xc0, + 0xc5, 0xa3, 0x6c, 0xad, 0xb8, 0x03, 0xae, 0x02, 0x1e, 0x0b, 0x7b, 0x68, 0x9d, 0x22, 0x2b, 0x26, 0x26, 0xef, 0x59, + 0x4f, 0xac, 0x07, 0x16, 0x39, 0x15, 0x2d, 0xa4, 0x75, 0x1f, 0x81, 0x4f, 0x0f, 0xc2, 0xe6, 0xb8, 0x03, 0xed, 0xc3, + 0xe3, 0x79, 0x33, 0x16, 0x2d, 0xe1, 0x0f, 0x19, 0x95, 0x81, 0xea, 0xa0, 0x43, 0xb2, 0x82, 0x99, 0x3a, 0xed, 0x40, + 0x74, 0x56, 0xe8, 0x92, 0xd3, 0xf4, 0xe9, 0xa1, 0x03, 0x89, 0x2a, 0x07, 0x9d, 0x25, 0xc6, 0x2e, 0x40, 0x93, 0xde, + 0x1e, 0x87, 0xf7, 0x7e, 0xfc, 0xbe, 0xa6, 0xdd, 0xfe, 0x9a, 0x56, 0x34, 0x93, 0x4d, 0x67, 0x5b, 0x4f, 0x40, 0x0b, + 0xbc, 0x7e, 0xed, 0xf0, 0x8f, 0x37, 0x6f, 0xdf, 0x24, 0x8d, 0xcd, 0x86, 0x57, 0x8f, 0x51, 0xab, 0x0c, 0xff, 0x1e, + 0x32, 0xfc, 0x3f, 0x93, 0x81, 0xca, 0xf1, 0x83, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x4f, 0xf4, 0x2a, 0x18, 0x9f, + 0x43, 0x40, 0x5f, 0x29, 0x0f, 0x9d, 0x71, 0x34, 0x3c, 0x82, 0x7e, 0xb0, 0x00, 0xec, 0xd6, 0xb9, 0x1a, 0x72, 0xb6, + 0x4a, 0x9b, 0xe9, 0x77, 0x87, 0x65, 0xb3, 0x73, 0x04, 0xfb, 0xc4, 0xf8, 0x6a, 0xce, 0x78, 0x49, 0x3b, 0x26, 0x8f, + 0x60, 0x2e, 0xa4, 0xfd, 0x76, 0x2d, 0x0f, 0x2d, 0xc9, 0x73, 0xf5, 0x24, 0x6a, 0x77, 0x8b, 0x02, 0x8a, 0x84, 0xa2, + 0xa4, 0x73, 0x9f, 0xd6, 0x47, 0xf3, 0x5c, 0xe7, 0x83, 0xf9, 0x2c, 0x7a, 0x76, 0x54, 0x07, 0xee, 0x20, 0xe1, 0x65, + 0x39, 0xa4, 0x62, 0x2b, 0x3e, 0xcf, 0xc0, 0x70, 0xda, 0x19, 0xa6, 0x82, 0xd4, 0xac, 0xda, 0xcf, 0x05, 0x64, 0x26, + 0x07, 0xaa, 0x07, 0x2b, 0x8e, 0xcb, 0xb5, 0x94, 0x0d, 0x07, 0xdd, 0x5d, 0x4e, 0xbb, 0xb9, 0xb7, 0x30, 0x13, 0xa7, + 0x23, 0x39, 0x5b, 0x8b, 0x39, 0x0e, 0x3b, 0x5a, 0x2f, 0x96, 0x24, 0xbb, 0x5b, 0x75, 0xcd, 0x9a, 0xe7, 0x4e, 0xa6, + 0x32, 0xe7, 0xfc, 0x89, 0x5f, 0x90, 0x90, 0x66, 0x8b, 0x7e, 0x55, 0x14, 0xc5, 0x02, 0xa0, 0xa0, 0x8e, 0xc9, 0x44, + 0xf3, 0x00, 0x8f, 0x14, 0xdb, 0x85, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x48, 0xeb, 0xcf, 0x16, 0x27, 0x77, 0xbc, + 0x05, 0xa4, 0x64, 0x01, 0x42, 0x5a, 0x88, 0x47, 0x30, 0xf3, 0x58, 0x13, 0xc6, 0x2f, 0xad, 0x57, 0xc7, 0x64, 0xd1, + 0x97, 0x14, 0x80, 0x45, 0xab, 0xd1, 0x85, 0x65, 0x01, 0x45, 0xc3, 0x14, 0xc6, 0x79, 0x30, 0xf6, 0xda, 0xdd, 0x11, + 0xf7, 0x07, 0xe4, 0x70, 0xa2, 0x2e, 0x2a, 0xba, 0x5b, 0x7c, 0x5c, 0x0b, 0xc9, 0x8a, 0xbd, 0xd3, 0x17, 0xd6, 0x39, + 0x1c, 0x16, 0x28, 0xa8, 0x4b, 0x20, 0xa5, 0x94, 0x2f, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x8b, 0x1e, 0xa7, 0xb3, 0x18, + 0x7d, 0x40, 0x3f, 0x97, 0xf5, 0x9f, 0xa8, 0xd5, 0x59, 0x3c, 0xd4, 0xa4, 0x83, 0x44, 0xef, 0x2c, 0x1b, 0xc0, 0xb4, + 0x9e, 0x3b, 0x13, 0x78, 0x57, 0xfd, 0x96, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xba, 0x5e, 0x9e, 0xf0, 0xf6, 0xdb, 0x1d, + 0x12, 0x4d, 0xc5, 0xf2, 0x9e, 0x4e, 0x93, 0x20, 0xef, 0x0c, 0x8f, 0x0f, 0xaf, 0x1b, 0xa9, 0xbd, 0x13, 0xd4, 0xa3, + 0x62, 0x4a, 0x7c, 0xef, 0x0b, 0x6f, 0x24, 0x2f, 0x8a, 0x60, 0x59, 0x9c, 0x91, 0x52, 0x65, 0xef, 0xc8, 0xfa, 0x53, + 0x11, 0x8c, 0x40, 0xc0, 0xe9, 0xdd, 0xc0, 0xfc, 0xc8, 0x74, 0x58, 0x1c, 0x2e, 0xa4, 0xe8, 0xa3, 0x3a, 0x5f, 0x77, + 0x95, 0x6d, 0x7d, 0xe1, 0xe8, 0x3e, 0x0b, 0x5f, 0xdd, 0x97, 0xa5, 0xc1, 0xe3, 0x65, 0x69, 0x80, 0x54, 0x23, 0xf3, + 0xb2, 0xd9, 0x25, 0x03, 0x5d, 0x20, 0x46, 0xf0, 0x3b, 0x78, 0x16, 0xbe, 0x06, 0xfe, 0xff, 0x4a, 0xbd, 0xf9, 0xc3, + 0xc5, 0xe6, 0x2b, 0x2a, 0xcd, 0x57, 0x56, 0x19, 0xe3, 0x9d, 0x72, 0x1e, 0x66, 0x50, 0x4e, 0x18, 0x16, 0x6c, 0xe5, + 0xff, 0x2f, 0xa0, 0xfd, 0x77, 0x1c, 0xc3, 0x17, 0xfe, 0x14, 0xcf, 0x90, 0x1e, 0x0c, 0x44, 0x38, 0x9c, 0xa2, 0xc9, + 0xab, 0x11, 0x1e, 0xf9, 0x48, 0xb5, 0x30, 0x63, 0x34, 0x81, 0x7e, 0x0f, 0xf9, 0x63, 0x1c, 0x4e, 0x60, 0x03, 0x05, + 0x3e, 0x8e, 0xde, 0x04, 0x21, 0x1e, 0x47, 0x40, 0x15, 0x78, 0x38, 0x0c, 0x90, 0xa1, 0x1d, 0xe3, 0x00, 0xc4, 0x29, + 0x92, 0xb0, 0x06, 0xa0, 0xb3, 0x10, 0x7b, 0x13, 0x10, 0x37, 0xc6, 0xde, 0x0c, 0x4f, 0xc7, 0x68, 0x8a, 0x27, 0x00, + 0x1d, 0x1e, 0x45, 0x95, 0x13, 0x61, 0x1f, 0xb6, 0xc3, 0x31, 0x99, 0xe2, 0x51, 0x88, 0xf4, 0x60, 0xe0, 0x98, 0x80, + 0x08, 0x07, 0x7b, 0xfe, 0x9b, 0x10, 0x07, 0x13, 0xd0, 0x3b, 0x1a, 0xbd, 0x00, 0xb1, 0xb3, 0x11, 0x32, 0xa3, 0x81, + 0x17, 0x14, 0x44, 0x8f, 0x81, 0x16, 0xfc, 0x75, 0x41, 0x03, 0x48, 0x7c, 0x14, 0xe2, 0x19, 0xc4, 0xae, 0xaf, 0xf8, + 0xcd, 0x68, 0x70, 0xf3, 0x7d, 0xe4, 0xfd, 0x61, 0xcc, 0xc2, 0xbf, 0x2e, 0x66, 0xbe, 0x42, 0x00, 0xa6, 0xa0, 0x1b, + 0xe4, 0x20, 0x3d, 0x18, 0xdd, 0xc0, 0x3c, 0x7d, 0x35, 0x43, 0x53, 0xe0, 0x1a, 0x4f, 0xd1, 0x0c, 0x45, 0x0a, 0x5d, + 0x60, 0x1f, 0x19, 0x26, 0x07, 0x98, 0xbe, 0x12, 0xc6, 0xd1, 0x9f, 0x18, 0xc6, 0xc7, 0x7c, 0xfa, 0x13, 0xbb, 0xf4, + 0xff, 0x48, 0x41, 0xd0, 0x8e, 0xe9, 0x36, 0x2c, 0x76, 0xcd, 0x95, 0x5e, 0x75, 0x51, 0x70, 0x43, 0x87, 0x6e, 0x04, + 0x2e, 0xf9, 0x3e, 0x62, 0x79, 0x52, 0xfa, 0xe9, 0x67, 0xdd, 0x39, 0x50, 0xfa, 0x69, 0xac, 0xcb, 0x79, 0x7a, 0x53, + 0x52, 0xf4, 0xfa, 0xfa, 0x1d, 0xdc, 0xca, 0xaa, 0x0a, 0xf1, 0x66, 0x0b, 0x97, 0xbf, 0x3d, 0x92, 0x8d, 0xba, 0xce, + 0x73, 0xe8, 0x15, 0xd5, 0x14, 0xee, 0x0d, 0xa8, 0x6f, 0x16, 0x30, 0xc6, 0xf1, 0xb2, 0x4b, 0xdf, 0x55, 0x94, 0x08, + 0x8a, 0x56, 0x6c, 0x43, 0x11, 0x93, 0xd0, 0x07, 0xd4, 0x14, 0x49, 0xa6, 0x86, 0x33, 0xa3, 0xa6, 0x83, 0x9e, 0x56, + 0x2b, 0x31, 0xdd, 0x30, 0x58, 0x02, 0x62, 0xd2, 0xbe, 0xed, 0x8d, 0xcb, 0xd0, 0x58, 0x75, 0x4d, 0xa5, 0x84, 0x8e, + 0x41, 0x59, 0x15, 0xa6, 0xb1, 0xba, 0x76, 0x22, 0xa2, 0x2f, 0x06, 0x89, 0xbb, 0x65, 0x05, 0x53, 0x97, 0xf9, 0x34, + 0xd6, 0xad, 0xa2, 0x92, 0xa0, 0xba, 0x15, 0xf3, 0xe5, 0x41, 0xcf, 0x2a, 0xca, 0x57, 0x70, 0x9b, 0x84, 0x77, 0x01, + 0xcd, 0x43, 0x46, 0xcb, 0xa6, 0x82, 0xe6, 0x24, 0xb9, 0xbe, 0xfe, 0xe9, 0xef, 0xea, 0x33, 0x85, 0x32, 0xe1, 0xcc, + 0x09, 0x7d, 0xbe, 0x61, 0x54, 0x93, 0x9e, 0x6f, 0x3c, 0x32, 0x1f, 0x1c, 0x5a, 0xe8, 0xd3, 0xc1, 0xbf, 0xfc, 0x33, + 0x29, 0xef, 0x4e, 0x9b, 0xbd, 0x24, 0xfd, 0x5f, 0x37, 0x9d, 0x86, 0x49, 0xac, 0x97, 0x35, 0x93, 0xe9, 0x35, 0x18, + 0x18, 0xbb, 0xe6, 0x01, 0x38, 0xa7, 0x1c, 0x30, 0xb4, 0x65, 0xcf, 0x03, 0x60, 0xff, 0x72, 0xf3, 0x02, 0xfd, 0xda, + 0xc2, 0x09, 0xa6, 0x06, 0x7b, 0xed, 0x65, 0x4d, 0x65, 0xd9, 0xe4, 0xc9, 0xbb, 0x5f, 0xae, 0x6f, 0xce, 0x1e, 0xaf, + 0x35, 0x11, 0xa2, 0x3c, 0x33, 0x1f, 0x42, 0xd6, 0x95, 0x64, 0x2d, 0xe9, 0xa4, 0x16, 0xeb, 0xa8, 0x10, 0x38, 0x79, + 0xa4, 0x9f, 0x17, 0xac, 0xa2, 0xc6, 0xa9, 0x9e, 0xd1, 0x4d, 0xd1, 0x97, 0x6c, 0x3c, 0xe9, 0x7e, 0x60, 0xa5, 0x6b, + 0x4e, 0x89, 0x6b, 0x8e, 0x8c, 0xab, 0x3f, 0x13, 0xfd, 0x0b, 0x65, 0x37, 0xa3, 0x8e, 0x36, 0x12, 0x00, 0x00}; } // namespace captive_portal } // namespace esphome diff --git a/esphome/components/web_server/server_index.h b/esphome/components/web_server/server_index.h index 8eaaaf4581..4e6e136f8c 100644 --- a/esphome/components/web_server/server_index.h +++ b/esphome/components/web_server/server_index.h @@ -6,585 +6,596 @@ namespace esphome { namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xbd, 0x7d, 0xd9, 0x76, 0xdb, 0xc8, 0x92, 0xe0, 0xf3, - 0x9c, 0xd3, 0x7f, 0x30, 0x2f, 0x30, 0x4a, 0x6d, 0x03, 0x57, 0x20, 0x44, 0x52, 0x96, 0xed, 0x02, 0x05, 0xf2, 0xca, - 0x4b, 0x5d, 0xbb, 0xca, 0x5b, 0x59, 0xb2, 0x6b, 0x51, 0xb1, 0x2c, 0x88, 0x4c, 0x8a, 0x28, 0x83, 0x00, 0x0b, 0x48, - 0x6a, 0x29, 0x0a, 0x7d, 0xfa, 0xa9, 0x9f, 0xe6, 0x9c, 0x59, 0x1f, 0xfa, 0x65, 0x4e, 0xf7, 0xc3, 0x7c, 0xc4, 0x3c, - 0xf7, 0xa7, 0xdc, 0x1f, 0x98, 0xfe, 0x84, 0x89, 0x88, 0x5c, 0x90, 0x00, 0xa9, 0xc5, 0xd5, 0xd5, 0x7d, 0xbc, 0x08, - 0xc8, 0x35, 0x22, 0x32, 0x32, 0xb6, 0x8c, 0x84, 0x76, 0xef, 0x8c, 0xb3, 0x11, 0xbf, 0x98, 0x33, 0x6b, 0xca, 0x67, - 0x49, 0x7f, 0x57, 0xfe, 0xcf, 0xa2, 0x71, 0x7f, 0x37, 0x89, 0xd3, 0x4f, 0x56, 0xce, 0x92, 0x30, 0x1e, 0x65, 0xa9, - 0x35, 0xcd, 0xd9, 0x24, 0x1c, 0x47, 0x3c, 0x0a, 0xe2, 0x59, 0x74, 0xc2, 0xac, 0xad, 0xfe, 0xee, 0x8c, 0xf1, 0xc8, - 0x1a, 0x4d, 0xa3, 0xbc, 0x60, 0x3c, 0x7c, 0x7f, 0xf0, 0x55, 0xeb, 0x51, 0x7f, 0xb7, 0x18, 0xe5, 0xf1, 0x9c, 0x5b, - 0x38, 0x64, 0x38, 0xcb, 0xc6, 0x8b, 0x84, 0xf5, 0x4f, 0xa3, 0xdc, 0x7a, 0xc6, 0xc2, 0x37, 0xc7, 0xbf, 0xb0, 0x11, - 0xf7, 0xc7, 0x6c, 0x12, 0xa7, 0xec, 0x6d, 0x9e, 0xcd, 0x59, 0xce, 0x2f, 0xbc, 0xfd, 0xf5, 0x15, 0x31, 0x2b, 0xbc, - 0x4f, 0xba, 0xea, 0x84, 0xf1, 0x37, 0x67, 0xa9, 0xea, 0xf3, 0x94, 0x89, 0x49, 0xb2, 0xbc, 0xf0, 0x8a, 0x2b, 0xda, - 0xec, 0x5f, 0xcc, 0x8e, 0xb3, 0xa4, 0xf0, 0x9e, 0xe8, 0xfa, 0x79, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0xa7, 0x51, 0x61, - 0xb4, 0xf4, 0xde, 0xac, 0x69, 0x32, 0x97, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x62, 0xc6, 0xf2, 0xe8, 0x38, 0x61, 0x5e, - 0xcc, 0x42, 0x87, 0x79, 0xdc, 0x8b, 0xdd, 0xb0, 0xcf, 0xad, 0x38, 0xb5, 0xd8, 0xe0, 0x19, 0xa3, 0x92, 0x25, 0xd3, - 0xad, 0x82, 0x3b, 0x6d, 0x0f, 0xc8, 0x35, 0x89, 0x4f, 0x16, 0xfa, 0xfd, 0x2c, 0x8f, 0xb9, 0x7a, 0x3e, 0x8d, 0x92, - 0x05, 0x0b, 0xe2, 0xd2, 0x0d, 0xd8, 0x21, 0x1f, 0x86, 0xb1, 0xf7, 0x84, 0x06, 0x85, 0x21, 0x97, 0x93, 0x2c, 0x77, - 0x90, 0x56, 0x31, 0x8e, 0xcd, 0x2f, 0x2f, 0x1d, 0x1e, 0x2e, 0x4b, 0xd7, 0x7d, 0xc2, 0xfc, 0x51, 0x94, 0x24, 0x0e, - 0x4e, 0x7c, 0xf7, 0x6e, 0x8c, 0x33, 0xc6, 0x1e, 0x3f, 0x8c, 0x87, 0x6e, 0x2f, 0x9e, 0x38, 0x05, 0x73, 0xab, 0x7e, - 0xd9, 0xc4, 0x2a, 0x98, 0xc3, 0x5d, 0xf7, 0xcd, 0xd5, 0x7d, 0x72, 0xc6, 0x17, 0x39, 0xc0, 0x5e, 0x7a, 0x6f, 0xd4, - 0xcc, 0xfb, 0x58, 0xff, 0x89, 0x3a, 0xf6, 0x00, 0xf6, 0x82, 0x5b, 0x1f, 0xc2, 0xb3, 0x38, 0x1d, 0x67, 0x67, 0xfe, - 0xfe, 0x34, 0x82, 0x1f, 0xef, 0xb2, 0x8c, 0xdf, 0xbd, 0xeb, 0x9c, 0x66, 0xf1, 0xd8, 0x6a, 0x87, 0xa1, 0x59, 0x79, - 0xf1, 0x64, 0x7f, 0xff, 0xf2, 0xb2, 0x51, 0xe0, 0xa7, 0x11, 0x8f, 0x4f, 0x99, 0xe8, 0x0c, 0x00, 0xd8, 0xf0, 0x73, - 0xce, 0xd9, 0x78, 0x9f, 0x5f, 0x24, 0x50, 0xca, 0x18, 0x2f, 0x6c, 0xc0, 0xf1, 0x69, 0x36, 0x02, 0xb2, 0xa5, 0x06, - 0xe1, 0xa1, 0x69, 0xce, 0xe6, 0x49, 0x34, 0x62, 0x58, 0x0f, 0x23, 0x55, 0x3d, 0xaa, 0x46, 0xde, 0x57, 0xa1, 0x58, - 0x5e, 0xc7, 0xf5, 0x72, 0x16, 0xa6, 0xec, 0xcc, 0x7a, 0x15, 0xcd, 0x7b, 0xa3, 0x24, 0x2a, 0x0a, 0x2b, 0x63, 0x4b, - 0x42, 0x21, 0x5f, 0x8c, 0x80, 0x41, 0x08, 0xc1, 0x25, 0x90, 0x89, 0x4f, 0xe3, 0xc2, 0xff, 0xb8, 0x31, 0x2a, 0x8a, - 0x77, 0xac, 0x58, 0x24, 0x7c, 0x23, 0x84, 0xb5, 0xe0, 0x77, 0xc2, 0xf0, 0x2b, 0x97, 0x4f, 0xf3, 0xec, 0xcc, 0x7a, - 0x96, 0xe7, 0xd0, 0xdc, 0x86, 0x29, 0x45, 0x03, 0x2b, 0x2e, 0xac, 0x34, 0xe3, 0x96, 0x1e, 0x0c, 0x17, 0xd0, 0xb7, - 0xde, 0x17, 0xcc, 0x3a, 0x5a, 0xa4, 0x45, 0x34, 0x61, 0xd0, 0xf4, 0xc8, 0xca, 0x72, 0xeb, 0x08, 0x06, 0x3d, 0x82, - 0x25, 0x2b, 0x38, 0xec, 0x1a, 0xdf, 0x76, 0x7b, 0x34, 0x17, 0x14, 0x1e, 0xb0, 0x73, 0x1e, 0xb2, 0x12, 0x18, 0xd3, - 0x2a, 0x34, 0x1a, 0x8e, 0xbb, 0x4c, 0xa0, 0x80, 0x85, 0x39, 0x43, 0x96, 0x75, 0xcc, 0xc6, 0x7a, 0x71, 0x3e, 0xdc, - 0xbd, 0xab, 0x69, 0x0d, 0x34, 0x71, 0xa0, 0x6d, 0xd1, 0x68, 0xeb, 0x09, 0xc4, 0x6b, 0x24, 0x72, 0x3d, 0xe6, 0x4b, - 0xf2, 0xed, 0x5f, 0xa4, 0xa3, 0xfa, 0xd8, 0x50, 0x59, 0xf2, 0x6c, 0x9f, 0xe7, 0x71, 0x7a, 0x02, 0x40, 0xc8, 0x99, - 0xcc, 0x26, 0x65, 0x29, 0x16, 0xff, 0x2d, 0x0b, 0x59, 0xd8, 0xc7, 0xd1, 0x33, 0xe6, 0xd8, 0x05, 0xf5, 0xb0, 0xc3, - 0x10, 0x49, 0x0f, 0x0c, 0xc6, 0x06, 0x2c, 0x60, 0x9b, 0xb6, 0xed, 0x7d, 0xe5, 0x7a, 0x17, 0xc8, 0x41, 0xbe, 0xef, - 0x13, 0xfb, 0x8a, 0xce, 0x71, 0xd8, 0x41, 0xa0, 0xfd, 0x84, 0xa5, 0x27, 0x7c, 0x3a, 0x60, 0x87, 0xed, 0x61, 0xc0, - 0x01, 0xaa, 0xf1, 0x62, 0xc4, 0x1c, 0xe4, 0x47, 0x2f, 0xc7, 0xed, 0xb3, 0xe9, 0xc0, 0x14, 0xb8, 0x30, 0x77, 0x08, - 0xc7, 0xda, 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x63, 0x96, 0x1b, 0x70, 0xe8, 0x66, - 0xbd, 0xda, 0x0a, 0x2e, 0x60, 0x85, 0xa0, 0x9f, 0x35, 0x59, 0xa4, 0x23, 0x1e, 0x83, 0xe0, 0xb2, 0x37, 0x01, 0x5c, - 0xb1, 0x72, 0x7a, 0xe1, 0x6c, 0xb7, 0x74, 0x9d, 0xd8, 0xdd, 0x64, 0x87, 0xf9, 0x66, 0x67, 0xe8, 0x21, 0x94, 0x9a, - 0xf8, 0x12, 0xf1, 0x18, 0x10, 0x2c, 0xbd, 0xf7, 0x4c, 0x6f, 0xcf, 0x0f, 0x03, 0xe6, 0xaf, 0xf2, 0x71, 0xc8, 0xfd, - 0x59, 0x34, 0x47, 0x6c, 0x18, 0xf1, 0x40, 0x94, 0x8e, 0x10, 0xba, 0xda, 0xba, 0x20, 0xc5, 0xfc, 0x8a, 0x05, 0x5c, - 0x20, 0x08, 0xec, 0xd9, 0x67, 0xd1, 0x68, 0x0a, 0x5b, 0xbc, 0x22, 0xdc, 0x58, 0x6d, 0x87, 0x51, 0xce, 0x22, 0xce, - 0x9e, 0x25, 0x0c, 0xdf, 0x70, 0x05, 0xa0, 0xa7, 0x0d, 0xbc, 0xae, 0xf6, 0x5d, 0x12, 0xf3, 0xd7, 0x19, 0xcc, 0xd3, - 0x13, 0x4c, 0x02, 0x5c, 0x9c, 0xc3, 0x26, 0x47, 0x16, 0xd9, 0xe3, 0xb0, 0x5a, 0xc7, 0x0b, 0x0e, 0xeb, 0x96, 0x62, - 0x0b, 0x1b, 0xa8, 0xed, 0xc5, 0x3e, 0x07, 0x22, 0x3e, 0xc9, 0x52, 0x0e, 0xc3, 0x01, 0xbc, 0x9a, 0x83, 0xfc, 0x68, - 0x3e, 0x67, 0xe9, 0xf8, 0xc9, 0x34, 0x4e, 0xc6, 0x40, 0x8d, 0x12, 0xf0, 0x4d, 0x59, 0x08, 0x78, 0x02, 0x32, 0xc1, - 0xf5, 0x18, 0xd1, 0xf2, 0x21, 0x23, 0xf3, 0xd0, 0xb6, 0x7b, 0x28, 0x81, 0x24, 0x16, 0x28, 0x83, 0x68, 0xe1, 0xde, - 0x81, 0xe8, 0x2f, 0x5c, 0xbe, 0x19, 0xc6, 0x7a, 0x19, 0x25, 0x81, 0xdf, 0xa2, 0xa4, 0x01, 0xfa, 0x33, 0x90, 0x81, - 0x3d, 0x14, 0x5c, 0xdf, 0x49, 0xa9, 0x93, 0x30, 0x85, 0x21, 0x10, 0x60, 0x84, 0x12, 0x44, 0xd2, 0xe0, 0x6d, 0x96, - 0x5c, 0x4c, 0xe2, 0x24, 0xd9, 0x5f, 0xcc, 0xe7, 0x59, 0xce, 0xbd, 0xaf, 0xc3, 0x25, 0xcf, 0x2a, 0x5c, 0x69, 0x93, - 0x17, 0x67, 0x31, 0x47, 0x82, 0xba, 0xcb, 0x51, 0x04, 0x4b, 0xfd, 0x38, 0xcb, 0x12, 0x16, 0xa5, 0x80, 0x06, 0x1b, - 0xd8, 0x76, 0x90, 0x2e, 0x92, 0xa4, 0x77, 0x0c, 0xc3, 0x7e, 0xea, 0x51, 0xb5, 0x90, 0xf8, 0x01, 0x3d, 0xef, 0xe5, - 0x79, 0x74, 0x01, 0x0d, 0xb1, 0x0d, 0xf0, 0x22, 0xac, 0xd6, 0xd7, 0xfb, 0x6f, 0x5e, 0xfb, 0x82, 0xf1, 0xe3, 0xc9, - 0x05, 0x00, 0x5a, 0x56, 0x52, 0x73, 0x92, 0x67, 0xb3, 0xc6, 0xd4, 0x48, 0x87, 0x38, 0x64, 0xbd, 0x2b, 0x40, 0x88, - 0x69, 0x64, 0x58, 0x25, 0x66, 0x42, 0xf0, 0x9a, 0xf8, 0x59, 0x56, 0xe2, 0x1e, 0x18, 0xe0, 0x43, 0x20, 0x8a, 0x61, - 0xca, 0xeb, 0xa1, 0xe5, 0xf9, 0xc5, 0x32, 0x0e, 0x09, 0xce, 0x39, 0xea, 0x5f, 0x84, 0x71, 0x14, 0xc1, 0xec, 0x4b, - 0x31, 0x60, 0xa9, 0x20, 0x8e, 0xcb, 0xd2, 0x8b, 0x34, 0x13, 0xa3, 0xc4, 0x43, 0x81, 0xc2, 0x61, 0x1b, 0x5d, 0x5e, - 0x32, 0x78, 0x71, 0xbd, 0x6f, 0xc2, 0x65, 0xa4, 0xf0, 0x41, 0x0d, 0x85, 0xfb, 0x2b, 0x10, 0x72, 0x02, 0x35, 0xd9, - 0x29, 0xe8, 0x41, 0x80, 0xf3, 0x6b, 0x10, 0xb5, 0x93, 0x04, 0xa1, 0xb8, 0xd3, 0xf1, 0x40, 0x83, 0x3e, 0x99, 0x46, - 0xe9, 0x09, 0x1b, 0x07, 0x11, 0x2b, 0xa5, 0xe4, 0xdd, 0xb3, 0x60, 0x8d, 0x81, 0x9d, 0x0a, 0xeb, 0xf9, 0xc1, 0xab, - 0x97, 0x72, 0xe5, 0x6a, 0xc2, 0x18, 0x16, 0x69, 0x01, 0x6a, 0x15, 0xc4, 0xb6, 0x14, 0xc7, 0xcf, 0xb8, 0x92, 0xde, - 0xa2, 0x24, 0x2e, 0xde, 0xcf, 0xc1, 0xc4, 0x60, 0x6f, 0x61, 0x18, 0x98, 0x3e, 0x84, 0xa9, 0xa8, 0x1c, 0xe6, 0x13, - 0x15, 0x63, 0x5d, 0x04, 0x9d, 0x05, 0xa6, 0xe2, 0x35, 0x73, 0xdc, 0x12, 0x58, 0x95, 0xc7, 0x23, 0x2b, 0x1a, 0x8f, - 0x5f, 0xa4, 0x31, 0x8f, 0xa3, 0x24, 0xfe, 0x8d, 0x28, 0xb9, 0x44, 0x1e, 0xe3, 0x3d, 0xb9, 0x08, 0x80, 0x3b, 0xf5, - 0x48, 0x5c, 0x25, 0x64, 0xef, 0x10, 0x31, 0x84, 0xb4, 0x4c, 0xc2, 0xc3, 0xa1, 0x04, 0x2f, 0xf1, 0xe7, 0x8b, 0x62, - 0x8a, 0x84, 0x95, 0x03, 0xa3, 0x20, 0xcf, 0x8e, 0x0b, 0x96, 0x9f, 0xb2, 0xb1, 0xe6, 0x80, 0x02, 0xb0, 0xa2, 0xe6, - 0x60, 0xbc, 0xd0, 0x8c, 0x8e, 0xd2, 0xa1, 0x0c, 0x86, 0xea, 0x99, 0x62, 0x96, 0x49, 0x66, 0xd6, 0x16, 0x8e, 0x96, - 0x02, 0x8e, 0x30, 0x2a, 0xa4, 0x24, 0xc8, 0x43, 0x85, 0xe1, 0x14, 0xa4, 0x10, 0x68, 0x05, 0x73, 0x9b, 0x2b, 0x4d, - 0xf6, 0x6c, 0x41, 0x2a, 0x21, 0x87, 0x8e, 0xb0, 0x91, 0x09, 0xd2, 0xdc, 0x85, 0x5d, 0x05, 0x52, 0x5e, 0x82, 0x2b, - 0xa4, 0x88, 0x32, 0x73, 0x90, 0x01, 0xc2, 0x6f, 0x84, 0x2e, 0xf4, 0xb1, 0x05, 0xb1, 0x81, 0xaf, 0x57, 0x1e, 0x08, - 0x2b, 0xf1, 0xae, 0x10, 0xf1, 0xae, 0x00, 0x1b, 0x27, 0x46, 0x7e, 0xf2, 0xee, 0x70, 0x3f, 0xcd, 0xf6, 0x46, 0x23, - 0x56, 0x14, 0x19, 0xc0, 0x76, 0x87, 0xda, 0x5f, 0x65, 0x68, 0x01, 0x25, 0x5d, 0x2d, 0xeb, 0xec, 0x82, 0x34, 0xb8, - 0xa9, 0x56, 0x94, 0x4e, 0x0f, 0xec, 0x8f, 0x1f, 0x41, 0x66, 0x7b, 0x92, 0x0c, 0x40, 0xf5, 0x55, 0xc3, 0x4f, 0xd8, - 0x33, 0x75, 0xca, 0xac, 0xb5, 0x2f, 0x9d, 0x3a, 0x48, 0x1e, 0x0c, 0xeb, 0x96, 0xc6, 0x82, 0xae, 0x1d, 0x1a, 0x57, - 0x43, 0x2a, 0xc8, 0xe5, 0x09, 0xa9, 0x6c, 0x63, 0x19, 0xc1, 0x6a, 0x2b, 0x3d, 0x22, 0xbd, 0xc2, 0xa6, 0x20, 0x40, - 0x0f, 0xd9, 0xb0, 0x27, 0xeb, 0xc3, 0x5c, 0x50, 0x2e, 0x67, 0xbf, 0x2e, 0x58, 0xc1, 0x05, 0xeb, 0xc2, 0xb8, 0x05, - 0x8c, 0x5b, 0xae, 0x58, 0x87, 0x35, 0xdb, 0x71, 0x1d, 0x6c, 0x6f, 0xe6, 0xa8, 0xc7, 0x0a, 0xe4, 0xe4, 0xeb, 0xd9, - 0x09, 0x61, 0x65, 0xee, 0xe5, 0xe5, 0x37, 0x6a, 0x90, 0x6a, 0x29, 0xb5, 0x0d, 0xd4, 0x58, 0x13, 0x5b, 0x35, 0x19, - 0xdb, 0xae, 0x54, 0xa8, 0x77, 0x3a, 0xbd, 0x1a, 0x1f, 0xc0, 0x9e, 0x6b, 0x6b, 0x96, 0xae, 0x8c, 0xed, 0xb7, 0x8a, - 0xa6, 0x6f, 0xc4, 0xc8, 0x64, 0x8d, 0xb2, 0x9b, 0xb9, 0x47, 0xed, 0x78, 0x68, 0xbb, 0x52, 0x57, 0x09, 0x86, 0x45, - 0x5d, 0x30, 0x34, 0xa1, 0x9e, 0xeb, 0x2e, 0xb6, 0x66, 0x2a, 0x16, 0xaa, 0xb5, 0x56, 0x0e, 0x04, 0x0f, 0x0f, 0xc1, - 0x38, 0x59, 0xeb, 0x1f, 0xbc, 0x8e, 0x66, 0x0c, 0x29, 0xea, 0x5d, 0xd5, 0x40, 0x3a, 0x10, 0xd0, 0x64, 0xd8, 0x54, - 0x6f, 0xdc, 0x15, 0x56, 0x53, 0x7d, 0x7f, 0xc5, 0x60, 0x45, 0x80, 0x7d, 0x5d, 0xae, 0x59, 0x22, 0xd2, 0x9b, 0x82, - 0x4b, 0x34, 0x7d, 0x44, 0x99, 0x58, 0x13, 0x52, 0xf0, 0x80, 0x3c, 0x2c, 0x7f, 0x63, 0xe1, 0x64, 0x2b, 0xa6, 0x70, - 0xe4, 0x28, 0x53, 0x80, 0xce, 0xa4, 0x04, 0x40, 0x5c, 0xd2, 0xcf, 0xda, 0xc6, 0x42, 0xb2, 0xed, 0x23, 0x1f, 0xf8, - 0x93, 0x24, 0xe2, 0x4e, 0x67, 0xab, 0xed, 0x02, 0x1f, 0x82, 0x10, 0x07, 0x1d, 0x01, 0xe6, 0x7d, 0x85, 0x0a, 0x43, - 0x54, 0x62, 0x97, 0xfb, 0x60, 0x14, 0x4d, 0xe3, 0x09, 0x77, 0x52, 0x54, 0x22, 0x6e, 0xc9, 0x12, 0x50, 0x32, 0x7a, - 0x5f, 0x81, 0x94, 0xe0, 0x42, 0xba, 0x88, 0x6a, 0x2d, 0xd0, 0x14, 0xa4, 0x24, 0xa5, 0x48, 0x0b, 0x2a, 0x08, 0x0c, - 0xa1, 0xd2, 0x53, 0x1c, 0x05, 0xfa, 0x2d, 0x1e, 0x88, 0x41, 0x83, 0x15, 0x8b, 0x32, 0x1e, 0xc4, 0xab, 0x85, 0xa0, - 0x86, 0x7d, 0x9e, 0xbd, 0xcc, 0xce, 0x58, 0xfe, 0x24, 0x42, 0xd8, 0x03, 0xd1, 0xbd, 0x04, 0x49, 0x4f, 0x02, 0x9d, - 0xf5, 0x14, 0xaf, 0x9c, 0x12, 0xd2, 0xb0, 0x10, 0xb3, 0x18, 0x15, 0x21, 0x68, 0x39, 0xa2, 0x7d, 0x8a, 0x5b, 0x8a, - 0xf6, 0x1e, 0xaa, 0x12, 0xa6, 0x79, 0x6b, 0xef, 0x65, 0x9d, 0xb7, 0x60, 0x84, 0xb9, 0xe2, 0xd6, 0xfa, 0x8e, 0x75, - 0x3d, 0xa9, 0x9b, 0x1d, 0xc9, 0x5b, 0x86, 0x32, 0x03, 0xfd, 0x71, 0x79, 0x59, 0x19, 0xe9, 0xa0, 0x4c, 0xb5, 0x34, - 0x47, 0xcb, 0x49, 0x6c, 0x09, 0xb7, 0x04, 0x65, 0x84, 0x86, 0x57, 0x9e, 0x25, 0x89, 0xa1, 0x8b, 0xbc, 0xb8, 0xe7, - 0x34, 0xd4, 0x11, 0x40, 0x31, 0xab, 0x69, 0xa4, 0x01, 0x0f, 0x74, 0x05, 0x2a, 0x25, 0xa5, 0x8d, 0xbc, 0xaa, 0x89, - 0x80, 0x38, 0x1d, 0xb3, 0x5c, 0x38, 0x68, 0x52, 0x87, 0xc2, 0x84, 0x29, 0x30, 0x34, 0x1b, 0x83, 0x84, 0x57, 0x08, - 0x80, 0x79, 0xe2, 0x4f, 0xb3, 0x82, 0xeb, 0x3a, 0x13, 0xfa, 0xf8, 0xf2, 0x32, 0x16, 0xfe, 0x22, 0x32, 0x40, 0xce, - 0x66, 0xd9, 0x29, 0x5b, 0x03, 0x75, 0x4f, 0x0d, 0x66, 0x82, 0x6c, 0x0c, 0x03, 0x4a, 0x14, 0x54, 0xcb, 0x3c, 0x89, - 0x47, 0x4c, 0x6b, 0xa9, 0x99, 0x0f, 0x06, 0x1d, 0x3b, 0x07, 0x19, 0xc1, 0xdc, 0x7e, 0xbf, 0xdf, 0xf6, 0x3a, 0x6e, - 0x29, 0x08, 0xbe, 0x5c, 0xa1, 0xe8, 0x35, 0xfa, 0x51, 0x9a, 0xe0, 0xeb, 0x64, 0x01, 0x77, 0x0d, 0xa5, 0xc8, 0x85, - 0x9f, 0xe4, 0x49, 0x41, 0xec, 0x7a, 0x63, 0x18, 0x94, 0x33, 0x25, 0xb8, 0xd1, 0xc4, 0x15, 0xdb, 0xf6, 0x9d, 0x26, - 0x9b, 0x66, 0x27, 0xb5, 0xc3, 0xd4, 0xc2, 0xc8, 0x35, 0x2f, 0xb4, 0x07, 0x6c, 0x2e, 0x0f, 0x5a, 0x89, 0x54, 0x0d, - 0xbc, 0x0e, 0x10, 0x0a, 0x4f, 0xd7, 0x59, 0x41, 0xa9, 0xea, 0x2c, 0x85, 0xb8, 0xde, 0x40, 0xef, 0x99, 0x04, 0x73, - 0x1d, 0x09, 0xf6, 0xa5, 0x40, 0xe0, 0xe8, 0x91, 0x89, 0xf5, 0x7a, 0x02, 0xcb, 0x73, 0x1c, 0x8d, 0x3e, 0x69, 0x70, - 0x2b, 0xb2, 0x37, 0xd9, 0xc0, 0x69, 0x94, 0x84, 0x86, 0xb8, 0x32, 0xf1, 0x56, 0x12, 0xba, 0xb6, 0x51, 0xc0, 0x21, - 0x5b, 0x61, 0xfb, 0xe6, 0x42, 0x37, 0xb9, 0x5d, 0xb2, 0x87, 0xf2, 0x9f, 0x34, 0x97, 0x5c, 0xc3, 0x72, 0x5c, 0x49, - 0x03, 0xae, 0x18, 0x0f, 0x96, 0xa6, 0x01, 0x09, 0xf0, 0x5d, 0x39, 0x8e, 0x8b, 0xab, 0x49, 0xf0, 0x87, 0x82, 0xf9, - 0xd4, 0x98, 0xe9, 0x46, 0x48, 0xb5, 0x84, 0x93, 0x66, 0xb0, 0x06, 0x4d, 0x1a, 0x0f, 0x4a, 0xd4, 0x7c, 0x83, 0x86, - 0x0a, 0x71, 0xfc, 0x89, 0xa8, 0x42, 0x13, 0x0c, 0xc1, 0xc8, 0xbd, 0x42, 0x32, 0x5c, 0xb6, 0x2a, 0x5a, 0xa4, 0x4c, - 0x8d, 0x49, 0xa5, 0x6a, 0x96, 0xcb, 0xc0, 0xc0, 0xa2, 0xdd, 0xea, 0x4b, 0x4b, 0x5c, 0x89, 0xdc, 0x34, 0xd4, 0xc2, - 0xa4, 0x50, 0xde, 0x84, 0x93, 0xa3, 0xdf, 0xa5, 0xac, 0x77, 0x13, 0x9f, 0x5c, 0xe1, 0x93, 0xfb, 0x86, 0x0f, 0x65, - 0xf2, 0x76, 0x31, 0x28, 0x82, 0xaf, 0x6b, 0x95, 0x68, 0x9f, 0xfa, 0x28, 0x98, 0x5d, 0x2d, 0x74, 0x41, 0xa0, 0x48, - 0x36, 0x49, 0x07, 0x92, 0xdf, 0x50, 0x6c, 0x54, 0x9e, 0x51, 0xe6, 0x8a, 0x0d, 0x52, 0xf3, 0x4a, 0x33, 0x2f, 0x75, - 0x1b, 0xf6, 0x7b, 0x59, 0x4a, 0x3a, 0x71, 0x41, 0x99, 0xd8, 0xbb, 0x8e, 0x36, 0x5e, 0x1a, 0x66, 0xc2, 0xfa, 0x15, - 0xc6, 0x4e, 0x8d, 0x42, 0xa9, 0x14, 0x81, 0x38, 0x36, 0xbe, 0x56, 0x96, 0x41, 0xe6, 0xaf, 0xb1, 0xa7, 0x00, 0x94, - 0x04, 0x16, 0x5f, 0x53, 0xc9, 0x8b, 0xc2, 0x3a, 0x1d, 0xef, 0x10, 0x1d, 0x2b, 0x11, 0x5a, 0x13, 0xf9, 0x5a, 0x9f, - 0xc5, 0x7e, 0xcd, 0x25, 0x34, 0x29, 0x99, 0x0f, 0xf2, 0xc0, 0x56, 0x81, 0x88, 0x4a, 0xb7, 0x25, 0x83, 0x84, 0x1c, - 0xd2, 0x55, 0xa2, 0xd7, 0x46, 0x32, 0x68, 0x9d, 0x0a, 0x89, 0x96, 0x0e, 0xc3, 0xc8, 0x41, 0xc7, 0x9d, 0xd6, 0x62, - 0x85, 0x90, 0x4d, 0x7b, 0x93, 0x58, 0x11, 0x9d, 0xd3, 0x1c, 0x4d, 0x38, 0x53, 0xa7, 0x3b, 0x0e, 0xa0, 0x03, 0x62, - 0x7f, 0x85, 0xf5, 0xd6, 0x9a, 0x9d, 0xae, 0x5f, 0x39, 0x7c, 0x97, 0x97, 0x11, 0xf2, 0x83, 0x30, 0x78, 0x61, 0xcd, - 0x06, 0x4a, 0xf6, 0xee, 0xbd, 0xc4, 0x56, 0x64, 0x7f, 0x56, 0x25, 0x95, 0xa7, 0x50, 0xe3, 0xdc, 0xfa, 0x3a, 0x31, - 0x33, 0xb4, 0xa8, 0x2a, 0xf6, 0x0d, 0xa9, 0xbe, 0xaf, 0x14, 0x76, 0x85, 0xf2, 0xbe, 0x1c, 0x3a, 0x76, 0x5d, 0x37, - 0xc8, 0xc9, 0x79, 0xb9, 0xb3, 0xce, 0x85, 0xbc, 0x7b, 0xd7, 0xf4, 0x99, 0x4e, 0xf5, 0xf0, 0x4f, 0x1c, 0x54, 0xce, - 0xc5, 0x45, 0x4a, 0x16, 0xcc, 0x13, 0xa5, 0x8e, 0x56, 0x1c, 0xd0, 0x76, 0x0f, 0x3d, 0xed, 0xe8, 0x2c, 0x8a, 0xb9, - 0xa5, 0x47, 0x11, 0x9e, 0x36, 0xca, 0x27, 0x69, 0x74, 0x00, 0x5e, 0x68, 0x42, 0x92, 0x13, 0x6e, 0xda, 0xa2, 0xc5, - 0x68, 0xca, 0x30, 0x04, 0xae, 0xec, 0x09, 0x53, 0xf6, 0xdc, 0x41, 0xbc, 0xc5, 0xc0, 0x6c, 0x3d, 0xec, 0x65, 0xb3, - 0x7b, 0xcd, 0xfc, 0x87, 0x35, 0x02, 0xd9, 0x36, 0x53, 0x75, 0x65, 0xe3, 0x5d, 0x8a, 0x48, 0x8c, 0xb0, 0xad, 0x1b, - 0x5b, 0xda, 0xfa, 0xbd, 0x86, 0x7b, 0x5d, 0x39, 0xe6, 0x35, 0xa5, 0xda, 0xd0, 0xc3, 0xca, 0xcd, 0x61, 0xa6, 0x23, - 0x2f, 0x56, 0xd0, 0xed, 0x89, 0xa0, 0x10, 0x38, 0x11, 0xda, 0x1e, 0x54, 0xdc, 0x40, 0xa4, 0xe4, 0x4a, 0xab, 0x66, - 0x8b, 0x64, 0x2c, 0x81, 0x05, 0x17, 0x96, 0x4b, 0x3e, 0x3a, 0x8b, 0x93, 0xa4, 0x2a, 0xfd, 0x43, 0x05, 0xbc, 0x18, - 0xf6, 0x26, 0xd1, 0x2e, 0x30, 0x5a, 0x28, 0x10, 0x5c, 0x6d, 0x84, 0xbd, 0x77, 0xdc, 0x6a, 0xdd, 0x45, 0xc4, 0x91, - 0x9b, 0xd1, 0x08, 0xa8, 0xc7, 0x08, 0xab, 0x66, 0xed, 0xbd, 0x67, 0x18, 0x52, 0x33, 0xf0, 0x41, 0x75, 0x46, 0xc5, - 0x9f, 0x65, 0x4f, 0x7d, 0x26, 0x7a, 0x37, 0xaa, 0xae, 0x66, 0x40, 0x45, 0x05, 0x3e, 0xcc, 0x10, 0x4b, 0x5b, 0x05, - 0x02, 0x72, 0x3d, 0x2c, 0x4a, 0x01, 0x93, 0x34, 0x58, 0x50, 0x0a, 0xac, 0xb5, 0xb2, 0x7b, 0x79, 0x53, 0x30, 0x87, - 0x42, 0xe1, 0xa2, 0xff, 0x93, 0x6c, 0x36, 0x47, 0xcb, 0xac, 0xc1, 0xd4, 0xd0, 0xe0, 0x7d, 0xa3, 0xbe, 0x5c, 0x53, - 0x56, 0xeb, 0x43, 0x3b, 0xb2, 0xc6, 0x4f, 0xda, 0x51, 0x06, 0x87, 0x6a, 0xa1, 0x8b, 0xea, 0x76, 0x73, 0x53, 0xc4, - 0xac, 0xe3, 0x71, 0x9f, 0xf4, 0xb6, 0xb6, 0x26, 0x3d, 0x4d, 0x03, 0x92, 0x49, 0x92, 0xe1, 0x4d, 0x06, 0x28, 0x2b, - 0xe2, 0x2c, 0xcb, 0x06, 0xf9, 0x96, 0x65, 0x89, 0xeb, 0xf7, 0x6d, 0x6f, 0xaf, 0xe6, 0x59, 0x7b, 0x7b, 0x57, 0xbb, - 0xc8, 0x55, 0x9d, 0xf4, 0x20, 0x0f, 0x87, 0x50, 0xb4, 0x62, 0x53, 0x86, 0xcb, 0x59, 0x36, 0x66, 0x81, 0x0d, 0xdd, - 0x53, 0xbb, 0x94, 0x9b, 0x26, 0x81, 0xcd, 0x91, 0x30, 0x67, 0xf9, 0xae, 0x1e, 0x49, 0x0d, 0xf6, 0x80, 0x05, 0xb4, - 0xb9, 0xf0, 0x5d, 0x78, 0x92, 0x64, 0xc7, 0x51, 0x72, 0x20, 0x14, 0x78, 0xad, 0xe5, 0x07, 0x70, 0x19, 0xc9, 0x62, - 0x35, 0x94, 0xd4, 0x77, 0x83, 0xef, 0x82, 0x9b, 0x7b, 0x54, 0xde, 0x8a, 0xdd, 0xf1, 0xdb, 0x7e, 0xc7, 0x56, 0x11, - 0xb1, 0x97, 0xe6, 0x74, 0xa0, 0x71, 0x0a, 0xa0, 0xcc, 0x01, 0x68, 0xb2, 0xc2, 0x9b, 0xb2, 0xf0, 0xe5, 0xe0, 0xa5, - 0x72, 0xa9, 0x33, 0x70, 0x21, 0xc0, 0xc9, 0x4f, 0x62, 0xde, 0xc2, 0xf3, 0x48, 0xdb, 0x5b, 0x8a, 0x0a, 0x8c, 0x2b, - 0x52, 0x5c, 0xba, 0x54, 0xde, 0xa0, 0xf7, 0x31, 0x3c, 0x82, 0x66, 0x1b, 0x1b, 0x4b, 0xe7, 0x55, 0xc4, 0xa7, 0x7e, - 0x1e, 0xa5, 0xe3, 0x6c, 0xe6, 0xb8, 0x9b, 0xb6, 0xed, 0xfa, 0x05, 0x79, 0x22, 0x5f, 0xba, 0xe5, 0xc6, 0x91, 0x37, - 0x62, 0xa1, 0x3d, 0xb0, 0x37, 0x3f, 0x7a, 0x07, 0x2c, 0x3c, 0xda, 0xdd, 0x58, 0x8e, 0x58, 0xd9, 0x3f, 0xf2, 0xce, - 0x75, 0xcc, 0xdd, 0x7b, 0x8b, 0x52, 0x06, 0x7a, 0x85, 0xfd, 0x73, 0x09, 0x06, 0xb0, 0x1b, 0xc5, 0xdf, 0x41, 0xca, - 0xbd, 0xa7, 0x03, 0x11, 0x19, 0xa7, 0xbd, 0xbc, 0xb4, 0x33, 0x8a, 0x18, 0xd8, 0x77, 0xb4, 0xb3, 0x7a, 0xf7, 0x6e, - 0xa5, 0xe6, 0xab, 0x52, 0xf0, 0x3e, 0xc2, 0x9a, 0xa7, 0xee, 0x3d, 0xa7, 0xa3, 0x95, 0xfa, 0x46, 0x1e, 0x33, 0x52, - 0x9a, 0xab, 0x76, 0x82, 0x63, 0x6c, 0xf1, 0xf5, 0xdb, 0xfa, 0x50, 0x44, 0x29, 0xfc, 0x18, 0xac, 0x97, 0x08, 0xd4, - 0x37, 0x38, 0x38, 0xde, 0x41, 0xb8, 0xb5, 0xeb, 0x0c, 0x02, 0xe7, 0x4e, 0xab, 0x75, 0xf9, 0xd3, 0xd6, 0xe1, 0xcf, - 0x51, 0xeb, 0xb7, 0xbd, 0xd6, 0x8f, 0x43, 0xf7, 0xd2, 0xf9, 0x69, 0x6b, 0x70, 0x28, 0xdf, 0x0e, 0x7f, 0xee, 0xff, - 0x54, 0x0c, 0xff, 0x24, 0x0a, 0x37, 0x5c, 0x77, 0xeb, 0xc4, 0x5b, 0xb0, 0x70, 0xab, 0xd5, 0xea, 0xc3, 0xd3, 0x1c, - 0x9e, 0xf0, 0xe7, 0x19, 0xfc, 0xb8, 0x3c, 0xb4, 0xfe, 0xd3, 0x4f, 0xe9, 0xdf, 0xfc, 0x94, 0x0f, 0x71, 0xcc, 0xc3, - 0x9f, 0x7f, 0x2a, 0xec, 0x7b, 0xfd, 0x70, 0x6b, 0xb8, 0xe9, 0x3a, 0xba, 0xe6, 0x4f, 0x61, 0xf5, 0x08, 0xad, 0x0e, - 0x7f, 0x96, 0x6f, 0xf6, 0xbd, 0xa3, 0xdd, 0x7e, 0x38, 0xbc, 0x74, 0xec, 0xcb, 0x7b, 0xee, 0xa5, 0xeb, 0x5e, 0x6e, - 0xe0, 0x3c, 0x27, 0x30, 0xfa, 0x3d, 0xf8, 0x79, 0x0a, 0x3f, 0x6d, 0xf8, 0x39, 0x81, 0x9f, 0x3f, 0x43, 0x37, 0x11, - 0x7f, 0xbb, 0xa4, 0x58, 0xc8, 0x25, 0x1e, 0x58, 0x44, 0xb0, 0x0a, 0xee, 0xc6, 0x56, 0xec, 0x6d, 0x10, 0xd1, 0x60, - 0x1f, 0xfa, 0xbe, 0x8f, 0x61, 0x52, 0x67, 0xf9, 0x71, 0x03, 0x16, 0x1d, 0x39, 0x67, 0x23, 0x60, 0x9e, 0x88, 0x1c, - 0x14, 0x01, 0x17, 0x67, 0xab, 0x05, 0x1e, 0xae, 0x7a, 0xe3, 0x70, 0x83, 0x39, 0x60, 0x14, 0xbc, 0x66, 0xf8, 0xd0, - 0x75, 0xbd, 0x67, 0xf2, 0xcc, 0x10, 0xf7, 0xb9, 0x60, 0xad, 0x34, 0x13, 0x26, 0x8d, 0xed, 0x7a, 0xf3, 0x35, 0x95, - 0xb0, 0xad, 0xd3, 0x13, 0xa8, 0x9b, 0x89, 0x83, 0xb6, 0xef, 0x58, 0xf4, 0x09, 0xb7, 0xe4, 0x2b, 0xe3, 0x10, 0x78, - 0xc5, 0x92, 0x6f, 0x1a, 0x8d, 0x86, 0x8d, 0x28, 0xdc, 0xb1, 0xc7, 0x0c, 0x66, 0x58, 0x31, 0x11, 0x39, 0x29, 0x4d, - 0x61, 0xd9, 0xc2, 0xe4, 0x6f, 0xa3, 0x9c, 0x6f, 0x54, 0x86, 0x6d, 0x58, 0xb3, 0x64, 0x9b, 0x96, 0xfe, 0x2d, 0xa6, - 0x40, 0xd3, 0x92, 0xce, 0x3f, 0xcc, 0xf1, 0xc3, 0x94, 0xd0, 0x7a, 0xed, 0x70, 0xf0, 0xd0, 0x0b, 0x90, 0x3b, 0xa2, - 0x9f, 0xf3, 0x16, 0xd5, 0x18, 0xfc, 0x95, 0x61, 0x06, 0x4f, 0xcc, 0x87, 0x21, 0x9a, 0x65, 0xa9, 0x83, 0x5b, 0x29, - 0x8a, 0xfb, 0x17, 0xb8, 0x33, 0xd2, 0xd2, 0xdb, 0x0f, 0xd5, 0x8e, 0x39, 0xc8, 0x19, 0xfb, 0x2e, 0x4a, 0x3e, 0xb1, - 0xdc, 0x39, 0xf7, 0x3a, 0xdd, 0x2f, 0xa9, 0xb3, 0x87, 0xb6, 0xd9, 0xbb, 0xea, 0x18, 0x4d, 0x99, 0x05, 0xea, 0x88, - 0xb0, 0xd5, 0xf1, 0x72, 0x8c, 0x6a, 0x21, 0x09, 0x0a, 0x2f, 0x0b, 0xbb, 0xc4, 0xe1, 0xf6, 0x6e, 0x71, 0x7a, 0xd2, - 0xb7, 0x03, 0xdb, 0x06, 0x8b, 0xff, 0x80, 0xc2, 0x56, 0xc2, 0xb0, 0x00, 0x83, 0x6c, 0x37, 0xee, 0xf1, 0xcd, 0xcd, - 0x2a, 0xe0, 0x84, 0x07, 0xe9, 0xd4, 0x3d, 0xf1, 0x22, 0x6f, 0x1a, 0xc2, 0x80, 0x23, 0x68, 0x86, 0x5d, 0x7a, 0xa3, - 0xdd, 0x58, 0x4e, 0x83, 0xb1, 0x10, 0x3f, 0x89, 0x0a, 0xfe, 0x02, 0xe3, 0x11, 0xe1, 0x08, 0x8d, 0x7d, 0x9f, 0x9d, - 0xb3, 0x91, 0xb2, 0x33, 0x80, 0x50, 0x91, 0xdb, 0x73, 0x47, 0xa1, 0xd1, 0x0c, 0xe6, 0x0e, 0xc3, 0x83, 0x81, 0x0d, - 0x7b, 0x09, 0x76, 0x65, 0x18, 0x1d, 0x76, 0x86, 0x83, 0x34, 0x5c, 0xb0, 0x40, 0xd3, 0x56, 0x16, 0xcd, 0x6b, 0x45, - 0xdd, 0xe1, 0xc0, 0x99, 0x80, 0x91, 0x0e, 0xb6, 0xb8, 0x83, 0x6f, 0x18, 0xa1, 0x28, 0xc2, 0x77, 0xec, 0xe4, 0xd9, - 0xf9, 0xdc, 0xb1, 0x77, 0xb7, 0xec, 0x4d, 0x2c, 0xf5, 0x6c, 0x60, 0x2f, 0x98, 0x3b, 0x3c, 0x73, 0xcd, 0xce, 0xdb, - 0x43, 0x04, 0x15, 0x0b, 0x71, 0xf2, 0xb3, 0x81, 0xdd, 0x17, 0x53, 0xb7, 0x61, 0xd0, 0x54, 0x2e, 0x3f, 0xae, 0xe8, - 0x01, 0xa1, 0xaa, 0xba, 0x2a, 0xe8, 0xa0, 0xac, 0x1b, 0x38, 0x53, 0x13, 0x89, 0x16, 0x4e, 0x26, 0xa9, 0x00, 0x0e, - 0x0f, 0x36, 0x83, 0x49, 0x8d, 0x6e, 0xdb, 0xc3, 0xc1, 0x59, 0x70, 0xcf, 0xbe, 0xa7, 0x5e, 0x4e, 0x59, 0x70, 0xc2, - 0xc4, 0xf4, 0xa7, 0x20, 0xed, 0xf0, 0xe7, 0x09, 0x03, 0x24, 0xcf, 0xa8, 0x68, 0x21, 0x8b, 0xe6, 0x58, 0x74, 0x10, - 0x20, 0xa8, 0x5e, 0xa1, 0xad, 0x3f, 0xb1, 0x26, 0xe3, 0x90, 0x60, 0xbf, 0x7b, 0x17, 0x96, 0x66, 0xb3, 0x33, 0xc4, - 0xf3, 0x86, 0x9c, 0x17, 0xdf, 0xc5, 0x1c, 0x54, 0xc2, 0x56, 0xdf, 0x76, 0x07, 0xb6, 0x85, 0x4b, 0xdb, 0xcb, 0x36, - 0x43, 0x41, 0xe1, 0x78, 0xf3, 0x80, 0x05, 0xd3, 0x7e, 0xd8, 0x1e, 0x38, 0xb9, 0x50, 0x1d, 0x09, 0x9e, 0x5b, 0x0a, - 0x09, 0xde, 0xf6, 0xa6, 0x20, 0xd0, 0x91, 0x73, 0x37, 0xec, 0x4d, 0x55, 0x08, 0x45, 0x1f, 0x37, 0xc7, 0x6e, 0x10, - 0xc3, 0x0f, 0xa7, 0x85, 0x4c, 0x33, 0xd5, 0x7d, 0xb5, 0x66, 0x76, 0x83, 0xb1, 0xb2, 0xc8, 0x93, 0x30, 0xdb, 0x74, - 0x30, 0x42, 0x0b, 0x92, 0x76, 0x77, 0x00, 0x30, 0x6c, 0x3a, 0x8a, 0xd3, 0xb6, 0x14, 0xab, 0x29, 0xfb, 0xfc, 0x50, - 0x2f, 0xc7, 0x94, 0x0d, 0xa6, 0xcc, 0xaf, 0xb4, 0x0f, 0x80, 0x15, 0x24, 0x5e, 0x3e, 0x54, 0x67, 0x5e, 0xcf, 0x6b, - 0xe7, 0x5b, 0x4b, 0x25, 0x8a, 0x98, 0x67, 0x48, 0x28, 0x5e, 0x6a, 0x37, 0x4c, 0x98, 0xdb, 0x73, 0x24, 0x86, 0x66, - 0xf9, 0xb0, 0x0d, 0x4c, 0xaf, 0x02, 0xec, 0xa9, 0xb9, 0x2d, 0x92, 0xb0, 0x6a, 0xee, 0x1d, 0x02, 0x6b, 0x0f, 0x81, - 0x87, 0x68, 0x1b, 0xf5, 0x54, 0x34, 0x9f, 0x25, 0xe1, 0xf3, 0xc6, 0x71, 0x71, 0x84, 0x27, 0x42, 0xfb, 0xfe, 0x68, - 0x91, 0x83, 0x3c, 0xe0, 0xaf, 0xc1, 0x32, 0x08, 0x65, 0x53, 0x74, 0xf4, 0xf0, 0x08, 0xd8, 0x23, 0xc4, 0x1b, 0x61, - 0x73, 0xa3, 0x1a, 0x2d, 0x4a, 0x32, 0x5e, 0xe8, 0x60, 0xb8, 0xc7, 0xa5, 0x6b, 0x8f, 0x82, 0x41, 0x9e, 0x18, 0x3b, - 0x78, 0xe6, 0xef, 0x8f, 0xb0, 0x1a, 0x27, 0x28, 0xdc, 0x92, 0x76, 0x5b, 0x25, 0xfe, 0xf6, 0xfd, 0x14, 0x24, 0x38, - 0xd6, 0x81, 0x9f, 0x75, 0xf7, 0x6e, 0x22, 0x91, 0xda, 0x4d, 0x7b, 0x74, 0x12, 0x81, 0xf1, 0xe0, 0xdc, 0x4f, 0xa1, - 0x1a, 0x49, 0x44, 0x45, 0x39, 0x5a, 0xa0, 0xe6, 0xa9, 0x5a, 0x05, 0xdf, 0xa1, 0x19, 0x81, 0xe7, 0x18, 0xb6, 0x26, - 0x3f, 0x55, 0x37, 0x16, 0xb1, 0x7c, 0xd7, 0xa5, 0xa3, 0x2d, 0x3c, 0x80, 0x14, 0x8c, 0x26, 0x18, 0xc6, 0xa5, 0xa0, - 0x64, 0xc5, 0x7f, 0x1f, 0x8d, 0x58, 0xf9, 0xf4, 0x30, 0xdb, 0xdc, 0x1c, 0x8a, 0x73, 0x0b, 0x62, 0x1c, 0x6e, 0x44, - 0x57, 0xe3, 0x0a, 0x80, 0xfa, 0x74, 0x4e, 0x5c, 0x0f, 0x4c, 0x2b, 0xd6, 0x74, 0x29, 0xf6, 0xc9, 0x61, 0x06, 0xa0, - 0xe0, 0x96, 0x73, 0xe8, 0x0f, 0xfe, 0x3c, 0x04, 0xf7, 0xd8, 0xff, 0x93, 0xbb, 0xa5, 0x04, 0x4d, 0x4f, 0x9e, 0x29, - 0x2e, 0xe9, 0x8c, 0xb5, 0xe3, 0x51, 0x6c, 0x34, 0x28, 0xbc, 0x14, 0x30, 0x00, 0x6d, 0x0e, 0x32, 0xa1, 0xe2, 0x20, - 0xe4, 0xa8, 0xc0, 0xf6, 0x71, 0xf3, 0x73, 0xdc, 0xd9, 0x4f, 0xc1, 0xc2, 0x1b, 0xe8, 0xb7, 0x97, 0xf0, 0xf6, 0x67, - 0xfd, 0xf6, 0x0b, 0x0b, 0x7e, 0x29, 0x65, 0xe8, 0xbe, 0x36, 0xc5, 0x03, 0x35, 0x45, 0x29, 0x96, 0xc8, 0xa0, 0x21, - 0x73, 0xf3, 0x95, 0x98, 0x0d, 0x77, 0x4b, 0x20, 0x86, 0x12, 0x5d, 0xb9, 0xcf, 0xa3, 0x13, 0x24, 0xae, 0x6b, 0x92, - 0xc2, 0xc8, 0x25, 0x30, 0x11, 0xae, 0xf8, 0x96, 0x98, 0xb3, 0xdf, 0x06, 0x1b, 0xbc, 0x96, 0x77, 0x80, 0xf6, 0x1d, - 0x9b, 0xcd, 0xf9, 0xc5, 0x3e, 0x29, 0xfa, 0x40, 0xa6, 0x0d, 0x88, 0xb3, 0xf3, 0x76, 0x2f, 0xde, 0xe5, 0xbd, 0x18, - 0xa4, 0x7a, 0xae, 0x58, 0x0c, 0xf7, 0xaa, 0xf7, 0x16, 0xa3, 0x94, 0x26, 0x33, 0x79, 0x35, 0xf4, 0xba, 0x12, 0xbd, - 0xcd, 0x4d, 0x40, 0xb0, 0x67, 0x74, 0xe5, 0xa2, 0x6b, 0x59, 0x0a, 0x9a, 0x00, 0x44, 0x8f, 0xea, 0x2c, 0x47, 0x1c, - 0x87, 0xd9, 0x6c, 0x50, 0x3c, 0x62, 0xee, 0xda, 0x51, 0x71, 0x4c, 0xec, 0x2e, 0x13, 0x76, 0x00, 0x33, 0xe2, 0xf2, - 0x56, 0x47, 0x44, 0x87, 0x45, 0x7f, 0x1d, 0xdf, 0xfe, 0xe8, 0xb1, 0xcd, 0x8e, 0x0b, 0x1a, 0xa4, 0x36, 0xd6, 0xc3, - 0x6a, 0x2c, 0xa8, 0x0f, 0x3f, 0x6a, 0x2a, 0x95, 0xc5, 0xe6, 0x66, 0x59, 0x3f, 0xaa, 0x55, 0x3b, 0xb8, 0x76, 0x9a, - 0x72, 0xde, 0xcc, 0x06, 0xe1, 0x40, 0xc4, 0x04, 0x0a, 0xb4, 0xb4, 0xb2, 0x62, 0x80, 0x21, 0x65, 0x39, 0xca, 0xa7, - 0x90, 0x79, 0x71, 0x59, 0xea, 0xd4, 0x17, 0x19, 0x8f, 0x0c, 0xf1, 0xd4, 0x93, 0x8c, 0x15, 0x50, 0xb0, 0x5e, 0xea, - 0x25, 0xb4, 0x44, 0x80, 0xf9, 0x33, 0x95, 0x43, 0x23, 0x2c, 0x90, 0x28, 0x34, 0xcc, 0x12, 0x65, 0x7c, 0x16, 0x61, - 0x0c, 0xda, 0xfe, 0x49, 0x2d, 0xf6, 0x55, 0x28, 0xa3, 0xa3, 0x38, 0xcc, 0x87, 0x01, 0xd5, 0x2f, 0xa4, 0x04, 0x9b, - 0x86, 0xef, 0x81, 0x8d, 0x2a, 0xc7, 0x93, 0x04, 0xe1, 0xd3, 0x38, 0x67, 0xe4, 0x29, 0x6c, 0x48, 0x98, 0xa5, 0x69, - 0x1b, 0xa9, 0x76, 0x91, 0x19, 0x84, 0x72, 0x51, 0xf0, 0x1a, 0x67, 0x17, 0x59, 0xb8, 0xd2, 0x1a, 0xcc, 0x8f, 0x37, - 0x26, 0x40, 0xd9, 0xe5, 0x65, 0x26, 0x7c, 0xdc, 0x88, 0xec, 0x0d, 0x5d, 0x31, 0x1d, 0x28, 0xa4, 0x02, 0x27, 0x22, - 0x8b, 0x87, 0xce, 0x50, 0x68, 0x84, 0x03, 0x3a, 0x45, 0xce, 0x5d, 0x63, 0xd3, 0xe7, 0x03, 0xed, 0x1b, 0xa5, 0xa1, - 0x93, 0x80, 0x10, 0x10, 0xb8, 0x1b, 0xd6, 0x54, 0x3a, 0x48, 0x83, 0x84, 0x4a, 0xd1, 0xcf, 0x01, 0xfc, 0xc3, 0x48, - 0x52, 0x00, 0xec, 0x87, 0x6a, 0xa4, 0x88, 0xb2, 0x2c, 0x70, 0x01, 0x68, 0xae, 0x7d, 0x5c, 0x09, 0x5f, 0x18, 0xa8, - 0x30, 0x3d, 0xcd, 0xca, 0x4b, 0xa1, 0x44, 0x1e, 0xaf, 0x49, 0x59, 0x23, 0x99, 0x7c, 0x8a, 0x0e, 0x9f, 0xf2, 0xae, - 0x5f, 0x4b, 0x3c, 0x74, 0xc1, 0x53, 0x58, 0x56, 0xf5, 0xfc, 0x2a, 0xe4, 0xe4, 0x5c, 0x83, 0xae, 0x90, 0x42, 0x7f, - 0xc5, 0x49, 0xde, 0x7b, 0xe5, 0x57, 0xb5, 0xd4, 0x18, 0xca, 0xde, 0xaf, 0x6b, 0x86, 0xe5, 0xe5, 0xbc, 0x0a, 0x53, - 0x10, 0x70, 0x4b, 0x96, 0x04, 0x4b, 0xa9, 0x21, 0xc0, 0xc2, 0xf6, 0x48, 0x2b, 0x05, 0x79, 0xa9, 0xc3, 0x3b, 0x4f, - 0xc1, 0x0a, 0x30, 0x0e, 0xb5, 0x54, 0x32, 0x8d, 0x24, 0xbe, 0x54, 0xa2, 0xc0, 0x94, 0xfb, 0x23, 0xf0, 0x53, 0x9b, - 0x27, 0x5d, 0xe7, 0xae, 0x1f, 0xcf, 0x30, 0xb5, 0x87, 0x40, 0x8f, 0xbd, 0x3b, 0x60, 0x4a, 0xd4, 0x75, 0x58, 0x41, - 0x1c, 0x9a, 0xd5, 0x34, 0x0b, 0x98, 0x31, 0x6d, 0xd0, 0x92, 0x6d, 0xb0, 0xe5, 0x72, 0xb0, 0x8f, 0xc4, 0xf6, 0xac, - 0x56, 0x40, 0xe8, 0x1a, 0x34, 0x30, 0xe4, 0x2e, 0x15, 0x5a, 0x98, 0xf7, 0xba, 0x54, 0x84, 0xfb, 0x73, 0xc0, 0xa5, - 0x15, 0x9c, 0x79, 0x19, 0x0d, 0xbc, 0x1f, 0x1f, 0x27, 0x98, 0xf8, 0x82, 0x58, 0x81, 0x1d, 0x1c, 0x74, 0x9a, 0x4d, - 0x81, 0x53, 0x71, 0x91, 0x32, 0x58, 0x56, 0x94, 0xda, 0xf0, 0x43, 0x8a, 0x6c, 0xdd, 0xe5, 0x81, 0xee, 0x42, 0x2c, - 0x80, 0x9d, 0x7e, 0xc3, 0xc8, 0xb7, 0xac, 0x97, 0x01, 0x83, 0x53, 0xad, 0x71, 0x10, 0xf8, 0xcd, 0xcd, 0x64, 0x58, - 0xa6, 0xc4, 0x76, 0x4d, 0x56, 0x17, 0x90, 0xc3, 0x50, 0x4d, 0xdc, 0x41, 0x58, 0x2a, 0x7b, 0xbc, 0x28, 0x67, 0xb8, - 0x5c, 0xca, 0x42, 0x6e, 0x9e, 0x57, 0xd3, 0x7c, 0x6e, 0xa5, 0xd9, 0x74, 0xbc, 0x15, 0x5f, 0x14, 0xfc, 0x03, 0x27, - 0x96, 0x56, 0x3d, 0xa5, 0x56, 0x78, 0x94, 0xb9, 0x25, 0xeb, 0x94, 0xd4, 0xea, 0xba, 0x81, 0x6a, 0x84, 0xa7, 0x69, - 0xd8, 0x08, 0x84, 0x98, 0xe0, 0xe2, 0xd7, 0x4d, 0x26, 0xa6, 0xbd, 0x25, 0xa4, 0x8e, 0xb0, 0x7b, 0x28, 0x27, 0xb8, - 0xab, 0x79, 0xf6, 0x79, 0x38, 0xbf, 0x9a, 0xb9, 0xf7, 0x0c, 0xe6, 0x7e, 0x1c, 0x72, 0x83, 0xd1, 0x63, 0x99, 0xf0, - 0x23, 0x63, 0x1f, 0xb9, 0xaa, 0x7a, 0x72, 0x12, 0x56, 0x22, 0x4b, 0x3c, 0x19, 0x47, 0x1d, 0xc6, 0xa9, 0x68, 0x4d, - 0x90, 0x5d, 0x5e, 0x16, 0xe6, 0x5e, 0xa0, 0xa0, 0xa9, 0xc7, 0xeb, 0x71, 0xda, 0x8a, 0x9d, 0x8d, 0x48, 0xe4, 0xde, - 0xab, 0x5a, 0x24, 0xb2, 0xe2, 0x73, 0x1c, 0xe9, 0x8a, 0x83, 0xdc, 0x27, 0x27, 0xab, 0x9b, 0x54, 0xe8, 0x16, 0x8d, - 0xb6, 0xb1, 0x47, 0xf5, 0x81, 0xa4, 0x9e, 0x51, 0x81, 0x55, 0x8d, 0x7d, 0xf7, 0x6e, 0x47, 0xa4, 0x5b, 0x2a, 0xc5, - 0x06, 0x4b, 0x0b, 0xa3, 0x19, 0xa3, 0x60, 0x50, 0x52, 0x64, 0xa0, 0x46, 0xf9, 0x15, 0x82, 0x61, 0x8f, 0x1a, 0x80, - 0xe2, 0x5c, 0x5f, 0xfd, 0xb8, 0x94, 0x6c, 0x21, 0x20, 0x71, 0x97, 0x0c, 0xc4, 0x9a, 0x60, 0x66, 0xe4, 0x93, 0xf7, - 0xc0, 0x79, 0x03, 0x86, 0x0e, 0x01, 0xf8, 0x05, 0x62, 0xd3, 0x83, 0x89, 0x6d, 0x13, 0x51, 0xf4, 0xd9, 0xc0, 0x73, - 0x00, 0x76, 0x5e, 0x85, 0x46, 0xdf, 0x55, 0x29, 0x60, 0xc8, 0x06, 0x6e, 0xc0, 0xaa, 0xb0, 0xdc, 0xde, 0x73, 0x70, - 0x1b, 0xe0, 0xf5, 0x99, 0x6c, 0xbe, 0x81, 0x79, 0x82, 0xd5, 0xd9, 0x85, 0x5f, 0x59, 0xd6, 0xe2, 0xdc, 0xe9, 0xa0, - 0x51, 0xaf, 0x28, 0x21, 0x6a, 0xf7, 0xb1, 0xf6, 0x39, 0x46, 0x58, 0xc4, 0xfb, 0x2b, 0x7c, 0xd7, 0xe3, 0x96, 0x7b, - 0x1a, 0x2d, 0xc2, 0x74, 0x95, 0x34, 0x06, 0x25, 0xeb, 0x7e, 0x32, 0xe2, 0x5e, 0xee, 0x8b, 0x58, 0x70, 0x85, 0x23, - 0xab, 0x42, 0x8a, 0x0d, 0x24, 0xe9, 0x69, 0x8f, 0x0e, 0xd8, 0x37, 0x9a, 0xbd, 0x80, 0x32, 0xef, 0x2b, 0x52, 0x49, - 0x48, 0x69, 0x76, 0x43, 0x24, 0x09, 0x6b, 0x45, 0x9e, 0x3a, 0xef, 0x3b, 0xda, 0xe7, 0x56, 0x12, 0xc1, 0x08, 0x4e, - 0xc2, 0x74, 0xac, 0x3c, 0x68, 0x0a, 0x70, 0x15, 0x1d, 0x31, 0x7d, 0x13, 0x90, 0xdf, 0x0c, 0xe4, 0xf6, 0x4a, 0x72, - 0x6d, 0xae, 0x61, 0x78, 0x82, 0x04, 0xab, 0x22, 0x11, 0x78, 0x44, 0x8d, 0x09, 0xc9, 0xeb, 0x3c, 0x0f, 0x30, 0xe1, - 0x6b, 0x7b, 0x13, 0x00, 0xca, 0xc9, 0x55, 0x71, 0x56, 0x02, 0xdd, 0x80, 0xe5, 0xfa, 0x38, 0x35, 0x2a, 0x12, 0x17, - 0x37, 0xa6, 0xab, 0x5b, 0xfa, 0x33, 0xb4, 0x9c, 0xc9, 0x10, 0xd3, 0x41, 0x10, 0x90, 0xa9, 0x8f, 0x99, 0x23, 0x64, - 0xae, 0xb0, 0x3e, 0xe7, 0x4e, 0x6d, 0xea, 0x1e, 0xa3, 0x6e, 0x9e, 0xa4, 0x16, 0xaf, 0xd3, 0xa6, 0x94, 0x88, 0x49, - 0x89, 0x39, 0x13, 0xa9, 0xd8, 0x4c, 0x89, 0x3b, 0xb7, 0xbe, 0xd1, 0x42, 0xda, 0x68, 0x33, 0x91, 0x83, 0xcd, 0x2a, - 0x79, 0x4f, 0x60, 0x3c, 0x17, 0x84, 0x2f, 0x91, 0xb1, 0x96, 0x63, 0xe6, 0x98, 0x08, 0x56, 0x2f, 0xa6, 0x22, 0x7f, - 0xe7, 0xe8, 0x34, 0x7b, 0x83, 0x1e, 0xa4, 0xde, 0x40, 0x62, 0xd6, 0xc4, 0x77, 0x21, 0x0d, 0x75, 0x84, 0x40, 0x65, - 0x54, 0xcb, 0x74, 0x9c, 0x58, 0x85, 0x6f, 0x04, 0x5f, 0xbd, 0xd5, 0xc7, 0xf9, 0xc6, 0x73, 0x63, 0x35, 0x82, 0x18, - 0xbc, 0x85, 0x7c, 0xe8, 0x49, 0x11, 0x0e, 0x84, 0xcb, 0x37, 0x37, 0x7b, 0xf9, 0x2e, 0xaf, 0x42, 0x24, 0x15, 0x8c, - 0x31, 0x66, 0x14, 0xe3, 0x9e, 0xa8, 0xa9, 0xc5, 0x1c, 0x06, 0x96, 0xad, 0xc3, 0x1c, 0x0f, 0x00, 0xa0, 0xa5, 0x29, - 0xbd, 0x6a, 0x2a, 0x54, 0x9e, 0xe7, 0x12, 0x3e, 0xd5, 0x21, 0xaa, 0x6a, 0xfc, 0x76, 0x7d, 0x06, 0x0a, 0xc1, 0x7d, - 0xa7, 0xe3, 0xe1, 0x21, 0x04, 0xac, 0xa2, 0x90, 0x05, 0x7a, 0x83, 0xf6, 0xaa, 0x44, 0x28, 0x66, 0x4e, 0xd6, 0x63, - 0x86, 0x93, 0x0a, 0xb6, 0x50, 0x09, 0x4b, 0xa5, 0x05, 0x7e, 0xb5, 0x11, 0x9a, 0xa7, 0x8c, 0x7b, 0xaf, 0x2a, 0x9c, - 0x41, 0x7f, 0x30, 0x6f, 0x95, 0x51, 0xdf, 0xae, 0x9c, 0xc8, 0x54, 0x60, 0xe2, 0x66, 0x96, 0xda, 0xef, 0x97, 0x75, - 0xda, 0xcf, 0x2b, 0xe4, 0x3e, 0x27, 0xcd, 0xd7, 0xb9, 0x85, 0xe6, 0x93, 0xe1, 0x7e, 0xa5, 0xfc, 0xd0, 0xc2, 0xa8, - 0x29, 0xbf, 0xbc, 0xae, 0xfc, 0x0a, 0x4f, 0x85, 0xb7, 0xfa, 0x5d, 0x14, 0xba, 0xa8, 0xcf, 0xc1, 0x10, 0xd2, 0x8f, - 0xe0, 0x1a, 0x1a, 0x3c, 0x28, 0x92, 0xc5, 0x62, 0xed, 0x82, 0xb8, 0x3e, 0xe6, 0x54, 0x3b, 0x94, 0x31, 0x46, 0x3c, - 0x2d, 0x39, 0x48, 0x32, 0x38, 0x18, 0xbf, 0x81, 0x01, 0x31, 0x29, 0x09, 0xe9, 0x10, 0x3a, 0x6b, 0x33, 0x11, 0x95, - 0xbb, 0x78, 0xb3, 0x71, 0x59, 0x53, 0x28, 0xc2, 0x4e, 0x30, 0x53, 0x29, 0x15, 0x04, 0xd2, 0xe4, 0xbb, 0xd3, 0xa9, - 0x05, 0x43, 0x0b, 0xd7, 0x54, 0x40, 0x5e, 0xdb, 0xf5, 0xa0, 0xc9, 0x7b, 0x8a, 0xa1, 0xaf, 0x53, 0x23, 0x5e, 0x66, - 0xf0, 0x35, 0x6c, 0xfe, 0x9a, 0x28, 0xc9, 0x43, 0x26, 0x62, 0xaf, 0xe0, 0x13, 0x21, 0x9b, 0x82, 0x9d, 0x09, 0xf4, - 0x43, 0xbb, 0xb2, 0x97, 0xee, 0x16, 0x95, 0x4b, 0x8b, 0xc6, 0x56, 0xa2, 0x66, 0xcd, 0x0f, 0xe3, 0xcd, 0x14, 0xf6, - 0xb3, 0x47, 0x09, 0x04, 0xa4, 0xa9, 0x9c, 0xa4, 0x9a, 0xf7, 0x30, 0x1d, 0x02, 0x48, 0xb0, 0xfb, 0x09, 0x2c, 0xf4, - 0x9b, 0x12, 0x13, 0x2c, 0xaa, 0xc6, 0x6e, 0x73, 0xd0, 0x9a, 0x73, 0xd2, 0x7c, 0x73, 0xd4, 0xda, 0x9b, 0xca, 0x7a, - 0xc6, 0xec, 0x00, 0xdb, 0x76, 0x37, 0x8b, 0xc3, 0x74, 0xb3, 0x33, 0x34, 0x04, 0x17, 0x1e, 0xff, 0x27, 0x25, 0xa6, - 0x81, 0xe4, 0x52, 0x37, 0x7e, 0x42, 0x1d, 0x86, 0xff, 0x2d, 0x49, 0x01, 0x0f, 0x6a, 0xab, 0xb1, 0xe2, 0xdc, 0x2b, - 0x8e, 0x92, 0xcb, 0xaa, 0xda, 0xd5, 0x12, 0x34, 0x74, 0x23, 0x19, 0x13, 0xc5, 0x3c, 0x27, 0x00, 0x46, 0xb1, 0xf9, - 0x53, 0xa6, 0x93, 0xbc, 0x7f, 0x59, 0x9b, 0xda, 0xed, 0xfb, 0x7e, 0x94, 0x9f, 0xd0, 0x91, 0x8a, 0xca, 0xe6, 0x24, - 0xe6, 0xdf, 0x16, 0x60, 0x9a, 0x13, 0x1f, 0xea, 0xb9, 0x86, 0xa1, 0x00, 0x5f, 0xd9, 0x50, 0x6a, 0xb6, 0x97, 0xbf, - 0x77, 0xb6, 0xfb, 0x92, 0x28, 0x82, 0x05, 0x1a, 0x74, 0xb9, 0x02, 0x5f, 0xc0, 0x32, 0xb8, 0x25, 0xfd, 0xf4, 0xa6, - 0xbf, 0x0a, 0x3e, 0x63, 0xff, 0x0b, 0x40, 0xab, 0x02, 0x03, 0xca, 0x9d, 0xa6, 0x61, 0x25, 0xc4, 0x25, 0x2a, 0xcc, - 0x2a, 0xce, 0x1f, 0xd7, 0x79, 0xdd, 0xb4, 0x2c, 0x31, 0x28, 0x3f, 0x77, 0x0d, 0x37, 0xbe, 0xd7, 0xc8, 0x1f, 0xdf, - 0x7b, 0x0e, 0xba, 0x9d, 0x48, 0x7b, 0xf7, 0x6e, 0x7e, 0x87, 0x2c, 0x34, 0xbc, 0x17, 0x36, 0x87, 0xb6, 0x48, 0x97, - 0x5c, 0x3d, 0x63, 0x31, 0xde, 0x16, 0xa1, 0x32, 0x7c, 0xc0, 0x82, 0x39, 0x60, 0x08, 0x1e, 0x3b, 0x95, 0xc9, 0x67, - 0xd8, 0x68, 0x8a, 0x5d, 0x73, 0x61, 0xf0, 0x81, 0xaa, 0x2c, 0x24, 0x2f, 0xd6, 0xc9, 0xf6, 0xec, 0x14, 0x9e, 0x5f, - 0xc6, 0x05, 0x50, 0x07, 0xd0, 0xaf, 0xa8, 0x2c, 0x36, 0x90, 0x8b, 0x9b, 0xb2, 0xd6, 0x2b, 0x1a, 0x8f, 0xaf, 0xed, - 0xc2, 0xea, 0x0a, 0x7c, 0x1a, 0xa5, 0xe3, 0x44, 0x4c, 0x62, 0x26, 0x55, 0xae, 0xc9, 0xb5, 0xd1, 0xbd, 0xb4, 0x45, - 0xf3, 0x5c, 0x48, 0xf0, 0x8a, 0xc0, 0x0d, 0xa1, 0xaf, 0xf4, 0xe5, 0x7a, 0x03, 0x05, 0x8f, 0xda, 0x9b, 0x8b, 0x60, - 0x62, 0xe2, 0x31, 0x43, 0x6a, 0xfa, 0x75, 0x38, 0x15, 0xdf, 0xfc, 0xb6, 0xe2, 0xf0, 0xeb, 0x9c, 0xb1, 0x86, 0x02, - 0x20, 0x3e, 0x79, 0x70, 0xb5, 0x9b, 0xf4, 0x4a, 0x69, 0x07, 0xa5, 0x11, 0xe2, 0xdb, 0x0a, 0x5f, 0x77, 0xa9, 0xf8, - 0xca, 0x55, 0xf7, 0xbe, 0x8e, 0x99, 0x71, 0xc1, 0xe8, 0x39, 0x9f, 0x25, 0x8d, 0x6b, 0x37, 0x74, 0x57, 0xe7, 0x47, - 0xef, 0x07, 0x99, 0xb7, 0x70, 0x0c, 0x6c, 0x72, 0xcc, 0x9c, 0xe7, 0xde, 0x6b, 0xe3, 0x44, 0xf9, 0x5b, 0xf3, 0x88, - 0x57, 0x0e, 0xb3, 0xee, 0x24, 0xf9, 0xdb, 0xc1, 0xb7, 0xc1, 0xd5, 0x2d, 0x8d, 0x13, 0xe4, 0xae, 0x3a, 0x41, 0x26, - 0xca, 0xcd, 0xf4, 0x86, 0xdb, 0xbb, 0xad, 0x40, 0x10, 0xa7, 0x62, 0xfa, 0xa8, 0x1c, 0xd7, 0x8f, 0x16, 0xa8, 0x54, - 0x44, 0x7c, 0xaa, 0x72, 0x57, 0xae, 0x4c, 0x0d, 0xf5, 0xb8, 0x4e, 0x66, 0xa1, 0x69, 0xd6, 0xe4, 0x52, 0x36, 0x3d, - 0x46, 0xa6, 0xd9, 0xa9, 0x36, 0xbf, 0x7b, 0xe5, 0x21, 0x1d, 0x43, 0x73, 0xb1, 0x56, 0x0b, 0xee, 0x77, 0x15, 0x85, - 0x77, 0xbd, 0xd8, 0x48, 0x65, 0xa8, 0x59, 0x8f, 0xa2, 0x8f, 0xe3, 0x36, 0x73, 0x79, 0x94, 0xfd, 0x59, 0x03, 0xc0, - 0x74, 0x84, 0x45, 0x77, 0xd3, 0x33, 0xf6, 0x04, 0x7a, 0x7a, 0x22, 0x83, 0x44, 0xaf, 0x74, 0xbe, 0x6a, 0x95, 0x58, - 0xba, 0x86, 0xc0, 0xee, 0x35, 0x19, 0xab, 0x92, 0x76, 0xab, 0xf5, 0xab, 0x79, 0x3e, 0x4f, 0xf9, 0x4a, 0x9e, 0x4f, - 0xcd, 0xa2, 0xbb, 0xd3, 0x76, 0xaf, 0x4f, 0x0d, 0x15, 0x73, 0xad, 0x6f, 0xf2, 0x3b, 0xa6, 0xeb, 0x60, 0xa8, 0x45, - 0x90, 0x59, 0xed, 0xaa, 0x67, 0x65, 0x39, 0xab, 0x67, 0x72, 0xcc, 0x84, 0x6f, 0x2a, 0xdd, 0x21, 0xba, 0x61, 0xaa, - 0x66, 0xfa, 0xb1, 0xb1, 0x2d, 0x64, 0x9b, 0xe7, 0x17, 0xe3, 0x1c, 0x28, 0x2d, 0xf7, 0x97, 0x09, 0xc3, 0x8f, 0x97, - 0x97, 0x3f, 0x0a, 0x39, 0x55, 0x75, 0xf4, 0x96, 0x2f, 0x75, 0xcf, 0x60, 0x56, 0x2a, 0x27, 0xe2, 0x23, 0x5b, 0x3f, - 0x78, 0x73, 0xf7, 0x0a, 0x58, 0x3e, 0x02, 0x76, 0x1f, 0x99, 0xd3, 0x18, 0xaa, 0xda, 0xc0, 0x3f, 0xac, 0x1f, 0x6c, - 0xdd, 0x1e, 0xfe, 0x61, 0xf0, 0x43, 0x70, 0x6d, 0x63, 0x63, 0x1b, 0x6f, 0xd7, 0x12, 0x41, 0x5e, 0xe1, 0x81, 0x3e, - 0x5e, 0x7d, 0x14, 0xb4, 0x5c, 0x27, 0xb6, 0x07, 0x0e, 0x85, 0xad, 0x41, 0xbe, 0x49, 0x99, 0x34, 0x5a, 0x14, 0x3c, - 0x9b, 0xc9, 0x19, 0x0a, 0x79, 0xcd, 0xc7, 0x41, 0xdb, 0x11, 0xfe, 0x06, 0x4e, 0xed, 0x78, 0x79, 0xf9, 0x09, 0xfa, - 0x80, 0xa7, 0x2b, 0xa5, 0xa9, 0x88, 0x53, 0xca, 0x2d, 0xba, 0x5a, 0xe7, 0xc1, 0x48, 0x71, 0x31, 0x45, 0xa5, 0xe3, - 0x2e, 0xaf, 0x9d, 0x8d, 0x9c, 0xfe, 0x12, 0xaf, 0x2e, 0xd2, 0xe5, 0x23, 0x91, 0xad, 0x5a, 0x7a, 0x2f, 0xf4, 0xe9, - 0xb6, 0x3d, 0x63, 0x7c, 0x9a, 0x8d, 0xe9, 0x60, 0xc6, 0xc7, 0x89, 0xf0, 0xfa, 0xc4, 0x58, 0xdf, 0x2d, 0x02, 0xd3, - 0xcd, 0xb1, 0xc9, 0x0f, 0xc7, 0xeb, 0xcd, 0x66, 0x8d, 0x3b, 0x78, 0xe3, 0x3c, 0x71, 0x96, 0x25, 0x46, 0x54, 0x96, - 0x1a, 0x1e, 0xd0, 0x0a, 0x71, 0xf3, 0x9e, 0x09, 0x8c, 0xcb, 0x2e, 0x48, 0x6a, 0xbb, 0x81, 0xc0, 0xc5, 0x9e, 0xc4, - 0x2c, 0x19, 0xdb, 0x1e, 0x94, 0x07, 0xfa, 0x62, 0x34, 0xdd, 0x02, 0xa6, 0xe5, 0xb5, 0xb3, 0xb3, 0xd4, 0xf6, 0xaa, - 0xa9, 0x02, 0x98, 0x25, 0xcb, 0xe3, 0x13, 0x64, 0xdd, 0x6f, 0xa0, 0x8b, 0x18, 0x30, 0x36, 0xae, 0xcc, 0xb9, 0xcb, - 0x75, 0x2b, 0xe2, 0x1b, 0x4d, 0xa4, 0x49, 0x7d, 0x48, 0x7d, 0x87, 0x61, 0xad, 0xae, 0x72, 0x90, 0xc0, 0x3d, 0xf2, - 0x6e, 0x89, 0x4b, 0x4f, 0x9f, 0x59, 0x4c, 0xaa, 0xf4, 0x2d, 0x75, 0x2d, 0xae, 0x19, 0xf6, 0x8a, 0x07, 0x60, 0x7f, - 0x60, 0xdc, 0x22, 0x16, 0xf1, 0x76, 0x5e, 0x4b, 0x61, 0x6d, 0xcc, 0x81, 0xe6, 0x86, 0x1b, 0xbc, 0x60, 0xd5, 0x9a, - 0x81, 0x19, 0x66, 0x9c, 0x91, 0xfc, 0x66, 0xdc, 0xab, 0x9a, 0x38, 0x72, 0x15, 0x40, 0xf4, 0x2d, 0xe9, 0x92, 0x1c, - 0x5e, 0xc9, 0x72, 0xd5, 0x19, 0xf2, 0xaf, 0xb0, 0xce, 0x7a, 0x71, 0x02, 0x66, 0xd2, 0x94, 0x97, 0x98, 0x98, 0x22, - 0x2e, 0x37, 0xcb, 0x98, 0xa7, 0xe9, 0xb3, 0x68, 0x07, 0x27, 0x37, 0x12, 0x38, 0x62, 0xdf, 0x58, 0x86, 0x66, 0xc2, - 0x46, 0x4c, 0xa4, 0x51, 0x29, 0x25, 0x7c, 0x20, 0x97, 0x5a, 0xf2, 0x97, 0xb9, 0xbc, 0xfa, 0x72, 0x9b, 0xe0, 0x80, - 0xbc, 0x06, 0x96, 0x43, 0xe3, 0xb8, 0x65, 0x20, 0x11, 0x8b, 0x01, 0x31, 0x6a, 0x55, 0xae, 0x26, 0xa3, 0x3a, 0x99, - 0xaf, 0x90, 0x0b, 0x15, 0x79, 0x70, 0x4b, 0xa0, 0xe4, 0xcf, 0x31, 0x75, 0x30, 0x2b, 0xb5, 0x9b, 0x16, 0x9b, 0x24, - 0xef, 0x99, 0x01, 0xc9, 0xf5, 0xd7, 0xf0, 0xd0, 0xf8, 0xc5, 0x2b, 0x73, 0x4a, 0xf8, 0xa2, 0x8c, 0xa5, 0xa5, 0x31, - 0x97, 0xfe, 0x83, 0xbc, 0x4f, 0x2b, 0x01, 0xfb, 0x15, 0xc4, 0x94, 0x81, 0x4b, 0x6c, 0x5c, 0x90, 0x94, 0xd7, 0xf2, - 0x94, 0xdd, 0xd7, 0x50, 0xbe, 0x2b, 0x26, 0x5d, 0xa5, 0xb2, 0xae, 0xb0, 0xea, 0x7e, 0x5d, 0xb0, 0xfc, 0x62, 0x9f, - 0x61, 0x6e, 0x32, 0x1a, 0x64, 0x2b, 0x66, 0x36, 0xe5, 0x57, 0x7b, 0xd7, 0x7e, 0xe5, 0xa1, 0xa4, 0x43, 0xb5, 0x4a, - 0x37, 0xaf, 0xdc, 0x70, 0x8c, 0x1b, 0x37, 0x1c, 0x01, 0x6c, 0x0c, 0x3b, 0x55, 0xa4, 0xd6, 0xf9, 0xef, 0xab, 0xe1, - 0x27, 0xda, 0x6b, 0x43, 0xbd, 0xeb, 0x86, 0x6b, 0xd3, 0xd3, 0xaf, 0x41, 0xd5, 0xc8, 0x12, 0xba, 0x0e, 0x55, 0x4c, - 0x46, 0xa2, 0xc4, 0x74, 0x95, 0xf2, 0xa8, 0xaf, 0x11, 0xe7, 0x20, 0x6e, 0x28, 0x7f, 0xf1, 0x2f, 0xe1, 0xc5, 0x51, - 0x80, 0x46, 0xd4, 0x72, 0x92, 0xa5, 0xbc, 0x35, 0x89, 0x66, 0x71, 0x72, 0x11, 0x2c, 0xe2, 0xd6, 0x2c, 0x4b, 0xb3, - 0x62, 0x0e, 0x5c, 0xe9, 0x15, 0x17, 0x60, 0xc3, 0xcf, 0x5a, 0x8b, 0xd8, 0x7b, 0xce, 0x92, 0x53, 0xc6, 0xe3, 0x51, - 0xe4, 0xd9, 0x7b, 0x39, 0x88, 0x07, 0xeb, 0x75, 0x94, 0xe7, 0xd9, 0x99, 0xed, 0xbd, 0xcb, 0x8e, 0x81, 0x69, 0xbd, - 0x37, 0xe7, 0x17, 0x27, 0x2c, 0xf5, 0xde, 0x1f, 0x2f, 0x52, 0xbe, 0xf0, 0x8a, 0x28, 0x2d, 0x5a, 0x05, 0xcb, 0xe3, - 0x09, 0xa8, 0x89, 0x24, 0xcb, 0x5b, 0x98, 0xff, 0x3c, 0x63, 0x41, 0x12, 0x9f, 0x4c, 0xb9, 0x35, 0x8e, 0xf2, 0x4f, - 0xbd, 0x56, 0x6b, 0x9e, 0xc7, 0xb3, 0x28, 0xbf, 0x68, 0x51, 0x8b, 0xe0, 0x8b, 0xf6, 0x76, 0xf4, 0xe5, 0xe4, 0x7e, - 0x8f, 0xe7, 0xd0, 0x37, 0x46, 0x2a, 0x06, 0x20, 0x7c, 0xac, 0xed, 0x9d, 0xf6, 0xac, 0xb8, 0x23, 0x4e, 0x94, 0xa2, - 0x94, 0x97, 0x47, 0xde, 0x19, 0x03, 0xb8, 0xfd, 0x63, 0x9e, 0x7a, 0xe0, 0xcb, 0xf1, 0x2c, 0x5d, 0x8e, 0x16, 0x79, - 0x01, 0x03, 0xcc, 0xb3, 0x38, 0xe5, 0x2c, 0xef, 0x1d, 0x67, 0x39, 0x90, 0xad, 0x95, 0x47, 0xe3, 0x78, 0x51, 0x04, - 0xf7, 0xe7, 0xe7, 0x3d, 0xb4, 0x15, 0x4e, 0xf2, 0x6c, 0x91, 0x8e, 0xe5, 0x5c, 0x71, 0x0a, 0x1b, 0x23, 0xe6, 0x66, - 0x05, 0x7d, 0x09, 0x05, 0xe0, 0x4b, 0x59, 0x94, 0xb7, 0x4e, 0xb0, 0x33, 0x1a, 0xfa, 0xed, 0x31, 0x3b, 0xf1, 0xf2, - 0x93, 0xe3, 0xc8, 0xe9, 0x74, 0x1f, 0x7a, 0xea, 0x9f, 0xbf, 0xe3, 0x82, 0xe1, 0xbe, 0xb6, 0xb8, 0xd3, 0x6e, 0xff, - 0xad, 0xdb, 0x6b, 0xcc, 0x42, 0x00, 0x05, 0x9d, 0xf9, 0xb9, 0x55, 0x64, 0x09, 0xac, 0xcf, 0xba, 0x9e, 0xbd, 0x39, - 0xf8, 0x4d, 0x71, 0x7a, 0x12, 0x74, 0xe7, 0xe7, 0x25, 0x62, 0x17, 0x88, 0x84, 0x4c, 0x89, 0xa4, 0x7c, 0x5b, 0xfe, - 0x5e, 0x88, 0x1f, 0xad, 0x87, 0xb8, 0xab, 0x20, 0xae, 0xa8, 0xde, 0x1a, 0xc3, 0x3e, 0x20, 0xf2, 0x77, 0x0a, 0x01, - 0xc8, 0x14, 0x9c, 0xc0, 0x5c, 0xc1, 0x41, 0x2f, 0xbf, 0x1b, 0x8c, 0xee, 0x7a, 0x30, 0x1e, 0xdd, 0x04, 0x46, 0x9e, - 0x8e, 0x97, 0xf5, 0x75, 0xed, 0x80, 0x73, 0xda, 0x9b, 0x32, 0xe4, 0xa7, 0xa0, 0x8b, 0xcf, 0x67, 0xf1, 0x98, 0x4f, - 0xc5, 0x23, 0xb1, 0xf3, 0x99, 0xa8, 0xdb, 0x69, 0xb7, 0xc5, 0x7b, 0x01, 0x0a, 0x2d, 0xe8, 0xf8, 0xd8, 0x00, 0x98, - 0xe8, 0xab, 0xab, 0x3e, 0x62, 0xf3, 0xdd, 0x8d, 0x5f, 0xaa, 0xf1, 0x2e, 0x54, 0xde, 0xa0, 0x50, 0x11, 0xea, 0x9b, - 0x2d, 0x98, 0xf1, 0x96, 0xf7, 0x3b, 0xfa, 0xa0, 0x6a, 0xf0, 0x1d, 0x23, 0xad, 0x17, 0x70, 0xcf, 0xcc, 0x05, 0xea, - 0xa5, 0x7d, 0x0c, 0x49, 0xb5, 0x5a, 0x2e, 0xe8, 0x0d, 0x86, 0x21, 0x24, 0x3a, 0x10, 0x74, 0xf2, 0x41, 0x41, 0xdf, - 0xd4, 0xc8, 0xdc, 0xa0, 0x70, 0x32, 0x17, 0xb6, 0x7c, 0xa6, 0xe5, 0x3a, 0x28, 0x69, 0xf0, 0xb2, 0xbf, 0x62, 0xb2, - 0x01, 0x48, 0xef, 0x4a, 0xd2, 0x7e, 0xaf, 0x4f, 0x9e, 0x94, 0xc7, 0x97, 0x8d, 0x88, 0x70, 0xe0, 0xea, 0xf3, 0x29, - 0xba, 0xdd, 0xfa, 0x3b, 0x31, 0x46, 0x46, 0xcd, 0x96, 0xed, 0x0e, 0x98, 0x4e, 0xca, 0xc2, 0xe4, 0x33, 0x56, 0xe2, - 0x28, 0x5f, 0xb3, 0xf0, 0x7b, 0x0c, 0xbc, 0xb2, 0x50, 0x78, 0x69, 0xca, 0x47, 0x9b, 0x5d, 0x77, 0xfb, 0x1f, 0x16, - 0x3c, 0xa6, 0x64, 0xe7, 0xc3, 0xe1, 0xbf, 0xc1, 0x67, 0x70, 0x34, 0x06, 0x45, 0xb6, 0xc8, 0x47, 0x14, 0x04, 0x5c, - 0x0d, 0x26, 0xd8, 0xa4, 0xcb, 0x6d, 0x8f, 0x69, 0x15, 0x82, 0x1e, 0x76, 0xed, 0xfb, 0x09, 0x9c, 0xce, 0x57, 0xc4, - 0xf5, 0x05, 0x19, 0x40, 0x51, 0x10, 0xa2, 0x56, 0x1c, 0x53, 0x2a, 0x1d, 0x5d, 0xed, 0xf4, 0x17, 0x69, 0x0c, 0x42, - 0xf4, 0x63, 0x3c, 0xa6, 0x2b, 0x2d, 0xf1, 0x98, 0xce, 0x38, 0x5a, 0x94, 0xd3, 0x84, 0x41, 0x73, 0x28, 0x90, 0xb4, - 0xc5, 0x67, 0x99, 0x23, 0x63, 0xb7, 0x6c, 0x3c, 0xa7, 0x30, 0xb4, 0xf0, 0x38, 0x9b, 0x45, 0x71, 0x1a, 0xe0, 0xa7, - 0x47, 0x3c, 0x3d, 0x62, 0x80, 0x5d, 0x3c, 0xf8, 0xa9, 0xc8, 0xdc, 0x71, 0xfd, 0x5f, 0x40, 0x44, 0x51, 0xff, 0x52, - 0xba, 0x78, 0x1a, 0x2e, 0x75, 0x82, 0x5c, 0x2f, 0x05, 0xb1, 0xc6, 0x95, 0x39, 0xcc, 0x28, 0x84, 0xb2, 0xcb, 0xe9, - 0xc7, 0xa0, 0xd5, 0x09, 0x3a, 0xda, 0x2b, 0xae, 0x5d, 0x74, 0x15, 0x69, 0x32, 0xf2, 0xb2, 0x24, 0xc1, 0xa0, 0x9f, - 0x05, 0x9c, 0xd5, 0xbb, 0x86, 0xd5, 0x93, 0x2c, 0x8f, 0xb1, 0xa1, 0x93, 0xd4, 0xa9, 0x01, 0x41, 0xc7, 0x0c, 0x57, - 0x4c, 0xe5, 0x96, 0x11, 0x31, 0xe1, 0x63, 0x92, 0x0d, 0xf5, 0x6b, 0x4a, 0x78, 0x25, 0xa9, 0x9e, 0x5d, 0xa5, 0xb3, - 0xa4, 0x8f, 0x76, 0x85, 0x30, 0xb1, 0x88, 0xc7, 0x42, 0x1b, 0x76, 0xb7, 0x6d, 0xfd, 0x79, 0x04, 0x54, 0xfa, 0x14, - 0xda, 0x1b, 0x4b, 0x47, 0xe5, 0xec, 0xe7, 0x30, 0xd7, 0x9e, 0x50, 0xa8, 0x74, 0xd9, 0xdf, 0xee, 0x6f, 0x2c, 0x79, - 0xb9, 0xbb, 0x25, 0x7a, 0xf7, 0x8f, 0xca, 0x82, 0x74, 0x9f, 0x19, 0xa3, 0xab, 0xa6, 0x10, 0x75, 0x30, 0x2c, 0x65, - 0x06, 0xe3, 0xb8, 0xb9, 0xb6, 0xe7, 0x44, 0x30, 0x5a, 0xb2, 0x5d, 0x81, 0x99, 0x50, 0x51, 0x0e, 0xdb, 0x5d, 0xe7, - 0xba, 0x14, 0x22, 0xbd, 0xa3, 0xb7, 0x0a, 0xc5, 0x11, 0x42, 0x30, 0xd8, 0x58, 0xc6, 0x65, 0xb8, 0xb1, 0x64, 0xe9, - 0x28, 0x1b, 0xb3, 0xf7, 0xef, 0x5e, 0xe0, 0x75, 0x86, 0x2c, 0x45, 0xb9, 0x97, 0xb9, 0xe5, 0x11, 0x18, 0x42, 0x08, - 0x69, 0xae, 0xbe, 0x26, 0x03, 0xc0, 0x88, 0x98, 0x8e, 0x45, 0xa3, 0x22, 0x28, 0xac, 0xb4, 0xad, 0x81, 0x80, 0x10, - 0x1c, 0x4e, 0x2c, 0x00, 0x53, 0x91, 0x7a, 0x31, 0xc0, 0x4f, 0xb4, 0xee, 0xc3, 0x40, 0xbb, 0x5b, 0xa2, 0x11, 0xe0, - 0x9a, 0x23, 0x1a, 0x15, 0xaa, 0x98, 0xfd, 0x63, 0xa2, 0x3b, 0x8e, 0x4f, 0x35, 0x39, 0x29, 0x15, 0xba, 0xbf, 0x9b, - 0x44, 0xc7, 0x2c, 0x81, 0x21, 0x8b, 0xcb, 0xcb, 0x36, 0x8c, 0x24, 0x5e, 0xad, 0xdd, 0x38, 0x9d, 0x2f, 0xe4, 0x57, - 0xbd, 0x60, 0xe2, 0x0e, 0x1e, 0xb4, 0xe2, 0xa5, 0x83, 0x81, 0x3a, 0x39, 0x0c, 0xe4, 0x00, 0x00, 0x22, 0x1d, 0x5a, - 0x20, 0x74, 0x15, 0xab, 0x40, 0x69, 0x3c, 0x5e, 0x2d, 0x83, 0xdd, 0x39, 0xc7, 0xd2, 0x14, 0x9e, 0x67, 0x71, 0x8a, - 0x8f, 0x05, 0x3e, 0x46, 0xe7, 0xf8, 0x98, 0xc1, 0xa3, 0xc6, 0x3d, 0x2f, 0xed, 0x7f, 0xd7, 0x55, 0xc9, 0xe4, 0x0a, - 0x58, 0x9a, 0x00, 0xd9, 0xe5, 0x25, 0xa8, 0x17, 0x4d, 0x82, 0xdd, 0x2d, 0x20, 0x16, 0x72, 0x8f, 0xf8, 0xc6, 0x0b, - 0x33, 0xc9, 0xc8, 0x8a, 0x79, 0x4b, 0x94, 0x5b, 0xa4, 0xc4, 0x43, 0xf0, 0xf1, 0x72, 0xa7, 0x61, 0xab, 0x78, 0x32, - 0x9b, 0xe5, 0x09, 0xbe, 0xb8, 0xb6, 0x25, 0x3e, 0xc2, 0x21, 0x88, 0x42, 0x8f, 0x88, 0xa1, 0x2e, 0xe3, 0xf2, 0xf3, - 0x3a, 0x71, 0x68, 0xe3, 0x2c, 0x60, 0x2e, 0xa2, 0xd2, 0xe1, 0x51, 0x9c, 0x88, 0xc6, 0x6b, 0xf0, 0x69, 0xa4, 0x25, - 0x12, 0x3a, 0xbb, 0x5b, 0x15, 0x6c, 0x00, 0xfc, 0x48, 0x5c, 0xea, 0x76, 0x44, 0xca, 0xa5, 0x2d, 0xca, 0xe9, 0xa8, - 0x5e, 0x6e, 0x73, 0x19, 0x48, 0x16, 0xa1, 0x79, 0x8d, 0x2a, 0xa5, 0x48, 0xda, 0x93, 0x28, 0x5d, 0xd7, 0x14, 0xa0, - 0x9f, 0x33, 0x36, 0xf6, 0x6c, 0x0b, 0xe4, 0xab, 0x78, 0xfe, 0x98, 0xb0, 0x53, 0x26, 0x3f, 0xcc, 0xa2, 0x07, 0xd1, - 0x95, 0x23, 0xb0, 0x00, 0xb8, 0xbc, 0x33, 0x2a, 0xd9, 0x53, 0xe1, 0x28, 0x29, 0x51, 0x47, 0xc4, 0xb3, 0x8d, 0x41, - 0x9b, 0x73, 0xb4, 0xeb, 0xc3, 0x7a, 0xa0, 0x93, 0x6c, 0x5b, 0xc0, 0x4b, 0x66, 0xe3, 0xcd, 0xc8, 0xc1, 0x00, 0xc7, - 0x39, 0x36, 0x4d, 0x59, 0x51, 0xac, 0x03, 0x0b, 0x9c, 0x60, 0xcf, 0xae, 0x9a, 0xd8, 0xb5, 0x0e, 0x00, 0x40, 0x77, - 0x67, 0x47, 0x4c, 0x0b, 0x15, 0x6c, 0x32, 0x81, 0x8d, 0x87, 0x1a, 0x23, 0xe1, 0x18, 0xf6, 0x0f, 0xfb, 0xf6, 0x6b, - 0xd8, 0xe3, 0x36, 0xf8, 0x57, 0xae, 0x3e, 0xc0, 0xa5, 0xe9, 0x95, 0x10, 0x32, 0xe6, 0x10, 0x9d, 0x8d, 0x61, 0xf4, - 0x93, 0x81, 0x54, 0x36, 0xfa, 0xb4, 0x06, 0x27, 0xe0, 0xc2, 0x0d, 0x11, 0x40, 0x6e, 0xc8, 0x56, 0xfb, 0x5f, 0xff, - 0xe9, 0x7f, 0xfd, 0x37, 0x18, 0x9b, 0xfa, 0xb9, 0xa5, 0x75, 0x75, 0xab, 0xff, 0x09, 0xad, 0x16, 0xe9, 0x0d, 0xed, - 0xfe, 0xfa, 0x0f, 0xff, 0x1d, 0x9a, 0xd1, 0x45, 0x23, 0x90, 0x59, 0x04, 0xd1, 0x08, 0x6d, 0xbb, 0xcf, 0x02, 0xa9, - 0x36, 0xc8, 0x95, 0x33, 0xfd, 0x23, 0x82, 0x5d, 0xf0, 0x6c, 0x7e, 0x2d, 0x38, 0x08, 0xf5, 0x28, 0xc9, 0x0a, 0xa6, - 0xe1, 0x11, 0x72, 0xfe, 0xf3, 0x00, 0xa2, 0xb9, 0xe6, 0xb0, 0x9b, 0x0a, 0x4b, 0x8f, 0x23, 0x56, 0x68, 0xdd, 0x38, - 0x8d, 0x05, 0x2c, 0x18, 0x27, 0x74, 0x28, 0x5c, 0x02, 0x4b, 0x26, 0x9e, 0xe0, 0x81, 0x04, 0x8f, 0x5b, 0xff, 0x78, - 0xd9, 0xfa, 0xc1, 0x34, 0xc3, 0x89, 0xb1, 0x44, 0x84, 0x48, 0x8d, 0x00, 0x3f, 0x41, 0x38, 0x7e, 0xd4, 0xcf, 0xd1, - 0xb9, 0x7e, 0x46, 0x01, 0x2a, 0x26, 0x00, 0x3d, 0x38, 0x43, 0x13, 0xc7, 0x9c, 0x41, 0x64, 0x37, 0x54, 0xee, 0xb1, - 0x91, 0x24, 0x23, 0x84, 0xe4, 0x47, 0xcc, 0x25, 0xc5, 0x9b, 0x43, 0x8b, 0x9c, 0x7d, 0x4c, 0xb2, 0x33, 0x8c, 0xb9, - 0x21, 0x91, 0xae, 0xaa, 0x2f, 0xff, 0xe5, 0x9f, 0x7d, 0xff, 0x5f, 0xfe, 0xf9, 0x8a, 0x06, 0x53, 0xd8, 0x13, 0x60, - 0x24, 0xf3, 0x50, 0xd3, 0xb9, 0x81, 0xd6, 0xfa, 0x41, 0x11, 0xcf, 0xf5, 0x35, 0x12, 0x71, 0x2c, 0x95, 0x78, 0xcb, - 0x47, 0x42, 0x5b, 0x33, 0xc5, 0xcd, 0xb3, 0x20, 0x64, 0x57, 0x4c, 0x83, 0x55, 0x37, 0xcc, 0x73, 0xe4, 0x06, 0xd7, - 0xd0, 0xe5, 0x33, 0x31, 0x5e, 0x0f, 0xc6, 0x8d, 0x10, 0x78, 0xa0, 0x65, 0x84, 0x1e, 0x7a, 0x22, 0xb4, 0x48, 0x20, - 0x96, 0x41, 0xea, 0x94, 0x1a, 0x40, 0x9e, 0x75, 0x40, 0x13, 0x50, 0x93, 0xb8, 0xd2, 0xe1, 0x64, 0x06, 0x1d, 0xe7, - 0xfd, 0x57, 0x78, 0x59, 0xd0, 0xc2, 0xde, 0xa8, 0xc1, 0x0b, 0x32, 0x38, 0x38, 0x19, 0x1c, 0x52, 0xd3, 0x99, 0xba, - 0x1e, 0x1d, 0xa7, 0x6c, 0xbd, 0x4e, 0xff, 0x88, 0xdd, 0x6b, 0x5a, 0x99, 0x4b, 0xad, 0x1c, 0x4b, 0x2b, 0x5a, 0x6a, - 0x65, 0xfc, 0x24, 0x4c, 0x43, 0x2b, 0xc7, 0x57, 0x6a, 0x65, 0xa4, 0xdc, 0x00, 0x47, 0x0e, 0xed, 0x4d, 0x8c, 0x0e, - 0x19, 0x36, 0x00, 0x47, 0xfb, 0x67, 0x34, 0x65, 0xa3, 0x4f, 0xd2, 0xfc, 0x21, 0x04, 0x30, 0x3c, 0xa2, 0x8d, 0x3c, - 0x81, 0x01, 0xa8, 0xf2, 0xa3, 0x52, 0x6f, 0x7a, 0x7c, 0x34, 0x26, 0xe0, 0xee, 0x72, 0xc2, 0x50, 0xf4, 0xc3, 0x9a, - 0x7d, 0xcd, 0xca, 0x2d, 0x1c, 0x47, 0x6c, 0x18, 0xf1, 0x0c, 0x98, 0x6d, 0xe1, 0x60, 0x47, 0xde, 0x52, 0x04, 0xdb, - 0x02, 0xfb, 0xed, 0x9b, 0xfd, 0x03, 0xdb, 0x3b, 0xce, 0xc6, 0x17, 0x81, 0x0d, 0xde, 0x0c, 0x58, 0x39, 0xae, 0xcf, - 0xa7, 0x2c, 0x75, 0x94, 0x3f, 0x91, 0x25, 0xe0, 0xaa, 0x65, 0x27, 0xe2, 0xdb, 0x10, 0xcd, 0x83, 0x02, 0x20, 0x2c, - 0x7d, 0x3c, 0xb2, 0xbf, 0xcb, 0xc5, 0x77, 0x55, 0x79, 0x8e, 0x8f, 0x7d, 0x4c, 0x95, 0xd8, 0xdd, 0x82, 0x07, 0x7c, - 0xd9, 0x47, 0xbd, 0xa7, 0xdf, 0x04, 0xb0, 0x85, 0x78, 0xdf, 0xc2, 0xf6, 0x5b, 0xaa, 0x2f, 0x42, 0xd1, 0x97, 0xdc, - 0xa6, 0x4d, 0xfe, 0xca, 0x66, 0xa4, 0xb1, 0xc7, 0x68, 0x12, 0x92, 0xc9, 0x0f, 0x24, 0xe1, 0x63, 0x5d, 0x22, 0xcc, - 0x0c, 0xa3, 0x88, 0x46, 0xa9, 0x8c, 0x02, 0x59, 0x85, 0x13, 0x92, 0x19, 0x29, 0x26, 0x83, 0x9f, 0x04, 0xfe, 0x91, - 0xf9, 0x1d, 0x34, 0xf1, 0xc9, 0x22, 0x8d, 0xe4, 0xe1, 0x5f, 0xbc, 0x33, 0xe6, 0x5d, 0x1c, 0x51, 0x4b, 0xe5, 0x74, - 0x63, 0x34, 0x08, 0x83, 0x13, 0x6d, 0x15, 0x5d, 0x01, 0x3b, 0x28, 0x89, 0xe6, 0x05, 0x0b, 0xd4, 0x83, 0xf4, 0xbf, - 0xd1, 0x8d, 0x5f, 0x0d, 0x78, 0x98, 0xf6, 0x52, 0xc9, 0xa7, 0x4b, 0xd3, 0x41, 0x7f, 0x00, 0x0e, 0x3a, 0x5e, 0x2e, - 0x68, 0x45, 0xa0, 0xe5, 0xd3, 0x20, 0x61, 0x13, 0x5e, 0x72, 0xbc, 0xbd, 0xbe, 0x54, 0x11, 0x11, 0xbf, 0xbb, 0x03, - 0x4e, 0xbb, 0xe5, 0xe3, 0xff, 0x37, 0x8d, 0x3d, 0x0e, 0x52, 0x70, 0xb2, 0xe9, 0x3a, 0x0b, 0x5e, 0x15, 0x04, 0x88, - 0xcc, 0xf7, 0xa5, 0x31, 0xd1, 0x88, 0x61, 0xb4, 0xa8, 0xe4, 0x39, 0xc8, 0x6d, 0x8f, 0xe7, 0x66, 0x3b, 0x90, 0xb7, - 0x2b, 0x21, 0xa3, 0xd5, 0xa0, 0xc5, 0xb6, 0x2b, 0xfd, 0x8f, 0xd5, 0xc6, 0x2a, 0xf2, 0x53, 0x7f, 0x5b, 0xa1, 0x90, - 0x11, 0xa3, 0x2a, 0x85, 0xaa, 0x59, 0x8a, 0x1e, 0x26, 0x4e, 0xab, 0xd1, 0xab, 0x1b, 0x2d, 0xd2, 0x92, 0xb6, 0xfd, - 0x21, 0x6d, 0x7b, 0x12, 0x63, 0xc3, 0xa5, 0x98, 0x7b, 0x14, 0x25, 0x23, 0x07, 0x01, 0xb0, 0x5a, 0xd6, 0x23, 0xa0, - 0xa6, 0xab, 0x22, 0x28, 0xfe, 0x43, 0x24, 0x6e, 0x29, 0x84, 0xde, 0x1a, 0x2a, 0x1d, 0x0d, 0xcb, 0xb2, 0x77, 0xc1, - 0x9c, 0xc3, 0xdf, 0xe4, 0x65, 0x08, 0x71, 0x07, 0x56, 0x7f, 0x47, 0xb0, 0x5d, 0xba, 0x43, 0x8f, 0x31, 0xe3, 0xeb, - 0x6c, 0xb6, 0xe2, 0x68, 0xdb, 0xeb, 0x52, 0x3c, 0x01, 0x7b, 0xbf, 0x72, 0x6c, 0x34, 0x62, 0xa9, 0xea, 0xa2, 0x45, - 0x1c, 0x66, 0x53, 0x47, 0x11, 0xcd, 0xff, 0xe6, 0xaa, 0xa0, 0xcc, 0xb7, 0x37, 0x07, 0x65, 0xf8, 0x2d, 0x83, 0x32, - 0xdf, 0xfe, 0xc1, 0x41, 0x99, 0x6f, 0xcc, 0xa0, 0x0c, 0xca, 0xca, 0x17, 0x9f, 0x13, 0x39, 0xc9, 0xb3, 0xb3, 0x22, - 0xec, 0xc8, 0x24, 0x00, 0x10, 0x3b, 0xff, 0x31, 0x21, 0x14, 0x98, 0xa8, 0x11, 0x40, 0xa1, 0x88, 0x89, 0xc8, 0x5b, - 0x04, 0x09, 0x2f, 0xe3, 0x15, 0x6d, 0x9d, 0x20, 0xd8, 0xba, 0xaf, 0x6e, 0x44, 0x81, 0x77, 0xe8, 0xea, 0xb0, 0x51, - 0x57, 0x45, 0x34, 0x02, 0xfa, 0xa4, 0xa9, 0xee, 0xd8, 0xdd, 0x54, 0x99, 0x69, 0xe6, 0x08, 0x3d, 0x75, 0xe0, 0x20, - 0x38, 0x68, 0x69, 0xff, 0xe7, 0xc3, 0x4e, 0x6f, 0xbb, 0x33, 0x83, 0xde, 0xa0, 0x4b, 0xe1, 0xad, 0xdd, 0xdb, 0xde, - 0xc6, 0xb7, 0x33, 0xf5, 0xd6, 0xc5, 0xb7, 0x58, 0xbd, 0xed, 0xe0, 0xdb, 0x48, 0xbd, 0x3d, 0xc0, 0xb7, 0xb1, 0x7a, - 0x7b, 0x88, 0x6f, 0xa7, 0x76, 0x79, 0xc8, 0x35, 0x70, 0x0f, 0x81, 0xb1, 0xc8, 0xb1, 0x08, 0x54, 0x19, 0xec, 0x5b, - 0xbc, 0x49, 0x18, 0x9d, 0x04, 0xb1, 0x27, 0x1c, 0xb0, 0x20, 0xf7, 0xce, 0x40, 0xf8, 0x07, 0x94, 0x38, 0xf7, 0x14, - 0x3f, 0x29, 0x01, 0xfe, 0xca, 0x41, 0x3c, 0x63, 0xea, 0xdb, 0xba, 0x0a, 0x6b, 0xb0, 0x25, 0x0f, 0xdb, 0xc3, 0xb2, - 0xa7, 0xd7, 0x49, 0x04, 0x6c, 0x54, 0x62, 0x02, 0xad, 0x5c, 0x55, 0x27, 0xa6, 0x6b, 0xe9, 0x15, 0xbe, 0x42, 0x95, - 0x18, 0x2e, 0xfb, 0x04, 0x6c, 0xa4, 0xd6, 0x39, 0x38, 0x79, 0x6b, 0xd5, 0x0b, 0x42, 0xa4, 0x15, 0x0a, 0xe1, 0xa4, - 0xdf, 0x0e, 0xa2, 0x13, 0xfd, 0xfc, 0x0a, 0x8c, 0xde, 0xe8, 0x84, 0xdd, 0xa4, 0x6a, 0x08, 0x44, 0x53, 0xcd, 0x28, - 0x20, 0xc8, 0x2a, 0x82, 0xa5, 0x41, 0x67, 0x53, 0xaa, 0x19, 0xa4, 0x4e, 0x5d, 0xf1, 0xd0, 0xf4, 0xf5, 0x22, 0xa0, - 0x68, 0x55, 0xb0, 0x0b, 0xb6, 0x37, 0x95, 0x0a, 0x0a, 0x43, 0x05, 0x16, 0x5c, 0xab, 0x8d, 0xb4, 0x3f, 0x7e, 0xa5, - 0x4e, 0xb2, 0x94, 0x3a, 0x32, 0x0f, 0x2a, 0xf4, 0x29, 0xc5, 0xaa, 0x84, 0xfc, 0xa2, 0x33, 0xc2, 0x3f, 0x52, 0xfe, - 0x7e, 0x31, 0x99, 0x4c, 0xae, 0x55, 0x4f, 0x5f, 0x8c, 0x27, 0xac, 0xcb, 0x76, 0x7a, 0x18, 0xc4, 0x6e, 0x49, 0x89, - 0xd8, 0x29, 0x89, 0x76, 0xcb, 0xdb, 0x35, 0x46, 0xe1, 0x09, 0x1a, 0xeb, 0xf6, 0x7a, 0xac, 0x04, 0xaa, 0x2c, 0x41, - 0x7e, 0x9f, 0xc4, 0x69, 0xd0, 0x2e, 0xfd, 0x53, 0x29, 0xf8, 0xbf, 0x78, 0xf4, 0xe8, 0x51, 0xe9, 0x8f, 0xd5, 0x5b, - 0x7b, 0x3c, 0x2e, 0xfd, 0xd1, 0x52, 0xa3, 0xd1, 0x6e, 0x4f, 0x26, 0xa5, 0x1f, 0xab, 0x82, 0xed, 0xee, 0x68, 0xbc, - 0xdd, 0x2d, 0xfd, 0x33, 0xa3, 0x45, 0xe9, 0x33, 0xf9, 0x96, 0xb3, 0x71, 0x2d, 0x12, 0xfe, 0xb0, 0x0d, 0x95, 0x82, - 0xd1, 0x96, 0xe8, 0xe8, 0x89, 0xc7, 0x20, 0x5a, 0xf0, 0x0c, 0x6c, 0x2c, 0xe0, 0x6d, 0x10, 0xd0, 0x13, 0x29, 0xde, - 0xc5, 0xa7, 0x6b, 0x51, 0xa8, 0xbf, 0x30, 0x65, 0x3a, 0x32, 0x33, 0xc9, 0x73, 0x4e, 0xaa, 0xa0, 0x59, 0x8d, 0x9c, - 0x45, 0xd5, 0x2f, 0x42, 0x5e, 0x49, 0x7b, 0x94, 0x36, 0xd8, 0x52, 0xc8, 0xf8, 0x1f, 0xaf, 0x92, 0xf1, 0x3f, 0xdc, - 0x2c, 0xe3, 0x8f, 0x6f, 0x27, 0xe2, 0x7f, 0xf8, 0x83, 0x45, 0xfc, 0x8f, 0xa6, 0x88, 0x17, 0x42, 0x6c, 0x0f, 0xac, - 0x58, 0x32, 0x5f, 0x8f, 0xb3, 0xf3, 0x16, 0x6e, 0x89, 0xdc, 0x26, 0xe9, 0xb9, 0x71, 0x2b, 0xe1, 0xbf, 0x26, 0xb5, - 0x49, 0x0d, 0x66, 0x7c, 0x07, 0x97, 0x67, 0x27, 0x27, 0x09, 0x53, 0x32, 0xde, 0xa8, 0x20, 0xcb, 0xf8, 0x4d, 0x1a, - 0xda, 0x6f, 0xc0, 0x49, 0x35, 0x4a, 0x26, 0x13, 0x28, 0x9a, 0x4c, 0x6c, 0x95, 0xfa, 0x0b, 0xf2, 0x8c, 0x5a, 0xbd, - 0xae, 0x95, 0x50, 0xab, 0xaf, 0xbe, 0x32, 0xcb, 0xcc, 0x02, 0x19, 0xf5, 0x32, 0xed, 0x09, 0x59, 0x33, 0x8e, 0x0b, - 0xdc, 0x83, 0xd5, 0x77, 0x7b, 0xd1, 0x64, 0x99, 0x81, 0x52, 0x89, 0x47, 0xf8, 0x41, 0x98, 0xe6, 0x37, 0x52, 0x44, - 0x9a, 0xf6, 0x2a, 0x72, 0xd5, 0x51, 0xae, 0xf1, 0x39, 0xbe, 0xea, 0xf8, 0x16, 0x16, 0x5f, 0xa6, 0x6a, 0x3c, 0xbe, - 0x78, 0x31, 0x76, 0xf6, 0xc0, 0x94, 0x8d, 0x8b, 0x37, 0x69, 0x23, 0x05, 0x4e, 0x80, 0x1d, 0x86, 0x26, 0xa6, 0xa5, - 0x20, 0x58, 0x75, 0x17, 0xa0, 0xaa, 0xec, 0x19, 0x9d, 0x64, 0xa6, 0x14, 0x0e, 0x39, 0xa8, 0x91, 0x25, 0x30, 0x07, - 0x93, 0xba, 0x90, 0xbe, 0xcb, 0x2e, 0xf2, 0x47, 0x4e, 0xe5, 0x07, 0xbc, 0xe9, 0xf4, 0x61, 0x29, 0xf5, 0x87, 0xcc, - 0x2c, 0xa8, 0x7a, 0x62, 0xc0, 0x5f, 0xcc, 0x30, 0x2e, 0x55, 0x90, 0x1f, 0x08, 0x37, 0xc7, 0xaf, 0x0d, 0x89, 0x21, - 0x54, 0x2c, 0xbd, 0xa2, 0xde, 0xe5, 0xa5, 0xf9, 0xd1, 0xcb, 0xda, 0x07, 0x12, 0x1b, 0x3c, 0xc0, 0xf0, 0x6b, 0xb5, - 0xa8, 0x0d, 0xb2, 0x05, 0x77, 0x1c, 0x6a, 0xe5, 0xb8, 0xa5, 0xb7, 0xd3, 0x6e, 0x83, 0x8a, 0xf1, 0xc5, 0x27, 0x8d, - 0x1c, 0xdd, 0x59, 0xe2, 0x7b, 0x55, 0xe8, 0x7e, 0xe5, 0x13, 0x63, 0x9a, 0xc4, 0xf8, 0x0d, 0x14, 0x81, 0xa8, 0x71, - 0x0d, 0x46, 0x2d, 0x62, 0xf3, 0xdd, 0x57, 0x6e, 0x9c, 0x41, 0x58, 0x77, 0x1d, 0x07, 0xcb, 0x34, 0xd1, 0x7a, 0x21, - 0xb6, 0x15, 0x56, 0xcd, 0x2a, 0x38, 0x37, 0xe8, 0xcc, 0xe2, 0xcc, 0x88, 0x71, 0xd7, 0xb6, 0x41, 0xa9, 0x82, 0xdc, - 0x22, 0x52, 0xbd, 0x87, 0xf1, 0x58, 0xe1, 0x03, 0x2b, 0xa0, 0xeb, 0xde, 0xa7, 0x01, 0x39, 0xfa, 0xa5, 0x9a, 0xd1, - 0x55, 0x95, 0x2a, 0x28, 0xcd, 0x53, 0x0a, 0x03, 0x19, 0x0a, 0x36, 0xc3, 0x1a, 0xa7, 0x42, 0x6f, 0xc1, 0x34, 0x24, - 0x80, 0xb5, 0x53, 0x86, 0x9e, 0x89, 0xad, 0xc0, 0x16, 0xd2, 0x02, 0x94, 0x1e, 0x76, 0xe8, 0x5b, 0x35, 0xd0, 0xd3, - 0xd5, 0x18, 0xf5, 0x75, 0x7e, 0xda, 0xc5, 0x91, 0x5f, 0x9c, 0x79, 0xf0, 0xcf, 0xfa, 0xd3, 0x12, 0xa4, 0xfc, 0xf1, - 0xa7, 0x98, 0x83, 0x51, 0x3d, 0x6f, 0x61, 0x24, 0x84, 0x42, 0xa6, 0x52, 0x1d, 0xd2, 0xa9, 0xaa, 0xb8, 0xfd, 0xd4, - 0x5b, 0x14, 0xe8, 0xce, 0x91, 0xdf, 0x12, 0xa4, 0x59, 0xca, 0x7a, 0xf5, 0xd3, 0x73, 0xd3, 0x75, 0x50, 0xc4, 0x1a, - 0x2e, 0x33, 0x74, 0xff, 0xf8, 0x05, 0xb8, 0x7f, 0x42, 0x8d, 0xb6, 0x95, 0xdf, 0xd0, 0x5e, 0xdb, 0x3e, 0x90, 0xb4, - 0xdd, 0x24, 0x6b, 0x21, 0x5f, 0xf5, 0x8f, 0xae, 0xf2, 0x6f, 0x6e, 0x3a, 0x4b, 0xc6, 0xf8, 0xac, 0xfa, 0x67, 0x1c, - 0xc2, 0x37, 0x8b, 0xe9, 0x2c, 0xf9, 0x36, 0x90, 0x05, 0xd1, 0x04, 0x3f, 0x16, 0x78, 0x9b, 0x96, 0xc7, 0x94, 0xc8, - 0xb9, 0x44, 0xb5, 0x1e, 0x74, 0x1e, 0x81, 0xc3, 0x76, 0xeb, 0xe1, 0xaf, 0x47, 0xbf, 0x94, 0x34, 0x52, 0xf7, 0x71, - 0x6d, 0xbb, 0x87, 0xf2, 0x22, 0x89, 0x2e, 0xc0, 0x6f, 0x24, 0x1b, 0xe3, 0x18, 0x03, 0xb9, 0xbd, 0x79, 0x26, 0x93, - 0x22, 0x72, 0x96, 0xd0, 0x6f, 0xe4, 0x90, 0x4b, 0xb1, 0xfd, 0x60, 0x7e, 0xae, 0x56, 0xa3, 0xd3, 0x48, 0x76, 0xf8, - 0x43, 0x73, 0x1a, 0xae, 0x4e, 0xa2, 0xa8, 0x9f, 0xcb, 0xef, 0x00, 0x0c, 0xc2, 0xb0, 0x69, 0xe5, 0x02, 0xaa, 0x36, - 0x94, 0x18, 0x59, 0x1d, 0xd5, 0x40, 0x96, 0xbf, 0x0d, 0xaa, 0x32, 0x2a, 0x58, 0x0f, 0xbf, 0xda, 0x18, 0x83, 0x77, - 0x2a, 0x8d, 0xa7, 0x59, 0x3c, 0x1e, 0x27, 0xac, 0xa7, 0xec, 0x23, 0xab, 0xf3, 0x00, 0x93, 0x22, 0xcc, 0x25, 0xab, - 0xaf, 0x8a, 0x41, 0x3c, 0x4d, 0xa7, 0xe8, 0x18, 0xec, 0x35, 0xfc, 0xf4, 0xe2, 0x5a, 0x72, 0xca, 0x6c, 0x81, 0x76, - 0x45, 0x3c, 0x7a, 0xae, 0xe3, 0xb2, 0x03, 0xc6, 0x22, 0x2d, 0x78, 0xbb, 0xc7, 0xb3, 0x79, 0xd0, 0xda, 0xae, 0x23, - 0x82, 0x55, 0x1a, 0x05, 0x6f, 0x0d, 0x5a, 0x1e, 0x5a, 0x07, 0x42, 0xcb, 0x59, 0x7e, 0x47, 0x96, 0xd1, 0x00, 0xf8, - 0x79, 0x3f, 0x5d, 0x54, 0xd6, 0x91, 0xf9, 0xf7, 0xd9, 0x2d, 0x5f, 0xae, 0xdf, 0x2d, 0x5f, 0xaa, 0xdd, 0x72, 0x3d, - 0xc7, 0x7e, 0x31, 0xe9, 0xe0, 0x9f, 0x5e, 0x85, 0x10, 0xac, 0x0a, 0x90, 0xc3, 0x42, 0xbb, 0xb8, 0xd5, 0x85, 0xff, - 0x68, 0xe8, 0xb6, 0x87, 0x7f, 0x7c, 0xb0, 0x00, 0xdb, 0x16, 0x16, 0xe2, 0xbf, 0x76, 0xad, 0xaa, 0x73, 0x1f, 0xeb, - 0xb0, 0xd7, 0xce, 0x6a, 0x5d, 0xf7, 0xfa, 0x4d, 0x0b, 0xf2, 0x8a, 0x3b, 0x81, 0x12, 0xc6, 0xe0, 0xaa, 0x45, 0xc7, - 0xc7, 0x50, 0x3a, 0xc9, 0x46, 0x8b, 0xe2, 0xef, 0x24, 0xfc, 0x92, 0x88, 0xd7, 0x6e, 0xe9, 0xc6, 0x38, 0xaa, 0xab, - 0xc8, 0xb0, 0x51, 0x23, 0x2c, 0xf5, 0x3a, 0x05, 0x05, 0x30, 0x26, 0x73, 0xba, 0xfe, 0xfd, 0x35, 0x9b, 0xe0, 0x3f, - 0x64, 0x6d, 0xd6, 0x22, 0xf3, 0x6f, 0x25, 0xc6, 0xb5, 0x44, 0xf8, 0x2c, 0x1a, 0x98, 0x6b, 0xd8, 0x7e, 0xb4, 0x1e, - 0xdc, 0x43, 0x35, 0xd3, 0x50, 0x29, 0x05, 0xa9, 0x77, 0xc0, 0x0b, 0x88, 0x16, 0x09, 0xbf, 0x7e, 0xd4, 0xab, 0x38, - 0x63, 0x65, 0xd4, 0x6b, 0x04, 0x7a, 0xd5, 0xf6, 0x96, 0x52, 0xfa, 0x8b, 0x2f, 0xef, 0xe3, 0x1f, 0x11, 0xfb, 0x3a, - 0xae, 0x7c, 0x23, 0x11, 0x1b, 0x40, 0xdf, 0x68, 0xa3, 0xe6, 0xfc, 0x08, 0x0d, 0x4e, 0xfe, 0xcf, 0x6d, 0x5b, 0xa3, - 0xb1, 0x7e, 0xab, 0xe6, 0xd2, 0x2a, 0xfd, 0xac, 0xd6, 0x9f, 0x37, 0xf8, 0x2d, 0xdb, 0x8e, 0x84, 0x43, 0x50, 0x6f, - 0x2b, 0x7f, 0x3b, 0xca, 0x4a, 0x63, 0x45, 0xf1, 0xdb, 0xb6, 0xaf, 0x4c, 0x62, 0xea, 0xb1, 0x11, 0x1e, 0x6b, 0x27, - 0x52, 0x9e, 0x6f, 0x63, 0x0f, 0xe1, 0x47, 0xfe, 0x85, 0x85, 0xf7, 0xf0, 0xc3, 0x62, 0xd6, 0xf9, 0x2c, 0x49, 0xc1, - 0xac, 0x9a, 0x72, 0x3e, 0x0f, 0xb6, 0xb6, 0xce, 0xce, 0xce, 0xfc, 0xb3, 0x6d, 0x3f, 0xcb, 0x4f, 0xb6, 0xba, 0xed, - 0x76, 0x1b, 0xbf, 0x07, 0x65, 0x5b, 0xa7, 0x31, 0x3b, 0x7b, 0x0c, 0xee, 0x87, 0xfd, 0xd0, 0x7a, 0x64, 0x3d, 0xdc, - 0xb6, 0x76, 0x1e, 0xd8, 0x16, 0x29, 0x00, 0x28, 0xd9, 0xb6, 0x2d, 0xa1, 0x00, 0x42, 0x1b, 0x8a, 0xfb, 0xbb, 0x27, - 0xca, 0x86, 0xc3, 0x7c, 0x7b, 0x61, 0x21, 0x81, 0xff, 0x96, 0x7d, 0x62, 0xf5, 0xad, 0x2e, 0xca, 0x5a, 0x52, 0x8d, - 0xa8, 0x57, 0xdc, 0xef, 0xa3, 0x68, 0x1e, 0x10, 0x1b, 0x99, 0x85, 0x18, 0x26, 0x13, 0xa5, 0x34, 0x05, 0xda, 0xa5, - 0xc7, 0xf0, 0x84, 0x19, 0x5a, 0x16, 0x3c, 0xbf, 0xea, 0x3e, 0x04, 0x1d, 0x77, 0xda, 0xba, 0x3f, 0x6a, 0xb7, 0x3a, - 0x56, 0xa7, 0xd5, 0xf5, 0x1f, 0x5a, 0x5d, 0xf1, 0x3f, 0xc8, 0xc8, 0x6d, 0xab, 0x03, 0x4f, 0xdb, 0x16, 0xbc, 0x9f, - 0xde, 0x17, 0xe9, 0x17, 0x91, 0xbd, 0xd5, 0xdf, 0xc5, 0x5f, 0x8f, 0x04, 0x48, 0x7d, 0x69, 0x8b, 0x5f, 0xe8, 0x66, - 0x7f, 0x61, 0x96, 0x76, 0x1e, 0xad, 0x2d, 0xee, 0x3e, 0x5c, 0x5b, 0xbc, 0xfd, 0x60, 0x6d, 0xf1, 0xfd, 0x9d, 0x7a, - 0xf1, 0xd6, 0x89, 0xa8, 0xd2, 0x72, 0x21, 0xb4, 0x67, 0x11, 0x30, 0xca, 0xb9, 0xd3, 0x01, 0x38, 0xdb, 0x56, 0x0b, - 0x7f, 0x3c, 0xec, 0xba, 0xba, 0xd7, 0x31, 0xf6, 0xd2, 0x58, 0x3e, 0x7c, 0x04, 0x58, 0x3e, 0xef, 0x3e, 0x18, 0x61, - 0x3b, 0x42, 0x14, 0xfe, 0x9d, 0x6e, 0x3f, 0x1a, 0x81, 0x46, 0xb0, 0xf0, 0x1f, 0xfc, 0x99, 0xee, 0x74, 0x47, 0xe2, - 0xa5, 0x8d, 0xf5, 0x1f, 0x3a, 0x0f, 0x0b, 0x68, 0x8a, 0x7f, 0x7e, 0xd3, 0x26, 0x34, 0x1a, 0xf0, 0xe6, 0xb8, 0xf7, - 0x81, 0x46, 0x8f, 0xa6, 0x5d, 0xff, 0xcb, 0xd3, 0x87, 0xfe, 0xa3, 0x69, 0xe7, 0xe1, 0x07, 0xf1, 0x96, 0x00, 0x05, - 0xbf, 0xc4, 0x7f, 0x1f, 0xb6, 0xdb, 0xd3, 0x56, 0xc7, 0x7f, 0x74, 0xba, 0xed, 0x6f, 0x27, 0xad, 0x07, 0xfe, 0x23, - 0xfc, 0x57, 0x0d, 0x37, 0xcd, 0x66, 0xcc, 0xb6, 0x70, 0xbd, 0x1b, 0x7e, 0xaf, 0x39, 0x47, 0xf7, 0xbe, 0xb5, 0x73, - 0xff, 0xf9, 0x23, 0x58, 0xa3, 0x69, 0xa7, 0x0b, 0xff, 0x5f, 0xf5, 0xf8, 0x01, 0x09, 0x2f, 0x07, 0x8e, 0x18, 0x66, - 0xca, 0x2a, 0xc2, 0xd1, 0xb7, 0xc9, 0xee, 0x79, 0xdf, 0x5f, 0x15, 0x00, 0x61, 0xfc, 0xe6, 0x20, 0x37, 0xbf, 0x5d, - 0x04, 0x84, 0x3e, 0x9c, 0xff, 0x07, 0x46, 0x40, 0xbe, 0x6f, 0x06, 0xb9, 0xcf, 0x57, 0xf3, 0x03, 0x9b, 0xce, 0xda, - 0x6b, 0xe6, 0x1c, 0xfe, 0x85, 0x0d, 0x31, 0x2b, 0x1c, 0x5a, 0x73, 0x6e, 0xc6, 0x83, 0x32, 0xdc, 0xc8, 0xe7, 0x32, - 0xea, 0x5f, 0xf0, 0x2b, 0x08, 0x12, 0xdf, 0x4c, 0x90, 0x5f, 0x6f, 0x47, 0x8f, 0xf8, 0x0f, 0xa6, 0x47, 0xc1, 0x0d, - 0x7a, 0xd4, 0x22, 0xee, 0x14, 0x31, 0x20, 0x47, 0x7f, 0x9f, 0xde, 0x9d, 0xef, 0xf1, 0xab, 0x62, 0x5b, 0x0c, 0x4b, - 0x0a, 0x5b, 0xe4, 0x24, 0xbe, 0xfb, 0x9c, 0x13, 0x02, 0x91, 0x38, 0x1d, 0xda, 0x32, 0x08, 0x33, 0xc7, 0xcf, 0xef, - 0xaa, 0x97, 0x53, 0x71, 0x39, 0x27, 0xa4, 0x9b, 0x75, 0x3b, 0x3a, 0x80, 0x83, 0xb9, 0xec, 0xe1, 0x32, 0xe3, 0x11, - 0xfe, 0x7e, 0x27, 0x1e, 0xf3, 0x04, 0xef, 0xfd, 0xca, 0x3b, 0x72, 0x98, 0x7a, 0xfd, 0x2d, 0xa6, 0x8d, 0xab, 0x83, - 0x82, 0x19, 0x06, 0x0d, 0x5e, 0xb1, 0x71, 0x1c, 0x39, 0xb6, 0x33, 0x87, 0x5d, 0x0b, 0x63, 0xb6, 0x6a, 0x39, 0xdb, - 0x94, 0xae, 0xed, 0xda, 0xea, 0x57, 0x0a, 0xe5, 0xf8, 0x89, 0xb6, 0xf0, 0x50, 0x06, 0x19, 0x6d, 0xe9, 0x05, 0xc0, - 0xf8, 0xaa, 0x24, 0x47, 0x81, 0x5f, 0x59, 0x0e, 0xb6, 0x30, 0x1d, 0x3a, 0x7e, 0x17, 0x5c, 0x09, 0x2a, 0xc6, 0x4f, - 0x5e, 0xfd, 0xe0, 0xb4, 0xb6, 0xc1, 0xb4, 0x31, 0xba, 0xe9, 0x81, 0x86, 0x2b, 0xa1, 0x24, 0x11, 0x20, 0x68, 0x94, - 0x7a, 0xfa, 0x77, 0xac, 0x55, 0x21, 0xa3, 0xe2, 0xf1, 0xc5, 0x81, 0xbc, 0xd6, 0x6e, 0x63, 0xf4, 0x96, 0xa2, 0xf6, - 0xd5, 0x27, 0xb5, 0x36, 0x41, 0x65, 0xd0, 0x2f, 0xba, 0xa4, 0x33, 0x70, 0xd4, 0x0a, 0x98, 0x55, 0x6e, 0x49, 0xef, - 0x21, 0xb4, 0x85, 0x4e, 0x18, 0xb3, 0xd3, 0x78, 0x24, 0x45, 0xbb, 0x67, 0xc9, 0xdb, 0x30, 0x2d, 0xc2, 0x22, 0xec, - 0x78, 0xc2, 0x7f, 0x86, 0x17, 0xd4, 0x6c, 0x61, 0x9a, 0xd9, 0xfd, 0x7b, 0x3d, 0x0d, 0x49, 0x3d, 0x21, 0xdf, 0xc6, - 0xdf, 0xba, 0x79, 0x08, 0xfe, 0xda, 0xdf, 0x85, 0xf7, 0xf0, 0xf7, 0x6e, 0xde, 0x1b, 0xda, 0xae, 0x4f, 0x82, 0xf1, - 0x5e, 0xf5, 0xcb, 0x37, 0x51, 0x2a, 0x6c, 0x82, 0x0e, 0xf3, 0x6e, 0xab, 0xcc, 0xa4, 0xe2, 0xea, 0xee, 0x54, 0x8a, - 0x0b, 0x9e, 0x0d, 0x49, 0x05, 0x42, 0xb4, 0xeb, 0xef, 0x18, 0xe2, 0xf0, 0xb4, 0x85, 0x3f, 0x6b, 0x02, 0xf1, 0x3e, - 0x34, 0x50, 0x12, 0xf1, 0x25, 0x34, 0xdf, 0x16, 0xc2, 0x17, 0xfa, 0xfd, 0x48, 0xe2, 0x4a, 0x88, 0xaa, 0x3a, 0xc7, - 0xac, 0x39, 0x48, 0x12, 0xf9, 0x02, 0xb6, 0x67, 0xc4, 0x9c, 0x04, 0xbb, 0xca, 0x88, 0xca, 0x53, 0xe8, 0xeb, 0xe8, - 0x2f, 0x55, 0xaf, 0xab, 0xf3, 0x6a, 0xbb, 0x67, 0xcd, 0x14, 0xc8, 0xf0, 0x8d, 0xc3, 0x2a, 0xba, 0x9d, 0x21, 0xbe, - 0xd8, 0x26, 0xb6, 0x72, 0xf5, 0x4d, 0xbb, 0x35, 0x59, 0xc0, 0xe6, 0xa6, 0x60, 0x15, 0xd3, 0xd0, 0xbe, 0xc0, 0xf4, - 0x19, 0xfc, 0x59, 0x15, 0xab, 0x07, 0xc9, 0x50, 0x7e, 0x12, 0xe1, 0x2f, 0x9c, 0xa1, 0x1f, 0x65, 0xb5, 0x01, 0x39, - 0x7d, 0x92, 0x93, 0x20, 0x7d, 0x31, 0x2e, 0x9b, 0x48, 0x80, 0xcd, 0x80, 0xbf, 0xbf, 0xb0, 0xba, 0x0d, 0x22, 0xaf, - 0x79, 0x62, 0x6a, 0xc1, 0x38, 0xce, 0xe9, 0xf6, 0xb0, 0xc2, 0xbf, 0x16, 0xd5, 0xac, 0x48, 0x4d, 0xbb, 0x92, 0x15, - 0x03, 0x1b, 0x8b, 0xec, 0x40, 0x26, 0xc0, 0x99, 0xdf, 0xa4, 0x36, 0xaf, 0x77, 0x8e, 0x45, 0xee, 0x1b, 0x7e, 0xb3, - 0xdf, 0x16, 0x44, 0xb6, 0x41, 0x94, 0x5d, 0x89, 0x13, 0x19, 0x38, 0x78, 0x2b, 0xb2, 0xfa, 0x25, 0x4c, 0xe6, 0x86, - 0xb7, 0xcd, 0xd5, 0xd2, 0xe3, 0xd2, 0x3a, 0xb8, 0x32, 0x86, 0x77, 0xcc, 0x22, 0xee, 0x47, 0x29, 0xe5, 0x29, 0x39, - 0x86, 0x58, 0xf0, 0x3a, 0x6c, 0xdb, 0x2d, 0x41, 0xf2, 0x18, 0xbf, 0xa5, 0x4a, 0x90, 0xde, 0x87, 0x42, 0x95, 0x4b, - 0xfd, 0x7d, 0x2d, 0x15, 0x78, 0xda, 0xed, 0xbf, 0x39, 0xd8, 0xb3, 0xc4, 0xc6, 0xde, 0xdd, 0x82, 0xd7, 0x5d, 0xf2, - 0x8e, 0x45, 0xc6, 0x46, 0x28, 0x32, 0x36, 0x2c, 0x91, 0xe7, 0x25, 0x52, 0x67, 0xb7, 0x04, 0xd6, 0xb6, 0xc5, 0xd2, - 0x91, 0x08, 0xeb, 0xcd, 0xc0, 0x83, 0x88, 0xf1, 0x33, 0x66, 0x5b, 0xd8, 0xb5, 0x85, 0x0b, 0x6f, 0xab, 0xe4, 0x17, - 0x65, 0x33, 0xf0, 0x54, 0x05, 0x01, 0x41, 0xd3, 0x33, 0x95, 0x07, 0x23, 0x87, 0xd2, 0x69, 0xb1, 0xab, 0xad, 0x8b, - 0xc5, 0xf1, 0x0c, 0xc4, 0x92, 0xca, 0x5d, 0x79, 0x2f, 0x3b, 0xec, 0xd2, 0x54, 0xfd, 0xa3, 0x72, 0x5d, 0x94, 0x72, - 0xda, 0xe9, 0xef, 0x46, 0xd2, 0x06, 0xc2, 0xbd, 0x5c, 0xc0, 0x66, 0x06, 0xd5, 0x87, 0x86, 0x86, 0x1f, 0x67, 0x5b, - 0x67, 0xec, 0xb8, 0x15, 0xcd, 0xe3, 0x2a, 0x24, 0x88, 0x1a, 0xb1, 0xbf, 0xab, 0x94, 0xa3, 0x4c, 0xf5, 0x94, 0x8f, - 0x91, 0x91, 0xdc, 0x81, 0x84, 0x24, 0x86, 0x2d, 0x65, 0xbc, 0x91, 0x0c, 0x49, 0x58, 0x0c, 0x00, 0x96, 0xf8, 0x59, - 0xc5, 0x25, 0xa5, 0x66, 0x28, 0xed, 0xfe, 0x5f, 0xff, 0xf7, 0xff, 0x91, 0xa1, 0x46, 0xa0, 0x2d, 0x80, 0x85, 0xd9, - 0x31, 0xd5, 0xa9, 0x23, 0x3b, 0x07, 0xe7, 0x34, 0x1e, 0xb7, 0xa6, 0x51, 0x32, 0x01, 0x08, 0x0a, 0x26, 0xee, 0x14, - 0xc8, 0x7a, 0xe0, 0x0a, 0x09, 0x96, 0x79, 0x62, 0x2f, 0xc1, 0xab, 0x17, 0xe1, 0xb2, 0xfd, 0xae, 0xdc, 0x59, 0x95, - 0xb3, 0x4c, 0x0c, 0x6e, 0x64, 0xd2, 0x1a, 0x3c, 0x58, 0xcb, 0xa6, 0x55, 0xbf, 0x13, 0x4a, 0x0a, 0x13, 0x56, 0x4b, - 0xa5, 0x85, 0x96, 0xfa, 0x70, 0xe4, 0x5f, 0xff, 0xe9, 0xbf, 0xfc, 0x0f, 0xf5, 0x8a, 0x67, 0x1e, 0x7f, 0xfd, 0xc7, - 0xbf, 0xff, 0x7f, 0xff, 0xf7, 0xbf, 0x62, 0xa6, 0xb2, 0x3c, 0x17, 0xa1, 0xad, 0x65, 0x55, 0x87, 0x22, 0x62, 0x8f, - 0x59, 0x95, 0x13, 0x52, 0x4f, 0xb9, 0xdd, 0xa7, 0x09, 0x89, 0x41, 0x25, 0x74, 0xc4, 0xe7, 0x94, 0xa2, 0x4d, 0x54, - 0xbb, 0x86, 0x7c, 0xb0, 0x94, 0x16, 0x1d, 0xf5, 0xdb, 0x3b, 0x6d, 0xbb, 0x5a, 0xde, 0xbe, 0xd1, 0x77, 0x0b, 0x17, - 0xe6, 0x56, 0x89, 0x39, 0xbe, 0x5e, 0xb6, 0xa5, 0x0a, 0x6d, 0x61, 0x49, 0x59, 0x95, 0x5b, 0x18, 0x73, 0x5e, 0xe2, - 0x6b, 0xd0, 0x35, 0x8a, 0x69, 0x95, 0x6b, 0x7d, 0x7a, 0xbf, 0x2c, 0x00, 0xd1, 0x09, 0x2e, 0x8d, 0x08, 0xa0, 0xd1, - 0x79, 0x6a, 0x0b, 0xad, 0x95, 0xe4, 0xa2, 0xa4, 0x51, 0x84, 0x87, 0x73, 0xff, 0xd1, 0xdf, 0x96, 0x7f, 0x9e, 0xa1, - 0x95, 0x60, 0x39, 0xb3, 0xe8, 0x5c, 0xfa, 0x3d, 0x0f, 0xda, 0xed, 0xf9, 0xb9, 0xbb, 0xac, 0x66, 0xf0, 0xae, 0x9a, - 0x8c, 0x82, 0x6e, 0xe6, 0x80, 0x74, 0x10, 0xab, 0xe3, 0x7b, 0x60, 0xea, 0xb7, 0x31, 0x1c, 0x54, 0x96, 0x7f, 0x5a, - 0x52, 0x88, 0x29, 0xfe, 0x0d, 0x0f, 0x4c, 0x65, 0x34, 0x0e, 0x4a, 0x0c, 0x2c, 0x96, 0x46, 0xaf, 0xae, 0xe8, 0x35, - 0xed, 0xac, 0xa6, 0xac, 0x98, 0x07, 0xbe, 0xe6, 0x51, 0xed, 0x7d, 0x3c, 0x7c, 0x9d, 0x76, 0xbc, 0x69, 0x77, 0xa9, - 0x87, 0xe7, 0x3c, 0x9b, 0x99, 0x27, 0xbc, 0x2c, 0x62, 0x23, 0x36, 0x51, 0x51, 0x4c, 0x59, 0x2f, 0x4e, 0x6f, 0xcb, - 0x2f, 0x70, 0xbb, 0x01, 0x6d, 0xb3, 0x88, 0x07, 0xc4, 0xb4, 0x3d, 0xf3, 0x0c, 0x38, 0xc2, 0xd3, 0xf5, 0x6c, 0x69, - 0xcc, 0xd5, 0x13, 0x4d, 0x31, 0x56, 0x58, 0x4f, 0x07, 0x2a, 0x7d, 0xea, 0x6e, 0x0e, 0x25, 0x42, 0x0d, 0xbf, 0xca, - 0xa3, 0xd5, 0x77, 0x35, 0x1f, 0x5d, 0x8a, 0x66, 0x70, 0x8b, 0xd7, 0xd6, 0x0b, 0x35, 0x29, 0x6a, 0x3f, 0x80, 0xf5, - 0x43, 0x60, 0xda, 0xcd, 0x56, 0x54, 0x88, 0xad, 0xde, 0x85, 0xbf, 0x6a, 0x7b, 0x3c, 0x9a, 0xcf, 0xa9, 0xa1, 0x0b, - 0xdc, 0x48, 0x76, 0x35, 0x4a, 0x0a, 0x4a, 0x1b, 0x10, 0xa7, 0xf4, 0xb2, 0x8d, 0x64, 0x5b, 0xf1, 0x24, 0xcf, 0xef, - 0xe9, 0x57, 0x8c, 0xff, 0x7f, 0x2a, 0xa5, 0xd0, 0x17, 0x78, 0x7c, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xbd, 0x7d, 0xd9, 0x76, 0xe3, 0xc6, 0x92, 0xe0, 0xf3, + 0x9c, 0x33, 0x7f, 0x30, 0x2f, 0x28, 0x58, 0x5d, 0x05, 0x5c, 0x81, 0x10, 0x49, 0x95, 0xaa, 0xca, 0xa0, 0x40, 0x5e, + 0xd5, 0x62, 0x57, 0xd9, 0xb5, 0xb9, 0xa4, 0xb2, 0xaf, 0x2d, 0xeb, 0x4a, 0x10, 0x99, 0x14, 0xe1, 0x02, 0x01, 0x1a, + 0x48, 0x6a, 0x31, 0x85, 0x3e, 0xfd, 0xd4, 0x4f, 0x7d, 0xce, 0x6c, 0xfd, 0xd0, 0x0f, 0xd3, 0xa7, 0xfb, 0x61, 0x3e, + 0x62, 0x9e, 0xfb, 0x53, 0xee, 0x0f, 0x4c, 0x7f, 0xc2, 0x44, 0x44, 0x2e, 0x48, 0x80, 0xa4, 0x24, 0xbb, 0x7d, 0xe7, + 0x78, 0x11, 0x90, 0x6b, 0x44, 0x64, 0x64, 0x6c, 0x19, 0x09, 0xee, 0xde, 0x1b, 0x65, 0x43, 0x7e, 0x35, 0x63, 0xd6, + 0x84, 0x4f, 0x93, 0xfe, 0xae, 0xfc, 0x3f, 0x8b, 0x46, 0xfd, 0xdd, 0x24, 0x4e, 0x3f, 0x59, 0x39, 0x4b, 0xc2, 0x78, + 0x98, 0xa5, 0xd6, 0x24, 0x67, 0xe3, 0x70, 0x14, 0xf1, 0x28, 0x88, 0xa7, 0xd1, 0x19, 0xb3, 0xb6, 0xfa, 0xbb, 0x53, + 0xc6, 0x23, 0x6b, 0x38, 0x89, 0xf2, 0x82, 0xf1, 0xf0, 0xe3, 0xc1, 0x17, 0xad, 0x27, 0xfd, 0xdd, 0x62, 0x98, 0xc7, + 0x33, 0x6e, 0xe1, 0x90, 0xe1, 0x34, 0x1b, 0xcd, 0x13, 0xd6, 0x3f, 0x8f, 0x72, 0xeb, 0x05, 0x0b, 0xdf, 0x9d, 0xfe, + 0xc4, 0x86, 0xdc, 0x1f, 0xb1, 0x71, 0x9c, 0xb2, 0xf7, 0x79, 0x36, 0x63, 0x39, 0xbf, 0xf2, 0xf6, 0x57, 0x57, 0xc4, + 0xac, 0xf0, 0x9e, 0xe9, 0xaa, 0x33, 0xc6, 0xdf, 0x5d, 0xa4, 0xaa, 0xcf, 0x73, 0x26, 0x26, 0xc9, 0xf2, 0xc2, 0x8b, + 0xd7, 0xb4, 0xd9, 0xbf, 0x9a, 0x9e, 0x66, 0x49, 0xe1, 0x7d, 0xd2, 0xf5, 0xb3, 0x3c, 0xe3, 0x19, 0x82, 0xe5, 0x4f, + 0xa2, 0xc2, 0x68, 0xe9, 0xbd, 0x5b, 0xd1, 0x64, 0x26, 0x2b, 0x5f, 0x15, 0x2f, 0xd2, 0xf9, 0x94, 0xe5, 0xd1, 0x69, + 0xc2, 0xbc, 0x9c, 0x85, 0x0e, 0xf3, 0xb8, 0x17, 0xbb, 0x61, 0x9f, 0x5b, 0x71, 0x6a, 0xb1, 0xc1, 0x0b, 0x46, 0x25, + 0x0b, 0xa6, 0x5b, 0x05, 0xf7, 0xda, 0x1e, 0x90, 0x6b, 0x1c, 0x9f, 0xcd, 0xf5, 0xfb, 0x45, 0x1e, 0x73, 0xf5, 0x7c, + 0x1e, 0x25, 0x73, 0x16, 0xc4, 0xa5, 0x1b, 0xb0, 0x43, 0x7e, 0x14, 0xc6, 0xde, 0x27, 0x1a, 0x14, 0x86, 0x5c, 0x8c, + 0xb3, 0xdc, 0x41, 0x5a, 0xc5, 0x38, 0x36, 0xbf, 0xbe, 0x76, 0x78, 0xb8, 0x28, 0x5d, 0xf7, 0x13, 0xf3, 0x87, 0x51, + 0x92, 0x38, 0x38, 0xf1, 0xfd, 0xfb, 0x39, 0xce, 0x18, 0x7b, 0xfc, 0x30, 0x3e, 0x72, 0x7b, 0xf1, 0xd8, 0x89, 0x99, + 0x5b, 0xf5, 0xcb, 0xc6, 0x56, 0xcc, 0x1c, 0xee, 0xba, 0xef, 0xd6, 0xf7, 0xc9, 0x19, 0x9f, 0xe7, 0x00, 0x7b, 0xe9, + 0xbd, 0x53, 0x33, 0xef, 0x63, 0xfd, 0x33, 0xea, 0xd8, 0x03, 0xd8, 0x0b, 0x6e, 0x7d, 0x11, 0x5e, 0xc4, 0xe9, 0x28, + 0xbb, 0xf0, 0xf7, 0x27, 0x11, 0xfc, 0xf9, 0x90, 0x65, 0xfc, 0xfe, 0x7d, 0xe7, 0x3c, 0x8b, 0x47, 0x56, 0x3b, 0x0c, + 0xcd, 0xca, 0xab, 0x67, 0xfb, 0xfb, 0xd7, 0xd7, 0x8d, 0x02, 0x3f, 0x8d, 0x78, 0x7c, 0xce, 0x44, 0x67, 0x00, 0xc0, + 0x86, 0xbf, 0x33, 0xce, 0x46, 0xfb, 0xfc, 0x2a, 0x81, 0x52, 0xc6, 0x78, 0x61, 0x03, 0x8e, 0xcf, 0xb3, 0x21, 0x90, + 0x2d, 0x35, 0x08, 0x0f, 0x4d, 0x73, 0x36, 0x4b, 0xa2, 0x21, 0xc3, 0x7a, 0x18, 0xa9, 0xea, 0x51, 0x35, 0xf2, 0xbe, + 0x0b, 0xc5, 0xf2, 0x3a, 0xae, 0x97, 0xb1, 0x30, 0x65, 0x17, 0xd6, 0x9b, 0x68, 0xd6, 0x1b, 0x26, 0x51, 0x51, 0x58, + 0x29, 0x5b, 0x10, 0x0a, 0xf9, 0x7c, 0x08, 0x0c, 0x42, 0x08, 0x2e, 0x80, 0x4c, 0x7c, 0x12, 0x17, 0xfe, 0xf1, 0xc6, + 0xb0, 0x28, 0x3e, 0xb0, 0x62, 0x9e, 0xf0, 0x8d, 0x10, 0xd6, 0x82, 0xdf, 0x0b, 0xc3, 0xef, 0x5c, 0x3e, 0xc9, 0xb3, + 0x0b, 0xeb, 0x45, 0x9e, 0x43, 0x73, 0x1b, 0xa6, 0x14, 0x0d, 0xac, 0x18, 0xc6, 0xca, 0xb8, 0xa5, 0x07, 0xc3, 0x05, + 0xf4, 0xad, 0x8f, 0x05, 0xb3, 0x4e, 0xe6, 0x69, 0x11, 0x8d, 0x19, 0x34, 0x3d, 0xb1, 0xb2, 0xdc, 0x3a, 0x81, 0x41, + 0x4f, 0x60, 0xc9, 0x0a, 0x0e, 0xbb, 0xc6, 0xb7, 0xdd, 0x1e, 0xcd, 0x05, 0x85, 0x07, 0xec, 0x92, 0x87, 0xac, 0x04, + 0xc6, 0xb4, 0x0a, 0x8d, 0x86, 0xe3, 0x2e, 0x12, 0x28, 0x60, 0x61, 0xc6, 0x90, 0x65, 0x1d, 0xb3, 0xb1, 0x5e, 0x9c, + 0x2f, 0xee, 0xdf, 0xd7, 0xb4, 0x06, 0x9a, 0x38, 0xd0, 0xb6, 0x68, 0xb4, 0xf5, 0x04, 0xe2, 0x35, 0x12, 0xb9, 0x1e, + 0xf3, 0x25, 0xf9, 0xf6, 0xaf, 0xd2, 0x61, 0x7d, 0x6c, 0xa8, 0x2c, 0x79, 0xb6, 0xcf, 0xf3, 0x38, 0x3d, 0x03, 0x20, + 0xe4, 0x4c, 0x66, 0x93, 0xb2, 0x14, 0x8b, 0xff, 0x9e, 0x85, 0x2c, 0xec, 0xe3, 0xe8, 0x29, 0x73, 0xec, 0x82, 0x7a, + 0xd8, 0x61, 0x88, 0xa4, 0x07, 0x06, 0x63, 0x03, 0x16, 0xb0, 0x4d, 0xdb, 0xf6, 0xbe, 0x73, 0xbd, 0x2b, 0xe4, 0x20, + 0xdf, 0xf7, 0x89, 0x7d, 0x45, 0xe7, 0x38, 0xec, 0x20, 0xd0, 0x7e, 0xc2, 0xd2, 0x33, 0x3e, 0x19, 0xb0, 0xc3, 0xf6, + 0x51, 0xc0, 0x01, 0xaa, 0xd1, 0x7c, 0xc8, 0x1c, 0xe4, 0x47, 0x2f, 0xc7, 0xed, 0xb3, 0xe9, 0xc0, 0x14, 0xb8, 0x30, + 0xf7, 0x08, 0xc7, 0xda, 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x53, 0x96, 0x1b, 0x70, + 0xe8, 0x66, 0xbd, 0xda, 0x0a, 0xce, 0x61, 0x85, 0xa0, 0x9f, 0x35, 0x9e, 0xa7, 0x43, 0x1e, 0x83, 0xe0, 0xb2, 0x37, + 0x01, 0x5c, 0xb1, 0x72, 0x7a, 0xe1, 0x6c, 0xb7, 0x74, 0x9d, 0xd8, 0xdd, 0x64, 0x87, 0xf9, 0x66, 0xe7, 0xc8, 0x43, + 0x28, 0x35, 0xf1, 0x25, 0xe2, 0x31, 0x20, 0x58, 0x7a, 0x1f, 0x99, 0xde, 0x9e, 0x5f, 0x0c, 0x98, 0xbf, 0xcc, 0xc7, + 0x21, 0xf7, 0xa7, 0xd1, 0x0c, 0xb1, 0x61, 0xc4, 0x03, 0x51, 0x3a, 0x44, 0xe8, 0x6a, 0xeb, 0x82, 0x14, 0xf3, 0x2b, + 0x16, 0x70, 0x81, 0x20, 0xb0, 0x67, 0x5f, 0x44, 0xc3, 0x09, 0x6c, 0xf1, 0x8a, 0x70, 0x23, 0xb5, 0x1d, 0x86, 0x39, + 0x8b, 0x38, 0x7b, 0x91, 0x30, 0x7c, 0xc3, 0x15, 0x80, 0x9e, 0xb6, 0xeb, 0xe5, 0x6a, 0xdf, 0x25, 0x31, 0x7f, 0x9b, + 0xc1, 0x3c, 0x3d, 0xc1, 0x24, 0xc0, 0xc5, 0xf9, 0xfd, 0xfb, 0x31, 0xb2, 0xc8, 0x1e, 0x87, 0xd5, 0x3a, 0x9d, 0x73, + 0x58, 0xb7, 0x14, 0x5b, 0xd8, 0x40, 0x6d, 0x2f, 0xf6, 0x39, 0x10, 0xf1, 0x59, 0x96, 0x72, 0x18, 0x0e, 0xe0, 0xd5, + 0x1c, 0xe4, 0x47, 0xb3, 0x19, 0x4b, 0x47, 0xcf, 0x26, 0x71, 0x32, 0x02, 0x6a, 0x94, 0x80, 0x6f, 0xc2, 0x42, 0xc0, + 0x13, 0x90, 0x09, 0x6e, 0xc6, 0x88, 0x96, 0x0f, 0x19, 0x99, 0x87, 0xb6, 0xdd, 0x43, 0x09, 0x24, 0xb1, 0x40, 0x19, + 0x44, 0x0b, 0xf7, 0x01, 0x44, 0x7f, 0xe1, 0xf2, 0xcd, 0x30, 0xd6, 0xcb, 0x28, 0x09, 0xfc, 0x1e, 0x25, 0x0d, 0xd0, + 0x9f, 0x81, 0x0c, 0xec, 0xa1, 0xe0, 0xfa, 0x4a, 0x4a, 0x9d, 0x88, 0x29, 0x0c, 0x81, 0x00, 0x43, 0x94, 0x20, 0x92, + 0x06, 0xef, 0xb3, 0xe4, 0x6a, 0x1c, 0x27, 0xc9, 0xfe, 0x7c, 0x36, 0xcb, 0x72, 0xee, 0x7d, 0x1d, 0x2e, 0x78, 0x56, + 0xe1, 0x4a, 0x9b, 0xbc, 0xb8, 0x88, 0x39, 0x12, 0xd4, 0x5d, 0x0c, 0x23, 0x58, 0xea, 0xa7, 0x59, 0x96, 0xb0, 0x28, + 0x05, 0x34, 0xd8, 0xc0, 0xb6, 0x83, 0x74, 0x9e, 0x24, 0xbd, 0x53, 0x18, 0xf6, 0x53, 0x8f, 0xaa, 0x85, 0xc4, 0x0f, + 0xe8, 0x79, 0x2f, 0xcf, 0xa3, 0x2b, 0x68, 0x88, 0x6d, 0x80, 0x17, 0x61, 0xb5, 0xbe, 0xda, 0x7f, 0xf7, 0xd6, 0x17, + 0x8c, 0x1f, 0x8f, 0xaf, 0x00, 0xd0, 0xb2, 0x92, 0x9a, 0xe3, 0x3c, 0x9b, 0x36, 0xa6, 0x46, 0x3a, 0xc4, 0x21, 0xeb, + 0xad, 0x01, 0x21, 0xa6, 0x91, 0x61, 0x95, 0x98, 0x09, 0xc1, 0x5b, 0xe2, 0x67, 0x59, 0x89, 0x7b, 0x60, 0x80, 0x0f, + 0x81, 0x28, 0x86, 0x29, 0x6f, 0x86, 0x96, 0xe7, 0x57, 0x8b, 0x38, 0x24, 0x38, 0x67, 0xa8, 0x7f, 0x11, 0xc6, 0x61, + 0x04, 0xb3, 0x2f, 0xc4, 0x80, 0xa5, 0x82, 0x38, 0x2e, 0x4b, 0x6f, 0xa2, 0x99, 0x18, 0x25, 0x1e, 0x0a, 0x14, 0x0e, + 0xdb, 0xe8, 0xfa, 0x9a, 0xc1, 0x8b, 0xeb, 0x7d, 0x13, 0x2e, 0x22, 0x85, 0x0f, 0x6a, 0x28, 0xdc, 0x5f, 0x81, 0x90, + 0x13, 0xa8, 0xc9, 0xce, 0x41, 0x0f, 0x02, 0x9c, 0x5f, 0x83, 0xfa, 0x1b, 0x27, 0x08, 0xc5, 0xbd, 0x8e, 0x07, 0x1a, + 0xf4, 0xd9, 0x24, 0x4a, 0xcf, 0xd8, 0x28, 0x98, 0xb0, 0x52, 0x4a, 0xde, 0x3d, 0x0b, 0xd6, 0x18, 0xd8, 0xa9, 0xb0, + 0x5e, 0x1e, 0xbc, 0x79, 0x2d, 0x57, 0xae, 0x26, 0x8c, 0x61, 0x91, 0xe6, 0xa0, 0x56, 0x41, 0x6c, 0x4b, 0x71, 0xfc, + 0x82, 0x2b, 0xe9, 0x2d, 0x4a, 0xe2, 0xe2, 0xe3, 0x0c, 0x4c, 0x0c, 0xf6, 0x1e, 0x86, 0x81, 0xe9, 0x43, 0x98, 0x8a, + 0xca, 0x61, 0x3e, 0x51, 0x31, 0xd2, 0x45, 0xd0, 0x59, 0x60, 0x2a, 0x5e, 0x33, 0xc7, 0x2d, 0x81, 0x55, 0x79, 0x3c, + 0xb4, 0xa2, 0xd1, 0xe8, 0x55, 0x1a, 0xf3, 0x38, 0x4a, 0xe2, 0x5f, 0x88, 0x92, 0x0b, 0xe4, 0x31, 0xde, 0x93, 0x8b, + 0x00, 0xb8, 0x53, 0x8f, 0xc4, 0x55, 0x42, 0xf6, 0x1e, 0x11, 0x43, 0x48, 0xcb, 0x24, 0x3c, 0x3c, 0x92, 0xe0, 0x25, + 0xfe, 0x6c, 0x5e, 0x4c, 0x90, 0xb0, 0x72, 0x60, 0x14, 0xe4, 0xd9, 0x69, 0xc1, 0xf2, 0x73, 0x36, 0xd2, 0x1c, 0x50, + 0x00, 0x56, 0xd4, 0x1c, 0x8c, 0x17, 0x9a, 0xd1, 0x51, 0x3a, 0x94, 0xc1, 0x50, 0x3d, 0x53, 0xcc, 0x32, 0xc9, 0xcc, + 0xda, 0xc2, 0xd1, 0x52, 0xc0, 0x11, 0x46, 0x85, 0x94, 0x04, 0x79, 0xa8, 0x30, 0x9c, 0x80, 0x14, 0x02, 0xad, 0x60, + 0x6e, 0x73, 0xa5, 0xc9, 0x5e, 0xcc, 0x49, 0x25, 0xe4, 0xd0, 0x11, 0x36, 0x32, 0x41, 0x9a, 0xbb, 0xb0, 0xab, 0x40, + 0xca, 0x4b, 0x70, 0x85, 0x14, 0x51, 0x66, 0x0e, 0x32, 0x40, 0xf8, 0x8d, 0xd0, 0x85, 0x3e, 0xb6, 0x20, 0x36, 0xf0, + 0xf5, 0xca, 0x03, 0x61, 0x25, 0xde, 0x15, 0x22, 0xde, 0x1a, 0xb0, 0x71, 0x62, 0xe4, 0x27, 0xef, 0x1e, 0xf7, 0xd3, + 0x6c, 0x6f, 0x38, 0x64, 0x45, 0x91, 0x01, 0x6c, 0xf7, 0xa8, 0xfd, 0x3a, 0x43, 0x0b, 0x28, 0xe9, 0x6a, 0x59, 0x67, + 0x17, 0xa4, 0xc1, 0x4d, 0xb5, 0xa2, 0x74, 0x7a, 0x60, 0x1f, 0x1f, 0x83, 0xcc, 0xf6, 0x24, 0x19, 0x80, 0xea, 0xcb, + 0x86, 0x9f, 0xb0, 0x67, 0xea, 0x94, 0x59, 0x69, 0x5f, 0x3a, 0x75, 0x90, 0x3c, 0x18, 0xd6, 0x2d, 0x8d, 0x05, 0x5d, + 0x39, 0x34, 0xae, 0x86, 0x54, 0x90, 0x8b, 0x33, 0x52, 0xd9, 0xc6, 0x32, 0x82, 0xd5, 0x56, 0x7a, 0x44, 0x7a, 0x85, + 0x4d, 0x41, 0x80, 0x1e, 0xb2, 0xa3, 0x9e, 0xac, 0x0f, 0x73, 0x41, 0xb9, 0x9c, 0xfd, 0x3c, 0x67, 0x05, 0x17, 0xac, + 0x0b, 0xe3, 0x82, 0xb9, 0x0a, 0x22, 0xb6, 0x69, 0x1d, 0xd6, 0x6c, 0xc7, 0x55, 0xb0, 0xbd, 0x9b, 0xa1, 0x1e, 0x2b, + 0x90, 0x93, 0x6f, 0x66, 0x27, 0x84, 0x95, 0xb9, 0xd7, 0xd7, 0xdf, 0xa8, 0x41, 0xaa, 0xa5, 0xd4, 0x36, 0x50, 0x63, + 0x4d, 0x6c, 0xd5, 0x64, 0x64, 0xbb, 0x52, 0xa1, 0xde, 0xeb, 0xf4, 0x6a, 0x7c, 0x00, 0x7b, 0xae, 0xad, 0x59, 0xba, + 0x32, 0xb6, 0xdf, 0x2b, 0x9a, 0xbe, 0x13, 0x23, 0x93, 0x35, 0xca, 0x6e, 0xe7, 0x1e, 0xb5, 0xe3, 0xa1, 0xed, 0x52, + 0x5d, 0x25, 0x18, 0xe6, 0x75, 0xc1, 0xd0, 0x84, 0x7a, 0xa6, 0xbb, 0xd8, 0x9a, 0xa9, 0x58, 0xa8, 0xd6, 0x5a, 0x39, + 0x10, 0x3c, 0x3c, 0x04, 0xe3, 0x64, 0xa5, 0x7f, 0xf0, 0x36, 0x9a, 0x32, 0xa4, 0xa8, 0xb7, 0xae, 0x81, 0x74, 0x20, + 0xa0, 0xc9, 0x51, 0x53, 0xbd, 0x71, 0x57, 0x58, 0x4d, 0xf5, 0xfd, 0x15, 0x83, 0x15, 0x01, 0xf6, 0x75, 0xb9, 0x62, + 0x89, 0x48, 0x6f, 0x0a, 0x2e, 0xd1, 0xf4, 0x11, 0x65, 0x62, 0x4d, 0x48, 0xc1, 0x03, 0xf2, 0xb0, 0xfc, 0x8d, 0x85, + 0x93, 0xad, 0x98, 0xc2, 0x91, 0xa3, 0x4c, 0x01, 0x3a, 0x93, 0x12, 0x00, 0x71, 0x49, 0x7f, 0x6b, 0x1b, 0x0b, 0xc9, + 0xb6, 0x8f, 0x7c, 0xe0, 0x8f, 0x93, 0x88, 0x3b, 0x9d, 0xad, 0xb6, 0x0b, 0x7c, 0x08, 0x42, 0x1c, 0x74, 0x04, 0x98, + 0xf7, 0x15, 0x2a, 0x8c, 0xbc, 0x05, 0x97, 0xfb, 0x60, 0x14, 0x4d, 0xe2, 0x31, 0x77, 0x12, 0x54, 0x22, 0x6e, 0xc9, + 0x12, 0x50, 0x32, 0x7a, 0x5f, 0x81, 0x94, 0xe0, 0x42, 0xba, 0x88, 0x6a, 0x2d, 0xd0, 0x14, 0xa4, 0x24, 0xa5, 0x48, + 0x0b, 0x2a, 0x08, 0x0c, 0xa1, 0xd2, 0x53, 0x1c, 0x05, 0xfa, 0x2d, 0x1e, 0x88, 0x41, 0x83, 0x25, 0x8b, 0x32, 0x1e, + 0xc4, 0xcb, 0x85, 0xa0, 0x86, 0x7d, 0x9e, 0xbd, 0xce, 0x2e, 0x58, 0xfe, 0x2c, 0x42, 0xd8, 0x03, 0xd1, 0xbd, 0x04, + 0x49, 0x4f, 0x02, 0x9d, 0xf5, 0x14, 0xaf, 0x9c, 0x13, 0xd2, 0xb0, 0x10, 0xd3, 0x18, 0x15, 0x21, 0x68, 0x39, 0xa2, + 0x7d, 0x8a, 0x5b, 0x8a, 0xf6, 0x1e, 0xaa, 0x12, 0xa6, 0x79, 0x6b, 0xef, 0x75, 0x9d, 0xb7, 0x60, 0x84, 0x99, 0xe2, + 0xd6, 0xfa, 0x8e, 0x75, 0x3d, 0xa9, 0x9b, 0x1d, 0xc9, 0x5b, 0x86, 0x32, 0x03, 0xfd, 0x71, 0x7d, 0x5d, 0x19, 0xe9, + 0xa0, 0x4c, 0xb5, 0x34, 0x47, 0xcb, 0x49, 0x6c, 0x09, 0xb7, 0x04, 0x65, 0x84, 0x86, 0x57, 0x9e, 0x25, 0x89, 0xa1, + 0x8b, 0xbc, 0xb8, 0xe7, 0x34, 0xd4, 0x11, 0x40, 0x31, 0xad, 0x69, 0xa4, 0x01, 0x0f, 0x74, 0x05, 0x2a, 0x25, 0xa5, + 0x8d, 0xbc, 0xaa, 0x89, 0x80, 0x38, 0x1d, 0xb1, 0x5c, 0x38, 0x68, 0x52, 0x87, 0xc2, 0x84, 0x29, 0x30, 0x34, 0x1b, + 0x81, 0x84, 0x57, 0x08, 0x80, 0x79, 0xe2, 0x4f, 0xb2, 0x82, 0xeb, 0x3a, 0x13, 0xfa, 0xf8, 0xfa, 0x3a, 0x16, 0xfe, + 0x22, 0x32, 0x40, 0xce, 0xa6, 0xd9, 0x39, 0x5b, 0x01, 0x75, 0x4f, 0x0d, 0x66, 0x82, 0x6c, 0x0c, 0x03, 0x4a, 0x14, + 0x54, 0xcb, 0x2c, 0x89, 0x87, 0x4c, 0x6b, 0xa9, 0xa9, 0x0f, 0x06, 0x1d, 0xbb, 0x04, 0x19, 0xc1, 0xdc, 0x7e, 0xbf, + 0xdf, 0xf6, 0x3a, 0x6e, 0x29, 0x08, 0xbe, 0x58, 0xa2, 0xe8, 0x0d, 0xfa, 0x51, 0x9a, 0xe0, 0xab, 0x64, 0x01, 0x77, + 0x0d, 0xa5, 0xc8, 0x85, 0x9f, 0xe4, 0x49, 0x41, 0xec, 0x7a, 0x23, 0x18, 0x94, 0x33, 0x25, 0xb8, 0xd1, 0xc4, 0x15, + 0xdb, 0xf6, 0x83, 0x26, 0x9b, 0x66, 0x27, 0xb5, 0xc3, 0xd4, 0xc2, 0xc8, 0x35, 0x2f, 0xb4, 0x07, 0x6c, 0x2e, 0x0f, + 0x5a, 0x89, 0x54, 0x0d, 0xbc, 0x0e, 0x10, 0x0a, 0x4f, 0xd7, 0x59, 0x42, 0xa9, 0xea, 0x2c, 0x85, 0xb8, 0xde, 0x40, + 0x1f, 0x99, 0x04, 0x73, 0x15, 0x09, 0xf6, 0xa5, 0x40, 0xe0, 0xe8, 0x91, 0x89, 0xf5, 0x7a, 0x06, 0xcb, 0x73, 0x1a, + 0x0d, 0x3f, 0x69, 0x70, 0x2b, 0xb2, 0x37, 0xd9, 0xc0, 0x69, 0x94, 0x84, 0x86, 0xb8, 0x32, 0xf1, 0x56, 0x12, 0xba, + 0xb6, 0x51, 0xc0, 0x21, 0x5b, 0x62, 0xfb, 0xe6, 0x42, 0x37, 0xb9, 0x5d, 0xb2, 0x87, 0xf2, 0x9f, 0x34, 0x97, 0xdc, + 0xc0, 0x72, 0x5c, 0x49, 0x03, 0xae, 0x18, 0x0f, 0x96, 0xa6, 0x01, 0x09, 0xf0, 0x5d, 0x39, 0x8a, 0x8b, 0xf5, 0x24, + 0xf8, 0x5d, 0xc1, 0x7c, 0x6e, 0xcc, 0x74, 0x2b, 0xa4, 0x5a, 0xc2, 0x49, 0x33, 0x58, 0x83, 0x26, 0x8d, 0x07, 0x25, + 0x6a, 0xbe, 0x46, 0x43, 0x85, 0x38, 0xfe, 0x4c, 0x54, 0xa1, 0x09, 0x86, 0x60, 0xe4, 0x5e, 0x21, 0x19, 0x2e, 0x5b, + 0x16, 0x2d, 0x52, 0xa6, 0xc6, 0xa4, 0x52, 0x35, 0xcb, 0x65, 0x60, 0x60, 0xd1, 0x6e, 0xf5, 0xa5, 0x25, 0xae, 0x44, + 0x6e, 0x1a, 0x6a, 0x61, 0x52, 0x28, 0x6f, 0xc2, 0xc9, 0xd1, 0xef, 0x52, 0xd6, 0xbb, 0x89, 0x4f, 0xae, 0xf0, 0xc9, + 0x7d, 0xc3, 0x87, 0x32, 0x79, 0xbb, 0x18, 0x14, 0xc1, 0xd7, 0xb5, 0x4a, 0xb4, 0x4f, 0x7d, 0x14, 0xcc, 0xae, 0x16, + 0xba, 0x20, 0x50, 0x24, 0x9b, 0xa4, 0x03, 0xc9, 0x6f, 0x28, 0x36, 0x2a, 0xcf, 0x28, 0x73, 0xc5, 0x06, 0xa9, 0x79, + 0xa5, 0x99, 0x97, 0xba, 0x0d, 0xfb, 0xbd, 0x2c, 0x25, 0x9d, 0xb8, 0xa0, 0x4c, 0xec, 0xdd, 0x44, 0x1b, 0x2f, 0x0d, + 0x33, 0x61, 0xfd, 0x0a, 0x63, 0xa7, 0x46, 0xa1, 0x54, 0x8a, 0x40, 0x1c, 0x1b, 0x5f, 0x2b, 0xcb, 0x20, 0xf3, 0x57, + 0xd8, 0x53, 0x00, 0x4a, 0x02, 0x8b, 0xaf, 0xa9, 0xe4, 0x45, 0x61, 0x9d, 0x8e, 0xf7, 0x88, 0x8e, 0x95, 0x08, 0xad, + 0x89, 0x7c, 0xad, 0xcf, 0x62, 0xbf, 0xe6, 0x12, 0x9a, 0x94, 0xcc, 0x07, 0x79, 0x60, 0xab, 0x40, 0x44, 0xa5, 0xdb, + 0x92, 0x41, 0x42, 0x0e, 0xe9, 0x32, 0xd1, 0x6b, 0x23, 0x19, 0xb4, 0x4e, 0x85, 0x44, 0x4b, 0x8f, 0xc2, 0xc8, 0x41, + 0xc7, 0x9d, 0xd6, 0x62, 0x89, 0x90, 0x4d, 0x7b, 0x93, 0x58, 0x11, 0x9d, 0xd3, 0x1c, 0x4d, 0x38, 0x53, 0xa7, 0x3b, + 0x0e, 0xa0, 0x03, 0x62, 0x7f, 0x89, 0xf5, 0x56, 0x9a, 0x9d, 0xae, 0x5f, 0x39, 0x7c, 0xd7, 0xd7, 0x13, 0xe4, 0x07, + 0x61, 0xf0, 0xc2, 0x9a, 0x0d, 0x94, 0xec, 0xdd, 0x7b, 0x8d, 0xad, 0xc8, 0xfe, 0xac, 0x4a, 0x2a, 0x4f, 0xa1, 0xc6, + 0xb9, 0xf5, 0x75, 0x62, 0x66, 0x68, 0x51, 0x55, 0xec, 0x1b, 0x52, 0x7d, 0x5f, 0x29, 0xec, 0x0a, 0xe5, 0x7d, 0x39, + 0x74, 0xec, 0xba, 0x6e, 0x90, 0x93, 0xf3, 0x72, 0x6f, 0x95, 0x0b, 0x79, 0xff, 0xbe, 0xe9, 0x33, 0x9d, 0xeb, 0xe1, + 0x9f, 0x39, 0xa8, 0x9c, 0x8b, 0xab, 0x94, 0x2c, 0x98, 0x67, 0x4a, 0x1d, 0x2d, 0x39, 0xa0, 0xed, 0x1e, 0x7a, 0xda, + 0xd1, 0x45, 0x14, 0x73, 0x4b, 0x8f, 0x22, 0x3c, 0x6d, 0x94, 0x4f, 0xd2, 0xe8, 0x00, 0xbc, 0xd0, 0x84, 0x24, 0x27, + 0xdc, 0xb4, 0x45, 0x8b, 0xe1, 0x84, 0x61, 0x08, 0x5c, 0xd9, 0x13, 0xa6, 0xec, 0xb9, 0x87, 0x78, 0x8b, 0x81, 0xd9, + 0x6a, 0xd8, 0xcb, 0x66, 0xf7, 0x9a, 0xf9, 0x0f, 0x6b, 0x04, 0xb2, 0x6d, 0xaa, 0xea, 0xca, 0xc6, 0xbb, 0x14, 0x91, + 0x18, 0x61, 0x5b, 0x35, 0xb6, 0xb4, 0xf5, 0x7b, 0x0d, 0xf7, 0xba, 0x72, 0xcc, 0x6b, 0x4a, 0xb5, 0xa1, 0x87, 0x95, + 0x9b, 0xc3, 0x4c, 0x47, 0x5e, 0xac, 0xa0, 0xdb, 0x13, 0x41, 0x21, 0x70, 0x22, 0xb4, 0x3d, 0xa8, 0xb8, 0x81, 0x48, + 0xc9, 0x95, 0x56, 0xcd, 0xe6, 0xc9, 0x48, 0x02, 0x0b, 0x2e, 0x2c, 0x97, 0x7c, 0x74, 0x11, 0x27, 0x49, 0x55, 0xfa, + 0xbb, 0x0a, 0x78, 0x31, 0xec, 0x6d, 0xa2, 0x5d, 0x60, 0x34, 0x57, 0x20, 0xb8, 0xda, 0x08, 0xfb, 0xe8, 0xb8, 0xd5, + 0xba, 0x8b, 0x88, 0x23, 0x37, 0xa3, 0x11, 0x50, 0x8f, 0x11, 0x56, 0xcd, 0xda, 0x7b, 0x2f, 0x30, 0xa4, 0x66, 0xe0, + 0x83, 0xea, 0x8c, 0x8a, 0x7f, 0x95, 0x3d, 0xf5, 0x2b, 0xd1, 0xbb, 0x55, 0x75, 0x35, 0x03, 0x2a, 0x2a, 0xf0, 0x61, + 0x86, 0x58, 0xda, 0x2a, 0x10, 0x90, 0xeb, 0x61, 0x51, 0x0a, 0x98, 0xa4, 0xc1, 0x82, 0x52, 0x60, 0xad, 0x95, 0xdd, + 0xeb, 0xdb, 0x82, 0x39, 0x14, 0x0a, 0x17, 0xfd, 0x9f, 0x65, 0xd3, 0x19, 0x5a, 0x66, 0x0d, 0xa6, 0x86, 0x06, 0x1f, + 0x1b, 0xf5, 0xe5, 0x8a, 0xb2, 0x5a, 0x1f, 0xda, 0x91, 0x35, 0x7e, 0xd2, 0x8e, 0x32, 0x38, 0x54, 0x73, 0x5d, 0x54, + 0xb7, 0x9b, 0x9b, 0x22, 0x66, 0x15, 0x8f, 0xfb, 0xa4, 0xb7, 0xb5, 0x35, 0xe9, 0x69, 0x1a, 0x90, 0x4c, 0x92, 0x0c, + 0x6f, 0x32, 0x40, 0x59, 0x11, 0x67, 0x51, 0x36, 0xc8, 0xb7, 0x28, 0x4b, 0x5c, 0xbf, 0x1f, 0x7a, 0x7b, 0x35, 0xcf, + 0xda, 0xdb, 0x5b, 0xef, 0x22, 0x57, 0x75, 0xd2, 0x83, 0x3c, 0x3c, 0x82, 0xa2, 0x25, 0x9b, 0x32, 0x5c, 0x4c, 0xb3, + 0x11, 0x0b, 0x6c, 0xe8, 0x9e, 0xda, 0xa5, 0xdc, 0x34, 0x11, 0x6c, 0x8e, 0x88, 0x39, 0x8b, 0x0f, 0xf5, 0x48, 0x6a, + 0xb0, 0x07, 0x2c, 0xa0, 0xcd, 0x85, 0xaf, 0xc2, 0xb3, 0x24, 0x3b, 0x8d, 0x92, 0x03, 0xa1, 0xc0, 0x6b, 0x2d, 0xbf, + 0x05, 0x97, 0x91, 0x2c, 0x56, 0x43, 0x49, 0x7d, 0x35, 0xf8, 0x2a, 0xb8, 0xbd, 0x47, 0xe5, 0xad, 0xd8, 0x1d, 0xbf, + 0xed, 0x77, 0x6c, 0x15, 0x11, 0xfb, 0xc9, 0x9c, 0x0e, 0x34, 0x4e, 0x01, 0x94, 0x39, 0x00, 0x4d, 0x56, 0x78, 0x43, + 0x16, 0xfe, 0x34, 0xf8, 0x49, 0xb9, 0xd4, 0x19, 0xb8, 0x10, 0xe0, 0xe4, 0x27, 0x31, 0x6f, 0xe1, 0x79, 0xa4, 0xed, + 0x2d, 0x44, 0x05, 0xc6, 0x15, 0x29, 0x2e, 0x5d, 0x2a, 0x6f, 0xd0, 0x3b, 0x0e, 0x4f, 0xa0, 0xd9, 0xc6, 0xc6, 0xc2, + 0x79, 0x13, 0xf1, 0x89, 0x9f, 0x47, 0xe9, 0x28, 0x9b, 0x3a, 0xee, 0xa6, 0x6d, 0xbb, 0x7e, 0x41, 0x9e, 0xc8, 0xe7, + 0x6e, 0xb9, 0x71, 0x02, 0x7e, 0x40, 0x68, 0x0f, 0xec, 0xcd, 0x63, 0xef, 0x80, 0x85, 0x27, 0xbb, 0x1b, 0x8b, 0x11, + 0x2b, 0xfb, 0x27, 0xde, 0xa5, 0x8e, 0xb9, 0x7b, 0xef, 0x51, 0xca, 0x40, 0xaf, 0xb0, 0x7f, 0x29, 0xc1, 0x00, 0x76, + 0xa3, 0xf8, 0x3b, 0x48, 0xb9, 0x8f, 0x74, 0x20, 0x22, 0xe3, 0xb4, 0xd7, 0xd7, 0x76, 0x46, 0x11, 0x03, 0xfb, 0x9e, + 0x76, 0x56, 0xef, 0xdf, 0xaf, 0xd4, 0x7c, 0x55, 0xea, 0xcd, 0x59, 0x58, 0xf3, 0xd4, 0xbd, 0x97, 0x74, 0xb4, 0x52, + 0xdf, 0xc8, 0x73, 0x46, 0x4a, 0x73, 0xd9, 0x4e, 0x70, 0x8c, 0x2d, 0xbe, 0x7a, 0x5b, 0x1f, 0x8a, 0x28, 0x85, 0x1f, + 0x83, 0xf5, 0x12, 0x81, 0xfa, 0x06, 0x07, 0xc7, 0x3b, 0x08, 0xb7, 0x76, 0x9d, 0x41, 0xe0, 0xdc, 0x6b, 0xb5, 0xae, + 0x7f, 0xdc, 0x3a, 0xfc, 0x73, 0xd4, 0xfa, 0x65, 0xaf, 0xf5, 0xc3, 0x91, 0x7b, 0xed, 0xfc, 0xb8, 0x35, 0x38, 0x94, + 0x6f, 0x87, 0x7f, 0xee, 0xff, 0x58, 0x1c, 0xfd, 0x41, 0x14, 0x6e, 0xb8, 0xee, 0xd6, 0x99, 0x37, 0x63, 0xe1, 0x56, + 0xab, 0xd5, 0x87, 0xa7, 0x33, 0x78, 0xc2, 0xbf, 0x17, 0xf0, 0xe7, 0xfa, 0xd0, 0xfa, 0x4f, 0x3f, 0xa6, 0xff, 0xf9, + 0xc7, 0xfc, 0x08, 0xc7, 0x3c, 0xfc, 0xf3, 0x8f, 0x85, 0xfd, 0xa0, 0x1f, 0x6e, 0x1d, 0x6d, 0xba, 0x8e, 0xae, 0xf9, + 0x43, 0x58, 0x3d, 0x42, 0xab, 0xc3, 0x3f, 0xcb, 0x37, 0xfb, 0xc1, 0xc9, 0x6e, 0x3f, 0x3c, 0xba, 0x76, 0xec, 0xeb, + 0x07, 0xee, 0xb5, 0xeb, 0x5e, 0x6f, 0xe0, 0x3c, 0xe7, 0x30, 0xfa, 0x03, 0xf8, 0x3b, 0x86, 0xbf, 0x36, 0xfc, 0x9d, + 0xc2, 0xdf, 0x3f, 0x43, 0x37, 0x11, 0x7f, 0xbb, 0xa6, 0x58, 0xc8, 0x35, 0x1e, 0x58, 0x44, 0xb0, 0x0a, 0xee, 0xc6, + 0x56, 0xec, 0x6d, 0x10, 0xd1, 0x60, 0x1f, 0xfa, 0xbe, 0x8f, 0x61, 0x52, 0x67, 0x71, 0xbc, 0x01, 0x8b, 0x8e, 0x9c, + 0xb3, 0x11, 0x30, 0x4f, 0x44, 0x0e, 0x8a, 0x80, 0x8b, 0xb3, 0xd5, 0x02, 0x0f, 0x57, 0xbd, 0x61, 0xb8, 0xc1, 0x1c, + 0x30, 0x0a, 0xde, 0x32, 0x7c, 0xe8, 0xba, 0xde, 0x0b, 0x79, 0x66, 0x88, 0xfb, 0x5c, 0xb0, 0x56, 0x9a, 0x09, 0x93, + 0xc6, 0x76, 0xbd, 0xd9, 0x8a, 0x4a, 0xd8, 0xd6, 0xe9, 0x19, 0xd4, 0x9d, 0x8a, 0x83, 0xb6, 0xef, 0x58, 0xf4, 0x09, + 0xb7, 0xe4, 0x1b, 0xe3, 0x10, 0x78, 0xc9, 0x92, 0x6f, 0x1a, 0x8d, 0x86, 0x8d, 0x28, 0xdc, 0xb1, 0xa7, 0x0c, 0x66, + 0x58, 0x32, 0x11, 0x39, 0x29, 0x4d, 0x61, 0xd9, 0xc2, 0xe4, 0xef, 0xa3, 0x9c, 0x6f, 0x54, 0x86, 0x6d, 0x58, 0xb3, + 0x64, 0x9b, 0x96, 0xfe, 0x1d, 0xa6, 0x40, 0xd3, 0x92, 0xce, 0x3f, 0xcc, 0xf1, 0xc3, 0x94, 0xd0, 0x7a, 0xeb, 0x70, + 0xf0, 0xd0, 0x0b, 0x90, 0x3b, 0xa2, 0x9f, 0xf3, 0x1e, 0xd5, 0x18, 0xfc, 0x2b, 0xc3, 0x0c, 0x9e, 0x98, 0x0f, 0x43, + 0x34, 0x8b, 0x52, 0x07, 0xb7, 0x52, 0x14, 0xf7, 0xaf, 0x70, 0x67, 0xa4, 0xa5, 0xb7, 0x1f, 0xaa, 0x1d, 0x73, 0x90, + 0x33, 0xf6, 0x5d, 0x94, 0x7c, 0x62, 0xb9, 0x73, 0xe9, 0x75, 0xba, 0x9f, 0x53, 0x67, 0x0f, 0x6d, 0xb3, 0x0f, 0xd5, + 0x31, 0x9a, 0x32, 0x0b, 0xd4, 0x11, 0x61, 0xab, 0xe3, 0xe5, 0x18, 0xd5, 0x42, 0x12, 0x14, 0x5e, 0x16, 0x76, 0x89, + 0xc3, 0xed, 0xdd, 0xe2, 0xfc, 0xac, 0x6f, 0x07, 0xb6, 0x0d, 0x16, 0xff, 0x01, 0x85, 0xad, 0x84, 0x61, 0x01, 0x06, + 0xd9, 0x6e, 0xdc, 0xe3, 0x9b, 0x9b, 0x55, 0xc0, 0x09, 0x0f, 0xd2, 0xa9, 0x7b, 0xe2, 0x45, 0xde, 0x24, 0x84, 0x01, + 0x87, 0xd0, 0x0c, 0xbb, 0xf4, 0x86, 0xbb, 0xb1, 0x9c, 0x06, 0x63, 0x21, 0x7e, 0x12, 0x15, 0xfc, 0x15, 0xc6, 0x23, + 0xc2, 0x21, 0x1a, 0xfb, 0x3e, 0xbb, 0x64, 0x43, 0x65, 0x67, 0x00, 0xa1, 0x22, 0xb7, 0xe7, 0x0e, 0x43, 0xa3, 0x19, + 0xcc, 0x1d, 0x86, 0x07, 0x03, 0x1b, 0xf6, 0x12, 0xec, 0xca, 0x30, 0x3a, 0xec, 0x1c, 0x0d, 0xd2, 0x70, 0xc6, 0x02, + 0x4d, 0x5b, 0x59, 0x74, 0x56, 0x2b, 0xea, 0x1e, 0x0d, 0x9c, 0x29, 0x18, 0xe9, 0x60, 0x8b, 0x3b, 0xf8, 0x86, 0x11, + 0x8a, 0x22, 0xfc, 0xc0, 0xce, 0x5e, 0x5c, 0xce, 0x1c, 0x7b, 0x77, 0xcb, 0xde, 0xc4, 0x52, 0xcf, 0x06, 0xf6, 0x82, + 0xb9, 0xc3, 0x0b, 0xd7, 0xec, 0xbc, 0x7d, 0x84, 0xa0, 0x62, 0x21, 0x4e, 0x7e, 0x31, 0xb0, 0xfb, 0x62, 0xea, 0x36, + 0x0c, 0x9a, 0xca, 0xe5, 0xc7, 0x15, 0x3d, 0x20, 0x54, 0x55, 0x57, 0x05, 0x1d, 0x94, 0x75, 0x03, 0x67, 0x62, 0x22, + 0xd1, 0xc2, 0xc9, 0x24, 0x15, 0xc0, 0xe1, 0xc1, 0x66, 0x30, 0xa9, 0xd1, 0x6d, 0xfb, 0x68, 0x70, 0x11, 0x3c, 0xb0, + 0x1f, 0xa8, 0x97, 0x31, 0x20, 0xc3, 0xc4, 0xf4, 0x63, 0x90, 0x76, 0xf8, 0xf7, 0x9c, 0x01, 0x92, 0x17, 0x54, 0x34, + 0x93, 0x45, 0x67, 0x58, 0x74, 0x10, 0x20, 0xa8, 0x5e, 0xa1, 0xad, 0x3f, 0xb1, 0x26, 0xa3, 0x90, 0x60, 0xbf, 0x7f, + 0x1f, 0x96, 0x66, 0xb3, 0x73, 0x84, 0xe7, 0x0d, 0x39, 0x2f, 0xbe, 0x8b, 0x39, 0xa8, 0x84, 0xad, 0xbe, 0xed, 0x0e, + 0x6c, 0x0b, 0x97, 0xb6, 0x97, 0x6d, 0x86, 0x82, 0xc2, 0xf1, 0xe6, 0x01, 0x0b, 0x26, 0xfd, 0xb0, 0x3d, 0x70, 0x72, + 0x19, 0x6e, 0xc4, 0x73, 0x4b, 0x21, 0xc1, 0xdb, 0xde, 0x04, 0x04, 0x3a, 0x72, 0xee, 0x86, 0xbd, 0xa9, 0x0a, 0xa1, + 0xe8, 0x78, 0x73, 0xe4, 0x06, 0x31, 0xfc, 0x71, 0x5a, 0xc8, 0x34, 0x13, 0xdd, 0x57, 0x6b, 0x66, 0x37, 0x18, 0x29, + 0x8b, 0x3c, 0x09, 0xb3, 0x4d, 0x07, 0x23, 0xb4, 0x20, 0x69, 0x77, 0x07, 0x00, 0xc3, 0xa6, 0xa3, 0x38, 0x6d, 0x4b, + 0xb1, 0x9a, 0xb2, 0xcf, 0x0f, 0xf5, 0x72, 0x0c, 0xd9, 0x60, 0xc8, 0xfc, 0x4a, 0xfb, 0x00, 0x58, 0x41, 0xe2, 0xe5, + 0x47, 0xea, 0xcc, 0xeb, 0x65, 0xed, 0x7c, 0x6b, 0xa1, 0x44, 0x11, 0xf3, 0x0c, 0x09, 0xc5, 0x4b, 0xed, 0x86, 0x09, + 0x73, 0x7b, 0x86, 0xc4, 0xd0, 0x2c, 0x1f, 0xb6, 0x81, 0xe9, 0x55, 0x80, 0x3d, 0x35, 0xb7, 0x45, 0x12, 0x56, 0xcd, + 0xbd, 0x43, 0x60, 0xed, 0x23, 0xe0, 0x21, 0xda, 0x46, 0x3d, 0x15, 0xcd, 0x67, 0x49, 0xf8, 0xb2, 0x71, 0x5c, 0x1c, + 0xe1, 0x89, 0xd0, 0xbe, 0x3f, 0x9c, 0xe7, 0x20, 0x0f, 0xf8, 0x5b, 0xb0, 0x0c, 0x42, 0xd9, 0x14, 0x1d, 0x3d, 0x3c, + 0x02, 0xf6, 0x08, 0xf1, 0x46, 0xd8, 0xdc, 0xa8, 0x46, 0x8b, 0x92, 0x8c, 0x17, 0x3a, 0x18, 0xee, 0x71, 0xe9, 0xda, + 0xa3, 0x60, 0x90, 0x27, 0xc6, 0x0e, 0x9e, 0xf9, 0xfb, 0x43, 0xac, 0xc6, 0x09, 0x0a, 0xb7, 0xa4, 0xdd, 0x56, 0x89, + 0xbf, 0x7d, 0x3f, 0x05, 0x09, 0x8e, 0x75, 0xe0, 0x67, 0xdd, 0xbf, 0x9f, 0x48, 0xa4, 0x76, 0xd3, 0x1e, 0x9d, 0x44, + 0x60, 0x3c, 0x38, 0xf7, 0x53, 0xa8, 0x46, 0x12, 0x51, 0x51, 0x8e, 0x16, 0xa8, 0x79, 0xaa, 0x56, 0xc1, 0x77, 0x68, + 0x46, 0xe0, 0x39, 0x86, 0xad, 0xc9, 0x4f, 0xd5, 0x8d, 0x45, 0x2c, 0xdf, 0x75, 0xe9, 0x68, 0x0b, 0x0f, 0x20, 0x05, + 0xa3, 0x09, 0x86, 0x71, 0x29, 0x28, 0x59, 0xf1, 0xdf, 0xb1, 0x11, 0x2b, 0x9f, 0x1c, 0x66, 0x9b, 0x9b, 0x47, 0xe2, + 0xdc, 0x82, 0x18, 0x87, 0x1b, 0xd1, 0xd5, 0xb8, 0x02, 0xa0, 0x3e, 0x9d, 0x13, 0xd7, 0x03, 0xd3, 0x8a, 0x35, 0x5d, + 0x8a, 0x7d, 0x72, 0x98, 0x01, 0x28, 0xb8, 0xe5, 0x1c, 0xfa, 0x83, 0x3f, 0x1e, 0x81, 0x7b, 0xec, 0xff, 0xc1, 0xdd, + 0x52, 0x82, 0xa6, 0x27, 0xcf, 0x14, 0x17, 0x74, 0xc6, 0xda, 0xf1, 0x28, 0x36, 0x1a, 0x14, 0x5e, 0x0a, 0x18, 0x80, + 0x36, 0x07, 0x99, 0x50, 0x71, 0x10, 0x72, 0x54, 0x60, 0xfb, 0xb8, 0xf9, 0x39, 0xee, 0xec, 0xe7, 0x60, 0xe1, 0x0d, + 0xf4, 0xdb, 0x6b, 0x78, 0xfb, 0xa3, 0x7e, 0xfb, 0x89, 0x05, 0xbf, 0x94, 0x32, 0x74, 0x5f, 0x9b, 0xe2, 0x91, 0x9a, + 0xa2, 0x14, 0x4b, 0x64, 0xd0, 0x90, 0xb9, 0xf9, 0x52, 0xcc, 0x86, 0xbb, 0x25, 0x10, 0x43, 0x89, 0xae, 0xdc, 0xe7, + 0xd1, 0x19, 0x12, 0xd7, 0x35, 0x49, 0x61, 0xe4, 0x12, 0x98, 0x08, 0x57, 0x7c, 0x4b, 0xcc, 0xd9, 0x6f, 0x83, 0x0d, + 0x5e, 0xcb, 0x3b, 0x40, 0xfb, 0x8e, 0x4d, 0x67, 0xfc, 0x6a, 0x9f, 0x14, 0x7d, 0x20, 0xd3, 0x06, 0xc4, 0xd9, 0x79, + 0xbb, 0x17, 0xef, 0xf2, 0x5e, 0x0c, 0x52, 0x3d, 0x57, 0x2c, 0x86, 0x7b, 0xd5, 0x7b, 0x8f, 0x51, 0x4a, 0x93, 0x99, + 0xbc, 0x1a, 0x7a, 0x5d, 0x89, 0xde, 0xe6, 0x26, 0x20, 0xd8, 0x33, 0xba, 0x72, 0xd1, 0xb5, 0x2c, 0x05, 0x4d, 0x00, + 0xa2, 0x27, 0x75, 0x96, 0x23, 0x8e, 0xc3, 0x6c, 0x36, 0x28, 0x1e, 0x31, 0x77, 0xe5, 0xa8, 0x38, 0x26, 0x76, 0x97, + 0x09, 0x3b, 0x80, 0x19, 0x71, 0x79, 0xab, 0x23, 0xa2, 0xc3, 0xa2, 0xbf, 0x8e, 0x6f, 0x1f, 0x7b, 0x6c, 0xb3, 0xe3, + 0x82, 0x06, 0xa9, 0x8d, 0xf5, 0xb8, 0x1a, 0x0b, 0xea, 0xc3, 0x63, 0x4d, 0xa5, 0xb2, 0xd8, 0xdc, 0x2c, 0xeb, 0x47, + 0xb5, 0x6a, 0x07, 0xd7, 0x4e, 0x53, 0x2e, 0x9b, 0xd9, 0x20, 0x1c, 0x88, 0x98, 0x40, 0x81, 0x96, 0x56, 0x56, 0x0c, + 0x30, 0xa4, 0x2c, 0x47, 0xf9, 0x14, 0x32, 0x2f, 0x2e, 0x4b, 0x9d, 0xfa, 0xf2, 0x4c, 0x06, 0x1d, 0xf1, 0xd4, 0x93, + 0x8c, 0x15, 0x50, 0xb0, 0x5e, 0xea, 0x25, 0xb4, 0x44, 0x80, 0xf9, 0x0b, 0x95, 0x43, 0x23, 0x2c, 0x90, 0x28, 0x34, + 0xcc, 0x12, 0x65, 0x7c, 0x16, 0x61, 0x0c, 0xda, 0xfe, 0x59, 0x2d, 0xf6, 0x55, 0x28, 0xa3, 0xa3, 0x38, 0xcc, 0x8f, + 0x02, 0xaa, 0x9f, 0x4b, 0x09, 0x36, 0x09, 0x3f, 0x02, 0x1b, 0x55, 0x8e, 0x27, 0x09, 0xc2, 0xe7, 0x71, 0xce, 0xc8, + 0x53, 0xd8, 0x90, 0x30, 0x4b, 0xd3, 0x36, 0x52, 0xed, 0x22, 0x33, 0x08, 0xe5, 0xc2, 0xfc, 0x13, 0xe3, 0xec, 0x22, + 0x0b, 0x97, 0x5a, 0x83, 0xf9, 0xf1, 0xce, 0x04, 0x28, 0xbb, 0xbe, 0xce, 0x84, 0x8f, 0x1b, 0x91, 0xbd, 0xa1, 0x2b, + 0x26, 0x03, 0x85, 0x54, 0xe0, 0x44, 0x64, 0xf1, 0xd0, 0x19, 0x0a, 0x8d, 0x70, 0x40, 0xa7, 0xc8, 0xb9, 0x6b, 0x6c, + 0xfa, 0x7c, 0xa0, 0x7d, 0xa3, 0x34, 0x74, 0x12, 0x10, 0x02, 0x02, 0x77, 0xc3, 0x9a, 0x4a, 0x07, 0x69, 0x90, 0x50, + 0x29, 0xfa, 0x39, 0x80, 0x7f, 0x18, 0x49, 0x0a, 0x80, 0xfd, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, 0x2e, 0x00, 0xcd, + 0xb5, 0x8f, 0x2b, 0xe1, 0x0b, 0x03, 0x15, 0xa6, 0xa7, 0x59, 0x79, 0x29, 0x94, 0xc8, 0xd3, 0x15, 0x29, 0x6b, 0x24, + 0x93, 0xcf, 0xd1, 0xe1, 0x53, 0xde, 0xf5, 0x5b, 0x89, 0x87, 0x2e, 0x78, 0x0e, 0xcb, 0xaa, 0x9e, 0xdf, 0x84, 0x9c, + 0x9c, 0x6b, 0xd0, 0x15, 0x52, 0xe8, 0x2f, 0x39, 0xc9, 0x7b, 0x6f, 0xfc, 0xaa, 0x96, 0x1a, 0x43, 0xd9, 0xc7, 0x55, + 0xcd, 0xb0, 0xbc, 0x9c, 0x55, 0x61, 0x0a, 0x02, 0x6e, 0xc1, 0x92, 0x60, 0x21, 0x35, 0x04, 0x58, 0xd8, 0x1e, 0x69, + 0xa5, 0x20, 0x2f, 0x75, 0x78, 0xe7, 0x39, 0x58, 0x01, 0xc6, 0xa1, 0x96, 0x4a, 0xa6, 0x91, 0xc4, 0x97, 0x4a, 0x14, + 0x98, 0x72, 0x7f, 0x08, 0x7e, 0x6a, 0xf3, 0xa4, 0xeb, 0xd2, 0xf5, 0xe3, 0x29, 0xa6, 0xf6, 0x10, 0xe8, 0xb1, 0x77, + 0x0f, 0x4c, 0x89, 0xba, 0x0e, 0x2b, 0x88, 0x43, 0xb3, 0x9a, 0x66, 0x01, 0x33, 0xa6, 0x0d, 0x5a, 0xb2, 0x0d, 0xb6, + 0x5c, 0x0e, 0xf6, 0x91, 0xd8, 0x9e, 0xd5, 0x0a, 0x08, 0x5d, 0x83, 0x06, 0x86, 0xdc, 0xa5, 0x42, 0x0b, 0xf3, 0x5e, + 0x97, 0x8a, 0x70, 0x7f, 0x0e, 0xb8, 0xb4, 0x82, 0x33, 0x2f, 0xa3, 0x81, 0xf7, 0xe3, 0xd3, 0x04, 0x13, 0x5f, 0x10, + 0x2b, 0xb0, 0x83, 0x83, 0x4e, 0xb3, 0x29, 0x70, 0x2a, 0x2e, 0x52, 0x06, 0xcb, 0x8a, 0x52, 0x1b, 0xfe, 0x48, 0x91, + 0xad, 0xbb, 0x3c, 0xd2, 0x5d, 0x88, 0x05, 0xb0, 0xd3, 0x2f, 0x18, 0xf9, 0x96, 0xf5, 0x32, 0x60, 0x70, 0xae, 0x35, + 0x0e, 0x02, 0xbf, 0xb9, 0x99, 0x1c, 0x95, 0x29, 0xb1, 0x5d, 0x93, 0xd5, 0x05, 0xe4, 0x98, 0x04, 0xd8, 0xc0, 0x1d, + 0x84, 0xa5, 0xb2, 0xc7, 0x8b, 0x72, 0x8a, 0xcb, 0xa5, 0x2c, 0xe4, 0xe6, 0x79, 0x35, 0xcd, 0xe7, 0x56, 0x9a, 0x4d, + 0xc7, 0x5b, 0xf1, 0x45, 0xc1, 0x3f, 0x70, 0x62, 0x69, 0xd5, 0x53, 0x6a, 0x85, 0x47, 0x99, 0x5b, 0xb2, 0x4e, 0x49, + 0xad, 0xae, 0x1b, 0xa8, 0x46, 0x78, 0x9a, 0x86, 0x8d, 0x40, 0x88, 0x09, 0x2e, 0x7e, 0xdb, 0x64, 0x62, 0xda, 0x5b, + 0x42, 0xea, 0x08, 0xbb, 0x87, 0x72, 0x82, 0xbb, 0x9a, 0x67, 0x5f, 0x86, 0xb3, 0xf5, 0xcc, 0xbd, 0x67, 0x30, 0xf7, + 0xd3, 0x90, 0x1b, 0x8c, 0x1e, 0xcb, 0x84, 0x1f, 0x19, 0xfb, 0xc8, 0x55, 0xd5, 0xb3, 0xb3, 0xb0, 0x12, 0x59, 0xe2, + 0xc9, 0x38, 0xea, 0x30, 0x4e, 0x45, 0x6b, 0x82, 0xec, 0xfa, 0xba, 0x30, 0xf7, 0x02, 0x05, 0x4d, 0x3d, 0x5e, 0x8f, + 0xd3, 0x56, 0xec, 0x6c, 0x44, 0x22, 0xf7, 0xde, 0xd4, 0x22, 0x91, 0x15, 0x9f, 0xe3, 0x48, 0x6b, 0x0e, 0x72, 0x9f, + 0x9d, 0x2d, 0x6f, 0x52, 0xa1, 0x5b, 0x34, 0xda, 0xc6, 0x1e, 0xd5, 0x07, 0x92, 0x7a, 0x46, 0x05, 0x56, 0x35, 0xf6, + 0xfd, 0xfb, 0x1d, 0x91, 0x6e, 0xa9, 0x14, 0x1b, 0x2c, 0x2d, 0x8c, 0x66, 0x8c, 0x82, 0x41, 0x49, 0x91, 0x81, 0x1a, + 0xe5, 0x6b, 0x04, 0xc3, 0x1e, 0x35, 0x00, 0xc5, 0xb9, 0xba, 0xfa, 0x69, 0x29, 0xd9, 0x42, 0x40, 0xe2, 0x2e, 0x18, + 0x88, 0x35, 0xc1, 0xcc, 0xc8, 0x27, 0x1f, 0x81, 0xf3, 0x06, 0x0c, 0x1d, 0x03, 0xf0, 0x0b, 0xc4, 0xa6, 0x07, 0x13, + 0xdb, 0x26, 0xa2, 0xe8, 0xb3, 0x81, 0x97, 0x00, 0xec, 0xac, 0x0a, 0x8d, 0x7e, 0xa8, 0x52, 0xc0, 0x90, 0x0d, 0xdc, + 0x80, 0x55, 0x61, 0xb9, 0xbd, 0x97, 0xe0, 0x36, 0xc0, 0xeb, 0x0b, 0xd9, 0x7c, 0x03, 0xf3, 0x04, 0xab, 0xb3, 0x0b, + 0xbf, 0xb2, 0xac, 0xc5, 0xb9, 0xd3, 0x41, 0xa3, 0x5e, 0x51, 0x42, 0xd4, 0xee, 0x63, 0xed, 0x4b, 0x8c, 0xb0, 0x88, + 0xf7, 0x37, 0xf8, 0xae, 0xc7, 0x2d, 0xf7, 0x34, 0x5a, 0x84, 0xe9, 0x32, 0x69, 0x0c, 0x4a, 0xd6, 0xfd, 0x64, 0xc4, + 0xbd, 0xdc, 0x17, 0xb1, 0xe0, 0x0a, 0x47, 0x56, 0x85, 0x14, 0x1b, 0x48, 0xd2, 0xd3, 0x1e, 0x1d, 0xb0, 0x6f, 0x34, + 0x7b, 0x01, 0x65, 0x3e, 0x56, 0xa4, 0x92, 0x90, 0xd2, 0xec, 0x86, 0x48, 0x12, 0xd6, 0x8a, 0x3c, 0x75, 0xde, 0x77, + 0xb4, 0xcf, 0xad, 0x24, 0x82, 0x11, 0x9c, 0x84, 0xe9, 0x58, 0x79, 0xd0, 0x14, 0xe0, 0x2a, 0x3a, 0x62, 0xfa, 0x26, + 0x20, 0xbf, 0x19, 0xc8, 0xed, 0xa5, 0xe4, 0xda, 0x5c, 0xc3, 0xf0, 0x0c, 0x09, 0x56, 0x45, 0x22, 0xf0, 0x88, 0x1a, + 0x70, 0xcc, 0x57, 0x79, 0x1e, 0x60, 0xc2, 0xd7, 0xf6, 0x26, 0x00, 0x94, 0x93, 0xab, 0xe2, 0x2c, 0x05, 0xba, 0x01, + 0xcb, 0xd5, 0x71, 0x6a, 0x54, 0x24, 0x2e, 0x6e, 0x4c, 0x57, 0xb7, 0xf4, 0xa7, 0x68, 0x39, 0x93, 0x21, 0xa6, 0x83, + 0x20, 0x20, 0x53, 0x9f, 0x32, 0x47, 0xc8, 0x5c, 0x61, 0x7d, 0xce, 0x9c, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x49, + 0x2d, 0x5e, 0xa7, 0x4d, 0x29, 0x11, 0x93, 0x12, 0xf3, 0x54, 0xa4, 0x62, 0x33, 0x25, 0xee, 0xdc, 0xfa, 0x46, 0x0b, + 0x69, 0xa3, 0x9d, 0x8a, 0x1c, 0x6c, 0x56, 0xc9, 0x7b, 0x02, 0xe3, 0xa5, 0x20, 0x7c, 0x89, 0x8c, 0xb5, 0x98, 0x33, + 0xc7, 0x44, 0xb0, 0x7a, 0x31, 0x15, 0xf9, 0x07, 0x47, 0xa7, 0xd9, 0x1b, 0xf4, 0x20, 0xf5, 0x06, 0x12, 0xb3, 0x26, + 0xbe, 0x0b, 0x69, 0xa8, 0x23, 0x04, 0x2a, 0xa3, 0x5a, 0xa6, 0xe3, 0xc4, 0x2a, 0x7c, 0x23, 0xf8, 0xea, 0xbd, 0x3e, + 0xce, 0x37, 0x9e, 0x1b, 0xab, 0x11, 0xc4, 0xe0, 0x2d, 0xe4, 0x47, 0x9e, 0x14, 0xe1, 0x40, 0xb8, 0x7c, 0x73, 0xb3, + 0x97, 0xef, 0xf2, 0x2a, 0x44, 0x52, 0xc1, 0x18, 0x63, 0x46, 0x31, 0xee, 0x89, 0x9a, 0x5a, 0xcc, 0x61, 0x60, 0xd9, + 0x3a, 0xcc, 0xf1, 0x00, 0x00, 0x5a, 0x9a, 0xd2, 0xab, 0xa6, 0x42, 0xe5, 0x79, 0x2e, 0xe1, 0x53, 0x1d, 0xa2, 0xaa, + 0xc6, 0xef, 0x57, 0x67, 0xa0, 0x10, 0xdc, 0xf7, 0x3a, 0x1e, 0x1e, 0x42, 0xc0, 0x2a, 0x0a, 0x59, 0xa0, 0x37, 0x68, + 0xaf, 0x4a, 0x84, 0x62, 0xe6, 0x64, 0x3d, 0x66, 0x38, 0xa9, 0x60, 0x0b, 0x95, 0xb0, 0x54, 0x5a, 0xe0, 0x57, 0x1b, + 0xa1, 0x79, 0xca, 0xb8, 0xf7, 0xa6, 0xc2, 0x19, 0xf4, 0x07, 0xf3, 0x96, 0x19, 0xf5, 0xfd, 0xd2, 0x89, 0x4c, 0x05, + 0x26, 0x6e, 0x66, 0xa9, 0xfd, 0x7e, 0x59, 0xa5, 0xfd, 0xbc, 0x42, 0xee, 0x73, 0xd2, 0x7c, 0x9d, 0x3b, 0x68, 0x3e, + 0x19, 0xee, 0x57, 0xca, 0x0f, 0x2d, 0x8c, 0x9a, 0xf2, 0xcb, 0xeb, 0xca, 0xaf, 0xf0, 0x54, 0x78, 0xab, 0xdf, 0x45, + 0xa1, 0x8b, 0xfa, 0x1c, 0x0c, 0x21, 0xfd, 0x08, 0xae, 0xa1, 0xc1, 0x83, 0x22, 0x59, 0x2c, 0xd6, 0x2e, 0x88, 0xeb, + 0x63, 0x4e, 0xb5, 0x43, 0x19, 0x63, 0xc4, 0xd3, 0x92, 0x83, 0x24, 0x83, 0x83, 0xf1, 0x1b, 0x18, 0x10, 0x93, 0x92, + 0x90, 0x0e, 0xa1, 0xb3, 0x32, 0x13, 0x51, 0xb9, 0x8b, 0xb7, 0x1b, 0x97, 0x35, 0x85, 0x22, 0xec, 0x04, 0x33, 0x95, + 0x52, 0x41, 0x20, 0x4d, 0xbe, 0x7b, 0x9d, 0x5a, 0x30, 0xb4, 0x70, 0x4d, 0x05, 0xe4, 0xb5, 0x5d, 0x0f, 0x9a, 0x7c, + 0xa4, 0x18, 0xfa, 0x2a, 0x35, 0xe2, 0x65, 0x06, 0x5f, 0xc3, 0xe6, 0xaf, 0x89, 0x92, 0x3c, 0x64, 0x22, 0xf6, 0x0a, + 0x3e, 0x11, 0xb2, 0x29, 0xd8, 0x99, 0x40, 0x3f, 0xb4, 0x2b, 0x7b, 0xe9, 0x6e, 0x51, 0xb9, 0xb4, 0x68, 0x6c, 0x25, + 0x6a, 0xd6, 0xfc, 0x30, 0xde, 0x4c, 0x61, 0x3f, 0x7b, 0x94, 0x40, 0x40, 0x9a, 0xca, 0x49, 0xaa, 0x79, 0x0f, 0xd3, + 0x23, 0x00, 0x09, 0x76, 0x3f, 0x81, 0x85, 0x7e, 0x53, 0x62, 0x82, 0x45, 0xd5, 0xd8, 0x6d, 0x06, 0x5a, 0x73, 0x46, + 0x9a, 0x6f, 0x86, 0x5a, 0x7b, 0x53, 0x59, 0xcf, 0x98, 0x1d, 0x60, 0xdb, 0xee, 0x66, 0x71, 0x98, 0x6e, 0x76, 0x8e, + 0x0c, 0xc1, 0x85, 0xc7, 0xff, 0x49, 0x89, 0x69, 0x20, 0xb9, 0xd4, 0x8d, 0x9f, 0x50, 0x87, 0xe1, 0xff, 0x16, 0xa4, + 0x80, 0x07, 0xb5, 0xd5, 0x58, 0x72, 0xee, 0x15, 0x47, 0xc9, 0x65, 0x55, 0xed, 0x6a, 0x09, 0x1a, 0xba, 0x91, 0x8c, + 0x89, 0x62, 0x9e, 0x13, 0x00, 0xa3, 0xd8, 0xfc, 0x39, 0xd3, 0x49, 0xde, 0xbf, 0xac, 0x4c, 0xed, 0xf6, 0x7d, 0x3f, + 0xca, 0xcf, 0xe8, 0x48, 0x45, 0x65, 0x73, 0x12, 0xf3, 0x6f, 0x0b, 0x30, 0xcd, 0x89, 0x0f, 0xf5, 0x5c, 0x47, 0xa1, + 0x00, 0x5f, 0xd9, 0x50, 0x6a, 0xb6, 0xd7, 0xbf, 0x75, 0xb6, 0x87, 0x92, 0x28, 0x82, 0x05, 0x1a, 0x74, 0x59, 0x83, + 0x2f, 0x60, 0x19, 0xdc, 0x91, 0x7e, 0x0a, 0xbe, 0x9f, 0xd6, 0xc1, 0x67, 0xec, 0x7f, 0x01, 0x68, 0x55, 0x60, 0x40, + 0xb9, 0xd3, 0x34, 0xac, 0x84, 0xb8, 0x44, 0x85, 0x59, 0xc5, 0xf9, 0xe3, 0x3a, 0xaf, 0x9b, 0x96, 0x25, 0x06, 0xe5, + 0x67, 0xae, 0xe1, 0xc6, 0xf7, 0x1a, 0xf9, 0xe3, 0x7b, 0x2f, 0x41, 0xb7, 0x13, 0x69, 0xef, 0xdf, 0xcf, 0xef, 0x91, + 0x85, 0x86, 0xf7, 0xc2, 0x66, 0xd0, 0x16, 0xe9, 0x92, 0xab, 0x67, 0x2c, 0xc6, 0xdb, 0x22, 0x54, 0x86, 0x0f, 0x58, + 0x30, 0x03, 0x0c, 0xc1, 0x63, 0xa7, 0x32, 0xf9, 0x0c, 0x1b, 0x4d, 0xb1, 0x6b, 0x2e, 0x0c, 0x3e, 0x50, 0x95, 0x85, + 0xe4, 0xc5, 0x3a, 0xd9, 0x5e, 0x9c, 0xc3, 0xf3, 0xeb, 0xb8, 0x00, 0xea, 0x00, 0xfa, 0x15, 0x95, 0xc5, 0x06, 0x72, + 0x71, 0x53, 0xd6, 0x7a, 0x45, 0xa3, 0xd1, 0x8d, 0x5d, 0x58, 0x5d, 0x81, 0x4f, 0xa2, 0x74, 0x94, 0x88, 0x49, 0xcc, + 0xa4, 0xca, 0x15, 0xb9, 0x36, 0xba, 0x97, 0xb6, 0x68, 0x5e, 0x0a, 0x09, 0x5e, 0x11, 0xb8, 0x21, 0xf4, 0x95, 0xbe, + 0x5c, 0x6d, 0xa0, 0xe0, 0x51, 0x7b, 0x73, 0x11, 0x4c, 0x4c, 0x3c, 0x66, 0x48, 0x4d, 0xbf, 0x0e, 0xa7, 0x56, 0x16, + 0x4b, 0x0e, 0xbf, 0xce, 0x19, 0x6b, 0x28, 0x00, 0xe2, 0x93, 0x47, 0xeb, 0xdd, 0xa4, 0x37, 0x4a, 0x3b, 0x28, 0x8d, + 0x10, 0xdf, 0x55, 0xf8, 0xba, 0x0b, 0xc5, 0x57, 0xae, 0xba, 0xf7, 0x75, 0xcc, 0x8c, 0x0b, 0x46, 0x2f, 0xf9, 0x34, + 0x69, 0x5c, 0xbb, 0xa1, 0xbb, 0x3a, 0xdf, 0x7b, 0x5f, 0xca, 0xbc, 0x85, 0x63, 0x60, 0x93, 0x63, 0xe6, 0xbc, 0xf4, + 0xde, 0x1a, 0x27, 0xca, 0x3f, 0x98, 0x47, 0xbc, 0x72, 0x98, 0x55, 0x27, 0xc9, 0x3f, 0x0c, 0x7e, 0x08, 0xd6, 0xb7, + 0x34, 0x4e, 0x90, 0xbb, 0xea, 0x04, 0x99, 0x28, 0xb7, 0xa1, 0x37, 0xdc, 0xde, 0x5d, 0x05, 0x82, 0x38, 0x15, 0xd3, + 0x47, 0xe5, 0xb8, 0x7e, 0xb4, 0x40, 0xa5, 0x22, 0xe2, 0x73, 0x95, 0xbb, 0xb2, 0x36, 0x35, 0xd4, 0xe3, 0x3a, 0x99, + 0x85, 0xa6, 0x59, 0x91, 0x4b, 0xd9, 0xf4, 0x18, 0x99, 0x66, 0xa7, 0xda, 0xfc, 0xee, 0xda, 0x43, 0x3a, 0x86, 0xe6, + 0x62, 0xad, 0x16, 0xdc, 0xef, 0x2a, 0x0a, 0xef, 0x7a, 0xb1, 0x91, 0xca, 0x50, 0xb3, 0x1e, 0x45, 0x1f, 0xc7, 0x6d, + 0xe6, 0xf2, 0x28, 0xfb, 0xb3, 0x06, 0x80, 0xe9, 0x08, 0x8b, 0xee, 0xa6, 0x67, 0xec, 0x09, 0xf4, 0xf4, 0x44, 0x06, + 0x89, 0xde, 0xe8, 0x7c, 0xd5, 0x2a, 0xb1, 0x74, 0x05, 0x81, 0xdd, 0x1b, 0x32, 0x56, 0x25, 0xed, 0x96, 0xeb, 0x97, + 0xf3, 0x7c, 0x9e, 0xf3, 0xa5, 0x3c, 0x9f, 0x9a, 0x45, 0x77, 0xaf, 0xed, 0xde, 0x9c, 0x1a, 0x2a, 0xe6, 0x5a, 0xdd, + 0xe4, 0x37, 0x4c, 0xd7, 0xc1, 0x50, 0x8b, 0x20, 0xb3, 0xda, 0x55, 0x2f, 0xca, 0x72, 0xa3, 0x9e, 0xc9, 0xb1, 0x21, + 0x7c, 0x53, 0xe9, 0x0e, 0xd1, 0x0d, 0x53, 0x35, 0xd3, 0xf7, 0x8d, 0x6d, 0x21, 0xdb, 0xbc, 0xbc, 0x1a, 0xe5, 0x40, + 0x69, 0xb9, 0xbf, 0x4c, 0x18, 0xbe, 0xbf, 0xbe, 0xfe, 0x5e, 0xc8, 0xa9, 0xaa, 0xa3, 0xb7, 0x78, 0xad, 0x7b, 0x06, + 0x1b, 0xa5, 0x72, 0x22, 0x2e, 0xd8, 0xea, 0xc1, 0x9b, 0xbb, 0x57, 0xc0, 0x72, 0x01, 0xd8, 0x5d, 0x30, 0xa7, 0x31, + 0x54, 0xb5, 0x81, 0xbf, 0x5c, 0x3d, 0xd8, 0xaa, 0x3d, 0xfc, 0xe5, 0xe0, 0xcb, 0xe0, 0xc6, 0xc6, 0xc6, 0x36, 0xde, + 0xae, 0x25, 0x82, 0xbc, 0xc1, 0x03, 0x7d, 0xbc, 0xfa, 0x28, 0x68, 0xb9, 0x4a, 0x6c, 0x0f, 0x1c, 0x0a, 0x5b, 0x83, + 0x7c, 0x93, 0x32, 0x69, 0x38, 0x2f, 0x78, 0x36, 0x95, 0x33, 0x14, 0xf2, 0x9a, 0x8f, 0x83, 0xb6, 0x23, 0xfc, 0x1b, + 0x38, 0xb5, 0xe3, 0xe5, 0xc5, 0x27, 0xe8, 0x03, 0x9e, 0xae, 0x94, 0xa6, 0x22, 0x4e, 0x29, 0xb7, 0xe8, 0x72, 0x9d, + 0x07, 0x23, 0xc5, 0xc5, 0x04, 0x95, 0x8e, 0xbb, 0xb8, 0x71, 0x36, 0x72, 0xfa, 0x4b, 0xbc, 0xba, 0x48, 0x97, 0x8f, + 0x44, 0xb6, 0x6a, 0xe9, 0xfd, 0xac, 0x4f, 0xb7, 0xed, 0x29, 0xe3, 0x93, 0x6c, 0x44, 0x07, 0x33, 0x3e, 0x4e, 0x84, + 0xd7, 0x27, 0x46, 0xfa, 0x6e, 0x11, 0x98, 0x6e, 0x8e, 0x4d, 0x7e, 0x38, 0x5e, 0x6f, 0x36, 0x6b, 0xdc, 0xc1, 0x3b, + 0xe7, 0x93, 0xb3, 0x28, 0x31, 0xa2, 0xb2, 0xd0, 0xf0, 0x80, 0x56, 0x88, 0x9b, 0xf7, 0x4c, 0x60, 0x5c, 0x76, 0x45, + 0x52, 0xdb, 0x0d, 0x04, 0x2e, 0xf6, 0x38, 0x66, 0xc9, 0xc8, 0xf6, 0xa0, 0x3c, 0xd0, 0x17, 0xa3, 0xe9, 0x16, 0x30, + 0x2d, 0xaf, 0x9d, 0x5d, 0xa4, 0xb6, 0x57, 0x4d, 0x15, 0xc0, 0x2c, 0x59, 0x1e, 0x9f, 0x21, 0xeb, 0x7e, 0x0d, 0x5d, + 0xc4, 0x80, 0xb1, 0x71, 0x65, 0xce, 0x5d, 0xac, 0x5a, 0x11, 0xdf, 0x68, 0x22, 0x4d, 0xea, 0x43, 0xea, 0x7b, 0x14, + 0xd6, 0xea, 0x2a, 0x07, 0x09, 0xdc, 0x23, 0xef, 0x8e, 0xb8, 0xf4, 0xf4, 0x99, 0xc5, 0xb8, 0x4a, 0xdf, 0x52, 0xd7, + 0xe2, 0x9a, 0x61, 0xaf, 0x78, 0x00, 0xf6, 0x07, 0xc6, 0x2d, 0x62, 0x11, 0x6f, 0xe7, 0xb5, 0x14, 0xd6, 0xc6, 0x1c, + 0x68, 0x6e, 0xb8, 0xc1, 0xcf, 0xac, 0x5a, 0x33, 0x30, 0xc3, 0x8c, 0x33, 0x92, 0x0f, 0xc6, 0xbd, 0xaa, 0xb1, 0x23, + 0x57, 0x01, 0x44, 0xdf, 0x82, 0x2e, 0xc9, 0xe1, 0x95, 0x2c, 0x57, 0x9d, 0x21, 0xbf, 0x82, 0x75, 0xd6, 0x8b, 0x13, + 0x30, 0x93, 0xa6, 0xbc, 0xc4, 0xc4, 0x14, 0x71, 0xb9, 0x59, 0xc6, 0x3c, 0x4d, 0x9f, 0x45, 0x3b, 0x38, 0xb9, 0x91, + 0xc0, 0x11, 0xfb, 0xc6, 0x32, 0x34, 0x13, 0x36, 0x62, 0x22, 0x8d, 0x4a, 0x29, 0xe1, 0x03, 0xb9, 0xd4, 0x92, 0xbf, + 0xcc, 0xe5, 0xd5, 0x97, 0xdb, 0x04, 0x07, 0xe4, 0x35, 0xb0, 0x1c, 0x1a, 0xc7, 0x2d, 0x03, 0x89, 0x58, 0x0c, 0x88, + 0x51, 0xab, 0x72, 0x39, 0x19, 0xd5, 0xc9, 0x7c, 0x85, 0x5c, 0xa8, 0xc8, 0x83, 0x5b, 0x02, 0x25, 0x7f, 0x8e, 0xa9, + 0x83, 0x59, 0xa9, 0xdd, 0xb4, 0xd8, 0x24, 0x79, 0xcf, 0x0c, 0x48, 0xae, 0xbe, 0x86, 0x87, 0xc6, 0x2f, 0x5e, 0x99, + 0x53, 0xc2, 0x17, 0x65, 0x2c, 0x2d, 0x8d, 0xb9, 0xf4, 0xdf, 0xca, 0xfb, 0xb4, 0x12, 0xb0, 0x57, 0x20, 0xa6, 0x0c, + 0x5c, 0x62, 0xe3, 0x82, 0xa4, 0xbc, 0x96, 0xa7, 0xec, 0xbe, 0x86, 0xf2, 0x5d, 0x32, 0xe9, 0x2a, 0x95, 0xb5, 0xc6, + 0xaa, 0xfb, 0x79, 0xce, 0xf2, 0xab, 0x7d, 0x86, 0xb9, 0xc9, 0x68, 0x90, 0x2d, 0x99, 0xd9, 0x94, 0x5f, 0xed, 0xdd, + 0xf8, 0x95, 0x87, 0x92, 0x0e, 0xd5, 0x2a, 0xdd, 0xbc, 0x74, 0xc3, 0x31, 0x6e, 0xdc, 0x70, 0x04, 0xb0, 0x31, 0xec, + 0x54, 0x91, 0x5a, 0xe7, 0xbf, 0x2f, 0x87, 0x9f, 0x68, 0xaf, 0x1d, 0xe9, 0x5d, 0x77, 0xb4, 0x32, 0x3d, 0xfd, 0x06, + 0x54, 0x8d, 0x2c, 0xa1, 0x9b, 0x50, 0xc5, 0x64, 0x24, 0x4a, 0x4c, 0x57, 0x29, 0x8f, 0xfa, 0x1a, 0x71, 0x0e, 0xe2, + 0x86, 0xf2, 0x17, 0xff, 0x14, 0x5e, 0x9d, 0x04, 0x68, 0x44, 0x2d, 0xc6, 0x59, 0xca, 0x5b, 0xe3, 0x68, 0x1a, 0x27, + 0x57, 0xc1, 0x3c, 0x6e, 0x4d, 0xb3, 0x34, 0x2b, 0x66, 0xc0, 0x95, 0x5e, 0x71, 0x05, 0x36, 0xfc, 0xb4, 0x35, 0x8f, + 0xbd, 0x97, 0x2c, 0x39, 0x67, 0x3c, 0x1e, 0x46, 0x9e, 0xbd, 0x97, 0x83, 0x78, 0xb0, 0xde, 0x46, 0x79, 0x9e, 0x5d, + 0xd8, 0xde, 0x87, 0xec, 0x14, 0x98, 0xd6, 0x7b, 0x77, 0x79, 0x75, 0xc6, 0x52, 0xef, 0xe3, 0xe9, 0x3c, 0xe5, 0x73, + 0xaf, 0x88, 0xd2, 0xa2, 0x55, 0xb0, 0x3c, 0x1e, 0x83, 0x9a, 0x48, 0xb2, 0xbc, 0x85, 0xf9, 0xcf, 0x53, 0x16, 0x24, + 0xf1, 0xd9, 0x84, 0x5b, 0xa3, 0x28, 0xff, 0xd4, 0x6b, 0xb5, 0x66, 0x79, 0x3c, 0x8d, 0xf2, 0xab, 0x16, 0xb5, 0x08, + 0x3e, 0x6b, 0x6f, 0x47, 0x9f, 0x8f, 0x1f, 0xf6, 0x78, 0x0e, 0x7d, 0x63, 0xa4, 0x62, 0x00, 0xc2, 0xc7, 0xda, 0xde, + 0x69, 0x4f, 0x8b, 0x7b, 0xe2, 0x44, 0x29, 0x4a, 0x79, 0x79, 0xe2, 0x5d, 0x31, 0x80, 0xdb, 0x3f, 0xe5, 0xa9, 0x07, + 0xbe, 0x1c, 0xcf, 0xd2, 0xc5, 0x70, 0x9e, 0x17, 0x30, 0xc0, 0x2c, 0x8b, 0x53, 0xce, 0xf2, 0xde, 0x69, 0x96, 0x03, + 0xd9, 0x5a, 0x79, 0x34, 0x8a, 0xe7, 0x45, 0xf0, 0x70, 0x76, 0xd9, 0x43, 0x5b, 0xe1, 0x2c, 0xcf, 0xe6, 0xe9, 0x48, + 0xce, 0x15, 0xa7, 0xb0, 0x31, 0x62, 0x6e, 0x56, 0xd0, 0x97, 0x50, 0x00, 0xbe, 0x94, 0x45, 0x79, 0xeb, 0x0c, 0x3b, + 0xa3, 0xa1, 0xdf, 0x1e, 0xb1, 0x33, 0x2f, 0x3f, 0x3b, 0x8d, 0x9c, 0x4e, 0xf7, 0xb1, 0xa7, 0xfe, 0xf3, 0x77, 0x5c, + 0x30, 0xdc, 0x57, 0x16, 0x77, 0xda, 0xed, 0xbf, 0x71, 0x7b, 0x8d, 0x59, 0x08, 0xa0, 0xa0, 0x33, 0xbb, 0xb4, 0x8a, + 0x2c, 0x81, 0xf5, 0x59, 0xd5, 0xb3, 0x37, 0x03, 0xbf, 0x29, 0x4e, 0xcf, 0x82, 0xee, 0xec, 0xb2, 0x44, 0xec, 0x02, + 0x91, 0x90, 0x29, 0x91, 0x94, 0x6f, 0x8b, 0xdf, 0x0a, 0xf1, 0x93, 0xd5, 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd5, 0x5b, + 0x23, 0xd8, 0x07, 0x44, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x80, 0x13, 0x98, 0x2b, 0x38, 0xe8, 0xe5, 0x37, 0x83, 0xd1, + 0x5d, 0x0d, 0xc6, 0x93, 0xdb, 0xc0, 0xc8, 0xd3, 0xd1, 0xa2, 0xbe, 0xae, 0x1d, 0x70, 0x4e, 0x7b, 0x13, 0x86, 0xfc, + 0x14, 0x74, 0xf1, 0xf9, 0x22, 0x1e, 0xf1, 0x89, 0x78, 0x24, 0x76, 0xbe, 0x10, 0x75, 0x3b, 0xed, 0xb6, 0x78, 0x2f, + 0x40, 0xa1, 0x05, 0x1d, 0x1f, 0x1b, 0x00, 0x13, 0x7d, 0xb1, 0xee, 0x23, 0x36, 0xdf, 0xdd, 0xfa, 0xa5, 0x1a, 0x8f, + 0xa9, 0xbc, 0x41, 0xa1, 0x22, 0xd4, 0x37, 0x5b, 0x30, 0xe3, 0x2d, 0xef, 0x77, 0xf4, 0x41, 0xd5, 0xe0, 0x3b, 0x46, + 0x5a, 0x2f, 0xe0, 0x9e, 0x99, 0x0b, 0xd4, 0x4b, 0xfb, 0x18, 0x92, 0x6a, 0xb5, 0x5c, 0xd0, 0x1b, 0x0c, 0x43, 0x48, + 0x74, 0x20, 0xe8, 0xe4, 0x83, 0x82, 0xbe, 0xa9, 0x91, 0xb9, 0x41, 0xe1, 0x64, 0x2e, 0x6c, 0xf9, 0x4c, 0xcb, 0x75, + 0x50, 0xd2, 0xe0, 0x65, 0x7f, 0xc1, 0x64, 0x03, 0x90, 0xde, 0x95, 0xa4, 0xe5, 0xd5, 0xd1, 0x93, 0x72, 0xf9, 0xb2, + 0x21, 0x51, 0x0e, 0x7c, 0x7d, 0x3e, 0x41, 0xbf, 0x5b, 0x7f, 0x28, 0xc6, 0x48, 0xa9, 0xd9, 0xb2, 0xdd, 0x01, 0xd3, + 0x59, 0x59, 0x98, 0x7d, 0xc6, 0x4a, 0x1c, 0xe5, 0x2b, 0xb0, 0xa4, 0x31, 0xf4, 0xfa, 0x73, 0x28, 0xdc, 0x34, 0xe5, + 0xa4, 0x6d, 0xdc, 0x74, 0xfd, 0x1f, 0x56, 0x3c, 0xa6, 0x6c, 0x67, 0x15, 0x1b, 0x07, 0xd7, 0xe5, 0x78, 0x28, 0xae, + 0x1d, 0x16, 0x98, 0x2d, 0xfe, 0xdb, 0x3d, 0x09, 0x47, 0xa3, 0x55, 0x64, 0xf3, 0x7c, 0x48, 0xa1, 0xc1, 0xe5, 0x10, + 0x83, 0x4d, 0x1a, 0xde, 0xf6, 0x98, 0x56, 0x2c, 0xe8, 0x77, 0xd7, 0xbe, 0xaa, 0xc0, 0xe9, 0xd4, 0x45, 0x5c, 0x6a, + 0x90, 0x61, 0x15, 0x05, 0x36, 0xea, 0xca, 0x11, 0x25, 0xd8, 0xd1, 0x85, 0x4f, 0x7f, 0x9e, 0xc6, 0x20, 0x5a, 0x8f, + 0xe3, 0x11, 0x5d, 0x74, 0x89, 0x47, 0x74, 0xf2, 0xd1, 0xa2, 0x4c, 0x27, 0x0c, 0xa5, 0x43, 0x81, 0x24, 0x38, 0x3e, + 0xcb, 0xcc, 0x19, 0xbb, 0x65, 0xe3, 0xe9, 0x85, 0xa1, 0x9b, 0x47, 0xd9, 0x34, 0x8a, 0xd3, 0x00, 0x3f, 0x48, 0xe2, + 0xe9, 0x11, 0x03, 0xec, 0xe2, 0xc1, 0x5f, 0x45, 0xfb, 0x8e, 0xeb, 0xff, 0x04, 0x82, 0x8b, 0xfa, 0x97, 0xd2, 0xf1, + 0xd3, 0x70, 0xa9, 0x73, 0xe5, 0x7a, 0x29, 0x08, 0x3b, 0xae, 0x8c, 0x64, 0x46, 0x81, 0x95, 0x5d, 0x4e, 0x7f, 0x06, + 0xad, 0x4e, 0xa0, 0xae, 0xfe, 0x9b, 0x2b, 0x60, 0x5c, 0x0c, 0xa8, 0x56, 0x85, 0x4a, 0xe4, 0x1b, 0xcc, 0x21, 0xf9, + 0xf3, 0xfa, 0x5a, 0x7f, 0x3c, 0xa0, 0x71, 0x81, 0x56, 0xa4, 0xdf, 0xc8, 0x4b, 0x98, 0x84, 0x85, 0x7e, 0x16, 0x98, + 0x56, 0xef, 0x1a, 0x5b, 0x4f, 0x6e, 0x25, 0x8c, 0x39, 0x9d, 0xa5, 0x4e, 0x0d, 0x0d, 0x3a, 0xbe, 0x58, 0x33, 0x95, + 0x5b, 0x46, 0xc4, 0xdc, 0x4f, 0x49, 0xe6, 0xd4, 0xaf, 0x3f, 0xe1, 0x55, 0xa7, 0x7a, 0xd6, 0x96, 0x62, 0xef, 0xe1, + 0xc9, 0xae, 0x10, 0x52, 0x16, 0xb1, 0x6e, 0x68, 0x83, 0xd4, 0xb0, 0xad, 0x3f, 0x0e, 0x81, 0xce, 0x9f, 0x42, 0x7b, + 0x63, 0xe1, 0xa8, 0xbb, 0x00, 0x39, 0xcc, 0xb5, 0x27, 0x14, 0x35, 0x7d, 0x44, 0xc0, 0xee, 0x6f, 0x2c, 0x78, 0xb9, + 0xbb, 0x25, 0x7a, 0xf7, 0x4f, 0xca, 0x82, 0x74, 0xaa, 0x19, 0xfb, 0xab, 0xa6, 0x10, 0x75, 0x30, 0x2c, 0x65, 0x1c, + 0xe3, 0xb8, 0xb9, 0xb6, 0x13, 0x45, 0x90, 0x5b, 0x32, 0x6e, 0x81, 0x19, 0x56, 0x51, 0x0e, 0x62, 0x44, 0xe7, 0xd0, + 0x14, 0x22, 0x6d, 0xa4, 0xb7, 0x0c, 0xc5, 0x09, 0x42, 0x30, 0xd8, 0x58, 0xc4, 0x65, 0xb8, 0xb1, 0x60, 0xe9, 0x30, + 0x1b, 0xb1, 0x8f, 0x1f, 0x5e, 0xe1, 0x35, 0x89, 0x2c, 0x45, 0x79, 0x9a, 0xb9, 0xe5, 0x09, 0x18, 0x58, 0x08, 0x69, + 0xae, 0xbe, 0x52, 0x03, 0xc0, 0x88, 0x58, 0x91, 0x45, 0xa3, 0x22, 0x28, 0xac, 0xb4, 0xad, 0x81, 0x80, 0x10, 0x1c, + 0x59, 0x2c, 0x00, 0x13, 0x94, 0x7a, 0x31, 0xc0, 0x4f, 0xb4, 0xee, 0xc3, 0x40, 0xbb, 0x5b, 0xa2, 0x11, 0xe0, 0x9a, + 0x23, 0x1a, 0x15, 0xaa, 0x98, 0x55, 0x64, 0xa2, 0x3b, 0x8a, 0xcf, 0x35, 0x39, 0x29, 0xc5, 0xba, 0xbf, 0x9b, 0x44, + 0xa7, 0x2c, 0x81, 0x21, 0x81, 0xaf, 0xda, 0x30, 0x92, 0x78, 0xb5, 0x76, 0xe3, 0x74, 0x36, 0x97, 0x5f, 0x0b, 0x83, + 0x89, 0x3b, 0x78, 0x80, 0x8b, 0x97, 0x19, 0x06, 0xea, 0x44, 0x32, 0x90, 0x03, 0x00, 0x88, 0x74, 0x18, 0x82, 0xd0, + 0x55, 0xac, 0x02, 0xa5, 0xf1, 0x68, 0xb9, 0x0c, 0xf6, 0xf7, 0x0c, 0x4b, 0x53, 0x78, 0x9e, 0xc6, 0x29, 0x3e, 0x16, + 0xf8, 0x18, 0x5d, 0xe2, 0x63, 0x06, 0x8f, 0x1a, 0xf7, 0xbc, 0xb4, 0xff, 0xaa, 0xab, 0x92, 0xc9, 0x15, 0xb0, 0x34, + 0x01, 0xb2, 0xeb, 0x6b, 0x50, 0x5b, 0x9a, 0x04, 0xbb, 0x5b, 0x40, 0x2c, 0xe4, 0x1e, 0xf1, 0xed, 0x18, 0x66, 0x92, + 0x91, 0x15, 0xb3, 0x96, 0x28, 0xb7, 0xc8, 0x38, 0x08, 0xc1, 0x77, 0xcc, 0x9d, 0x86, 0x0d, 0xe4, 0xc9, 0x2c, 0x99, + 0x67, 0xf8, 0xe2, 0xda, 0x96, 0xf8, 0xb8, 0x87, 0x20, 0x0a, 0x3d, 0x22, 0x86, 0xba, 0x8c, 0xcb, 0xcf, 0xf6, 0xc4, + 0xa1, 0x8d, 0xb3, 0x80, 0x19, 0x8a, 0xca, 0x8c, 0x47, 0x71, 0x22, 0x1a, 0xaf, 0xc0, 0xa7, 0x91, 0xee, 0x48, 0xe8, + 0xec, 0x6e, 0x55, 0xb0, 0x01, 0xf0, 0x4a, 0x22, 0x88, 0x54, 0x4e, 0x5b, 0x94, 0x53, 0x0a, 0x80, 0xdc, 0xe6, 0xd5, + 0x27, 0x9d, 0x80, 0x29, 0xc0, 0x88, 0x1e, 0x1d, 0xd3, 0x6c, 0x83, 0x21, 0x12, 0x0b, 0x67, 0x6c, 0x6c, 0x5d, 0xfb, + 0x2f, 0xff, 0xfc, 0x0f, 0xb6, 0x27, 0x40, 0xcc, 0xc6, 0x63, 0x90, 0x72, 0xd6, 0xba, 0x86, 0xff, 0xeb, 0x1f, 0xff, + 0xef, 0xff, 0xf9, 0xaf, 0xba, 0x6d, 0x0a, 0x4d, 0x4f, 0x02, 0x71, 0xb4, 0xa0, 0x49, 0x4a, 0x29, 0x9e, 0xf6, 0x38, + 0x4a, 0x57, 0x80, 0x74, 0x08, 0x54, 0x9a, 0x31, 0x36, 0xf2, 0x6c, 0x0b, 0x34, 0x81, 0x78, 0x3e, 0x4e, 0xd8, 0x39, + 0x93, 0x1f, 0x96, 0xd1, 0x83, 0xe8, 0xca, 0x21, 0x58, 0x30, 0x5c, 0xde, 0x79, 0x95, 0xdb, 0x40, 0xd1, 0x52, 0x52, + 0xbc, 0x4e, 0x30, 0xcf, 0x36, 0x06, 0x6d, 0xce, 0xd1, 0xae, 0x0f, 0xeb, 0x81, 0x4a, 0xb5, 0x6d, 0x01, 0x2f, 0x99, + 0xbd, 0xab, 0x20, 0x5e, 0x82, 0xeb, 0x34, 0xc7, 0xa6, 0x29, 0x2b, 0x8a, 0x55, 0x60, 0x01, 0x4d, 0x3c, 0xbb, 0x6a, + 0x62, 0xd7, 0x3a, 0x00, 0x00, 0xdd, 0x9d, 0x1d, 0x31, 0x2d, 0x54, 0xb0, 0xf1, 0x18, 0x36, 0x38, 0xea, 0xb6, 0x84, + 0xe3, 0xb1, 0x45, 0xd8, 0xb7, 0xdf, 0x82, 0x2c, 0xb1, 0xc1, 0x3f, 0x74, 0xf5, 0x01, 0x34, 0x4d, 0xaf, 0x84, 0x9d, + 0x31, 0x87, 0xe8, 0x6c, 0x0c, 0xa3, 0x9f, 0x0c, 0xa4, 0xb2, 0xe1, 0xa7, 0x55, 0x8c, 0xb1, 0x96, 0x11, 0xfe, 0xfd, + 0x5f, 0xfe, 0xf1, 0xbf, 0xc1, 0xd8, 0xd4, 0x6f, 0x3d, 0x17, 0x40, 0xab, 0xff, 0x09, 0xad, 0xe6, 0xe9, 0x2d, 0xed, + 0xfe, 0xf2, 0xf7, 0xff, 0x1d, 0x9a, 0xd1, 0x45, 0x29, 0xe0, 0x13, 0x82, 0x68, 0x88, 0xb6, 0xe9, 0xaf, 0x02, 0xa9, + 0x36, 0xc8, 0xda, 0x99, 0xfe, 0x09, 0xc1, 0x2e, 0x78, 0x36, 0xbb, 0x11, 0x1c, 0x84, 0x7a, 0x98, 0x64, 0x05, 0xd3, + 0xf0, 0x08, 0x7d, 0xf2, 0xeb, 0x00, 0xa2, 0xb9, 0x66, 0xb0, 0x6b, 0x0b, 0x4b, 0x8f, 0x23, 0x56, 0x68, 0xd5, 0x38, + 0x8d, 0x05, 0x2c, 0x18, 0x27, 0x74, 0x28, 0xdc, 0x03, 0x4b, 0x26, 0x9e, 0xe0, 0x81, 0x04, 0x9c, 0x5b, 0xff, 0xf8, + 0xda, 0xea, 0xc1, 0x34, 0xc3, 0x89, 0xb1, 0x44, 0x84, 0x4b, 0x8d, 0x00, 0x7f, 0x41, 0x08, 0x1f, 0xeb, 0xe7, 0xe8, + 0x52, 0x3f, 0xa3, 0xa0, 0x16, 0x13, 0x80, 0xbe, 0x9d, 0xa2, 0x31, 0x66, 0xce, 0x20, 0xb2, 0x33, 0x2a, 0xf7, 0xde, + 0x48, 0xf2, 0x11, 0xc2, 0xf8, 0x18, 0x73, 0x61, 0xf1, 0xe6, 0xd3, 0x3c, 0x67, 0xc7, 0x49, 0x76, 0x81, 0x31, 0x43, + 0x24, 0xd2, 0xba, 0xfa, 0xf2, 0xdf, 0xfe, 0xd5, 0xf7, 0xff, 0xed, 0x5f, 0xd7, 0x34, 0x98, 0xc0, 0x9e, 0x00, 0x23, + 0x9f, 0x87, 0x9a, 0xce, 0x0d, 0xb4, 0x56, 0x0f, 0x8a, 0x78, 0xae, 0xae, 0x91, 0x88, 0x63, 0xa9, 0xc4, 0x5b, 0x3e, + 0x12, 0xda, 0x9a, 0x29, 0x6e, 0x9f, 0x05, 0x21, 0x5b, 0x33, 0x0d, 0x56, 0xdd, 0x32, 0xcf, 0x89, 0x1b, 0xdc, 0x40, + 0x97, 0x5f, 0x89, 0xf1, 0x6a, 0x30, 0x6e, 0x85, 0xc0, 0x03, 0x6d, 0x26, 0xf4, 0xdd, 0x33, 0xa1, 0xad, 0x02, 0xb1, + 0x0c, 0x52, 0x77, 0xd5, 0x00, 0xf2, 0xac, 0x03, 0x9a, 0x80, 0x9a, 0xc4, 0x95, 0xad, 0x40, 0xe6, 0xd6, 0x69, 0xde, + 0x7f, 0x83, 0x97, 0x1d, 0x2d, 0xec, 0x8d, 0x96, 0x42, 0x41, 0x86, 0x0d, 0x27, 0xc3, 0x46, 0x6a, 0x54, 0xd3, 0xa6, + 0x40, 0xc7, 0x2f, 0x5b, 0x6d, 0x3b, 0x1c, 0x63, 0xf7, 0x9a, 0xf6, 0xe7, 0x52, 0xfb, 0xc7, 0xd2, 0xde, 0x97, 0xda, + 0x1f, 0x3f, 0x69, 0xd3, 0xd0, 0xfe, 0xf1, 0x5a, 0xed, 0x8f, 0x94, 0x1b, 0xe0, 0xc8, 0xa1, 0xbd, 0x89, 0xd1, 0x2d, + 0xc3, 0xd6, 0xe0, 0x68, 0x67, 0x0d, 0x27, 0x6c, 0xf8, 0x49, 0x9a, 0x59, 0x84, 0x00, 0x86, 0x77, 0xb4, 0x31, 0x29, + 0x30, 0x00, 0x93, 0xe1, 0xa4, 0xd4, 0x9b, 0x1e, 0x1f, 0x8d, 0x09, 0xb8, 0xbb, 0x18, 0x33, 0x14, 0xfd, 0xb0, 0x66, + 0x5f, 0xb1, 0x72, 0x0b, 0xc7, 0x11, 0x1b, 0x46, 0x3c, 0x03, 0x66, 0x5b, 0x38, 0xd8, 0x89, 0xb7, 0x10, 0xc1, 0xc2, + 0xc0, 0x7e, 0xff, 0x6e, 0xff, 0xc0, 0xf6, 0x4e, 0xb3, 0xd1, 0x55, 0x60, 0x83, 0x33, 0x06, 0xd6, 0x94, 0xeb, 0xf3, + 0x09, 0x4b, 0x1d, 0xe5, 0xf9, 0x64, 0x09, 0xb8, 0x9a, 0xd9, 0x99, 0xf8, 0xb6, 0x45, 0xf3, 0xa0, 0x03, 0x08, 0x4b, + 0x1f, 0xbf, 0xec, 0xef, 0x72, 0xf1, 0x5d, 0x58, 0x9e, 0xe3, 0x63, 0x1f, 0x53, 0x3d, 0x76, 0xb7, 0xe0, 0x01, 0x5f, + 0xf6, 0x51, 0xef, 0xd1, 0xdb, 0xc6, 0x62, 0xc9, 0x6d, 0x18, 0xe0, 0x10, 0x93, 0xbe, 0x40, 0xa1, 0xa0, 0x56, 0x27, + 0x01, 0x22, 0x06, 0x8f, 0x30, 0xd6, 0x96, 0x1a, 0x17, 0x21, 0x54, 0xfd, 0xb5, 0xe3, 0x52, 0xd9, 0xad, 0x34, 0xef, + 0x08, 0xcd, 0x52, 0x72, 0x5c, 0xb0, 0xf7, 0x48, 0x97, 0x08, 0x53, 0x87, 0x8a, 0xd6, 0x41, 0xa0, 0x6b, 0x2a, 0x73, + 0x45, 0x74, 0x30, 0x80, 0x21, 0x33, 0x57, 0x00, 0x02, 0x7f, 0x09, 0xed, 0x13, 0xf3, 0xfb, 0x6f, 0xe2, 0x53, 0x4d, + 0x9a, 0x38, 0x87, 0x7f, 0xf2, 0xae, 0x98, 0x77, 0x75, 0x42, 0x2d, 0x55, 0xb0, 0x01, 0xa3, 0x60, 0x18, 0x94, 0x69, + 0xab, 0xa8, 0x12, 0xd8, 0x69, 0x49, 0x34, 0x2b, 0x58, 0xa0, 0x1e, 0x64, 0xdc, 0x01, 0xc3, 0x17, 0xcb, 0x81, 0x1e, + 0xd3, 0x9e, 0x2b, 0xf9, 0x64, 0x61, 0x06, 0x26, 0x1e, 0xb5, 0xdb, 0x3d, 0xbc, 0x54, 0xd1, 0x8a, 0xc0, 0x3a, 0x48, + 0x83, 0x84, 0x8d, 0x79, 0xc9, 0xf1, 0xd6, 0xfe, 0x42, 0x45, 0x82, 0xfc, 0xee, 0x4e, 0xce, 0xa6, 0x96, 0x8f, 0xff, + 0xbf, 0x6d, 0xec, 0x51, 0x90, 0xf2, 0x49, 0x8b, 0xae, 0xf1, 0xe0, 0x15, 0x49, 0x80, 0xc8, 0x7c, 0x5f, 0x18, 0x13, + 0x0d, 0x19, 0x46, 0xc9, 0x4a, 0x9e, 0x83, 0xbc, 0xf7, 0x78, 0x6e, 0xb6, 0x03, 0x39, 0xbd, 0x14, 0x2a, 0x5b, 0x0e, + 0xd6, 0x6c, 0xbb, 0xd2, 0x3f, 0x5a, 0x6e, 0xac, 0x22, 0x5e, 0xf5, 0xb7, 0x25, 0x0a, 0x19, 0xb1, 0xb9, 0x52, 0xa8, + 0xa8, 0x85, 0xe8, 0x61, 0xe2, 0xb4, 0x1c, 0xb5, 0xbb, 0xd5, 0x62, 0x2e, 0x49, 0x5c, 0x1c, 0x92, 0xb8, 0x20, 0xf1, + 0x77, 0xb4, 0x10, 0x73, 0x0f, 0xa3, 0x64, 0xe8, 0x20, 0x00, 0x56, 0xcb, 0x7a, 0x02, 0xd4, 0x74, 0x55, 0xe4, 0xc8, + 0x7f, 0x8c, 0xc4, 0x2d, 0x85, 0xb0, 0x5c, 0x41, 0xa5, 0x93, 0xa3, 0xb2, 0xec, 0x31, 0xe6, 0x1c, 0x7e, 0x90, 0x97, + 0x40, 0xc4, 0xdd, 0x5f, 0xfd, 0xfd, 0xc4, 0x76, 0xe9, 0x1e, 0x79, 0x3f, 0x1b, 0x1f, 0xa5, 0xb3, 0x15, 0xb3, 0xdb, + 0x1e, 0x2c, 0x83, 0xd9, 0x53, 0x7e, 0x42, 0xf2, 0xa6, 0xbe, 0x26, 0x9b, 0x53, 0xff, 0x9f, 0x43, 0x1c, 0xe1, 0x8d, + 0x63, 0xa3, 0x89, 0x4e, 0x23, 0x5f, 0xb5, 0x88, 0x3f, 0x6d, 0xec, 0x2a, 0x8e, 0x40, 0xbe, 0x5e, 0x17, 0xc9, 0xfa, + 0xe6, 0xf6, 0x48, 0x56, 0x71, 0xc7, 0x48, 0xd6, 0x37, 0xbf, 0x73, 0x24, 0xeb, 0x6b, 0x33, 0x92, 0x85, 0x02, 0xfa, + 0xd5, 0xaf, 0x89, 0x36, 0xe5, 0xd9, 0x45, 0x11, 0x76, 0x64, 0xe6, 0x04, 0xc8, 0x3a, 0x0c, 0x3b, 0xfd, 0xf5, 0x23, + 0x4c, 0x30, 0x51, 0x23, 0xbe, 0x44, 0x01, 0x25, 0x91, 0xec, 0x09, 0x6a, 0x45, 0x86, 0x73, 0xda, 0x3a, 0xab, 0xb2, + 0xf5, 0x50, 0x5d, 0x23, 0x03, 0xd7, 0xd7, 0xd5, 0xa1, 0xb6, 0xae, 0x0a, 0xf8, 0x04, 0xf4, 0x1d, 0x58, 0xdd, 0xb1, + 0xbb, 0xa9, 0xd2, 0xf9, 0xcc, 0x11, 0x7a, 0xea, 0x94, 0x46, 0x30, 0xd1, 0xc2, 0xfe, 0x2f, 0x87, 0x9d, 0xde, 0x76, + 0x67, 0x0a, 0xbd, 0x41, 0x81, 0xc3, 0x5b, 0xbb, 0xb7, 0xbd, 0x8d, 0x6f, 0x17, 0xea, 0xad, 0x8b, 0x6f, 0xb1, 0x7a, + 0xdb, 0xc1, 0xb7, 0xa1, 0x7a, 0x7b, 0x84, 0x6f, 0x23, 0xf5, 0xf6, 0x18, 0xdf, 0xce, 0xed, 0xf2, 0x90, 0x6b, 0xe0, + 0x1e, 0x03, 0x5f, 0x91, 0x37, 0x13, 0xa8, 0x32, 0xd8, 0xf4, 0x78, 0xfd, 0x32, 0x3a, 0x0b, 0x62, 0x4f, 0x78, 0x97, + 0x41, 0xee, 0x5d, 0x80, 0xc6, 0x09, 0x28, 0xdb, 0xf0, 0x39, 0x7e, 0x87, 0x03, 0x9c, 0xa4, 0x83, 0x78, 0xca, 0xd4, + 0x07, 0x89, 0x15, 0xd6, 0x60, 0xc0, 0x1e, 0xb6, 0x8f, 0xca, 0x9e, 0x5e, 0x27, 0x11, 0xcf, 0x52, 0xd9, 0x1c, 0xb4, + 0x72, 0x55, 0x9d, 0x98, 0xae, 0xa5, 0x57, 0x78, 0x8d, 0xfe, 0x32, 0xe2, 0x11, 0x63, 0x30, 0xcc, 0x5a, 0x97, 0xe0, + 0xc1, 0xae, 0xd4, 0x69, 0x08, 0x91, 0xd6, 0x69, 0x84, 0x93, 0x7e, 0x3b, 0x88, 0xce, 0xf4, 0xf3, 0x1b, 0xb0, 0xb4, + 0xa3, 0x33, 0xd9, 0x72, 0xbd, 0x0e, 0x23, 0x10, 0x4d, 0xfd, 0xa5, 0x80, 0x20, 0x53, 0x0c, 0x96, 0x06, 0x3d, 0x69, + 0xa9, 0xbf, 0x90, 0x3a, 0x75, 0x8d, 0x46, 0xd3, 0xd7, 0x8b, 0x80, 0xa2, 0x55, 0xc1, 0x2e, 0x18, 0xfc, 0x54, 0x2a, + 0x28, 0x0c, 0x15, 0x58, 0x20, 0xaa, 0xd7, 0xa8, 0x32, 0x1d, 0x6c, 0x58, 0xab, 0xd0, 0x2c, 0xa5, 0xcb, 0xcc, 0xd3, + 0x1d, 0x7d, 0xb4, 0xb3, 0x2c, 0x5e, 0x3f, 0xeb, 0x0c, 0xf1, 0x1f, 0x29, 0xbc, 0x3f, 0x1b, 0x8f, 0xc7, 0x37, 0xea, + 0xb6, 0xcf, 0x46, 0x63, 0xd6, 0x65, 0x3b, 0x3d, 0x8c, 0xfc, 0xb7, 0xa4, 0x38, 0xed, 0x94, 0x44, 0xbb, 0xc5, 0xdd, + 0x1a, 0xa3, 0xe4, 0x05, 0x75, 0x77, 0x77, 0x25, 0x58, 0x02, 0x55, 0x16, 0x20, 0xfc, 0xcf, 0xe2, 0x34, 0x68, 0x97, + 0xfe, 0xb9, 0xd4, 0x1a, 0x9f, 0x3d, 0x79, 0xf2, 0xa4, 0xf4, 0x47, 0xea, 0xad, 0x3d, 0x1a, 0x95, 0xfe, 0x70, 0xa1, + 0xd1, 0x68, 0xb7, 0xc7, 0xe3, 0xd2, 0x8f, 0x55, 0xc1, 0x76, 0x77, 0x38, 0xda, 0xee, 0x96, 0xfe, 0x85, 0xd1, 0xa2, + 0xf4, 0x99, 0x7c, 0xcb, 0xd9, 0xa8, 0x76, 0x7c, 0xf0, 0xb8, 0x0d, 0x95, 0x82, 0xd1, 0x16, 0xe8, 0x5d, 0x8a, 0xc7, + 0x20, 0x9a, 0xf3, 0x0c, 0x0c, 0xbb, 0xb2, 0x57, 0x80, 0x7c, 0x1e, 0x4b, 0x09, 0x2f, 0xbe, 0xf7, 0x8b, 0x52, 0xfd, + 0x95, 0x29, 0xd5, 0x91, 0x99, 0x49, 0x9a, 0x17, 0xa4, 0x0d, 0x9a, 0xd5, 0xc8, 0x59, 0x54, 0xfd, 0x2a, 0x2c, 0x2a, + 0x61, 0x8f, 0xd2, 0x06, 0x5b, 0x0a, 0x19, 0xff, 0xc3, 0x3a, 0x19, 0xff, 0xfd, 0xed, 0x32, 0xfe, 0xf4, 0x6e, 0x22, + 0xfe, 0xfb, 0xdf, 0x59, 0xc4, 0xff, 0x60, 0x8a, 0x78, 0x21, 0xc4, 0xf6, 0xc0, 0x74, 0x26, 0x9b, 0xf9, 0x34, 0xbb, + 0x6c, 0xe1, 0x96, 0xc8, 0x6d, 0x92, 0x9e, 0xd3, 0x3b, 0x09, 0xff, 0x15, 0xf9, 0x60, 0x6a, 0x30, 0xe3, 0xe3, 0xc1, + 0x3c, 0x3b, 0x3b, 0x4b, 0x98, 0x92, 0xf1, 0x46, 0x05, 0x99, 0xe3, 0xef, 0xd2, 0xd0, 0x7e, 0x07, 0x9e, 0xb1, 0x51, + 0x32, 0x1e, 0x43, 0xd1, 0x78, 0x6c, 0xab, 0x7c, 0x69, 0x90, 0x67, 0xd4, 0xea, 0x6d, 0xad, 0x84, 0x5a, 0x7d, 0xf1, + 0x85, 0x59, 0x66, 0x16, 0xc8, 0x90, 0x9e, 0x69, 0x8c, 0xc8, 0x9a, 0x51, 0x5c, 0xe0, 0x1e, 0xac, 0x3e, 0x76, 0x8c, + 0xf6, 0xce, 0x14, 0x94, 0x4a, 0x3c, 0xc4, 0x73, 0x91, 0xe6, 0x87, 0x65, 0x44, 0x6e, 0xfb, 0x32, 0x72, 0xd5, 0xf9, + 0xb7, 0xf1, 0x0d, 0xc3, 0xea, 0xcc, 0x1b, 0x16, 0x5f, 0xe6, 0xb7, 0x3c, 0xbd, 0x7a, 0x35, 0x72, 0xf6, 0xc0, 0x1a, + 0x8e, 0x8b, 0x77, 0x69, 0x23, 0x6f, 0x50, 0x80, 0x1d, 0x86, 0x26, 0xa6, 0xa5, 0x20, 0x58, 0x75, 0x81, 0xa2, 0xaa, + 0xec, 0x19, 0x9d, 0x64, 0x7a, 0x19, 0x0e, 0x39, 0xa8, 0x91, 0x25, 0x30, 0x07, 0x93, 0xba, 0x90, 0x3e, 0x66, 0x2f, + 0x92, 0x6e, 0xce, 0xe5, 0x57, 0xcf, 0xe9, 0x70, 0x66, 0x21, 0xf5, 0x87, 0x4c, 0xc7, 0xa8, 0x7a, 0xe2, 0x79, 0x88, + 0x98, 0x61, 0x54, 0xaa, 0x33, 0x10, 0x20, 0xdc, 0x0c, 0x3f, 0xd1, 0x24, 0x86, 0x50, 0x07, 0x05, 0x15, 0xf5, 0xae, + 0xaf, 0xcd, 0x2f, 0x85, 0xd6, 0xbe, 0x2a, 0xd9, 0xe0, 0x01, 0x86, 0x9f, 0xf8, 0x45, 0x6d, 0x90, 0xcd, 0xb9, 0xe3, + 0x50, 0x2b, 0xc7, 0x2d, 0xbd, 0x9d, 0x76, 0x1b, 0x54, 0x8c, 0x2f, 0xbe, 0x03, 0xe5, 0xe8, 0xce, 0x12, 0xdf, 0x75, + 0xe7, 0x12, 0x4b, 0xdf, 0x65, 0xd3, 0x24, 0xc6, 0x0f, 0xc7, 0x08, 0x44, 0x8d, 0xbb, 0x43, 0x6a, 0x11, 0x9b, 0xef, + 0xbe, 0xf2, 0x1d, 0x0d, 0xc2, 0xba, 0xab, 0x38, 0x58, 0xe6, 0xd6, 0xd6, 0x0b, 0xb1, 0xad, 0xb0, 0x6a, 0x96, 0xc1, + 0xb9, 0x45, 0x67, 0x16, 0x17, 0x46, 0x00, 0xbf, 0xb6, 0x0d, 0x4a, 0x15, 0xc1, 0x17, 0x61, 0xf8, 0x3d, 0x0c, 0x36, + 0x0b, 0xc7, 0x5b, 0x01, 0x5d, 0x77, 0x79, 0x0d, 0xc8, 0xd1, 0x19, 0xd6, 0x8c, 0xae, 0xaa, 0x54, 0x41, 0x69, 0x1e, + 0xc1, 0x18, 0xc8, 0x50, 0x24, 0x1d, 0xd6, 0x38, 0x15, 0x7a, 0x0b, 0xa6, 0x21, 0x01, 0xac, 0xfd, 0x3a, 0x74, 0x6b, + 0x6c, 0x05, 0xb6, 0x90, 0x16, 0xa0, 0xf4, 0xb0, 0x43, 0xdf, 0xaa, 0x81, 0x9e, 0x2e, 0x07, 0xe0, 0x6f, 0x74, 0xf2, + 0x4e, 0xfc, 0xe2, 0xc2, 0x83, 0xff, 0xac, 0x3f, 0x2c, 0x40, 0xca, 0x9f, 0x7e, 0x8a, 0x39, 0xd8, 0xd4, 0xb3, 0x16, + 0x86, 0x5f, 0x28, 0x4e, 0x2b, 0xd5, 0x21, 0x1d, 0x45, 0x8b, 0x2b, 0x63, 0xbd, 0x79, 0x81, 0xbe, 0x20, 0x39, 0x3d, + 0x41, 0x9a, 0xa5, 0xac, 0x57, 0x4f, 0x39, 0x30, 0xfd, 0x0e, 0x45, 0xac, 0xa3, 0x45, 0x86, 0xbe, 0x23, 0xbf, 0x02, + 0xdf, 0x51, 0xa8, 0xd1, 0xb6, 0x72, 0x3a, 0xda, 0x2b, 0xdb, 0x07, 0x92, 0xb6, 0x9b, 0x64, 0x2d, 0xe4, 0xcb, 0xce, + 0xd5, 0x3a, 0xe7, 0xe8, 0xb6, 0x03, 0x78, 0x0c, 0x0a, 0xab, 0xff, 0x8c, 0xcc, 0x85, 0x66, 0x31, 0x1d, 0xc0, 0xdf, + 0x05, 0xb2, 0x20, 0x1a, 0xe3, 0x17, 0x16, 0xef, 0xd2, 0xf2, 0x94, 0xb2, 0x5f, 0x17, 0xa8, 0xd6, 0x83, 0xce, 0x13, + 0xf0, 0xf6, 0xee, 0x3c, 0xfc, 0xcd, 0xe8, 0x97, 0x92, 0x46, 0xea, 0x12, 0xb3, 0x6d, 0xf7, 0x50, 0x5e, 0x24, 0xd1, + 0x15, 0x38, 0x9d, 0x64, 0x63, 0x9c, 0x62, 0xf4, 0xb8, 0x37, 0xcb, 0x64, 0x26, 0x49, 0xce, 0x12, 0xfa, 0x19, 0x13, + 0xb9, 0x14, 0xdb, 0x8f, 0x66, 0x97, 0x6a, 0x35, 0x3a, 0x8d, 0x0c, 0x91, 0xdf, 0x35, 0x11, 0x64, 0x7d, 0xe6, 0x49, + 0x3d, 0x99, 0x61, 0x07, 0x60, 0x10, 0x86, 0x4d, 0x2b, 0x17, 0x50, 0xb5, 0xa1, 0xc4, 0x48, 0x85, 0xa9, 0x06, 0xb2, + 0xfc, 0x6d, 0x50, 0x95, 0x51, 0xc1, 0x7a, 0xf8, 0xa9, 0xcb, 0x18, 0x5c, 0x5b, 0x69, 0x3c, 0x4d, 0xe3, 0xd1, 0x28, + 0x61, 0x3d, 0x65, 0x1f, 0x59, 0x9d, 0x47, 0x98, 0x49, 0x62, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe2, 0x69, 0x3a, 0x45, + 0xa7, 0x60, 0xaf, 0xe1, 0xf7, 0x2a, 0x57, 0x92, 0x53, 0xa6, 0x58, 0xb4, 0x2b, 0xe2, 0xd1, 0x73, 0x1d, 0x97, 0x1d, + 0x30, 0x16, 0x69, 0xc1, 0xdb, 0x3d, 0x9e, 0xcd, 0x82, 0xd6, 0x76, 0x1d, 0x11, 0xac, 0xd2, 0x28, 0x78, 0x2b, 0xd0, + 0xf2, 0xd0, 0x3a, 0x10, 0x5a, 0xce, 0xf2, 0x3b, 0xb2, 0x8c, 0x06, 0xc0, 0x6f, 0x22, 0xea, 0xa2, 0xb2, 0x8e, 0xcc, + 0x5f, 0x67, 0xb7, 0x7c, 0xbe, 0x7a, 0xb7, 0x7c, 0xae, 0x76, 0xcb, 0xcd, 0x1c, 0xfb, 0xd9, 0xb8, 0x83, 0xff, 0xf4, + 0x2a, 0x84, 0x60, 0x55, 0x80, 0x1c, 0x16, 0xda, 0xc5, 0xad, 0x2e, 0xfc, 0x8f, 0x86, 0x6e, 0x7b, 0xf8, 0x8f, 0x0f, + 0x16, 0x60, 0xdb, 0xc2, 0x42, 0xfc, 0xaf, 0x5d, 0xab, 0xea, 0x3c, 0xc4, 0x3a, 0xec, 0xb5, 0xb3, 0x5c, 0xd7, 0xbd, + 0x79, 0xd3, 0x82, 0xbc, 0xe2, 0x4e, 0xa0, 0x84, 0x31, 0xb8, 0x6a, 0xd1, 0xe9, 0x29, 0x94, 0x8e, 0xb3, 0xe1, 0xbc, + 0xf8, 0x5b, 0x09, 0xbf, 0x24, 0xe2, 0x8d, 0x5b, 0xba, 0x31, 0x8e, 0xea, 0x2a, 0xd2, 0x92, 0xd4, 0x08, 0x0b, 0xbd, + 0x4e, 0x41, 0x01, 0x8c, 0xc9, 0x9c, 0xae, 0xff, 0x70, 0xc5, 0x26, 0xf8, 0xff, 0xb2, 0x36, 0x2b, 0x91, 0xf9, 0x8f, + 0x12, 0xe3, 0x46, 0x22, 0xfc, 0x2a, 0x1a, 0x98, 0x6b, 0xd8, 0x7e, 0xb2, 0x1a, 0xdc, 0x43, 0x35, 0xd3, 0x91, 0x52, + 0x0a, 0x52, 0xef, 0x80, 0x17, 0x10, 0xcd, 0x13, 0x7e, 0xf3, 0xa8, 0xeb, 0x38, 0x63, 0x69, 0xd4, 0x1b, 0x04, 0x7a, + 0xd5, 0xf6, 0x8e, 0x52, 0xfa, 0xb3, 0xcf, 0x1f, 0xe2, 0x3f, 0x22, 0x70, 0x76, 0x5a, 0xf9, 0x46, 0x22, 0x36, 0x80, + 0xbe, 0xd1, 0xb4, 0xe6, 0xfc, 0x08, 0x0d, 0x4e, 0xfe, 0xcf, 0x5d, 0x5b, 0xa3, 0xb1, 0x7e, 0xa7, 0xe6, 0xd2, 0x2a, + 0xfd, 0x55, 0xad, 0x7f, 0xdd, 0xe0, 0x77, 0x6c, 0x3b, 0x14, 0x0e, 0x41, 0xbd, 0xad, 0x8c, 0x07, 0x2e, 0x35, 0x56, + 0x14, 0xbf, 0x6b, 0xfb, 0xca, 0x24, 0xa6, 0x1e, 0xd3, 0xf0, 0x54, 0x3b, 0x91, 0xf2, 0xf0, 0x1e, 0x7b, 0x08, 0x3f, + 0xf2, 0x4b, 0x16, 0x3e, 0xc0, 0xaf, 0xb1, 0x59, 0x97, 0xd3, 0x24, 0x05, 0xb3, 0x6a, 0xc2, 0xf9, 0x2c, 0xd8, 0xda, + 0xba, 0xb8, 0xb8, 0xf0, 0x2f, 0xb6, 0xfd, 0x2c, 0x3f, 0xdb, 0xea, 0xb6, 0xdb, 0x6d, 0xfc, 0x88, 0x96, 0x6d, 0x9d, + 0xc7, 0xec, 0xe2, 0x29, 0xb8, 0x1f, 0xf6, 0x63, 0xeb, 0x89, 0xf5, 0x78, 0xdb, 0xda, 0x79, 0x64, 0x5b, 0xa4, 0x00, + 0xa0, 0x64, 0xdb, 0xb6, 0x84, 0x02, 0x08, 0x6d, 0x28, 0xee, 0xef, 0x9e, 0x29, 0x1b, 0x0e, 0x2f, 0x29, 0x08, 0x0b, + 0x09, 0xfc, 0xb7, 0xec, 0x13, 0xab, 0x6f, 0x75, 0x51, 0xd6, 0x92, 0x6a, 0x44, 0xbd, 0xe2, 0x7e, 0x1f, 0x46, 0xb3, + 0x80, 0xd8, 0xc8, 0x2c, 0xc4, 0x30, 0x99, 0x28, 0xa5, 0x29, 0xd0, 0x2e, 0x3d, 0x85, 0x27, 0xcc, 0x6a, 0xb3, 0xe0, + 0xf9, 0x4d, 0xf7, 0x31, 0xe8, 0xb8, 0xf3, 0xd6, 0xc3, 0x61, 0xbb, 0xd5, 0xb1, 0x3a, 0xad, 0xae, 0xff, 0xd8, 0xea, + 0x8a, 0xff, 0x83, 0x8c, 0xdc, 0xb6, 0x3a, 0xf0, 0xb4, 0x6d, 0xc1, 0xfb, 0xf9, 0x43, 0x91, 0x5b, 0x12, 0xd9, 0x5b, + 0xfd, 0x5d, 0xfc, 0x4d, 0x29, 0x40, 0xea, 0x73, 0x5b, 0xfc, 0x0a, 0x9e, 0xfd, 0x99, 0x59, 0xda, 0x79, 0xb2, 0xb2, + 0xb8, 0xfb, 0x78, 0x65, 0xf1, 0xf6, 0xa3, 0x95, 0xc5, 0x0f, 0x77, 0xea, 0xc5, 0x5b, 0x67, 0xa2, 0x4a, 0xcb, 0x85, + 0xd0, 0x9e, 0x46, 0xc0, 0x28, 0x97, 0x4e, 0x07, 0xe0, 0x6c, 0x5b, 0x2d, 0xfc, 0xf3, 0xb8, 0xeb, 0xea, 0x5e, 0xa7, + 0xd8, 0x4b, 0x63, 0xf9, 0xf8, 0x09, 0x60, 0xf9, 0xb2, 0xfb, 0x68, 0x88, 0xed, 0x08, 0x51, 0xf8, 0xef, 0x7c, 0xfb, + 0xc9, 0x10, 0x34, 0x82, 0x85, 0xff, 0xc1, 0x3f, 0x93, 0x9d, 0xee, 0x50, 0xbc, 0xb4, 0xb1, 0xfe, 0xdb, 0xce, 0xe3, + 0x02, 0x9a, 0xe2, 0x3f, 0xbf, 0x68, 0x13, 0x1a, 0x0d, 0x78, 0x73, 0xdc, 0x87, 0x40, 0xa3, 0x27, 0x93, 0xae, 0xff, + 0xf9, 0xf9, 0x63, 0xff, 0xc9, 0xa4, 0xf3, 0xf8, 0x5b, 0xf1, 0x96, 0x00, 0x05, 0x3f, 0xc7, 0xff, 0xbe, 0xdd, 0x6e, + 0x4f, 0x5a, 0x1d, 0xff, 0xc9, 0xf9, 0xb6, 0xbf, 0x9d, 0xb4, 0x1e, 0xf9, 0x4f, 0xf0, 0xbf, 0x6a, 0xb8, 0x49, 0x36, + 0x65, 0xb6, 0x85, 0xeb, 0xdd, 0xf0, 0x7b, 0xcd, 0x39, 0xba, 0x0f, 0xad, 0x9d, 0x87, 0x2f, 0x9f, 0xc0, 0x1a, 0x4d, + 0x3a, 0x5d, 0xf8, 0xff, 0xba, 0xc7, 0x6f, 0x91, 0xf0, 0x72, 0xe0, 0x88, 0x61, 0x7a, 0xb1, 0x22, 0x1c, 0x7d, 0xd0, + 0xed, 0x81, 0xf7, 0xa7, 0x75, 0x01, 0x10, 0xc6, 0x6f, 0x0d, 0x80, 0x70, 0x7e, 0xb7, 0x08, 0x08, 0xfd, 0xda, 0xc0, + 0xef, 0x18, 0x01, 0xf9, 0x53, 0x33, 0xc8, 0x7d, 0xc9, 0x96, 0x02, 0x1d, 0x4d, 0x67, 0xed, 0x2d, 0x73, 0x0e, 0xbf, + 0x64, 0x47, 0x98, 0x4a, 0x0f, 0xad, 0x39, 0x37, 0xe3, 0x41, 0x19, 0x6e, 0xe4, 0x4b, 0x26, 0x76, 0x72, 0xc1, 0xd7, + 0x10, 0x24, 0xbe, 0x9d, 0x20, 0xdf, 0xde, 0x8d, 0x1e, 0xf1, 0xef, 0x4c, 0x8f, 0x82, 0x1b, 0xf4, 0xa8, 0x45, 0xdc, + 0x29, 0x62, 0x40, 0x8e, 0xfe, 0x3e, 0xbd, 0x3b, 0x9c, 0xbe, 0xc5, 0xb6, 0xc5, 0xb0, 0xa8, 0xb0, 0x45, 0xce, 0xe6, + 0xd3, 0x5f, 0x73, 0x44, 0x20, 0xd2, 0xcd, 0x43, 0x5b, 0x46, 0x61, 0x66, 0xf8, 0xd1, 0x62, 0xf5, 0x72, 0x2e, 0xae, + 0x34, 0x85, 0x74, 0x1f, 0x71, 0x47, 0x47, 0x70, 0xf0, 0x06, 0x40, 0xb8, 0xc8, 0x78, 0x84, 0xbf, 0x8a, 0x05, 0xe4, + 0xa6, 0xdf, 0xcf, 0x8a, 0x79, 0x82, 0x97, 0xa6, 0xbd, 0xa1, 0xf8, 0x80, 0x2c, 0x3c, 0xca, 0xbb, 0x86, 0x98, 0xc2, + 0xfe, 0x0d, 0xa6, 0xdf, 0xab, 0xb3, 0x83, 0x29, 0xc6, 0x11, 0xde, 0xb0, 0x51, 0x1c, 0x39, 0xb6, 0x33, 0x83, 0x8d, + 0x0c, 0xb3, 0xb4, 0x6a, 0xb9, 0xef, 0x94, 0xf6, 0xee, 0xda, 0xea, 0xa7, 0x99, 0x72, 0xfc, 0xd4, 0x5d, 0x78, 0x28, + 0xe3, 0x8e, 0xb6, 0x74, 0x0c, 0x60, 0x7c, 0x55, 0x92, 0xa3, 0x0e, 0xa8, 0x8c, 0x09, 0x5b, 0x58, 0x13, 0x1d, 0xbf, + 0x0b, 0xde, 0x05, 0x15, 0xe3, 0xa7, 0xc3, 0xbe, 0x77, 0x5a, 0xdb, 0x60, 0xed, 0x18, 0xdd, 0xf4, 0x40, 0x47, 0xfa, + 0x97, 0x7e, 0xf4, 0xaf, 0xd1, 0xd5, 0x2f, 0x0c, 0xd8, 0x82, 0x23, 0x3e, 0x13, 0xb8, 0xdb, 0xf4, 0x89, 0x06, 0x99, + 0x50, 0x82, 0x17, 0xe6, 0xa0, 0xcc, 0x31, 0x7f, 0x95, 0x4c, 0x7c, 0x9a, 0x4c, 0xfc, 0x00, 0x61, 0x59, 0x35, 0x61, + 0xd5, 0xcf, 0x7f, 0x20, 0x05, 0x99, 0xa7, 0x67, 0x23, 0xea, 0x61, 0x86, 0x07, 0xfe, 0xad, 0x8a, 0xd5, 0x83, 0x8c, + 0x58, 0x81, 0x17, 0x8f, 0xbf, 0xe9, 0x42, 0x7f, 0x96, 0xe2, 0x61, 0x22, 0xca, 0xd1, 0x28, 0xad, 0x86, 0xaa, 0xe2, + 0x5e, 0xc5, 0xd3, 0xab, 0x03, 0xf9, 0x41, 0x03, 0x1b, 0x43, 0xd0, 0x74, 0xf4, 0x50, 0x7d, 0x4c, 0x6d, 0x13, 0xf4, + 0x1e, 0xfd, 0xc4, 0x29, 0x65, 0x0f, 0xa0, 0x6a, 0xc3, 0xfb, 0x04, 0x96, 0x74, 0x81, 0x42, 0x5b, 0x28, 0xb6, 0x11, + 0x3b, 0x8f, 0x87, 0x52, 0x3f, 0x79, 0x96, 0xbc, 0x07, 0xd5, 0x22, 0xba, 0x87, 0x1d, 0x4f, 0x04, 0x01, 0xe0, 0x05, + 0xd5, 0x73, 0x98, 0x66, 0x76, 0xff, 0x41, 0x6f, 0x1d, 0x65, 0xf1, 0xf7, 0x56, 0x0f, 0xc1, 0xe9, 0xfc, 0xdb, 0xf0, + 0x01, 0xfe, 0xe2, 0xea, 0x83, 0x23, 0xdb, 0xf5, 0x49, 0xba, 0x3f, 0xa8, 0x7e, 0x76, 0x15, 0x45, 0xdb, 0x26, 0x28, + 0x62, 0xef, 0xae, 0x1a, 0x59, 0x6a, 0xdf, 0xee, 0x4e, 0xa5, 0x7d, 0xe1, 0xd9, 0x10, 0xb7, 0xa0, 0x09, 0xba, 0xfe, + 0x8e, 0x21, 0xd3, 0xcf, 0x5b, 0xf8, 0xb7, 0x26, 0xd5, 0x1f, 0x42, 0x03, 0x25, 0xd6, 0x5f, 0x43, 0xf3, 0x6d, 0xa1, + 0x41, 0xa0, 0xdf, 0x0f, 0x24, 0x73, 0x85, 0xbc, 0xad, 0xf3, 0xf8, 0x8a, 0xd3, 0x30, 0x91, 0x69, 0x61, 0x7b, 0x46, + 0xe0, 0x4c, 0x6c, 0x39, 0x19, 0x16, 0x7a, 0x0e, 0x7d, 0x1d, 0xfd, 0x8d, 0xf2, 0x55, 0x75, 0x5e, 0x4d, 0x04, 0xac, + 0x98, 0x02, 0x37, 0x6d, 0xe3, 0xc4, 0xad, 0x27, 0x92, 0xb8, 0xf5, 0x47, 0x4e, 0xd6, 0x73, 0xab, 0xcc, 0xf6, 0x76, + 0x8d, 0xfd, 0xcf, 0xe9, 0x3b, 0xaa, 0x34, 0xc9, 0xab, 0x51, 0xd9, 0x9c, 0x1f, 0x6c, 0x16, 0xfc, 0xd1, 0xc9, 0xea, + 0x0a, 0x8f, 0xbc, 0x9b, 0x8b, 0xf9, 0x14, 0xa3, 0x38, 0xa7, 0x2b, 0xdf, 0x0a, 0xf4, 0x5a, 0x54, 0xb5, 0xa2, 0x12, + 0x89, 0x00, 0x56, 0x0c, 0x6c, 0x2c, 0xb2, 0x03, 0x99, 0xf5, 0x67, 0x7e, 0x48, 0xdc, 0xbc, 0x93, 0x3b, 0x12, 0x09, + 0x7f, 0xf8, 0x43, 0x0b, 0xb6, 0xa0, 0x8f, 0x0d, 0xa2, 0x74, 0xed, 0x2e, 0x21, 0x03, 0x0b, 0x71, 0xad, 0x7e, 0x39, + 0xcb, 0x94, 0x2e, 0xb6, 0x49, 0x68, 0x3d, 0x2e, 0x91, 0xd0, 0x95, 0x74, 0x3a, 0x65, 0x11, 0xf7, 0xa3, 0x94, 0x92, + 0xb3, 0x1c, 0x43, 0x06, 0x79, 0x1d, 0xb6, 0xed, 0x96, 0x20, 0xf8, 0x8c, 0x9f, 0x16, 0x13, 0x9b, 0xd9, 0x87, 0x42, + 0xfd, 0x59, 0xab, 0x7a, 0xa2, 0xf5, 0xa4, 0xdb, 0x7f, 0x77, 0xb0, 0x67, 0x89, 0x4d, 0xb9, 0xbb, 0x05, 0xaf, 0xbb, + 0xe4, 0x9e, 0x8b, 0x3c, 0x95, 0x50, 0xe4, 0xa9, 0x58, 0x22, 0xbb, 0x4d, 0x24, 0x26, 0x6f, 0x09, 0xb4, 0x6d, 0x8b, + 0xa5, 0x43, 0x11, 0x57, 0x9c, 0x82, 0x0b, 0x13, 0xe3, 0xc7, 0xe7, 0xb6, 0xb0, 0x6b, 0x0b, 0x17, 0xcc, 0x56, 0x29, + 0x3f, 0xca, 0x68, 0xe1, 0xa9, 0x8a, 0x42, 0x82, 0xa9, 0xc1, 0x54, 0xf6, 0x8f, 0x1c, 0x4a, 0x27, 0x1d, 0x2f, 0xb7, + 0x2e, 0xe6, 0xa7, 0x53, 0x10, 0x82, 0x2a, 0x63, 0xe7, 0xa3, 0xec, 0xb0, 0x4b, 0x53, 0xf5, 0x4f, 0x4a, 0x19, 0x26, + 0x55, 0x1f, 0x06, 0x6f, 0xfc, 0x88, 0xaa, 0xc0, 0x5e, 0x0a, 0x7d, 0x4c, 0x38, 0x99, 0x6c, 0x1b, 0x09, 0x27, 0x46, + 0x5d, 0x09, 0xa8, 0x6f, 0xf7, 0x4f, 0x82, 0x99, 0x1c, 0xef, 0x75, 0xb6, 0xf4, 0x83, 0xac, 0xa2, 0x3d, 0x28, 0x94, + 0x01, 0x25, 0x8f, 0x8b, 0x4b, 0x1b, 0x12, 0x60, 0x58, 0x41, 0x80, 0x49, 0xea, 0x77, 0x8b, 0xce, 0xb5, 0xed, 0x9d, + 0xb6, 0xca, 0xc9, 0x85, 0x32, 0xdc, 0x90, 0xa2, 0x8b, 0x31, 0x49, 0x2d, 0xb6, 0x3b, 0xe9, 0xf4, 0x77, 0x23, 0x69, + 0x39, 0xa2, 0xf0, 0x28, 0x40, 0x7a, 0x40, 0x67, 0x34, 0xcf, 0xfc, 0x38, 0xdb, 0xba, 0x60, 0xa7, 0xad, 0x68, 0x16, + 0x57, 0x81, 0x54, 0xb4, 0x23, 0xf4, 0x94, 0x59, 0x35, 0x13, 0x3e, 0x46, 0x0d, 0x24, 0x49, 0x70, 0x97, 0x32, 0x4a, + 0x4b, 0xe6, 0x37, 0xb0, 0x10, 0x50, 0x98, 0xe4, 0xba, 0x8a, 0xe6, 0x4a, 0x75, 0x5a, 0xda, 0xfd, 0xbf, 0xfc, 0xf3, + 0xff, 0x96, 0x01, 0x5a, 0xa0, 0x4a, 0x47, 0x8d, 0xd5, 0x20, 0x74, 0xb9, 0x8b, 0xf9, 0x4d, 0xd5, 0x11, 0x2e, 0xbb, + 0x04, 0x4f, 0x3f, 0x1e, 0xb5, 0x26, 0x51, 0x32, 0x06, 0xc0, 0xd6, 0x12, 0xc8, 0xcc, 0x7e, 0x90, 0x50, 0xd7, 0x8b, + 0x90, 0x05, 0x7f, 0x53, 0x96, 0xb5, 0xca, 0x6e, 0xa7, 0xdd, 0x6a, 0xe4, 0x5c, 0x1b, 0x1b, 0xaa, 0x96, 0x77, 0xad, + 0x7e, 0x95, 0x4c, 0x0a, 0x35, 0x56, 0x4b, 0xba, 0x86, 0x96, 0xfa, 0xa4, 0xe9, 0xdf, 0xff, 0xe5, 0x1f, 0xfe, 0x87, + 0x7a, 0xc5, 0x03, 0xa4, 0xbf, 0xfc, 0xd3, 0xdf, 0x61, 0x7e, 0xb3, 0xa5, 0x0f, 0x99, 0x48, 0x4e, 0x58, 0xd5, 0x09, + 0x93, 0x10, 0x18, 0x56, 0xe5, 0xd1, 0xd5, 0x93, 0xb3, 0xf7, 0x69, 0x42, 0xda, 0x6c, 0x12, 0x3a, 0xda, 0xb4, 0x65, + 0xc5, 0x23, 0x35, 0x92, 0x13, 0x2f, 0x42, 0x25, 0xd2, 0xfb, 0x4e, 0x99, 0x4f, 0xbe, 0x5e, 0x8d, 0x85, 0x0a, 0xff, + 0x61, 0x49, 0x59, 0x95, 0x5b, 0x18, 0x97, 0x5f, 0xe0, 0x6b, 0xd0, 0x35, 0x8a, 0x69, 0xf1, 0x6a, 0x7d, 0x7a, 0x3f, + 0xcd, 0x01, 0xfe, 0x31, 0x52, 0x5c, 0x04, 0x19, 0xe9, 0xcc, 0xb9, 0x85, 0x06, 0x5d, 0x72, 0x55, 0xd2, 0x28, 0xc2, + 0x0b, 0x7c, 0xf8, 0xe4, 0x6f, 0xca, 0x3f, 0x4e, 0xd1, 0x6c, 0xb2, 0x9c, 0x69, 0x74, 0x29, 0x7d, 0xc3, 0x47, 0xed, + 0xf6, 0xec, 0xd2, 0x5d, 0x54, 0x33, 0x78, 0xeb, 0x26, 0xa3, 0xc0, 0xa4, 0x39, 0x20, 0x1d, 0x56, 0xeb, 0x18, 0x28, + 0xb8, 0x43, 0x6d, 0x0c, 0x99, 0x95, 0xe5, 0x1f, 0x16, 0x14, 0x86, 0x8b, 0x7f, 0xc1, 0x43, 0x65, 0x19, 0xb1, 0x84, + 0x12, 0x03, 0x8b, 0x85, 0xd1, 0xab, 0x2b, 0x7a, 0x4d, 0x3a, 0xcb, 0x39, 0x41, 0xe6, 0xa1, 0xb8, 0x79, 0x9c, 0xfd, + 0x10, 0x0f, 0xa8, 0x27, 0x1d, 0x6f, 0xd2, 0x5d, 0xe8, 0xe1, 0x39, 0xcf, 0xa6, 0xe6, 0x29, 0x38, 0x8b, 0xd8, 0x90, + 0x8d, 0x55, 0xa4, 0x57, 0xd6, 0x8b, 0x13, 0xee, 0x72, 0xb2, 0xbd, 0x62, 0x2e, 0x09, 0x12, 0x9d, 0x7e, 0x03, 0x3c, + 0x9f, 0xe1, 0x06, 0x04, 0xfa, 0x67, 0x11, 0x0f, 0x88, 0x5f, 0x7b, 0xe6, 0x59, 0x7a, 0x84, 0x52, 0x26, 0x5b, 0x18, + 0xf0, 0xf4, 0x44, 0x53, 0x8c, 0xb9, 0xd6, 0x73, 0xb2, 0x4a, 0x9f, 0xba, 0x9b, 0x43, 0x89, 0x90, 0xcd, 0xb7, 0xf2, + 0x88, 0xfa, 0x69, 0x2d, 0xd6, 0x21, 0x55, 0x4c, 0xd7, 0xf5, 0x56, 0xd6, 0x0b, 0x4d, 0x2d, 0x6a, 0xbf, 0x05, 0x03, + 0x8c, 0xc0, 0xb4, 0x9b, 0xad, 0xa8, 0x10, 0x5b, 0x3d, 0x0d, 0xbf, 0xd5, 0x7e, 0x4d, 0x34, 0x9b, 0x51, 0x43, 0x17, + 0x98, 0x98, 0xac, 0x51, 0x94, 0x1d, 0x94, 0x7e, 0x21, 0xb2, 0x1d, 0x64, 0x1b, 0xb9, 0x11, 0xc4, 0x93, 0xcc, 0x83, + 0xa0, 0xdf, 0xb7, 0xff, 0x7f, 0x47, 0x48, 0x09, 0x5d, 0xf5, 0x7e, 0x00, 0x00}; } // namespace web_server } // namespace esphome From 7ef8d67831b11122f3b579c972a0045e748d4974 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:31:23 +1200 Subject: [PATCH 62/67] Bump version to 2023.6.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e698ffcc64..2197b34034 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b5" +__version__ = "2023.6.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 244a2125929fa8c77c1a64b7a7558d185edabadf Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Tue, 20 Jun 2023 19:53:44 -0400 Subject: [PATCH 63/67] airthings_wave: refactor to eliminate code duplication (#4910) --- CODEOWNERS | 1 + .../airthings_wave_base/__init__.py | 81 ++++++++++++ .../airthings_wave_base.cpp | 83 ++++++++++++ .../airthings_wave_base/airthings_wave_base.h | 50 ++++++++ .../airthings_wave_mini.cpp | 102 ++++----------- .../airthings_wave_mini/airthings_wave_mini.h | 34 +---- .../components/airthings_wave_mini/sensor.py | 74 ++--------- .../airthings_wave_plus.cpp | 118 +++++------------- .../airthings_wave_plus/airthings_wave_plus.h | 31 +---- .../components/airthings_wave_plus/sensor.py | 108 +++++----------- tests/test2.yaml | 6 +- 11 files changed, 317 insertions(+), 371 deletions(-) create mode 100644 esphome/components/airthings_wave_base/__init__.py create mode 100644 esphome/components/airthings_wave_base/airthings_wave_base.cpp create mode 100644 esphome/components/airthings_wave_base/airthings_wave_base.h diff --git a/CODEOWNERS b/CODEOWNERS index c6cbf3c2ab..b4ed234e22 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -17,6 +17,7 @@ esphome/components/adc/* @esphome/core esphome/components/adc128s102/* @DeerMaximum esphome/components/addressable_light/* @justfalter esphome/components/airthings_ble/* @jeromelaban +esphome/components/airthings_wave_base/* @jeromelaban @ncareau esphome/components/airthings_wave_mini/* @ncareau esphome/components/airthings_wave_plus/* @jeromelaban esphome/components/alarm_control_panel/* @grahambrown11 diff --git a/esphome/components/airthings_wave_base/__init__.py b/esphome/components/airthings_wave_base/__init__.py new file mode 100644 index 0000000000..3ff55fc6b0 --- /dev/null +++ b/esphome/components/airthings_wave_base/__init__.py @@ -0,0 +1,81 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, ble_client + +from esphome.const import ( + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + STATE_CLASS_MEASUREMENT, + UNIT_PERCENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + CONF_HUMIDITY, + CONF_TVOC, + CONF_PRESSURE, + CONF_TEMPERATURE, + UNIT_PARTS_PER_BILLION, + ICON_RADIATOR, +) + +CODEOWNERS = ["@ncareau", "@jeromelaban"] + +DEPENDENCIES = ["ble_client"] + +airthings_wave_base_ns = cg.esphome_ns.namespace("airthings_wave_base") +AirthingsWaveBase = airthings_wave_base_ns.class_( + "AirthingsWaveBase", cg.PollingComponent, ble_client.BLEClientNode +) + + +BASE_SCHEMA = ( + sensor.SENSOR_SCHEMA.extend( + { + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=0, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TVOC): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_BILLION, + icon=ICON_RADIATOR, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("5min")) + .extend(ble_client.BLE_CLIENT_SCHEMA) +) + + +async def wave_base_to_code(var, config): + await cg.register_component(var, config) + + await ble_client.register_ble_node(var, config) + + if CONF_HUMIDITY in config: + sens = await sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity(sens)) + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature(sens)) + if CONF_PRESSURE in config: + sens = await sensor.new_sensor(config[CONF_PRESSURE]) + cg.add(var.set_pressure(sens)) + if CONF_TVOC in config: + sens = await sensor.new_sensor(config[CONF_TVOC]) + cg.add(var.set_tvoc(sens)) diff --git a/esphome/components/airthings_wave_base/airthings_wave_base.cpp b/esphome/components/airthings_wave_base/airthings_wave_base.cpp new file mode 100644 index 0000000000..349d8d58eb --- /dev/null +++ b/esphome/components/airthings_wave_base/airthings_wave_base.cpp @@ -0,0 +1,83 @@ +#include "airthings_wave_base.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace airthings_wave_base { + +static const char *const TAG = "airthings_wave_base"; + +void AirthingsWaveBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + switch (event) { + case ESP_GATTC_OPEN_EVT: { + if (param->open.status == ESP_GATT_OK) { + ESP_LOGI(TAG, "Connected successfully!"); + } + break; + } + + case ESP_GATTC_DISCONNECT_EVT: { + ESP_LOGW(TAG, "Disconnected!"); + break; + } + + case ESP_GATTC_SEARCH_CMPL_EVT: { + this->handle_ = 0; + auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->sensors_data_characteristic_uuid_); + if (chr == nullptr) { + ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(), + this->sensors_data_characteristic_uuid_.to_string().c_str()); + break; + } + this->handle_ = chr->handle; + this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; + + this->request_read_values_(); + break; + } + + case ESP_GATTC_READ_CHAR_EVT: { + if (param->read.conn_id != this->parent()->get_conn_id()) + break; + if (param->read.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); + break; + } + if (param->read.handle == this->handle_) { + this->read_sensors(param->read.value, param->read.value_len); + } + break; + } + + default: + break; + } +} + +bool AirthingsWaveBase::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } + +void AirthingsWaveBase::update() { + if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { + if (!this->parent()->enabled) { + ESP_LOGW(TAG, "Reconnecting to device"); + this->parent()->set_enabled(true); + this->parent()->connect(); + } else { + ESP_LOGW(TAG, "Connection in progress"); + } + } +} + +void AirthingsWaveBase::request_read_values_() { + auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_, + ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); + } +} + +} // namespace airthings_wave_base +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_base/airthings_wave_base.h b/esphome/components/airthings_wave_base/airthings_wave_base.h new file mode 100644 index 0000000000..68c0b3497d --- /dev/null +++ b/esphome/components/airthings_wave_base/airthings_wave_base.h @@ -0,0 +1,50 @@ +#pragma once + +#ifdef USE_ESP32 + +#include +#include +#include +#include "esphome/components/ble_client/ble_client.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace airthings_wave_base { + +class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientNode { + public: + AirthingsWaveBase() = default; + + void update() override; + + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; + + void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } + void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; } + void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } + + protected: + bool is_valid_voc_value_(uint16_t voc); + + virtual void read_sensors(uint8_t *value, uint16_t value_len) = 0; + void request_read_values_(); + + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *tvoc_sensor_{nullptr}; + + uint16_t handle_; + esp32_ble_tracker::ESPBTUUID service_uuid_; + esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; +}; + +} // namespace airthings_wave_base +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp index 40873ec005..331a13434f 100644 --- a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp @@ -7,105 +7,47 @@ namespace airthings_wave_mini { static const char *const TAG = "airthings_wave_mini"; -void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - switch (event) { - case ESP_GATTC_OPEN_EVT: { - if (param->open.status == ESP_GATT_OK) { - ESP_LOGI(TAG, "Connected successfully!"); - } - break; - } - - case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGW(TAG, "Disconnected!"); - break; - } - - case ESP_GATTC_SEARCH_CMPL_EVT: { - this->handle_ = 0; - auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); - if (chr == nullptr) { - ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), - sensors_data_characteristic_uuid_.to_string().c_str()); - break; - } - this->handle_ = chr->handle; - this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; - - request_read_values_(); - break; - } - - case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->get_conn_id()) - break; - if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); - break; - } - if (param->read.handle == this->handle_) { - read_sensors_(param->read.value, param->read.value_len); - } - break; - } - - default: - break; - } -} - -void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) { +void AirthingsWaveMini::read_sensors(uint8_t *raw_value, uint16_t value_len) { auto *value = (WaveMiniReadings *) raw_value; if (sizeof(WaveMiniReadings) <= value_len) { - this->humidity_sensor_->publish_state(value->humidity / 100.0f); - this->pressure_sensor_->publish_state(value->pressure / 50.0f); - this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f); - if (is_valid_voc_value_(value->voc)) { + if (this->humidity_sensor_ != nullptr) { + this->humidity_sensor_->publish_state(value->humidity / 100.0f); + } + + if (this->pressure_sensor_ != nullptr) { + this->pressure_sensor_->publish_state(value->pressure / 50.0f); + } + + if (this->temperature_sensor_ != nullptr) { + this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f); + } + + if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) { this->tvoc_sensor_->publish_state(value->voc); } // This instance must not stay connected // so other clients can connect to it (e.g. the // mobile app). - parent()->set_enabled(false); - } -} - -bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } - -void AirthingsWaveMini::update() { - if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { - if (!parent()->enabled) { - ESP_LOGW(TAG, "Reconnecting to device"); - parent()->set_enabled(true); - parent()->connect(); - } else { - ESP_LOGW(TAG, "Connection in progress"); - } - } -} - -void AirthingsWaveMini::request_read_values_() { - auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_, - ESP_GATT_AUTH_REQ_NONE); - if (status) { - ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); + this->parent()->set_enabled(false); } } void AirthingsWaveMini::dump_config() { + // these really don't belong here, but there doesn't seem to be a + // practical way to have the base class use LOG_SENSOR and include + // the TAG from this component LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); } -AirthingsWaveMini::AirthingsWaveMini() - : PollingComponent(10000), - service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)), - sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {} +AirthingsWaveMini::AirthingsWaveMini() { + this->service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID); + this->sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID); +} } // namespace airthings_wave_mini } // namespace esphome diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.h b/esphome/components/airthings_wave_mini/airthings_wave_mini.h index 128774f9cb..ec4fd23e60 100644 --- a/esphome/components/airthings_wave_mini/airthings_wave_mini.h +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.h @@ -2,14 +2,7 @@ #ifdef USE_ESP32 -#include -#include -#include -#include "esphome/components/ble_client/ble_client.h" -#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/core/component.h" -#include "esphome/core/log.h" +#include "esphome/components/airthings_wave_base/airthings_wave_base.h" namespace esphome { namespace airthings_wave_mini { @@ -17,35 +10,14 @@ namespace airthings_wave_mini { static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba"; static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba"; -class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode { +class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase { public: AirthingsWaveMini(); void dump_config() override; - void update() override; - - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) override; - - void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } - void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } - void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; } - void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } protected: - bool is_valid_voc_value_(uint16_t voc); - - void read_sensors_(uint8_t *value, uint16_t value_len); - void request_read_values_(); - - sensor::Sensor *temperature_sensor_{nullptr}; - sensor::Sensor *humidity_sensor_{nullptr}; - sensor::Sensor *pressure_sensor_{nullptr}; - sensor::Sensor *tvoc_sensor_{nullptr}; - - uint16_t handle_; - esp32_ble_tracker::ESPBTUUID service_uuid_; - esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; + void read_sensors(uint8_t *value, uint16_t value_len) override; struct WaveMiniReadings { uint16_t unused01; diff --git a/esphome/components/airthings_wave_mini/sensor.py b/esphome/components/airthings_wave_mini/sensor.py index d38354fa84..0f4fd1a13a 100644 --- a/esphome/components/airthings_wave_mini/sensor.py +++ b/esphome/components/airthings_wave_mini/sensor.py @@ -1,82 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor, ble_client +from esphome.components import airthings_wave_base from esphome.const import ( - DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_TEMPERATURE, - DEVICE_CLASS_PRESSURE, - STATE_CLASS_MEASUREMENT, - UNIT_PERCENT, - UNIT_CELSIUS, - UNIT_HECTOPASCAL, CONF_ID, - CONF_HUMIDITY, - CONF_TVOC, - CONF_PRESSURE, - CONF_TEMPERATURE, - UNIT_PARTS_PER_BILLION, - ICON_RADIATOR, ) -DEPENDENCIES = ["ble_client"] +DEPENDENCIES = airthings_wave_base.DEPENDENCIES + +AUTO_LOAD = ["airthings_wave_base"] airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini") AirthingsWaveMini = airthings_wave_mini_ns.class_( - "AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode + "AirthingsWaveMini", airthings_wave_base.AirthingsWaveBase ) -CONFIG_SCHEMA = cv.All( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(AirthingsWaveMini), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( - unit_of_measurement=UNIT_PERCENT, - device_class=DEVICE_CLASS_HUMIDITY, - state_class=STATE_CLASS_MEASUREMENT, - accuracy_decimals=2, - ), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=2, - device_class=DEVICE_CLASS_TEMPERATURE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema( - unit_of_measurement=UNIT_HECTOPASCAL, - accuracy_decimals=2, - device_class=DEVICE_CLASS_PRESSURE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_TVOC): sensor.sensor_schema( - unit_of_measurement=UNIT_PARTS_PER_BILLION, - icon=ICON_RADIATOR, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - } - ) - .extend(cv.polling_component_schema("5min")) - .extend(ble_client.BLE_CLIENT_SCHEMA), +CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(AirthingsWaveMini), + } ) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - - await ble_client.register_ble_node(var, config) - - if CONF_HUMIDITY in config: - sens = await sensor.new_sensor(config[CONF_HUMIDITY]) - cg.add(var.set_humidity(sens)) - if CONF_TEMPERATURE in config: - sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) - cg.add(var.set_temperature(sens)) - if CONF_PRESSURE in config: - sens = await sensor.new_sensor(config[CONF_PRESSURE]) - cg.add(var.set_pressure(sens)) - if CONF_TVOC in config: - sens = await sensor.new_sensor(config[CONF_TVOC]) - cg.add(var.set_tvoc(sens)) + await airthings_wave_base.wave_base_to_code(var, config) diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 11f86307fe..acd3a4316d 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -7,55 +7,7 @@ namespace airthings_wave_plus { static const char *const TAG = "airthings_wave_plus"; -void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - switch (event) { - case ESP_GATTC_OPEN_EVT: { - if (param->open.status == ESP_GATT_OK) { - ESP_LOGI(TAG, "Connected successfully!"); - } - break; - } - - case ESP_GATTC_DISCONNECT_EVT: { - ESP_LOGW(TAG, "Disconnected!"); - break; - } - - case ESP_GATTC_SEARCH_CMPL_EVT: { - this->handle_ = 0; - auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); - if (chr == nullptr) { - ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), - sensors_data_characteristic_uuid_.to_string().c_str()); - break; - } - this->handle_ = chr->handle; - this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; - - request_read_values_(); - break; - } - - case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->get_conn_id()) - break; - if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); - break; - } - if (param->read.handle == this->handle_) { - read_sensors_(param->read.value, param->read.value_len); - } - break; - } - - default: - break; - } -} - -void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) { +void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) { auto *value = (WavePlusReadings *) raw_value; if (sizeof(WavePlusReadings) <= value_len) { @@ -64,26 +16,38 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) { if (value->version == 1) { ESP_LOGD(TAG, "ambient light = %d", value->ambientLight); - this->humidity_sensor_->publish_state(value->humidity / 2.0f); - if (is_valid_radon_value_(value->radon)) { + if (this->humidity_sensor_ != nullptr) { + this->humidity_sensor_->publish_state(value->humidity / 2.0f); + } + + if ((this->radon_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon)) { this->radon_sensor_->publish_state(value->radon); } - if (is_valid_radon_value_(value->radon_lt)) { + + if ((this->radon_long_term_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon_lt)) { this->radon_long_term_sensor_->publish_state(value->radon_lt); } - this->temperature_sensor_->publish_state(value->temperature / 100.0f); - this->pressure_sensor_->publish_state(value->pressure / 50.0f); - if (is_valid_co2_value_(value->co2)) { + + if (this->temperature_sensor_ != nullptr) { + this->temperature_sensor_->publish_state(value->temperature / 100.0f); + } + + if (this->pressure_sensor_ != nullptr) { + this->pressure_sensor_->publish_state(value->pressure / 50.0f); + } + + if ((this->co2_sensor_ != nullptr) && this->is_valid_co2_value_(value->co2)) { this->co2_sensor_->publish_state(value->co2); } - if (is_valid_voc_value_(value->voc)) { + + if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) { this->tvoc_sensor_->publish_state(value->voc); } // This instance must not stay connected // so other clients can connect to it (e.g. the // mobile app). - parent()->set_enabled(false); + this->parent()->set_enabled(false); } else { ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version); } @@ -92,44 +56,26 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) { bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; } -bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } - bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; } -void AirthingsWavePlus::update() { - if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { - if (!parent()->enabled) { - ESP_LOGW(TAG, "Reconnecting to device"); - parent()->set_enabled(true); - parent()->connect(); - } else { - ESP_LOGW(TAG, "Connection in progress"); - } - } -} - -void AirthingsWavePlus::request_read_values_() { - auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_, - ESP_GATT_AUTH_REQ_NONE); - if (status) { - ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); - } -} - void AirthingsWavePlus::dump_config() { + // these really don't belong here, but there doesn't seem to be a + // practical way to have the base class use LOG_SENSOR and include + // the TAG from this component LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); - LOG_SENSOR(" ", "Radon", this->radon_sensor_); - LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); - LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); + + LOG_SENSOR(" ", "Radon", this->radon_sensor_); + LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); } -AirthingsWavePlus::AirthingsWavePlus() - : PollingComponent(10000), - service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)), - sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {} +AirthingsWavePlus::AirthingsWavePlus() { + this->service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID); + this->sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID); +} } // namespace airthings_wave_plus } // namespace esphome diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 9dd6ed92d5..4acfb9279a 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -2,14 +2,7 @@ #ifdef USE_ESP32 -#include -#include -#include -#include "esphome/components/ble_client/ble_client.h" -#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/core/component.h" -#include "esphome/core/log.h" +#include "esphome/components/airthings_wave_base/airthings_wave_base.h" namespace esphome { namespace airthings_wave_plus { @@ -17,43 +10,25 @@ namespace airthings_wave_plus { static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba"; static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba"; -class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode { +class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase { public: AirthingsWavePlus(); void dump_config() override; - void update() override; - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) override; - - void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; } void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; } - void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } - void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; } void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; } - void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } protected: bool is_valid_radon_value_(uint16_t radon); - bool is_valid_voc_value_(uint16_t voc); bool is_valid_co2_value_(uint16_t co2); - void read_sensors_(uint8_t *value, uint16_t value_len); - void request_read_values_(); + void read_sensors(uint8_t *value, uint16_t value_len) override; - sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *radon_sensor_{nullptr}; sensor::Sensor *radon_long_term_sensor_{nullptr}; - sensor::Sensor *humidity_sensor_{nullptr}; - sensor::Sensor *pressure_sensor_{nullptr}; sensor::Sensor *co2_sensor_{nullptr}; - sensor::Sensor *tvoc_sensor_{nullptr}; - - uint16_t handle_; - esp32_ble_tracker::ESPBTUUID service_uuid_; - esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; struct WavePlusReadings { uint8_t version; diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 727fbe15fb..a5903b1d42 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -1,116 +1,64 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor, ble_client +from esphome.components import sensor, airthings_wave_base from esphome.const import ( DEVICE_CLASS_CARBON_DIOXIDE, - DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_TEMPERATURE, - DEVICE_CLASS_PRESSURE, STATE_CLASS_MEASUREMENT, - UNIT_PERCENT, - UNIT_CELSIUS, - UNIT_HECTOPASCAL, ICON_RADIOACTIVE, CONF_ID, CONF_RADON, CONF_RADON_LONG_TERM, - CONF_HUMIDITY, - CONF_TVOC, CONF_CO2, - CONF_PRESSURE, - CONF_TEMPERATURE, UNIT_BECQUEREL_PER_CUBIC_METER, UNIT_PARTS_PER_MILLION, - UNIT_PARTS_PER_BILLION, - ICON_RADIATOR, ) -DEPENDENCIES = ["ble_client"] +DEPENDENCIES = airthings_wave_base.DEPENDENCIES + +AUTO_LOAD = ["airthings_wave_base"] airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus") AirthingsWavePlus = airthings_wave_plus_ns.class_( - "AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode + "AirthingsWavePlus", airthings_wave_base.AirthingsWaveBase ) -CONFIG_SCHEMA = cv.All( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(AirthingsWavePlus), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( - unit_of_measurement=UNIT_PERCENT, - device_class=DEVICE_CLASS_HUMIDITY, - state_class=STATE_CLASS_MEASUREMENT, - accuracy_decimals=0, - ), - cv.Optional(CONF_RADON): sensor.sensor_schema( - unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER, - icon=ICON_RADIOACTIVE, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema( - unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER, - icon=ICON_RADIOACTIVE, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=2, - device_class=DEVICE_CLASS_TEMPERATURE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema( - unit_of_measurement=UNIT_HECTOPASCAL, - accuracy_decimals=1, - device_class=DEVICE_CLASS_PRESSURE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_CO2): sensor.sensor_schema( - unit_of_measurement=UNIT_PARTS_PER_MILLION, - accuracy_decimals=0, - device_class=DEVICE_CLASS_CARBON_DIOXIDE, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_TVOC): sensor.sensor_schema( - unit_of_measurement=UNIT_PARTS_PER_BILLION, - icon=ICON_RADIATOR, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - } - ) - .extend(cv.polling_component_schema("5min")) - .extend(ble_client.BLE_CLIENT_SCHEMA), +CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(AirthingsWavePlus), + cv.Optional(CONF_RADON): sensor.sensor_schema( + unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER, + icon=ICON_RADIOACTIVE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema( + unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER, + icon=ICON_RADIOACTIVE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CO2): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } ) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) + await airthings_wave_base.wave_base_to_code(var, config) - await ble_client.register_ble_node(var, config) - - if CONF_HUMIDITY in config: - sens = await sensor.new_sensor(config[CONF_HUMIDITY]) - cg.add(var.set_humidity(sens)) if CONF_RADON in config: sens = await sensor.new_sensor(config[CONF_RADON]) cg.add(var.set_radon(sens)) if CONF_RADON_LONG_TERM in config: sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM]) cg.add(var.set_radon_long_term(sens)) - if CONF_TEMPERATURE in config: - sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) - cg.add(var.set_temperature(sens)) - if CONF_PRESSURE in config: - sens = await sensor.new_sensor(config[CONF_PRESSURE]) - cg.add(var.set_pressure(sens)) if CONF_CO2 in config: sens = await sensor.new_sensor(config[CONF_CO2]) cg.add(var.set_co2(sens)) - if CONF_TVOC in config: - sens = await sensor.new_sensor(config[CONF_TVOC]) - cg.add(var.set_tvoc(sens)) diff --git a/tests/test2.yaml b/tests/test2.yaml index fa4b97c7c1..aa3e467816 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -312,7 +312,8 @@ sensor: id: freezer_temp_source reference_voltage: 3.19 number: 0 - - platform: airthings_wave_plus + - id: airthingswp + platform: airthings_wave_plus ble_client_id: airthings01 update_interval: 5min temperature: @@ -329,7 +330,8 @@ sensor: name: Wave Plus CO2 tvoc: name: Wave Plus VOC - - platform: airthings_wave_mini + - id: airthingswm + platform: airthings_wave_mini ble_client_id: airthingsmini01 update_interval: 5min temperature: From cd773a1decb5af2724c79e7272f291c5538bd82e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 22 Jun 2023 01:45:41 +0200 Subject: [PATCH 64/67] Migrate VOC sensors that use ppb to use volatile_organic_compounds_parts device class (#4982) --- esphome/components/airthings_wave_base/__init__.py | 2 ++ esphome/components/ccs811/sensor.py | 4 ++-- esphome/components/sgp30/sensor.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/airthings_wave_base/__init__.py b/esphome/components/airthings_wave_base/__init__.py index 3ff55fc6b0..c935ce108a 100644 --- a/esphome/components/airthings_wave_base/__init__.py +++ b/esphome/components/airthings_wave_base/__init__.py @@ -14,6 +14,7 @@ from esphome.const import ( CONF_TVOC, CONF_PRESSURE, CONF_TEMPERATURE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, UNIT_PARTS_PER_BILLION, ICON_RADIATOR, ) @@ -53,6 +54,7 @@ BASE_SCHEMA = ( unit_of_measurement=UNIT_PARTS_PER_BILLION, icon=ICON_RADIATOR, accuracy_decimals=0, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), } diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index cb5c1108ba..af3e6574ab 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -6,7 +6,7 @@ from esphome.const import ( ICON_RADIATOR, ICON_RESTART, DEVICE_CLASS_CARBON_DIOXIDE, - DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, STATE_CLASS_MEASUREMENT, UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_BILLION, @@ -43,7 +43,7 @@ CONFIG_SCHEMA = ( unit_of_measurement=UNIT_PARTS_PER_BILLION, icon=ICON_RADIATOR, accuracy_decimals=0, - device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema( diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index 0029e2c515..6f8ed42d25 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -11,7 +11,7 @@ from esphome.const import ( CONF_TVOC, ICON_RADIATOR, DEVICE_CLASS_CARBON_DIOXIDE, - DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, STATE_CLASS_MEASUREMENT, UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_BILLION, @@ -49,7 +49,7 @@ CONFIG_SCHEMA = ( unit_of_measurement=UNIT_PARTS_PER_BILLION, icon=ICON_RADIATOR, accuracy_decimals=0, - device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_ECO2_BASELINE): sensor.sensor_schema( From ef8180c8a87313f5f656cbce77011c62c76e4f81 Mon Sep 17 00:00:00 2001 From: "F.D.Castel" Date: Wed, 21 Jun 2023 20:48:17 -0300 Subject: [PATCH 65/67] dashboard: Adds "compressed=1" to /download.bin endpoint. (...) (#4966) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/dashboard/dashboard.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 8d8eb74b4b..22bbe0aae9 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -3,6 +3,7 @@ import binascii import codecs import collections import functools +import gzip import hashlib import hmac import json @@ -485,6 +486,7 @@ class DownloadBinaryRequestHandler(BaseHandler): @bind_config def get(self, configuration=None): type = self.get_argument("type", "firmware.bin") + compressed = self.get_argument("compressed", "0") == "1" storage_path = ext_storage_path(settings.config_dir, configuration) storage_json = StorageJSON.load(storage_path) @@ -534,6 +536,8 @@ class DownloadBinaryRequestHandler(BaseHandler): self.send_error(404) return + filename = filename + ".gz" if compressed else filename + self.set_header("Content-Type", "application/octet-stream") self.set_header("Content-Disposition", f'attachment; filename="{filename}"') self.set_header("Cache-Control", "no-cache") @@ -543,9 +547,20 @@ class DownloadBinaryRequestHandler(BaseHandler): with open(path, "rb") as f: while True: - data = f.read(16384) + # For a 528KB image used as benchmark: + # - using 256KB blocks resulted in the smallest file size. + # - blocks larger than 256KB didn't improve the size of compressed file. + # - blocks smaller than 256KB hindered compression, making the output file larger. + + # Read file in blocks of 256KB. + data = f.read(256 * 1024) + if not data: break + + if compressed: + data = gzip.compress(data, 9) + self.write(data) self.finish() From a2734330e14835b8e02959592610dd3d36255e45 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Jun 2023 11:50:46 +1200 Subject: [PATCH 66/67] Bump version to 2023.6.0b7 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2197b34034..71d136b97f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b6" +__version__ = "2023.6.0b7" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From ceca91d1e77ee20013ab6dc8e5d4b66d2b5ebdc0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:39:10 +1200 Subject: [PATCH 67/67] Bump version to 2023.6.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 71d136b97f..f49cff3b61 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.6.0b7" +__version__ = "2023.6.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = (