From 8a055675af8e428bf7b8338cd0b22d01c252c6dc Mon Sep 17 00:00:00 2001 From: lkomurcu <44089359+lkomurcu@users.noreply.github.com> Date: Sun, 19 Jun 2022 21:39:40 +0200 Subject: [PATCH 01/69] Move gas mbus config option being a define to being a build flag since its used in external libraries. (#3575) --- esphome/components/dsmr/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/dsmr/__init__.py b/esphome/components/dsmr/__init__.py index 7a7681082e..624a0f35a1 100644 --- a/esphome/components/dsmr/__init__.py +++ b/esphome/components/dsmr/__init__.py @@ -79,7 +79,7 @@ async def to_code(config): cg.add(var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds)) cg.add(var.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT].total_milliseconds)) - cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID]) + cg.add_build_flag("-DDSMR_GAS_MBUS_ID=" + str(config[CONF_GAS_MBUS_ID])) # DSMR Parser cg.add_library("glmnet/Dsmr", "0.5") From d0c646c72162ca0421df84bd941d54735282db52 Mon Sep 17 00:00:00 2001 From: ShellAddicted Date: Mon, 20 Jun 2022 01:17:58 +0200 Subject: [PATCH 02/69] Fix: Make MQTT over TLS actually work (#3580) --- esphome/components/mqtt/mqtt_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 157504fb41..aa0cf56c51 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -187,7 +187,7 @@ void MQTTClientComponent::start_connect_() { this->mqtt_backend_.set_credentials(username, password); - this->mqtt_backend_.set_server((uint32_t) this->ip_, this->credentials_.port); + this->mqtt_backend_.set_server(this->credentials_.address.c_str(), this->credentials_.port); if (!this->last_will_.topic.empty()) { this->mqtt_backend_.set_will(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain, this->last_will_.payload.c_str()); From ca13c4c1a657cf614379125e15a679213923abc7 Mon Sep 17 00:00:00 2001 From: kahrendt Date: Sun, 19 Jun 2022 19:27:00 -0400 Subject: [PATCH 03/69] Fix wrong type for voc_state*_ in sgp4x component (#3581) Co-authored-by: Martin <25747549+martgras@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sgp4x/sensor.py | 4 +++- esphome/components/sgp4x/sgp4x.cpp | 4 ++-- esphome/components/sgp4x/sgp4x.h | 6 +++--- platformio.ini | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/esphome/components/sgp4x/sensor.py b/esphome/components/sgp4x/sensor.py index 4855d7f066..1f6c5006a5 100644 --- a/esphome/components/sgp4x/sensor.py +++ b/esphome/components/sgp4x/sensor.py @@ -140,5 +140,7 @@ async def to_code(config): ) ) cg.add_library( - None, None, "https://github.com/Sensirion/arduino-gas-index-algorithm.git" + None, + None, + "https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1", ) diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index a6f57e0342..257c30075f 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -170,8 +170,8 @@ bool SGP4xComponent::measure_gas_indices_(int32_t &voc, int32_t &nox) { // much if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) { voc_algorithm_.get_states(this->voc_state0_, this->voc_state1_); - if ((uint32_t) abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF || - (uint32_t) abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) { + if (std::abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF || + std::abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) { this->seconds_since_last_store_ = 0; this->voc_baselines_storage_.state0 = this->voc_state0_; this->voc_baselines_storage_.state1 = this->voc_state1_; diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index 3060972fc3..3a8d8200a7 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -49,7 +49,7 @@ static const uint16_t SPG41_SELFTEST_TIME = 320; // 320 ms for self test static const uint16_t SGP40_MEASURE_TIME = 30; static const uint16_t SGP41_MEASURE_TIME = 55; // Store anyway if the baseline difference exceeds the max storage diff value -const uint32_t MAXIMUM_STORAGE_DIFF = 50; +const float MAXIMUM_STORAGE_DIFF = 50.0f; class SGP4xComponent; @@ -120,8 +120,8 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se sensor::Sensor *voc_sensor_{nullptr}; VOCGasIndexAlgorithm voc_algorithm_; optional voc_tuning_params_; - int32_t voc_state0_; - int32_t voc_state1_; + float voc_state0_; + float voc_state1_; int32_t voc_index_ = 0; sensor::Sensor *nox_sensor_{nullptr}; diff --git a/platformio.ini b/platformio.ini index fb33087a8a..733e942a84 100644 --- a/platformio.ini +++ b/platformio.ini @@ -40,7 +40,7 @@ lib_deps = wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 ; This is using the repository until a new release is published to PlatformIO - https://github.com/Sensirion/arduino-gas-index-algorithm.git ; Sensirion Gas Index Algorithm Arduino Library + https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE src_filter = From 8f67acadd817807f07a47fd883d307ffa7c15002 Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Mon, 20 Jun 2022 04:17:20 +0400 Subject: [PATCH 04/69] Media Player: added play_media action (#3579) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/media_player/__init__.py | 31 ++++++++++++++++++++ esphome/components/media_player/automation.h | 19 ++++++++++-- tests/test4.yaml | 6 ++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 3456417370..80f5fc558a 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -20,6 +20,9 @@ MediaPlayer = media_player_ns.class_("MediaPlayer") PlayAction = media_player_ns.class_( "PlayAction", automation.Action, cg.Parented.template(MediaPlayer) ) +PlayMediaAction = media_player_ns.class_( + "PlayMediaAction", automation.Action, cg.Parented.template(MediaPlayer) +) ToggleAction = media_player_ns.class_( "ToggleAction", automation.Action, cg.Parented.template(MediaPlayer) ) @@ -44,11 +47,14 @@ CONF_VOLUME = "volume" CONF_ON_IDLE = "on_idle" CONF_ON_PLAY = "on_play" CONF_ON_PAUSE = "on_pause" +CONF_MEDIA_URL = "media_url" StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template()) IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template()) PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template()) PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template()) +IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition) +IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition) async def setup_media_player_core_(var, config): @@ -103,6 +109,25 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPlayer)}) +@automation.register_action( + "media_player.play_media", + PlayMediaAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(MediaPlayer), + cv.Required(CONF_MEDIA_URL): cv.templatable(cv.url), + }, + key=CONF_MEDIA_URL, + ), +) +async def media_player_play_media_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + media_url = await cg.templatable(config[CONF_MEDIA_URL], args, cg.std_string) + cg.add(var.set_media_url(media_url)) + return var + + @automation.register_action("media_player.play", PlayAction, MEDIA_PLAYER_ACTION_SCHEMA) @automation.register_action( "media_player.toggle", ToggleAction, MEDIA_PLAYER_ACTION_SCHEMA @@ -117,6 +142,12 @@ MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPl @automation.register_action( "media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA ) +@automation.register_condition( + "media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA +) +@automation.register_condition( + "media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA +) async def media_player_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index c6deba7dc6..261e93775c 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -17,7 +17,7 @@ namespace media_player { #define MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(TRIGGER_CLASS, TRIGGER_STATE) \ class TRIGGER_CLASS : public Trigger<> { \ public: \ - TRIGGER_CLASS(MediaPlayer *player) { \ + explicit TRIGGER_CLASS(MediaPlayer *player) { \ player->add_on_state_callback([this, player]() { \ if (player->state == MediaPlayerState::MEDIA_PLAYER_STATE_##TRIGGER_STATE) \ this->trigger(); \ @@ -32,6 +32,11 @@ MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ToggleAction, TOGGLE) MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeUpAction, VOLUME_UP) MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeDownAction, VOLUME_DOWN) +template class PlayMediaAction : public Action, public Parented { + TEMPLATABLE_VALUE(std::string, media_url) + void play(Ts... x) override { this->parent_->make_call().set_media_url(this->media_url_.value(x...)).perform(); } +}; + template class VolumeSetAction : public Action, public Parented { TEMPLATABLE_VALUE(float, volume) void play(Ts... x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); } @@ -39,7 +44,7 @@ template class VolumeSetAction : public Action, public Pa class StateTrigger : public Trigger<> { public: - StateTrigger(MediaPlayer *player) { + explicit StateTrigger(MediaPlayer *player) { player->add_on_state_callback([this]() { this->trigger(); }); } }; @@ -48,5 +53,15 @@ MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED) +template class IsIdleCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_IDLE; } +}; + +template class IsPlayingCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; } +}; + } // namespace media_player } // namespace esphome diff --git a/tests/test4.yaml b/tests/test4.yaml index c82673b196..0dfbeed550 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -615,12 +615,18 @@ media_player: mute_pin: GPIO14 on_state: - media_player.play: + - media_player.play_media: http://localhost/media.mp3 + - media_player.play_media: !lambda 'return "http://localhost/media.mp3";' on_idle: - media_player.pause: on_play: - media_player.stop: on_pause: - media_player.toggle: + - wait_until: + media_player.is_idle: + - wait_until: + media_player.is_playing: - media_player.volume_up: - media_player.volume_down: - media_player.volume_set: 50% From 4c37c17df129698e6962bfab52358cc0a16f8f41 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 20 Jun 2022 19:31:17 -0400 Subject: [PATCH 05/69] Fix 2 small issues in BLEClient (#3544) --- esphome/components/ble_client/ble_client.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 7bef0d652c..d06c3c4cad 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -113,6 +113,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es } case ESP_GATTC_OPEN_EVT: { ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); + this->conn_id = param->open.conn_id; if (param->open.status != ESP_GATT_OK) { ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); this->set_states_(espbt::ClientState::IDLE); @@ -122,7 +123,10 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es } case ESP_GATTC_CONNECT_EVT: { ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str()); - this->conn_id = param->connect.conn_id; + if (this->conn_id != param->connect.conn_id) { + ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d", + this->address_str().c_str(), param->connect.conn_id, this->conn_id); + } auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id); if (ret) { ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret); @@ -183,9 +187,10 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es descr->uuid.to_string().c_str()); break; } - uint8_t notify_en = 1; - auto status = esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en), - ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + uint16_t notify_en = 1; + auto status = + esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en), + (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status); } From 020b2c05c828ca0faea7676ff73e8df37674cbb4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 23 Jun 2022 10:34:10 +1200 Subject: [PATCH 06/69] Bump version to 2022.6.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index cdb923a107..f7b593b541 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.1" +__version__ = "2022.6.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From df73170e5ae387960baeb8a27c14d962bad00119 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Sun, 24 Jul 2022 22:06:18 +0200 Subject: [PATCH 07/69] modbus: fix queue deduplicator erasing custom commands (#3650) --- .../modbus_controller/modbus_controller.cpp | 12 ++++++++++-- .../components/modbus_controller/modbus_controller.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 91e0dcc45f..b7fde157d8 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -108,8 +108,7 @@ void ModbusController::queue_command(const ModbusCommandItem &command) { // check if this commmand is already qeued. // not very effective but the queue is never really large for (auto &item : command_queue_) { - if (item->register_address == command.register_address && item->register_count == command.register_count && - item->register_type == command.register_type && item->function_code == command.function_code) { + if (item->is_equal(command)) { ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u", static_cast(command.register_type), command.register_address, command.register_count); // update the payload of the queued command @@ -489,6 +488,15 @@ bool ModbusCommandItem::send() { return true; } +bool ModbusCommandItem::is_equal(const ModbusCommandItem &other) { + // for custom commands we have to check for identical payloads, since + // address/count/type fields will be set to zero + return this->function_code == ModbusFunctionCode::CUSTOM + ? this->payload == other.payload + : other.register_address == this->register_address && other.register_count == this->register_count && + other.register_type == this->register_type && other.function_code == this->function_code; +} + void number_to_payload(std::vector &data, int64_t value, SensorValueType value_type) { switch (value_type) { case SensorValueType::U_WORD: diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 6aecf7f8a4..f67242a68e 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -395,6 +395,8 @@ class ModbusCommandItem { ModbusController *modbusdevice, const std::vector &values, std::function &data)> &&handler = nullptr); + + bool is_equal(const ModbusCommandItem &other); }; /** Modbus controller class. From 311980e0e478612a01020758d2b71bec2cd02fdc Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 31 Jul 2022 18:08:19 -0700 Subject: [PATCH 08/69] Update inkbird_ibsth1_mini.cpp (#3664) --- esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index 76013e28ff..94c22ae84d 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -55,7 +55,7 @@ bool InkbirdIbstH1Mini::parse_device(const esp32_ble_tracker::ESPBTDevice &devic ESP_LOGVV(TAG, "parse_device(): manufacturer data element length is expected to be of length 7"); return false; } - if (mnf_data.data[6] != 8) { + if ((mnf_data.data[6] != 8) && (mnf_data.data[6] != 6)) { ESP_LOGVV(TAG, "parse_device(): unexpected data"); return false; } From eb878710c1e6f750c0ca532ee4d258447bac30d6 Mon Sep 17 00:00:00 2001 From: Bryan Berg Date: Mon, 1 Aug 2022 16:32:02 -0700 Subject: [PATCH 09/69] Add CO device class to binary_sensor (#3656) --- esphome/components/binary_sensor/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 40f95d72f9..63fd34c7d5 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -29,6 +29,7 @@ from esphome.const import ( DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, + DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_COLD, DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, @@ -63,6 +64,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, + DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_COLD, DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, From 50f32a3aa59cf31cf55abc73543ee7f09fa7d96e Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 1 Aug 2022 22:07:32 -0700 Subject: [PATCH 10/69] Use application/json instead of text/json (#3671) --- esphome/components/web_server/web_server.cpp | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 18374d606b..f0a7efd12f 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -353,7 +353,7 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM if (obj->get_object_id() != match.id) continue; std::string data = this->sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } request->send(404); @@ -377,7 +377,7 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const if (obj->get_object_id() != match.id) continue; std::string data = this->text_sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } request->send(404); @@ -406,7 +406,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM if (request->method() == HTTP_GET) { std::string data = this->switch_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->defer([obj]() { obj->toggle(); }); request->send(200); @@ -462,7 +462,7 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con if (obj->get_object_id() != match.id) continue; std::string data = this->binary_sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } request->send(404); @@ -490,7 +490,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc if (request->method() == HTTP_GET) { std::string data = this->fan_json(obj, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->defer([obj]() { obj->toggle().perform(); }); request->send(200); @@ -551,7 +551,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa if (request->method() == HTTP_GET) { std::string data = this->light_json(obj, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->defer([obj]() { obj->toggle().perform(); }); request->send(200); @@ -630,7 +630,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa if (request->method() == HTTP_GET) { std::string data = this->cover_json(obj, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); continue; } @@ -687,7 +687,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM if (request->method() == HTTP_GET) { std::string data = this->number_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } if (match.method != "set") { @@ -741,7 +741,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM if (request->method() == HTTP_GET) { std::string data = this->select_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } @@ -788,7 +788,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url if (request->method() == HTTP_GET) { std::string data = this->climate_json(obj, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); return; } @@ -926,7 +926,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat if (request->method() == HTTP_GET) { std::string data = this->lock_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + request->send(200, "application/json", data.c_str()); } else if (match.method == "lock") { this->defer([obj]() { obj->lock(); }); request->send(200); From e5eaf7a3fecfbb9818890aed7ef7a56cce41ae0e Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 1 Aug 2022 22:09:17 -0700 Subject: [PATCH 11/69] Use correct struct members. (#3672) --- esphome/components/remote_base/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 165da95d67..5ccfc500cf 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -744,7 +744,8 @@ def rc6_binary_sensor(var, config): var.set_data( cg.StructInitializer( RC6Data, - ("device", config[CONF_DEVICE]), + ("mode", 0), + ("toggle", 0), ("address", config[CONF_ADDRESS]), ("command", config[CONF_COMMAND]), ) From bf8eddb13b4a5846c1e6bd21fb574e97e7bce684 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 8 Aug 2022 08:24:12 +1200 Subject: [PATCH 12/69] Bump version to 2022.6.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f7b593b541..f95628735d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.6.2" +__version__ = "2022.6.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From a97e3d827dc190da1fbc87f08b6b15b17a688255 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Aug 2022 15:26:02 +1200 Subject: [PATCH 13/69] Bump version to 2022.8.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6de2581fc8..0593c19672 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.7.0-dev" +__version__ = "2022.8.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From e2c8e69d125a248169bb92fe3a86dbca0f91f611 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Thu, 11 Aug 2022 04:32:55 +0300 Subject: [PATCH 14/69] Improve Web view for Climate components (#3706) --- esphome/components/web_server/server_index.h | 1136 +++++++++--------- esphome/components/web_server/web_server.cpp | 27 +- 2 files changed, 594 insertions(+), 569 deletions(-) diff --git a/esphome/components/web_server/server_index.h b/esphome/components/web_server/server_index.h index 719a804d0c..39c170f15b 100644 --- a/esphome/components/web_server/server_index.h +++ b/esphome/components/web_server/server_index.h @@ -6,568 +6,580 @@ namespace esphome { namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xd9, 0x76, 0xe3, 0xc6, 0x92, 0xe0, 0xf3, - 0x9c, 0x33, 0x7f, 0x30, 0x2f, 0x10, 0xac, 0x56, 0x01, 0x16, 0x08, 0x91, 0x54, 0x6d, 0x06, 0x05, 0xf2, 0xca, 0x55, - 0xe5, 0x5b, 0x65, 0xd7, 0xe6, 0x92, 0xaa, 0xbc, 0xc8, 0x74, 0x09, 0x22, 0x93, 0x22, 0x5c, 0x20, 0x40, 0x03, 0x49, - 0x2d, 0xa6, 0xd0, 0xa7, 0x9f, 0xfa, 0x69, 0xce, 0x99, 0xf5, 0xa1, 0x5f, 0xe6, 0xf4, 0xcb, 0x7c, 0xc4, 0x7c, 0xcf, - 0xfd, 0x81, 0xe9, 0x4f, 0x98, 0x88, 0xc8, 0x05, 0x09, 0x90, 0x5a, 0xec, 0xf6, 0xdc, 0x53, 0x8b, 0x80, 0x5c, 0x23, - 0x23, 0x23, 0x63, 0x4f, 0x68, 0x6f, 0x63, 0x9c, 0x8d, 0xf8, 0xe5, 0x9c, 0x59, 0x53, 0x3e, 0x4b, 0xfa, 0x7b, 0xf2, - 0x7f, 0x16, 0x8d, 0xfb, 0x7b, 0x49, 0x9c, 0x7e, 0xb2, 0x72, 0x96, 0x84, 0xf1, 0x28, 0x4b, 0xad, 0x69, 0xce, 0x26, - 0xe1, 0x38, 0xe2, 0x51, 0x10, 0xcf, 0xa2, 0x53, 0x66, 0xed, 0xf4, 0xf7, 0x66, 0x8c, 0x47, 0xd6, 0x68, 0x1a, 0xe5, - 0x05, 0xe3, 0xe1, 0xfb, 0xc3, 0xaf, 0x5a, 0x8f, 0xfb, 0x7b, 0xc5, 0x28, 0x8f, 0xe7, 0xdc, 0xc2, 0x21, 0xc3, 0x59, - 0x36, 0x5e, 0x24, 0xac, 0x7f, 0x16, 0xe5, 0xd6, 0x3e, 0x0b, 0xdf, 0x9c, 0xfc, 0xc2, 0x46, 0xdc, 0x1f, 0xb3, 0x49, - 0x9c, 0xb2, 0xb7, 0x79, 0x36, 0x67, 0x39, 0xbf, 0xf4, 0x2e, 0xd6, 0x57, 0xc4, 0xac, 0xf0, 0x9e, 0xe9, 0xaa, 0x53, - 0xc6, 0xdf, 0x9c, 0xa7, 0xaa, 0xcf, 0x53, 0x26, 0x26, 0xc9, 0xf2, 0xc2, 0xe3, 0xd7, 0xb4, 0x39, 0xb8, 0x9c, 0x9d, - 0x64, 0x49, 0xe1, 0x1d, 0xe8, 0xfa, 0x79, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0xa7, 0x51, 0x61, 0xb4, 0xf4, 0x3e, 0xad, - 0x69, 0x32, 0x97, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x62, 0xc6, 0xf2, 0xe8, 0x24, 0x61, 0x5e, 0xc1, 0x42, 0x87, 0x79, - 0xdc, 0x8b, 0xdd, 0xb0, 0xcf, 0xad, 0x38, 0xb5, 0xd8, 0x60, 0x9f, 0x51, 0xc9, 0x92, 0xe9, 0x56, 0xc1, 0x46, 0xdb, - 0x03, 0x74, 0x4d, 0xe2, 0xd3, 0x85, 0x7e, 0x3f, 0xcf, 0x63, 0xae, 0x9e, 0xcf, 0xa2, 0x64, 0xc1, 0x82, 0xb8, 0x74, - 0x03, 0x76, 0xc4, 0x87, 0x61, 0xec, 0x3d, 0xa1, 0x41, 0x61, 0xc8, 0xe5, 0x24, 0xcb, 0x1d, 0xc4, 0x55, 0x8c, 0x63, - 0xf3, 0xab, 0x2b, 0x87, 0x87, 0xcb, 0xd2, 0x75, 0x0f, 0x98, 0x3f, 0x8a, 0x92, 0xc4, 0xc1, 0x89, 0xb7, 0xb6, 0x0a, - 0x9c, 0x31, 0xf6, 0xf8, 0x51, 0x3c, 0x74, 0x7b, 0xf1, 0xc4, 0xe1, 0xcc, 0xad, 0xfa, 0x65, 0x13, 0x8b, 0x33, 0x87, - 0xbb, 0xee, 0xa7, 0xeb, 0xfb, 0xe4, 0x8c, 0x2f, 0x72, 0x80, 0xbd, 0xf4, 0xde, 0xa8, 0x99, 0x2f, 0xb0, 0xfe, 0x19, - 0x75, 0xec, 0x01, 0xec, 0x05, 0xb7, 0x3e, 0x84, 0xe7, 0x71, 0x3a, 0xce, 0xce, 0xfd, 0x83, 0x69, 0x04, 0x3f, 0xde, - 0x65, 0x19, 0xdf, 0xda, 0x72, 0xce, 0xb2, 0x78, 0x6c, 0xb5, 0xc3, 0xd0, 0xac, 0xbc, 0x7c, 0x72, 0x70, 0x70, 0x75, - 0xd5, 0x28, 0xf0, 0xd3, 0x88, 0xc7, 0x67, 0x4c, 0x74, 0x06, 0x00, 0x6c, 0xf8, 0x39, 0xe7, 0x6c, 0x7c, 0xc0, 0x2f, - 0x13, 0x28, 0x65, 0x8c, 0x17, 0x36, 0xac, 0xf1, 0x69, 0x36, 0x02, 0xb4, 0xa5, 0x06, 0xe2, 0xa1, 0x69, 0xce, 0xe6, - 0x49, 0x34, 0x62, 0x58, 0x0f, 0x23, 0x55, 0x3d, 0xaa, 0x46, 0xde, 0x57, 0xa1, 0xd8, 0x5e, 0xc7, 0xf5, 0x62, 0x16, - 0xa6, 0xec, 0xdc, 0x7a, 0x15, 0xcd, 0x7b, 0xa3, 0x24, 0x2a, 0x0a, 0xa0, 0xd7, 0x25, 0x2d, 0x21, 0x5f, 0x8c, 0x80, - 0x40, 0x68, 0x81, 0x4b, 0x44, 0xd3, 0x34, 0x2e, 0xfc, 0x8f, 0x9b, 0xa3, 0xa2, 0x78, 0xc7, 0x8a, 0x45, 0xc2, 0x37, - 0x43, 0xd8, 0x0b, 0xbe, 0x11, 0x86, 0x5f, 0xb9, 0x7c, 0x9a, 0x67, 0xe7, 0xd6, 0xb3, 0x3c, 0x87, 0xe6, 0x36, 0x4c, - 0x29, 0x1a, 0x58, 0x71, 0x61, 0xa5, 0x19, 0xb7, 0xf4, 0x60, 0xb8, 0x81, 0xbe, 0xf5, 0xbe, 0x60, 0xd6, 0xf1, 0x22, - 0x2d, 0xa2, 0x09, 0x83, 0xa6, 0xc7, 0x56, 0x96, 0x5b, 0xc7, 0x30, 0xe8, 0x31, 0x6c, 0x59, 0xc1, 0xe1, 0xd4, 0xf8, - 0xb6, 0xdb, 0xa3, 0xb9, 0xa0, 0xf0, 0x90, 0x5d, 0xf0, 0x90, 0x95, 0x40, 0x98, 0x56, 0xa1, 0x97, 0xe1, 0xb8, 0xcb, - 0x04, 0x0a, 0x58, 0x18, 0x33, 0x24, 0x59, 0xc7, 0x6c, 0xac, 0x37, 0xe7, 0xc3, 0xd6, 0x96, 0xc6, 0x35, 0xe0, 0xc4, - 0x81, 0xb6, 0x45, 0xa3, 0xad, 0x27, 0x16, 0x5e, 0x43, 0x91, 0xeb, 0x31, 0x5f, 0xa2, 0xef, 0xe0, 0x32, 0x1d, 0xd5, - 0xc7, 0x86, 0xca, 0x92, 0x67, 0x07, 0x3c, 0x8f, 0xd3, 0x53, 0x00, 0x42, 0xce, 0x64, 0x36, 0x29, 0x4b, 0xb1, 0xf9, - 0x4f, 0x58, 0xc8, 0xc2, 0x3e, 0x8e, 0x9e, 0x33, 0xc7, 0x2e, 0xa8, 0x87, 0x1d, 0x86, 0x88, 0x7a, 0x20, 0x30, 0x36, - 0x60, 0x01, 0xdb, 0xb6, 0x6d, 0xef, 0x2b, 0xd7, 0x3b, 0x47, 0x0a, 0xf2, 0x7d, 0x9f, 0xc8, 0x57, 0x74, 0x8e, 0xc3, - 0x0e, 0x02, 0xed, 0x27, 0x2c, 0x3d, 0xe5, 0xd3, 0x01, 0x3b, 0x6a, 0x0f, 0x03, 0x0e, 0x50, 0x8d, 0x17, 0x23, 0xe6, - 0x20, 0x3d, 0x7a, 0x05, 0x1e, 0x9f, 0x6d, 0x07, 0xa6, 0xc0, 0x8d, 0xd9, 0xa0, 0x35, 0xd6, 0xb6, 0xc6, 0x55, 0x24, - 0xaa, 0x00, 0x43, 0x3a, 0xb7, 0xe1, 0x84, 0x9d, 0xb0, 0xdc, 0x80, 0x43, 0x37, 0xeb, 0xd5, 0x76, 0x70, 0x01, 0x3b, - 0x04, 0xfd, 0xac, 0xc9, 0x22, 0x1d, 0xf1, 0x18, 0x18, 0x97, 0xbd, 0x0d, 0xe0, 0x8a, 0x9d, 0xd3, 0x1b, 0x67, 0xbb, - 0xa5, 0xeb, 0xc4, 0xee, 0x36, 0x3b, 0x2a, 0xb6, 0x3b, 0x43, 0x0f, 0xa1, 0xd4, 0xc8, 0x97, 0x0b, 0x8f, 0x61, 0x81, - 0x70, 0x46, 0x98, 0x3e, 0x9e, 0x1f, 0x06, 0xcc, 0x5f, 0xa5, 0xe3, 0x90, 0xfb, 0xb3, 0x68, 0x8e, 0xab, 0x61, 0x44, - 0x03, 0x51, 0x3a, 0x42, 0xe8, 0x6a, 0xfb, 0x82, 0x18, 0xf3, 0x2b, 0x12, 0x70, 0x01, 0x21, 0x70, 0x66, 0x9f, 0x45, - 0xa3, 0x29, 0x1c, 0xf1, 0x0a, 0x71, 0x63, 0x75, 0x1c, 0x46, 0x39, 0x8b, 0x38, 0x7b, 0x96, 0x30, 0x7c, 0xc3, 0x1d, - 0x80, 0x9e, 0xb6, 0xeb, 0x15, 0xea, 0xdc, 0x25, 0x31, 0x7f, 0x9d, 0xc1, 0x3c, 0x3d, 0x41, 0x24, 0x40, 0xc5, 0xc5, - 0xd6, 0x56, 0x8c, 0x24, 0xb2, 0xcf, 0x61, 0xb7, 0x4e, 0x16, 0xc0, 0x04, 0xec, 0x14, 0x5b, 0xd8, 0x80, 0x6d, 0x2f, - 0xf6, 0x39, 0x20, 0xf1, 0x49, 0x96, 0x72, 0x18, 0x0e, 0xe0, 0xd5, 0x14, 0xe4, 0x47, 0xf3, 0x39, 0x4b, 0xc7, 0x4f, - 0xa6, 0x71, 0x32, 0x06, 0x6c, 0x94, 0xb0, 0xde, 0x8c, 0x85, 0xb0, 0x4e, 0x58, 0x4c, 0x70, 0xf3, 0x8a, 0x68, 0xfb, - 0x90, 0x90, 0x79, 0x68, 0xdb, 0x3d, 0xe4, 0x40, 0x72, 0x15, 0xc8, 0x83, 0x68, 0xe3, 0xde, 0x01, 0xeb, 0x2f, 0x5c, - 0xbe, 0x1d, 0xc6, 0x7a, 0x1b, 0x25, 0x82, 0x9f, 0x20, 0xa7, 0x01, 0xfc, 0x33, 0xe0, 0x81, 0x3d, 0x64, 0x5c, 0xdf, - 0x49, 0xae, 0x93, 0x32, 0xb5, 0x42, 0x40, 0xc0, 0x08, 0x39, 0x88, 0xc4, 0xc1, 0xdb, 0x2c, 0xb9, 0x9c, 0xc4, 0x49, - 0x72, 0xb0, 0x98, 0xcf, 0xb3, 0x9c, 0x7b, 0x5f, 0x87, 0x4b, 0x9e, 0x55, 0x6b, 0xa5, 0x43, 0x5e, 0x9c, 0xc7, 0x1c, - 0x11, 0xea, 0x2e, 0x47, 0x11, 0x6c, 0xf5, 0x97, 0x59, 0x96, 0xb0, 0x28, 0x85, 0x65, 0xb0, 0x81, 0x6d, 0x07, 0xe9, - 0x22, 0x49, 0x7a, 0x27, 0x30, 0xec, 0xa7, 0x1e, 0x55, 0x0b, 0x8e, 0x1f, 0xd0, 0xf3, 0x7e, 0x9e, 0x47, 0x97, 0xd0, - 0x10, 0xdb, 0x00, 0x2d, 0xc2, 0x6e, 0x7d, 0x7d, 0xf0, 0xe6, 0xb5, 0x2f, 0x08, 0x3f, 0x9e, 0x5c, 0x02, 0xa0, 0x65, - 0xc5, 0x35, 0x27, 0x79, 0x36, 0x6b, 0x4c, 0x8d, 0x78, 0x88, 0x43, 0xd6, 0xbb, 0x06, 0x84, 0x98, 0x46, 0x86, 0x5d, - 0x62, 0x26, 0x04, 0xaf, 0x89, 0x9e, 0x65, 0x25, 0x9e, 0x81, 0x01, 0x3e, 0x04, 0xa2, 0x18, 0xa6, 0xbc, 0x19, 0x5a, - 0x9e, 0x5f, 0x2e, 0xe3, 0x90, 0xe0, 0x9c, 0xa3, 0xfc, 0x45, 0x18, 0x47, 0x11, 0xcc, 0xbe, 0x14, 0x03, 0x96, 0x0a, - 0xe2, 0xb8, 0x2c, 0xbd, 0x44, 0x13, 0x31, 0x72, 0x3c, 0x64, 0x28, 0x1c, 0x8e, 0xd1, 0xd5, 0x15, 0x83, 0x17, 0xd7, - 0xfb, 0x26, 0x5c, 0x46, 0x6a, 0x3d, 0x28, 0xa1, 0xf0, 0x7c, 0x05, 0x82, 0x4f, 0xa0, 0x24, 0x3b, 0x03, 0x39, 0x08, - 0x70, 0x7e, 0xed, 0x81, 0xfc, 0x4f, 0x10, 0x8a, 0x8d, 0x8e, 0x07, 0x12, 0xf4, 0xc9, 0x34, 0x4a, 0x4f, 0xd9, 0x38, - 0x48, 0x58, 0x29, 0x39, 0xef, 0xbe, 0x05, 0x7b, 0x0c, 0xe4, 0x54, 0x58, 0xcf, 0x0f, 0x5f, 0xbd, 0x94, 0x3b, 0x57, - 0x63, 0xc6, 0xb0, 0x49, 0x0b, 0x10, 0xab, 0xc0, 0xb6, 0x25, 0x3b, 0x7e, 0xc6, 0x15, 0xf7, 0x16, 0x25, 0x71, 0xf1, - 0x7e, 0x0e, 0x2a, 0x06, 0x7b, 0x0b, 0xc3, 0xc0, 0xf4, 0x21, 0x4c, 0x45, 0xe5, 0x30, 0x9f, 0xa8, 0x18, 0xeb, 0x22, - 0xe8, 0x2c, 0x56, 0x2a, 0x5e, 0x33, 0xc7, 0x2d, 0x81, 0x54, 0x79, 0x3c, 0xb2, 0xa2, 0xf1, 0xf8, 0x45, 0x1a, 0xf3, - 0x38, 0x4a, 0xe2, 0xdf, 0x08, 0x93, 0x4b, 0xa4, 0x31, 0xde, 0x93, 0x9b, 0x00, 0x6b, 0xa7, 0x1e, 0x89, 0xab, 0x98, - 0xec, 0x06, 0x21, 0x43, 0x70, 0xcb, 0x24, 0x3c, 0x1a, 0x4a, 0xf0, 0x12, 0x7f, 0xbe, 0x28, 0xa6, 0x88, 0x58, 0x39, - 0x30, 0x32, 0xf2, 0xec, 0xa4, 0x60, 0xf9, 0x19, 0x1b, 0x6b, 0x0a, 0x28, 0x60, 0x55, 0xd4, 0x1c, 0x94, 0x17, 0x9a, - 0xd1, 0x51, 0x32, 0x94, 0xc1, 0x50, 0x3d, 0x93, 0xcd, 0x32, 0x49, 0xcc, 0x5a, 0xc3, 0xd1, 0x5c, 0xc0, 0x11, 0x4a, - 0x85, 0xe4, 0x04, 0x45, 0xa8, 0x56, 0x38, 0x05, 0x2e, 0x04, 0x52, 0xc1, 0x3c, 0xe6, 0x4a, 0x92, 0x3d, 0x5b, 0x90, - 0x48, 0x28, 0xa0, 0x23, 0x1c, 0x64, 0x82, 0xb4, 0x70, 0xe1, 0x54, 0x01, 0x97, 0x97, 0xe0, 0x0a, 0x2e, 0xa2, 0xd4, - 0x1c, 0x24, 0x80, 0xf0, 0x1b, 0x21, 0x0b, 0x7d, 0x6c, 0x41, 0x64, 0xe0, 0xeb, 0x9d, 0x07, 0xc4, 0xca, 0x75, 0x57, - 0x0b, 0xf1, 0xae, 0x01, 0x1b, 0x27, 0x46, 0x7a, 0xf2, 0x36, 0xb8, 0x9f, 0x66, 0xfb, 0xa3, 0x11, 0x2b, 0x8a, 0x2c, - 0xdf, 0xda, 0xda, 0xa0, 0xf6, 0xd7, 0x29, 0x5a, 0x80, 0x49, 0x57, 0xf3, 0x3a, 0xbb, 0x20, 0x09, 0x6e, 0x8a, 0x15, - 0x25, 0xd3, 0x03, 0xfb, 0xe3, 0x47, 0xe0, 0xd9, 0x9e, 0x44, 0x03, 0x60, 0x7d, 0x55, 0xf1, 0x13, 0xfa, 0x4c, 0x1d, - 0x33, 0x6b, 0xf5, 0x4b, 0xa7, 0x0e, 0x92, 0x07, 0xc3, 0xba, 0xa5, 0xb1, 0xa1, 0x6b, 0x87, 0xc6, 0xdd, 0x90, 0x02, - 0x72, 0x79, 0x4a, 0x22, 0xdb, 0xd8, 0x46, 0xd0, 0xda, 0x4a, 0x8f, 0x50, 0xaf, 0x56, 0x93, 0x13, 0xa0, 0x47, 0x6c, - 0xd8, 0x93, 0xf5, 0x61, 0x21, 0x30, 0x97, 0xb3, 0x5f, 0x17, 0xac, 0xe0, 0x82, 0x74, 0x61, 0xdc, 0x1c, 0xc6, 0x2d, - 0x57, 0xb4, 0xc3, 0x9a, 0xee, 0xb8, 0x0e, 0xb6, 0x37, 0x73, 0x94, 0x63, 0x05, 0x52, 0xf2, 0xcd, 0xe4, 0x84, 0xb0, - 0x32, 0xf7, 0xea, 0xea, 0x1b, 0x35, 0x48, 0xb5, 0x95, 0x5a, 0x07, 0x6a, 0xec, 0x89, 0xad, 0x9a, 0x8c, 0x6d, 0x57, - 0x0a, 0xd4, 0x8d, 0x4e, 0xaf, 0x46, 0x07, 0x70, 0xe6, 0xda, 0x9a, 0xa4, 0x2b, 0x65, 0xfb, 0xad, 0xc2, 0xe9, 0x1b, - 0x31, 0x32, 0x69, 0xa3, 0xec, 0x76, 0xea, 0x51, 0x27, 0x1e, 0xda, 0xae, 0xd4, 0x55, 0x8c, 0x61, 0x51, 0x67, 0x0c, - 0x4d, 0xa8, 0xe7, 0xba, 0x8b, 0xad, 0x89, 0x8a, 0x85, 0x6a, 0xaf, 0x95, 0x01, 0xc1, 0xc3, 0x23, 0x50, 0x4e, 0xd6, - 0xda, 0x07, 0xaf, 0xa3, 0x19, 0x43, 0x8c, 0x7a, 0xd7, 0x35, 0x90, 0x06, 0x04, 0x34, 0x19, 0x36, 0xc5, 0x1b, 0x77, - 0x85, 0xd6, 0x54, 0x3f, 0x5f, 0x31, 0x68, 0x11, 0xa0, 0x5f, 0x97, 0x6b, 0xb6, 0x88, 0xe4, 0xa6, 0x24, 0x67, 0x85, - 0x1f, 0x51, 0x26, 0xf6, 0x84, 0x04, 0x3c, 0x2c, 0x1e, 0xb6, 0xbf, 0xb1, 0x71, 0xb2, 0x15, 0x53, 0x6b, 0xe4, 0xc8, - 0x53, 0x00, 0xcf, 0x24, 0x04, 0x80, 0x5d, 0xd2, 0xcf, 0xda, 0xc1, 0x42, 0xb4, 0x1d, 0x20, 0x1d, 0xf8, 0x93, 0x24, - 0xe2, 0x4e, 0x67, 0xa7, 0xed, 0x02, 0x1d, 0x02, 0x13, 0x07, 0x19, 0x01, 0xea, 0x7d, 0xb5, 0x14, 0x86, 0x4b, 0x89, - 0x5d, 0xee, 0x83, 0x52, 0x34, 0x8d, 0x27, 0xdc, 0xc9, 0x50, 0x88, 0xb8, 0x25, 0x4b, 0x40, 0xc8, 0xe8, 0x73, 0x05, - 0x5c, 0x82, 0x0b, 0xee, 0x22, 0xaa, 0x35, 0x43, 0x53, 0x90, 0x12, 0x97, 0x22, 0x29, 0xa8, 0x20, 0x30, 0x98, 0x4a, - 0x4f, 0x51, 0x14, 0xc8, 0xb7, 0x78, 0x20, 0x06, 0x0d, 0x56, 0x34, 0xca, 0x78, 0x10, 0xaf, 0x16, 0x82, 0x18, 0xf6, - 0x79, 0xf6, 0x32, 0x3b, 0x67, 0xf9, 0x93, 0x08, 0x61, 0x0f, 0x44, 0xf7, 0x12, 0x38, 0x3d, 0x31, 0x74, 0xd6, 0x53, - 0xb4, 0x72, 0x46, 0x8b, 0x86, 0x8d, 0x98, 0xc5, 0x28, 0x08, 0x41, 0xca, 0x11, 0xee, 0x53, 0x3c, 0x52, 0x74, 0xf6, - 0x50, 0x94, 0x30, 0x4d, 0x5b, 0xfb, 0x2f, 0xeb, 0xb4, 0x05, 0x23, 0xcc, 0x15, 0xb5, 0xd6, 0x4f, 0xac, 0xeb, 0x49, - 0xd9, 0xec, 0x48, 0xda, 0x32, 0x84, 0x19, 0xc8, 0x8f, 0xab, 0xab, 0x4a, 0x49, 0x07, 0x61, 0xaa, 0xb9, 0x39, 0x6a, - 0x4e, 0xe2, 0x48, 0xb8, 0x25, 0x08, 0x23, 0x54, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x59, 0xe4, 0xc5, 0x3d, 0xa7, 0x21, - 0x8e, 0x00, 0x8a, 0x59, 0x4d, 0x22, 0x0d, 0x78, 0xa0, 0x2b, 0x50, 0x28, 0x29, 0x69, 0xe4, 0x55, 0x4d, 0x04, 0xc4, - 0xe9, 0x98, 0xe5, 0xc2, 0x40, 0x93, 0x32, 0x14, 0x26, 0x4c, 0x81, 0xa0, 0xd9, 0x18, 0x38, 0xbc, 0x5a, 0x00, 0xa8, - 0x27, 0xfe, 0x34, 0x2b, 0xb8, 0xae, 0x33, 0xa1, 0x8f, 0xaf, 0xae, 0x62, 0x61, 0x2f, 0x22, 0x01, 0xe4, 0x6c, 0x96, - 0x9d, 0xb1, 0x35, 0x50, 0xf7, 0xd4, 0x60, 0x26, 0xc8, 0xc6, 0x30, 0x20, 0x44, 0x41, 0xb4, 0xcc, 0x93, 0x78, 0xc4, - 0xb4, 0x94, 0x9a, 0xf9, 0xa0, 0xd0, 0xb1, 0x0b, 0xe0, 0x11, 0xcc, 0xed, 0xf7, 0xfb, 0x6d, 0xaf, 0xe3, 0x96, 0x02, - 0xe1, 0xcb, 0x15, 0x8c, 0xde, 0x20, 0x1f, 0xa5, 0x0a, 0xbe, 0x8e, 0x17, 0x70, 0xd7, 0x10, 0x8a, 0x5c, 0xd8, 0x49, - 0x9e, 0x64, 0xc4, 0xae, 0x37, 0x86, 0x41, 0x39, 0x53, 0x8c, 0x1b, 0x55, 0x5c, 0x71, 0x6c, 0xdf, 0x69, 0xb4, 0x69, - 0x72, 0x52, 0x27, 0x4c, 0x6d, 0x8c, 0xdc, 0xf3, 0x42, 0x5b, 0xc0, 0xe6, 0xf6, 0xa0, 0x96, 0x48, 0xd5, 0x40, 0xeb, - 0x00, 0xa1, 0xb0, 0x74, 0x9d, 0x95, 0x25, 0x55, 0x9d, 0x25, 0x13, 0xd7, 0x07, 0xe8, 0x0d, 0x93, 0x60, 0xae, 0x43, - 0xc1, 0x81, 0x64, 0x08, 0x1c, 0x2d, 0x32, 0xb1, 0x5f, 0x4f, 0x60, 0x7b, 0x4e, 0xa2, 0xd1, 0x27, 0x0d, 0x6e, 0x85, - 0xf6, 0x26, 0x19, 0x38, 0x8d, 0x92, 0xd0, 0x60, 0x57, 0xe6, 0xba, 0x15, 0x87, 0xae, 0x1d, 0x14, 0x30, 0xc8, 0x56, - 0xc8, 0xbe, 0xb9, 0xd1, 0x4d, 0x6a, 0x97, 0xe4, 0xa1, 0xec, 0x27, 0x4d, 0x25, 0x37, 0x90, 0x1c, 0x57, 0xdc, 0x80, - 0x2b, 0xc2, 0x83, 0xad, 0x69, 0x40, 0x02, 0x74, 0x57, 0x8e, 0xe3, 0xe2, 0x7a, 0x14, 0xfc, 0xa9, 0x60, 0x3e, 0x35, - 0x66, 0xba, 0x15, 0x52, 0xcd, 0xe1, 0xa4, 0x1a, 0xac, 0x41, 0x93, 0xca, 0x83, 0x62, 0x35, 0xdf, 0xa0, 0xa2, 0x42, - 0x14, 0x7f, 0x2a, 0xaa, 0x50, 0x05, 0x43, 0x30, 0x0a, 0x2f, 0x97, 0x04, 0x97, 0xad, 0xb2, 0x16, 0xc9, 0x53, 0x63, - 0x12, 0xa9, 0x9a, 0xe4, 0x32, 0x50, 0xb0, 0xe8, 0xb4, 0xfa, 0x52, 0x13, 0x57, 0x2c, 0x37, 0x0d, 0x35, 0x33, 0xc9, - 0x95, 0x35, 0xe1, 0x14, 0x68, 0x77, 0x29, 0xed, 0xdd, 0x5c, 0x4f, 0xa1, 0xd6, 0x53, 0xf8, 0x86, 0x0d, 0x65, 0xd2, - 0x76, 0x3e, 0x00, 0x75, 0xbf, 0x56, 0x89, 0xfa, 0xa9, 0x8f, 0x8c, 0xd9, 0xd5, 0x4c, 0x17, 0x18, 0x8a, 0x24, 0x93, - 0x74, 0x20, 0xe9, 0x0d, 0xd9, 0x46, 0x65, 0x19, 0x65, 0xae, 0x38, 0x20, 0x35, 0xab, 0x34, 0xf3, 0x52, 0xb7, 0xa1, - 0xbf, 0x97, 0xa5, 0xc4, 0x13, 0x17, 0x98, 0x89, 0xbd, 0x9b, 0x70, 0xe3, 0xa5, 0x61, 0x26, 0xb4, 0x5f, 0xa1, 0xec, - 0xd4, 0x30, 0x94, 0x4a, 0x16, 0x88, 0x63, 0xe3, 0x6b, 0xa5, 0x19, 0x64, 0xfe, 0x1a, 0x7d, 0x0a, 0x40, 0x49, 0x60, - 0xf3, 0x35, 0x96, 0xbc, 0x28, 0xac, 0xe3, 0x71, 0x83, 0xf0, 0x58, 0xb1, 0xd0, 0x1a, 0xcb, 0xd7, 0xf2, 0x2c, 0xf6, - 0x6b, 0x26, 0xa1, 0x89, 0xc9, 0x62, 0x50, 0x04, 0xb6, 0x72, 0x44, 0x54, 0xb2, 0x2d, 0x19, 0x24, 0x64, 0x90, 0xae, - 0x22, 0xbd, 0x36, 0x92, 0x81, 0xeb, 0x54, 0x70, 0xb4, 0x74, 0x18, 0x46, 0x0e, 0x1a, 0xee, 0xb4, 0x17, 0x2b, 0x88, - 0x6c, 0xea, 0x9b, 0x44, 0x8a, 0x68, 0x9c, 0x16, 0xa8, 0xc2, 0x99, 0x32, 0xdd, 0x71, 0x60, 0x39, 0xc0, 0xf6, 0x57, - 0x48, 0x6f, 0xad, 0xda, 0xe9, 0xfa, 0x95, 0xc1, 0x77, 0x75, 0x95, 0x20, 0x3d, 0x08, 0x85, 0x17, 0xf6, 0x6c, 0xa0, - 0x78, 0xef, 0xfe, 0x4b, 0x6c, 0x45, 0xfa, 0x67, 0x55, 0x52, 0x59, 0x0a, 0x35, 0xca, 0xad, 0xef, 0x13, 0x33, 0x5d, - 0x8b, 0xaa, 0xe2, 0xc0, 0xe0, 0xea, 0x07, 0x4a, 0x60, 0x57, 0x4b, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x05, - 0x19, 0x2f, 0x1b, 0xeb, 0x4c, 0xc8, 0xad, 0x2d, 0xd3, 0x66, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x38, 0x17, 0x97, - 0x29, 0x69, 0x30, 0x4f, 0x94, 0x38, 0x5a, 0x31, 0x40, 0xdb, 0x3d, 0xb4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, - 0x45, 0x58, 0xda, 0xc8, 0x9f, 0xa4, 0xd2, 0x01, 0xeb, 0x42, 0x15, 0x92, 0x8c, 0x70, 0x53, 0x17, 0x2d, 0x46, 0x53, - 0x86, 0x2e, 0x70, 0xa5, 0x4f, 0x98, 0xbc, 0x67, 0x03, 0xd7, 0x2d, 0x06, 0x66, 0xeb, 0x61, 0x2f, 0x9b, 0xdd, 0x6b, - 0xea, 0x3f, 0xec, 0x11, 0xf0, 0xb6, 0x99, 0xaa, 0x2b, 0x1b, 0xef, 0x92, 0x45, 0xa2, 0x87, 0x6d, 0xdd, 0xd8, 0x52, - 0xd7, 0xef, 0x35, 0xcc, 0xeb, 0xca, 0x30, 0xaf, 0x09, 0xd5, 0x86, 0x1c, 0x56, 0x66, 0x0e, 0x33, 0x0d, 0x79, 0xb1, - 0x83, 0x6e, 0x4f, 0x38, 0x85, 0xc0, 0x88, 0xd0, 0xfa, 0xa0, 0xa2, 0x06, 0x42, 0x25, 0x57, 0x52, 0x35, 0x5b, 0x24, - 0x63, 0x09, 0x2c, 0x98, 0xb0, 0x5c, 0xd2, 0xd1, 0x79, 0x9c, 0x24, 0x55, 0xe9, 0x9f, 0xca, 0xe0, 0xc5, 0xb0, 0xb7, - 0xb1, 0x76, 0xb1, 0xa2, 0x85, 0x02, 0xc1, 0xd5, 0x4a, 0xd8, 0x7b, 0xc7, 0xad, 0xf6, 0x5d, 0x78, 0x1c, 0xb9, 0xe9, - 0x8d, 0x80, 0x7a, 0xf4, 0xb0, 0x6a, 0xd2, 0xde, 0x7f, 0x86, 0x2e, 0x35, 0x63, 0x3d, 0x28, 0xce, 0xa8, 0xf8, 0x77, - 0xe9, 0x53, 0xbf, 0x73, 0x79, 0xb7, 0x8a, 0xae, 0xa6, 0x43, 0x45, 0x39, 0x3e, 0x4c, 0x17, 0x4b, 0x5b, 0x39, 0x02, - 0x72, 0x3d, 0x2c, 0x72, 0x01, 0x13, 0x35, 0x58, 0x50, 0x8a, 0x55, 0x6b, 0x61, 0xf7, 0xf2, 0x36, 0x67, 0x0e, 0xb9, - 0xc2, 0x45, 0xff, 0x27, 0xd9, 0x6c, 0x8e, 0x9a, 0x59, 0x83, 0xa8, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa6, 0xac, - 0xd6, 0x87, 0x4e, 0x64, 0x8d, 0x9e, 0xb4, 0xa1, 0x0c, 0x06, 0xd5, 0x42, 0x17, 0xd5, 0xf5, 0xe6, 0x26, 0x8b, 0x59, - 0x47, 0xe3, 0x3e, 0xc9, 0x6d, 0xad, 0x4d, 0x7a, 0x1a, 0x07, 0xc4, 0x93, 0x24, 0xc1, 0x9b, 0x04, 0x50, 0x56, 0xc8, - 0x59, 0x96, 0x0d, 0xf4, 0x2d, 0xcb, 0x12, 0xf7, 0xef, 0xdb, 0xde, 0x7e, 0xcd, 0xb2, 0xf6, 0xf6, 0xaf, 0x37, 0x91, - 0xab, 0x3a, 0x69, 0x41, 0x1e, 0x0d, 0xa1, 0x68, 0x45, 0xa7, 0x0c, 0x97, 0xb3, 0x6c, 0xcc, 0x02, 0x1b, 0xba, 0xa7, - 0x76, 0xa9, 0xa4, 0x32, 0x1c, 0x8e, 0x94, 0x39, 0xcb, 0x77, 0x75, 0x4f, 0x6a, 0xb0, 0x0f, 0x24, 0xa0, 0xd5, 0x85, - 0xef, 0xc2, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0x21, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xc9, 0x48, 0x1a, 0xab, 0x21, - 0xa4, 0xbe, 0x1b, 0x7c, 0x17, 0xdc, 0xde, 0xa3, 0xb2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0xf2, 0x88, 0xbd, - 0x34, 0xa7, 0x03, 0x89, 0x53, 0x00, 0x66, 0x0e, 0x41, 0x92, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0x83, 0x97, 0xca, 0xa4, - 0xce, 0xc0, 0x84, 0x00, 0x23, 0x3f, 0x89, 0x79, 0x0b, 0xe3, 0x91, 0xb6, 0xb7, 0x14, 0x15, 0xe8, 0x57, 0x24, 0xbf, - 0x74, 0xa9, 0xac, 0x41, 0xef, 0x63, 0x78, 0x0c, 0xcd, 0x36, 0x37, 0x97, 0xce, 0xab, 0x88, 0x4f, 0xfd, 0x3c, 0x4a, - 0xc7, 0xd9, 0xcc, 0x71, 0xb7, 0x6d, 0xdb, 0xf5, 0x0b, 0xb2, 0x44, 0xbe, 0x70, 0xcb, 0xcd, 0x63, 0x6f, 0xca, 0x42, - 0x7b, 0x60, 0x6f, 0x7f, 0xf4, 0xde, 0xb2, 0xf0, 0x78, 0x6f, 0x73, 0x39, 0x65, 0x65, 0xff, 0xd8, 0xbb, 0xd0, 0x3e, - 0x77, 0xef, 0x2d, 0x72, 0x19, 0xe8, 0x15, 0xf6, 0x2f, 0x24, 0x18, 0x40, 0x6e, 0xe4, 0x7f, 0x07, 0x2e, 0xf7, 0x9e, - 0x02, 0x22, 0xd2, 0x4f, 0x7b, 0x75, 0x65, 0x67, 0xe4, 0x31, 0xb0, 0x37, 0xb4, 0xb1, 0xba, 0xb5, 0x55, 0x89, 0xf9, - 0xaa, 0xd4, 0x1b, 0xb1, 0xb0, 0x66, 0xa9, 0x7b, 0xef, 0x29, 0xb4, 0x52, 0x3f, 0xc8, 0x23, 0x46, 0x42, 0x73, 0x55, - 0x4f, 0x70, 0x8c, 0x23, 0xbe, 0xfe, 0x58, 0x1f, 0x09, 0x2f, 0x85, 0x1f, 0x83, 0xf6, 0x12, 0x81, 0xf8, 0x06, 0x03, - 0xc7, 0x3b, 0x0c, 0x77, 0xf6, 0x9c, 0x41, 0xe0, 0x6c, 0xb4, 0x5a, 0x57, 0x3f, 0xed, 0x1c, 0xfd, 0x1c, 0xb5, 0x7e, - 0xdb, 0x6f, 0xfd, 0x38, 0x74, 0xaf, 0x9c, 0x9f, 0x76, 0x06, 0x47, 0xf2, 0xed, 0xe8, 0xe7, 0xfe, 0x4f, 0xc5, 0xf0, - 0x73, 0x51, 0xb8, 0xe9, 0xba, 0x3b, 0xa7, 0x60, 0x29, 0x85, 0x3b, 0xad, 0x56, 0x1f, 0x9e, 0x16, 0xf0, 0x84, 0x3f, - 0x2f, 0xe1, 0xc7, 0xd5, 0x91, 0xf5, 0x1f, 0x7e, 0x4a, 0xff, 0xe3, 0x4f, 0xf9, 0x10, 0xc7, 0x3c, 0xfa, 0xf9, 0xa7, - 0xc2, 0xbe, 0xd7, 0x0f, 0x77, 0x86, 0xdb, 0xae, 0xa3, 0x6b, 0x3e, 0x0f, 0xab, 0x47, 0x68, 0x75, 0xf4, 0xb3, 0x7c, - 0xb3, 0xef, 0x1d, 0xef, 0xf5, 0xc3, 0xe1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x13, 0xe7, 0x99, - 0xc3, 0xe8, 0xf7, 0xe0, 0xe7, 0x19, 0xfc, 0xb4, 0xe1, 0xe7, 0x29, 0xfc, 0xfc, 0x19, 0xba, 0x09, 0xff, 0xdb, 0x15, - 0xf9, 0x42, 0xae, 0x30, 0x60, 0x11, 0xc1, 0x2e, 0xb8, 0x9b, 0x3b, 0xb1, 0x37, 0x21, 0xa4, 0xc1, 0x39, 0xf4, 0x7d, - 0x1f, 0xdd, 0xa4, 0xce, 0xf2, 0xe3, 0x26, 0x6c, 0x3a, 0x52, 0xce, 0x66, 0xc0, 0x3c, 0xe1, 0x39, 0x28, 0x02, 0x2e, - 0x62, 0xab, 0x05, 0x06, 0x57, 0xbd, 0x45, 0x38, 0x61, 0x0e, 0x28, 0x05, 0x87, 0x0c, 0x1f, 0xba, 0xae, 0xf7, 0x4c, - 0xc6, 0x0c, 0xf1, 0x9c, 0x0b, 0xd2, 0x4a, 0x33, 0xa1, 0xd2, 0xd8, 0xae, 0x37, 0x5f, 0x53, 0x09, 0xc7, 0x3a, 0x3d, - 0x85, 0xba, 0x4d, 0x11, 0x68, 0xfb, 0x8e, 0x45, 0x9f, 0xf0, 0x48, 0x3e, 0x37, 0x82, 0xc0, 0x2b, 0x9a, 0x7c, 0x53, - 0x69, 0x34, 0x74, 0x44, 0x61, 0x8e, 0x7d, 0xc9, 0x60, 0x86, 0x15, 0x15, 0x91, 0x93, 0xd0, 0x14, 0x9a, 0x2d, 0x4c, - 0xfe, 0x36, 0xca, 0xf9, 0x66, 0xa5, 0xd8, 0x86, 0x35, 0x4d, 0xb6, 0xa9, 0xe9, 0xdf, 0x61, 0x0a, 0x54, 0x2d, 0x29, - 0xfe, 0x61, 0x8e, 0x1f, 0xa6, 0xb4, 0xac, 0xd7, 0x0e, 0x07, 0x0b, 0xbd, 0x00, 0xbe, 0x23, 0xfa, 0x39, 0x6f, 0x51, - 0x8c, 0xc1, 0x5f, 0xe9, 0x66, 0xf0, 0xc4, 0x7c, 0xe8, 0xa2, 0x59, 0x96, 0xda, 0xb9, 0x95, 0x22, 0xbb, 0x7f, 0x81, - 0x27, 0x23, 0x2d, 0xbd, 0x83, 0x50, 0x9d, 0x98, 0xc3, 0x9c, 0xb1, 0xef, 0xa2, 0xe4, 0x13, 0xcb, 0x9d, 0x0b, 0xaf, - 0xd3, 0xfd, 0x82, 0x3a, 0x7b, 0xa8, 0x9b, 0xbd, 0xae, 0xc2, 0x68, 0x4a, 0x2d, 0x50, 0x21, 0xc2, 0x56, 0xc7, 0x43, - 0x8e, 0x41, 0x28, 0xc8, 0xbd, 0x2c, 0xec, 0x12, 0x85, 0xdb, 0x7b, 0xc5, 0xd9, 0x69, 0xdf, 0x0e, 0x6c, 0x1b, 0x34, - 0xfe, 0x43, 0x72, 0x5b, 0x09, 0xc5, 0x02, 0x14, 0xb2, 0xbd, 0xb8, 0xc7, 0xb7, 0xb7, 0x2b, 0x87, 0x13, 0x06, 0xd2, - 0xa9, 0x7b, 0xe2, 0x45, 0xde, 0x34, 0x84, 0x01, 0x47, 0xd0, 0x0c, 0xbb, 0xf4, 0x46, 0x7b, 0xb1, 0x9c, 0x06, 0x7d, - 0x21, 0x7e, 0x12, 0x15, 0xfc, 0x05, 0xfa, 0x23, 0xc2, 0x11, 0x2a, 0xfb, 0x3e, 0xbb, 0x60, 0x23, 0xa5, 0x67, 0x00, - 0xa2, 0x22, 0xb7, 0xe7, 0x8e, 0x42, 0xa3, 0x19, 0xcc, 0x1d, 0x86, 0x87, 0x03, 0x1b, 0xce, 0x12, 0x9c, 0xca, 0x30, - 0x3a, 0xea, 0x0c, 0x07, 0x69, 0x08, 0xbc, 0x56, 0xe3, 0x56, 0x16, 0x2d, 0x6a, 0x45, 0xdd, 0xe1, 0xc0, 0x39, 0x05, - 0x25, 0x1d, 0x74, 0x71, 0x07, 0xdf, 0xd0, 0x43, 0x91, 0x87, 0xef, 0xd8, 0xe9, 0xb3, 0x8b, 0xb9, 0x63, 0xef, 0xed, - 0xd8, 0xdb, 0x58, 0xea, 0xd9, 0x40, 0x5e, 0x30, 0x77, 0x78, 0xe9, 0x9a, 0x9d, 0x77, 0x87, 0x08, 0x2a, 0x16, 0xe2, - 0xe4, 0x97, 0x03, 0xbb, 0x2f, 0xa6, 0x6e, 0xc3, 0xa0, 0xa9, 0xdc, 0x7e, 0xdc, 0xd1, 0x43, 0x5a, 0xaa, 0xea, 0xaa, - 0xa0, 0x83, 0xb2, 0x6e, 0xe0, 0x4c, 0xcd, 0x45, 0xb4, 0x70, 0x32, 0x89, 0x05, 0x30, 0x78, 0xb0, 0x19, 0x4c, 0x6a, - 0x74, 0xdb, 0x1d, 0x0e, 0x2e, 0x83, 0x7b, 0xf6, 0x3d, 0xf5, 0x72, 0xc6, 0x02, 0xb0, 0x2e, 0x68, 0xfa, 0x33, 0x94, - 0x22, 0xf0, 0x73, 0xce, 0x60, 0x91, 0x97, 0x54, 0x34, 0x96, 0x45, 0x0b, 0x2c, 0x3a, 0x0c, 0x10, 0x54, 0x2f, 0xd7, - 0xda, 0x9f, 0xd8, 0x93, 0x71, 0x48, 0xb0, 0x6f, 0x6d, 0xc1, 0xd6, 0x6c, 0x77, 0x86, 0x18, 0x6f, 0xc8, 0x79, 0xf1, - 0x5d, 0xcc, 0x41, 0x24, 0xec, 0xf4, 0x6d, 0x77, 0x60, 0x5b, 0xb8, 0xb5, 0xbd, 0x6c, 0x3b, 0x14, 0x18, 0x8e, 0xb7, - 0xdf, 0xb2, 0x60, 0xda, 0x0f, 0xdb, 0x03, 0xa7, 0x10, 0xa2, 0x23, 0xc1, 0xb8, 0xa5, 0xe0, 0xe0, 0x6d, 0x6f, 0x0a, - 0x0c, 0x1d, 0x29, 0x77, 0xd3, 0xde, 0x56, 0x85, 0x50, 0xf4, 0x71, 0x7b, 0xec, 0x06, 0x31, 0xfc, 0x70, 0x5a, 0x48, - 0x34, 0x53, 0xdd, 0x57, 0x4b, 0x66, 0x37, 0x18, 0x2b, 0x8d, 0x3c, 0x09, 0xb3, 0x6d, 0x07, 0x3d, 0xb4, 0xc0, 0x69, - 0xf7, 0x06, 0x00, 0xc3, 0xb6, 0xa3, 0x28, 0x6d, 0x47, 0x91, 0x9a, 0xd2, 0xcf, 0x8f, 0xaa, 0xed, 0x60, 0x83, 0x88, - 0xf9, 0x95, 0xf4, 0x01, 0xb0, 0x82, 0xc4, 0x2b, 0x86, 0x2a, 0xe6, 0xf5, 0xbc, 0x16, 0xdf, 0x5a, 0x2a, 0x56, 0xc4, - 0x3c, 0x83, 0x43, 0xf1, 0x52, 0x9b, 0x61, 0x42, 0xdd, 0x9e, 0x23, 0x32, 0x34, 0xc9, 0x87, 0x6d, 0x20, 0x7a, 0xe5, - 0x60, 0x4f, 0xcd, 0x63, 0x91, 0x84, 0x55, 0x73, 0xef, 0x08, 0x48, 0x7b, 0x18, 0xbe, 0x16, 0x11, 0xc7, 0x9e, 0xf2, - 0xe6, 0xb3, 0x24, 0x7c, 0xde, 0x08, 0x17, 0x47, 0x18, 0x11, 0x3a, 0xf0, 0x47, 0x8b, 0x1c, 0xf8, 0x01, 0x7f, 0x0d, - 0x9a, 0x41, 0x28, 0x9b, 0xa2, 0xa1, 0x87, 0x21, 0x60, 0x8f, 0x16, 0xde, 0x70, 0x9b, 0x1b, 0xd5, 0xa8, 0x51, 0x92, - 0xf2, 0x42, 0x81, 0xe1, 0x1e, 0x97, 0xa6, 0x3d, 0x32, 0x06, 0x19, 0x31, 0x76, 0x30, 0xe6, 0xef, 0x8f, 0xb0, 0x1a, - 0x27, 0x28, 0xdc, 0x92, 0x4e, 0x5b, 0xc5, 0xfe, 0x0e, 0xfc, 0x14, 0x38, 0x38, 0xd6, 0x81, 0x9d, 0xb5, 0xb5, 0x95, - 0xc8, 0x45, 0xed, 0xa5, 0x3d, 0x8a, 0x44, 0xa0, 0x3f, 0xb8, 0xf0, 0x53, 0xa8, 0x46, 0x14, 0x51, 0x11, 0x69, 0xa0, - 0x66, 0x54, 0xad, 0x82, 0xef, 0xc8, 0xf4, 0xc0, 0x73, 0x74, 0x5b, 0x93, 0xa2, 0xa8, 0x1b, 0x0b, 0x5f, 0xbe, 0xeb, - 0x52, 0x68, 0x0b, 0x03, 0x90, 0x82, 0xd0, 0x04, 0xc1, 0xb8, 0xe4, 0x94, 0xac, 0xe8, 0xef, 0xa3, 0xe1, 0x2b, 0x9f, - 0x1e, 0x65, 0xdb, 0xdb, 0x43, 0x11, 0xb7, 0x20, 0xc2, 0xe1, 0x86, 0x77, 0x35, 0xae, 0x00, 0xa8, 0x4f, 0xe7, 0xc4, - 0x75, 0xc7, 0xb4, 0x22, 0x4d, 0x97, 0x7c, 0x9f, 0x1c, 0x66, 0x00, 0x0c, 0xee, 0x38, 0x47, 0xfe, 0xe0, 0x2f, 0x43, - 0x30, 0x8f, 0xfd, 0xcf, 0xdd, 0x1d, 0xc5, 0x68, 0x7a, 0x32, 0xa6, 0xb8, 0xa4, 0x18, 0x6b, 0xc7, 0x23, 0xdf, 0x68, - 0x90, 0x7b, 0x29, 0xac, 0x00, 0xa4, 0x39, 0xf0, 0x84, 0x8a, 0x82, 0x90, 0xa2, 0x02, 0xdb, 0xc7, 0xc3, 0xcf, 0xf1, - 0x64, 0xbf, 0x03, 0x0d, 0x6f, 0xa0, 0xdf, 0x9e, 0xc2, 0xdb, 0x5f, 0xf4, 0xdb, 0x97, 0x2c, 0xf8, 0xa5, 0x94, 0xae, - 0xfb, 0xda, 0x14, 0x0f, 0xd5, 0x14, 0xa5, 0xd8, 0x22, 0x03, 0x87, 0xcc, 0x5d, 0xf5, 0xd9, 0x70, 0xb7, 0x04, 0x64, - 0x28, 0xd6, 0x05, 0x3a, 0x5a, 0x74, 0x8a, 0xc8, 0x75, 0x4d, 0x54, 0x18, 0xb9, 0x04, 0xe6, 0x82, 0x2b, 0xba, 0x25, - 0xe2, 0xec, 0xb7, 0xdd, 0x65, 0xad, 0x2d, 0xe9, 0x77, 0x6c, 0x36, 0xe7, 0x97, 0x07, 0x24, 0xe8, 0x03, 0x99, 0x36, - 0x20, 0x62, 0xe7, 0xed, 0x5e, 0xbc, 0xc7, 0x7b, 0x31, 0x70, 0xf5, 0x42, 0x91, 0x18, 0x9e, 0x55, 0xef, 0x2d, 0x7a, - 0x29, 0x4d, 0x62, 0xf2, 0x6a, 0xcb, 0xeb, 0xca, 0xe5, 0x6d, 0x6f, 0xc3, 0x02, 0x7b, 0x46, 0x57, 0x2e, 0xba, 0x96, - 0xa5, 0xc0, 0x09, 0x40, 0xf4, 0xb8, 0x4e, 0x72, 0x44, 0x71, 0x98, 0xcd, 0x86, 0x8c, 0x83, 0xb9, 0x6b, 0x47, 0xc5, - 0x31, 0xb1, 0xbb, 0x4c, 0xd8, 0x81, 0x95, 0x11, 0x95, 0xb7, 0x3a, 0xc2, 0x3b, 0x2c, 0xfa, 0x6b, 0xff, 0xf6, 0x47, - 0x8f, 0x6d, 0x77, 0x5c, 0x90, 0x20, 0xb5, 0xb1, 0x1e, 0x55, 0x63, 0x41, 0x7d, 0xf8, 0x51, 0x63, 0xa9, 0xcc, 0xb7, - 0xb7, 0xcb, 0x7a, 0xa8, 0x56, 0x9d, 0xe0, 0x5a, 0x34, 0xe5, 0xa2, 0x99, 0x0d, 0xc2, 0x01, 0x89, 0x09, 0x14, 0x68, - 0x6e, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0xe4, 0x4f, 0x21, 0xf3, 0xe2, 0xb2, 0xd4, 0xa9, 0x2f, 0xd2, 0x1f, 0x19, - 0x62, 0xd4, 0x93, 0x94, 0x15, 0x10, 0xb0, 0x5e, 0xea, 0x25, 0xb4, 0x45, 0xb0, 0xf2, 0x67, 0x2a, 0x87, 0x46, 0x68, - 0x20, 0x51, 0x68, 0xa8, 0x25, 0x4a, 0xf9, 0xcc, 0xc3, 0x18, 0xa4, 0xfd, 0x93, 0x9a, 0xef, 0x2b, 0x57, 0x4a, 0x47, - 0x7e, 0x54, 0x0c, 0x03, 0xaa, 0x5f, 0x48, 0x0e, 0x36, 0x0d, 0xdf, 0x03, 0x19, 0x55, 0x86, 0x27, 0x31, 0xc2, 0xa7, - 0x71, 0xce, 0xc8, 0x52, 0xd8, 0x94, 0x30, 0x4b, 0xd5, 0x36, 0x52, 0xed, 0x22, 0xd3, 0x09, 0xe5, 0xc2, 0xfc, 0x53, - 0x23, 0x76, 0x91, 0x85, 0x2b, 0xad, 0x41, 0xfd, 0x78, 0x63, 0x02, 0x94, 0x5d, 0x5d, 0x65, 0xc2, 0xc6, 0x8d, 0x48, - 0xdf, 0xd0, 0x15, 0xd3, 0x81, 0x5a, 0x54, 0xe0, 0x44, 0xa4, 0xf1, 0x50, 0x0c, 0x85, 0x46, 0x38, 0xa4, 0x28, 0x72, - 0xe1, 0x1a, 0x87, 0xbe, 0x18, 0x68, 0xdb, 0x28, 0x0d, 0x9d, 0x04, 0x98, 0x80, 0x58, 0xbb, 0xa1, 0x4d, 0xa5, 0x83, - 0x34, 0x48, 0xa8, 0x14, 0xed, 0x1c, 0x58, 0x7f, 0x18, 0x49, 0x0c, 0x80, 0xfe, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, - 0x6e, 0x00, 0xcd, 0x75, 0x80, 0x3b, 0xe1, 0x0b, 0x05, 0x15, 0xa6, 0xa7, 0x59, 0x79, 0x29, 0x84, 0xc8, 0xab, 0x35, - 0x29, 0x6b, 0xc4, 0x93, 0xcf, 0xd0, 0xe0, 0x53, 0xd6, 0xf5, 0x6b, 0xb9, 0x0e, 0x5d, 0xf0, 0x14, 0xb6, 0x55, 0x3d, - 0xbf, 0x0a, 0x39, 0x19, 0xd7, 0x20, 0x2b, 0x24, 0xd3, 0x5f, 0x31, 0x92, 0xf7, 0x5f, 0xf9, 0x55, 0x2d, 0x35, 0x86, - 0xb2, 0xf7, 0xeb, 0x9a, 0x61, 0x79, 0x39, 0xaf, 0xdc, 0x14, 0x04, 0xdc, 0x92, 0x25, 0xc1, 0x52, 0x4a, 0x08, 0xd0, - 0xb0, 0x3d, 0x92, 0x4a, 0x41, 0x51, 0x6a, 0xf7, 0xce, 0x53, 0xd0, 0x02, 0x8c, 0xa0, 0x96, 0x4a, 0xa6, 0x91, 0xc8, - 0x97, 0x42, 0x14, 0x88, 0xf2, 0x60, 0x04, 0x76, 0x6a, 0x33, 0xd2, 0x75, 0xe1, 0xfa, 0xf1, 0x0c, 0x53, 0x7b, 0x08, - 0xf4, 0xd8, 0xdb, 0x00, 0x55, 0xa2, 0x2e, 0xc3, 0x72, 0xa2, 0xd0, 0xac, 0x26, 0x59, 0x40, 0x8d, 0x69, 0x83, 0x94, - 0x6c, 0x83, 0x2e, 0x57, 0x80, 0x7e, 0x24, 0x8e, 0x67, 0xb5, 0x03, 0x42, 0xd6, 0xa0, 0x82, 0x21, 0x4f, 0xa9, 0x90, - 0xc2, 0xbc, 0xd7, 0xa5, 0x22, 0x3c, 0x9f, 0x03, 0x2e, 0xb5, 0xe0, 0xcc, 0xcb, 0x68, 0xe0, 0x83, 0xf8, 0x24, 0xc1, - 0xc4, 0x17, 0x5c, 0x15, 0xe8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x28, 0x15, 0x37, 0x29, 0x83, 0x6d, 0x45, 0xae, 0x0d, - 0x3f, 0x24, 0xcb, 0xd6, 0x5d, 0x1e, 0xea, 0x2e, 0x44, 0x02, 0xd8, 0xe9, 0x25, 0x7a, 0xbe, 0x65, 0xbd, 0x74, 0x18, - 0x9c, 0x69, 0x89, 0x83, 0xc0, 0x6f, 0x6f, 0x27, 0xc3, 0x32, 0x25, 0xb2, 0x6b, 0x92, 0xba, 0x80, 0x1c, 0x86, 0x6a, - 0xae, 0x1d, 0x98, 0xa5, 0xd2, 0xc7, 0xf3, 0x72, 0x86, 0xdb, 0xa5, 0x34, 0xe4, 0x66, 0xbc, 0x9a, 0xe6, 0x73, 0x2b, - 0xc9, 0xa6, 0xfd, 0xad, 0xf8, 0xa2, 0xe0, 0x1f, 0x38, 0xb1, 0xd4, 0xea, 0x29, 0xb5, 0xc2, 0xa3, 0xcc, 0x2d, 0x59, - 0xa7, 0xb8, 0x56, 0xd7, 0x0d, 0x54, 0x23, 0x8c, 0xa6, 0x61, 0x23, 0x60, 0x62, 0x82, 0x8a, 0x5f, 0x37, 0x89, 0x98, - 0xce, 0x96, 0xe0, 0x3a, 0x42, 0xef, 0xa1, 0x9c, 0xe0, 0xae, 0xa6, 0xd9, 0xe7, 0xe1, 0xfc, 0x7a, 0xe2, 0xde, 0x37, - 0x88, 0xfb, 0xcb, 0x90, 0x1b, 0x84, 0x1e, 0xcb, 0x84, 0x1f, 0xe9, 0xfb, 0x28, 0x54, 0xd5, 0x93, 0xd3, 0xb0, 0x62, - 0x59, 0xe2, 0xc9, 0x08, 0x75, 0x18, 0x51, 0xd1, 0x1a, 0x23, 0xbb, 0xba, 0xca, 0xcd, 0xb3, 0x40, 0x4e, 0x53, 0x8f, - 0xd7, 0xfd, 0xb4, 0x15, 0x39, 0x1b, 0x9e, 0xc8, 0xfd, 0x57, 0x35, 0x4f, 0x64, 0x45, 0xe7, 0x38, 0xd2, 0x35, 0x81, - 0xdc, 0x27, 0xa7, 0xab, 0x87, 0x54, 0xc8, 0x16, 0xbd, 0x6c, 0xe3, 0x8c, 0xea, 0x80, 0xa4, 0x9e, 0x51, 0x81, 0x55, - 0x8d, 0xbd, 0xb5, 0xd5, 0x11, 0xe9, 0x96, 0x4a, 0xb0, 0xc1, 0xd6, 0xc2, 0x68, 0xc6, 0x28, 0xe8, 0x94, 0x14, 0x19, - 0xa8, 0x51, 0x7e, 0x0d, 0x63, 0xd8, 0xa7, 0x06, 0x20, 0x38, 0xd7, 0x57, 0x7f, 0x59, 0x4a, 0xb2, 0x10, 0x90, 0xb8, - 0x4b, 0x06, 0x6c, 0x4d, 0x10, 0x33, 0xd2, 0xc9, 0x7b, 0xa0, 0xbc, 0x01, 0x43, 0x1b, 0x01, 0xec, 0x02, 0x71, 0xe8, - 0x41, 0xc5, 0xb6, 0x09, 0x29, 0x3a, 0x36, 0xf0, 0x1c, 0x80, 0x9d, 0x57, 0xae, 0xd1, 0x77, 0x55, 0x0a, 0x18, 0x92, - 0x81, 0x1b, 0xb0, 0xca, 0x2d, 0xb7, 0xff, 0x1c, 0xcc, 0x06, 0x78, 0x7d, 0x26, 0x9b, 0x6f, 0x62, 0x9e, 0x60, 0x15, - 0xbb, 0xf0, 0x2b, 0xcd, 0x5a, 0xc4, 0x9d, 0x0e, 0x1b, 0xf5, 0x0a, 0x13, 0xa2, 0xf6, 0x00, 0x6b, 0xdf, 0xa3, 0x87, - 0x45, 0xbc, 0xbf, 0xc2, 0x77, 0x3d, 0x6e, 0xb9, 0xaf, 0x97, 0x45, 0x2b, 0x5d, 0x45, 0x8d, 0x81, 0xc9, 0xba, 0x9d, - 0x8c, 0x6b, 0x2f, 0x0f, 0x84, 0x2f, 0xb8, 0x5a, 0x23, 0xab, 0x5c, 0x8a, 0x8d, 0x45, 0xd2, 0xd3, 0x3e, 0x05, 0xd8, - 0x37, 0x9b, 0xbd, 0x00, 0x33, 0xef, 0x2b, 0x54, 0x49, 0x48, 0x69, 0x76, 0x83, 0x25, 0x09, 0x6d, 0x45, 0x46, 0x9d, - 0x0f, 0x1c, 0x6d, 0x73, 0x2b, 0x8e, 0x60, 0x38, 0x27, 0x61, 0x3a, 0x56, 0x1e, 0x36, 0x19, 0xb8, 0xf2, 0x8e, 0x98, - 0xb6, 0x09, 0xf0, 0x6f, 0x06, 0x7c, 0x7b, 0x25, 0xb9, 0xb6, 0xd0, 0x30, 0x3c, 0x41, 0x84, 0x55, 0x9e, 0x08, 0x34, - 0x14, 0x60, 0x8d, 0x6b, 0x2d, 0x0f, 0x50, 0xe1, 0x6b, 0x67, 0x13, 0x00, 0x12, 0x59, 0x41, 0xce, 0x8a, 0xa3, 0x1b, - 0x56, 0xb9, 0xde, 0x4f, 0x8d, 0x82, 0xc4, 0xc5, 0x83, 0xe9, 0xea, 0x96, 0xfe, 0x0c, 0x35, 0x67, 0x52, 0xc4, 0xb4, - 0x13, 0x04, 0xfd, 0xa3, 0xcc, 0xc9, 0x69, 0x3a, 0xa1, 0x7d, 0xce, 0x9d, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x89, - 0x2d, 0x5e, 0xc7, 0x4d, 0x29, 0x17, 0x26, 0x39, 0xe6, 0xa6, 0x48, 0xc5, 0x66, 0x8a, 0xdd, 0xb9, 0xf5, 0x83, 0x16, - 0xd2, 0x41, 0xdb, 0x14, 0x39, 0xd8, 0xac, 0xe2, 0xf7, 0x04, 0xc6, 0x73, 0x81, 0xf8, 0xf2, 0x15, 0x25, 0xe9, 0x30, - 0xc7, 0x5c, 0x60, 0xf5, 0x62, 0x0a, 0xf2, 0x77, 0x8e, 0x4e, 0xb3, 0x37, 0xf0, 0x41, 0xe2, 0x0d, 0x38, 0x66, 0x8d, - 0x7d, 0xe7, 0x52, 0x51, 0x47, 0x08, 0x54, 0x46, 0xb5, 0x4c, 0xc7, 0x89, 0x95, 0xfb, 0x46, 0xd0, 0xd5, 0x5b, 0x1d, - 0xce, 0x37, 0x9e, 0x1b, 0xbb, 0x11, 0xc4, 0x60, 0x2d, 0x14, 0x43, 0x4f, 0xb2, 0xf0, 0x1c, 0xb6, 0x67, 0x7b, 0xbb, - 0x57, 0xec, 0xf1, 0xca, 0x45, 0x52, 0xc1, 0x18, 0x63, 0x46, 0x31, 0x9e, 0x89, 0x9a, 0x58, 0x44, 0x64, 0xcb, 0xd6, - 0x61, 0x81, 0x01, 0x00, 0x68, 0x69, 0x72, 0xaf, 0x9a, 0x08, 0x95, 0xf1, 0x5c, 0x5a, 0x4f, 0x15, 0x44, 0x55, 0x8d, - 0xdf, 0xae, 0xcf, 0x40, 0x21, 0xb8, 0x37, 0x3a, 0x1e, 0x06, 0x21, 0x60, 0x17, 0x05, 0x2f, 0xd0, 0x07, 0xb4, 0x57, - 0x25, 0x42, 0x31, 0x73, 0xb2, 0x1e, 0x33, 0x8c, 0x54, 0xd0, 0x85, 0x4a, 0xd8, 0x2a, 0xcd, 0xf0, 0xab, 0x83, 0xd0, - 0x8c, 0x32, 0xee, 0xbf, 0xaa, 0xd6, 0x0c, 0xf2, 0x83, 0x79, 0xab, 0x84, 0xfa, 0x76, 0x25, 0x22, 0x53, 0x81, 0x89, - 0x87, 0x59, 0x4a, 0xbf, 0x5f, 0xd6, 0x49, 0x3f, 0x2f, 0x97, 0xe7, 0x9c, 0x24, 0x5f, 0xe7, 0x0e, 0x92, 0x4f, 0xba, - 0xfb, 0x95, 0xf0, 0x43, 0x0d, 0xa3, 0x26, 0xfc, 0xea, 0x5b, 0x1a, 0xe6, 0x9e, 0x72, 0x6f, 0xf5, 0xbb, 0xc8, 0x74, - 0x51, 0x9e, 0x83, 0x22, 0xa4, 0x1f, 0xc1, 0x34, 0x34, 0x68, 0x50, 0x24, 0x8b, 0xc5, 0xda, 0x04, 0x71, 0x7d, 0xcc, - 0xa9, 0x76, 0x28, 0x63, 0x8c, 0x68, 0x5a, 0x52, 0x90, 0x24, 0x70, 0x50, 0x7e, 0x03, 0x03, 0x62, 0x12, 0x12, 0xd2, - 0x20, 0x74, 0xd6, 0x66, 0x22, 0x2a, 0x73, 0xf1, 0x76, 0xe5, 0xb2, 0x26, 0x50, 0x84, 0x9e, 0x60, 0xa6, 0x52, 0x2a, - 0x08, 0xa4, 0xca, 0xb7, 0xd1, 0xa9, 0x39, 0x43, 0x73, 0xd7, 0x14, 0x40, 0x5e, 0xdb, 0xf5, 0xa0, 0xc9, 0x7b, 0xf2, - 0xa1, 0xaf, 0x13, 0x23, 0x5e, 0x66, 0xd0, 0x35, 0x1c, 0xfe, 0x1a, 0x2b, 0x29, 0x42, 0x26, 0x7c, 0xaf, 0x60, 0x13, - 0x21, 0x99, 0x82, 0x9e, 0x09, 0xf8, 0x43, 0xbd, 0xb2, 0x97, 0xee, 0xe5, 0x95, 0x49, 0x8b, 0xca, 0x56, 0xa2, 0x66, - 0x2d, 0x8e, 0xe2, 0xed, 0x14, 0xce, 0xb3, 0x47, 0x09, 0x04, 0x24, 0xa9, 0x9c, 0xa4, 0x9a, 0xf7, 0x28, 0x1d, 0x02, - 0x48, 0x70, 0xfa, 0x09, 0x2c, 0xb4, 0x9b, 0x12, 0x13, 0x2c, 0xaa, 0xc6, 0x6e, 0x73, 0x90, 0x9a, 0x73, 0x92, 0x7c, - 0x73, 0x94, 0xda, 0xdb, 0x4a, 0x7b, 0xc6, 0xec, 0x00, 0xdb, 0x76, 0xb7, 0xf3, 0xa3, 0x74, 0xbb, 0x33, 0x34, 0x18, - 0x17, 0x86, 0xff, 0x93, 0x12, 0xd3, 0x40, 0x0a, 0x29, 0x1b, 0x3f, 0xa1, 0x0c, 0xc3, 0xff, 0x96, 0x24, 0x80, 0x07, - 0xb5, 0xdd, 0x58, 0x31, 0xee, 0x15, 0x45, 0xc9, 0x6d, 0x55, 0xed, 0x6a, 0x09, 0x1a, 0xba, 0x91, 0xf4, 0x89, 0x62, - 0x9e, 0x13, 0x00, 0xa3, 0xc8, 0xfc, 0x1d, 0xd3, 0x49, 0xde, 0xbf, 0xac, 0x4d, 0xed, 0xf6, 0x7d, 0x3f, 0xca, 0x4f, - 0x29, 0xa4, 0xa2, 0xb2, 0x39, 0x89, 0xf8, 0x77, 0x05, 0x98, 0xe6, 0xc4, 0x47, 0x7a, 0xae, 0x61, 0x28, 0xc0, 0x57, - 0x3a, 0x94, 0x9a, 0xed, 0xe9, 0x1f, 0x9d, 0xed, 0xbe, 0x44, 0x8a, 0x20, 0x81, 0x06, 0x5e, 0xae, 0x59, 0x2f, 0xac, - 0x32, 0xb8, 0x23, 0xfe, 0x14, 0x7c, 0x5f, 0x5e, 0x07, 0x9f, 0x71, 0xfe, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x83, - 0xa6, 0x62, 0x25, 0xd8, 0x25, 0x0a, 0xcc, 0xca, 0xcf, 0x1f, 0xd7, 0x69, 0xdd, 0xd4, 0x2c, 0xd1, 0x29, 0x3f, 0x77, - 0x0d, 0x33, 0xbe, 0xd7, 0xc8, 0x1f, 0xdf, 0x7f, 0x0e, 0xb2, 0x9d, 0x50, 0xbb, 0xb5, 0x55, 0x6c, 0x90, 0x86, 0x86, - 0xf7, 0xc2, 0xe6, 0xd0, 0x16, 0xf1, 0x52, 0xa8, 0x67, 0x2c, 0xc6, 0xdb, 0x22, 0x54, 0x86, 0x0f, 0x58, 0x30, 0x87, - 0x15, 0x82, 0xc5, 0x4e, 0x65, 0xf2, 0x19, 0x0e, 0x9a, 0x22, 0xd7, 0x42, 0x28, 0x7c, 0x39, 0x88, 0x4a, 0x49, 0x8b, - 0x75, 0xb4, 0x3d, 0x3b, 0x83, 0xe7, 0x97, 0x71, 0x01, 0xd8, 0x81, 0xe5, 0x57, 0x58, 0x16, 0x07, 0xc8, 0xc5, 0x43, - 0x59, 0xeb, 0x15, 0x8d, 0xc7, 0x37, 0x76, 0x61, 0x75, 0x01, 0x3e, 0x8d, 0xd2, 0x71, 0x22, 0x26, 0x31, 0x93, 0x2a, - 0xd7, 0xe4, 0xda, 0xe8, 0x5e, 0x5a, 0xa3, 0x79, 0x2e, 0x38, 0x78, 0x85, 0xe0, 0x06, 0xd3, 0x57, 0xf2, 0x72, 0xbd, - 0x82, 0x82, 0xa1, 0xf6, 0xe6, 0x26, 0x98, 0x2b, 0xf1, 0x98, 0xc1, 0x35, 0xfd, 0x3a, 0x9c, 0x8a, 0x6e, 0x5e, 0xae, - 0x18, 0xfc, 0x3a, 0x67, 0xac, 0x21, 0x00, 0x88, 0x4e, 0x1e, 0x5e, 0x6f, 0x26, 0xbd, 0x52, 0xd2, 0x41, 0x49, 0x84, - 0xf8, 0xae, 0xcc, 0xd7, 0x5d, 0x2a, 0xba, 0x72, 0xd5, 0xbd, 0xaf, 0x19, 0x33, 0x2e, 0x18, 0x3d, 0xe7, 0xb3, 0xa4, - 0x71, 0xed, 0x86, 0xee, 0xea, 0xfc, 0xe8, 0xfd, 0x20, 0xf3, 0x16, 0x66, 0x40, 0x26, 0x20, 0x0a, 0x9e, 0x7b, 0xaf, - 0x8d, 0x88, 0xf2, 0xb7, 0x66, 0x88, 0x57, 0x0e, 0xb3, 0x2e, 0x92, 0xfc, 0xed, 0xe0, 0xdb, 0xe0, 0xfa, 0x96, 0x46, - 0x04, 0xb9, 0xab, 0x22, 0xc8, 0x84, 0xb9, 0x99, 0x3e, 0x70, 0xfb, 0x77, 0x65, 0x08, 0x22, 0x2a, 0xa6, 0x43, 0xe5, - 0xb8, 0x7f, 0xb4, 0x41, 0xa5, 0x42, 0xe2, 0x53, 0x95, 0xbb, 0x72, 0x6d, 0x6a, 0xa8, 0xc7, 0x75, 0x32, 0x0b, 0x4d, - 0xb3, 0x26, 0x97, 0xb2, 0x69, 0x31, 0x32, 0x4d, 0x4e, 0xb5, 0xf9, 0xdd, 0x6b, 0x83, 0x74, 0x0c, 0xd5, 0xc5, 0x5a, - 0x2d, 0x98, 0xdf, 0x95, 0x17, 0xde, 0xf5, 0x62, 0x23, 0x95, 0xa1, 0xa6, 0x3d, 0x8a, 0x3e, 0x8e, 0xdb, 0xcc, 0xe5, - 0x51, 0xfa, 0x67, 0x0d, 0x00, 0xd3, 0x10, 0x16, 0xdd, 0x4d, 0xcb, 0xd8, 0x13, 0xcb, 0xd3, 0x13, 0x19, 0x28, 0x7a, - 0xae, 0xf3, 0x55, 0xab, 0xc4, 0xd2, 0x35, 0x08, 0x76, 0x6f, 0xc8, 0x58, 0x95, 0xb8, 0x5b, 0xad, 0x5f, 0xcd, 0xf3, - 0x79, 0xca, 0x57, 0xf2, 0x7c, 0x6a, 0x1a, 0xdd, 0x46, 0xdb, 0xbd, 0x39, 0x35, 0x54, 0xcc, 0xb5, 0xbe, 0xc9, 0x1f, - 0x98, 0xae, 0x83, 0xae, 0x16, 0x81, 0x66, 0x75, 0xaa, 0x9e, 0x95, 0xe5, 0xac, 0x9e, 0xc9, 0x31, 0x13, 0xb6, 0xa9, - 0x34, 0x87, 0xe8, 0x86, 0xa9, 0x9a, 0xe9, 0xc7, 0xc6, 0xb1, 0x90, 0x6d, 0x9e, 0x5f, 0x8e, 0x73, 0xc0, 0xb4, 0x3c, - 0x5f, 0x26, 0x0c, 0x3f, 0x5e, 0x5d, 0xfd, 0x28, 0xf8, 0x54, 0xd5, 0xd1, 0x5b, 0xbe, 0xd4, 0x3d, 0x83, 0x59, 0xa9, - 0x8c, 0x88, 0x13, 0xb6, 0x7e, 0xf0, 0xe6, 0xe9, 0x15, 0xb0, 0x9c, 0xc0, 0xea, 0x4e, 0x98, 0xd3, 0x18, 0xaa, 0x3a, - 0xc0, 0x3f, 0xac, 0x1f, 0x6c, 0xdd, 0x19, 0xfe, 0x61, 0xf0, 0x43, 0x70, 0x63, 0x63, 0xe3, 0x18, 0xef, 0xd6, 0x12, - 0x41, 0x5e, 0x61, 0x40, 0x1f, 0xaf, 0x3e, 0x0a, 0x5c, 0xae, 0x63, 0xdb, 0x03, 0x87, 0xdc, 0xd6, 0xc0, 0xdf, 0x24, - 0x4f, 0x1a, 0x2d, 0x0a, 0x9e, 0xcd, 0xe4, 0x0c, 0x85, 0xbc, 0xe6, 0xe3, 0xa0, 0xee, 0x08, 0x7f, 0x03, 0xa7, 0x16, - 0x5e, 0x5e, 0x7e, 0x82, 0x3e, 0x60, 0xe9, 0x4a, 0x6e, 0x2a, 0xfc, 0x94, 0xf2, 0x88, 0xae, 0xd6, 0x79, 0x30, 0x52, - 0x5c, 0x4c, 0x51, 0xe8, 0xb8, 0xcb, 0x1b, 0x67, 0x23, 0xa3, 0xbf, 0xc4, 0xab, 0x8b, 0x74, 0xf9, 0x48, 0x64, 0xab, - 0x96, 0xde, 0x2f, 0x3a, 0xba, 0x6d, 0xcf, 0x18, 0x9f, 0x66, 0x63, 0x0a, 0xcc, 0xf8, 0x38, 0x11, 0x5e, 0x9f, 0x18, - 0xeb, 0xbb, 0x45, 0xa0, 0xba, 0x39, 0x36, 0xd9, 0xe1, 0x78, 0xbd, 0xd9, 0xac, 0x71, 0x07, 0x6f, 0x9c, 0x27, 0xce, - 0xb2, 0x44, 0x8f, 0xca, 0x52, 0xc3, 0x03, 0x52, 0x21, 0x6e, 0xde, 0x33, 0x81, 0x71, 0xd9, 0x25, 0x71, 0x6d, 0x37, - 0x10, 0x6b, 0xb1, 0x27, 0x31, 0x4b, 0xc6, 0xb6, 0x07, 0xe5, 0x81, 0xbe, 0x18, 0x4d, 0xb7, 0x80, 0x69, 0x7b, 0xed, - 0xec, 0x3c, 0xb5, 0xbd, 0x6a, 0xaa, 0x00, 0x66, 0xc9, 0xf2, 0xf8, 0x14, 0x49, 0xf7, 0x1b, 0xe8, 0x22, 0x06, 0x8c, - 0x8d, 0x2b, 0x73, 0xee, 0x72, 0xdd, 0x8e, 0xf8, 0x46, 0x13, 0xa9, 0x52, 0x1f, 0x51, 0xdf, 0x61, 0x58, 0xab, 0xab, - 0x0c, 0x24, 0x30, 0x8f, 0xbc, 0x3b, 0xae, 0xa5, 0xa7, 0x63, 0x16, 0x93, 0x2a, 0x7d, 0x4b, 0x5d, 0x8b, 0x6b, 0xba, - 0xbd, 0xe2, 0x01, 0xe8, 0x1f, 0xe8, 0xb7, 0x88, 0x85, 0xbf, 0x9d, 0xd7, 0x52, 0x58, 0x1b, 0x73, 0xe4, 0xe8, 0x6b, - 0x0f, 0x7e, 0x61, 0xd5, 0x9e, 0x81, 0x1a, 0x66, 0xc4, 0x48, 0x7e, 0x33, 0xee, 0x55, 0x4d, 0x1c, 0xb9, 0x0b, 0xc0, - 0xfa, 0x96, 0x74, 0x49, 0x0e, 0xaf, 0x64, 0xb9, 0x2a, 0x86, 0xfc, 0x1b, 0xec, 0xb3, 0xde, 0x9c, 0x80, 0x99, 0x38, - 0xe5, 0x25, 0x26, 0xa6, 0x88, 0xcb, 0xcd, 0xd2, 0xe7, 0x69, 0xda, 0x2c, 0xda, 0xc0, 0x29, 0x8c, 0x04, 0x8e, 0xd8, - 0x37, 0xb6, 0xa1, 0x99, 0xb0, 0x11, 0x13, 0x6a, 0x54, 0x4a, 0x09, 0x1f, 0xc8, 0xad, 0x96, 0xf4, 0x65, 0x6e, 0xaf, - 0xbe, 0xdc, 0x26, 0x28, 0xa0, 0xa8, 0x81, 0xe5, 0xd0, 0x38, 0x6e, 0x19, 0xc8, 0x85, 0xc5, 0xb0, 0x30, 0x6a, 0x55, - 0xae, 0x26, 0xa3, 0x3a, 0x99, 0xaf, 0x16, 0x17, 0x2a, 0xf4, 0xe0, 0x91, 0x40, 0xce, 0x5f, 0x60, 0xea, 0x60, 0x56, - 0x6a, 0x33, 0x2d, 0x36, 0x51, 0xde, 0x33, 0x1d, 0x92, 0xeb, 0xaf, 0xe1, 0xa1, 0xf2, 0x8b, 0x57, 0xe6, 0x14, 0xf3, - 0x45, 0x1e, 0x4b, 0x5b, 0x63, 0x6e, 0xfd, 0xaf, 0xf2, 0x3e, 0xad, 0x04, 0xec, 0x37, 0x60, 0x53, 0xc6, 0x5a, 0x62, - 0xe3, 0x82, 0xa4, 0xbc, 0x96, 0xa7, 0xf4, 0xbe, 0x86, 0xf0, 0x5d, 0x51, 0xe9, 0x2a, 0x91, 0x75, 0x8d, 0x56, 0xf7, - 0xeb, 0x82, 0xe5, 0x97, 0x07, 0x0c, 0x73, 0x93, 0x51, 0x21, 0x5b, 0x51, 0xb3, 0x29, 0xbf, 0xda, 0xbb, 0xf1, 0x2b, - 0x0f, 0x25, 0x05, 0xd5, 0x2a, 0xd9, 0xbc, 0x72, 0xc3, 0x31, 0x6e, 0xdc, 0x70, 0x8c, 0x7b, 0x14, 0x57, 0xae, 0x50, - 0xad, 0xf3, 0xdf, 0x57, 0xdd, 0x4f, 0x74, 0xd6, 0x86, 0xfa, 0xd4, 0x0d, 0xd7, 0xa6, 0xa7, 0xdf, 0xb0, 0x54, 0x23, - 0x4b, 0xe8, 0xa6, 0xa5, 0x62, 0x32, 0x12, 0x25, 0xa6, 0xab, 0x94, 0x47, 0x7d, 0x8d, 0xb8, 0x00, 0x76, 0x43, 0xf9, - 0x8b, 0x7f, 0x0d, 0xcf, 0x8f, 0x03, 0x54, 0xa2, 0x96, 0x93, 0x2c, 0xe5, 0xad, 0x49, 0x34, 0x8b, 0x93, 0xcb, 0x60, - 0x11, 0xb7, 0x66, 0x59, 0x9a, 0x15, 0x73, 0xa0, 0x4a, 0xaf, 0xb8, 0x04, 0x1d, 0x7e, 0xd6, 0x5a, 0xc4, 0xde, 0x73, - 0x96, 0x9c, 0x31, 0x1e, 0x8f, 0x22, 0xcf, 0xde, 0xcf, 0x81, 0x3d, 0x58, 0xaf, 0xa3, 0x3c, 0xcf, 0xce, 0x6d, 0xef, - 0x5d, 0x76, 0x02, 0x44, 0xeb, 0xbd, 0xb9, 0xb8, 0x3c, 0x65, 0xa9, 0xf7, 0xfe, 0x64, 0x91, 0xf2, 0x85, 0x57, 0x44, - 0x69, 0xd1, 0x2a, 0x58, 0x1e, 0x4f, 0x40, 0x4c, 0x24, 0x59, 0xde, 0xc2, 0xfc, 0xe7, 0x19, 0x0b, 0x92, 0xf8, 0x74, - 0xca, 0xad, 0x71, 0x94, 0x7f, 0xea, 0xb5, 0x5a, 0xf3, 0x3c, 0x9e, 0x45, 0xf9, 0x65, 0x8b, 0x5a, 0x04, 0x9f, 0xb5, - 0x77, 0xa3, 0x2f, 0x26, 0xf7, 0x7b, 0x3c, 0x87, 0xbe, 0x31, 0x62, 0x31, 0x00, 0xe6, 0x63, 0xed, 0x3e, 0x68, 0xcf, - 0x8a, 0x0d, 0x11, 0x51, 0x8a, 0x52, 0x5e, 0x1e, 0x7b, 0x1f, 0x41, 0xb7, 0x3d, 0xf6, 0x4f, 0x78, 0xea, 0x81, 0x2d, - 0xc7, 0xb3, 0x74, 0x39, 0x5a, 0xe4, 0x05, 0x0c, 0x30, 0xcf, 0xe2, 0x94, 0xb3, 0xbc, 0x77, 0x92, 0xe5, 0x80, 0xb6, - 0x56, 0x1e, 0x8d, 0xe3, 0x45, 0x11, 0xdc, 0x9f, 0x5f, 0xf4, 0x50, 0x57, 0x38, 0xcd, 0xb3, 0x45, 0x3a, 0x96, 0x73, - 0xc5, 0x29, 0x1c, 0x8c, 0x98, 0x9b, 0x15, 0xf4, 0x25, 0x14, 0x80, 0x2f, 0x65, 0x51, 0xde, 0x3a, 0xc5, 0xce, 0xa8, - 0xe8, 0xb7, 0xc7, 0xec, 0xd4, 0xcb, 0x4f, 0x4f, 0x22, 0xa7, 0xd3, 0x7d, 0xe4, 0xa9, 0x7f, 0xfe, 0x03, 0x17, 0x14, - 0xf7, 0xb5, 0xc5, 0x9d, 0x76, 0xfb, 0x1f, 0xdc, 0x5e, 0x63, 0x16, 0x02, 0x28, 0xe8, 0xcc, 0x2f, 0xac, 0x22, 0x4b, - 0x60, 0x7f, 0xd6, 0xf5, 0xec, 0xcd, 0xc1, 0x6e, 0x8a, 0xd3, 0xd3, 0xa0, 0x3b, 0xbf, 0x28, 0x71, 0x75, 0x81, 0x48, - 0xc8, 0x94, 0x8b, 0x94, 0x6f, 0xcb, 0x3f, 0x0a, 0xf1, 0xe3, 0xf5, 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd6, 0x5b, 0x63, - 0x38, 0x07, 0x84, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x82, 0x11, 0x98, 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, - 0x0f, 0xc6, 0xe3, 0xdb, 0xc0, 0xc8, 0xd3, 0xf1, 0xb2, 0xbe, 0xaf, 0x1d, 0x30, 0x4e, 0x7b, 0x53, 0x86, 0xf4, 0x14, - 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf3, 0xa9, 0x78, 0x24, 0x72, 0x3e, 0x17, 0x75, 0x0f, 0xda, 0x6d, 0xf1, 0x5e, 0x80, - 0x40, 0x0b, 0x3a, 0x3e, 0x36, 0x00, 0x22, 0x7a, 0x71, 0xdd, 0x47, 0x6c, 0x3e, 0xdc, 0xfa, 0xa5, 0x1a, 0xef, 0x52, - 0xe5, 0x0d, 0x0a, 0x11, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0x45, 0xbf, 0xa3, 0x03, 0x55, 0x83, 0x0f, 0x8c, 0xa4, - 0x5e, 0xc0, 0x3d, 0x33, 0x17, 0xa8, 0x97, 0xf6, 0xd1, 0x25, 0xd5, 0x6a, 0xb9, 0x20, 0x37, 0x18, 0xba, 0x90, 0x28, - 0x20, 0xe8, 0x14, 0x83, 0x9c, 0xbe, 0xa9, 0x91, 0xb9, 0x41, 0xee, 0x64, 0x2e, 0x1c, 0xf9, 0x4c, 0xf3, 0xf5, 0x62, - 0x6b, 0x0b, 0xac, 0xec, 0x17, 0x4c, 0x36, 0x00, 0xee, 0x4d, 0xae, 0xae, 0xef, 0x43, 0x61, 0x4a, 0x29, 0x43, 0x6a, - 0x76, 0xd3, 0x15, 0x7d, 0xd8, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x86, 0xff, 0x0e, 0xc5, 0xde, 0xd1, 0x86, 0x65, 0x91, - 0x2d, 0xf2, 0x11, 0x79, 0xea, 0x56, 0x2d, 0x7e, 0x9b, 0x04, 0xae, 0xed, 0x31, 0xcd, 0xe7, 0xd1, 0x0c, 0xae, 0x7d, - 0xe4, 0x80, 0x53, 0x10, 0x44, 0xdc, 0x31, 0x90, 0x5e, 0x0e, 0x05, 0x21, 0x8a, 0xae, 0x31, 0xe5, 0xbb, 0xd1, 0xfd, - 0x4b, 0x7f, 0x91, 0xc6, 0xc0, 0xe9, 0x3e, 0xc6, 0x63, 0xba, 0x77, 0x12, 0x8f, 0x29, 0x10, 0xd1, 0xa2, 0xc4, 0x23, - 0xf4, 0x6c, 0x43, 0x81, 0xfa, 0x0e, 0x0b, 0x3c, 0xcb, 0x44, 0x16, 0xbb, 0x65, 0x63, 0x30, 0xc1, 0x10, 0x95, 0xe3, - 0x6c, 0x16, 0xc5, 0x69, 0x80, 0xdf, 0x07, 0xf1, 0xf4, 0x88, 0x01, 0x76, 0xf1, 0xe0, 0x27, 0x93, 0xb9, 0x68, 0x1d, - 0xd7, 0xff, 0x05, 0xf8, 0x08, 0xf5, 0x2f, 0xa5, 0x1d, 0xa6, 0xe1, 0x52, 0x61, 0xde, 0x7a, 0x29, 0xf0, 0x1e, 0xae, - 0x74, 0x56, 0x46, 0x7e, 0x8e, 0x3d, 0x4e, 0x3f, 0x06, 0xad, 0x4e, 0xd0, 0xd1, 0xa6, 0x6b, 0xed, 0x36, 0xaa, 0xc8, - 0x65, 0x91, 0x37, 0x1a, 0x09, 0x06, 0xfd, 0x2c, 0xe0, 0xac, 0xde, 0x35, 0xac, 0x9e, 0xa4, 0x4b, 0x74, 0xe0, 0x9c, - 0xa6, 0x4e, 0x0d, 0x08, 0x8a, 0x05, 0x5c, 0x33, 0x95, 0x5b, 0x46, 0x24, 0x94, 0xbe, 0xa4, 0x03, 0x5c, 0xbf, 0x4b, - 0x84, 0xf7, 0x86, 0xea, 0x29, 0x50, 0x8a, 0xe4, 0x16, 0xc7, 0x7b, 0xe2, 0xc4, 0x5b, 0x44, 0x63, 0xa1, 0x0d, 0x47, - 0xd0, 0xb6, 0xfe, 0x32, 0x02, 0x2c, 0x7d, 0x0a, 0xed, 0xcd, 0xa5, 0xa3, 0x12, 0xeb, 0x73, 0x98, 0x6b, 0x5f, 0x48, - 0x3d, 0xba, 0x91, 0x6f, 0xf7, 0x37, 0x97, 0xbc, 0xdc, 0xdb, 0x11, 0xbd, 0xfb, 0xc7, 0x65, 0x41, 0x02, 0xca, 0x74, - 0xa4, 0x55, 0x53, 0x88, 0x3a, 0x18, 0x96, 0xd2, 0x77, 0x71, 0xdc, 0x42, 0x2b, 0x5d, 0xc2, 0x63, 0x2c, 0xc9, 0x2e, - 0xc7, 0x74, 0xa5, 0x28, 0x87, 0x33, 0xa9, 0x13, 0x52, 0x72, 0x91, 0x83, 0xd1, 0x5b, 0x85, 0xe2, 0x18, 0x21, 0x18, - 0x6c, 0x2e, 0xe3, 0x32, 0xdc, 0x5c, 0x66, 0xe5, 0x31, 0x68, 0x26, 0x08, 0x55, 0xa1, 0x3e, 0xef, 0x02, 0x13, 0x0b, - 0x27, 0x8b, 0x45, 0x23, 0xe0, 0xb4, 0xac, 0xb4, 0xad, 0x81, 0x80, 0x06, 0x2c, 0x40, 0x2c, 0x00, 0xdd, 0x8d, 0x7a, - 0x31, 0x58, 0x8b, 0x68, 0xdd, 0x87, 0x81, 0xf6, 0x76, 0x44, 0x23, 0x58, 0x57, 0x8e, 0x20, 0x57, 0xcb, 0xc2, 0x74, - 0x1c, 0x73, 0x69, 0x49, 0x74, 0xc2, 0x12, 0xe8, 0x9f, 0x5f, 0x5d, 0xb5, 0xa1, 0x9b, 0x78, 0xb5, 0xf6, 0xe2, 0x74, - 0xbe, 0x90, 0xdf, 0xd4, 0x82, 0x59, 0x3a, 0x18, 0xe6, 0xc4, 0x94, 0xff, 0x81, 0x8a, 0xdb, 0x05, 0x36, 0x8d, 0x6b, - 0x03, 0x3c, 0x14, 0x32, 0x40, 0x50, 0x2a, 0x1a, 0x80, 0xd2, 0x78, 0xbc, 0x5a, 0xa6, 0x97, 0x51, 0xc0, 0x0b, 0x9c, - 0xc1, 0x39, 0x3e, 0xa7, 0xf0, 0x3c, 0x8b, 0x53, 0x7c, 0xcc, 0xf1, 0x31, 0xba, 0xc0, 0xc7, 0xac, 0xb4, 0xff, 0x2e, - 0xe8, 0xb6, 0x34, 0x02, 0xb2, 0xab, 0x2b, 0x60, 0xee, 0x1a, 0x05, 0x40, 0x10, 0xe2, 0xdb, 0x2a, 0xcc, 0xc4, 0x16, - 0x2b, 0xe6, 0x2d, 0x51, 0x6e, 0x91, 0xf0, 0x0c, 0xc1, 0xb6, 0xca, 0x9d, 0x86, 0x8e, 0xe0, 0xc9, 0x2c, 0x92, 0x27, - 0xf8, 0xe2, 0xda, 0x96, 0xf8, 0xf8, 0x85, 0x40, 0x07, 0x3d, 0xe2, 0xda, 0x74, 0x19, 0x97, 0x9f, 0xb5, 0x89, 0x43, - 0x1b, 0x67, 0x01, 0x35, 0x0d, 0x99, 0x3d, 0x8f, 0xe2, 0x44, 0x34, 0x5e, 0xb3, 0x92, 0x46, 0x3a, 0x20, 0x2d, 0x64, - 0x6f, 0xa7, 0x82, 0x0d, 0x80, 0x1f, 0x89, 0xcb, 0xd4, 0x15, 0xf4, 0xb6, 0xa8, 0xa2, 0x28, 0xb9, 0x3c, 0xbc, 0x03, - 0xe1, 0x0f, 0xd7, 0xeb, 0x1c, 0x82, 0x5d, 0x17, 0xa5, 0xf5, 0x16, 0x00, 0xf1, 0x9c, 0xb1, 0xb1, 0x67, 0x5b, 0xc0, - 0x26, 0xc5, 0xf3, 0xc7, 0x84, 0x9d, 0x31, 0xf9, 0x11, 0x14, 0xdd, 0x57, 0x57, 0x8e, 0x40, 0xda, 0x72, 0x79, 0x3f, - 0x53, 0x52, 0x9e, 0x5a, 0x97, 0x5c, 0x7d, 0x1d, 0x78, 0xcf, 0x36, 0x06, 0x6d, 0xce, 0xd1, 0xae, 0x0f, 0xeb, 0x75, - 0x40, 0x91, 0xb5, 0x01, 0x4c, 0xd2, 0xcf, 0x6e, 0x5a, 0x0a, 0xf4, 0x63, 0x93, 0x09, 0x1c, 0x00, 0x15, 0xb7, 0xd0, - 0xa7, 0x5b, 0x00, 0x03, 0x66, 0xa6, 0x67, 0x8b, 0x16, 0x76, 0xd5, 0x56, 0x3f, 0x21, 0x2a, 0x92, 0x6c, 0xf4, 0xa9, - 0x36, 0xc5, 0x02, 0x09, 0x08, 0xc7, 0x6a, 0xf0, 0x29, 0xfb, 0xdf, 0xfe, 0xf5, 0x7f, 0xfe, 0x57, 0x18, 0x8e, 0x3a, - 0xb8, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x01, 0xad, 0x16, 0xe9, 0x2d, 0xed, 0xfe, 0xf6, 0xcf, 0xff, 0x0d, 0x9a, 0xd1, - 0xcd, 0x1a, 0xb7, 0x3c, 0x0e, 0xec, 0x11, 0x6a, 0x32, 0x77, 0x03, 0xa4, 0xd6, 0xf5, 0xda, 0xf1, 0xff, 0x05, 0x81, - 0x2d, 0x78, 0x36, 0xbf, 0x11, 0x08, 0x84, 0x75, 0x94, 0x64, 0x05, 0x13, 0x50, 0x08, 0x36, 0x79, 0x47, 0x30, 0x68, - 0x86, 0x39, 0x90, 0x6c, 0x61, 0x89, 0xde, 0x02, 0xfb, 0xb5, 0xde, 0x8d, 0x5d, 0x29, 0x18, 0x27, 0xd0, 0xc9, 0x03, - 0x00, 0xfb, 0x20, 0x9e, 0xe0, 0x81, 0x4e, 0x33, 0x6c, 0xbb, 0xce, 0x17, 0x68, 0x0c, 0xa1, 0x89, 0x4c, 0x8c, 0x20, - 0x5c, 0x1d, 0xaa, 0x1f, 0xfc, 0x04, 0xd6, 0xf2, 0x51, 0x3f, 0x47, 0x17, 0xfa, 0x19, 0xd9, 0x0f, 0x0c, 0x0b, 0x82, - 0x62, 0x86, 0x3a, 0x40, 0x73, 0x61, 0xea, 0xa4, 0x56, 0xfc, 0x81, 0xa9, 0xe4, 0xb0, 0x8f, 0x98, 0x0f, 0x89, 0xb7, - 0x5f, 0x16, 0x39, 0xab, 0x38, 0x26, 0x36, 0x10, 0xac, 0xc8, 0xac, 0xff, 0x98, 0x64, 0xe7, 0xe5, 0x75, 0x75, 0x53, - 0xa0, 0xe2, 0x72, 0x6f, 0x1c, 0x9f, 0xf5, 0x25, 0x22, 0x1b, 0x6b, 0x59, 0xed, 0xd2, 0x5c, 0x18, 0x56, 0xc9, 0x75, - 0xc9, 0x47, 0x5c, 0x96, 0xd7, 0x46, 0x01, 0x80, 0xe3, 0xee, 0x9d, 0xe4, 0x7d, 0xb9, 0x80, 0x57, 0x78, 0x61, 0x8b, - 0x20, 0x41, 0x3e, 0x2e, 0x64, 0x0c, 0x27, 0x19, 0x63, 0xb2, 0x7a, 0xd4, 0x5a, 0x33, 0xc5, 0xd2, 0xb1, 0x61, 0x8d, - 0x0b, 0x73, 0xc9, 0x85, 0x63, 0xa9, 0x0e, 0x49, 0x2e, 0x8c, 0x1f, 0xe0, 0x68, 0x70, 0xe1, 0xf8, 0x5a, 0x2e, 0x8c, - 0x6b, 0x1b, 0xe0, 0xc8, 0xa1, 0xbd, 0x8d, 0xb6, 0xb8, 0x21, 0x15, 0x38, 0x0a, 0x37, 0x30, 0xc0, 0x46, 0x9f, 0xa4, - 0x6c, 0x23, 0x50, 0xd1, 0x18, 0x95, 0xd2, 0x9a, 0x04, 0x9b, 0x64, 0xcf, 0xc1, 0xe2, 0x18, 0x64, 0x9b, 0x39, 0x32, - 0x58, 0xc2, 0x13, 0x86, 0xc7, 0xff, 0x78, 0x07, 0xfb, 0x8a, 0xcd, 0x2c, 0xe9, 0x19, 0xa4, 0xcf, 0x0e, 0x0d, 0xe0, - 0x2d, 0x85, 0x3b, 0x23, 0xb0, 0xdf, 0xbe, 0x39, 0x38, 0xb4, 0xbd, 0x93, 0x6c, 0x7c, 0x19, 0xd8, 0xa0, 0x8a, 0x82, - 0x24, 0x73, 0x7d, 0x3e, 0x65, 0xa9, 0xa3, 0x94, 0xc1, 0x2c, 0x01, 0x65, 0x38, 0x3b, 0x15, 0xb7, 0xef, 0x9b, 0xae, - 0x58, 0x40, 0x1b, 0x7d, 0x9e, 0xaf, 0xbf, 0xc7, 0xc5, 0x97, 0x2b, 0x79, 0x8e, 0x8f, 0x7d, 0x0c, 0x46, 0xef, 0xed, - 0xc0, 0x03, 0xbe, 0x1c, 0x20, 0x05, 0xe9, 0x37, 0x01, 0x67, 0x21, 0xde, 0x77, 0xb0, 0xfd, 0x8e, 0xea, 0x8b, 0x50, - 0x28, 0x1a, 0xd0, 0xfa, 0x5a, 0xa5, 0x04, 0xd0, 0xd8, 0x63, 0x22, 0x41, 0xdc, 0x18, 0xc0, 0x01, 0x1f, 0xeb, 0x12, - 0x41, 0xa6, 0x46, 0x11, 0x8d, 0x52, 0xb1, 0x7f, 0x59, 0x85, 0x13, 0x12, 0xfa, 0xc4, 0x64, 0xf0, 0x93, 0xc0, 0x3f, - 0x36, 0xbf, 0x34, 0x25, 0x3e, 0x0a, 0xa3, 0x17, 0x79, 0xf4, 0x57, 0xb0, 0x61, 0xbd, 0xf3, 0x63, 0x6a, 0xa9, 0xcc, - 0x1a, 0xb4, 0xb7, 0xd1, 0xfc, 0x6b, 0x2b, 0xfb, 0x15, 0x24, 0x5e, 0x12, 0xcd, 0x0b, 0x16, 0xa8, 0x07, 0x69, 0xe1, - 0xa0, 0xa1, 0xb4, 0x6a, 0x52, 0x9a, 0x92, 0xb1, 0xe4, 0xd3, 0xa5, 0x69, 0x02, 0x3d, 0x04, 0x13, 0x08, 0xd3, 0xb7, - 0x5b, 0x11, 0xb0, 0xf7, 0x34, 0x48, 0xd8, 0x84, 0x97, 0x1c, 0xef, 0x07, 0x2f, 0x95, 0xcd, 0xe9, 0x77, 0x1f, 0x80, - 0x59, 0x64, 0xf9, 0xf8, 0xff, 0x6d, 0x63, 0x8f, 0x83, 0x14, 0xcc, 0x18, 0xba, 0x30, 0x80, 0x97, 0xb1, 0x00, 0x22, - 0xf3, 0x7d, 0x69, 0x4c, 0x34, 0x62, 0x68, 0x8f, 0x97, 0x3c, 0xb7, 0xf8, 0xd4, 0xe3, 0xb9, 0xd9, 0x0e, 0x34, 0xa5, - 0x15, 0xa3, 0x7c, 0xd5, 0x2c, 0xdc, 0x75, 0xa5, 0xf2, 0xb8, 0xda, 0x58, 0xd9, 0xd6, 0xf5, 0xb7, 0x15, 0x0c, 0x19, - 0x5e, 0x80, 0x52, 0x70, 0xbe, 0xa5, 0xe8, 0x61, 0xae, 0x69, 0xd5, 0x3f, 0x70, 0xab, 0xee, 0x51, 0xd2, 0xd9, 0x3e, - 0xa2, 0xb3, 0x4d, 0xcc, 0x65, 0xb8, 0x14, 0x73, 0x8f, 0xa2, 0x64, 0xe4, 0x20, 0x00, 0x56, 0xcb, 0xba, 0x0f, 0xd8, - 0x04, 0x2e, 0x3d, 0x2c, 0xcb, 0xde, 0x25, 0x73, 0x8e, 0x7e, 0x93, 0x79, 0xe4, 0xe2, 0xfa, 0xa0, 0xfe, 0x04, 0x5b, - 0xbb, 0x74, 0x87, 0xde, 0xf7, 0xc6, 0x77, 0xad, 0x6c, 0x45, 0xa9, 0xb6, 0x07, 0xf8, 0xfd, 0x3e, 0xc4, 0xbe, 0xaf, - 0x1c, 0x1b, 0xb5, 0x10, 0xaa, 0xb9, 0x6c, 0x11, 0xe1, 0xd8, 0xd8, 0x4d, 0x78, 0x41, 0xbf, 0xba, 0xce, 0x98, 0xfd, - 0xee, 0x76, 0x63, 0x96, 0xdd, 0xd1, 0x98, 0xfd, 0xee, 0x4f, 0x36, 0x66, 0xbf, 0x6a, 0x1a, 0xb3, 0xbf, 0xfe, 0x1e, - 0x63, 0x36, 0xcf, 0xce, 0x8b, 0xb0, 0x23, 0x83, 0xa7, 0xc0, 0x4c, 0xfe, 0x3e, 0x56, 0x2d, 0x4c, 0xd4, 0xb0, 0x69, - 0xc9, 0x88, 0x15, 0xf9, 0x5e, 0xc0, 0xab, 0xa5, 0x09, 0xd9, 0xd6, 0x89, 0x55, 0xad, 0xfb, 0xea, 0x26, 0x09, 0xe8, - 0xf5, 0xae, 0xbe, 0x03, 0xd5, 0x55, 0x46, 0x66, 0x40, 0x9f, 0x82, 0xd4, 0x1d, 0xbb, 0xdb, 0x2a, 0xa3, 0xc7, 0x1c, - 0xa1, 0xa7, 0x1c, 0xb5, 0x82, 0x7c, 0x96, 0xf6, 0x7f, 0x3a, 0xea, 0xf4, 0x76, 0x3b, 0x33, 0xe8, 0x0d, 0x72, 0x0b, - 0xde, 0xda, 0xbd, 0xdd, 0x5d, 0x7c, 0x3b, 0x57, 0x6f, 0x5d, 0x7c, 0x8b, 0xd5, 0xdb, 0x03, 0x7c, 0x1b, 0xa9, 0xb7, - 0x87, 0xf8, 0x36, 0x56, 0x6f, 0x8f, 0xf0, 0xed, 0xcc, 0x2e, 0x8f, 0xb8, 0x06, 0xee, 0x11, 0xd0, 0x15, 0x29, 0x89, - 0x81, 0x2a, 0x83, 0xd3, 0x88, 0x37, 0xb0, 0xa2, 0xd3, 0x20, 0xf6, 0x84, 0x02, 0x1d, 0x14, 0xde, 0x39, 0xb0, 0xf4, - 0x80, 0x12, 0x8e, 0x9e, 0xe2, 0x55, 0x7c, 0xd0, 0x3d, 0x0f, 0xe3, 0x19, 0x53, 0xdf, 0x24, 0x55, 0xab, 0x06, 0x35, - 0x05, 0xec, 0xed, 0xb2, 0xa7, 0xf7, 0x49, 0xd8, 0xd0, 0x2a, 0x77, 0x82, 0x76, 0xae, 0xaa, 0x13, 0xd3, 0xb5, 0xf4, - 0x0e, 0x5f, 0x23, 0x20, 0x40, 0x00, 0x2b, 0xa3, 0x74, 0x02, 0x6a, 0x40, 0xeb, 0x02, 0x94, 0xf4, 0xb5, 0x42, 0x03, - 0x21, 0xd2, 0x62, 0x82, 0xd6, 0xa4, 0xdf, 0x0e, 0xa3, 0x53, 0xfd, 0xfc, 0x0a, 0xf4, 0xa9, 0xe8, 0x94, 0xdd, 0x26, - 0x40, 0x08, 0x44, 0x53, 0x78, 0x28, 0x20, 0x48, 0x0b, 0x81, 0xad, 0x41, 0x63, 0x41, 0x0a, 0x0f, 0xc4, 0x4e, 0x5d, - 0x9c, 0xd0, 0xf4, 0xf5, 0x22, 0xc0, 0x68, 0x55, 0xb0, 0x07, 0x6a, 0x1d, 0x95, 0x0a, 0x0c, 0x43, 0x05, 0x16, 0xdc, - 0x28, 0x63, 0x84, 0x2a, 0x72, 0x93, 0xa4, 0xb1, 0x94, 0x90, 0x31, 0x1d, 0xbc, 0xda, 0xbb, 0xbb, 0xca, 0xf7, 0x3e, - 0xeb, 0x8c, 0xf0, 0x8f, 0xe4, 0xaa, 0x9f, 0x4d, 0x26, 0x93, 0x1b, 0x85, 0xce, 0x67, 0xe3, 0x09, 0xeb, 0xb2, 0x07, - 0x3d, 0x74, 0xfe, 0xb5, 0xa4, 0x2f, 0xae, 0x53, 0x12, 0xee, 0x96, 0x77, 0x6b, 0x8c, 0xce, 0x38, 0x90, 0x43, 0x77, - 0x97, 0x4e, 0x25, 0x60, 0x65, 0x09, 0x5c, 0xf9, 0x34, 0x4e, 0x83, 0x76, 0xe9, 0x9f, 0x49, 0x76, 0xfe, 0xd9, 0xe3, - 0xc7, 0x8f, 0x4b, 0x7f, 0xac, 0xde, 0xda, 0xe3, 0x71, 0xe9, 0x8f, 0x96, 0x7a, 0x19, 0xed, 0xf6, 0x64, 0x52, 0xfa, - 0xb1, 0x2a, 0xd8, 0xed, 0x8e, 0xc6, 0xbb, 0xdd, 0xd2, 0x3f, 0x37, 0x5a, 0x94, 0x3e, 0x93, 0x6f, 0x39, 0x1b, 0xd7, - 0x3c, 0x88, 0x8f, 0xc0, 0x78, 0xf5, 0x05, 0xa1, 0x2d, 0xd1, 0x64, 0x10, 0x8f, 0x41, 0xb4, 0xe0, 0x60, 0xeb, 0x02, - 0x6f, 0x67, 0xc0, 0x9f, 0x27, 0x92, 0xb7, 0x8b, 0x4f, 0x7e, 0x22, 0x47, 0xff, 0xd5, 0xe4, 0xe8, 0x48, 0xcc, 0xc4, - 0xcd, 0x19, 0xc9, 0x81, 0x66, 0x35, 0x52, 0x16, 0x55, 0xff, 0x1a, 0xb2, 0x8a, 0xd9, 0x23, 0xb7, 0xc1, 0x96, 0x82, - 0xc7, 0x7f, 0x7d, 0x1d, 0x8f, 0xff, 0xe6, 0x76, 0x1e, 0x7f, 0x72, 0x37, 0x16, 0xff, 0xcd, 0x9f, 0xcc, 0xe2, 0xbf, - 0x6e, 0xb2, 0xf8, 0xcd, 0x3b, 0xb1, 0xf8, 0x35, 0x89, 0x1f, 0xa4, 0x9a, 0xbe, 0x49, 0x43, 0xfb, 0x0d, 0xd8, 0x30, - 0x46, 0xc9, 0x64, 0x02, 0x45, 0x93, 0x89, 0xad, 0x92, 0x1d, 0x81, 0x13, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, - 0xd5, 0x57, 0x66, 0x99, 0x59, 0x20, 0xfd, 0x0d, 0xa6, 0x7c, 0x57, 0x35, 0x52, 0x65, 0x56, 0x9f, 0x06, 0x19, 0xc7, - 0x05, 0x9e, 0x26, 0x2c, 0x28, 0x79, 0x76, 0x7a, 0x9a, 0x30, 0xfd, 0xed, 0x33, 0xd5, 0xd2, 0x7c, 0x33, 0xe7, 0x33, - 0xcb, 0x07, 0x26, 0xb4, 0x41, 0x0d, 0xd0, 0x9e, 0x70, 0x64, 0xd2, 0xe7, 0xa0, 0x45, 0xd8, 0xfa, 0x4c, 0x7e, 0x37, - 0x98, 0xfc, 0xa9, 0x4b, 0xc9, 0x7e, 0x65, 0x40, 0xb3, 0xea, 0x8a, 0x2e, 0x4c, 0x91, 0x02, 0x32, 0x2e, 0x95, 0xdb, - 0x12, 0xa0, 0x9d, 0xe3, 0x47, 0x4e, 0x74, 0xca, 0xd2, 0xca, 0x37, 0x85, 0x34, 0x9b, 0xc0, 0x8f, 0x1e, 0x88, 0x29, - 0xc4, 0x67, 0x02, 0xf5, 0xb8, 0x22, 0x0e, 0xe8, 0xd4, 0xd6, 0x68, 0xac, 0x2a, 0x0c, 0xcd, 0xa5, 0xa8, 0x9c, 0x93, - 0xd5, 0x79, 0xd6, 0x8a, 0xe6, 0xeb, 0x85, 0xf2, 0xdd, 0xa6, 0xbb, 0x45, 0x34, 0x14, 0xe7, 0x76, 0x5f, 0xdb, 0x98, - 0x35, 0x9a, 0x29, 0xeb, 0x5e, 0x38, 0x9a, 0xe8, 0x24, 0xbb, 0xa8, 0xdb, 0x48, 0x26, 0x0c, 0x68, 0x3e, 0xe9, 0xbd, - 0x57, 0x75, 0xaa, 0xa0, 0x34, 0xbd, 0xa2, 0x22, 0xd3, 0x8b, 0x48, 0x83, 0x7c, 0x60, 0xb0, 0x03, 0xa9, 0x60, 0xca, - 0x30, 0x0f, 0x71, 0x17, 0x6d, 0x47, 0xa0, 0x32, 0x6d, 0x2b, 0x60, 0x51, 0x3a, 0xe4, 0xe8, 0x6b, 0xc2, 0x0e, 0x7d, - 0xab, 0x06, 0x70, 0xaa, 0x6d, 0xb3, 0xdb, 0x19, 0x3e, 0x98, 0x16, 0xe7, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, - 0xf3, 0x25, 0xb0, 0xb0, 0x93, 0x4f, 0x31, 0x07, 0x85, 0x71, 0xde, 0x42, 0xa3, 0x98, 0xdc, 0x3b, 0x92, 0xd7, 0x53, - 0xa8, 0x45, 0x5c, 0x89, 0xe8, 0x2d, 0x0a, 0xb4, 0x40, 0x48, 0xd5, 0x0e, 0xd2, 0x2c, 0x65, 0xbd, 0x7a, 0x48, 0xcd, - 0xd4, 0x76, 0x15, 0xb6, 0x86, 0xcb, 0x0c, 0x2d, 0x16, 0x7e, 0x09, 0x16, 0x8b, 0x90, 0x11, 0x6d, 0x15, 0x8e, 0x69, - 0xaf, 0x6d, 0x1f, 0x48, 0x64, 0x6e, 0x93, 0x28, 0xcc, 0x57, 0x55, 0xfa, 0xeb, 0x54, 0xf2, 0xdb, 0x02, 0x4c, 0xdd, - 0x07, 0x0f, 0x3c, 0xf5, 0xcf, 0x88, 0xcc, 0x35, 0x8b, 0x29, 0xc0, 0x74, 0x17, 0xc8, 0x82, 0x68, 0x82, 0x5f, 0x10, - 0xbb, 0x4b, 0xcb, 0x13, 0xca, 0xee, 0x5a, 0xa2, 0xcc, 0x0a, 0x3a, 0x8f, 0xc1, 0xc6, 0xb8, 0xf3, 0xf0, 0x37, 0x2f, - 0xbf, 0x94, 0x38, 0x52, 0x97, 0xf4, 0x6c, 0xbb, 0x87, 0xa7, 0x39, 0x89, 0x2e, 0xc1, 0xd4, 0x21, 0x01, 0x7a, 0x82, - 0xce, 0xae, 0xde, 0x3c, 0x93, 0x91, 0xd2, 0x9c, 0x25, 0xf4, 0x99, 0x7e, 0xb9, 0x15, 0xbb, 0x0f, 0xe7, 0x17, 0x6a, - 0x37, 0x3a, 0x8d, 0x08, 0xe8, 0x9f, 0x1a, 0xe8, 0xbc, 0x3e, 0xb2, 0x5a, 0x0f, 0xd6, 0x3d, 0x00, 0x18, 0x84, 0xd4, - 0x6e, 0xe5, 0x02, 0xaa, 0x36, 0x94, 0x18, 0xa1, 0xde, 0x6a, 0x20, 0xcb, 0xdf, 0x05, 0x09, 0x11, 0x81, 0xbd, 0x8b, - 0x9f, 0x72, 0x8b, 0xc1, 0xa0, 0x92, 0x9a, 0xc1, 0x2c, 0x1e, 0x8f, 0x13, 0xd6, 0x53, 0xc2, 0xdf, 0xea, 0x3c, 0xc4, - 0x48, 0xa9, 0xb9, 0x65, 0xf5, 0x5d, 0x31, 0x90, 0xa7, 0xf1, 0x14, 0x9d, 0x80, 0x32, 0x82, 0xdf, 0x63, 0x5b, 0x8b, - 0x4e, 0x19, 0x42, 0x6c, 0x57, 0xc8, 0xa3, 0xe7, 0xfa, 0x5a, 0x1e, 0x80, 0x26, 0x44, 0x1b, 0x0e, 0x46, 0x75, 0x36, - 0x0f, 0x5a, 0xbb, 0xf5, 0x85, 0x60, 0x95, 0x5e, 0x82, 0xb7, 0x66, 0x59, 0x1e, 0xd0, 0x44, 0x4b, 0x7c, 0xf8, 0xc7, - 0xf2, 0x3b, 0xb2, 0x8c, 0x06, 0xc0, 0x6f, 0x7e, 0xe9, 0xa2, 0xb2, 0xbe, 0x98, 0xff, 0x3f, 0xa7, 0xe5, 0x8b, 0xf5, - 0xa7, 0xe5, 0x0b, 0x75, 0x5a, 0x6e, 0xa6, 0xd8, 0xcf, 0x26, 0x1d, 0xfc, 0xd3, 0xab, 0x16, 0x04, 0xbb, 0x02, 0xe8, - 0xb0, 0x50, 0xe9, 0x6b, 0x75, 0xe1, 0x3f, 0x1a, 0xba, 0xed, 0xe1, 0x1f, 0x1f, 0xd4, 0x9b, 0xb6, 0x85, 0x85, 0xf8, - 0xaf, 0x5d, 0xab, 0xea, 0xdc, 0xc7, 0x3a, 0xec, 0xf5, 0x60, 0xb5, 0xae, 0x7b, 0xf3, 0xa1, 0x05, 0x7e, 0xc5, 0x9d, - 0x40, 0x31, 0x63, 0xb0, 0x43, 0xa2, 0x93, 0x13, 0x28, 0x9d, 0x64, 0xa3, 0x45, 0xf1, 0x8f, 0x12, 0x7e, 0x89, 0xc4, - 0x1b, 0x8f, 0x74, 0x63, 0x1c, 0xd5, 0x55, 0x84, 0xdd, 0xd5, 0x08, 0x4b, 0xbd, 0x4f, 0x41, 0x01, 0x84, 0xc9, 0x9c, - 0xae, 0x7f, 0x7f, 0xcd, 0x21, 0xf8, 0xbb, 0xec, 0xcd, 0xda, 0xc5, 0xfc, 0x7b, 0x91, 0x71, 0x23, 0x12, 0x7e, 0x17, - 0x0e, 0xcc, 0x3d, 0x6c, 0x3f, 0x5e, 0x0f, 0xee, 0x91, 0x9a, 0x69, 0xa8, 0x84, 0x82, 0x94, 0x3b, 0xa0, 0xe2, 0x46, - 0x8b, 0x84, 0xdf, 0x3c, 0xea, 0x75, 0x94, 0xb1, 0x32, 0xea, 0x0d, 0x0c, 0xbd, 0x6a, 0x7b, 0x47, 0x2e, 0xfd, 0xd9, - 0x17, 0xf7, 0xf1, 0x8f, 0xf0, 0xea, 0x9c, 0x54, 0x8a, 0xbf, 0x30, 0x7c, 0x51, 0xf1, 0xdf, 0xac, 0x69, 0xf6, 0x42, - 0x82, 0x93, 0x72, 0x7f, 0xd7, 0xd6, 0xa8, 0xcf, 0xde, 0xa9, 0xb9, 0xd4, 0x83, 0x7e, 0x57, 0xeb, 0xdf, 0x37, 0xf8, - 0x1d, 0xdb, 0x8e, 0x84, 0xce, 0x5c, 0x6f, 0x2b, 0x7f, 0x65, 0xc2, 0x6a, 0x63, 0x81, 0xe7, 0xbb, 0x36, 0x57, 0x1b, - 0x44, 0xed, 0x37, 0xc3, 0x13, 0x6d, 0x1e, 0xc9, 0xb0, 0x1b, 0xb6, 0x17, 0x16, 0xd2, 0xb7, 0x2c, 0xbc, 0x87, 0x9f, - 0x1a, 0xb2, 0x2e, 0x66, 0x49, 0x0a, 0x3a, 0xd5, 0x94, 0xf3, 0x79, 0xb0, 0xb3, 0x73, 0x7e, 0x7e, 0xee, 0x9f, 0xef, - 0xfa, 0x59, 0x7e, 0xba, 0xd3, 0x6d, 0xb7, 0xdb, 0xf8, 0x85, 0x18, 0xdb, 0x3a, 0x8b, 0xd9, 0xf9, 0x97, 0xd9, 0x45, - 0x68, 0x3f, 0xb2, 0x1e, 0x5b, 0x8f, 0x76, 0xad, 0x07, 0x0f, 0x6d, 0x8b, 0xb8, 0x3f, 0x94, 0xec, 0xda, 0x96, 0xe0, - 0xfe, 0xa1, 0x0d, 0xc5, 0xfd, 0xbd, 0x53, 0xa5, 0xc0, 0x61, 0x06, 0xae, 0x50, 0x8f, 0xc0, 0x66, 0xc9, 0x3e, 0xb1, - 0xfa, 0x39, 0x17, 0x65, 0x2d, 0x29, 0x43, 0xd4, 0x2b, 0x1e, 0xf6, 0x51, 0x34, 0x0f, 0x88, 0x86, 0xcc, 0x42, 0x74, - 0x00, 0x89, 0x52, 0x9a, 0x02, 0xa3, 0xba, 0x27, 0xf0, 0x04, 0x1a, 0xfb, 0xd4, 0x82, 0xe7, 0x57, 0xdd, 0x47, 0x20, - 0xe0, 0xce, 0x5a, 0xf7, 0x47, 0xed, 0x56, 0xc7, 0xea, 0xb4, 0xba, 0xfe, 0x23, 0xab, 0x2b, 0xfe, 0x07, 0x06, 0xb9, - 0x6b, 0x75, 0xe0, 0x69, 0xd7, 0x82, 0xf7, 0xb3, 0xfb, 0x22, 0x24, 0x1c, 0xd9, 0x3b, 0xfd, 0x3d, 0xfc, 0x85, 0x29, - 0xb0, 0xa8, 0x2f, 0x6c, 0xf1, 0x2b, 0x9e, 0xec, 0xcf, 0xcc, 0xd2, 0xce, 0xe3, 0xb5, 0xc5, 0xdd, 0x47, 0x6b, 0x8b, - 0x77, 0x1f, 0xae, 0x2d, 0xbe, 0xff, 0xa0, 0x5e, 0xbc, 0x73, 0x2a, 0xaa, 0x34, 0x53, 0x08, 0xed, 0x59, 0x04, 0x54, - 0x72, 0xe1, 0x74, 0x00, 0xce, 0xb6, 0xd5, 0xc2, 0x1f, 0x8f, 0xba, 0xae, 0xee, 0x75, 0x82, 0xbd, 0xf4, 0x2a, 0x1f, - 0x3d, 0x86, 0x55, 0x3e, 0xef, 0x3e, 0x1c, 0x61, 0x3b, 0x5a, 0x28, 0xfc, 0x3b, 0xdb, 0x7d, 0x3c, 0x02, 0x71, 0x60, - 0xe1, 0x3f, 0xf8, 0x33, 0x7d, 0xd0, 0x1d, 0x89, 0x97, 0x36, 0xd6, 0x7f, 0xe8, 0x3c, 0x2a, 0xa0, 0x29, 0xfe, 0xf9, - 0x4d, 0xeb, 0xcf, 0xa8, 0xbe, 0x9b, 0xe3, 0xde, 0x07, 0x1c, 0x3d, 0x9e, 0x76, 0xfd, 0x2f, 0xce, 0x1e, 0xf9, 0x8f, - 0xa7, 0x9d, 0x47, 0x1f, 0xc4, 0x5b, 0x02, 0x18, 0xfc, 0x02, 0xff, 0x7d, 0xd8, 0x6d, 0x83, 0x69, 0xeb, 0x3f, 0x3e, - 0xdb, 0xf5, 0x77, 0x93, 0xd6, 0x43, 0xff, 0x31, 0xfe, 0xab, 0x86, 0x9b, 0x66, 0x33, 0x66, 0x5b, 0xb8, 0xdf, 0x0d, - 0xbb, 0xd0, 0x9c, 0xa3, 0x7b, 0xdf, 0x7a, 0x70, 0xff, 0xf9, 0x63, 0xd8, 0xa3, 0x69, 0xa7, 0x0b, 0xff, 0x5f, 0xf7, - 0xf8, 0x01, 0x11, 0x2f, 0x07, 0x8e, 0x18, 0xe6, 0xce, 0x29, 0xc4, 0xd1, 0xd7, 0x8a, 0xee, 0x79, 0x3f, 0x5e, 0x67, - 0xda, 0xff, 0x70, 0xbb, 0x69, 0xff, 0xd7, 0x3b, 0xba, 0x6f, 0x7f, 0xf8, 0x93, 0x6d, 0xfb, 0x1f, 0x9b, 0xb6, 0xfd, - 0x39, 0x5b, 0x31, 0xee, 0x9b, 0xf6, 0xd9, 0x21, 0x73, 0x8e, 0xbe, 0x65, 0x43, 0xcc, 0x13, 0x85, 0xd6, 0x7f, 0xad, - 0x79, 0x3a, 0x32, 0x3c, 0xc8, 0xe7, 0x4c, 0x9c, 0xe4, 0xef, 0xaf, 0x43, 0x08, 0xe3, 0xb7, 0x22, 0xe4, 0xc5, 0xdd, - 0xf0, 0x41, 0x9f, 0x16, 0xff, 0x13, 0xf1, 0xf1, 0xbd, 0x89, 0x8f, 0x9a, 0x2f, 0x99, 0x8c, 0x79, 0xb2, 0xc1, 0x0f, - 0xe8, 0xdd, 0xb1, 0x77, 0x18, 0xbe, 0x15, 0xb6, 0x48, 0x7e, 0x7a, 0xf7, 0x7b, 0xfc, 0xde, 0x22, 0x8d, 0x32, 0xb4, - 0xa5, 0x83, 0x62, 0x8e, 0x1f, 0xe3, 0x54, 0x2f, 0x67, 0x22, 0x55, 0x3f, 0xa4, 0x7b, 0x36, 0xf7, 0xb5, 0x73, 0x03, - 0x33, 0x5b, 0xc3, 0x65, 0xc6, 0x23, 0xfc, 0x6d, 0x2f, 0x3c, 0xe6, 0x09, 0xde, 0x02, 0x94, 0x37, 0x66, 0x30, 0x11, - 0xf3, 0x5b, 0x4c, 0x22, 0x55, 0xee, 0xef, 0x19, 0x3a, 0x0c, 0x5e, 0xb1, 0x71, 0x1c, 0x39, 0xb6, 0x33, 0x87, 0x13, - 0x0b, 0x63, 0xb6, 0x6a, 0x19, 0x9c, 0x94, 0xbc, 0xe9, 0xda, 0xea, 0x17, 0x8c, 0xe4, 0xf8, 0xc1, 0xa6, 0xf0, 0x48, - 0xba, 0xce, 0x6c, 0xa9, 0xfe, 0xc3, 0xf8, 0xaa, 0x24, 0x47, 0xd6, 0x5d, 0xa9, 0x0c, 0xb6, 0xd0, 0x19, 0x3a, 0x7e, - 0x17, 0x6c, 0x08, 0x2a, 0xc6, 0x0f, 0xe0, 0xfc, 0xe0, 0xb4, 0x76, 0x41, 0xa7, 0x31, 0xba, 0xe9, 0x81, 0x86, 0x2b, - 0x1f, 0xdf, 0x14, 0x7e, 0x83, 0x46, 0xa9, 0xa7, 0x7f, 0xe3, 0x12, 0x50, 0x86, 0xca, 0xf5, 0xff, 0xf2, 0xf2, 0x50, - 0x5e, 0x72, 0xb5, 0xd1, 0x27, 0x49, 0xbe, 0xe8, 0xea, 0x03, 0x3b, 0xdb, 0x20, 0x2e, 0xe8, 0xd7, 0xde, 0x51, 0x50, - 0x16, 0x25, 0x02, 0xe6, 0x98, 0x5a, 0xd2, 0x6c, 0x08, 0x6d, 0x21, 0x0f, 0xc6, 0xec, 0x2c, 0x1e, 0x49, 0xb6, 0xee, - 0x59, 0x32, 0x37, 0xbe, 0x45, 0xab, 0x08, 0x3b, 0x9e, 0x30, 0x9c, 0xe1, 0x05, 0x65, 0x54, 0x98, 0x66, 0x76, 0xff, - 0x5e, 0x4f, 0x43, 0x52, 0x4f, 0xcf, 0xb5, 0xf1, 0x77, 0xf0, 0x1d, 0x81, 0xa1, 0xf6, 0x8f, 0xe1, 0x3d, 0xfc, 0x2d, - 0x7c, 0xf7, 0x86, 0xb6, 0xeb, 0x13, 0x53, 0xbc, 0x57, 0xfd, 0x2a, 0x3e, 0xe4, 0x08, 0xdb, 0x20, 0xbf, 0xbc, 0xbb, - 0x0a, 0x32, 0x29, 0xb4, 0xba, 0x0f, 0x2a, 0xa1, 0x05, 0xcf, 0x06, 0x97, 0x02, 0x06, 0xda, 0xf5, 0x1f, 0x18, 0xac, - 0xf0, 0xac, 0x85, 0x3f, 0x6b, 0xcc, 0xf0, 0x3e, 0x34, 0x50, 0xdc, 0xf0, 0x25, 0x34, 0xdf, 0x15, 0x8c, 0x17, 0xfa, - 0xfd, 0x48, 0xac, 0x4a, 0xb0, 0xa9, 0x3a, 0xc5, 0xac, 0x09, 0x8f, 0x88, 0x78, 0xb6, 0xed, 0x39, 0xfa, 0xfb, 0xfe, - 0x92, 0x5c, 0xe5, 0xe5, 0xa4, 0xa7, 0xd0, 0xd7, 0xd1, 0xdf, 0xad, 0x5d, 0x57, 0xe7, 0xd5, 0x4e, 0xce, 0x9a, 0x29, - 0x90, 0xe0, 0x1b, 0x21, 0x18, 0xca, 0xd5, 0x16, 0xdf, 0x6f, 0x12, 0xc7, 0xb8, 0xfa, 0xc2, 0xd5, 0x9a, 0x74, 0x43, - 0xf3, 0x50, 0xb0, 0x8a, 0x68, 0xe8, 0x5c, 0x00, 0x23, 0xa0, 0x9f, 0x55, 0xb1, 0x7a, 0x90, 0x04, 0xe5, 0x27, 0x11, - 0xfe, 0xfa, 0x09, 0xfa, 0x51, 0x56, 0x07, 0x90, 0xd3, 0x07, 0xfa, 0x08, 0xd2, 0x17, 0xe3, 0xb2, 0xb9, 0x08, 0xd0, - 0x17, 0xf0, 0xb7, 0x99, 0x55, 0xb9, 0xe1, 0xf2, 0xd2, 0x17, 0x86, 0xc1, 0xc7, 0x71, 0x4e, 0x77, 0x09, 0xd5, 0xfa, - 0x6b, 0xd7, 0xfc, 0x2a, 0x54, 0xd3, 0xa9, 0x64, 0xc5, 0xc0, 0xc6, 0x22, 0x5b, 0x65, 0xe9, 0x98, 0x5f, 0xa8, 0x35, - 0x2f, 0x7b, 0x8d, 0x45, 0x9a, 0x0e, 0x7e, 0xc1, 0xdb, 0x16, 0x48, 0xb6, 0x81, 0x8d, 0x5d, 0xbb, 0x26, 0x52, 0x6e, - 0xf0, 0x8e, 0x54, 0xf5, 0x2b, 0x59, 0xcc, 0x03, 0x6f, 0x9b, 0xbb, 0xa5, 0xc7, 0xa5, 0x7d, 0x70, 0xa5, 0xa7, 0xf0, - 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0xf7, 0x70, 0x0c, 0xb6, 0xe0, 0x75, 0xd8, 0xae, 0x5b, 0x02, 0xe7, 0x31, 0x7e, - 0x67, 0x8d, 0x40, 0xbd, 0x0f, 0x85, 0x6e, 0xe5, 0xb5, 0x9b, 0x76, 0xfb, 0x6f, 0x0e, 0xf7, 0x2d, 0x71, 0x9a, 0xf7, - 0x76, 0xe0, 0x75, 0x8f, 0x6c, 0x61, 0x91, 0x52, 0x10, 0x8a, 0x94, 0x02, 0x4b, 0x64, 0xc3, 0x84, 0xf6, 0x8e, 0x58, - 0xa6, 0x6d, 0xb1, 0x74, 0x24, 0x3c, 0x78, 0x33, 0xb0, 0x15, 0x62, 0xfc, 0x8a, 0xd1, 0x0e, 0x76, 0x6b, 0xe1, 0x4e, - 0xc3, 0x11, 0x10, 0x3e, 0x3e, 0xa5, 0x20, 0xf0, 0xd4, 0x96, 0xfe, 0x3e, 0x10, 0xeb, 0x4c, 0x65, 0x62, 0xc8, 0xa1, - 0x74, 0x5e, 0xde, 0x6a, 0xeb, 0x62, 0x71, 0x32, 0x03, 0x3e, 0xa4, 0x92, 0x29, 0xde, 0xcb, 0x0e, 0x7b, 0x34, 0x15, - 0x66, 0x01, 0xae, 0x3a, 0x21, 0xa7, 0x9d, 0xfe, 0x5e, 0x24, 0xf5, 0x1d, 0x3c, 0xbb, 0x05, 0x1c, 0x5e, 0x10, 0x73, - 0xa8, 0x54, 0xf8, 0x71, 0xb6, 0x73, 0xce, 0x4e, 0x5a, 0xd1, 0x3c, 0xae, 0x7c, 0x7f, 0x28, 0xfd, 0xfa, 0x7b, 0x4a, - 0x10, 0xca, 0x84, 0x33, 0xf9, 0x18, 0x19, 0x89, 0x07, 0x88, 0x38, 0x22, 0xd0, 0x52, 0x3a, 0x16, 0x49, 0x69, 0x04, - 0xe4, 0x03, 0xac, 0x44, 0xbf, 0xca, 0x01, 0x29, 0x25, 0x41, 0x69, 0xf7, 0xff, 0xf6, 0xbf, 0xfe, 0xb7, 0xf4, 0x29, - 0x02, 0x5a, 0x01, 0x2c, 0xcc, 0xdc, 0xa8, 0x62, 0x67, 0xec, 0x02, 0xac, 0xd0, 0x78, 0xdc, 0x9a, 0x46, 0xc9, 0x04, - 0x20, 0x28, 0x98, 0xb8, 0xbb, 0x21, 0xeb, 0x81, 0x0a, 0x24, 0x58, 0x66, 0xd8, 0x59, 0x82, 0x57, 0x2f, 0xc2, 0x1d, - 0xfb, 0x43, 0x19, 0x7c, 0x2a, 0xb7, 0x94, 0x08, 0xda, 0xc8, 0xe7, 0x33, 0x68, 0xae, 0x96, 0xd3, 0xa7, 0x7e, 0x23, - 0x8c, 0x64, 0x1e, 0xac, 0x96, 0xd0, 0x07, 0x2d, 0x75, 0xa0, 0xe0, 0xdf, 0xfe, 0xf5, 0x3f, 0xff, 0x77, 0xf5, 0x8a, - 0xfe, 0xff, 0xbf, 0xfd, 0xcb, 0x3f, 0xfd, 0xdf, 0xff, 0xf3, 0x5f, 0x30, 0x39, 0x52, 0xc6, 0x08, 0xe8, 0x28, 0x59, - 0x55, 0x80, 0x40, 0x9c, 0xa9, 0x7a, 0xb6, 0xdf, 0x01, 0xcd, 0x42, 0x04, 0x29, 0x41, 0x22, 0x62, 0xa6, 0x24, 0x50, - 0x42, 0xd5, 0x0d, 0x38, 0x83, 0xfd, 0xb3, 0x28, 0x4a, 0x6d, 0x3f, 0x68, 0xdb, 0xd5, 0x9e, 0xf6, 0x8d, 0xbe, 0x3b, - 0xb8, 0x1b, 0x77, 0xca, 0x14, 0xf1, 0xf5, 0x5e, 0x2d, 0x95, 0xe3, 0x0a, 0x4b, 0xca, 0xaa, 0xdc, 0x42, 0x8f, 0xf2, - 0x12, 0x5f, 0x83, 0xae, 0x51, 0x4c, 0x5b, 0x5b, 0xeb, 0xd3, 0xfb, 0x65, 0x51, 0xf0, 0x78, 0x82, 0xfb, 0x21, 0xdc, - 0x63, 0x14, 0x0a, 0x6c, 0xa1, 0x4a, 0x92, 0x5c, 0x96, 0x34, 0x8a, 0x30, 0x61, 0xee, 0x3f, 0xfe, 0x87, 0xf2, 0x2f, - 0x33, 0x54, 0x05, 0x2c, 0x67, 0x16, 0x5d, 0x48, 0xc3, 0xe6, 0x61, 0xbb, 0x3d, 0xbf, 0x70, 0x97, 0xd5, 0x0c, 0xde, - 0x75, 0x93, 0x91, 0x4b, 0xcd, 0x1c, 0x90, 0x62, 0x88, 0xda, 0x7b, 0x07, 0xba, 0x7c, 0x1b, 0x9d, 0x3d, 0x65, 0xf9, - 0xf9, 0x92, 0x1c, 0x48, 0xf1, 0x6f, 0x18, 0xeb, 0x93, 0xbe, 0x36, 0x28, 0x31, 0x56, 0xb1, 0x34, 0x7a, 0x75, 0x45, - 0xaf, 0x69, 0x67, 0x35, 0xd3, 0xc4, 0x8c, 0x55, 0x9a, 0x51, 0x46, 0xcc, 0xc3, 0x80, 0x0e, 0xde, 0xb4, 0xbb, 0xd4, - 0xc3, 0x73, 0x9e, 0xcd, 0xcc, 0xe0, 0x24, 0x8b, 0xd8, 0x88, 0x4d, 0x94, 0x8f, 0x52, 0xd6, 0x8b, 0xc0, 0x63, 0xf9, - 0x19, 0x9e, 0x31, 0xc0, 0x6d, 0x16, 0xf1, 0x80, 0x28, 0xb5, 0x67, 0x86, 0x2f, 0x23, 0x0c, 0x0c, 0x67, 0x4b, 0x63, - 0xae, 0x9e, 0x68, 0x8a, 0x9e, 0xc0, 0x7a, 0x7e, 0x4a, 0xe9, 0x53, 0x77, 0x73, 0x28, 0xe1, 0x48, 0x78, 0x51, 0x65, - 0x87, 0x54, 0x26, 0xf6, 0xbb, 0x9a, 0x39, 0x2e, 0x99, 0x31, 0x18, 0xc1, 0xb7, 0x37, 0x16, 0x52, 0x52, 0x34, 0xfd, - 0x15, 0x94, 0x1f, 0x5a, 0x80, 0xdd, 0x6c, 0x45, 0x85, 0xd8, 0xea, 0x5d, 0xf8, 0x42, 0xab, 0xe2, 0xd1, 0x7c, 0x4e, - 0x0d, 0x5d, 0xa0, 0x53, 0x52, 0xa9, 0x91, 0x71, 0x50, 0x2c, 0x5c, 0x84, 0x9e, 0x65, 0x1b, 0x49, 0xd0, 0xe2, 0x49, - 0x06, 0xa5, 0xe9, 0xf7, 0x0d, 0xff, 0x3f, 0xdf, 0x8d, 0x21, 0x2b, 0x85, 0x78, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xc9, 0x76, 0xdb, 0xc8, 0xb2, 0xe0, 0xba, + 0xcf, 0xe9, 0x3f, 0xe8, 0x0d, 0x84, 0xd2, 0x93, 0x81, 0x12, 0x08, 0x91, 0x94, 0x65, 0xbb, 0x40, 0x41, 0xbc, 0xf2, + 0x50, 0xd7, 0xae, 0xf2, 0x54, 0x96, 0xec, 0x1a, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, 0x92, + 0x1a, 0x8a, 0xc2, 0x3b, 0xbd, 0xea, 0x55, 0x9f, 0xd3, 0xe3, 0xe2, 0x6d, 0xfa, 0xbc, 0x5e, 0xf4, 0x47, 0xf4, 0xba, + 0x3f, 0xe5, 0xfe, 0x40, 0xbf, 0x4f, 0xe8, 0x88, 0xc8, 0x01, 0x09, 0x90, 0x1a, 0x5c, 0xaf, 0xfa, 0x1e, 0x0f, 0x02, + 0x72, 0x8c, 0x88, 0x8c, 0x8c, 0x29, 0x23, 0xa1, 0xdd, 0xb5, 0x51, 0x36, 0xe4, 0x97, 0x33, 0x66, 0x4d, 0xf8, 0x34, + 0xd9, 0xdb, 0x95, 0xff, 0xb3, 0x68, 0xb4, 0xb7, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x0f, 0xb3, 0xd4, + 0x9a, 0xe4, 0x6c, 0x1c, 0x8e, 0x22, 0x1e, 0x05, 0xf1, 0x34, 0x3a, 0x65, 0xd6, 0xd6, 0xde, 0xee, 0x94, 0xf1, 0xc8, + 0x1a, 0x4e, 0xa2, 0xbc, 0x60, 0x3c, 0x7c, 0x7f, 0xf8, 0x75, 0xeb, 0xd1, 0xde, 0x6e, 0x31, 0xcc, 0xe3, 0x19, 0xb7, + 0x70, 0xc8, 0x70, 0x9a, 0x8d, 0xe6, 0x09, 0xdb, 0x3b, 0x8b, 0x72, 0xeb, 0x82, 0x85, 0x6f, 0x4e, 0x7e, 0x65, 0x43, + 0xee, 0x8f, 0xd8, 0x38, 0x4e, 0xd9, 0xdb, 0x3c, 0x9b, 0xb1, 0x9c, 0x5f, 0x7a, 0xcf, 0x56, 0x57, 0xc4, 0xac, 0xf0, + 0x0e, 0x74, 0xd5, 0x29, 0xe3, 0x6f, 0xce, 0x53, 0xd5, 0xe7, 0x29, 0x13, 0x93, 0x64, 0x79, 0xe1, 0xf1, 0x6b, 0xda, + 0x1c, 0x5c, 0x4e, 0x4f, 0xb2, 0xa4, 0xf0, 0x3e, 0xe9, 0xfa, 0x59, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0x27, 0x51, 0x61, + 0xb4, 0xf4, 0x9e, 0xac, 0x68, 0x32, 0x93, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x7c, 0xca, 0xf2, 0xe8, 0x24, 0x61, 0x5e, + 0xc1, 0x42, 0x87, 0x79, 0xdc, 0x8b, 0xdd, 0x70, 0x8f, 0x5b, 0x71, 0x6a, 0xb1, 0xfe, 0x05, 0xa3, 0x92, 0x05, 0xd3, + 0xad, 0x82, 0xb5, 0xb6, 0x07, 0xe4, 0x1a, 0xc7, 0xa7, 0x73, 0xfd, 0x7e, 0x9e, 0xc7, 0x5c, 0x3d, 0x9f, 0x45, 0xc9, + 0x9c, 0x05, 0x71, 0xe9, 0x06, 0xec, 0x88, 0x0f, 0xc2, 0xd8, 0x7b, 0x42, 0x83, 0xc2, 0x90, 0x8b, 0x71, 0x96, 0x3b, + 0x48, 0xab, 0x18, 0xc7, 0xe6, 0x57, 0x57, 0x0e, 0x0f, 0x17, 0xa5, 0xeb, 0x7e, 0x62, 0xfe, 0x30, 0x4a, 0x12, 0x07, + 0x27, 0xde, 0xd8, 0x28, 0x70, 0xc6, 0xd8, 0xe3, 0x47, 0xf1, 0xc0, 0xed, 0xc5, 0x63, 0x87, 0x33, 0xb7, 0xea, 0x97, + 0x8d, 0x2d, 0xce, 0x1c, 0xee, 0xba, 0x4f, 0xae, 0xef, 0x93, 0x33, 0x3e, 0xcf, 0x01, 0xf6, 0xd2, 0x7b, 0xa3, 0x66, + 0x7e, 0x86, 0xf5, 0x07, 0xd4, 0xb1, 0x07, 0xb0, 0x17, 0xdc, 0xfa, 0x10, 0x9e, 0xc7, 0xe9, 0x28, 0x3b, 0xf7, 0x0f, + 0x26, 0x11, 0xfc, 0x78, 0x97, 0x65, 0x7c, 0x63, 0xc3, 0x39, 0xcb, 0xe2, 0x91, 0xd5, 0x0e, 0x43, 0xb3, 0xf2, 0xf2, + 0xc9, 0xc1, 0xc1, 0xd5, 0x55, 0xa3, 0xc0, 0x4f, 0x23, 0x1e, 0x9f, 0x31, 0xd1, 0x19, 0x00, 0xb0, 0xe1, 0xe7, 0x8c, + 0xb3, 0xd1, 0x01, 0xbf, 0x4c, 0xa0, 0x94, 0x31, 0x5e, 0xd8, 0x80, 0xe3, 0xd3, 0x6c, 0x08, 0x64, 0x4b, 0x0d, 0xc2, + 0x43, 0xd3, 0x9c, 0xcd, 0x92, 0x68, 0xc8, 0xb0, 0x1e, 0x46, 0xaa, 0x7a, 0x54, 0x8d, 0xbc, 0xaf, 0x43, 0xb1, 0xbc, + 0x8e, 0xeb, 0xc5, 0x2c, 0x4c, 0xd9, 0xb9, 0xf5, 0x2a, 0x9a, 0xf5, 0x86, 0x49, 0x54, 0x14, 0xc0, 0xaf, 0x0b, 0x42, + 0x21, 0x9f, 0x0f, 0x81, 0x41, 0x08, 0xc1, 0x05, 0x92, 0x69, 0x12, 0x17, 0xfe, 0xc7, 0xf5, 0x61, 0x51, 0xbc, 0x63, + 0xc5, 0x3c, 0xe1, 0xeb, 0x21, 0xac, 0x05, 0x5f, 0x0b, 0xc3, 0xaf, 0x5d, 0x3e, 0xc9, 0xb3, 0x73, 0xeb, 0x59, 0x9e, + 0x43, 0x73, 0x1b, 0xa6, 0x14, 0x0d, 0xac, 0xb8, 0xb0, 0xd2, 0x8c, 0x5b, 0x7a, 0x30, 0x5c, 0x40, 0xdf, 0x7a, 0x5f, + 0x30, 0xeb, 0x78, 0x9e, 0x16, 0xd1, 0x98, 0x41, 0xd3, 0x63, 0x2b, 0xcb, 0xad, 0x63, 0x18, 0xf4, 0x18, 0x96, 0xac, + 0xe0, 0xb0, 0x6b, 0x7c, 0xdb, 0xed, 0xd1, 0x5c, 0x50, 0x78, 0xc8, 0x2e, 0x78, 0xc8, 0x4a, 0x60, 0x4c, 0xab, 0xd0, + 0x68, 0x38, 0xee, 0x22, 0x81, 0x02, 0x16, 0xc6, 0x0c, 0x59, 0xd6, 0x31, 0x1b, 0xeb, 0xc5, 0xf9, 0xb0, 0xb1, 0xa1, + 0x69, 0x0d, 0x34, 0x71, 0xa0, 0x6d, 0xd1, 0x68, 0xeb, 0x09, 0xc4, 0x6b, 0x24, 0x72, 0x3d, 0xe6, 0x4b, 0xf2, 0x1d, + 0x5c, 0xa6, 0xc3, 0xfa, 0xd8, 0x50, 0x59, 0xf2, 0xec, 0x80, 0xe7, 0x71, 0x7a, 0x0a, 0x40, 0xc8, 0x99, 0xcc, 0x26, + 0x65, 0x29, 0x16, 0xff, 0x0d, 0x0b, 0x59, 0xb8, 0x87, 0xa3, 0xe7, 0xcc, 0xb1, 0x0b, 0xea, 0x61, 0x87, 0x21, 0x92, + 0x1e, 0x18, 0x8c, 0xf5, 0x59, 0xc0, 0x36, 0x6d, 0xdb, 0xfb, 0xda, 0xf5, 0x2e, 0x91, 0x83, 0x7c, 0xdf, 0x27, 0xf6, + 0x15, 0x9d, 0xe3, 0xb0, 0x83, 0x40, 0xfb, 0x09, 0x4b, 0x4f, 0xf9, 0xa4, 0xcf, 0x8e, 0xda, 0x83, 0x80, 0x03, 0x54, + 0xa3, 0xf9, 0x90, 0x39, 0xc8, 0x8f, 0x5e, 0x8e, 0xdb, 0x67, 0xd3, 0x81, 0x29, 0x70, 0x61, 0xd6, 0x08, 0xc7, 0xda, + 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x13, 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, 0x47, 0xf9, 0x66, 0x67, 0xe0, 0x21, 0x94, 0x9a, 0xf8, 0x12, + 0xf1, 0x18, 0x10, 0x2c, 0xbd, 0xb7, 0x4c, 0x6f, 0xcf, 0x0f, 0x7d, 0xe6, 0x2f, 0xf3, 0x71, 0xc8, 0xfd, 0x69, 0x34, + 0x43, 0x6c, 0x18, 0xf1, 0x40, 0x94, 0x0e, 0x11, 0xba, 0xda, 0xba, 0x20, 0xc5, 0xfc, 0x8a, 0x05, 0x5c, 0x20, 0x08, + 0xec, 0xd9, 0x67, 0xd1, 0x70, 0x02, 0x5b, 0xbc, 0x22, 0xdc, 0x48, 0x6d, 0x87, 0x61, 0xce, 0x22, 0xce, 0x9e, 0x25, + 0x0c, 0xdf, 0x70, 0x05, 0xa0, 0xa7, 0xed, 0x7a, 0xb9, 0xda, 0x77, 0x49, 0xcc, 0x5f, 0x67, 0x30, 0x4f, 0x4f, 0x30, + 0x09, 0x70, 0x71, 0xbe, 0xb1, 0x11, 0x23, 0x8b, 0xec, 0x73, 0x58, 0xad, 0x93, 0x39, 0x08, 0x01, 0x3b, 0xc5, 0x16, + 0x36, 0x50, 0xdb, 0x8b, 0x7d, 0x0e, 0x44, 0x7c, 0x92, 0xa5, 0x1c, 0x86, 0x03, 0x78, 0x35, 0x07, 0xf9, 0xd1, 0x6c, + 0xc6, 0xd2, 0xd1, 0x93, 0x49, 0x9c, 0x8c, 0x80, 0x1a, 0x25, 0xe0, 0x9b, 0xb1, 0x10, 0xf0, 0x04, 0x64, 0x82, 0x9b, + 0x31, 0xa2, 0xe5, 0x43, 0x46, 0xe6, 0xa1, 0x6d, 0xf7, 0x50, 0x02, 0x49, 0x2c, 0x50, 0x06, 0xd1, 0xc2, 0xbd, 0x03, + 0xd1, 0x5f, 0xb8, 0x7c, 0x33, 0x8c, 0xf5, 0x32, 0x4a, 0x02, 0xbf, 0x41, 0x49, 0x03, 0xf4, 0x67, 0x20, 0x03, 0x7b, + 0x28, 0xb8, 0xbe, 0x97, 0x52, 0x27, 0x65, 0x0a, 0x43, 0x20, 0xc0, 0x10, 0x25, 0x88, 0xa4, 0xc1, 0xdb, 0x2c, 0xb9, + 0x1c, 0xc7, 0x49, 0x72, 0x30, 0x9f, 0xcd, 0xb2, 0x9c, 0x7b, 0xdf, 0x84, 0x0b, 0x9e, 0x55, 0xb8, 0xd2, 0x26, 0x2f, + 0xce, 0x63, 0x8e, 0x04, 0x75, 0x17, 0xc3, 0x08, 0x96, 0xfa, 0x71, 0x96, 0x25, 0x2c, 0x4a, 0x01, 0x0d, 0xd6, 0xb7, + 0xed, 0x20, 0x9d, 0x27, 0x49, 0xef, 0x04, 0x86, 0xfd, 0xd4, 0xa3, 0x6a, 0x21, 0xf1, 0x03, 0x7a, 0xde, 0xcf, 0xf3, + 0xe8, 0x12, 0x1a, 0x62, 0x1b, 0xe0, 0x45, 0x58, 0xad, 0x6f, 0x0e, 0xde, 0xbc, 0xf6, 0x05, 0xe3, 0xc7, 0xe3, 0x4b, + 0x00, 0xb4, 0xac, 0xa4, 0xe6, 0x38, 0xcf, 0xa6, 0x8d, 0xa9, 0x91, 0x0e, 0x71, 0xc8, 0x7a, 0xd7, 0x80, 0x10, 0xd3, + 0xc8, 0xb0, 0x4a, 0xcc, 0x84, 0xe0, 0x35, 0xf1, 0xb3, 0xac, 0xc4, 0x3d, 0xd0, 0xc7, 0x87, 0x40, 0x14, 0xc3, 0x94, + 0x37, 0x43, 0xcb, 0xf3, 0xcb, 0x45, 0x1c, 0x12, 0x9c, 0x33, 0xd4, 0xbf, 0x08, 0xe3, 0x30, 0x82, 0xd9, 0x17, 0x62, + 0xc0, 0x52, 0x41, 0x1c, 0x97, 0xa5, 0x97, 0x68, 0x26, 0x46, 0x89, 0x87, 0x02, 0x85, 0xc3, 0x36, 0xba, 0xba, 0x62, + 0xf0, 0xe2, 0x7a, 0xdf, 0x86, 0x8b, 0x48, 0xe1, 0x83, 0x1a, 0x0a, 0xf7, 0x57, 0x20, 0xe4, 0x04, 0x6a, 0xb2, 0x33, + 0xd0, 0x83, 0x00, 0xe7, 0x37, 0x1e, 0xe8, 0xff, 0x04, 0xa1, 0x58, 0xeb, 0x78, 0xa0, 0x41, 0x9f, 0x4c, 0xa2, 0xf4, + 0x94, 0x8d, 0x82, 0x84, 0x95, 0x52, 0xf2, 0xee, 0x5b, 0xb0, 0xc6, 0xc0, 0x4e, 0x85, 0xf5, 0xfc, 0xf0, 0xd5, 0x4b, + 0xb9, 0x72, 0x35, 0x61, 0x0c, 0x8b, 0x34, 0x07, 0xb5, 0x0a, 0x62, 0x5b, 0x8a, 0xe3, 0x67, 0x5c, 0x49, 0x6f, 0x51, + 0x12, 0x17, 0xef, 0x67, 0x60, 0x62, 0xb0, 0xb7, 0x30, 0x0c, 0x4c, 0x1f, 0xc2, 0x54, 0x54, 0x0e, 0xf3, 0x89, 0x8a, + 0x91, 0x2e, 0x82, 0xce, 0x02, 0x53, 0xf1, 0x9a, 0x39, 0x6e, 0x09, 0xac, 0xca, 0xe3, 0xa1, 0x15, 0x8d, 0x46, 0x2f, + 0xd2, 0x98, 0xc7, 0x51, 0x12, 0xff, 0x4e, 0x94, 0x5c, 0x20, 0x8f, 0xf1, 0x9e, 0x5c, 0x04, 0xc0, 0x9d, 0x7a, 0x24, + 0xae, 0x12, 0xb2, 0x6b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x68, 0x20, 0xc1, 0x4b, 0xfc, 0xd9, 0xbc, 0x98, 0x20, + 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0x93, 0x82, 0xe5, 0x67, 0x6c, 0xa4, 0x39, 0xa0, 0x00, 0xac, 0xa8, 0x39, 0x18, + 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, 0xa5, 0x80, + 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x01, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, 0x93, 0x3d, + 0x9b, 0x93, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, 0x0a, 0x29, + 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, 0xc2, 0x4a, + 0xbc, 0x2b, 0x44, 0xbc, 0x6b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x35, 0xee, 0xa7, 0xd9, 0xfe, 0x70, 0xc8, 0x8a, + 0x22, 0x03, 0xd8, 0xd6, 0xa8, 0xfd, 0x75, 0x86, 0x16, 0x50, 0xd2, 0xd5, 0xb2, 0xce, 0x2e, 0x48, 0x83, 0x9b, 0x6a, + 0x45, 0xe9, 0xf4, 0xc0, 0xfe, 0xf8, 0x11, 0x64, 0xb6, 0x27, 0xc9, 0x00, 0x54, 0x5f, 0x36, 0xfc, 0x84, 0x3d, 0x53, + 0xa7, 0xcc, 0x4a, 0xfb, 0xd2, 0xa9, 0x83, 0xe4, 0xc1, 0xb0, 0x6e, 0x69, 0x2c, 0xe8, 0xca, 0xa1, 0x71, 0x35, 0xa4, + 0x82, 0x5c, 0x9c, 0x92, 0xca, 0x36, 0x96, 0x11, 0xac, 0xb6, 0xd2, 0x23, 0xd2, 0x2b, 0x6c, 0x0a, 0x02, 0xf4, 0x88, + 0x0d, 0x7a, 0xb2, 0x3e, 0xcc, 0x05, 0xe5, 0x72, 0xf6, 0xdb, 0x9c, 0x15, 0x5c, 0xb0, 0x2e, 0x8c, 0x5b, 0xc0, 0xb8, + 0xe5, 0x92, 0x75, 0x58, 0xb3, 0x1d, 0x57, 0xc1, 0xf6, 0x66, 0x86, 0x7a, 0xac, 0x40, 0x4e, 0xbe, 0x99, 0x9d, 0x10, + 0x56, 0xe6, 0x5e, 0x5d, 0x7d, 0xab, 0x06, 0xa9, 0x96, 0x52, 0xdb, 0x40, 0x8d, 0x35, 0xb1, 0x55, 0x93, 0x91, 0xed, + 0x4a, 0x85, 0xba, 0xd6, 0xe9, 0xd5, 0xf8, 0x00, 0xf6, 0x5c, 0x5b, 0xb3, 0x74, 0x65, 0x6c, 0xbf, 0x55, 0x34, 0x7d, + 0x23, 0x46, 0x26, 0x6b, 0x94, 0xdd, 0xce, 0x3d, 0x6a, 0xc7, 0x43, 0xdb, 0xa5, 0xba, 0x4a, 0x30, 0xcc, 0xeb, 0x82, + 0xa1, 0x09, 0xf5, 0x4c, 0x77, 0xb1, 0x35, 0x53, 0xb1, 0x50, 0xad, 0xb5, 0x72, 0x20, 0x78, 0x78, 0x04, 0xc6, 0xc9, + 0x4a, 0xff, 0xe0, 0x75, 0x34, 0x65, 0x48, 0x51, 0xef, 0xba, 0x06, 0xd2, 0x81, 0x80, 0x26, 0x83, 0xa6, 0x7a, 0xe3, + 0xae, 0xb0, 0x9a, 0xea, 0xfb, 0x2b, 0x06, 0x2b, 0x02, 0xec, 0xeb, 0x72, 0xc5, 0x12, 0x91, 0xde, 0x14, 0x5c, 0xa2, + 0xe9, 0x23, 0xca, 0xc4, 0x9a, 0x90, 0x82, 0x07, 0xe4, 0x61, 0xf9, 0x1b, 0x0b, 0x27, 0x5b, 0x31, 0x85, 0x23, 0x47, + 0x99, 0x02, 0x74, 0x26, 0x25, 0x00, 0xe2, 0x92, 0x7e, 0xd6, 0x36, 0x16, 0x92, 0xed, 0x00, 0xf9, 0xc0, 0x1f, 0x27, + 0x11, 0x77, 0x3a, 0x5b, 0x6d, 0x17, 0xf8, 0x10, 0x84, 0x38, 0xe8, 0x08, 0x30, 0xef, 0x2b, 0x54, 0x18, 0xa2, 0x12, + 0xbb, 0xdc, 0x07, 0xa3, 0x68, 0x12, 0x8f, 0xb9, 0x93, 0xa1, 0x12, 0x71, 0x4b, 0x96, 0x80, 0x92, 0xd1, 0xfb, 0x0a, + 0xa4, 0x04, 0x17, 0xd2, 0x45, 0x54, 0x6b, 0x81, 0xa6, 0x20, 0x25, 0x29, 0x45, 0x5a, 0x50, 0x41, 0x60, 0x08, 0x95, + 0x9e, 0xe2, 0x28, 0xd0, 0x6f, 0x71, 0x5f, 0x0c, 0x1a, 0x2c, 0x59, 0x94, 0x71, 0x3f, 0x5e, 0x2e, 0x04, 0x35, 0xec, + 0xf3, 0xec, 0x65, 0x76, 0xce, 0xf2, 0x27, 0x11, 0xc2, 0x1e, 0x88, 0xee, 0x25, 0x48, 0x7a, 0x12, 0xe8, 0xac, 0xa7, + 0x78, 0xe5, 0x8c, 0x90, 0x86, 0x85, 0x98, 0xc6, 0xa8, 0x08, 0x41, 0xcb, 0x11, 0xed, 0x53, 0xdc, 0x52, 0xb4, 0xf7, + 0x50, 0x95, 0x30, 0xcd, 0x5b, 0xfb, 0x2f, 0xeb, 0xbc, 0x05, 0x23, 0xcc, 0x14, 0xb7, 0xd6, 0x77, 0xac, 0xeb, 0x49, + 0xdd, 0xec, 0x48, 0xde, 0x32, 0x94, 0x19, 0xe8, 0x8f, 0xab, 0xab, 0xca, 0x48, 0x07, 0x65, 0xaa, 0xa5, 0x39, 0x5a, + 0x4e, 0x62, 0x4b, 0xb8, 0x25, 0x28, 0x23, 0x34, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x5d, 0xe4, 0xc5, 0x3d, 0xa7, 0xa1, + 0x8e, 0x00, 0x8a, 0x69, 0x4d, 0x23, 0xf5, 0x79, 0xa0, 0x2b, 0x50, 0x29, 0x29, 0x6d, 0xe4, 0x55, 0x4d, 0x04, 0xc4, + 0xe9, 0x88, 0xe5, 0xc2, 0x41, 0x93, 0x3a, 0x14, 0x26, 0x4c, 0x81, 0xa1, 0xd9, 0x08, 0x24, 0xbc, 0x42, 0x00, 0xcc, + 0x13, 0x7f, 0x92, 0x15, 0x5c, 0xd7, 0x99, 0xd0, 0xc7, 0x57, 0x57, 0xb1, 0xf0, 0x17, 0x91, 0x01, 0x72, 0x36, 0xcd, + 0xce, 0xd8, 0x0a, 0xa8, 0x7b, 0x6a, 0x30, 0x13, 0x64, 0x63, 0x18, 0x50, 0xa2, 0xa0, 0x5a, 0x66, 0x49, 0x3c, 0x64, + 0x5a, 0x4b, 0x4d, 0x7d, 0x30, 0xe8, 0xd8, 0x05, 0xc8, 0x08, 0xe6, 0xee, 0xed, 0xed, 0xb5, 0xbd, 0x8e, 0x5b, 0x0a, + 0x82, 0x2f, 0x96, 0x28, 0x7a, 0x83, 0x7e, 0x94, 0x26, 0xf8, 0x2a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, 0xe1, 0x27, + 0x79, 0x52, 0x10, 0xbb, 0xde, 0x08, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, 0xc9, 0xa6, + 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, 0x03, 0xaf, + 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x96, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, 0x5c, 0x45, + 0x82, 0x03, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x9c, 0x44, 0xc3, 0x4f, 0x1a, 0xdc, 0x8a, + 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, 0xc8, 0x96, + 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0x37, 0xb0, 0x1c, 0x57, 0xd2, 0x80, + 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe2, 0xe2, 0x7a, 0x12, 0xfc, 0xa9, 0x60, 0x3e, 0x35, + 0x66, 0xba, 0x15, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, 0xa1, 0x42, + 0x1c, 0x7f, 0x2a, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0x2d, 0x8b, 0x16, 0x29, 0x53, 0x63, + 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, 0x30, 0x29, + 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, 0x99, 0xbc, + 0x5d, 0xf4, 0x8b, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, 0x92, 0x4d, + 0xd2, 0xbe, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, 0xdd, 0x86, + 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0x6e, 0xa2, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, 0x85, 0xb1, + 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x2b, 0xec, 0x29, 0x00, 0x25, 0x81, + 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x35, 0xa2, 0x63, 0x25, 0x42, 0x6b, 0x22, 0x5f, 0xeb, 0xb3, 0xd8, + 0xaf, 0xb9, 0x84, 0x26, 0x25, 0xf3, 0x7e, 0x1e, 0xd8, 0x2a, 0x10, 0x51, 0xe9, 0xb6, 0xa4, 0x9f, 0x90, 0x43, 0xba, + 0x4c, 0xf4, 0xda, 0x48, 0x06, 0xad, 0x53, 0x21, 0xd1, 0xd2, 0x41, 0x18, 0x39, 0xe8, 0xb8, 0xd3, 0x5a, 0x2c, 0x11, + 0xb2, 0x69, 0x6f, 0x12, 0x2b, 0xa2, 0x73, 0x9a, 0xa3, 0x09, 0x67, 0xea, 0x74, 0xc7, 0x01, 0x74, 0x40, 0xec, 0x2f, + 0xb1, 0xde, 0x4a, 0xb3, 0xd3, 0xf5, 0x2b, 0x87, 0xef, 0xea, 0x2a, 0x41, 0x7e, 0x10, 0x06, 0x2f, 0xac, 0x59, 0x5f, + 0xc9, 0xde, 0xfd, 0x97, 0xd8, 0x8a, 0xec, 0xcf, 0xaa, 0xa4, 0xf2, 0x14, 0x6a, 0x9c, 0x5b, 0x5f, 0x27, 0x66, 0x86, + 0x16, 0x55, 0xc5, 0x81, 0x21, 0xd5, 0x0f, 0x94, 0xc2, 0xae, 0x50, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x39, + 0x39, 0x2f, 0x6b, 0xab, 0x5c, 0xc8, 0x8d, 0x0d, 0xd3, 0x67, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x39, 0x17, 0x97, + 0x29, 0x59, 0x30, 0x4f, 0x94, 0x3a, 0x5a, 0x72, 0x40, 0xdb, 0x3d, 0xf4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, + 0x45, 0x78, 0xda, 0x28, 0x9f, 0xa4, 0xd1, 0x01, 0x78, 0xa1, 0x09, 0x49, 0x4e, 0xb8, 0x69, 0x8b, 0x16, 0xc3, 0x09, + 0xc3, 0x10, 0xb8, 0xb2, 0x27, 0x4c, 0xd9, 0xb3, 0x86, 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, 0x1e, 0x27, 0x49, 0x55, 0xfa, 0xa7, 0x0a, 0x78, 0x31, 0xec, 0x6d, + 0xa2, 0x5d, 0x60, 0x34, 0x57, 0x20, 0xb8, 0xda, 0x08, 0x7b, 0xef, 0xb8, 0xd5, 0xba, 0x8b, 0x88, 0x23, 0x37, 0xa3, + 0x11, 0x50, 0x8f, 0x11, 0x56, 0xcd, 0xda, 0xfb, 0xcf, 0x30, 0xa4, 0x66, 0xe0, 0x83, 0xea, 0x8c, 0x8a, 0x3f, 0xcb, + 0x9e, 0xfa, 0x4c, 0xf4, 0x6e, 0x55, 0x5d, 0xcd, 0x80, 0x8a, 0x0a, 0x7c, 0x98, 0x21, 0x96, 0xb6, 0x0a, 0x04, 0xe4, + 0x7a, 0x58, 0x94, 0x02, 0x26, 0x69, 0xb0, 0xa0, 0x14, 0x58, 0x6b, 0x65, 0xf7, 0xf2, 0xb6, 0x60, 0x0e, 0x85, 0xc2, + 0x45, 0xff, 0x27, 0xd9, 0x74, 0x86, 0x96, 0x59, 0x83, 0xa9, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa2, 0xac, 0xd6, + 0x87, 0x76, 0x64, 0x8d, 0x9f, 0xb4, 0xa3, 0x0c, 0x0e, 0xd5, 0x5c, 0x17, 0xd5, 0xed, 0xe6, 0xa6, 0x88, 0x59, 0xc5, + 0xe3, 0x3e, 0xe9, 0x6d, 0x6d, 0x4d, 0x7a, 0x9a, 0x06, 0x24, 0x93, 0x24, 0xc3, 0x9b, 0x0c, 0x50, 0x56, 0xc4, 0x59, + 0x94, 0x0d, 0xf2, 0x2d, 0xca, 0x12, 0xd7, 0xef, 0xbb, 0xde, 0x7e, 0xcd, 0xb3, 0xf6, 0xf6, 0xaf, 0x77, 0x91, 0xab, + 0x3a, 0xe9, 0x41, 0x1e, 0x0d, 0xa0, 0x68, 0xc9, 0xa6, 0x0c, 0x17, 0xd3, 0x6c, 0xc4, 0x02, 0x1b, 0xba, 0xa7, 0x76, + 0xa9, 0xb4, 0x32, 0x6c, 0x8e, 0x94, 0x39, 0x8b, 0x77, 0xf5, 0x48, 0x6a, 0xb0, 0x0f, 0x2c, 0xa0, 0xcd, 0x85, 0xef, + 0xc3, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0xa1, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xcb, 0x48, 0x16, 0xab, 0xa1, 0xa4, + 0xbe, 0xef, 0x7f, 0x1f, 0xdc, 0xde, 0xa3, 0xf2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0x8a, 0x88, 0xbd, 0x34, + 0xa7, 0x03, 0x8d, 0x53, 0x00, 0x65, 0x0e, 0x41, 0x93, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0xfd, 0x97, 0xca, 0xa5, 0xce, + 0xc0, 0x85, 0x00, 0x27, 0x3f, 0x89, 0x79, 0x0b, 0xcf, 0x23, 0x6d, 0x6f, 0x21, 0x2a, 0x30, 0xae, 0x48, 0x71, 0xe9, + 0x52, 0x79, 0x83, 0xde, 0xc7, 0xf0, 0x18, 0x9a, 0xad, 0xaf, 0x2f, 0x9c, 0x57, 0x11, 0x9f, 0xf8, 0x79, 0x94, 0x8e, + 0xb2, 0xa9, 0xe3, 0x6e, 0xda, 0xb6, 0xeb, 0x17, 0xe4, 0x89, 0x7c, 0xe5, 0x96, 0xeb, 0xc7, 0xde, 0x84, 0x85, 0x76, + 0xdf, 0xde, 0xfc, 0xe8, 0xbd, 0x67, 0xe1, 0xf1, 0xee, 0xfa, 0x62, 0xc2, 0xca, 0xbd, 0x63, 0xef, 0x42, 0xc7, 0xdc, + 0xbd, 0xb7, 0x28, 0x65, 0xa0, 0x57, 0xb8, 0x77, 0x21, 0xc1, 0x00, 0x76, 0xa3, 0xf8, 0x3b, 0x48, 0xb9, 0xf7, 0x74, + 0x20, 0x22, 0xe3, 0xb4, 0x57, 0x57, 0x76, 0x46, 0x11, 0x03, 0x7b, 0x4d, 0x3b, 0xab, 0x1b, 0x1b, 0x95, 0x9a, 0xaf, + 0x4a, 0xbd, 0x21, 0x0b, 0x6b, 0x9e, 0xba, 0x77, 0x48, 0x47, 0x2b, 0xf5, 0x8d, 0x3c, 0x64, 0xa4, 0x34, 0x97, 0xed, + 0x04, 0xc7, 0xd8, 0xe2, 0xab, 0xb7, 0xf5, 0x91, 0x88, 0x52, 0xf8, 0x31, 0x58, 0x2f, 0x11, 0xa8, 0x6f, 0x70, 0x70, + 0xbc, 0xc3, 0x70, 0x6b, 0xd7, 0xe9, 0x07, 0xce, 0x5a, 0xab, 0x75, 0xf5, 0xf3, 0xd6, 0xd1, 0x2f, 0x51, 0xeb, 0xf7, + 0xfd, 0xd6, 0x4f, 0x03, 0xf7, 0xca, 0xf9, 0x79, 0xab, 0x7f, 0x24, 0xdf, 0x8e, 0x7e, 0xd9, 0xfb, 0xb9, 0x18, 0x7c, + 0x29, 0x0a, 0xd7, 0x5d, 0x77, 0xeb, 0x14, 0x3c, 0xa5, 0x70, 0xab, 0xd5, 0xda, 0x83, 0xa7, 0x39, 0x3c, 0xe1, 0xcf, + 0x73, 0xf8, 0x71, 0x75, 0x64, 0xfd, 0x9b, 0x9f, 0xd3, 0x7f, 0xfb, 0x73, 0x3e, 0xc0, 0x31, 0x8f, 0x7e, 0xf9, 0xb9, + 0xb0, 0xef, 0xed, 0x85, 0x5b, 0x83, 0x4d, 0xd7, 0xd1, 0x35, 0x5f, 0x86, 0xd5, 0x23, 0xb4, 0x3a, 0xfa, 0x45, 0xbe, + 0xd9, 0xf7, 0x8e, 0x77, 0xf7, 0xc2, 0xc1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x1d, 0xe7, 0x99, + 0xc1, 0xe8, 0xf7, 0xe0, 0xe7, 0x29, 0xfc, 0xb4, 0xe1, 0xe7, 0x19, 0xfc, 0xfc, 0x05, 0xba, 0x89, 0xf8, 0xdb, 0x15, + 0xc5, 0x42, 0xae, 0xf0, 0xc0, 0x22, 0x82, 0x55, 0x70, 0xd7, 0xb7, 0x62, 0x6f, 0x4c, 0x44, 0x83, 0x7d, 0xe8, 0xfb, + 0x3e, 0x86, 0x49, 0x9d, 0xc5, 0xc7, 0x75, 0x58, 0x74, 0xe4, 0x9c, 0xf5, 0x80, 0x79, 0x22, 0x72, 0x50, 0x04, 0x5c, + 0x9c, 0xad, 0x16, 0x78, 0xb8, 0xea, 0x8d, 0xc2, 0x31, 0x73, 0xc0, 0x28, 0x78, 0xce, 0xf0, 0xa1, 0xeb, 0x7a, 0xcf, + 0xe4, 0x99, 0x21, 0xee, 0x73, 0xc1, 0x5a, 0x69, 0x26, 0x4c, 0x1a, 0xdb, 0xf5, 0x66, 0x2b, 0x2a, 0x61, 0x5b, 0xa7, + 0xa7, 0x50, 0xb7, 0x2e, 0x0e, 0xda, 0xbe, 0x67, 0xd1, 0x27, 0xdc, 0x92, 0xaf, 0x8d, 0x43, 0xe0, 0x25, 0x4b, 0xbe, + 0x69, 0x34, 0x1a, 0x36, 0xa2, 0x70, 0xc7, 0x1e, 0x33, 0x98, 0x61, 0xc9, 0x44, 0xe4, 0xa4, 0x34, 0x85, 0x65, 0x0b, + 0x93, 0xbf, 0x8d, 0x72, 0xbe, 0x5e, 0x19, 0xb6, 0x61, 0xcd, 0x92, 0x6d, 0x5a, 0xfa, 0x77, 0x98, 0x02, 0x4d, 0x4b, + 0x3a, 0xff, 0x30, 0xc7, 0x0f, 0x53, 0x42, 0xeb, 0xb5, 0xc3, 0xc1, 0x43, 0x2f, 0x40, 0xee, 0x88, 0x7e, 0xce, 0x5b, + 0x54, 0x63, 0xf0, 0x57, 0x86, 0x19, 0x3c, 0x31, 0x1f, 0x86, 0x68, 0x16, 0xa5, 0x0e, 0x6e, 0xa5, 0x28, 0xee, 0x5f, + 0xe0, 0xce, 0x48, 0x4b, 0xef, 0x20, 0x54, 0x3b, 0xe6, 0x30, 0x67, 0xec, 0xfb, 0x28, 0xf9, 0xc4, 0x72, 0xe7, 0xc2, + 0xeb, 0x74, 0xbf, 0xa2, 0xce, 0x1e, 0xda, 0x66, 0xaf, 0xaa, 0x63, 0x34, 0x65, 0x16, 0xa8, 0x23, 0xc2, 0x56, 0xc7, + 0xcb, 0x31, 0xaa, 0x85, 0x24, 0x28, 0xbc, 0x2c, 0xec, 0x12, 0x87, 0xdb, 0xbb, 0xc5, 0xd9, 0xe9, 0x9e, 0x1d, 0xd8, + 0x36, 0x58, 0xfc, 0x87, 0x14, 0xb6, 0x12, 0x86, 0x05, 0x18, 0x64, 0xbb, 0x71, 0x8f, 0x6f, 0x6e, 0x56, 0x01, 0x27, + 0x3c, 0x48, 0xa7, 0xee, 0x89, 0x17, 0x79, 0x93, 0x10, 0x06, 0x1c, 0x42, 0x33, 0xec, 0xd2, 0x1b, 0xee, 0xc6, 0x72, + 0x1a, 0x8c, 0x85, 0xf8, 0x49, 0x54, 0xf0, 0x17, 0x18, 0x8f, 0x08, 0x87, 0x68, 0xec, 0xfb, 0xec, 0x82, 0x0d, 0x95, + 0x9d, 0x01, 0x84, 0x8a, 0xdc, 0x9e, 0x3b, 0x0c, 0x8d, 0x66, 0x30, 0x77, 0x18, 0x1e, 0xf6, 0x6d, 0xd8, 0x4b, 0xb0, + 0x2b, 0xc3, 0xe8, 0xa8, 0x33, 0xe8, 0xa7, 0x21, 0xc8, 0x5a, 0x4d, 0x5b, 0x59, 0x34, 0xaf, 0x15, 0x75, 0x07, 0x7d, + 0xe7, 0x0c, 0x8c, 0x74, 0xb0, 0xc5, 0x1d, 0x7c, 0xc3, 0x08, 0x45, 0x11, 0xbe, 0x63, 0xa7, 0xcf, 0x2e, 0x66, 0x8e, + 0xbd, 0xbb, 0x65, 0x6f, 0x62, 0xa9, 0x67, 0x03, 0x7b, 0xc1, 0xdc, 0xe1, 0xb9, 0x6b, 0x76, 0xde, 0x1e, 0x20, 0xa8, + 0x58, 0x88, 0x93, 0x9f, 0xf7, 0xed, 0x3d, 0x31, 0x75, 0x1b, 0x06, 0x4d, 0xe5, 0xf2, 0xe3, 0x8a, 0x1e, 0x12, 0xaa, + 0xaa, 0xab, 0x82, 0x0e, 0xca, 0xba, 0x81, 0x33, 0x31, 0x91, 0x68, 0xe1, 0x64, 0x92, 0x0a, 0xe0, 0xf0, 0x60, 0x33, + 0x98, 0xd4, 0xe8, 0xb6, 0x3d, 0xe8, 0x9f, 0x07, 0xf7, 0xec, 0x7b, 0xea, 0xe5, 0x94, 0x05, 0xe0, 0x5d, 0xd0, 0xf4, + 0xa7, 0xa8, 0x45, 0xe0, 0xe7, 0x8c, 0x01, 0x92, 0xe7, 0x54, 0x34, 0x92, 0x45, 0x73, 0x2c, 0x3a, 0x0c, 0x10, 0x54, + 0xaf, 0xd0, 0xd6, 0x9f, 0x58, 0x93, 0x51, 0x48, 0xb0, 0x6f, 0x6c, 0xc0, 0xd2, 0x6c, 0x76, 0x06, 0x78, 0xde, 0x90, + 0xf3, 0xe2, 0xfb, 0x98, 0x83, 0x4a, 0xd8, 0xda, 0xb3, 0xdd, 0xbe, 0x6d, 0xe1, 0xd2, 0xf6, 0xb2, 0xcd, 0x50, 0x50, + 0x38, 0xde, 0x7c, 0xcf, 0x82, 0xc9, 0x5e, 0xd8, 0xee, 0x3b, 0xb9, 0x50, 0x1d, 0x09, 0x9e, 0x5b, 0x0a, 0x09, 0xde, + 0xf6, 0x26, 0x20, 0xd0, 0x91, 0x73, 0xd7, 0xed, 0x4d, 0x55, 0x08, 0x45, 0x1f, 0x37, 0x47, 0x6e, 0x10, 0xc3, 0x0f, + 0xa7, 0x85, 0x4c, 0x33, 0xd1, 0x7d, 0xb5, 0x66, 0x76, 0x83, 0x91, 0xb2, 0xc8, 0x93, 0x30, 0xdb, 0x74, 0x30, 0x42, + 0x0b, 0x92, 0x76, 0xb7, 0x0f, 0x30, 0x6c, 0x3a, 0x8a, 0xd3, 0xb6, 0x14, 0xab, 0x29, 0xfb, 0xfc, 0xa8, 0x5a, 0x0e, + 0xd6, 0x8f, 0x98, 0x5f, 0x69, 0x1f, 0x00, 0x2b, 0x48, 0xbc, 0x7c, 0xa0, 0xce, 0xbc, 0x9e, 0xd7, 0xce, 0xb7, 0x16, + 0x4a, 0x14, 0x31, 0xcf, 0x90, 0x50, 0xbc, 0xd4, 0x6e, 0x98, 0x30, 0xb7, 0x67, 0x48, 0x0c, 0xcd, 0xf2, 0x61, 0x1b, + 0x98, 0x5e, 0x05, 0xd8, 0x53, 0x73, 0x5b, 0x24, 0x61, 0xd5, 0xdc, 0x3b, 0x02, 0xd6, 0x1e, 0x84, 0xaf, 0xc4, 0x89, + 0x63, 0x4f, 0x45, 0xf3, 0x59, 0x12, 0x3e, 0x6f, 0x1c, 0x17, 0x47, 0x78, 0x22, 0x74, 0xe0, 0x0f, 0xe7, 0x39, 0xc8, + 0x03, 0xfe, 0x1a, 0x2c, 0x83, 0x50, 0x36, 0x45, 0x47, 0x0f, 0x8f, 0x80, 0x3d, 0x42, 0xbc, 0x11, 0x36, 0x37, 0xaa, + 0xd1, 0xa2, 0x24, 0xe3, 0x85, 0x0e, 0x86, 0x7b, 0x5c, 0xba, 0xf6, 0x28, 0x18, 0xe4, 0x89, 0xb1, 0x83, 0x67, 0xfe, + 0xfe, 0x10, 0xab, 0x71, 0x82, 0xc2, 0x2d, 0x69, 0xb7, 0x55, 0xe2, 0xef, 0xc0, 0x4f, 0x41, 0x82, 0x63, 0x1d, 0xf8, + 0x59, 0x1b, 0x1b, 0x89, 0x44, 0x6a, 0x37, 0xed, 0xd1, 0x49, 0x04, 0xc6, 0x83, 0x73, 0x3f, 0x85, 0x6a, 0x24, 0x11, + 0x15, 0xe5, 0x68, 0x81, 0x9a, 0xa7, 0x6a, 0x15, 0x7c, 0x47, 0x66, 0x04, 0x9e, 0x63, 0xd8, 0x9a, 0xfc, 0x54, 0xdd, + 0x58, 0xc4, 0xf2, 0x5d, 0x97, 0x8e, 0xb6, 0xf0, 0x00, 0x52, 0x30, 0x9a, 0x60, 0x18, 0x97, 0x82, 0x92, 0x15, 0xff, + 0x7d, 0x34, 0x62, 0xe5, 0x93, 0xa3, 0x6c, 0x73, 0x73, 0x20, 0xce, 0x2d, 0x88, 0x71, 0xb8, 0x11, 0x5d, 0x8d, 0x2b, + 0x00, 0xea, 0xd3, 0x39, 0x71, 0x3d, 0x30, 0xad, 0x58, 0xd3, 0xa5, 0xd8, 0x27, 0x87, 0x19, 0x80, 0x82, 0x5b, 0xce, + 0x91, 0xdf, 0xff, 0xcb, 0x00, 0xdc, 0x63, 0xff, 0x4b, 0x77, 0x4b, 0x09, 0x9a, 0x9e, 0x3c, 0x53, 0x5c, 0xd0, 0x19, + 0x6b, 0xc7, 0xa3, 0xd8, 0x68, 0x50, 0x78, 0x29, 0x60, 0x00, 0xda, 0x1c, 0x64, 0x42, 0xc5, 0x41, 0xc8, 0x51, 0x81, + 0xed, 0xe3, 0xe6, 0xe7, 0xb8, 0xb3, 0x9f, 0x82, 0x85, 0xd7, 0xd7, 0x6f, 0x8f, 0xe1, 0xed, 0x2f, 0xfa, 0xed, 0x25, + 0x0b, 0x7e, 0x2d, 0x65, 0xe8, 0xbe, 0x36, 0xc5, 0x03, 0x35, 0x45, 0x29, 0x96, 0xc8, 0xa0, 0x21, 0x73, 0xf3, 0xa5, + 0x98, 0x0d, 0x77, 0x4b, 0x54, 0x3b, 0x52, 0x74, 0xe5, 0x3e, 0x8f, 0x4e, 0x91, 0xb8, 0xae, 0x49, 0x0a, 0x23, 0x97, + 0xc0, 0x44, 0xb8, 0xe2, 0x5b, 0x62, 0xce, 0xbd, 0x36, 0xd8, 0xe0, 0xb5, 0xbc, 0x03, 0xb4, 0xef, 0xd8, 0x74, 0xc6, + 0x2f, 0x0f, 0x48, 0xd1, 0x07, 0x32, 0x6d, 0x40, 0x9c, 0x9d, 0xb7, 0x7b, 0xf1, 0x2e, 0xef, 0xc5, 0x20, 0xd5, 0x73, + 0xc5, 0x62, 0xb8, 0x57, 0xbd, 0xb7, 0x18, 0xa5, 0x34, 0x99, 0xc9, 0xab, 0xa1, 0xd7, 0x95, 0xe8, 0x6d, 0x6e, 0x02, + 0x82, 0x3d, 0xa3, 0x2b, 0x17, 0x5d, 0xcb, 0x52, 0xd0, 0x04, 0x20, 0x7a, 0x54, 0x67, 0x39, 0xe2, 0x38, 0xcc, 0x66, + 0x43, 0xc1, 0xc1, 0xdc, 0x95, 0xa3, 0xe2, 0x98, 0xd8, 0x5d, 0x26, 0xec, 0x00, 0x66, 0xc4, 0xe5, 0xad, 0x8e, 0x88, + 0x0e, 0x8b, 0xfe, 0x3a, 0xbe, 0xfd, 0xd1, 0x63, 0x9b, 0x1d, 0x17, 0x34, 0x48, 0x6d, 0xac, 0x87, 0xd5, 0x58, 0x50, + 0x1f, 0x7e, 0xd4, 0x54, 0x2a, 0x8b, 0xcd, 0xcd, 0xb2, 0x7e, 0x54, 0xab, 0x76, 0x70, 0xed, 0x34, 0xe5, 0xa2, 0x99, + 0x0d, 0xc2, 0x81, 0x88, 0x09, 0x14, 0x68, 0x69, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0x94, 0x4f, 0x21, 0xf3, 0xe2, + 0xb2, 0xd4, 0xa9, 0x2f, 0x32, 0x1e, 0x19, 0xe2, 0xa9, 0x27, 0x19, 0x2b, 0xa0, 0x60, 0xbd, 0xd4, 0x4b, 0x68, 0x89, + 0x00, 0xf3, 0x67, 0x2a, 0x87, 0x46, 0x58, 0x20, 0x51, 0x68, 0x98, 0x25, 0xca, 0xf8, 0x2c, 0xc2, 0x18, 0xb4, 0xfd, + 0x93, 0x5a, 0xec, 0xab, 0x50, 0x46, 0x47, 0x71, 0x94, 0x0f, 0x02, 0xaa, 0x9f, 0x4b, 0x09, 0x36, 0x09, 0xdf, 0x03, + 0x1b, 0x55, 0x8e, 0x27, 0x09, 0xc2, 0xa7, 0x71, 0xce, 0xc8, 0x53, 0x58, 0x97, 0x30, 0x4b, 0xd3, 0x36, 0x52, 0xed, + 0x22, 0x33, 0x08, 0xe5, 0xc2, 0xfc, 0x13, 0xe3, 0xec, 0x22, 0x0b, 0x97, 0x5a, 0x83, 0xf9, 0xf1, 0xc6, 0x04, 0x28, + 0xbb, 0xba, 0xca, 0x84, 0x8f, 0x1b, 0x91, 0xbd, 0xa1, 0x2b, 0x26, 0x7d, 0x85, 0x54, 0xe0, 0x44, 0x64, 0xf1, 0xd0, + 0x19, 0x0a, 0x8d, 0x70, 0x48, 0xa7, 0xc8, 0xb9, 0x6b, 0x6c, 0xfa, 0xbc, 0xaf, 0x7d, 0xa3, 0x34, 0x74, 0x12, 0x10, + 0x02, 0x02, 0x77, 0xc3, 0x9a, 0x4a, 0xfb, 0x69, 0x90, 0x50, 0x29, 0xfa, 0x39, 0x80, 0x7f, 0x18, 0x49, 0x0a, 0x80, + 0xfd, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, 0x2e, 0x00, 0xcd, 0x75, 0x80, 0x2b, 0xe1, 0x0b, 0x03, 0x15, 0xa6, 0xa7, + 0x59, 0x79, 0x29, 0x94, 0xc8, 0xbb, 0x15, 0x29, 0x6b, 0x24, 0x93, 0xcf, 0xd0, 0xe1, 0x53, 0xde, 0xf5, 0x6b, 0x89, + 0x87, 0x2e, 0x78, 0x0a, 0xcb, 0xaa, 0x9e, 0x5f, 0x85, 0x9c, 0x9c, 0x6b, 0xd0, 0x15, 0x52, 0xe8, 0x2f, 0x39, 0xc9, + 0xfb, 0xaf, 0xfc, 0xaa, 0x96, 0x1a, 0x43, 0xd9, 0xfb, 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, 0x29, 0x58, 0x01, + 0xc6, 0xa1, 0x96, 0x4a, 0xa6, 0x91, 0xc4, 0x97, 0x4a, 0x14, 0x98, 0xf2, 0x60, 0x08, 0x7e, 0x6a, 0xf3, 0xa4, 0xeb, + 0xc2, 0xf5, 0xe3, 0x29, 0xa6, 0xf6, 0x10, 0xe8, 0xb1, 0xb7, 0x06, 0xa6, 0x44, 0x5d, 0x87, 0x15, 0xc4, 0xa1, 0x59, + 0x4d, 0xb3, 0x80, 0x19, 0xd3, 0x06, 0x2d, 0xd9, 0x06, 0x5b, 0x2e, 0x07, 0xfb, 0x48, 0x6c, 0xcf, 0x6a, 0x05, 0x84, + 0xae, 0x41, 0x03, 0x43, 0xee, 0x52, 0xa1, 0x85, 0x79, 0xaf, 0x4b, 0x45, 0xb8, 0x3f, 0xfb, 0x5c, 0x5a, 0xc1, 0x99, + 0x97, 0xd1, 0xc0, 0x07, 0xf1, 0x49, 0x82, 0x89, 0x2f, 0x88, 0x15, 0xd8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x38, 0x15, + 0x17, 0x29, 0x83, 0x65, 0x45, 0xa9, 0x0d, 0x3f, 0xa4, 0xc8, 0xd6, 0x5d, 0x1e, 0xe8, 0x2e, 0xc4, 0x02, 0xd8, 0xe9, + 0x57, 0x8c, 0x7c, 0xcb, 0x7a, 0x19, 0x30, 0x38, 0xd3, 0x1a, 0x07, 0x81, 0xdf, 0xdc, 0x4c, 0x06, 0x65, 0x4a, 0x6c, + 0xd7, 0x64, 0x75, 0x01, 0x39, 0x0c, 0xd5, 0xc4, 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, 0xdf, 0x77, 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, 0xdd, 0x64, 0x62, 0xda, 0x5b, 0x42, 0xea, 0x08, 0xbb, 0x87, 0x72, 0x82, 0xbb, 0x9a, 0x67, + 0x9f, 0x87, 0xb3, 0xeb, 0x99, 0x7b, 0xdf, 0x60, 0xee, 0xc7, 0x21, 0x37, 0x18, 0x3d, 0x96, 0x09, 0x3f, 0x32, 0xf6, + 0x91, 0xab, 0xaa, 0x27, 0xa7, 0x61, 0x25, 0xb2, 0xc4, 0x93, 0x71, 0xd4, 0x61, 0x9c, 0x8a, 0xd6, 0x04, 0xd9, 0xd5, + 0x55, 0x61, 0xee, 0x05, 0x0a, 0x9a, 0x7a, 0xbc, 0x1e, 0xa7, 0xad, 0xd8, 0xd9, 0x88, 0x44, 0xee, 0xbf, 0xaa, 0x45, + 0x22, 0x2b, 0x3e, 0xc7, 0x91, 0xae, 0x39, 0xc8, 0x7d, 0x72, 0xba, 0xbc, 0x49, 0x85, 0x6e, 0xd1, 0x68, 0x1b, 0x7b, + 0x54, 0x1f, 0x48, 0xea, 0x19, 0x15, 0x58, 0xd5, 0xd8, 0x1b, 0x1b, 0x1d, 0x91, 0x6e, 0xa9, 0x14, 0x1b, 0x2c, 0x2d, + 0x8c, 0x66, 0x8c, 0x82, 0x41, 0x49, 0x91, 0x81, 0x1a, 0xe5, 0xd7, 0x08, 0x86, 0x7d, 0x6a, 0x00, 0x8a, 0x73, 0x75, + 0xf5, 0xe3, 0x52, 0xb2, 0x85, 0x80, 0xc4, 0x5d, 0x30, 0x10, 0x6b, 0x82, 0x99, 0x91, 0x4f, 0xde, 0x03, 0xe7, 0xf5, + 0x19, 0xfa, 0x08, 0xe0, 0x17, 0x88, 0x4d, 0x0f, 0x26, 0xb6, 0x4d, 0x44, 0xd1, 0x67, 0x03, 0xcf, 0x01, 0xd8, 0x59, + 0x15, 0x1a, 0x7d, 0x57, 0xa5, 0x80, 0x21, 0x1b, 0xb8, 0x01, 0xab, 0xc2, 0x72, 0xfb, 0xcf, 0xc1, 0x6d, 0x80, 0xd7, + 0x67, 0xb2, 0xf9, 0x3a, 0xe6, 0x09, 0x56, 0x67, 0x17, 0x7e, 0x65, 0x59, 0x8b, 0x73, 0xa7, 0xc3, 0x46, 0xbd, 0xa2, + 0x84, 0xa8, 0x3d, 0xc0, 0xda, 0x43, 0x8c, 0xb0, 0x88, 0xf7, 0x57, 0xf8, 0xae, 0xc7, 0x2d, 0xf7, 0x35, 0x5a, 0x84, + 0xe9, 0x32, 0x69, 0x0c, 0x4a, 0xd6, 0xfd, 0x64, 0xc4, 0xbd, 0x3c, 0x10, 0xb1, 0xe0, 0x0a, 0x47, 0x56, 0x85, 0x14, + 0x1b, 0x48, 0xd2, 0xd3, 0x3e, 0x1d, 0xb0, 0xaf, 0x37, 0x7b, 0x01, 0x65, 0xde, 0x57, 0xa4, 0x92, 0x90, 0xd2, 0xec, + 0x86, 0x48, 0x12, 0xd6, 0x8a, 0x3c, 0x75, 0x3e, 0x70, 0xb4, 0xcf, 0xad, 0x24, 0x82, 0x11, 0x9c, 0x84, 0xe9, 0x58, + 0x79, 0xd8, 0x14, 0xe0, 0x2a, 0x3a, 0x62, 0xfa, 0x26, 0x20, 0xbf, 0x19, 0xc8, 0xed, 0xa5, 0xe4, 0xda, 0x5c, 0xc3, + 0xf0, 0x04, 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, 0xdf, 0x31, 0x47, 0xc8, 0x5c, 0x61, 0x7d, + 0xce, 0x9c, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x49, 0x2d, 0x5e, 0xa7, 0x4d, 0x29, 0x11, 0x93, 0x12, 0x73, 0x5d, + 0xa4, 0x62, 0x33, 0x25, 0xee, 0xdc, 0xfa, 0x46, 0x0b, 0x69, 0xa3, 0xad, 0x8b, 0x1c, 0x6c, 0x56, 0xc9, 0x7b, 0x02, + 0xe3, 0xb9, 0x20, 0x7c, 0xf9, 0x8a, 0x92, 0x74, 0x98, 0x63, 0x22, 0x58, 0xbd, 0x98, 0x8a, 0xfc, 0x9d, 0xa3, 0xd3, + 0xec, 0x0d, 0x7a, 0x90, 0x7a, 0x03, 0x89, 0x59, 0x13, 0xdf, 0x85, 0x34, 0xd4, 0x11, 0x02, 0x95, 0x51, 0x2d, 0xd3, + 0x71, 0x62, 0x15, 0xbe, 0x11, 0x7c, 0xf5, 0x56, 0x1f, 0xe7, 0x1b, 0xcf, 0x8d, 0xd5, 0x08, 0x62, 0xf0, 0x16, 0xf2, + 0x81, 0x27, 0x45, 0x38, 0x10, 0x2e, 0xdf, 0xdc, 0xec, 0xe5, 0xbb, 0xbc, 0x0a, 0x91, 0x54, 0x30, 0xc6, 0x98, 0x51, + 0x8c, 0x7b, 0xa2, 0xa6, 0x16, 0x73, 0x18, 0x58, 0xb6, 0x0e, 0x73, 0x3c, 0x00, 0x80, 0x96, 0xa6, 0xf4, 0xaa, 0xa9, + 0x50, 0x79, 0x9e, 0x4b, 0xf8, 0x54, 0x87, 0xa8, 0xaa, 0xf1, 0xdb, 0xd5, 0x19, 0x28, 0x04, 0xf7, 0x5a, 0xc7, 0xc3, + 0x43, 0x08, 0x58, 0x45, 0x21, 0x0b, 0xf4, 0x06, 0xed, 0x55, 0x89, 0x50, 0xcc, 0x9c, 0xac, 0xc7, 0x0c, 0x27, 0x15, + 0x6c, 0xa1, 0x12, 0x96, 0x4a, 0x0b, 0xfc, 0x6a, 0x23, 0x34, 0x4f, 0x19, 0xf7, 0x5f, 0x55, 0x38, 0x83, 0xfe, 0x60, + 0xde, 0x32, 0xa3, 0xbe, 0x5d, 0x3a, 0x91, 0xa9, 0xc0, 0xc4, 0xcd, 0x2c, 0xb5, 0xdf, 0xaf, 0xab, 0xb4, 0x9f, 0x57, + 0xc8, 0x7d, 0x4e, 0x9a, 0xaf, 0x73, 0x07, 0xcd, 0x27, 0xc3, 0xfd, 0x4a, 0xf9, 0xa1, 0x85, 0x51, 0x53, 0x7e, 0x79, + 0x5d, 0xf9, 0x15, 0x9e, 0x0a, 0x6f, 0xed, 0x75, 0x51, 0xe8, 0xa2, 0x3e, 0x07, 0x43, 0x48, 0x3f, 0x82, 0x6b, 0x68, + 0xf0, 0xa0, 0x48, 0x16, 0x8b, 0xb5, 0x0b, 0xe2, 0xfa, 0x98, 0x53, 0xed, 0x50, 0xc6, 0x18, 0xf1, 0xb4, 0xe4, 0x20, + 0xc9, 0xe0, 0x60, 0xfc, 0x06, 0x06, 0xc4, 0xa4, 0x24, 0xa4, 0x43, 0xe8, 0xac, 0xcc, 0x44, 0x54, 0xee, 0xe2, 0xed, + 0xc6, 0x65, 0x4d, 0xa1, 0x08, 0x3b, 0xc1, 0x4c, 0xa5, 0x54, 0x10, 0x48, 0x93, 0x6f, 0xad, 0x53, 0x0b, 0x86, 0x16, + 0xae, 0xa9, 0x80, 0xbc, 0xb6, 0xeb, 0x41, 0x93, 0xf7, 0x14, 0x43, 0x5f, 0xa5, 0x46, 0xbc, 0xcc, 0xe0, 0x6b, 0xd8, + 0xfc, 0x35, 0x51, 0x92, 0x87, 0x4c, 0xc4, 0x5e, 0xc1, 0x27, 0x42, 0x36, 0x05, 0x3b, 0x13, 0xe8, 0x87, 0x76, 0x65, + 0x2f, 0xdd, 0x2d, 0x2a, 0x97, 0x16, 0x8d, 0xad, 0x44, 0xcd, 0x9a, 0x1f, 0xc5, 0x9b, 0x29, 0xec, 0x67, 0x8f, 0x12, + 0x08, 0x48, 0x53, 0x39, 0x49, 0x35, 0xef, 0x51, 0x3a, 0x00, 0x90, 0x60, 0xf7, 0x13, 0x58, 0xe8, 0x37, 0x25, 0x26, + 0x58, 0x54, 0x8d, 0xdd, 0x66, 0xa0, 0x35, 0x67, 0xa4, 0xf9, 0x66, 0xa8, 0xb5, 0x37, 0x95, 0xf5, 0x8c, 0xd9, 0x01, + 0xb6, 0xed, 0x6e, 0x16, 0x47, 0xe9, 0x66, 0x67, 0x60, 0x08, 0x2e, 0x3c, 0xfe, 0x4f, 0x4a, 0x4c, 0x03, 0xc9, 0xa5, + 0x6e, 0xfc, 0x84, 0x3a, 0x0c, 0xff, 0x5b, 0x90, 0x02, 0xee, 0xd7, 0x56, 0x63, 0xc9, 0xb9, 0x57, 0x1c, 0x25, 0x97, + 0x55, 0xb5, 0xab, 0x25, 0x68, 0xe8, 0x46, 0x32, 0x26, 0x8a, 0x79, 0x4e, 0x00, 0x8c, 0x62, 0xf3, 0xa7, 0x4c, 0x27, + 0x79, 0xff, 0xba, 0x32, 0xb5, 0xdb, 0xf7, 0xfd, 0x28, 0x3f, 0xa5, 0x23, 0x15, 0x95, 0xcd, 0x49, 0xcc, 0xbf, 0x2d, + 0xc0, 0x34, 0x27, 0x3e, 0xd2, 0x73, 0x0d, 0x42, 0x01, 0xbe, 0xb2, 0xa1, 0xd4, 0x6c, 0x8f, 0xff, 0xe8, 0x6c, 0xf7, + 0x25, 0x51, 0x04, 0x0b, 0x34, 0xe8, 0x72, 0x0d, 0xbe, 0x80, 0x65, 0x70, 0x47, 0xfa, 0x29, 0xf8, 0x5e, 0x5e, 0x07, + 0x9f, 0xb1, 0xff, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x4e, 0xd3, 0xb0, 0x12, 0xe2, 0x12, 0x15, 0x66, 0x15, 0xe7, + 0x8f, 0xeb, 0xbc, 0x6e, 0x5a, 0x96, 0x18, 0x94, 0x9f, 0xb9, 0x86, 0x1b, 0xdf, 0x6b, 0xe4, 0x8f, 0xef, 0x3f, 0x07, + 0xdd, 0x4e, 0xa4, 0xdd, 0xd8, 0xc8, 0xd7, 0xc8, 0x42, 0xc3, 0x7b, 0x61, 0x33, 0x68, 0x8b, 0x74, 0xc9, 0xd5, 0x33, + 0x16, 0xe3, 0x6d, 0x11, 0x2a, 0xc3, 0x07, 0x2c, 0x98, 0x01, 0x86, 0xe0, 0xb1, 0x53, 0x99, 0x7c, 0x86, 0x8d, 0xa6, + 0xd8, 0x35, 0x17, 0x06, 0x1f, 0xa8, 0xca, 0x42, 0xf2, 0x62, 0x9d, 0x6c, 0xcf, 0xce, 0xe0, 0xf9, 0x65, 0x5c, 0x00, + 0x75, 0x00, 0xfd, 0x8a, 0xca, 0x62, 0x03, 0xb9, 0xb8, 0x29, 0x6b, 0xbd, 0xa2, 0xd1, 0xe8, 0xc6, 0x2e, 0xac, 0xae, + 0xc0, 0x27, 0x51, 0x3a, 0x4a, 0xc4, 0x24, 0x66, 0x52, 0xe5, 0x8a, 0x5c, 0x1b, 0xdd, 0x4b, 0x5b, 0x34, 0xcf, 0x85, + 0x04, 0xaf, 0x08, 0xdc, 0x10, 0xfa, 0x4a, 0x5f, 0xae, 0x36, 0x50, 0xf0, 0xa8, 0xbd, 0xb9, 0x08, 0x26, 0x26, 0x1e, + 0x33, 0xa4, 0xa6, 0x5f, 0x87, 0x53, 0x2b, 0x8b, 0x25, 0x87, 0x5f, 0xe7, 0x8c, 0x35, 0x14, 0x00, 0xf1, 0xc9, 0x83, + 0xeb, 0xdd, 0xa4, 0x57, 0x4a, 0x3b, 0x28, 0x8d, 0x10, 0xdf, 0x55, 0xf8, 0xba, 0x0b, 0xc5, 0x57, 0xae, 0xba, 0xf7, + 0x35, 0x65, 0xc6, 0x05, 0xa3, 0xe7, 0x7c, 0x9a, 0x34, 0xae, 0xdd, 0xd0, 0x5d, 0x9d, 0x9f, 0xbc, 0x1f, 0x65, 0xde, + 0xc2, 0x14, 0xd8, 0x04, 0x54, 0xc1, 0x73, 0xef, 0xb5, 0x71, 0xa2, 0xfc, 0x9d, 0x79, 0xc4, 0x2b, 0x87, 0x59, 0x75, + 0x92, 0xfc, 0x5d, 0xff, 0xbb, 0xe0, 0xfa, 0x96, 0xc6, 0x09, 0x72, 0x57, 0x9d, 0x20, 0x13, 0xe5, 0xa6, 0x7a, 0xc3, + 0xed, 0xdf, 0x55, 0x20, 0x88, 0x53, 0x31, 0x7d, 0x54, 0x8e, 0xeb, 0x47, 0x0b, 0x54, 0x2a, 0x22, 0x3e, 0x55, 0xb9, + 0x2b, 0xd7, 0xa6, 0x86, 0x7a, 0x5c, 0x27, 0xb3, 0xd0, 0x34, 0x2b, 0x72, 0x29, 0x9b, 0x1e, 0x23, 0xd3, 0xec, 0x54, + 0x9b, 0xdf, 0xbd, 0xf6, 0x90, 0x8e, 0xa1, 0xb9, 0x58, 0xab, 0x05, 0xf7, 0xbb, 0x8a, 0xc2, 0xbb, 0x5e, 0x6c, 0xa4, + 0x32, 0xd4, 0xac, 0x47, 0xd1, 0xc7, 0x71, 0x9b, 0xb9, 0x3c, 0xca, 0xfe, 0xac, 0x01, 0x60, 0x3a, 0xc2, 0xa2, 0xbb, + 0xe9, 0x19, 0x7b, 0x02, 0x3d, 0x3d, 0x91, 0x41, 0xa2, 0xd7, 0x3a, 0x5f, 0xb5, 0x4a, 0x2c, 0x5d, 0x41, 0x60, 0xf7, + 0x86, 0x8c, 0x55, 0x49, 0xbb, 0xe5, 0xfa, 0xe5, 0x3c, 0x9f, 0xa7, 0x7c, 0x29, 0xcf, 0xa7, 0x66, 0xd1, 0xad, 0xb5, + 0xdd, 0x9b, 0x53, 0x43, 0xc5, 0x5c, 0xab, 0x9b, 0xfc, 0x81, 0xe9, 0x3a, 0x18, 0x6a, 0x11, 0x64, 0x56, 0xbb, 0xea, + 0x59, 0x59, 0x4e, 0xeb, 0x99, 0x1c, 0x53, 0xe1, 0x9b, 0x4a, 0x77, 0x88, 0x6e, 0x98, 0xaa, 0x99, 0x7e, 0x6a, 0x6c, + 0x0b, 0xd9, 0xe6, 0xf9, 0xe5, 0x28, 0x07, 0x4a, 0xcb, 0xfd, 0x65, 0xc2, 0xf0, 0xd3, 0xd5, 0xd5, 0x4f, 0x42, 0x4e, + 0x55, 0x1d, 0xbd, 0xc5, 0x4b, 0xdd, 0x33, 0x98, 0x96, 0xca, 0x89, 0x38, 0x61, 0xab, 0x07, 0x6f, 0xee, 0x5e, 0x01, + 0xcb, 0x09, 0x60, 0x77, 0xc2, 0x9c, 0xc6, 0x50, 0xd5, 0x06, 0xfe, 0x71, 0xf5, 0x60, 0xab, 0xf6, 0xf0, 0x8f, 0xfd, + 0x1f, 0x83, 0x1b, 0x1b, 0x1b, 0xdb, 0x78, 0xbb, 0x96, 0x08, 0xf2, 0x0a, 0x0f, 0xf4, 0xf1, 0xea, 0xa3, 0xa0, 0xe5, + 0x2a, 0xb1, 0xdd, 0x77, 0x28, 0x6c, 0x0d, 0xf2, 0x4d, 0xca, 0xa4, 0xe1, 0xbc, 0xe0, 0xd9, 0x54, 0xce, 0x50, 0xc8, + 0x6b, 0x3e, 0x0e, 0xda, 0x8e, 0xf0, 0x37, 0x70, 0x6a, 0xc7, 0xcb, 0x8b, 0x4f, 0xd0, 0x07, 0x3c, 0x5d, 0x29, 0x4d, + 0x45, 0x9c, 0x52, 0x6e, 0xd1, 0xe5, 0x3a, 0x0f, 0x46, 0x8a, 0x8b, 0x09, 0x2a, 0x1d, 0x77, 0x71, 0xe3, 0x6c, 0xe4, + 0xf4, 0x97, 0x78, 0x75, 0x91, 0x2e, 0x1f, 0x89, 0x6c, 0xd5, 0xd2, 0xfb, 0x5d, 0x9f, 0x6e, 0xdb, 0x53, 0xc6, 0x27, + 0xd9, 0x88, 0x0e, 0x66, 0x7c, 0x9c, 0x08, 0xaf, 0x4f, 0x8c, 0xf4, 0xdd, 0x22, 0x30, 0xdd, 0x1c, 0x9b, 0xfc, 0x70, + 0xbc, 0xde, 0x6c, 0xd6, 0xb8, 0xfd, 0x37, 0xce, 0x13, 0x67, 0x51, 0x62, 0x44, 0x65, 0xa1, 0xe1, 0x01, 0xad, 0x10, + 0x37, 0xef, 0x99, 0xc0, 0xb8, 0xec, 0x92, 0xa4, 0xb6, 0x1b, 0x08, 0x5c, 0xec, 0x71, 0xcc, 0x92, 0x91, 0xed, 0x41, + 0x79, 0xa0, 0x2f, 0x46, 0xd3, 0x2d, 0x60, 0x5a, 0x5e, 0x3b, 0x3b, 0x4f, 0x6d, 0xaf, 0x9a, 0x2a, 0x80, 0x59, 0xb2, + 0x3c, 0x3e, 0x45, 0xd6, 0xfd, 0x16, 0xba, 0x88, 0x01, 0x63, 0xe3, 0xca, 0x9c, 0xbb, 0x58, 0xb5, 0x22, 0xbe, 0xd1, + 0x44, 0x9a, 0xd4, 0x47, 0xd4, 0x77, 0x10, 0xd6, 0xea, 0x2a, 0x07, 0x09, 0xdc, 0x23, 0xef, 0x8e, 0xb8, 0xf4, 0xf4, + 0x99, 0xc5, 0xb8, 0x4a, 0xdf, 0x52, 0xd7, 0xe2, 0x9a, 0x61, 0xaf, 0xb8, 0x0f, 0xf6, 0x07, 0xc6, 0x2d, 0x62, 0x11, + 0x6f, 0xe7, 0xb5, 0x14, 0xd6, 0xc6, 0x1c, 0x68, 0x6e, 0xb8, 0xc1, 0xef, 0xac, 0x5a, 0x33, 0x30, 0xc3, 0x8c, 0x33, + 0x92, 0xdf, 0x8d, 0x7b, 0x55, 0x63, 0x47, 0xae, 0x02, 0x88, 0xbe, 0x05, 0x5d, 0x92, 0xc3, 0x2b, 0x59, 0xae, 0x3a, + 0x43, 0xfe, 0x0d, 0xd6, 0x59, 0x2f, 0x4e, 0xc0, 0x4c, 0x9a, 0xf2, 0x12, 0x13, 0x53, 0xc4, 0xe5, 0x66, 0x19, 0xf3, + 0x34, 0x7d, 0x16, 0xed, 0xe0, 0xe4, 0x46, 0x02, 0x47, 0xec, 0x1b, 0xcb, 0xd0, 0x4c, 0xd8, 0x88, 0x89, 0x34, 0x2a, + 0xa5, 0x84, 0xf7, 0xe5, 0x52, 0x4b, 0xfe, 0x32, 0x97, 0x57, 0x5f, 0x6e, 0x13, 0x1c, 0x90, 0xd7, 0xc0, 0x72, 0x68, + 0x1c, 0xb7, 0x0c, 0x24, 0x62, 0x31, 0x20, 0x46, 0xad, 0xca, 0xe5, 0x64, 0x54, 0x27, 0xf3, 0x15, 0x72, 0xa1, 0x22, + 0x0f, 0x6e, 0x09, 0x94, 0xfc, 0x39, 0xa6, 0x0e, 0x66, 0xa5, 0x76, 0xd3, 0x62, 0x93, 0xe4, 0x3d, 0x33, 0x20, 0xb9, + 0xfa, 0x1a, 0x1e, 0x1a, 0xbf, 0x78, 0x65, 0x4e, 0x09, 0x5f, 0x94, 0xb1, 0xb4, 0x34, 0xe6, 0xd2, 0xbf, 0x90, 0xf7, + 0x69, 0x25, 0x60, 0xbf, 0x81, 0x98, 0x32, 0x70, 0x89, 0x8d, 0x0b, 0x92, 0xf2, 0x5a, 0x9e, 0xb2, 0xfb, 0x1a, 0xca, + 0x77, 0xc9, 0xa4, 0xab, 0x54, 0xd6, 0x35, 0x56, 0xdd, 0x6f, 0x73, 0x96, 0x5f, 0x1e, 0x30, 0xcc, 0x4d, 0x46, 0x83, + 0x6c, 0xc9, 0xcc, 0xa6, 0xfc, 0x6a, 0xef, 0xc6, 0xaf, 0x3c, 0x94, 0x74, 0xa8, 0x56, 0xe9, 0xe6, 0xa5, 0x1b, 0x8e, + 0x71, 0xe3, 0x86, 0x23, 0x80, 0x8d, 0x61, 0xa7, 0x8a, 0xd4, 0x3a, 0xff, 0x7d, 0x39, 0xfc, 0x44, 0x7b, 0x6d, 0xa0, + 0x77, 0xdd, 0x60, 0x65, 0x7a, 0xfa, 0x0d, 0xa8, 0x1a, 0x59, 0x42, 0x37, 0xa1, 0x8a, 0xc9, 0x48, 0x94, 0x98, 0xae, + 0x52, 0x1e, 0xf5, 0x35, 0xe2, 0x1c, 0xc4, 0x0d, 0xe5, 0x2f, 0xfe, 0x35, 0xbc, 0x3c, 0x0e, 0xd0, 0x88, 0x5a, 0x8c, + 0xb3, 0x94, 0xb7, 0xc6, 0xd1, 0x34, 0x4e, 0x2e, 0x83, 0x79, 0xdc, 0x9a, 0x66, 0x69, 0x56, 0xcc, 0x80, 0x2b, 0xbd, + 0xe2, 0x12, 0x6c, 0xf8, 0x69, 0x6b, 0x1e, 0x7b, 0xcf, 0x59, 0x72, 0xc6, 0x78, 0x3c, 0x8c, 0x3c, 0x7b, 0x3f, 0x07, + 0xf1, 0x60, 0xbd, 0x8e, 0xf2, 0x3c, 0x3b, 0xb7, 0xbd, 0x77, 0xd9, 0x09, 0x30, 0xad, 0xf7, 0xe6, 0xe2, 0xf2, 0x94, + 0xa5, 0xde, 0xfb, 0x93, 0x79, 0xca, 0xe7, 0x5e, 0x11, 0xa5, 0x45, 0xab, 0x60, 0x79, 0x3c, 0x06, 0x35, 0x91, 0x64, + 0x79, 0x0b, 0xf3, 0x9f, 0xa7, 0x2c, 0x48, 0xe2, 0xd3, 0x09, 0xb7, 0x46, 0x51, 0xfe, 0xa9, 0xd7, 0x6a, 0xcd, 0xf2, + 0x78, 0x1a, 0xe5, 0x97, 0x2d, 0x6a, 0x11, 0x7c, 0xd1, 0xde, 0x8e, 0xbe, 0x1a, 0xdf, 0xef, 0xf1, 0x1c, 0xfa, 0xc6, + 0x48, 0xc5, 0x00, 0x84, 0x8f, 0xb5, 0xbd, 0xd3, 0x9e, 0x16, 0x6b, 0xe2, 0x44, 0x29, 0x4a, 0x79, 0x79, 0xec, 0x7d, + 0x64, 0x00, 0xb7, 0x7f, 0xc2, 0x53, 0x0f, 0x7c, 0x39, 0x9e, 0xa5, 0x8b, 0xe1, 0x3c, 0x2f, 0x60, 0x80, 0x59, 0x16, + 0xa7, 0x9c, 0xe5, 0xbd, 0x93, 0x2c, 0x07, 0xb2, 0xb5, 0xf2, 0x68, 0x14, 0xcf, 0x8b, 0xe0, 0xfe, 0xec, 0xa2, 0x87, + 0xb6, 0xc2, 0x69, 0x9e, 0xcd, 0xd3, 0x91, 0x9c, 0x2b, 0x4e, 0x61, 0x63, 0xc4, 0xdc, 0xac, 0xa0, 0x2f, 0xa1, 0x00, + 0x7c, 0x29, 0x8b, 0xf2, 0xd6, 0x29, 0x76, 0x46, 0x43, 0xbf, 0x3d, 0x62, 0xa7, 0x5e, 0x7e, 0x7a, 0x12, 0x39, 0x9d, + 0xee, 0x43, 0x4f, 0xfd, 0xf3, 0x77, 0x5c, 0x30, 0xdc, 0x57, 0x16, 0x77, 0xda, 0xed, 0x7f, 0x70, 0x7b, 0x8d, 0x59, + 0x08, 0xa0, 0xa0, 0x33, 0xbb, 0xb0, 0x8a, 0x2c, 0x81, 0xf5, 0x59, 0xd5, 0xb3, 0x37, 0x03, 0xbf, 0x29, 0x4e, 0x4f, + 0x83, 0xee, 0xec, 0xa2, 0x44, 0xec, 0x02, 0x91, 0x90, 0x29, 0x91, 0x94, 0x6f, 0x8b, 0x3f, 0x0a, 0xf1, 0xa3, 0xd5, + 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd5, 0x5b, 0x23, 0xd8, 0x07, 0x44, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x80, 0x13, 0x98, + 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, 0x0d, 0xc6, 0xa3, 0xdb, 0xc0, 0xc8, 0xd3, 0xd1, 0xa2, 0xbe, 0xae, + 0x1d, 0x70, 0x4e, 0x7b, 0x13, 0x86, 0xfc, 0x14, 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf1, 0x89, 0x78, 0x24, 0x76, 0x3e, + 0x17, 0x75, 0x3b, 0xed, 0xb6, 0x78, 0x2f, 0x40, 0xa1, 0x05, 0x1d, 0x1f, 0x1b, 0x00, 0x13, 0x7d, 0xb8, 0xee, 0x23, + 0x36, 0x5f, 0xdf, 0xfa, 0xa5, 0x1a, 0xef, 0x5c, 0xe5, 0x0d, 0x0a, 0x15, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0xf9, + 0x5e, 0x47, 0x1f, 0x54, 0xf5, 0xbf, 0x66, 0xa4, 0xf5, 0x02, 0xee, 0x99, 0xb9, 0x40, 0xbd, 0x74, 0x0f, 0x43, 0x52, + 0xad, 0x96, 0x0b, 0x7a, 0x83, 0x61, 0x08, 0x89, 0x0e, 0x04, 0x9d, 0xbc, 0x5f, 0xd0, 0x37, 0x35, 0x32, 0x37, 0x28, + 0x9c, 0xcc, 0x85, 0x2d, 0x9f, 0x69, 0xb9, 0x0e, 0x4a, 0x1a, 0xbc, 0xec, 0x0f, 0x4c, 0x36, 0x00, 0xe9, 0x4d, 0xa1, + 0xae, 0x1f, 0x42, 0xe1, 0x4a, 0x29, 0x47, 0x6a, 0x7a, 0xd3, 0x15, 0x7d, 0x58, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x06, + 0xff, 0x0a, 0xc3, 0xde, 0xd1, 0x8e, 0x65, 0x91, 0xcd, 0xf3, 0x21, 0x45, 0xea, 0x96, 0x3d, 0x7e, 0x9b, 0x14, 0xae, + 0xed, 0x31, 0x2d, 0xe7, 0xd1, 0x0d, 0xae, 0x7d, 0xe4, 0x80, 0xd3, 0x21, 0x88, 0xb8, 0x63, 0x20, 0xa3, 0x1c, 0x0a, + 0x42, 0x54, 0x5d, 0x23, 0xca, 0x77, 0xa3, 0xfb, 0x97, 0xfe, 0x3c, 0x8d, 0x41, 0xd2, 0x7d, 0x8c, 0x47, 0x74, 0xef, + 0x24, 0x1e, 0xd1, 0x41, 0x44, 0x8b, 0x12, 0x8f, 0x30, 0xb2, 0x0d, 0x05, 0xea, 0x3b, 0x2c, 0xf0, 0x2c, 0x13, 0x59, + 0xec, 0x96, 0x8d, 0x87, 0x09, 0x86, 0xaa, 0x1c, 0x65, 0xd3, 0x28, 0x4e, 0x03, 0xfc, 0x3e, 0x88, 0xa7, 0x47, 0x0c, + 0xb0, 0x8b, 0x07, 0x3f, 0x99, 0xcc, 0x45, 0xeb, 0xb8, 0xfe, 0xaf, 0x20, 0x47, 0xa8, 0x7f, 0x29, 0xfd, 0x30, 0x0d, + 0x97, 0x3a, 0xe6, 0xad, 0x97, 0x82, 0xec, 0xe1, 0xca, 0x66, 0x65, 0x14, 0xe7, 0xd8, 0xe5, 0xf4, 0xa3, 0xdf, 0xea, + 0x04, 0x1d, 0xed, 0xba, 0xd6, 0x6e, 0xa3, 0x8a, 0x5c, 0x16, 0x79, 0xa3, 0x91, 0x60, 0xd0, 0xcf, 0x02, 0xce, 0xea, + 0x5d, 0xc3, 0xea, 0x49, 0xbe, 0xc4, 0x00, 0xce, 0x69, 0xea, 0xd4, 0x80, 0xa0, 0xb3, 0x80, 0x6b, 0xa6, 0x72, 0xcb, + 0x88, 0x94, 0xd2, 0x63, 0xda, 0xc0, 0xf5, 0xbb, 0x44, 0x78, 0x6f, 0xa8, 0x9e, 0x02, 0xa5, 0x58, 0x6e, 0x74, 0xbc, + 0x2b, 0x76, 0xbc, 0x45, 0x3c, 0x16, 0xda, 0xb0, 0x05, 0x6d, 0xeb, 0x2f, 0x43, 0xa0, 0xd2, 0xa7, 0xd0, 0x5e, 0x5f, + 0x38, 0x2a, 0xb1, 0x3e, 0x87, 0xb9, 0xf6, 0x85, 0xd6, 0xa3, 0x1b, 0xf9, 0xf6, 0xde, 0xfa, 0x82, 0x97, 0xbb, 0x5b, + 0xa2, 0xf7, 0xde, 0x71, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, 0xf4, 0x5d, 0x1c, + 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, 0x84, 0x94, 0x42, + 0xe4, 0x60, 0xf4, 0x96, 0xa1, 0x38, 0x46, 0x08, 0xfa, 0xeb, 0x8b, 0xb8, 0x0c, 0xd7, 0x17, 0x59, 0x79, 0x0c, 0x96, + 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, 0x6d, 0xab, 0x2f, + 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xf7, 0x60, 0xa0, 0xdd, 0x2d, 0xd1, + 0x08, 0xf0, 0xca, 0x11, 0xe4, 0x0a, 0x2d, 0x4c, 0xc7, 0x31, 0x51, 0x1b, 0xc5, 0x67, 0x9a, 0x74, 0x94, 0x9b, 0xbc, + 0xb7, 0x9b, 0x44, 0x27, 0x2c, 0x81, 0x21, 0x8b, 0xab, 0xab, 0x36, 0x8c, 0x24, 0x5e, 0xad, 0xdd, 0x38, 0x9d, 0xcd, + 0xe5, 0x67, 0xb6, 0x60, 0xe2, 0x0e, 0x9e, 0x7c, 0xe2, 0x2d, 0x80, 0xbe, 0x3a, 0xca, 0x0b, 0xe4, 0x00, 0x00, 0x22, + 0x9d, 0x22, 0x20, 0x74, 0x15, 0x5b, 0x40, 0x69, 0x3c, 0x5a, 0x2e, 0xd3, 0x98, 0xe5, 0xf0, 0x02, 0xdb, 0x72, 0x86, + 0xcf, 0x29, 0x3c, 0x4f, 0xe3, 0x14, 0x1f, 0x0b, 0x7c, 0x8c, 0x2e, 0xf0, 0x31, 0x2b, 0xed, 0xbf, 0xcb, 0x0a, 0x58, + 0x9a, 0x00, 0xd9, 0xd5, 0x15, 0xc8, 0x7b, 0x4d, 0x82, 0xdd, 0x2d, 0x20, 0x16, 0x72, 0x8a, 0xf8, 0xe8, 0x0a, 0x33, + 0xc9, 0xc8, 0x8a, 0x59, 0x4b, 0x94, 0x5b, 0xa4, 0x55, 0x43, 0x70, 0xba, 0x72, 0xa7, 0x61, 0x3c, 0x78, 0x32, 0xbd, + 0xe4, 0x09, 0xbe, 0xb8, 0xb6, 0x25, 0xbe, 0x8a, 0x21, 0x88, 0x42, 0x8f, 0x88, 0xa1, 0x2e, 0xe3, 0xf2, 0x7b, 0x37, + 0x71, 0x68, 0xe3, 0x2c, 0x60, 0xbf, 0xa1, 0x16, 0xe0, 0x51, 0x9c, 0x88, 0xc6, 0x2b, 0xf0, 0x69, 0xe4, 0x09, 0x12, + 0x3a, 0xbb, 0x5b, 0x15, 0x6c, 0x00, 0xfc, 0x50, 0xdc, 0xb2, 0x76, 0x44, 0x0e, 0xa4, 0x2d, 0xca, 0xe9, 0xec, 0x5c, + 0x6e, 0x69, 0x19, 0xd9, 0x15, 0xb1, 0x72, 0x8d, 0x2a, 0xe5, 0x2c, 0xda, 0xe3, 0x28, 0x5d, 0xd5, 0x14, 0xa0, 0x9f, + 0x31, 0x36, 0xf2, 0x6c, 0x0b, 0x64, 0xa9, 0x78, 0xfe, 0x98, 0xb0, 0x33, 0x26, 0xbf, 0x94, 0xa2, 0x07, 0xd1, 0x95, + 0x43, 0x50, 0xc9, 0x5c, 0x5e, 0xe2, 0x94, 0xec, 0xa9, 0x70, 0x94, 0x94, 0xa8, 0x23, 0xe2, 0xd9, 0xc6, 0xa0, 0xcd, + 0x39, 0xda, 0xf5, 0x61, 0xbd, 0x0e, 0x58, 0xbb, 0xb6, 0x80, 0x97, 0xec, 0xb8, 0xdb, 0x91, 0x83, 0x01, 0xd8, 0x78, + 0x0c, 0xdb, 0x45, 0x45, 0x96, 0xb5, 0x2c, 0x10, 0x50, 0x81, 0x53, 0xea, 0xd9, 0xa2, 0x85, 0x5d, 0xb5, 0xd5, 0x4f, + 0x92, 0x38, 0x49, 0x36, 0xfc, 0xb4, 0x62, 0x2e, 0xe0, 0x8e, 0x75, 0x11, 0x69, 0x6d, 0xc8, 0x37, 0xfb, 0x5f, 0xfe, + 0xf9, 0xbf, 0xff, 0x67, 0x18, 0x98, 0xfa, 0xb9, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x06, 0xad, 0xe6, 0xe9, 0x2d, 0xed, + 0xfe, 0xf6, 0x1f, 0xfe, 0x0b, 0x34, 0xa3, 0x1b, 0x39, 0x6e, 0x79, 0x4c, 0x10, 0x0d, 0xd1, 0x08, 0xfa, 0x2c, 0x90, + 0x6a, 0x83, 0x5c, 0x3b, 0xd3, 0x3f, 0x21, 0xd8, 0x05, 0xcf, 0x66, 0x37, 0x82, 0x83, 0x50, 0x0f, 0x93, 0xac, 0x60, + 0x1a, 0x1e, 0x21, 0x6b, 0x3f, 0x0f, 0x20, 0x9a, 0x6b, 0x06, 0x5c, 0x5e, 0x58, 0x7a, 0x1c, 0xb1, 0x3c, 0xab, 0xc6, + 0x69, 0xac, 0x5e, 0xc1, 0x38, 0xa1, 0x43, 0x71, 0x05, 0x58, 0x2f, 0xf1, 0x04, 0x0f, 0x24, 0x10, 0xdc, 0xfa, 0x57, + 0xbe, 0x56, 0x0f, 0xa6, 0xf9, 0x53, 0x8c, 0x25, 0x42, 0x29, 0x6a, 0x04, 0xf8, 0x09, 0x72, 0xea, 0xa3, 0x7e, 0x8e, + 0x2e, 0xf4, 0x33, 0xca, 0x32, 0x31, 0x01, 0xe8, 0xa2, 0x29, 0x9a, 0x19, 0xe6, 0x0c, 0x22, 0x0d, 0xa0, 0xf2, 0x23, + 0x8d, 0x6c, 0x12, 0x21, 0xbc, 0x3e, 0x62, 0xd2, 0x25, 0x5e, 0xb1, 0x99, 0xe7, 0xec, 0x63, 0x92, 0x9d, 0x63, 0x70, + 0x0a, 0x89, 0x74, 0x5d, 0x7d, 0x69, 0xf9, 0xbe, 0x75, 0x4d, 0xf5, 0x04, 0x76, 0x01, 0xd8, 0x92, 0x3c, 0xd4, 0x54, + 0x6e, 0x20, 0xb5, 0x7a, 0x48, 0xc4, 0x72, 0x75, 0x8d, 0x44, 0x1b, 0x4b, 0x25, 0xd6, 0xf2, 0x11, 0x91, 0xf6, 0xda, + 0x15, 0x53, 0xdc, 0x3e, 0x0f, 0xc2, 0x76, 0xcd, 0x44, 0x58, 0x75, 0xeb, 0x4c, 0xc7, 0x6e, 0x70, 0x03, 0x65, 0x3e, + 0x13, 0xeb, 0xd5, 0x80, 0xdc, 0x01, 0x06, 0x0f, 0x34, 0x80, 0xd0, 0x11, 0x4f, 0x84, 0x84, 0x0f, 0x68, 0x2d, 0xa4, + 0xb8, 0xaf, 0x41, 0xe4, 0x59, 0xff, 0xe7, 0x7f, 0x1e, 0xd2, 0x1c, 0xd8, 0x26, 0xae, 0xf4, 0x2b, 0x99, 0x23, 0x27, + 0xb9, 0x52, 0xbf, 0xaf, 0xf0, 0x82, 0x1d, 0x8d, 0x82, 0x4a, 0xb6, 0x20, 0x9b, 0x80, 0x93, 0x4d, 0x60, 0xea, 0x61, + 0xf4, 0x32, 0xb2, 0xd5, 0xfa, 0xf6, 0x23, 0xf6, 0xab, 0x69, 0x4c, 0x2e, 0x35, 0x66, 0x2c, 0xad, 0x59, 0xa9, 0x31, + 0xf1, 0xfb, 0x29, 0x0d, 0x8d, 0x19, 0x5f, 0xab, 0x31, 0x91, 0x76, 0x7d, 0x1c, 0x39, 0xb4, 0x37, 0x31, 0x94, 0x62, + 0x68, 0x70, 0x8e, 0xb6, 0x09, 0xf8, 0xcf, 0xc3, 0x4f, 0xd2, 0x34, 0x21, 0xc8, 0x31, 0x96, 0x20, 0x8d, 0x2d, 0xb2, + 0x4b, 0x04, 0x76, 0xc7, 0xa5, 0xde, 0xf8, 0xf8, 0x68, 0x4c, 0xc0, 0xdd, 0xc5, 0x98, 0xa1, 0x58, 0x3e, 0xde, 0xc2, + 0x21, 0xc4, 0x7e, 0x29, 0xe9, 0x19, 0x90, 0xda, 0xa2, 0x71, 0xbc, 0x85, 0x08, 0x4a, 0x05, 0xf6, 0xdb, 0x37, 0x07, + 0x87, 0xb6, 0x77, 0x92, 0x8d, 0x2e, 0x03, 0x1b, 0x1c, 0x0a, 0x30, 0x3e, 0x5c, 0x9f, 0x4f, 0x58, 0xea, 0x28, 0x93, + 0x3e, 0x4b, 0xc0, 0xa5, 0xc9, 0x4e, 0xc5, 0x37, 0x14, 0x9a, 0x01, 0x75, 0x20, 0x26, 0x7d, 0x64, 0x71, 0x6f, 0x97, + 0x8b, 0xef, 0x8f, 0xf2, 0x1c, 0x1f, 0xf7, 0x30, 0xa5, 0x60, 0x77, 0x0b, 0x1e, 0xf0, 0xe5, 0x00, 0xd5, 0x91, 0x7e, + 0x13, 0x70, 0x16, 0xe2, 0x7d, 0x0b, 0xdb, 0x6f, 0xa9, 0xbe, 0x08, 0xc5, 0x9e, 0x64, 0x35, 0x6d, 0x75, 0x57, 0xa6, + 0x1c, 0x8d, 0x3d, 0x42, 0x4b, 0x8d, 0xac, 0x6e, 0x20, 0x05, 0x1f, 0xe9, 0x12, 0xa1, 0xfd, 0x8d, 0x22, 0x1a, 0xa5, + 0xd2, 0xd5, 0xb2, 0x0a, 0x27, 0x24, 0x2a, 0x8a, 0xc9, 0xe0, 0x27, 0x81, 0x7f, 0x6c, 0x7e, 0x2f, 0x4c, 0x7c, 0xda, + 0x47, 0x23, 0x79, 0xf4, 0x57, 0xef, 0x23, 0xf3, 0x2e, 0x8f, 0xa9, 0xa5, 0x72, 0x4e, 0x31, 0x6a, 0x82, 0x4e, 0x7c, + 0x5b, 0x45, 0x21, 0xc0, 0x3c, 0x49, 0xa2, 0x59, 0xc1, 0x02, 0xf5, 0x20, 0xfd, 0x54, 0x74, 0x77, 0x97, 0x03, 0x03, + 0xa6, 0x19, 0x53, 0xf2, 0xc9, 0xc2, 0x74, 0x64, 0x1f, 0x80, 0x23, 0x8b, 0x49, 0xf8, 0xad, 0x08, 0x94, 0x6f, 0x1a, + 0x24, 0x6c, 0xcc, 0x4b, 0x8e, 0xb7, 0xbc, 0x17, 0x2a, 0x72, 0xe0, 0x77, 0x77, 0xc0, 0xb9, 0xb5, 0x7c, 0xfc, 0xff, + 0xb6, 0xb1, 0x47, 0x41, 0x0a, 0xce, 0x28, 0x5d, 0xfb, 0xc0, 0x2b, 0x75, 0x00, 0x91, 0xf9, 0xbe, 0x30, 0x26, 0x1a, + 0x32, 0x8c, 0xaa, 0x94, 0x3c, 0x07, 0xb1, 0xed, 0xf1, 0xdc, 0x6c, 0x07, 0xe2, 0x76, 0x29, 0xb4, 0xb2, 0xec, 0xdc, + 0x6f, 0xbb, 0xd2, 0x05, 0x58, 0x6e, 0xac, 0x22, 0x24, 0xf5, 0xb7, 0x25, 0x0a, 0x19, 0xb1, 0x9c, 0x52, 0x68, 0x9a, + 0x85, 0xe8, 0x61, 0xe2, 0xb4, 0x1c, 0xe5, 0xb9, 0xd5, 0x50, 0x2c, 0x69, 0xc7, 0x1f, 0xd1, 0x8e, 0x27, 0x19, 0x36, + 0x58, 0x88, 0xb9, 0x87, 0x51, 0x32, 0x74, 0x10, 0x00, 0xab, 0x65, 0x3d, 0x02, 0x6a, 0xba, 0x2a, 0xd2, 0xe0, 0x3f, + 0x44, 0xe2, 0x96, 0x42, 0xe2, 0xad, 0xa0, 0xd2, 0xf1, 0xa0, 0x2c, 0x7b, 0xe7, 0xcc, 0x39, 0xfa, 0x5d, 0x5e, 0x1a, + 0x10, 0x77, 0x45, 0xf5, 0xf7, 0xf6, 0xda, 0xa5, 0x3b, 0xf0, 0x7e, 0x30, 0x3e, 0x62, 0x66, 0x2b, 0x86, 0xb6, 0x3d, + 0x58, 0x86, 0x1f, 0x42, 0xec, 0xfb, 0xca, 0xb1, 0xd1, 0xb2, 0xa4, 0x9a, 0xcb, 0x16, 0xf1, 0x97, 0x8d, 0xdd, 0x44, + 0xc8, 0xfb, 0xfb, 0xeb, 0x22, 0x17, 0xdf, 0xdc, 0x1e, 0xb9, 0x60, 0x77, 0x8c, 0x5c, 0x7c, 0xf3, 0x27, 0x47, 0x2e, + 0xbe, 0x6f, 0x46, 0x2e, 0x7e, 0xfb, 0x9c, 0xc8, 0x45, 0x9e, 0x9d, 0x17, 0x61, 0x47, 0x9e, 0x94, 0x83, 0xcc, 0xf9, + 0xfb, 0x84, 0x30, 0x60, 0xa2, 0x46, 0x00, 0x83, 0x22, 0x16, 0x22, 0xb9, 0x0f, 0x24, 0xbb, 0x8c, 0x17, 0xb4, 0x75, + 0x16, 0x5d, 0xeb, 0xbe, 0xba, 0x36, 0x04, 0x1e, 0x9b, 0xab, 0x2f, 0xbc, 0x75, 0x55, 0x44, 0x21, 0xa0, 0xef, 0x7e, + 0xea, 0x8e, 0xdd, 0x4d, 0x95, 0xbe, 0x65, 0x8e, 0xd0, 0x53, 0x51, 0x79, 0xc1, 0x3e, 0x0b, 0xfb, 0xdf, 0x1d, 0x75, + 0x7a, 0xdb, 0x9d, 0x29, 0xf4, 0x06, 0x2d, 0x0a, 0x6f, 0xed, 0xde, 0xf6, 0x36, 0xbe, 0x9d, 0xab, 0xb7, 0x2e, 0xbe, + 0xc5, 0xea, 0x6d, 0x07, 0xdf, 0x86, 0xea, 0xed, 0x01, 0xbe, 0x8d, 0xd4, 0xdb, 0x43, 0x7c, 0x3b, 0xb3, 0xcb, 0x23, + 0xae, 0x81, 0x7b, 0x08, 0x7c, 0x45, 0xc6, 0x7e, 0xa0, 0xca, 0x60, 0xd3, 0xe2, 0x75, 0xbb, 0xe8, 0x34, 0x88, 0x3d, + 0xe1, 0x14, 0x05, 0xb9, 0x77, 0x0e, 0x92, 0x3f, 0xa0, 0xec, 0xb2, 0xa7, 0xf8, 0xdd, 0x05, 0xf0, 0x21, 0x0e, 0xe3, + 0x29, 0x53, 0x1f, 0xa0, 0x55, 0x58, 0x83, 0x1d, 0x79, 0xd4, 0x1e, 0x94, 0x3d, 0xbd, 0x4e, 0x22, 0x60, 0xa2, 0x4e, + 0xef, 0x69, 0xe5, 0xaa, 0x3a, 0x31, 0x5d, 0x4b, 0xaf, 0xf0, 0x35, 0x7a, 0xc4, 0x70, 0xa3, 0xc7, 0x60, 0x21, 0xb5, + 0x2e, 0xc0, 0xf1, 0x5a, 0xa9, 0x5b, 0x10, 0x22, 0xad, 0x4d, 0x08, 0x27, 0xfd, 0x76, 0x18, 0x9d, 0xea, 0xe7, 0x57, + 0x60, 0xf0, 0x46, 0xa7, 0xec, 0x36, 0x3d, 0x43, 0x20, 0x9a, 0x3a, 0x46, 0x01, 0x41, 0xf6, 0x10, 0x2c, 0x0d, 0x3a, + 0x80, 0x52, 0xc7, 0x20, 0x75, 0xea, 0x5a, 0x87, 0xa6, 0xaf, 0x17, 0x01, 0x45, 0xab, 0x82, 0x5d, 0xb0, 0xbb, 0xa9, + 0x54, 0x50, 0x18, 0x2a, 0xb0, 0xe0, 0x46, 0x55, 0xa4, 0x7d, 0xe4, 0x6b, 0x15, 0x92, 0xa5, 0x74, 0x91, 0x19, 0xcd, + 0xd7, 0xa1, 0xfc, 0x65, 0xf1, 0xf8, 0x45, 0x67, 0x88, 0x7f, 0xa4, 0xf0, 0xfd, 0x62, 0x3c, 0x1e, 0xdf, 0xa8, 0x9b, + 0xbe, 0x18, 0x8d, 0x59, 0x97, 0xed, 0xf4, 0x30, 0xd2, 0xdb, 0x92, 0xe2, 0xb0, 0x53, 0x12, 0xed, 0x16, 0x77, 0x6b, + 0x8c, 0x92, 0x13, 0xd4, 0xd5, 0xdd, 0x95, 0x58, 0x09, 0x54, 0x59, 0x80, 0xf0, 0x3e, 0x8d, 0xd3, 0xa0, 0x5d, 0xfa, + 0x67, 0x52, 0xea, 0x7f, 0xf1, 0xe8, 0xd1, 0xa3, 0xd2, 0x1f, 0xa9, 0xb7, 0xf6, 0x68, 0x54, 0xfa, 0xc3, 0x85, 0x46, + 0xa3, 0xdd, 0x1e, 0x8f, 0x4b, 0x3f, 0x56, 0x05, 0xdb, 0xdd, 0xe1, 0x68, 0xbb, 0x5b, 0xfa, 0xe7, 0x46, 0x8b, 0xd2, + 0x67, 0xf2, 0x2d, 0x67, 0xa3, 0x5a, 0xb8, 0xf8, 0x61, 0x1b, 0x2a, 0x05, 0xa3, 0x2d, 0xd0, 0xc9, 0x13, 0x8f, 0x41, + 0x34, 0xe7, 0x59, 0x79, 0x0c, 0xb2, 0x9d, 0x81, 0x7c, 0x1e, 0x4b, 0xd9, 0x2e, 0xbe, 0xef, 0x8a, 0x12, 0xfd, 0x37, + 0x53, 0xa2, 0x23, 0x33, 0x93, 0x34, 0x67, 0xa4, 0x07, 0x9a, 0xd5, 0xc8, 0x59, 0x54, 0xfd, 0x5b, 0xc8, 0x2a, 0x61, + 0x8f, 0xd2, 0x06, 0x5b, 0x0a, 0x19, 0xff, 0xed, 0x75, 0x32, 0xfe, 0xbb, 0xdb, 0x65, 0xfc, 0xc9, 0xdd, 0x44, 0xfc, + 0x77, 0x7f, 0xb2, 0x88, 0xff, 0xd6, 0x14, 0xf1, 0x42, 0x88, 0x5d, 0x82, 0xf5, 0x4a, 0x66, 0xeb, 0x49, 0x76, 0xd1, + 0xc2, 0x2d, 0x91, 0xdb, 0x24, 0x3d, 0xd7, 0xef, 0x24, 0xfc, 0x57, 0xe4, 0xff, 0xa8, 0xc1, 0x8c, 0x8f, 0xc5, 0xf2, + 0xec, 0xf4, 0x34, 0x61, 0x4a, 0xc6, 0x1b, 0x15, 0x64, 0x0e, 0xbf, 0x49, 0x43, 0xfb, 0x0d, 0x38, 0xa8, 0x46, 0xc9, + 0x78, 0x0c, 0x45, 0xe3, 0xb1, 0xad, 0xf2, 0x63, 0x41, 0x9e, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, 0xf5, 0xd7, + 0x66, 0x99, 0x59, 0x20, 0x23, 0x51, 0xa6, 0x31, 0x21, 0x6b, 0x46, 0x71, 0x81, 0x7b, 0xb0, 0xfa, 0xb8, 0x2d, 0xda, + 0x2b, 0x53, 0x50, 0x2a, 0xf1, 0x10, 0xbf, 0x9a, 0xd2, 0xfc, 0x90, 0x88, 0xc8, 0x65, 0x5e, 0x46, 0xae, 0x3a, 0xef, + 0x34, 0xbe, 0x59, 0x57, 0x9d, 0x71, 0xc2, 0xe2, 0xcb, 0x7c, 0x86, 0xc7, 0x97, 0x2f, 0x46, 0xce, 0x25, 0xd8, 0xb1, + 0x71, 0xf1, 0x26, 0x6d, 0xe4, 0x89, 0x09, 0xb0, 0xc3, 0xd0, 0xc4, 0xb4, 0x14, 0x04, 0xab, 0x12, 0xe6, 0xab, 0xca, + 0x9e, 0xd1, 0x49, 0xa6, 0x13, 0xe1, 0x90, 0xfd, 0x1a, 0x59, 0x02, 0x73, 0x30, 0xa9, 0x0b, 0xe9, 0xe3, 0xe5, 0x22, + 0xc9, 0xe2, 0x4c, 0x7e, 0xe5, 0x9a, 0xa2, 0xff, 0x0b, 0xa9, 0x3f, 0xe4, 0xf1, 0x7b, 0xd5, 0x13, 0x03, 0xee, 0x62, + 0x86, 0x51, 0xa9, 0x82, 0xec, 0x40, 0xb8, 0x19, 0x7e, 0x92, 0x47, 0x0c, 0xa1, 0x62, 0xd9, 0x15, 0xf5, 0xf0, 0x13, + 0x28, 0xd5, 0x97, 0x21, 0x6b, 0x5f, 0x11, 0x6c, 0xf0, 0x00, 0x7e, 0xdd, 0x9f, 0xa3, 0x36, 0xc8, 0xe6, 0xdc, 0x71, + 0xa8, 0x95, 0xe3, 0x96, 0x5e, 0x77, 0x07, 0x3c, 0x4a, 0xd7, 0x17, 0xdf, 0xfd, 0x71, 0x74, 0x67, 0x89, 0xef, 0x75, + 0xa1, 0xf3, 0xa5, 0xef, 0x70, 0x69, 0x12, 0xe3, 0x87, 0x42, 0x04, 0xa2, 0xc6, 0x5d, 0x11, 0xb5, 0x88, 0xcd, 0x77, + 0x5f, 0xb9, 0x6f, 0x06, 0x61, 0xdd, 0x55, 0x1c, 0x2c, 0xe3, 0x64, 0xf5, 0x42, 0x6c, 0x2b, 0xac, 0x9a, 0x65, 0x70, + 0x6e, 0xd1, 0x99, 0xc5, 0xb9, 0x11, 0x77, 0xae, 0x6d, 0x83, 0x52, 0x05, 0x9e, 0x45, 0xf4, 0xf8, 0x12, 0x63, 0xa4, + 0xc2, 0xf7, 0x55, 0x40, 0xd7, 0xbd, 0x4e, 0x03, 0x72, 0xf4, 0x47, 0x35, 0xa3, 0xab, 0x2a, 0x55, 0x50, 0x9a, 0xa7, + 0x04, 0x06, 0x32, 0x14, 0x00, 0x86, 0x35, 0x4e, 0x85, 0xde, 0x82, 0x69, 0x48, 0x00, 0x6b, 0x8f, 0x0c, 0xdd, 0x12, + 0x5b, 0x81, 0x2d, 0xa4, 0x05, 0x28, 0x3d, 0xec, 0xb0, 0x67, 0xd5, 0x40, 0x4f, 0x97, 0xe3, 0xc6, 0x37, 0x39, 0x69, + 0x97, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, 0xcb, 0x05, 0x48, 0xf9, 0x93, 0x4f, 0x31, 0x07, 0x9b, 0x7a, 0xd6, + 0xc2, 0x28, 0x08, 0x85, 0x31, 0xa5, 0x3a, 0xa4, 0xa3, 0x47, 0x71, 0x45, 0xa8, 0x37, 0x2f, 0xd0, 0x97, 0x23, 0xa7, + 0x25, 0x48, 0xb3, 0x94, 0xf5, 0xea, 0x47, 0xcc, 0xa6, 0xdf, 0xa0, 0x88, 0x35, 0x58, 0x64, 0xe8, 0xfb, 0xf1, 0x4b, + 0xf0, 0xfd, 0x84, 0x1a, 0x6d, 0x2b, 0xa7, 0xa1, 0xbd, 0xb2, 0x7d, 0x20, 0x69, 0xbb, 0x49, 0xd6, 0x42, 0xbe, 0xec, + 0x1c, 0x5d, 0xe7, 0xdc, 0xdc, 0x76, 0xe0, 0xda, 0xdd, 0xd9, 0xf1, 0xd4, 0x3f, 0xe3, 0xa4, 0xba, 0x59, 0x4c, 0x07, + 0xae, 0x77, 0x81, 0x2c, 0x88, 0xc6, 0xf8, 0x45, 0xbd, 0xbb, 0xb4, 0x3c, 0xa1, 0x6c, 0xc7, 0x05, 0xaa, 0xf5, 0xa0, + 0xf3, 0x08, 0xbc, 0xb5, 0x3b, 0x0f, 0x7f, 0x33, 0xfa, 0xa5, 0xa4, 0x91, 0xba, 0xb4, 0x6a, 0xdb, 0x3d, 0x94, 0x17, + 0x49, 0x74, 0x09, 0x4e, 0x23, 0xd9, 0x18, 0x27, 0x18, 0xc4, 0xed, 0xcd, 0x32, 0x99, 0x39, 0x90, 0xb3, 0x84, 0x7e, + 0x6d, 0x85, 0x5c, 0x8a, 0xed, 0x07, 0xb3, 0x0b, 0xb5, 0x1a, 0x9d, 0x46, 0x46, 0xc0, 0x9f, 0x7a, 0xf0, 0x7f, 0x7d, + 0xa6, 0x41, 0xfd, 0xf0, 0x7a, 0x07, 0x60, 0x10, 0x86, 0x4d, 0x2b, 0x17, 0x50, 0xb5, 0xa1, 0xc4, 0x48, 0x7d, 0xa8, + 0x06, 0xb2, 0xfc, 0x6d, 0x50, 0x95, 0x51, 0xc1, 0x7a, 0xf8, 0x69, 0xc3, 0x18, 0x5c, 0x53, 0x69, 0x3c, 0x4d, 0xe3, + 0xd1, 0x28, 0x61, 0x3d, 0x65, 0x1f, 0x59, 0x9d, 0x07, 0x98, 0x39, 0x60, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe2, 0x69, + 0x3a, 0x45, 0x27, 0x60, 0xaf, 0xe1, 0xf7, 0x09, 0x57, 0x92, 0x53, 0x1e, 0xa9, 0xb7, 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, 0xe0, 0xe9, 0xa2, 0xb2, + 0x8e, 0xcc, 0xff, 0x9f, 0xdd, 0xf2, 0xd5, 0xea, 0xdd, 0xf2, 0x95, 0xda, 0x2d, 0x37, 0x73, 0xec, 0x17, 0xe3, 0x0e, + 0xfe, 0xe9, 0x55, 0x08, 0xc1, 0xaa, 0x00, 0x39, 0x2c, 0xb4, 0x8b, 0x5b, 0x5d, 0xf8, 0x8f, 0x86, 0x6e, 0x7b, 0xf8, + 0xc7, 0x07, 0x0b, 0xb0, 0x6d, 0x61, 0x21, 0xfe, 0x6b, 0xd7, 0xaa, 0x3a, 0xf7, 0xb1, 0x0e, 0x7b, 0xed, 0x2c, 0xd7, + 0x75, 0x6f, 0xde, 0xb4, 0x20, 0xaf, 0xb8, 0x13, 0x28, 0x61, 0x0c, 0xae, 0x5a, 0x74, 0x72, 0x02, 0xa5, 0xe3, 0x6c, + 0x38, 0x2f, 0xfe, 0x51, 0xc2, 0x2f, 0x89, 0x78, 0xe3, 0x96, 0x6e, 0x8c, 0xa3, 0xba, 0x8a, 0x34, 0x14, 0x35, 0xc2, + 0x42, 0xaf, 0x53, 0x50, 0x00, 0x63, 0x32, 0xa7, 0xeb, 0xdf, 0x5f, 0xb1, 0x09, 0xfe, 0x2e, 0x6b, 0xb3, 0x12, 0x99, + 0x7f, 0x2d, 0x31, 0x6e, 0x24, 0xc2, 0x67, 0xd1, 0xc0, 0x5c, 0xc3, 0xf6, 0xa3, 0xd5, 0xe0, 0x1e, 0xa9, 0x99, 0x06, + 0x4a, 0x29, 0x48, 0xbd, 0x03, 0x5e, 0x40, 0x34, 0x4f, 0xf8, 0xcd, 0xa3, 0x5e, 0xc7, 0x19, 0x4b, 0xa3, 0xde, 0x20, + 0xd0, 0xab, 0xb6, 0x77, 0x94, 0xd2, 0x5f, 0x7c, 0x75, 0x1f, 0xff, 0x88, 0xc0, 0xd7, 0x49, 0xe5, 0x1b, 0x89, 0xd8, + 0x00, 0xfa, 0x46, 0xeb, 0x35, 0xe7, 0x47, 0x68, 0x70, 0xf2, 0x7f, 0xee, 0xda, 0x1a, 0x8d, 0xf5, 0x3b, 0x35, 0x97, + 0x56, 0xe9, 0x67, 0xb5, 0xfe, 0xbc, 0xc1, 0xef, 0xd8, 0x76, 0x28, 0x1c, 0x82, 0x7a, 0x5b, 0xf9, 0x2b, 0x44, 0x96, + 0x1a, 0x2b, 0x8a, 0xdf, 0xb5, 0x7d, 0x65, 0x12, 0x53, 0x8f, 0xf5, 0xf0, 0x44, 0x3b, 0x91, 0xf2, 0xcc, 0x19, 0x7b, + 0x08, 0x3f, 0xf2, 0x27, 0x16, 0xde, 0xc3, 0xaf, 0x6f, 0x59, 0x17, 0xd3, 0x24, 0x05, 0xb3, 0x6a, 0xc2, 0xf9, 0x2c, + 0xd8, 0xda, 0x3a, 0x3f, 0x3f, 0xf7, 0xcf, 0xb7, 0xfd, 0x2c, 0x3f, 0xdd, 0xea, 0xb6, 0xdb, 0x6d, 0xfc, 0x68, 0x92, + 0x6d, 0x9d, 0xc5, 0xec, 0xfc, 0x31, 0xb8, 0x1f, 0xf6, 0x43, 0xeb, 0x91, 0xf5, 0x70, 0xdb, 0xda, 0x79, 0x60, 0x5b, + 0xa4, 0x00, 0xa0, 0x64, 0xdb, 0xb6, 0x84, 0x02, 0x08, 0x6d, 0x28, 0xde, 0xdb, 0x3d, 0x55, 0x36, 0x1c, 0x26, 0xa5, + 0x0b, 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, 0x81, 0x27, 0x70, 0x0b, + 0x26, 0x16, 0x3c, 0xbf, 0xea, 0x3e, 0x04, 0x1d, 0x77, 0xd6, 0xba, 0x3f, 0x6c, 0xb7, 0x3a, 0x56, 0xa7, 0xd5, 0xf5, + 0x1f, 0x5a, 0x5d, 0xf1, 0x3f, 0xc8, 0xc8, 0x6d, 0xab, 0x03, 0x4f, 0xdb, 0x16, 0xbc, 0x9f, 0xdd, 0x17, 0x29, 0x11, + 0x91, 0xbd, 0xb5, 0xb7, 0x8b, 0xbf, 0x43, 0x08, 0x90, 0xfa, 0xca, 0x16, 0xbf, 0xf5, 0xcc, 0xfe, 0xc2, 0x2c, 0xed, + 0x3c, 0x5a, 0x59, 0xdc, 0x7d, 0xb8, 0xb2, 0x78, 0xfb, 0xc1, 0xca, 0xe2, 0xfb, 0x3b, 0xf5, 0xe2, 0xad, 0x53, 0x51, + 0xa5, 0xe5, 0x42, 0x68, 0x4f, 0x23, 0x60, 0x94, 0x0b, 0xa7, 0x03, 0x70, 0xb6, 0xad, 0x16, 0xfe, 0x78, 0xd8, 0x75, + 0x75, 0xaf, 0x13, 0xec, 0xa5, 0xb1, 0x7c, 0xf8, 0x08, 0xb0, 0x7c, 0xde, 0x7d, 0x30, 0xc4, 0x76, 0x84, 0x28, 0xfc, + 0x3b, 0xdb, 0x7e, 0x34, 0x04, 0x8d, 0x60, 0xe1, 0x3f, 0xf8, 0x33, 0xd9, 0xe9, 0x0e, 0xc5, 0x4b, 0x1b, 0xeb, 0x3f, + 0x74, 0x1e, 0x16, 0xd0, 0x14, 0xff, 0xfc, 0xae, 0x4d, 0x68, 0x34, 0xe0, 0xcd, 0x71, 0xef, 0x03, 0x8d, 0x1e, 0x4d, + 0xba, 0xfe, 0x57, 0x67, 0x0f, 0xfd, 0x47, 0x93, 0xce, 0xc3, 0x0f, 0xe2, 0x2d, 0x01, 0x0a, 0x7e, 0x85, 0xff, 0x3e, + 0x6c, 0xb7, 0x27, 0xad, 0x8e, 0xff, 0xe8, 0x6c, 0xdb, 0xdf, 0x4e, 0x5a, 0x0f, 0xfc, 0x47, 0xf8, 0xaf, 0x1a, 0x6e, + 0x92, 0x4d, 0x99, 0x6d, 0xe1, 0x7a, 0x37, 0xfc, 0x5e, 0x73, 0x8e, 0xee, 0x7d, 0x6b, 0xe7, 0xfe, 0xf3, 0x47, 0xb0, + 0x46, 0x93, 0x4e, 0x17, 0xfe, 0xbf, 0xee, 0xf1, 0x03, 0x12, 0x5e, 0x0e, 0x1c, 0x31, 0x4c, 0x27, 0x55, 0x84, 0xa3, + 0x0f, 0x78, 0xdd, 0xf3, 0x7e, 0xbc, 0x2e, 0x00, 0xf2, 0xd7, 0xdb, 0x03, 0x20, 0x3f, 0xdc, 0x31, 0xc8, 0xfd, 0xd7, + 0x3f, 0x39, 0x02, 0xf2, 0x63, 0x33, 0xc8, 0xbd, 0xcf, 0x96, 0x02, 0x1d, 0x4d, 0x67, 0xed, 0x39, 0x73, 0x8e, 0x7e, + 0x62, 0x03, 0x4c, 0x9d, 0x86, 0xd6, 0x3f, 0xd4, 0xe2, 0x41, 0x19, 0x6e, 0xe4, 0x7d, 0x26, 0x76, 0x32, 0xe3, 0xd7, + 0x10, 0x84, 0xf3, 0x5b, 0x09, 0xf2, 0xe2, 0x6e, 0xf4, 0xe0, 0xfc, 0xcf, 0xa5, 0x07, 0x7d, 0xbd, 0x5f, 0xd1, 0xa3, + 0x16, 0x71, 0xa7, 0x88, 0x01, 0x39, 0xfa, 0x07, 0xf4, 0xee, 0xd8, 0x5b, 0x0c, 0xdf, 0x0a, 0x5b, 0xe4, 0x03, 0xbe, + 0xfb, 0x9c, 0xd3, 0x01, 0x91, 0x59, 0x1c, 0xda, 0x32, 0x00, 0x33, 0xc3, 0xef, 0xd3, 0xaa, 0x97, 0x33, 0x71, 0x7b, + 0x25, 0xa4, 0xab, 0x67, 0x3b, 0x3a, 0x78, 0x83, 0xc9, 0xde, 0xe1, 0x22, 0xe3, 0x11, 0xfe, 0x02, 0x24, 0x1e, 0xf3, + 0x04, 0x2f, 0xc6, 0xca, 0x4b, 0x64, 0x98, 0x9b, 0xfc, 0x1d, 0xe6, 0x55, 0xab, 0x43, 0x82, 0x29, 0x06, 0x0c, 0x5e, + 0xb1, 0x51, 0x1c, 0x39, 0xb6, 0x33, 0x83, 0x1d, 0x0b, 0x63, 0xb6, 0x6a, 0x49, 0xcd, 0x94, 0xcf, 0xec, 0xda, 0xea, + 0x77, 0xee, 0xe4, 0xf8, 0x0d, 0xb3, 0xf0, 0x48, 0x06, 0x18, 0x6d, 0xe9, 0x01, 0xc0, 0xf8, 0xaa, 0x24, 0x47, 0x61, + 0x5f, 0x59, 0x0d, 0xb6, 0x30, 0x1b, 0x3a, 0x7e, 0x17, 0xdc, 0x08, 0x2a, 0xc6, 0x6f, 0x42, 0xfd, 0xe8, 0xb4, 0xb6, + 0xc1, 0xac, 0x31, 0xba, 0xe9, 0x81, 0x06, 0x4b, 0x61, 0x24, 0x11, 0x1c, 0x68, 0x94, 0x7a, 0xfa, 0x97, 0x90, 0x55, + 0xe1, 0xa2, 0xe2, 0xf1, 0xe5, 0xa1, 0xbc, 0xf7, 0x6d, 0x63, 0xe4, 0x96, 0x22, 0xf6, 0xd5, 0x37, 0xa7, 0x36, 0x41, + 0x5d, 0xd0, 0x6f, 0x82, 0xa4, 0x73, 0x6f, 0xd4, 0x08, 0x98, 0x76, 0x6d, 0x49, 0xcf, 0x21, 0xb4, 0x85, 0x3e, 0x18, + 0xb1, 0xb3, 0x78, 0x28, 0xc5, 0xba, 0x67, 0xc9, 0xeb, 0x22, 0x2d, 0xc2, 0x22, 0xec, 0x78, 0xc2, 0x77, 0x86, 0x17, + 0xd4, 0x6a, 0x61, 0x9a, 0xd9, 0x7b, 0xf7, 0x7a, 0x1a, 0x92, 0x7a, 0xc6, 0xba, 0x8d, 0xbf, 0x96, 0xf2, 0x08, 0x7c, + 0xb5, 0x7f, 0x0c, 0xef, 0xe1, 0x2f, 0xa6, 0xbc, 0x37, 0xb0, 0x5d, 0x9f, 0x84, 0xe2, 0xbd, 0xea, 0xb7, 0x53, 0xa2, + 0x44, 0xd8, 0x04, 0xfd, 0xe5, 0xdd, 0x55, 0x91, 0x49, 0xa5, 0xd5, 0xdd, 0xa9, 0x94, 0x16, 0x3c, 0x1b, 0x52, 0x0a, + 0x04, 0x68, 0xd7, 0xdf, 0x31, 0x44, 0xe1, 0x59, 0x0b, 0x7f, 0xd6, 0x84, 0xe1, 0x7d, 0x68, 0xa0, 0xa4, 0xe1, 0x4b, + 0x68, 0xbe, 0x2d, 0x04, 0x2f, 0xf4, 0xfb, 0x89, 0x44, 0x95, 0x10, 0x53, 0x75, 0x8e, 0x59, 0x71, 0x88, 0x24, 0x72, + 0x04, 0x6c, 0xcf, 0x88, 0x37, 0x09, 0x76, 0x95, 0xd1, 0x94, 0xa7, 0xd0, 0xd7, 0xd1, 0x9f, 0x72, 0x5e, 0x55, 0xe7, + 0xd5, 0x76, 0xce, 0x8a, 0x29, 0x90, 0xe1, 0x1b, 0x07, 0x55, 0x74, 0x7d, 0x41, 0x7c, 0xd2, 0x4c, 0x6c, 0xe3, 0xea, + 0xa3, 0x6f, 0x2b, 0x32, 0x70, 0xcd, 0x4d, 0xc1, 0x2a, 0xa6, 0xa1, 0x7d, 0x81, 0x69, 0x33, 0xf8, 0xb3, 0x2a, 0x56, + 0x0f, 0x92, 0xa1, 0xfc, 0x24, 0xc2, 0xdf, 0xc8, 0x42, 0x3f, 0xca, 0x6a, 0x03, 0x72, 0xfa, 0x66, 0x25, 0x41, 0xfa, + 0x62, 0x54, 0x36, 0x91, 0x00, 0x7b, 0x01, 0x7f, 0xc1, 0x5f, 0x75, 0x5d, 0x42, 0xde, 0x83, 0xc4, 0x9c, 0x82, 0x51, + 0x9c, 0xd3, 0xf5, 0x5a, 0x85, 0x7f, 0x2d, 0xa2, 0x59, 0x91, 0x9a, 0x76, 0x25, 0x2b, 0xfa, 0x36, 0x16, 0xd9, 0x81, + 0x4c, 0x48, 0x33, 0x3f, 0xda, 0x6c, 0xde, 0x7f, 0x1c, 0x89, 0x5c, 0x34, 0xfc, 0xa8, 0xbd, 0x2d, 0x88, 0x6c, 0x83, + 0x18, 0xbb, 0x16, 0x27, 0x32, 0x6e, 0xf0, 0xda, 0x60, 0xf5, 0x5b, 0x8a, 0xcc, 0x0d, 0x6f, 0x9b, 0xab, 0xa5, 0xc7, + 0xa5, 0x75, 0x70, 0x65, 0xfc, 0xee, 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0x4f, 0x72, 0x0c, 0xb1, 0xe0, 0x75, 0xd8, + 0xb6, 0x5b, 0x82, 0xe4, 0x31, 0x7e, 0x8d, 0x93, 0x20, 0xbd, 0x0f, 0x85, 0x55, 0xd2, 0xd6, 0xee, 0xa4, 0xbb, 0xf7, + 0xe6, 0x70, 0xdf, 0x12, 0xbb, 0x79, 0x77, 0x0b, 0x5e, 0x77, 0xc9, 0x1d, 0x16, 0xf9, 0x19, 0xa1, 0xc8, 0xcf, 0xb0, + 0x44, 0x52, 0x57, 0x68, 0x6f, 0x09, 0x34, 0x6d, 0x8b, 0xa5, 0x43, 0x11, 0xc3, 0x9b, 0x82, 0xbb, 0x10, 0xe3, 0x87, + 0xbd, 0xb6, 0xb0, 0x5b, 0x0b, 0x57, 0x1a, 0xb6, 0x80, 0x08, 0xf8, 0x29, 0x03, 0x81, 0xa7, 0x2a, 0xe2, 0x07, 0x6a, + 0x9d, 0xa9, 0x64, 0x17, 0x39, 0x94, 0xce, 0x4b, 0x5d, 0x6e, 0x5d, 0xcc, 0x4f, 0xa6, 0x20, 0x87, 0x54, 0x82, 0xca, + 0x7b, 0xd9, 0x61, 0x97, 0xa6, 0xa2, 0x0c, 0x94, 0xa5, 0x88, 0xe4, 0xa4, 0xb3, 0xb7, 0x1b, 0x49, 0x7b, 0x07, 0xf7, + 0x6e, 0x01, 0x9b, 0x17, 0xd4, 0x1c, 0x1a, 0x15, 0x7e, 0x9c, 0x6d, 0x9d, 0xb3, 0x93, 0x56, 0x34, 0x8b, 0xab, 0xf0, + 0x1f, 0x6a, 0xbf, 0xbd, 0x5d, 0xa5, 0x08, 0x65, 0xaa, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, 0x1c, 0x31, 0x68, + 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, 0xb4, 0xf7, 0xfe, + 0xf6, 0x3f, 0xfe, 0x97, 0x0c, 0x2b, 0x02, 0x59, 0x01, 0x2c, 0x4c, 0x83, 0xa9, 0x4e, 0x18, 0xd9, 0x05, 0x38, 0xa2, + 0xf1, 0xa8, 0x35, 0x89, 0x92, 0x31, 0x40, 0x50, 0x30, 0x71, 0x9d, 0x49, 0xd6, 0x03, 0x17, 0x48, 0xb0, 0xcc, 0xc3, + 0x79, 0x09, 0x5e, 0xbd, 0x08, 0x57, 0xec, 0x0f, 0xe5, 0xae, 0xaa, 0x9c, 0x61, 0x62, 0x68, 0x23, 0x93, 0xd5, 0xe0, + 0xb9, 0x5a, 0x36, 0xab, 0xfa, 0x25, 0x49, 0x52, 0x78, 0xb0, 0x5a, 0x2a, 0x2b, 0xb4, 0xd4, 0x07, 0x21, 0xff, 0xf2, + 0xcf, 0xff, 0xf1, 0xbf, 0xaa, 0x57, 0x3c, 0xdf, 0xf8, 0xdb, 0x3f, 0xfd, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x13, 0x26, + 0x07, 0xcb, 0x33, 0x10, 0xda, 0x4a, 0x56, 0x75, 0x00, 0x22, 0xf6, 0x94, 0x55, 0x39, 0x1c, 0xf5, 0x94, 0xd7, 0x03, + 0x9a, 0x90, 0x78, 0x53, 0x42, 0x47, 0x7c, 0x4d, 0x29, 0xd2, 0x44, 0xb5, 0x1b, 0xc8, 0x07, 0x4b, 0x69, 0xd1, 0xb1, + 0xbe, 0xbd, 0xd3, 0xb6, 0xab, 0xe5, 0xdd, 0x33, 0xfa, 0x6e, 0xe1, 0xc2, 0xdc, 0x29, 0x03, 0xc7, 0xd7, 0xcb, 0xb6, + 0x50, 0x61, 0x2c, 0x2c, 0x29, 0xab, 0x72, 0x0b, 0xe3, 0xcb, 0x0b, 0x7c, 0x0d, 0xba, 0x46, 0x31, 0xad, 0x72, 0xad, + 0x4f, 0xef, 0xd7, 0x39, 0x20, 0x3a, 0xc6, 0xa5, 0x11, 0xc1, 0x32, 0x3a, 0x3b, 0x6d, 0xa1, 0x75, 0x92, 0x5c, 0x96, + 0x34, 0x8a, 0xf0, 0x66, 0xee, 0x3f, 0xfa, 0x87, 0xf2, 0x2f, 0x53, 0xb4, 0x0a, 0x2c, 0x67, 0x1a, 0x5d, 0x48, 0x1f, + 0xe7, 0x41, 0xbb, 0x3d, 0xbb, 0x70, 0x17, 0xd5, 0x0c, 0xde, 0x75, 0x93, 0x51, 0x80, 0xcd, 0x1c, 0x90, 0x0e, 0x5d, + 0x75, 0x2c, 0x0f, 0xcc, 0xfa, 0x36, 0x86, 0x7e, 0xca, 0xf2, 0xcb, 0x05, 0x85, 0x93, 0xe2, 0xdf, 0xf1, 0x70, 0x54, + 0x46, 0xde, 0xa0, 0xc4, 0xc0, 0x62, 0x61, 0xf4, 0xea, 0x8a, 0x5e, 0x93, 0xce, 0x72, 0x6e, 0x8a, 0x79, 0xb8, 0x6b, + 0x1e, 0xcb, 0xde, 0xc7, 0x83, 0xd6, 0x49, 0xc7, 0x9b, 0x74, 0x17, 0x7a, 0x78, 0xce, 0xb3, 0xa9, 0x79, 0x9a, 0xcb, + 0x22, 0x36, 0x64, 0x63, 0x15, 0xb1, 0x94, 0xf5, 0xe2, 0xa4, 0xb6, 0xfc, 0x02, 0xb7, 0x1b, 0xd0, 0x36, 0x8b, 0x78, + 0x40, 0x4c, 0xdb, 0x33, 0xcf, 0x7b, 0x23, 0x3c, 0x49, 0xcf, 0x16, 0xc6, 0x5c, 0x3d, 0xd1, 0x14, 0xe3, 0x82, 0xf5, + 0xbc, 0x9f, 0xd2, 0xa7, 0xee, 0xe6, 0x50, 0x22, 0xac, 0xf0, 0x42, 0x1e, 0xa3, 0xbe, 0xab, 0xf9, 0xe3, 0x52, 0x14, + 0x83, 0x0b, 0xbc, 0xb2, 0x5e, 0xa8, 0x45, 0x51, 0xfb, 0x02, 0xac, 0x1d, 0x02, 0xd3, 0x6e, 0xb6, 0xa2, 0x42, 0x6c, + 0xf5, 0x2e, 0x7c, 0xa1, 0x6d, 0xef, 0x68, 0x36, 0xa3, 0x86, 0x2e, 0x70, 0x23, 0xd9, 0xd0, 0x28, 0x29, 0x28, 0x45, + 0x40, 0x9c, 0xc8, 0xcb, 0x36, 0x92, 0x6d, 0xc5, 0x93, 0x3c, 0xab, 0xa7, 0xdf, 0xb9, 0xfd, 0xff, 0x00, 0x52, 0x99, + 0xa6, 0xd8, 0x89, 0x7b, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f0a7efd12f..5c06a97d47 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -839,6 +839,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); + int8_t accuracy = traits.get_temperature_accuracy_decimals(); char __buf[16]; if (start_config == DETAIL_ALL) { @@ -873,12 +874,15 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf } } + bool has_state = false; root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode)); - root["max_temp"] = traits.get_visual_max_temperature(); - root["min_temp"] = traits.get_visual_min_temperature(); + root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), accuracy); + root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), accuracy); root["step"] = traits.get_visual_temperature_step(); if (traits.get_supports_action()) { root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action)); + root["state"] = root["action"]; + has_state = true; } if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) { root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value())); @@ -896,14 +900,23 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode)); } if (traits.get_supports_current_temperature()) { - root["current_temperature"] = obj->current_temperature; + if (!std::isnan(obj->current_temperature)) { + root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, accuracy); + } else { + root["current_temperature"] = "NA"; + } } if (traits.get_supports_two_point_target_temperature()) { - root["current_temperature_low"] = obj->target_temperature_low; - root["current_temperature_high"] = obj->target_temperature_low; + root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, accuracy); + root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, accuracy); + if (!has_state) { + root["state"] = + value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f, accuracy); + } } else { - root["target_temperature"] = obj->target_temperature; - root["state"] = obj->target_temperature; + root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, accuracy); + if (!has_state) + root["state"] = root["target_temperature"]; } }); } From 4759b4fe2e00f704e64d5b102bd907e1c095316b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Aug 2022 13:57:42 +1200 Subject: [PATCH 15/69] Add vector include (#3707) --- esphome/components/e131/e131.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 648cfb4585..8bf8999c21 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -4,9 +4,10 @@ #include "esphome/core/component.h" +#include #include #include -#include +#include class UDP; From 7f41b7cd939c1e488fcc2fb66490ab007be6c54a Mon Sep 17 00:00:00 2001 From: Peter Galantha Date: Wed, 10 Aug 2022 19:00:09 -0700 Subject: [PATCH 16/69] Add state_class total (#3608) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api.proto | 1 + esphome/components/api/api_pb2.cpp | 2 ++ esphome/components/api/api_pb2.h | 1 + esphome/components/sensor/__init__.py | 1 + esphome/components/sensor/sensor.cpp | 2 ++ esphome/components/sensor/sensor.h | 1 + esphome/const.py | 3 +++ 7 files changed, 11 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 3e9a62f3d8..88a74540d0 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -473,6 +473,7 @@ enum SensorStateClass { STATE_CLASS_NONE = 0; STATE_CLASS_MEASUREMENT = 1; STATE_CLASS_TOTAL_INCREASING = 2; + STATE_CLASS_TOTAL = 3; } enum SensorLastResetType { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 70f909c07a..b91c9bd600 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -108,6 +108,8 @@ template<> const char *proto_enum_to_string(enums::Sens return "STATE_CLASS_MEASUREMENT"; case enums::STATE_CLASS_TOTAL_INCREASING: return "STATE_CLASS_TOTAL_INCREASING"; + case enums::STATE_CLASS_TOTAL: + return "STATE_CLASS_TOTAL"; default: return "UNKNOWN"; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index ec1cdc35ac..f9981fdbb7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -53,6 +53,7 @@ enum SensorStateClass : uint32_t { STATE_CLASS_NONE = 0, STATE_CLASS_MEASUREMENT = 1, STATE_CLASS_TOTAL_INCREASING = 2, + STATE_CLASS_TOTAL = 3, }; enum SensorLastResetType : uint32_t { LAST_RESET_NONE = 0, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index e88cd05c31..d6ba038057 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -109,6 +109,7 @@ STATE_CLASSES = { "": StateClasses.STATE_CLASS_NONE, "measurement": StateClasses.STATE_CLASS_MEASUREMENT, "total_increasing": StateClasses.STATE_CLASS_TOTAL_INCREASING, + "total": StateClasses.STATE_CLASS_TOTAL, } validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_") diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index c0869631aa..a729791e7e 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -12,6 +12,8 @@ std::string state_class_to_string(StateClass state_class) { return "measurement"; case STATE_CLASS_TOTAL_INCREASING: return "total_increasing"; + case STATE_CLASS_TOTAL: + return "total"; case STATE_CLASS_NONE: default: return ""; diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index f29125ee42..ba9edd68d0 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -36,6 +36,7 @@ enum StateClass : uint8_t { STATE_CLASS_NONE = 0, STATE_CLASS_MEASUREMENT = 1, STATE_CLASS_TOTAL_INCREASING = 2, + STATE_CLASS_TOTAL = 3, }; std::string state_class_to_string(StateClass state_class); diff --git a/esphome/const.py b/esphome/const.py index 0593c19672..4e1e293e94 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -956,6 +956,9 @@ STATE_CLASS_MEASUREMENT = "measurement" # The state represents a total that only increases, a decrease is considered a reset. STATE_CLASS_TOTAL_INCREASING = "total_increasing" +# The state represents a total amount that can both increase and decrease, e.g. a net energy meter. +STATE_CLASS_TOTAL = "total" + KEY_CORE = "core" KEY_TARGET_PLATFORM = "target_platform" KEY_TARGET_FRAMEWORK = "target_framework" From b918abfd54230cac83f0797ed077f15281bc924c Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 14 Aug 2022 22:21:08 +0200 Subject: [PATCH 17/69] add gradient color V2.0 (#3709) --- esphome/core/color.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/esphome/core/color.h b/esphome/core/color.h index 4e7733ca85..7596eeb0cf 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -139,9 +139,19 @@ struct Color { return Color(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)), uint8_t((uint16_t(b) * 255U / max_rgb)), w); } - Color fade_to_white(uint8_t amnt) { return Color(255, 255, 255, 255) - (*this * amnt); } - Color fade_to_black(uint8_t amnt) { return *this * amnt; } - Color gradient(const Color &to_color, uint8_t amnt) { return (*this * amnt) + (to_color * (255 - amnt)); } + + Color gradient(const Color &to_color, uint8_t amnt) { + Color new_color; + float amnt_f = float(amnt) / 255.0f; + new_color.r = amnt_f * (to_color.r - (*this).r) + (*this).r; + new_color.g = amnt_f * (to_color.g - (*this).g) + (*this).g; + new_color.b = amnt_f * (to_color.b - (*this).b) + (*this).b; + new_color.w = amnt_f * (to_color.w - (*this).w) + (*this).w; + return new_color; + } + Color fade_to_white(uint8_t amnt) { return (*this).gradient(Color::WHITE, amnt); } + Color fade_to_black(uint8_t amnt) { return (*this).gradient(Color::BLACK, amnt); } + Color lighten(uint8_t delta) { return *this + delta; } Color darken(uint8_t delta) { return *this - delta; } From 84c051d097b010b4175a6244fc61f8862bcdada1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:23:35 +1200 Subject: [PATCH 18/69] Bump version to 2022.8.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4e1e293e94..59fa466d11 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.0b1" +__version__ = "2022.8.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 0d0733dd940f65e829734c92b534772e6d06c5d0 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Mon, 15 Aug 2022 01:28:29 +0300 Subject: [PATCH 19/69] Webui small fixes (#3713) --- esphome/components/climate/climate_traits.cpp | 11 +- esphome/components/web_server/server_index.h | 1148 ++++++++--------- esphome/components/web_server/web_server.cpp | 18 +- esphome/core/helpers.cpp | 13 + esphome/core/helpers.h | 3 + 5 files changed, 604 insertions(+), 589 deletions(-) diff --git a/esphome/components/climate/climate_traits.cpp b/esphome/components/climate/climate_traits.cpp index 16c9cd05be..38ded6cdf7 100644 --- a/esphome/components/climate/climate_traits.cpp +++ b/esphome/components/climate/climate_traits.cpp @@ -1,19 +1,10 @@ #include "climate_traits.h" -#include namespace esphome { namespace climate { int8_t ClimateTraits::get_temperature_accuracy_decimals() const { - // use printf %g to find number of digits based on temperature step - char buf[32]; - sprintf(buf, "%.5g", this->visual_temperature_step_); - std::string str{buf}; - size_t dot_pos = str.find('.'); - if (dot_pos == std::string::npos) - return 0; - - return str.length() - dot_pos - 1; + return step_to_accuracy_decimals(this->visual_temperature_step_); } } // namespace climate diff --git a/esphome/components/web_server/server_index.h b/esphome/components/web_server/server_index.h index 39c170f15b..75c7130151 100644 --- a/esphome/components/web_server/server_index.h +++ b/esphome/components/web_server/server_index.h @@ -6,580 +6,580 @@ namespace esphome { namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xc9, 0x76, 0xdb, 0xc8, 0xb2, 0xe0, 0xba, - 0xcf, 0xe9, 0x3f, 0xe8, 0x0d, 0x84, 0xd2, 0x93, 0x81, 0x12, 0x08, 0x91, 0x94, 0x65, 0xbb, 0x40, 0x41, 0xbc, 0xf2, - 0x50, 0xd7, 0xae, 0xf2, 0x54, 0x96, 0xec, 0x1a, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, 0x92, - 0x1a, 0x8a, 0xc2, 0x3b, 0xbd, 0xea, 0x55, 0x9f, 0xd3, 0xe3, 0xe2, 0x6d, 0xfa, 0xbc, 0x5e, 0xf4, 0x47, 0xf4, 0xba, - 0x3f, 0xe5, 0xfe, 0x40, 0xbf, 0x4f, 0xe8, 0x88, 0xc8, 0x01, 0x09, 0x90, 0x1a, 0x5c, 0xaf, 0xfa, 0x1e, 0x0f, 0x02, - 0x72, 0x8c, 0x88, 0x8c, 0x8c, 0x29, 0x23, 0xa1, 0xdd, 0xb5, 0x51, 0x36, 0xe4, 0x97, 0x33, 0x66, 0x4d, 0xf8, 0x34, - 0xd9, 0xdb, 0x95, 0xff, 0xb3, 0x68, 0xb4, 0xb7, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x0f, 0xb3, 0xd4, - 0x9a, 0xe4, 0x6c, 0x1c, 0x8e, 0x22, 0x1e, 0x05, 0xf1, 0x34, 0x3a, 0x65, 0xd6, 0xd6, 0xde, 0xee, 0x94, 0xf1, 0xc8, - 0x1a, 0x4e, 0xa2, 0xbc, 0x60, 0x3c, 0x7c, 0x7f, 0xf8, 0x75, 0xeb, 0xd1, 0xde, 0x6e, 0x31, 0xcc, 0xe3, 0x19, 0xb7, - 0x70, 0xc8, 0x70, 0x9a, 0x8d, 0xe6, 0x09, 0xdb, 0x3b, 0x8b, 0x72, 0xeb, 0x82, 0x85, 0x6f, 0x4e, 0x7e, 0x65, 0x43, - 0xee, 0x8f, 0xd8, 0x38, 0x4e, 0xd9, 0xdb, 0x3c, 0x9b, 0xb1, 0x9c, 0x5f, 0x7a, 0xcf, 0x56, 0x57, 0xc4, 0xac, 0xf0, - 0x0e, 0x74, 0xd5, 0x29, 0xe3, 0x6f, 0xce, 0x53, 0xd5, 0xe7, 0x29, 0x13, 0x93, 0x64, 0x79, 0xe1, 0xf1, 0x6b, 0xda, - 0x1c, 0x5c, 0x4e, 0x4f, 0xb2, 0xa4, 0xf0, 0x3e, 0xe9, 0xfa, 0x59, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0x27, 0x51, 0x61, - 0xb4, 0xf4, 0x9e, 0xac, 0x68, 0x32, 0x93, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x7c, 0xca, 0xf2, 0xe8, 0x24, 0x61, 0x5e, - 0xc1, 0x42, 0x87, 0x79, 0xdc, 0x8b, 0xdd, 0x70, 0x8f, 0x5b, 0x71, 0x6a, 0xb1, 0xfe, 0x05, 0xa3, 0x92, 0x05, 0xd3, - 0xad, 0x82, 0xb5, 0xb6, 0x07, 0xe4, 0x1a, 0xc7, 0xa7, 0x73, 0xfd, 0x7e, 0x9e, 0xc7, 0x5c, 0x3d, 0x9f, 0x45, 0xc9, - 0x9c, 0x05, 0x71, 0xe9, 0x06, 0xec, 0x88, 0x0f, 0xc2, 0xd8, 0x7b, 0x42, 0x83, 0xc2, 0x90, 0x8b, 0x71, 0x96, 0x3b, - 0x48, 0xab, 0x18, 0xc7, 0xe6, 0x57, 0x57, 0x0e, 0x0f, 0x17, 0xa5, 0xeb, 0x7e, 0x62, 0xfe, 0x30, 0x4a, 0x12, 0x07, - 0x27, 0xde, 0xd8, 0x28, 0x70, 0xc6, 0xd8, 0xe3, 0x47, 0xf1, 0xc0, 0xed, 0xc5, 0x63, 0x87, 0x33, 0xb7, 0xea, 0x97, - 0x8d, 0x2d, 0xce, 0x1c, 0xee, 0xba, 0x4f, 0xae, 0xef, 0x93, 0x33, 0x3e, 0xcf, 0x01, 0xf6, 0xd2, 0x7b, 0xa3, 0x66, - 0x7e, 0x86, 0xf5, 0x07, 0xd4, 0xb1, 0x07, 0xb0, 0x17, 0xdc, 0xfa, 0x10, 0x9e, 0xc7, 0xe9, 0x28, 0x3b, 0xf7, 0x0f, - 0x26, 0x11, 0xfc, 0x78, 0x97, 0x65, 0x7c, 0x63, 0xc3, 0x39, 0xcb, 0xe2, 0x91, 0xd5, 0x0e, 0x43, 0xb3, 0xf2, 0xf2, - 0xc9, 0xc1, 0xc1, 0xd5, 0x55, 0xa3, 0xc0, 0x4f, 0x23, 0x1e, 0x9f, 0x31, 0xd1, 0x19, 0x00, 0xb0, 0xe1, 0xe7, 0x8c, - 0xb3, 0xd1, 0x01, 0xbf, 0x4c, 0xa0, 0x94, 0x31, 0x5e, 0xd8, 0x80, 0xe3, 0xd3, 0x6c, 0x08, 0x64, 0x4b, 0x0d, 0xc2, - 0x43, 0xd3, 0x9c, 0xcd, 0x92, 0x68, 0xc8, 0xb0, 0x1e, 0x46, 0xaa, 0x7a, 0x54, 0x8d, 0xbc, 0xaf, 0x43, 0xb1, 0xbc, - 0x8e, 0xeb, 0xc5, 0x2c, 0x4c, 0xd9, 0xb9, 0xf5, 0x2a, 0x9a, 0xf5, 0x86, 0x49, 0x54, 0x14, 0xc0, 0xaf, 0x0b, 0x42, - 0x21, 0x9f, 0x0f, 0x81, 0x41, 0x08, 0xc1, 0x05, 0x92, 0x69, 0x12, 0x17, 0xfe, 0xc7, 0xf5, 0x61, 0x51, 0xbc, 0x63, - 0xc5, 0x3c, 0xe1, 0xeb, 0x21, 0xac, 0x05, 0x5f, 0x0b, 0xc3, 0xaf, 0x5d, 0x3e, 0xc9, 0xb3, 0x73, 0xeb, 0x59, 0x9e, - 0x43, 0x73, 0x1b, 0xa6, 0x14, 0x0d, 0xac, 0xb8, 0xb0, 0xd2, 0x8c, 0x5b, 0x7a, 0x30, 0x5c, 0x40, 0xdf, 0x7a, 0x5f, - 0x30, 0xeb, 0x78, 0x9e, 0x16, 0xd1, 0x98, 0x41, 0xd3, 0x63, 0x2b, 0xcb, 0xad, 0x63, 0x18, 0xf4, 0x18, 0x96, 0xac, - 0xe0, 0xb0, 0x6b, 0x7c, 0xdb, 0xed, 0xd1, 0x5c, 0x50, 0x78, 0xc8, 0x2e, 0x78, 0xc8, 0x4a, 0x60, 0x4c, 0xab, 0xd0, - 0x68, 0x38, 0xee, 0x22, 0x81, 0x02, 0x16, 0xc6, 0x0c, 0x59, 0xd6, 0x31, 0x1b, 0xeb, 0xc5, 0xf9, 0xb0, 0xb1, 0xa1, - 0x69, 0x0d, 0x34, 0x71, 0xa0, 0x6d, 0xd1, 0x68, 0xeb, 0x09, 0xc4, 0x6b, 0x24, 0x72, 0x3d, 0xe6, 0x4b, 0xf2, 0x1d, - 0x5c, 0xa6, 0xc3, 0xfa, 0xd8, 0x50, 0x59, 0xf2, 0xec, 0x80, 0xe7, 0x71, 0x7a, 0x0a, 0x40, 0xc8, 0x99, 0xcc, 0x26, - 0x65, 0x29, 0x16, 0xff, 0x0d, 0x0b, 0x59, 0xb8, 0x87, 0xa3, 0xe7, 0xcc, 0xb1, 0x0b, 0xea, 0x61, 0x87, 0x21, 0x92, - 0x1e, 0x18, 0x8c, 0xf5, 0x59, 0xc0, 0x36, 0x6d, 0xdb, 0xfb, 0xda, 0xf5, 0x2e, 0x91, 0x83, 0x7c, 0xdf, 0x27, 0xf6, - 0x15, 0x9d, 0xe3, 0xb0, 0x83, 0x40, 0xfb, 0x09, 0x4b, 0x4f, 0xf9, 0xa4, 0xcf, 0x8e, 0xda, 0x83, 0x80, 0x03, 0x54, - 0xa3, 0xf9, 0x90, 0x39, 0xc8, 0x8f, 0x5e, 0x8e, 0xdb, 0x67, 0xd3, 0x81, 0x29, 0x70, 0x61, 0xd6, 0x08, 0xc7, 0xda, - 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x13, 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, 0x47, 0xf9, 0x66, 0x67, 0xe0, 0x21, 0x94, 0x9a, 0xf8, 0x12, - 0xf1, 0x18, 0x10, 0x2c, 0xbd, 0xb7, 0x4c, 0x6f, 0xcf, 0x0f, 0x7d, 0xe6, 0x2f, 0xf3, 0x71, 0xc8, 0xfd, 0x69, 0x34, - 0x43, 0x6c, 0x18, 0xf1, 0x40, 0x94, 0x0e, 0x11, 0xba, 0xda, 0xba, 0x20, 0xc5, 0xfc, 0x8a, 0x05, 0x5c, 0x20, 0x08, - 0xec, 0xd9, 0x67, 0xd1, 0x70, 0x02, 0x5b, 0xbc, 0x22, 0xdc, 0x48, 0x6d, 0x87, 0x61, 0xce, 0x22, 0xce, 0x9e, 0x25, - 0x0c, 0xdf, 0x70, 0x05, 0xa0, 0xa7, 0xed, 0x7a, 0xb9, 0xda, 0x77, 0x49, 0xcc, 0x5f, 0x67, 0x30, 0x4f, 0x4f, 0x30, - 0x09, 0x70, 0x71, 0xbe, 0xb1, 0x11, 0x23, 0x8b, 0xec, 0x73, 0x58, 0xad, 0x93, 0x39, 0x08, 0x01, 0x3b, 0xc5, 0x16, - 0x36, 0x50, 0xdb, 0x8b, 0x7d, 0x0e, 0x44, 0x7c, 0x92, 0xa5, 0x1c, 0x86, 0x03, 0x78, 0x35, 0x07, 0xf9, 0xd1, 0x6c, - 0xc6, 0xd2, 0xd1, 0x93, 0x49, 0x9c, 0x8c, 0x80, 0x1a, 0x25, 0xe0, 0x9b, 0xb1, 0x10, 0xf0, 0x04, 0x64, 0x82, 0x9b, - 0x31, 0xa2, 0xe5, 0x43, 0x46, 0xe6, 0xa1, 0x6d, 0xf7, 0x50, 0x02, 0x49, 0x2c, 0x50, 0x06, 0xd1, 0xc2, 0xbd, 0x03, - 0xd1, 0x5f, 0xb8, 0x7c, 0x33, 0x8c, 0xf5, 0x32, 0x4a, 0x02, 0xbf, 0x41, 0x49, 0x03, 0xf4, 0x67, 0x20, 0x03, 0x7b, - 0x28, 0xb8, 0xbe, 0x97, 0x52, 0x27, 0x65, 0x0a, 0x43, 0x20, 0xc0, 0x10, 0x25, 0x88, 0xa4, 0xc1, 0xdb, 0x2c, 0xb9, - 0x1c, 0xc7, 0x49, 0x72, 0x30, 0x9f, 0xcd, 0xb2, 0x9c, 0x7b, 0xdf, 0x84, 0x0b, 0x9e, 0x55, 0xb8, 0xd2, 0x26, 0x2f, - 0xce, 0x63, 0x8e, 0x04, 0x75, 0x17, 0xc3, 0x08, 0x96, 0xfa, 0x71, 0x96, 0x25, 0x2c, 0x4a, 0x01, 0x0d, 0xd6, 0xb7, - 0xed, 0x20, 0x9d, 0x27, 0x49, 0xef, 0x04, 0x86, 0xfd, 0xd4, 0xa3, 0x6a, 0x21, 0xf1, 0x03, 0x7a, 0xde, 0xcf, 0xf3, - 0xe8, 0x12, 0x1a, 0x62, 0x1b, 0xe0, 0x45, 0x58, 0xad, 0x6f, 0x0e, 0xde, 0xbc, 0xf6, 0x05, 0xe3, 0xc7, 0xe3, 0x4b, - 0x00, 0xb4, 0xac, 0xa4, 0xe6, 0x38, 0xcf, 0xa6, 0x8d, 0xa9, 0x91, 0x0e, 0x71, 0xc8, 0x7a, 0xd7, 0x80, 0x10, 0xd3, - 0xc8, 0xb0, 0x4a, 0xcc, 0x84, 0xe0, 0x35, 0xf1, 0xb3, 0xac, 0xc4, 0x3d, 0xd0, 0xc7, 0x87, 0x40, 0x14, 0xc3, 0x94, - 0x37, 0x43, 0xcb, 0xf3, 0xcb, 0x45, 0x1c, 0x12, 0x9c, 0x33, 0xd4, 0xbf, 0x08, 0xe3, 0x30, 0x82, 0xd9, 0x17, 0x62, - 0xc0, 0x52, 0x41, 0x1c, 0x97, 0xa5, 0x97, 0x68, 0x26, 0x46, 0x89, 0x87, 0x02, 0x85, 0xc3, 0x36, 0xba, 0xba, 0x62, - 0xf0, 0xe2, 0x7a, 0xdf, 0x86, 0x8b, 0x48, 0xe1, 0x83, 0x1a, 0x0a, 0xf7, 0x57, 0x20, 0xe4, 0x04, 0x6a, 0xb2, 0x33, - 0xd0, 0x83, 0x00, 0xe7, 0x37, 0x1e, 0xe8, 0xff, 0x04, 0xa1, 0x58, 0xeb, 0x78, 0xa0, 0x41, 0x9f, 0x4c, 0xa2, 0xf4, - 0x94, 0x8d, 0x82, 0x84, 0x95, 0x52, 0xf2, 0xee, 0x5b, 0xb0, 0xc6, 0xc0, 0x4e, 0x85, 0xf5, 0xfc, 0xf0, 0xd5, 0x4b, - 0xb9, 0x72, 0x35, 0x61, 0x0c, 0x8b, 0x34, 0x07, 0xb5, 0x0a, 0x62, 0x5b, 0x8a, 0xe3, 0x67, 0x5c, 0x49, 0x6f, 0x51, - 0x12, 0x17, 0xef, 0x67, 0x60, 0x62, 0xb0, 0xb7, 0x30, 0x0c, 0x4c, 0x1f, 0xc2, 0x54, 0x54, 0x0e, 0xf3, 0x89, 0x8a, - 0x91, 0x2e, 0x82, 0xce, 0x02, 0x53, 0xf1, 0x9a, 0x39, 0x6e, 0x09, 0xac, 0xca, 0xe3, 0xa1, 0x15, 0x8d, 0x46, 0x2f, - 0xd2, 0x98, 0xc7, 0x51, 0x12, 0xff, 0x4e, 0x94, 0x5c, 0x20, 0x8f, 0xf1, 0x9e, 0x5c, 0x04, 0xc0, 0x9d, 0x7a, 0x24, - 0xae, 0x12, 0xb2, 0x6b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x68, 0x20, 0xc1, 0x4b, 0xfc, 0xd9, 0xbc, 0x98, 0x20, - 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0x93, 0x82, 0xe5, 0x67, 0x6c, 0xa4, 0x39, 0xa0, 0x00, 0xac, 0xa8, 0x39, 0x18, - 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, 0xa5, 0x80, - 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x01, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, 0x93, 0x3d, - 0x9b, 0x93, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, 0x0a, 0x29, - 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, 0xc2, 0x4a, - 0xbc, 0x2b, 0x44, 0xbc, 0x6b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x35, 0xee, 0xa7, 0xd9, 0xfe, 0x70, 0xc8, 0x8a, - 0x22, 0x03, 0xd8, 0xd6, 0xa8, 0xfd, 0x75, 0x86, 0x16, 0x50, 0xd2, 0xd5, 0xb2, 0xce, 0x2e, 0x48, 0x83, 0x9b, 0x6a, - 0x45, 0xe9, 0xf4, 0xc0, 0xfe, 0xf8, 0x11, 0x64, 0xb6, 0x27, 0xc9, 0x00, 0x54, 0x5f, 0x36, 0xfc, 0x84, 0x3d, 0x53, - 0xa7, 0xcc, 0x4a, 0xfb, 0xd2, 0xa9, 0x83, 0xe4, 0xc1, 0xb0, 0x6e, 0x69, 0x2c, 0xe8, 0xca, 0xa1, 0x71, 0x35, 0xa4, - 0x82, 0x5c, 0x9c, 0x92, 0xca, 0x36, 0x96, 0x11, 0xac, 0xb6, 0xd2, 0x23, 0xd2, 0x2b, 0x6c, 0x0a, 0x02, 0xf4, 0x88, - 0x0d, 0x7a, 0xb2, 0x3e, 0xcc, 0x05, 0xe5, 0x72, 0xf6, 0xdb, 0x9c, 0x15, 0x5c, 0xb0, 0x2e, 0x8c, 0x5b, 0xc0, 0xb8, - 0xe5, 0x92, 0x75, 0x58, 0xb3, 0x1d, 0x57, 0xc1, 0xf6, 0x66, 0x86, 0x7a, 0xac, 0x40, 0x4e, 0xbe, 0x99, 0x9d, 0x10, - 0x56, 0xe6, 0x5e, 0x5d, 0x7d, 0xab, 0x06, 0xa9, 0x96, 0x52, 0xdb, 0x40, 0x8d, 0x35, 0xb1, 0x55, 0x93, 0x91, 0xed, - 0x4a, 0x85, 0xba, 0xd6, 0xe9, 0xd5, 0xf8, 0x00, 0xf6, 0x5c, 0x5b, 0xb3, 0x74, 0x65, 0x6c, 0xbf, 0x55, 0x34, 0x7d, - 0x23, 0x46, 0x26, 0x6b, 0x94, 0xdd, 0xce, 0x3d, 0x6a, 0xc7, 0x43, 0xdb, 0xa5, 0xba, 0x4a, 0x30, 0xcc, 0xeb, 0x82, - 0xa1, 0x09, 0xf5, 0x4c, 0x77, 0xb1, 0x35, 0x53, 0xb1, 0x50, 0xad, 0xb5, 0x72, 0x20, 0x78, 0x78, 0x04, 0xc6, 0xc9, - 0x4a, 0xff, 0xe0, 0x75, 0x34, 0x65, 0x48, 0x51, 0xef, 0xba, 0x06, 0xd2, 0x81, 0x80, 0x26, 0x83, 0xa6, 0x7a, 0xe3, - 0xae, 0xb0, 0x9a, 0xea, 0xfb, 0x2b, 0x06, 0x2b, 0x02, 0xec, 0xeb, 0x72, 0xc5, 0x12, 0x91, 0xde, 0x14, 0x5c, 0xa2, - 0xe9, 0x23, 0xca, 0xc4, 0x9a, 0x90, 0x82, 0x07, 0xe4, 0x61, 0xf9, 0x1b, 0x0b, 0x27, 0x5b, 0x31, 0x85, 0x23, 0x47, - 0x99, 0x02, 0x74, 0x26, 0x25, 0x00, 0xe2, 0x92, 0x7e, 0xd6, 0x36, 0x16, 0x92, 0xed, 0x00, 0xf9, 0xc0, 0x1f, 0x27, - 0x11, 0x77, 0x3a, 0x5b, 0x6d, 0x17, 0xf8, 0x10, 0x84, 0x38, 0xe8, 0x08, 0x30, 0xef, 0x2b, 0x54, 0x18, 0xa2, 0x12, - 0xbb, 0xdc, 0x07, 0xa3, 0x68, 0x12, 0x8f, 0xb9, 0x93, 0xa1, 0x12, 0x71, 0x4b, 0x96, 0x80, 0x92, 0xd1, 0xfb, 0x0a, - 0xa4, 0x04, 0x17, 0xd2, 0x45, 0x54, 0x6b, 0x81, 0xa6, 0x20, 0x25, 0x29, 0x45, 0x5a, 0x50, 0x41, 0x60, 0x08, 0x95, - 0x9e, 0xe2, 0x28, 0xd0, 0x6f, 0x71, 0x5f, 0x0c, 0x1a, 0x2c, 0x59, 0x94, 0x71, 0x3f, 0x5e, 0x2e, 0x04, 0x35, 0xec, - 0xf3, 0xec, 0x65, 0x76, 0xce, 0xf2, 0x27, 0x11, 0xc2, 0x1e, 0x88, 0xee, 0x25, 0x48, 0x7a, 0x12, 0xe8, 0xac, 0xa7, - 0x78, 0xe5, 0x8c, 0x90, 0x86, 0x85, 0x98, 0xc6, 0xa8, 0x08, 0x41, 0xcb, 0x11, 0xed, 0x53, 0xdc, 0x52, 0xb4, 0xf7, - 0x50, 0x95, 0x30, 0xcd, 0x5b, 0xfb, 0x2f, 0xeb, 0xbc, 0x05, 0x23, 0xcc, 0x14, 0xb7, 0xd6, 0x77, 0xac, 0xeb, 0x49, - 0xdd, 0xec, 0x48, 0xde, 0x32, 0x94, 0x19, 0xe8, 0x8f, 0xab, 0xab, 0xca, 0x48, 0x07, 0x65, 0xaa, 0xa5, 0x39, 0x5a, - 0x4e, 0x62, 0x4b, 0xb8, 0x25, 0x28, 0x23, 0x34, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x5d, 0xe4, 0xc5, 0x3d, 0xa7, 0xa1, - 0x8e, 0x00, 0x8a, 0x69, 0x4d, 0x23, 0xf5, 0x79, 0xa0, 0x2b, 0x50, 0x29, 0x29, 0x6d, 0xe4, 0x55, 0x4d, 0x04, 0xc4, - 0xe9, 0x88, 0xe5, 0xc2, 0x41, 0x93, 0x3a, 0x14, 0x26, 0x4c, 0x81, 0xa1, 0xd9, 0x08, 0x24, 0xbc, 0x42, 0x00, 0xcc, - 0x13, 0x7f, 0x92, 0x15, 0x5c, 0xd7, 0x99, 0xd0, 0xc7, 0x57, 0x57, 0xb1, 0xf0, 0x17, 0x91, 0x01, 0x72, 0x36, 0xcd, - 0xce, 0xd8, 0x0a, 0xa8, 0x7b, 0x6a, 0x30, 0x13, 0x64, 0x63, 0x18, 0x50, 0xa2, 0xa0, 0x5a, 0x66, 0x49, 0x3c, 0x64, - 0x5a, 0x4b, 0x4d, 0x7d, 0x30, 0xe8, 0xd8, 0x05, 0xc8, 0x08, 0xe6, 0xee, 0xed, 0xed, 0xb5, 0xbd, 0x8e, 0x5b, 0x0a, - 0x82, 0x2f, 0x96, 0x28, 0x7a, 0x83, 0x7e, 0x94, 0x26, 0xf8, 0x2a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, 0xe1, 0x27, - 0x79, 0x52, 0x10, 0xbb, 0xde, 0x08, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, 0xc9, 0xa6, - 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, 0x03, 0xaf, - 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x96, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, 0x5c, 0x45, - 0x82, 0x03, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x9c, 0x44, 0xc3, 0x4f, 0x1a, 0xdc, 0x8a, - 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, 0xc8, 0x96, - 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0x37, 0xb0, 0x1c, 0x57, 0xd2, 0x80, - 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe2, 0xe2, 0x7a, 0x12, 0xfc, 0xa9, 0x60, 0x3e, 0x35, - 0x66, 0xba, 0x15, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, 0xa1, 0x42, - 0x1c, 0x7f, 0x2a, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0x2d, 0x8b, 0x16, 0x29, 0x53, 0x63, - 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, 0x30, 0x29, - 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, 0x99, 0xbc, - 0x5d, 0xf4, 0x8b, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, 0x92, 0x4d, - 0xd2, 0xbe, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, 0xdd, 0x86, - 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0x6e, 0xa2, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, 0x85, 0xb1, - 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x2b, 0xec, 0x29, 0x00, 0x25, 0x81, - 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x35, 0xa2, 0x63, 0x25, 0x42, 0x6b, 0x22, 0x5f, 0xeb, 0xb3, 0xd8, - 0xaf, 0xb9, 0x84, 0x26, 0x25, 0xf3, 0x7e, 0x1e, 0xd8, 0x2a, 0x10, 0x51, 0xe9, 0xb6, 0xa4, 0x9f, 0x90, 0x43, 0xba, - 0x4c, 0xf4, 0xda, 0x48, 0x06, 0xad, 0x53, 0x21, 0xd1, 0xd2, 0x41, 0x18, 0x39, 0xe8, 0xb8, 0xd3, 0x5a, 0x2c, 0x11, - 0xb2, 0x69, 0x6f, 0x12, 0x2b, 0xa2, 0x73, 0x9a, 0xa3, 0x09, 0x67, 0xea, 0x74, 0xc7, 0x01, 0x74, 0x40, 0xec, 0x2f, - 0xb1, 0xde, 0x4a, 0xb3, 0xd3, 0xf5, 0x2b, 0x87, 0xef, 0xea, 0x2a, 0x41, 0x7e, 0x10, 0x06, 0x2f, 0xac, 0x59, 0x5f, - 0xc9, 0xde, 0xfd, 0x97, 0xd8, 0x8a, 0xec, 0xcf, 0xaa, 0xa4, 0xf2, 0x14, 0x6a, 0x9c, 0x5b, 0x5f, 0x27, 0x66, 0x86, - 0x16, 0x55, 0xc5, 0x81, 0x21, 0xd5, 0x0f, 0x94, 0xc2, 0xae, 0x50, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x39, - 0x39, 0x2f, 0x6b, 0xab, 0x5c, 0xc8, 0x8d, 0x0d, 0xd3, 0x67, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x39, 0x17, 0x97, - 0x29, 0x59, 0x30, 0x4f, 0x94, 0x3a, 0x5a, 0x72, 0x40, 0xdb, 0x3d, 0xf4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, - 0x45, 0x78, 0xda, 0x28, 0x9f, 0xa4, 0xd1, 0x01, 0x78, 0xa1, 0x09, 0x49, 0x4e, 0xb8, 0x69, 0x8b, 0x16, 0xc3, 0x09, - 0xc3, 0x10, 0xb8, 0xb2, 0x27, 0x4c, 0xd9, 0xb3, 0x86, 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, 0x1e, 0x27, 0x49, 0x55, 0xfa, 0xa7, 0x0a, 0x78, 0x31, 0xec, 0x6d, - 0xa2, 0x5d, 0x60, 0x34, 0x57, 0x20, 0xb8, 0xda, 0x08, 0x7b, 0xef, 0xb8, 0xd5, 0xba, 0x8b, 0x88, 0x23, 0x37, 0xa3, - 0x11, 0x50, 0x8f, 0x11, 0x56, 0xcd, 0xda, 0xfb, 0xcf, 0x30, 0xa4, 0x66, 0xe0, 0x83, 0xea, 0x8c, 0x8a, 0x3f, 0xcb, - 0x9e, 0xfa, 0x4c, 0xf4, 0x6e, 0x55, 0x5d, 0xcd, 0x80, 0x8a, 0x0a, 0x7c, 0x98, 0x21, 0x96, 0xb6, 0x0a, 0x04, 0xe4, - 0x7a, 0x58, 0x94, 0x02, 0x26, 0x69, 0xb0, 0xa0, 0x14, 0x58, 0x6b, 0x65, 0xf7, 0xf2, 0xb6, 0x60, 0x0e, 0x85, 0xc2, - 0x45, 0xff, 0x27, 0xd9, 0x74, 0x86, 0x96, 0x59, 0x83, 0xa9, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa2, 0xac, 0xd6, - 0x87, 0x76, 0x64, 0x8d, 0x9f, 0xb4, 0xa3, 0x0c, 0x0e, 0xd5, 0x5c, 0x17, 0xd5, 0xed, 0xe6, 0xa6, 0x88, 0x59, 0xc5, - 0xe3, 0x3e, 0xe9, 0x6d, 0x6d, 0x4d, 0x7a, 0x9a, 0x06, 0x24, 0x93, 0x24, 0xc3, 0x9b, 0x0c, 0x50, 0x56, 0xc4, 0x59, - 0x94, 0x0d, 0xf2, 0x2d, 0xca, 0x12, 0xd7, 0xef, 0xbb, 0xde, 0x7e, 0xcd, 0xb3, 0xf6, 0xf6, 0xaf, 0x77, 0x91, 0xab, - 0x3a, 0xe9, 0x41, 0x1e, 0x0d, 0xa0, 0x68, 0xc9, 0xa6, 0x0c, 0x17, 0xd3, 0x6c, 0xc4, 0x02, 0x1b, 0xba, 0xa7, 0x76, - 0xa9, 0xb4, 0x32, 0x6c, 0x8e, 0x94, 0x39, 0x8b, 0x77, 0xf5, 0x48, 0x6a, 0xb0, 0x0f, 0x2c, 0xa0, 0xcd, 0x85, 0xef, - 0xc3, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0xa1, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xcb, 0x48, 0x16, 0xab, 0xa1, 0xa4, - 0xbe, 0xef, 0x7f, 0x1f, 0xdc, 0xde, 0xa3, 0xf2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0x8a, 0x88, 0xbd, 0x34, - 0xa7, 0x03, 0x8d, 0x53, 0x00, 0x65, 0x0e, 0x41, 0x93, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0xfd, 0x97, 0xca, 0xa5, 0xce, - 0xc0, 0x85, 0x00, 0x27, 0x3f, 0x89, 0x79, 0x0b, 0xcf, 0x23, 0x6d, 0x6f, 0x21, 0x2a, 0x30, 0xae, 0x48, 0x71, 0xe9, - 0x52, 0x79, 0x83, 0xde, 0xc7, 0xf0, 0x18, 0x9a, 0xad, 0xaf, 0x2f, 0x9c, 0x57, 0x11, 0x9f, 0xf8, 0x79, 0x94, 0x8e, - 0xb2, 0xa9, 0xe3, 0x6e, 0xda, 0xb6, 0xeb, 0x17, 0xe4, 0x89, 0x7c, 0xe5, 0x96, 0xeb, 0xc7, 0xde, 0x84, 0x85, 0x76, - 0xdf, 0xde, 0xfc, 0xe8, 0xbd, 0x67, 0xe1, 0xf1, 0xee, 0xfa, 0x62, 0xc2, 0xca, 0xbd, 0x63, 0xef, 0x42, 0xc7, 0xdc, - 0xbd, 0xb7, 0x28, 0x65, 0xa0, 0x57, 0xb8, 0x77, 0x21, 0xc1, 0x00, 0x76, 0xa3, 0xf8, 0x3b, 0x48, 0xb9, 0xf7, 0x74, - 0x20, 0x22, 0xe3, 0xb4, 0x57, 0x57, 0x76, 0x46, 0x11, 0x03, 0x7b, 0x4d, 0x3b, 0xab, 0x1b, 0x1b, 0x95, 0x9a, 0xaf, - 0x4a, 0xbd, 0x21, 0x0b, 0x6b, 0x9e, 0xba, 0x77, 0x48, 0x47, 0x2b, 0xf5, 0x8d, 0x3c, 0x64, 0xa4, 0x34, 0x97, 0xed, - 0x04, 0xc7, 0xd8, 0xe2, 0xab, 0xb7, 0xf5, 0x91, 0x88, 0x52, 0xf8, 0x31, 0x58, 0x2f, 0x11, 0xa8, 0x6f, 0x70, 0x70, - 0xbc, 0xc3, 0x70, 0x6b, 0xd7, 0xe9, 0x07, 0xce, 0x5a, 0xab, 0x75, 0xf5, 0xf3, 0xd6, 0xd1, 0x2f, 0x51, 0xeb, 0xf7, - 0xfd, 0xd6, 0x4f, 0x03, 0xf7, 0xca, 0xf9, 0x79, 0xab, 0x7f, 0x24, 0xdf, 0x8e, 0x7e, 0xd9, 0xfb, 0xb9, 0x18, 0x7c, - 0x29, 0x0a, 0xd7, 0x5d, 0x77, 0xeb, 0x14, 0x3c, 0xa5, 0x70, 0xab, 0xd5, 0xda, 0x83, 0xa7, 0x39, 0x3c, 0xe1, 0xcf, - 0x73, 0xf8, 0x71, 0x75, 0x64, 0xfd, 0x9b, 0x9f, 0xd3, 0x7f, 0xfb, 0x73, 0x3e, 0xc0, 0x31, 0x8f, 0x7e, 0xf9, 0xb9, - 0xb0, 0xef, 0xed, 0x85, 0x5b, 0x83, 0x4d, 0xd7, 0xd1, 0x35, 0x5f, 0x86, 0xd5, 0x23, 0xb4, 0x3a, 0xfa, 0x45, 0xbe, - 0xd9, 0xf7, 0x8e, 0x77, 0xf7, 0xc2, 0xc1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x1d, 0xe7, 0x99, - 0xc1, 0xe8, 0xf7, 0xe0, 0xe7, 0x29, 0xfc, 0xb4, 0xe1, 0xe7, 0x19, 0xfc, 0xfc, 0x05, 0xba, 0x89, 0xf8, 0xdb, 0x15, - 0xc5, 0x42, 0xae, 0xf0, 0xc0, 0x22, 0x82, 0x55, 0x70, 0xd7, 0xb7, 0x62, 0x6f, 0x4c, 0x44, 0x83, 0x7d, 0xe8, 0xfb, - 0x3e, 0x86, 0x49, 0x9d, 0xc5, 0xc7, 0x75, 0x58, 0x74, 0xe4, 0x9c, 0xf5, 0x80, 0x79, 0x22, 0x72, 0x50, 0x04, 0x5c, - 0x9c, 0xad, 0x16, 0x78, 0xb8, 0xea, 0x8d, 0xc2, 0x31, 0x73, 0xc0, 0x28, 0x78, 0xce, 0xf0, 0xa1, 0xeb, 0x7a, 0xcf, - 0xe4, 0x99, 0x21, 0xee, 0x73, 0xc1, 0x5a, 0x69, 0x26, 0x4c, 0x1a, 0xdb, 0xf5, 0x66, 0x2b, 0x2a, 0x61, 0x5b, 0xa7, - 0xa7, 0x50, 0xb7, 0x2e, 0x0e, 0xda, 0xbe, 0x67, 0xd1, 0x27, 0xdc, 0x92, 0xaf, 0x8d, 0x43, 0xe0, 0x25, 0x4b, 0xbe, - 0x69, 0x34, 0x1a, 0x36, 0xa2, 0x70, 0xc7, 0x1e, 0x33, 0x98, 0x61, 0xc9, 0x44, 0xe4, 0xa4, 0x34, 0x85, 0x65, 0x0b, - 0x93, 0xbf, 0x8d, 0x72, 0xbe, 0x5e, 0x19, 0xb6, 0x61, 0xcd, 0x92, 0x6d, 0x5a, 0xfa, 0x77, 0x98, 0x02, 0x4d, 0x4b, - 0x3a, 0xff, 0x30, 0xc7, 0x0f, 0x53, 0x42, 0xeb, 0xb5, 0xc3, 0xc1, 0x43, 0x2f, 0x40, 0xee, 0x88, 0x7e, 0xce, 0x5b, - 0x54, 0x63, 0xf0, 0x57, 0x86, 0x19, 0x3c, 0x31, 0x1f, 0x86, 0x68, 0x16, 0xa5, 0x0e, 0x6e, 0xa5, 0x28, 0xee, 0x5f, - 0xe0, 0xce, 0x48, 0x4b, 0xef, 0x20, 0x54, 0x3b, 0xe6, 0x30, 0x67, 0xec, 0xfb, 0x28, 0xf9, 0xc4, 0x72, 0xe7, 0xc2, - 0xeb, 0x74, 0xbf, 0xa2, 0xce, 0x1e, 0xda, 0x66, 0xaf, 0xaa, 0x63, 0x34, 0x65, 0x16, 0xa8, 0x23, 0xc2, 0x56, 0xc7, - 0xcb, 0x31, 0xaa, 0x85, 0x24, 0x28, 0xbc, 0x2c, 0xec, 0x12, 0x87, 0xdb, 0xbb, 0xc5, 0xd9, 0xe9, 0x9e, 0x1d, 0xd8, - 0x36, 0x58, 0xfc, 0x87, 0x14, 0xb6, 0x12, 0x86, 0x05, 0x18, 0x64, 0xbb, 0x71, 0x8f, 0x6f, 0x6e, 0x56, 0x01, 0x27, - 0x3c, 0x48, 0xa7, 0xee, 0x89, 0x17, 0x79, 0x93, 0x10, 0x06, 0x1c, 0x42, 0x33, 0xec, 0xd2, 0x1b, 0xee, 0xc6, 0x72, - 0x1a, 0x8c, 0x85, 0xf8, 0x49, 0x54, 0xf0, 0x17, 0x18, 0x8f, 0x08, 0x87, 0x68, 0xec, 0xfb, 0xec, 0x82, 0x0d, 0x95, - 0x9d, 0x01, 0x84, 0x8a, 0xdc, 0x9e, 0x3b, 0x0c, 0x8d, 0x66, 0x30, 0x77, 0x18, 0x1e, 0xf6, 0x6d, 0xd8, 0x4b, 0xb0, - 0x2b, 0xc3, 0xe8, 0xa8, 0x33, 0xe8, 0xa7, 0x21, 0xc8, 0x5a, 0x4d, 0x5b, 0x59, 0x34, 0xaf, 0x15, 0x75, 0x07, 0x7d, - 0xe7, 0x0c, 0x8c, 0x74, 0xb0, 0xc5, 0x1d, 0x7c, 0xc3, 0x08, 0x45, 0x11, 0xbe, 0x63, 0xa7, 0xcf, 0x2e, 0x66, 0x8e, - 0xbd, 0xbb, 0x65, 0x6f, 0x62, 0xa9, 0x67, 0x03, 0x7b, 0xc1, 0xdc, 0xe1, 0xb9, 0x6b, 0x76, 0xde, 0x1e, 0x20, 0xa8, - 0x58, 0x88, 0x93, 0x9f, 0xf7, 0xed, 0x3d, 0x31, 0x75, 0x1b, 0x06, 0x4d, 0xe5, 0xf2, 0xe3, 0x8a, 0x1e, 0x12, 0xaa, - 0xaa, 0xab, 0x82, 0x0e, 0xca, 0xba, 0x81, 0x33, 0x31, 0x91, 0x68, 0xe1, 0x64, 0x92, 0x0a, 0xe0, 0xf0, 0x60, 0x33, - 0x98, 0xd4, 0xe8, 0xb6, 0x3d, 0xe8, 0x9f, 0x07, 0xf7, 0xec, 0x7b, 0xea, 0xe5, 0x94, 0x05, 0xe0, 0x5d, 0xd0, 0xf4, - 0xa7, 0xa8, 0x45, 0xe0, 0xe7, 0x8c, 0x01, 0x92, 0xe7, 0x54, 0x34, 0x92, 0x45, 0x73, 0x2c, 0x3a, 0x0c, 0x10, 0x54, - 0xaf, 0xd0, 0xd6, 0x9f, 0x58, 0x93, 0x51, 0x48, 0xb0, 0x6f, 0x6c, 0xc0, 0xd2, 0x6c, 0x76, 0x06, 0x78, 0xde, 0x90, - 0xf3, 0xe2, 0xfb, 0x98, 0x83, 0x4a, 0xd8, 0xda, 0xb3, 0xdd, 0xbe, 0x6d, 0xe1, 0xd2, 0xf6, 0xb2, 0xcd, 0x50, 0x50, - 0x38, 0xde, 0x7c, 0xcf, 0x82, 0xc9, 0x5e, 0xd8, 0xee, 0x3b, 0xb9, 0x50, 0x1d, 0x09, 0x9e, 0x5b, 0x0a, 0x09, 0xde, - 0xf6, 0x26, 0x20, 0xd0, 0x91, 0x73, 0xd7, 0xed, 0x4d, 0x55, 0x08, 0x45, 0x1f, 0x37, 0x47, 0x6e, 0x10, 0xc3, 0x0f, - 0xa7, 0x85, 0x4c, 0x33, 0xd1, 0x7d, 0xb5, 0x66, 0x76, 0x83, 0x91, 0xb2, 0xc8, 0x93, 0x30, 0xdb, 0x74, 0x30, 0x42, - 0x0b, 0x92, 0x76, 0xb7, 0x0f, 0x30, 0x6c, 0x3a, 0x8a, 0xd3, 0xb6, 0x14, 0xab, 0x29, 0xfb, 0xfc, 0xa8, 0x5a, 0x0e, - 0xd6, 0x8f, 0x98, 0x5f, 0x69, 0x1f, 0x00, 0x2b, 0x48, 0xbc, 0x7c, 0xa0, 0xce, 0xbc, 0x9e, 0xd7, 0xce, 0xb7, 0x16, - 0x4a, 0x14, 0x31, 0xcf, 0x90, 0x50, 0xbc, 0xd4, 0x6e, 0x98, 0x30, 0xb7, 0x67, 0x48, 0x0c, 0xcd, 0xf2, 0x61, 0x1b, - 0x98, 0x5e, 0x05, 0xd8, 0x53, 0x73, 0x5b, 0x24, 0x61, 0xd5, 0xdc, 0x3b, 0x02, 0xd6, 0x1e, 0x84, 0xaf, 0xc4, 0x89, - 0x63, 0x4f, 0x45, 0xf3, 0x59, 0x12, 0x3e, 0x6f, 0x1c, 0x17, 0x47, 0x78, 0x22, 0x74, 0xe0, 0x0f, 0xe7, 0x39, 0xc8, - 0x03, 0xfe, 0x1a, 0x2c, 0x83, 0x50, 0x36, 0x45, 0x47, 0x0f, 0x8f, 0x80, 0x3d, 0x42, 0xbc, 0x11, 0x36, 0x37, 0xaa, - 0xd1, 0xa2, 0x24, 0xe3, 0x85, 0x0e, 0x86, 0x7b, 0x5c, 0xba, 0xf6, 0x28, 0x18, 0xe4, 0x89, 0xb1, 0x83, 0x67, 0xfe, - 0xfe, 0x10, 0xab, 0x71, 0x82, 0xc2, 0x2d, 0x69, 0xb7, 0x55, 0xe2, 0xef, 0xc0, 0x4f, 0x41, 0x82, 0x63, 0x1d, 0xf8, - 0x59, 0x1b, 0x1b, 0x89, 0x44, 0x6a, 0x37, 0xed, 0xd1, 0x49, 0x04, 0xc6, 0x83, 0x73, 0x3f, 0x85, 0x6a, 0x24, 0x11, - 0x15, 0xe5, 0x68, 0x81, 0x9a, 0xa7, 0x6a, 0x15, 0x7c, 0x47, 0x66, 0x04, 0x9e, 0x63, 0xd8, 0x9a, 0xfc, 0x54, 0xdd, - 0x58, 0xc4, 0xf2, 0x5d, 0x97, 0x8e, 0xb6, 0xf0, 0x00, 0x52, 0x30, 0x9a, 0x60, 0x18, 0x97, 0x82, 0x92, 0x15, 0xff, - 0x7d, 0x34, 0x62, 0xe5, 0x93, 0xa3, 0x6c, 0x73, 0x73, 0x20, 0xce, 0x2d, 0x88, 0x71, 0xb8, 0x11, 0x5d, 0x8d, 0x2b, - 0x00, 0xea, 0xd3, 0x39, 0x71, 0x3d, 0x30, 0xad, 0x58, 0xd3, 0xa5, 0xd8, 0x27, 0x87, 0x19, 0x80, 0x82, 0x5b, 0xce, - 0x91, 0xdf, 0xff, 0xcb, 0x00, 0xdc, 0x63, 0xff, 0x4b, 0x77, 0x4b, 0x09, 0x9a, 0x9e, 0x3c, 0x53, 0x5c, 0xd0, 0x19, - 0x6b, 0xc7, 0xa3, 0xd8, 0x68, 0x50, 0x78, 0x29, 0x60, 0x00, 0xda, 0x1c, 0x64, 0x42, 0xc5, 0x41, 0xc8, 0x51, 0x81, - 0xed, 0xe3, 0xe6, 0xe7, 0xb8, 0xb3, 0x9f, 0x82, 0x85, 0xd7, 0xd7, 0x6f, 0x8f, 0xe1, 0xed, 0x2f, 0xfa, 0xed, 0x25, - 0x0b, 0x7e, 0x2d, 0x65, 0xe8, 0xbe, 0x36, 0xc5, 0x03, 0x35, 0x45, 0x29, 0x96, 0xc8, 0xa0, 0x21, 0x73, 0xf3, 0xa5, - 0x98, 0x0d, 0x77, 0x4b, 0x54, 0x3b, 0x52, 0x74, 0xe5, 0x3e, 0x8f, 0x4e, 0x91, 0xb8, 0xae, 0x49, 0x0a, 0x23, 0x97, - 0xc0, 0x44, 0xb8, 0xe2, 0x5b, 0x62, 0xce, 0xbd, 0x36, 0xd8, 0xe0, 0xb5, 0xbc, 0x03, 0xb4, 0xef, 0xd8, 0x74, 0xc6, - 0x2f, 0x0f, 0x48, 0xd1, 0x07, 0x32, 0x6d, 0x40, 0x9c, 0x9d, 0xb7, 0x7b, 0xf1, 0x2e, 0xef, 0xc5, 0x20, 0xd5, 0x73, - 0xc5, 0x62, 0xb8, 0x57, 0xbd, 0xb7, 0x18, 0xa5, 0x34, 0x99, 0xc9, 0xab, 0xa1, 0xd7, 0x95, 0xe8, 0x6d, 0x6e, 0x02, - 0x82, 0x3d, 0xa3, 0x2b, 0x17, 0x5d, 0xcb, 0x52, 0xd0, 0x04, 0x20, 0x7a, 0x54, 0x67, 0x39, 0xe2, 0x38, 0xcc, 0x66, - 0x43, 0xc1, 0xc1, 0xdc, 0x95, 0xa3, 0xe2, 0x98, 0xd8, 0x5d, 0x26, 0xec, 0x00, 0x66, 0xc4, 0xe5, 0xad, 0x8e, 0x88, - 0x0e, 0x8b, 0xfe, 0x3a, 0xbe, 0xfd, 0xd1, 0x63, 0x9b, 0x1d, 0x17, 0x34, 0x48, 0x6d, 0xac, 0x87, 0xd5, 0x58, 0x50, - 0x1f, 0x7e, 0xd4, 0x54, 0x2a, 0x8b, 0xcd, 0xcd, 0xb2, 0x7e, 0x54, 0xab, 0x76, 0x70, 0xed, 0x34, 0xe5, 0xa2, 0x99, - 0x0d, 0xc2, 0x81, 0x88, 0x09, 0x14, 0x68, 0x69, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0x94, 0x4f, 0x21, 0xf3, 0xe2, - 0xb2, 0xd4, 0xa9, 0x2f, 0x32, 0x1e, 0x19, 0xe2, 0xa9, 0x27, 0x19, 0x2b, 0xa0, 0x60, 0xbd, 0xd4, 0x4b, 0x68, 0x89, - 0x00, 0xf3, 0x67, 0x2a, 0x87, 0x46, 0x58, 0x20, 0x51, 0x68, 0x98, 0x25, 0xca, 0xf8, 0x2c, 0xc2, 0x18, 0xb4, 0xfd, - 0x93, 0x5a, 0xec, 0xab, 0x50, 0x46, 0x47, 0x71, 0x94, 0x0f, 0x02, 0xaa, 0x9f, 0x4b, 0x09, 0x36, 0x09, 0xdf, 0x03, - 0x1b, 0x55, 0x8e, 0x27, 0x09, 0xc2, 0xa7, 0x71, 0xce, 0xc8, 0x53, 0x58, 0x97, 0x30, 0x4b, 0xd3, 0x36, 0x52, 0xed, - 0x22, 0x33, 0x08, 0xe5, 0xc2, 0xfc, 0x13, 0xe3, 0xec, 0x22, 0x0b, 0x97, 0x5a, 0x83, 0xf9, 0xf1, 0xc6, 0x04, 0x28, - 0xbb, 0xba, 0xca, 0x84, 0x8f, 0x1b, 0x91, 0xbd, 0xa1, 0x2b, 0x26, 0x7d, 0x85, 0x54, 0xe0, 0x44, 0x64, 0xf1, 0xd0, - 0x19, 0x0a, 0x8d, 0x70, 0x48, 0xa7, 0xc8, 0xb9, 0x6b, 0x6c, 0xfa, 0xbc, 0xaf, 0x7d, 0xa3, 0x34, 0x74, 0x12, 0x10, - 0x02, 0x02, 0x77, 0xc3, 0x9a, 0x4a, 0xfb, 0x69, 0x90, 0x50, 0x29, 0xfa, 0x39, 0x80, 0x7f, 0x18, 0x49, 0x0a, 0x80, - 0xfd, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, 0x2e, 0x00, 0xcd, 0x75, 0x80, 0x2b, 0xe1, 0x0b, 0x03, 0x15, 0xa6, 0xa7, - 0x59, 0x79, 0x29, 0x94, 0xc8, 0xbb, 0x15, 0x29, 0x6b, 0x24, 0x93, 0xcf, 0xd0, 0xe1, 0x53, 0xde, 0xf5, 0x6b, 0x89, - 0x87, 0x2e, 0x78, 0x0a, 0xcb, 0xaa, 0x9e, 0x5f, 0x85, 0x9c, 0x9c, 0x6b, 0xd0, 0x15, 0x52, 0xe8, 0x2f, 0x39, 0xc9, - 0xfb, 0xaf, 0xfc, 0xaa, 0x96, 0x1a, 0x43, 0xd9, 0xfb, 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, 0x29, 0x58, 0x01, - 0xc6, 0xa1, 0x96, 0x4a, 0xa6, 0x91, 0xc4, 0x97, 0x4a, 0x14, 0x98, 0xf2, 0x60, 0x08, 0x7e, 0x6a, 0xf3, 0xa4, 0xeb, - 0xc2, 0xf5, 0xe3, 0x29, 0xa6, 0xf6, 0x10, 0xe8, 0xb1, 0xb7, 0x06, 0xa6, 0x44, 0x5d, 0x87, 0x15, 0xc4, 0xa1, 0x59, - 0x4d, 0xb3, 0x80, 0x19, 0xd3, 0x06, 0x2d, 0xd9, 0x06, 0x5b, 0x2e, 0x07, 0xfb, 0x48, 0x6c, 0xcf, 0x6a, 0x05, 0x84, - 0xae, 0x41, 0x03, 0x43, 0xee, 0x52, 0xa1, 0x85, 0x79, 0xaf, 0x4b, 0x45, 0xb8, 0x3f, 0xfb, 0x5c, 0x5a, 0xc1, 0x99, - 0x97, 0xd1, 0xc0, 0x07, 0xf1, 0x49, 0x82, 0x89, 0x2f, 0x88, 0x15, 0xd8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x38, 0x15, - 0x17, 0x29, 0x83, 0x65, 0x45, 0xa9, 0x0d, 0x3f, 0xa4, 0xc8, 0xd6, 0x5d, 0x1e, 0xe8, 0x2e, 0xc4, 0x02, 0xd8, 0xe9, - 0x57, 0x8c, 0x7c, 0xcb, 0x7a, 0x19, 0x30, 0x38, 0xd3, 0x1a, 0x07, 0x81, 0xdf, 0xdc, 0x4c, 0x06, 0x65, 0x4a, 0x6c, - 0xd7, 0x64, 0x75, 0x01, 0x39, 0x0c, 0xd5, 0xc4, 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, 0xdf, 0x77, 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, 0xdd, 0x64, 0x62, 0xda, 0x5b, 0x42, 0xea, 0x08, 0xbb, 0x87, 0x72, 0x82, 0xbb, 0x9a, 0x67, - 0x9f, 0x87, 0xb3, 0xeb, 0x99, 0x7b, 0xdf, 0x60, 0xee, 0xc7, 0x21, 0x37, 0x18, 0x3d, 0x96, 0x09, 0x3f, 0x32, 0xf6, - 0x91, 0xab, 0xaa, 0x27, 0xa7, 0x61, 0x25, 0xb2, 0xc4, 0x93, 0x71, 0xd4, 0x61, 0x9c, 0x8a, 0xd6, 0x04, 0xd9, 0xd5, - 0x55, 0x61, 0xee, 0x05, 0x0a, 0x9a, 0x7a, 0xbc, 0x1e, 0xa7, 0xad, 0xd8, 0xd9, 0x88, 0x44, 0xee, 0xbf, 0xaa, 0x45, - 0x22, 0x2b, 0x3e, 0xc7, 0x91, 0xae, 0x39, 0xc8, 0x7d, 0x72, 0xba, 0xbc, 0x49, 0x85, 0x6e, 0xd1, 0x68, 0x1b, 0x7b, - 0x54, 0x1f, 0x48, 0xea, 0x19, 0x15, 0x58, 0xd5, 0xd8, 0x1b, 0x1b, 0x1d, 0x91, 0x6e, 0xa9, 0x14, 0x1b, 0x2c, 0x2d, - 0x8c, 0x66, 0x8c, 0x82, 0x41, 0x49, 0x91, 0x81, 0x1a, 0xe5, 0xd7, 0x08, 0x86, 0x7d, 0x6a, 0x00, 0x8a, 0x73, 0x75, - 0xf5, 0xe3, 0x52, 0xb2, 0x85, 0x80, 0xc4, 0x5d, 0x30, 0x10, 0x6b, 0x82, 0x99, 0x91, 0x4f, 0xde, 0x03, 0xe7, 0xf5, - 0x19, 0xfa, 0x08, 0xe0, 0x17, 0x88, 0x4d, 0x0f, 0x26, 0xb6, 0x4d, 0x44, 0xd1, 0x67, 0x03, 0xcf, 0x01, 0xd8, 0x59, - 0x15, 0x1a, 0x7d, 0x57, 0xa5, 0x80, 0x21, 0x1b, 0xb8, 0x01, 0xab, 0xc2, 0x72, 0xfb, 0xcf, 0xc1, 0x6d, 0x80, 0xd7, - 0x67, 0xb2, 0xf9, 0x3a, 0xe6, 0x09, 0x56, 0x67, 0x17, 0x7e, 0x65, 0x59, 0x8b, 0x73, 0xa7, 0xc3, 0x46, 0xbd, 0xa2, - 0x84, 0xa8, 0x3d, 0xc0, 0xda, 0x43, 0x8c, 0xb0, 0x88, 0xf7, 0x57, 0xf8, 0xae, 0xc7, 0x2d, 0xf7, 0x35, 0x5a, 0x84, - 0xe9, 0x32, 0x69, 0x0c, 0x4a, 0xd6, 0xfd, 0x64, 0xc4, 0xbd, 0x3c, 0x10, 0xb1, 0xe0, 0x0a, 0x47, 0x56, 0x85, 0x14, - 0x1b, 0x48, 0xd2, 0xd3, 0x3e, 0x1d, 0xb0, 0xaf, 0x37, 0x7b, 0x01, 0x65, 0xde, 0x57, 0xa4, 0x92, 0x90, 0xd2, 0xec, - 0x86, 0x48, 0x12, 0xd6, 0x8a, 0x3c, 0x75, 0x3e, 0x70, 0xb4, 0xcf, 0xad, 0x24, 0x82, 0x11, 0x9c, 0x84, 0xe9, 0x58, - 0x79, 0xd8, 0x14, 0xe0, 0x2a, 0x3a, 0x62, 0xfa, 0x26, 0x20, 0xbf, 0x19, 0xc8, 0xed, 0xa5, 0xe4, 0xda, 0x5c, 0xc3, - 0xf0, 0x04, 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, 0xdf, 0x31, 0x47, 0xc8, 0x5c, 0x61, 0x7d, - 0xce, 0x9c, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x49, 0x2d, 0x5e, 0xa7, 0x4d, 0x29, 0x11, 0x93, 0x12, 0x73, 0x5d, - 0xa4, 0x62, 0x33, 0x25, 0xee, 0xdc, 0xfa, 0x46, 0x0b, 0x69, 0xa3, 0xad, 0x8b, 0x1c, 0x6c, 0x56, 0xc9, 0x7b, 0x02, - 0xe3, 0xb9, 0x20, 0x7c, 0xf9, 0x8a, 0x92, 0x74, 0x98, 0x63, 0x22, 0x58, 0xbd, 0x98, 0x8a, 0xfc, 0x9d, 0xa3, 0xd3, - 0xec, 0x0d, 0x7a, 0x90, 0x7a, 0x03, 0x89, 0x59, 0x13, 0xdf, 0x85, 0x34, 0xd4, 0x11, 0x02, 0x95, 0x51, 0x2d, 0xd3, - 0x71, 0x62, 0x15, 0xbe, 0x11, 0x7c, 0xf5, 0x56, 0x1f, 0xe7, 0x1b, 0xcf, 0x8d, 0xd5, 0x08, 0x62, 0xf0, 0x16, 0xf2, - 0x81, 0x27, 0x45, 0x38, 0x10, 0x2e, 0xdf, 0xdc, 0xec, 0xe5, 0xbb, 0xbc, 0x0a, 0x91, 0x54, 0x30, 0xc6, 0x98, 0x51, - 0x8c, 0x7b, 0xa2, 0xa6, 0x16, 0x73, 0x18, 0x58, 0xb6, 0x0e, 0x73, 0x3c, 0x00, 0x80, 0x96, 0xa6, 0xf4, 0xaa, 0xa9, - 0x50, 0x79, 0x9e, 0x4b, 0xf8, 0x54, 0x87, 0xa8, 0xaa, 0xf1, 0xdb, 0xd5, 0x19, 0x28, 0x04, 0xf7, 0x5a, 0xc7, 0xc3, - 0x43, 0x08, 0x58, 0x45, 0x21, 0x0b, 0xf4, 0x06, 0xed, 0x55, 0x89, 0x50, 0xcc, 0x9c, 0xac, 0xc7, 0x0c, 0x27, 0x15, - 0x6c, 0xa1, 0x12, 0x96, 0x4a, 0x0b, 0xfc, 0x6a, 0x23, 0x34, 0x4f, 0x19, 0xf7, 0x5f, 0x55, 0x38, 0x83, 0xfe, 0x60, - 0xde, 0x32, 0xa3, 0xbe, 0x5d, 0x3a, 0x91, 0xa9, 0xc0, 0xc4, 0xcd, 0x2c, 0xb5, 0xdf, 0xaf, 0xab, 0xb4, 0x9f, 0x57, - 0xc8, 0x7d, 0x4e, 0x9a, 0xaf, 0x73, 0x07, 0xcd, 0x27, 0xc3, 0xfd, 0x4a, 0xf9, 0xa1, 0x85, 0x51, 0x53, 0x7e, 0x79, - 0x5d, 0xf9, 0x15, 0x9e, 0x0a, 0x6f, 0xed, 0x75, 0x51, 0xe8, 0xa2, 0x3e, 0x07, 0x43, 0x48, 0x3f, 0x82, 0x6b, 0x68, - 0xf0, 0xa0, 0x48, 0x16, 0x8b, 0xb5, 0x0b, 0xe2, 0xfa, 0x98, 0x53, 0xed, 0x50, 0xc6, 0x18, 0xf1, 0xb4, 0xe4, 0x20, - 0xc9, 0xe0, 0x60, 0xfc, 0x06, 0x06, 0xc4, 0xa4, 0x24, 0xa4, 0x43, 0xe8, 0xac, 0xcc, 0x44, 0x54, 0xee, 0xe2, 0xed, - 0xc6, 0x65, 0x4d, 0xa1, 0x08, 0x3b, 0xc1, 0x4c, 0xa5, 0x54, 0x10, 0x48, 0x93, 0x6f, 0xad, 0x53, 0x0b, 0x86, 0x16, - 0xae, 0xa9, 0x80, 0xbc, 0xb6, 0xeb, 0x41, 0x93, 0xf7, 0x14, 0x43, 0x5f, 0xa5, 0x46, 0xbc, 0xcc, 0xe0, 0x6b, 0xd8, - 0xfc, 0x35, 0x51, 0x92, 0x87, 0x4c, 0xc4, 0x5e, 0xc1, 0x27, 0x42, 0x36, 0x05, 0x3b, 0x13, 0xe8, 0x87, 0x76, 0x65, - 0x2f, 0xdd, 0x2d, 0x2a, 0x97, 0x16, 0x8d, 0xad, 0x44, 0xcd, 0x9a, 0x1f, 0xc5, 0x9b, 0x29, 0xec, 0x67, 0x8f, 0x12, - 0x08, 0x48, 0x53, 0x39, 0x49, 0x35, 0xef, 0x51, 0x3a, 0x00, 0x90, 0x60, 0xf7, 0x13, 0x58, 0xe8, 0x37, 0x25, 0x26, - 0x58, 0x54, 0x8d, 0xdd, 0x66, 0xa0, 0x35, 0x67, 0xa4, 0xf9, 0x66, 0xa8, 0xb5, 0x37, 0x95, 0xf5, 0x8c, 0xd9, 0x01, - 0xb6, 0xed, 0x6e, 0x16, 0x47, 0xe9, 0x66, 0x67, 0x60, 0x08, 0x2e, 0x3c, 0xfe, 0x4f, 0x4a, 0x4c, 0x03, 0xc9, 0xa5, - 0x6e, 0xfc, 0x84, 0x3a, 0x0c, 0xff, 0x5b, 0x90, 0x02, 0xee, 0xd7, 0x56, 0x63, 0xc9, 0xb9, 0x57, 0x1c, 0x25, 0x97, - 0x55, 0xb5, 0xab, 0x25, 0x68, 0xe8, 0x46, 0x32, 0x26, 0x8a, 0x79, 0x4e, 0x00, 0x8c, 0x62, 0xf3, 0xa7, 0x4c, 0x27, - 0x79, 0xff, 0xba, 0x32, 0xb5, 0xdb, 0xf7, 0xfd, 0x28, 0x3f, 0xa5, 0x23, 0x15, 0x95, 0xcd, 0x49, 0xcc, 0xbf, 0x2d, - 0xc0, 0x34, 0x27, 0x3e, 0xd2, 0x73, 0x0d, 0x42, 0x01, 0xbe, 0xb2, 0xa1, 0xd4, 0x6c, 0x8f, 0xff, 0xe8, 0x6c, 0xf7, - 0x25, 0x51, 0x04, 0x0b, 0x34, 0xe8, 0x72, 0x0d, 0xbe, 0x80, 0x65, 0x70, 0x47, 0xfa, 0x29, 0xf8, 0x5e, 0x5e, 0x07, - 0x9f, 0xb1, 0xff, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x4e, 0xd3, 0xb0, 0x12, 0xe2, 0x12, 0x15, 0x66, 0x15, 0xe7, - 0x8f, 0xeb, 0xbc, 0x6e, 0x5a, 0x96, 0x18, 0x94, 0x9f, 0xb9, 0x86, 0x1b, 0xdf, 0x6b, 0xe4, 0x8f, 0xef, 0x3f, 0x07, - 0xdd, 0x4e, 0xa4, 0xdd, 0xd8, 0xc8, 0xd7, 0xc8, 0x42, 0xc3, 0x7b, 0x61, 0x33, 0x68, 0x8b, 0x74, 0xc9, 0xd5, 0x33, - 0x16, 0xe3, 0x6d, 0x11, 0x2a, 0xc3, 0x07, 0x2c, 0x98, 0x01, 0x86, 0xe0, 0xb1, 0x53, 0x99, 0x7c, 0x86, 0x8d, 0xa6, - 0xd8, 0x35, 0x17, 0x06, 0x1f, 0xa8, 0xca, 0x42, 0xf2, 0x62, 0x9d, 0x6c, 0xcf, 0xce, 0xe0, 0xf9, 0x65, 0x5c, 0x00, - 0x75, 0x00, 0xfd, 0x8a, 0xca, 0x62, 0x03, 0xb9, 0xb8, 0x29, 0x6b, 0xbd, 0xa2, 0xd1, 0xe8, 0xc6, 0x2e, 0xac, 0xae, - 0xc0, 0x27, 0x51, 0x3a, 0x4a, 0xc4, 0x24, 0x66, 0x52, 0xe5, 0x8a, 0x5c, 0x1b, 0xdd, 0x4b, 0x5b, 0x34, 0xcf, 0x85, - 0x04, 0xaf, 0x08, 0xdc, 0x10, 0xfa, 0x4a, 0x5f, 0xae, 0x36, 0x50, 0xf0, 0xa8, 0xbd, 0xb9, 0x08, 0x26, 0x26, 0x1e, - 0x33, 0xa4, 0xa6, 0x5f, 0x87, 0x53, 0x2b, 0x8b, 0x25, 0x87, 0x5f, 0xe7, 0x8c, 0x35, 0x14, 0x00, 0xf1, 0xc9, 0x83, - 0xeb, 0xdd, 0xa4, 0x57, 0x4a, 0x3b, 0x28, 0x8d, 0x10, 0xdf, 0x55, 0xf8, 0xba, 0x0b, 0xc5, 0x57, 0xae, 0xba, 0xf7, - 0x35, 0x65, 0xc6, 0x05, 0xa3, 0xe7, 0x7c, 0x9a, 0x34, 0xae, 0xdd, 0xd0, 0x5d, 0x9d, 0x9f, 0xbc, 0x1f, 0x65, 0xde, - 0xc2, 0x14, 0xd8, 0x04, 0x54, 0xc1, 0x73, 0xef, 0xb5, 0x71, 0xa2, 0xfc, 0x9d, 0x79, 0xc4, 0x2b, 0x87, 0x59, 0x75, - 0x92, 0xfc, 0x5d, 0xff, 0xbb, 0xe0, 0xfa, 0x96, 0xc6, 0x09, 0x72, 0x57, 0x9d, 0x20, 0x13, 0xe5, 0xa6, 0x7a, 0xc3, - 0xed, 0xdf, 0x55, 0x20, 0x88, 0x53, 0x31, 0x7d, 0x54, 0x8e, 0xeb, 0x47, 0x0b, 0x54, 0x2a, 0x22, 0x3e, 0x55, 0xb9, - 0x2b, 0xd7, 0xa6, 0x86, 0x7a, 0x5c, 0x27, 0xb3, 0xd0, 0x34, 0x2b, 0x72, 0x29, 0x9b, 0x1e, 0x23, 0xd3, 0xec, 0x54, - 0x9b, 0xdf, 0xbd, 0xf6, 0x90, 0x8e, 0xa1, 0xb9, 0x58, 0xab, 0x05, 0xf7, 0xbb, 0x8a, 0xc2, 0xbb, 0x5e, 0x6c, 0xa4, - 0x32, 0xd4, 0xac, 0x47, 0xd1, 0xc7, 0x71, 0x9b, 0xb9, 0x3c, 0xca, 0xfe, 0xac, 0x01, 0x60, 0x3a, 0xc2, 0xa2, 0xbb, - 0xe9, 0x19, 0x7b, 0x02, 0x3d, 0x3d, 0x91, 0x41, 0xa2, 0xd7, 0x3a, 0x5f, 0xb5, 0x4a, 0x2c, 0x5d, 0x41, 0x60, 0xf7, - 0x86, 0x8c, 0x55, 0x49, 0xbb, 0xe5, 0xfa, 0xe5, 0x3c, 0x9f, 0xa7, 0x7c, 0x29, 0xcf, 0xa7, 0x66, 0xd1, 0xad, 0xb5, - 0xdd, 0x9b, 0x53, 0x43, 0xc5, 0x5c, 0xab, 0x9b, 0xfc, 0x81, 0xe9, 0x3a, 0x18, 0x6a, 0x11, 0x64, 0x56, 0xbb, 0xea, - 0x59, 0x59, 0x4e, 0xeb, 0x99, 0x1c, 0x53, 0xe1, 0x9b, 0x4a, 0x77, 0x88, 0x6e, 0x98, 0xaa, 0x99, 0x7e, 0x6a, 0x6c, - 0x0b, 0xd9, 0xe6, 0xf9, 0xe5, 0x28, 0x07, 0x4a, 0xcb, 0xfd, 0x65, 0xc2, 0xf0, 0xd3, 0xd5, 0xd5, 0x4f, 0x42, 0x4e, - 0x55, 0x1d, 0xbd, 0xc5, 0x4b, 0xdd, 0x33, 0x98, 0x96, 0xca, 0x89, 0x38, 0x61, 0xab, 0x07, 0x6f, 0xee, 0x5e, 0x01, - 0xcb, 0x09, 0x60, 0x77, 0xc2, 0x9c, 0xc6, 0x50, 0xd5, 0x06, 0xfe, 0x71, 0xf5, 0x60, 0xab, 0xf6, 0xf0, 0x8f, 0xfd, - 0x1f, 0x83, 0x1b, 0x1b, 0x1b, 0xdb, 0x78, 0xbb, 0x96, 0x08, 0xf2, 0x0a, 0x0f, 0xf4, 0xf1, 0xea, 0xa3, 0xa0, 0xe5, - 0x2a, 0xb1, 0xdd, 0x77, 0x28, 0x6c, 0x0d, 0xf2, 0x4d, 0xca, 0xa4, 0xe1, 0xbc, 0xe0, 0xd9, 0x54, 0xce, 0x50, 0xc8, - 0x6b, 0x3e, 0x0e, 0xda, 0x8e, 0xf0, 0x37, 0x70, 0x6a, 0xc7, 0xcb, 0x8b, 0x4f, 0xd0, 0x07, 0x3c, 0x5d, 0x29, 0x4d, - 0x45, 0x9c, 0x52, 0x6e, 0xd1, 0xe5, 0x3a, 0x0f, 0x46, 0x8a, 0x8b, 0x09, 0x2a, 0x1d, 0x77, 0x71, 0xe3, 0x6c, 0xe4, - 0xf4, 0x97, 0x78, 0x75, 0x91, 0x2e, 0x1f, 0x89, 0x6c, 0xd5, 0xd2, 0xfb, 0x5d, 0x9f, 0x6e, 0xdb, 0x53, 0xc6, 0x27, - 0xd9, 0x88, 0x0e, 0x66, 0x7c, 0x9c, 0x08, 0xaf, 0x4f, 0x8c, 0xf4, 0xdd, 0x22, 0x30, 0xdd, 0x1c, 0x9b, 0xfc, 0x70, - 0xbc, 0xde, 0x6c, 0xd6, 0xb8, 0xfd, 0x37, 0xce, 0x13, 0x67, 0x51, 0x62, 0x44, 0x65, 0xa1, 0xe1, 0x01, 0xad, 0x10, - 0x37, 0xef, 0x99, 0xc0, 0xb8, 0xec, 0x92, 0xa4, 0xb6, 0x1b, 0x08, 0x5c, 0xec, 0x71, 0xcc, 0x92, 0x91, 0xed, 0x41, - 0x79, 0xa0, 0x2f, 0x46, 0xd3, 0x2d, 0x60, 0x5a, 0x5e, 0x3b, 0x3b, 0x4f, 0x6d, 0xaf, 0x9a, 0x2a, 0x80, 0x59, 0xb2, - 0x3c, 0x3e, 0x45, 0xd6, 0xfd, 0x16, 0xba, 0x88, 0x01, 0x63, 0xe3, 0xca, 0x9c, 0xbb, 0x58, 0xb5, 0x22, 0xbe, 0xd1, - 0x44, 0x9a, 0xd4, 0x47, 0xd4, 0x77, 0x10, 0xd6, 0xea, 0x2a, 0x07, 0x09, 0xdc, 0x23, 0xef, 0x8e, 0xb8, 0xf4, 0xf4, - 0x99, 0xc5, 0xb8, 0x4a, 0xdf, 0x52, 0xd7, 0xe2, 0x9a, 0x61, 0xaf, 0xb8, 0x0f, 0xf6, 0x07, 0xc6, 0x2d, 0x62, 0x11, - 0x6f, 0xe7, 0xb5, 0x14, 0xd6, 0xc6, 0x1c, 0x68, 0x6e, 0xb8, 0xc1, 0xef, 0xac, 0x5a, 0x33, 0x30, 0xc3, 0x8c, 0x33, - 0x92, 0xdf, 0x8d, 0x7b, 0x55, 0x63, 0x47, 0xae, 0x02, 0x88, 0xbe, 0x05, 0x5d, 0x92, 0xc3, 0x2b, 0x59, 0xae, 0x3a, - 0x43, 0xfe, 0x0d, 0xd6, 0x59, 0x2f, 0x4e, 0xc0, 0x4c, 0x9a, 0xf2, 0x12, 0x13, 0x53, 0xc4, 0xe5, 0x66, 0x19, 0xf3, - 0x34, 0x7d, 0x16, 0xed, 0xe0, 0xe4, 0x46, 0x02, 0x47, 0xec, 0x1b, 0xcb, 0xd0, 0x4c, 0xd8, 0x88, 0x89, 0x34, 0x2a, - 0xa5, 0x84, 0xf7, 0xe5, 0x52, 0x4b, 0xfe, 0x32, 0x97, 0x57, 0x5f, 0x6e, 0x13, 0x1c, 0x90, 0xd7, 0xc0, 0x72, 0x68, - 0x1c, 0xb7, 0x0c, 0x24, 0x62, 0x31, 0x20, 0x46, 0xad, 0xca, 0xe5, 0x64, 0x54, 0x27, 0xf3, 0x15, 0x72, 0xa1, 0x22, - 0x0f, 0x6e, 0x09, 0x94, 0xfc, 0x39, 0xa6, 0x0e, 0x66, 0xa5, 0x76, 0xd3, 0x62, 0x93, 0xe4, 0x3d, 0x33, 0x20, 0xb9, - 0xfa, 0x1a, 0x1e, 0x1a, 0xbf, 0x78, 0x65, 0x4e, 0x09, 0x5f, 0x94, 0xb1, 0xb4, 0x34, 0xe6, 0xd2, 0xbf, 0x90, 0xf7, - 0x69, 0x25, 0x60, 0xbf, 0x81, 0x98, 0x32, 0x70, 0x89, 0x8d, 0x0b, 0x92, 0xf2, 0x5a, 0x9e, 0xb2, 0xfb, 0x1a, 0xca, - 0x77, 0xc9, 0xa4, 0xab, 0x54, 0xd6, 0x35, 0x56, 0xdd, 0x6f, 0x73, 0x96, 0x5f, 0x1e, 0x30, 0xcc, 0x4d, 0x46, 0x83, - 0x6c, 0xc9, 0xcc, 0xa6, 0xfc, 0x6a, 0xef, 0xc6, 0xaf, 0x3c, 0x94, 0x74, 0xa8, 0x56, 0xe9, 0xe6, 0xa5, 0x1b, 0x8e, - 0x71, 0xe3, 0x86, 0x23, 0x80, 0x8d, 0x61, 0xa7, 0x8a, 0xd4, 0x3a, 0xff, 0x7d, 0x39, 0xfc, 0x44, 0x7b, 0x6d, 0xa0, - 0x77, 0xdd, 0x60, 0x65, 0x7a, 0xfa, 0x0d, 0xa8, 0x1a, 0x59, 0x42, 0x37, 0xa1, 0x8a, 0xc9, 0x48, 0x94, 0x98, 0xae, - 0x52, 0x1e, 0xf5, 0x35, 0xe2, 0x1c, 0xc4, 0x0d, 0xe5, 0x2f, 0xfe, 0x35, 0xbc, 0x3c, 0x0e, 0xd0, 0x88, 0x5a, 0x8c, - 0xb3, 0x94, 0xb7, 0xc6, 0xd1, 0x34, 0x4e, 0x2e, 0x83, 0x79, 0xdc, 0x9a, 0x66, 0x69, 0x56, 0xcc, 0x80, 0x2b, 0xbd, - 0xe2, 0x12, 0x6c, 0xf8, 0x69, 0x6b, 0x1e, 0x7b, 0xcf, 0x59, 0x72, 0xc6, 0x78, 0x3c, 0x8c, 0x3c, 0x7b, 0x3f, 0x07, - 0xf1, 0x60, 0xbd, 0x8e, 0xf2, 0x3c, 0x3b, 0xb7, 0xbd, 0x77, 0xd9, 0x09, 0x30, 0xad, 0xf7, 0xe6, 0xe2, 0xf2, 0x94, - 0xa5, 0xde, 0xfb, 0x93, 0x79, 0xca, 0xe7, 0x5e, 0x11, 0xa5, 0x45, 0xab, 0x60, 0x79, 0x3c, 0x06, 0x35, 0x91, 0x64, - 0x79, 0x0b, 0xf3, 0x9f, 0xa7, 0x2c, 0x48, 0xe2, 0xd3, 0x09, 0xb7, 0x46, 0x51, 0xfe, 0xa9, 0xd7, 0x6a, 0xcd, 0xf2, - 0x78, 0x1a, 0xe5, 0x97, 0x2d, 0x6a, 0x11, 0x7c, 0xd1, 0xde, 0x8e, 0xbe, 0x1a, 0xdf, 0xef, 0xf1, 0x1c, 0xfa, 0xc6, - 0x48, 0xc5, 0x00, 0x84, 0x8f, 0xb5, 0xbd, 0xd3, 0x9e, 0x16, 0x6b, 0xe2, 0x44, 0x29, 0x4a, 0x79, 0x79, 0xec, 0x7d, - 0x64, 0x00, 0xb7, 0x7f, 0xc2, 0x53, 0x0f, 0x7c, 0x39, 0x9e, 0xa5, 0x8b, 0xe1, 0x3c, 0x2f, 0x60, 0x80, 0x59, 0x16, - 0xa7, 0x9c, 0xe5, 0xbd, 0x93, 0x2c, 0x07, 0xb2, 0xb5, 0xf2, 0x68, 0x14, 0xcf, 0x8b, 0xe0, 0xfe, 0xec, 0xa2, 0x87, - 0xb6, 0xc2, 0x69, 0x9e, 0xcd, 0xd3, 0x91, 0x9c, 0x2b, 0x4e, 0x61, 0x63, 0xc4, 0xdc, 0xac, 0xa0, 0x2f, 0xa1, 0x00, - 0x7c, 0x29, 0x8b, 0xf2, 0xd6, 0x29, 0x76, 0x46, 0x43, 0xbf, 0x3d, 0x62, 0xa7, 0x5e, 0x7e, 0x7a, 0x12, 0x39, 0x9d, - 0xee, 0x43, 0x4f, 0xfd, 0xf3, 0x77, 0x5c, 0x30, 0xdc, 0x57, 0x16, 0x77, 0xda, 0xed, 0x7f, 0x70, 0x7b, 0x8d, 0x59, - 0x08, 0xa0, 0xa0, 0x33, 0xbb, 0xb0, 0x8a, 0x2c, 0x81, 0xf5, 0x59, 0xd5, 0xb3, 0x37, 0x03, 0xbf, 0x29, 0x4e, 0x4f, - 0x83, 0xee, 0xec, 0xa2, 0x44, 0xec, 0x02, 0x91, 0x90, 0x29, 0x91, 0x94, 0x6f, 0x8b, 0x3f, 0x0a, 0xf1, 0xa3, 0xd5, - 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd5, 0x5b, 0x23, 0xd8, 0x07, 0x44, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x80, 0x13, 0x98, - 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, 0x0d, 0xc6, 0xa3, 0xdb, 0xc0, 0xc8, 0xd3, 0xd1, 0xa2, 0xbe, 0xae, - 0x1d, 0x70, 0x4e, 0x7b, 0x13, 0x86, 0xfc, 0x14, 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf1, 0x89, 0x78, 0x24, 0x76, 0x3e, - 0x17, 0x75, 0x3b, 0xed, 0xb6, 0x78, 0x2f, 0x40, 0xa1, 0x05, 0x1d, 0x1f, 0x1b, 0x00, 0x13, 0x7d, 0xb8, 0xee, 0x23, - 0x36, 0x5f, 0xdf, 0xfa, 0xa5, 0x1a, 0xef, 0x5c, 0xe5, 0x0d, 0x0a, 0x15, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0xf9, - 0x5e, 0x47, 0x1f, 0x54, 0xf5, 0xbf, 0x66, 0xa4, 0xf5, 0x02, 0xee, 0x99, 0xb9, 0x40, 0xbd, 0x74, 0x0f, 0x43, 0x52, - 0xad, 0x96, 0x0b, 0x7a, 0x83, 0x61, 0x08, 0x89, 0x0e, 0x04, 0x9d, 0xbc, 0x5f, 0xd0, 0x37, 0x35, 0x32, 0x37, 0x28, - 0x9c, 0xcc, 0x85, 0x2d, 0x9f, 0x69, 0xb9, 0x0e, 0x4a, 0x1a, 0xbc, 0xec, 0x0f, 0x4c, 0x36, 0x00, 0xe9, 0x4d, 0xa1, - 0xae, 0x1f, 0x42, 0xe1, 0x4a, 0x29, 0x47, 0x6a, 0x7a, 0xd3, 0x15, 0x7d, 0x58, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x06, - 0xff, 0x0a, 0xc3, 0xde, 0xd1, 0x8e, 0x65, 0x91, 0xcd, 0xf3, 0x21, 0x45, 0xea, 0x96, 0x3d, 0x7e, 0x9b, 0x14, 0xae, - 0xed, 0x31, 0x2d, 0xe7, 0xd1, 0x0d, 0xae, 0x7d, 0xe4, 0x80, 0xd3, 0x21, 0x88, 0xb8, 0x63, 0x20, 0xa3, 0x1c, 0x0a, - 0x42, 0x54, 0x5d, 0x23, 0xca, 0x77, 0xa3, 0xfb, 0x97, 0xfe, 0x3c, 0x8d, 0x41, 0xd2, 0x7d, 0x8c, 0x47, 0x74, 0xef, - 0x24, 0x1e, 0xd1, 0x41, 0x44, 0x8b, 0x12, 0x8f, 0x30, 0xb2, 0x0d, 0x05, 0xea, 0x3b, 0x2c, 0xf0, 0x2c, 0x13, 0x59, - 0xec, 0x96, 0x8d, 0x87, 0x09, 0x86, 0xaa, 0x1c, 0x65, 0xd3, 0x28, 0x4e, 0x03, 0xfc, 0x3e, 0x88, 0xa7, 0x47, 0x0c, - 0xb0, 0x8b, 0x07, 0x3f, 0x99, 0xcc, 0x45, 0xeb, 0xb8, 0xfe, 0xaf, 0x20, 0x47, 0xa8, 0x7f, 0x29, 0xfd, 0x30, 0x0d, - 0x97, 0x3a, 0xe6, 0xad, 0x97, 0x82, 0xec, 0xe1, 0xca, 0x66, 0x65, 0x14, 0xe7, 0xd8, 0xe5, 0xf4, 0xa3, 0xdf, 0xea, - 0x04, 0x1d, 0xed, 0xba, 0xd6, 0x6e, 0xa3, 0x8a, 0x5c, 0x16, 0x79, 0xa3, 0x91, 0x60, 0xd0, 0xcf, 0x02, 0xce, 0xea, - 0x5d, 0xc3, 0xea, 0x49, 0xbe, 0xc4, 0x00, 0xce, 0x69, 0xea, 0xd4, 0x80, 0xa0, 0xb3, 0x80, 0x6b, 0xa6, 0x72, 0xcb, - 0x88, 0x94, 0xd2, 0x63, 0xda, 0xc0, 0xf5, 0xbb, 0x44, 0x78, 0x6f, 0xa8, 0x9e, 0x02, 0xa5, 0x58, 0x6e, 0x74, 0xbc, - 0x2b, 0x76, 0xbc, 0x45, 0x3c, 0x16, 0xda, 0xb0, 0x05, 0x6d, 0xeb, 0x2f, 0x43, 0xa0, 0xd2, 0xa7, 0xd0, 0x5e, 0x5f, - 0x38, 0x2a, 0xb1, 0x3e, 0x87, 0xb9, 0xf6, 0x85, 0xd6, 0xa3, 0x1b, 0xf9, 0xf6, 0xde, 0xfa, 0x82, 0x97, 0xbb, 0x5b, - 0xa2, 0xf7, 0xde, 0x71, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, 0xf4, 0x5d, 0x1c, - 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, 0x84, 0x94, 0x42, - 0xe4, 0x60, 0xf4, 0x96, 0xa1, 0x38, 0x46, 0x08, 0xfa, 0xeb, 0x8b, 0xb8, 0x0c, 0xd7, 0x17, 0x59, 0x79, 0x0c, 0x96, - 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, 0x6d, 0xab, 0x2f, - 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xf7, 0x60, 0xa0, 0xdd, 0x2d, 0xd1, - 0x08, 0xf0, 0xca, 0x11, 0xe4, 0x0a, 0x2d, 0x4c, 0xc7, 0x31, 0x51, 0x1b, 0xc5, 0x67, 0x9a, 0x74, 0x94, 0x9b, 0xbc, - 0xb7, 0x9b, 0x44, 0x27, 0x2c, 0x81, 0x21, 0x8b, 0xab, 0xab, 0x36, 0x8c, 0x24, 0x5e, 0xad, 0xdd, 0x38, 0x9d, 0xcd, - 0xe5, 0x67, 0xb6, 0x60, 0xe2, 0x0e, 0x9e, 0x7c, 0xe2, 0x2d, 0x80, 0xbe, 0x3a, 0xca, 0x0b, 0xe4, 0x00, 0x00, 0x22, - 0x9d, 0x22, 0x20, 0x74, 0x15, 0x5b, 0x40, 0x69, 0x3c, 0x5a, 0x2e, 0xd3, 0x98, 0xe5, 0xf0, 0x02, 0xdb, 0x72, 0x86, - 0xcf, 0x29, 0x3c, 0x4f, 0xe3, 0x14, 0x1f, 0x0b, 0x7c, 0x8c, 0x2e, 0xf0, 0x31, 0x2b, 0xed, 0xbf, 0xcb, 0x0a, 0x58, - 0x9a, 0x00, 0xd9, 0xd5, 0x15, 0xc8, 0x7b, 0x4d, 0x82, 0xdd, 0x2d, 0x20, 0x16, 0x72, 0x8a, 0xf8, 0xe8, 0x0a, 0x33, - 0xc9, 0xc8, 0x8a, 0x59, 0x4b, 0x94, 0x5b, 0xa4, 0x55, 0x43, 0x70, 0xba, 0x72, 0xa7, 0x61, 0x3c, 0x78, 0x32, 0xbd, - 0xe4, 0x09, 0xbe, 0xb8, 0xb6, 0x25, 0xbe, 0x8a, 0x21, 0x88, 0x42, 0x8f, 0x88, 0xa1, 0x2e, 0xe3, 0xf2, 0x7b, 0x37, - 0x71, 0x68, 0xe3, 0x2c, 0x60, 0xbf, 0xa1, 0x16, 0xe0, 0x51, 0x9c, 0x88, 0xc6, 0x2b, 0xf0, 0x69, 0xe4, 0x09, 0x12, - 0x3a, 0xbb, 0x5b, 0x15, 0x6c, 0x00, 0xfc, 0x50, 0xdc, 0xb2, 0x76, 0x44, 0x0e, 0xa4, 0x2d, 0xca, 0xe9, 0xec, 0x5c, - 0x6e, 0x69, 0x19, 0xd9, 0x15, 0xb1, 0x72, 0x8d, 0x2a, 0xe5, 0x2c, 0xda, 0xe3, 0x28, 0x5d, 0xd5, 0x14, 0xa0, 0x9f, - 0x31, 0x36, 0xf2, 0x6c, 0x0b, 0x64, 0xa9, 0x78, 0xfe, 0x98, 0xb0, 0x33, 0x26, 0xbf, 0x94, 0xa2, 0x07, 0xd1, 0x95, - 0x43, 0x50, 0xc9, 0x5c, 0x5e, 0xe2, 0x94, 0xec, 0xa9, 0x70, 0x94, 0x94, 0xa8, 0x23, 0xe2, 0xd9, 0xc6, 0xa0, 0xcd, - 0x39, 0xda, 0xf5, 0x61, 0xbd, 0x0e, 0x58, 0xbb, 0xb6, 0x80, 0x97, 0xec, 0xb8, 0xdb, 0x91, 0x83, 0x01, 0xd8, 0x78, - 0x0c, 0xdb, 0x45, 0x45, 0x96, 0xb5, 0x2c, 0x10, 0x50, 0x81, 0x53, 0xea, 0xd9, 0xa2, 0x85, 0x5d, 0xb5, 0xd5, 0x4f, - 0x92, 0x38, 0x49, 0x36, 0xfc, 0xb4, 0x62, 0x2e, 0xe0, 0x8e, 0x75, 0x11, 0x69, 0x6d, 0xc8, 0x37, 0xfb, 0x5f, 0xfe, - 0xf9, 0xbf, 0xff, 0x67, 0x18, 0x98, 0xfa, 0xb9, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x06, 0xad, 0xe6, 0xe9, 0x2d, 0xed, - 0xfe, 0xf6, 0x1f, 0xfe, 0x0b, 0x34, 0xa3, 0x1b, 0x39, 0x6e, 0x79, 0x4c, 0x10, 0x0d, 0xd1, 0x08, 0xfa, 0x2c, 0x90, - 0x6a, 0x83, 0x5c, 0x3b, 0xd3, 0x3f, 0x21, 0xd8, 0x05, 0xcf, 0x66, 0x37, 0x82, 0x83, 0x50, 0x0f, 0x93, 0xac, 0x60, - 0x1a, 0x1e, 0x21, 0x6b, 0x3f, 0x0f, 0x20, 0x9a, 0x6b, 0x06, 0x5c, 0x5e, 0x58, 0x7a, 0x1c, 0xb1, 0x3c, 0xab, 0xc6, - 0x69, 0xac, 0x5e, 0xc1, 0x38, 0xa1, 0x43, 0x71, 0x05, 0x58, 0x2f, 0xf1, 0x04, 0x0f, 0x24, 0x10, 0xdc, 0xfa, 0x57, - 0xbe, 0x56, 0x0f, 0xa6, 0xf9, 0x53, 0x8c, 0x25, 0x42, 0x29, 0x6a, 0x04, 0xf8, 0x09, 0x72, 0xea, 0xa3, 0x7e, 0x8e, - 0x2e, 0xf4, 0x33, 0xca, 0x32, 0x31, 0x01, 0xe8, 0xa2, 0x29, 0x9a, 0x19, 0xe6, 0x0c, 0x22, 0x0d, 0xa0, 0xf2, 0x23, - 0x8d, 0x6c, 0x12, 0x21, 0xbc, 0x3e, 0x62, 0xd2, 0x25, 0x5e, 0xb1, 0x99, 0xe7, 0xec, 0x63, 0x92, 0x9d, 0x63, 0x70, - 0x0a, 0x89, 0x74, 0x5d, 0x7d, 0x69, 0xf9, 0xbe, 0x75, 0x4d, 0xf5, 0x04, 0x76, 0x01, 0xd8, 0x92, 0x3c, 0xd4, 0x54, - 0x6e, 0x20, 0xb5, 0x7a, 0x48, 0xc4, 0x72, 0x75, 0x8d, 0x44, 0x1b, 0x4b, 0x25, 0xd6, 0xf2, 0x11, 0x91, 0xf6, 0xda, - 0x15, 0x53, 0xdc, 0x3e, 0x0f, 0xc2, 0x76, 0xcd, 0x44, 0x58, 0x75, 0xeb, 0x4c, 0xc7, 0x6e, 0x70, 0x03, 0x65, 0x3e, - 0x13, 0xeb, 0xd5, 0x80, 0xdc, 0x01, 0x06, 0x0f, 0x34, 0x80, 0xd0, 0x11, 0x4f, 0x84, 0x84, 0x0f, 0x68, 0x2d, 0xa4, - 0xb8, 0xaf, 0x41, 0xe4, 0x59, 0xff, 0xe7, 0x7f, 0x1e, 0xd2, 0x1c, 0xd8, 0x26, 0xae, 0xf4, 0x2b, 0x99, 0x23, 0x27, - 0xb9, 0x52, 0xbf, 0xaf, 0xf0, 0x82, 0x1d, 0x8d, 0x82, 0x4a, 0xb6, 0x20, 0x9b, 0x80, 0x93, 0x4d, 0x60, 0xea, 0x61, - 0xf4, 0x32, 0xb2, 0xd5, 0xfa, 0xf6, 0x23, 0xf6, 0xab, 0x69, 0x4c, 0x2e, 0x35, 0x66, 0x2c, 0xad, 0x59, 0xa9, 0x31, - 0xf1, 0xfb, 0x29, 0x0d, 0x8d, 0x19, 0x5f, 0xab, 0x31, 0x91, 0x76, 0x7d, 0x1c, 0x39, 0xb4, 0x37, 0x31, 0x94, 0x62, - 0x68, 0x70, 0x8e, 0xb6, 0x09, 0xf8, 0xcf, 0xc3, 0x4f, 0xd2, 0x34, 0x21, 0xc8, 0x31, 0x96, 0x20, 0x8d, 0x2d, 0xb2, - 0x4b, 0x04, 0x76, 0xc7, 0xa5, 0xde, 0xf8, 0xf8, 0x68, 0x4c, 0xc0, 0xdd, 0xc5, 0x98, 0xa1, 0x58, 0x3e, 0xde, 0xc2, - 0x21, 0xc4, 0x7e, 0x29, 0xe9, 0x19, 0x90, 0xda, 0xa2, 0x71, 0xbc, 0x85, 0x08, 0x4a, 0x05, 0xf6, 0xdb, 0x37, 0x07, - 0x87, 0xb6, 0x77, 0x92, 0x8d, 0x2e, 0x03, 0x1b, 0x1c, 0x0a, 0x30, 0x3e, 0x5c, 0x9f, 0x4f, 0x58, 0xea, 0x28, 0x93, - 0x3e, 0x4b, 0xc0, 0xa5, 0xc9, 0x4e, 0xc5, 0x37, 0x14, 0x9a, 0x01, 0x75, 0x20, 0x26, 0x7d, 0x64, 0x71, 0x6f, 0x97, - 0x8b, 0xef, 0x8f, 0xf2, 0x1c, 0x1f, 0xf7, 0x30, 0xa5, 0x60, 0x77, 0x0b, 0x1e, 0xf0, 0xe5, 0x00, 0xd5, 0x91, 0x7e, - 0x13, 0x70, 0x16, 0xe2, 0x7d, 0x0b, 0xdb, 0x6f, 0xa9, 0xbe, 0x08, 0xc5, 0x9e, 0x64, 0x35, 0x6d, 0x75, 0x57, 0xa6, - 0x1c, 0x8d, 0x3d, 0x42, 0x4b, 0x8d, 0xac, 0x6e, 0x20, 0x05, 0x1f, 0xe9, 0x12, 0xa1, 0xfd, 0x8d, 0x22, 0x1a, 0xa5, - 0xd2, 0xd5, 0xb2, 0x0a, 0x27, 0x24, 0x2a, 0x8a, 0xc9, 0xe0, 0x27, 0x81, 0x7f, 0x6c, 0x7e, 0x2f, 0x4c, 0x7c, 0xda, - 0x47, 0x23, 0x79, 0xf4, 0x57, 0xef, 0x23, 0xf3, 0x2e, 0x8f, 0xa9, 0xa5, 0x72, 0x4e, 0x31, 0x6a, 0x82, 0x4e, 0x7c, - 0x5b, 0x45, 0x21, 0xc0, 0x3c, 0x49, 0xa2, 0x59, 0xc1, 0x02, 0xf5, 0x20, 0xfd, 0x54, 0x74, 0x77, 0x97, 0x03, 0x03, - 0xa6, 0x19, 0x53, 0xf2, 0xc9, 0xc2, 0x74, 0x64, 0x1f, 0x80, 0x23, 0x8b, 0x49, 0xf8, 0xad, 0x08, 0x94, 0x6f, 0x1a, - 0x24, 0x6c, 0xcc, 0x4b, 0x8e, 0xb7, 0xbc, 0x17, 0x2a, 0x72, 0xe0, 0x77, 0x77, 0xc0, 0xb9, 0xb5, 0x7c, 0xfc, 0xff, - 0xb6, 0xb1, 0x47, 0x41, 0x0a, 0xce, 0x28, 0x5d, 0xfb, 0xc0, 0x2b, 0x75, 0x00, 0x91, 0xf9, 0xbe, 0x30, 0x26, 0x1a, - 0x32, 0x8c, 0xaa, 0x94, 0x3c, 0x07, 0xb1, 0xed, 0xf1, 0xdc, 0x6c, 0x07, 0xe2, 0x76, 0x29, 0xb4, 0xb2, 0xec, 0xdc, - 0x6f, 0xbb, 0xd2, 0x05, 0x58, 0x6e, 0xac, 0x22, 0x24, 0xf5, 0xb7, 0x25, 0x0a, 0x19, 0xb1, 0x9c, 0x52, 0x68, 0x9a, - 0x85, 0xe8, 0x61, 0xe2, 0xb4, 0x1c, 0xe5, 0xb9, 0xd5, 0x50, 0x2c, 0x69, 0xc7, 0x1f, 0xd1, 0x8e, 0x27, 0x19, 0x36, - 0x58, 0x88, 0xb9, 0x87, 0x51, 0x32, 0x74, 0x10, 0x00, 0xab, 0x65, 0x3d, 0x02, 0x6a, 0xba, 0x2a, 0xd2, 0xe0, 0x3f, - 0x44, 0xe2, 0x96, 0x42, 0xe2, 0xad, 0xa0, 0xd2, 0xf1, 0xa0, 0x2c, 0x7b, 0xe7, 0xcc, 0x39, 0xfa, 0x5d, 0x5e, 0x1a, - 0x10, 0x77, 0x45, 0xf5, 0xf7, 0xf6, 0xda, 0xa5, 0x3b, 0xf0, 0x7e, 0x30, 0x3e, 0x62, 0x66, 0x2b, 0x86, 0xb6, 0x3d, - 0x58, 0x86, 0x1f, 0x42, 0xec, 0xfb, 0xca, 0xb1, 0xd1, 0xb2, 0xa4, 0x9a, 0xcb, 0x16, 0xf1, 0x97, 0x8d, 0xdd, 0x44, - 0xc8, 0xfb, 0xfb, 0xeb, 0x22, 0x17, 0xdf, 0xdc, 0x1e, 0xb9, 0x60, 0x77, 0x8c, 0x5c, 0x7c, 0xf3, 0x27, 0x47, 0x2e, - 0xbe, 0x6f, 0x46, 0x2e, 0x7e, 0xfb, 0x9c, 0xc8, 0x45, 0x9e, 0x9d, 0x17, 0x61, 0x47, 0x9e, 0x94, 0x83, 0xcc, 0xf9, - 0xfb, 0x84, 0x30, 0x60, 0xa2, 0x46, 0x00, 0x83, 0x22, 0x16, 0x22, 0xb9, 0x0f, 0x24, 0xbb, 0x8c, 0x17, 0xb4, 0x75, - 0x16, 0x5d, 0xeb, 0xbe, 0xba, 0x36, 0x04, 0x1e, 0x9b, 0xab, 0x2f, 0xbc, 0x75, 0x55, 0x44, 0x21, 0xa0, 0xef, 0x7e, - 0xea, 0x8e, 0xdd, 0x4d, 0x95, 0xbe, 0x65, 0x8e, 0xd0, 0x53, 0x51, 0x79, 0xc1, 0x3e, 0x0b, 0xfb, 0xdf, 0x1d, 0x75, - 0x7a, 0xdb, 0x9d, 0x29, 0xf4, 0x06, 0x2d, 0x0a, 0x6f, 0xed, 0xde, 0xf6, 0x36, 0xbe, 0x9d, 0xab, 0xb7, 0x2e, 0xbe, - 0xc5, 0xea, 0x6d, 0x07, 0xdf, 0x86, 0xea, 0xed, 0x01, 0xbe, 0x8d, 0xd4, 0xdb, 0x43, 0x7c, 0x3b, 0xb3, 0xcb, 0x23, - 0xae, 0x81, 0x7b, 0x08, 0x7c, 0x45, 0xc6, 0x7e, 0xa0, 0xca, 0x60, 0xd3, 0xe2, 0x75, 0xbb, 0xe8, 0x34, 0x88, 0x3d, - 0xe1, 0x14, 0x05, 0xb9, 0x77, 0x0e, 0x92, 0x3f, 0xa0, 0xec, 0xb2, 0xa7, 0xf8, 0xdd, 0x05, 0xf0, 0x21, 0x0e, 0xe3, - 0x29, 0x53, 0x1f, 0xa0, 0x55, 0x58, 0x83, 0x1d, 0x79, 0xd4, 0x1e, 0x94, 0x3d, 0xbd, 0x4e, 0x22, 0x60, 0xa2, 0x4e, - 0xef, 0x69, 0xe5, 0xaa, 0x3a, 0x31, 0x5d, 0x4b, 0xaf, 0xf0, 0x35, 0x7a, 0xc4, 0x70, 0xa3, 0xc7, 0x60, 0x21, 0xb5, - 0x2e, 0xc0, 0xf1, 0x5a, 0xa9, 0x5b, 0x10, 0x22, 0xad, 0x4d, 0x08, 0x27, 0xfd, 0x76, 0x18, 0x9d, 0xea, 0xe7, 0x57, - 0x60, 0xf0, 0x46, 0xa7, 0xec, 0x36, 0x3d, 0x43, 0x20, 0x9a, 0x3a, 0x46, 0x01, 0x41, 0xf6, 0x10, 0x2c, 0x0d, 0x3a, - 0x80, 0x52, 0xc7, 0x20, 0x75, 0xea, 0x5a, 0x87, 0xa6, 0xaf, 0x17, 0x01, 0x45, 0xab, 0x82, 0x5d, 0xb0, 0xbb, 0xa9, - 0x54, 0x50, 0x18, 0x2a, 0xb0, 0xe0, 0x46, 0x55, 0xa4, 0x7d, 0xe4, 0x6b, 0x15, 0x92, 0xa5, 0x74, 0x91, 0x19, 0xcd, - 0xd7, 0xa1, 0xfc, 0x65, 0xf1, 0xf8, 0x45, 0x67, 0x88, 0x7f, 0xa4, 0xf0, 0xfd, 0x62, 0x3c, 0x1e, 0xdf, 0xa8, 0x9b, - 0xbe, 0x18, 0x8d, 0x59, 0x97, 0xed, 0xf4, 0x30, 0xd2, 0xdb, 0x92, 0xe2, 0xb0, 0x53, 0x12, 0xed, 0x16, 0x77, 0x6b, - 0x8c, 0x92, 0x13, 0xd4, 0xd5, 0xdd, 0x95, 0x58, 0x09, 0x54, 0x59, 0x80, 0xf0, 0x3e, 0x8d, 0xd3, 0xa0, 0x5d, 0xfa, - 0x67, 0x52, 0xea, 0x7f, 0xf1, 0xe8, 0xd1, 0xa3, 0xd2, 0x1f, 0xa9, 0xb7, 0xf6, 0x68, 0x54, 0xfa, 0xc3, 0x85, 0x46, - 0xa3, 0xdd, 0x1e, 0x8f, 0x4b, 0x3f, 0x56, 0x05, 0xdb, 0xdd, 0xe1, 0x68, 0xbb, 0x5b, 0xfa, 0xe7, 0x46, 0x8b, 0xd2, - 0x67, 0xf2, 0x2d, 0x67, 0xa3, 0x5a, 0xb8, 0xf8, 0x61, 0x1b, 0x2a, 0x05, 0xa3, 0x2d, 0xd0, 0xc9, 0x13, 0x8f, 0x41, - 0x34, 0xe7, 0x59, 0x79, 0x0c, 0xb2, 0x9d, 0x81, 0x7c, 0x1e, 0x4b, 0xd9, 0x2e, 0xbe, 0xef, 0x8a, 0x12, 0xfd, 0x37, - 0x53, 0xa2, 0x23, 0x33, 0x93, 0x34, 0x67, 0xa4, 0x07, 0x9a, 0xd5, 0xc8, 0x59, 0x54, 0xfd, 0x5b, 0xc8, 0x2a, 0x61, - 0x8f, 0xd2, 0x06, 0x5b, 0x0a, 0x19, 0xff, 0xed, 0x75, 0x32, 0xfe, 0xbb, 0xdb, 0x65, 0xfc, 0xc9, 0xdd, 0x44, 0xfc, - 0x77, 0x7f, 0xb2, 0x88, 0xff, 0xd6, 0x14, 0xf1, 0x42, 0x88, 0x5d, 0x82, 0xf5, 0x4a, 0x66, 0xeb, 0x49, 0x76, 0xd1, - 0xc2, 0x2d, 0x91, 0xdb, 0x24, 0x3d, 0xd7, 0xef, 0x24, 0xfc, 0x57, 0xe4, 0xff, 0xa8, 0xc1, 0x8c, 0x8f, 0xc5, 0xf2, - 0xec, 0xf4, 0x34, 0x61, 0x4a, 0xc6, 0x1b, 0x15, 0x64, 0x0e, 0xbf, 0x49, 0x43, 0xfb, 0x0d, 0x38, 0xa8, 0x46, 0xc9, - 0x78, 0x0c, 0x45, 0xe3, 0xb1, 0xad, 0xf2, 0x63, 0x41, 0x9e, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, 0xf5, 0xd7, - 0x66, 0x99, 0x59, 0x20, 0x23, 0x51, 0xa6, 0x31, 0x21, 0x6b, 0x46, 0x71, 0x81, 0x7b, 0xb0, 0xfa, 0xb8, 0x2d, 0xda, - 0x2b, 0x53, 0x50, 0x2a, 0xf1, 0x10, 0xbf, 0x9a, 0xd2, 0xfc, 0x90, 0x88, 0xc8, 0x65, 0x5e, 0x46, 0xae, 0x3a, 0xef, - 0x34, 0xbe, 0x59, 0x57, 0x9d, 0x71, 0xc2, 0xe2, 0xcb, 0x7c, 0x86, 0xc7, 0x97, 0x2f, 0x46, 0xce, 0x25, 0xd8, 0xb1, - 0x71, 0xf1, 0x26, 0x6d, 0xe4, 0x89, 0x09, 0xb0, 0xc3, 0xd0, 0xc4, 0xb4, 0x14, 0x04, 0xab, 0x12, 0xe6, 0xab, 0xca, - 0x9e, 0xd1, 0x49, 0xa6, 0x13, 0xe1, 0x90, 0xfd, 0x1a, 0x59, 0x02, 0x73, 0x30, 0xa9, 0x0b, 0xe9, 0xe3, 0xe5, 0x22, - 0xc9, 0xe2, 0x4c, 0x7e, 0xe5, 0x9a, 0xa2, 0xff, 0x0b, 0xa9, 0x3f, 0xe4, 0xf1, 0x7b, 0xd5, 0x13, 0x03, 0xee, 0x62, - 0x86, 0x51, 0xa9, 0x82, 0xec, 0x40, 0xb8, 0x19, 0x7e, 0x92, 0x47, 0x0c, 0xa1, 0x62, 0xd9, 0x15, 0xf5, 0xf0, 0x13, - 0x28, 0xd5, 0x97, 0x21, 0x6b, 0x5f, 0x11, 0x6c, 0xf0, 0x00, 0x7e, 0xdd, 0x9f, 0xa3, 0x36, 0xc8, 0xe6, 0xdc, 0x71, - 0xa8, 0x95, 0xe3, 0x96, 0x5e, 0x77, 0x07, 0x3c, 0x4a, 0xd7, 0x17, 0xdf, 0xfd, 0x71, 0x74, 0x67, 0x89, 0xef, 0x75, - 0xa1, 0xf3, 0xa5, 0xef, 0x70, 0x69, 0x12, 0xe3, 0x87, 0x42, 0x04, 0xa2, 0xc6, 0x5d, 0x11, 0xb5, 0x88, 0xcd, 0x77, - 0x5f, 0xb9, 0x6f, 0x06, 0x61, 0xdd, 0x55, 0x1c, 0x2c, 0xe3, 0x64, 0xf5, 0x42, 0x6c, 0x2b, 0xac, 0x9a, 0x65, 0x70, - 0x6e, 0xd1, 0x99, 0xc5, 0xb9, 0x11, 0x77, 0xae, 0x6d, 0x83, 0x52, 0x05, 0x9e, 0x45, 0xf4, 0xf8, 0x12, 0x63, 0xa4, - 0xc2, 0xf7, 0x55, 0x40, 0xd7, 0xbd, 0x4e, 0x03, 0x72, 0xf4, 0x47, 0x35, 0xa3, 0xab, 0x2a, 0x55, 0x50, 0x9a, 0xa7, - 0x04, 0x06, 0x32, 0x14, 0x00, 0x86, 0x35, 0x4e, 0x85, 0xde, 0x82, 0x69, 0x48, 0x00, 0x6b, 0x8f, 0x0c, 0xdd, 0x12, - 0x5b, 0x81, 0x2d, 0xa4, 0x05, 0x28, 0x3d, 0xec, 0xb0, 0x67, 0xd5, 0x40, 0x4f, 0x97, 0xe3, 0xc6, 0x37, 0x39, 0x69, - 0x97, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, 0xcb, 0x05, 0x48, 0xf9, 0x93, 0x4f, 0x31, 0x07, 0x9b, 0x7a, 0xd6, - 0xc2, 0x28, 0x08, 0x85, 0x31, 0xa5, 0x3a, 0xa4, 0xa3, 0x47, 0x71, 0x45, 0xa8, 0x37, 0x2f, 0xd0, 0x97, 0x23, 0xa7, - 0x25, 0x48, 0xb3, 0x94, 0xf5, 0xea, 0x47, 0xcc, 0xa6, 0xdf, 0xa0, 0x88, 0x35, 0x58, 0x64, 0xe8, 0xfb, 0xf1, 0x4b, - 0xf0, 0xfd, 0x84, 0x1a, 0x6d, 0x2b, 0xa7, 0xa1, 0xbd, 0xb2, 0x7d, 0x20, 0x69, 0xbb, 0x49, 0xd6, 0x42, 0xbe, 0xec, - 0x1c, 0x5d, 0xe7, 0xdc, 0xdc, 0x76, 0xe0, 0xda, 0xdd, 0xd9, 0xf1, 0xd4, 0x3f, 0xe3, 0xa4, 0xba, 0x59, 0x4c, 0x07, - 0xae, 0x77, 0x81, 0x2c, 0x88, 0xc6, 0xf8, 0x45, 0xbd, 0xbb, 0xb4, 0x3c, 0xa1, 0x6c, 0xc7, 0x05, 0xaa, 0xf5, 0xa0, - 0xf3, 0x08, 0xbc, 0xb5, 0x3b, 0x0f, 0x7f, 0x33, 0xfa, 0xa5, 0xa4, 0x91, 0xba, 0xb4, 0x6a, 0xdb, 0x3d, 0x94, 0x17, - 0x49, 0x74, 0x09, 0x4e, 0x23, 0xd9, 0x18, 0x27, 0x18, 0xc4, 0xed, 0xcd, 0x32, 0x99, 0x39, 0x90, 0xb3, 0x84, 0x7e, - 0x6d, 0x85, 0x5c, 0x8a, 0xed, 0x07, 0xb3, 0x0b, 0xb5, 0x1a, 0x9d, 0x46, 0x46, 0xc0, 0x9f, 0x7a, 0xf0, 0x7f, 0x7d, - 0xa6, 0x41, 0xfd, 0xf0, 0x7a, 0x07, 0x60, 0x10, 0x86, 0x4d, 0x2b, 0x17, 0x50, 0xb5, 0xa1, 0xc4, 0x48, 0x7d, 0xa8, - 0x06, 0xb2, 0xfc, 0x6d, 0x50, 0x95, 0x51, 0xc1, 0x7a, 0xf8, 0x69, 0xc3, 0x18, 0x5c, 0x53, 0x69, 0x3c, 0x4d, 0xe3, - 0xd1, 0x28, 0x61, 0x3d, 0x65, 0x1f, 0x59, 0x9d, 0x07, 0x98, 0x39, 0x60, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe2, 0x69, - 0x3a, 0x45, 0x27, 0x60, 0xaf, 0xe1, 0xf7, 0x09, 0x57, 0x92, 0x53, 0x1e, 0xa9, 0xb7, 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, 0xe0, 0xe9, 0xa2, 0xb2, - 0x8e, 0xcc, 0xff, 0x9f, 0xdd, 0xf2, 0xd5, 0xea, 0xdd, 0xf2, 0x95, 0xda, 0x2d, 0x37, 0x73, 0xec, 0x17, 0xe3, 0x0e, - 0xfe, 0xe9, 0x55, 0x08, 0xc1, 0xaa, 0x00, 0x39, 0x2c, 0xb4, 0x8b, 0x5b, 0x5d, 0xf8, 0x8f, 0x86, 0x6e, 0x7b, 0xf8, - 0xc7, 0x07, 0x0b, 0xb0, 0x6d, 0x61, 0x21, 0xfe, 0x6b, 0xd7, 0xaa, 0x3a, 0xf7, 0xb1, 0x0e, 0x7b, 0xed, 0x2c, 0xd7, - 0x75, 0x6f, 0xde, 0xb4, 0x20, 0xaf, 0xb8, 0x13, 0x28, 0x61, 0x0c, 0xae, 0x5a, 0x74, 0x72, 0x02, 0xa5, 0xe3, 0x6c, - 0x38, 0x2f, 0xfe, 0x51, 0xc2, 0x2f, 0x89, 0x78, 0xe3, 0x96, 0x6e, 0x8c, 0xa3, 0xba, 0x8a, 0x34, 0x14, 0x35, 0xc2, - 0x42, 0xaf, 0x53, 0x50, 0x00, 0x63, 0x32, 0xa7, 0xeb, 0xdf, 0x5f, 0xb1, 0x09, 0xfe, 0x2e, 0x6b, 0xb3, 0x12, 0x99, - 0x7f, 0x2d, 0x31, 0x6e, 0x24, 0xc2, 0x67, 0xd1, 0xc0, 0x5c, 0xc3, 0xf6, 0xa3, 0xd5, 0xe0, 0x1e, 0xa9, 0x99, 0x06, - 0x4a, 0x29, 0x48, 0xbd, 0x03, 0x5e, 0x40, 0x34, 0x4f, 0xf8, 0xcd, 0xa3, 0x5e, 0xc7, 0x19, 0x4b, 0xa3, 0xde, 0x20, - 0xd0, 0xab, 0xb6, 0x77, 0x94, 0xd2, 0x5f, 0x7c, 0x75, 0x1f, 0xff, 0x88, 0xc0, 0xd7, 0x49, 0xe5, 0x1b, 0x89, 0xd8, - 0x00, 0xfa, 0x46, 0xeb, 0x35, 0xe7, 0x47, 0x68, 0x70, 0xf2, 0x7f, 0xee, 0xda, 0x1a, 0x8d, 0xf5, 0x3b, 0x35, 0x97, - 0x56, 0xe9, 0x67, 0xb5, 0xfe, 0xbc, 0xc1, 0xef, 0xd8, 0x76, 0x28, 0x1c, 0x82, 0x7a, 0x5b, 0xf9, 0x2b, 0x44, 0x96, - 0x1a, 0x2b, 0x8a, 0xdf, 0xb5, 0x7d, 0x65, 0x12, 0x53, 0x8f, 0xf5, 0xf0, 0x44, 0x3b, 0x91, 0xf2, 0xcc, 0x19, 0x7b, - 0x08, 0x3f, 0xf2, 0x27, 0x16, 0xde, 0xc3, 0xaf, 0x6f, 0x59, 0x17, 0xd3, 0x24, 0x05, 0xb3, 0x6a, 0xc2, 0xf9, 0x2c, - 0xd8, 0xda, 0x3a, 0x3f, 0x3f, 0xf7, 0xcf, 0xb7, 0xfd, 0x2c, 0x3f, 0xdd, 0xea, 0xb6, 0xdb, 0x6d, 0xfc, 0x68, 0x92, - 0x6d, 0x9d, 0xc5, 0xec, 0xfc, 0x31, 0xb8, 0x1f, 0xf6, 0x43, 0xeb, 0x91, 0xf5, 0x70, 0xdb, 0xda, 0x79, 0x60, 0x5b, - 0xa4, 0x00, 0xa0, 0x64, 0xdb, 0xb6, 0x84, 0x02, 0x08, 0x6d, 0x28, 0xde, 0xdb, 0x3d, 0x55, 0x36, 0x1c, 0x26, 0xa5, - 0x0b, 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, 0x81, 0x27, 0x70, 0x0b, - 0x26, 0x16, 0x3c, 0xbf, 0xea, 0x3e, 0x04, 0x1d, 0x77, 0xd6, 0xba, 0x3f, 0x6c, 0xb7, 0x3a, 0x56, 0xa7, 0xd5, 0xf5, - 0x1f, 0x5a, 0x5d, 0xf1, 0x3f, 0xc8, 0xc8, 0x6d, 0xab, 0x03, 0x4f, 0xdb, 0x16, 0xbc, 0x9f, 0xdd, 0x17, 0x29, 0x11, - 0x91, 0xbd, 0xb5, 0xb7, 0x8b, 0xbf, 0x43, 0x08, 0x90, 0xfa, 0xca, 0x16, 0xbf, 0xf5, 0xcc, 0xfe, 0xc2, 0x2c, 0xed, - 0x3c, 0x5a, 0x59, 0xdc, 0x7d, 0xb8, 0xb2, 0x78, 0xfb, 0xc1, 0xca, 0xe2, 0xfb, 0x3b, 0xf5, 0xe2, 0xad, 0x53, 0x51, - 0xa5, 0xe5, 0x42, 0x68, 0x4f, 0x23, 0x60, 0x94, 0x0b, 0xa7, 0x03, 0x70, 0xb6, 0xad, 0x16, 0xfe, 0x78, 0xd8, 0x75, - 0x75, 0xaf, 0x13, 0xec, 0xa5, 0xb1, 0x7c, 0xf8, 0x08, 0xb0, 0x7c, 0xde, 0x7d, 0x30, 0xc4, 0x76, 0x84, 0x28, 0xfc, - 0x3b, 0xdb, 0x7e, 0x34, 0x04, 0x8d, 0x60, 0xe1, 0x3f, 0xf8, 0x33, 0xd9, 0xe9, 0x0e, 0xc5, 0x4b, 0x1b, 0xeb, 0x3f, - 0x74, 0x1e, 0x16, 0xd0, 0x14, 0xff, 0xfc, 0xae, 0x4d, 0x68, 0x34, 0xe0, 0xcd, 0x71, 0xef, 0x03, 0x8d, 0x1e, 0x4d, - 0xba, 0xfe, 0x57, 0x67, 0x0f, 0xfd, 0x47, 0x93, 0xce, 0xc3, 0x0f, 0xe2, 0x2d, 0x01, 0x0a, 0x7e, 0x85, 0xff, 0x3e, - 0x6c, 0xb7, 0x27, 0xad, 0x8e, 0xff, 0xe8, 0x6c, 0xdb, 0xdf, 0x4e, 0x5a, 0x0f, 0xfc, 0x47, 0xf8, 0xaf, 0x1a, 0x6e, - 0x92, 0x4d, 0x99, 0x6d, 0xe1, 0x7a, 0x37, 0xfc, 0x5e, 0x73, 0x8e, 0xee, 0x7d, 0x6b, 0xe7, 0xfe, 0xf3, 0x47, 0xb0, - 0x46, 0x93, 0x4e, 0x17, 0xfe, 0xbf, 0xee, 0xf1, 0x03, 0x12, 0x5e, 0x0e, 0x1c, 0x31, 0x4c, 0x27, 0x55, 0x84, 0xa3, - 0x0f, 0x78, 0xdd, 0xf3, 0x7e, 0xbc, 0x2e, 0x00, 0xf2, 0xd7, 0xdb, 0x03, 0x20, 0x3f, 0xdc, 0x31, 0xc8, 0xfd, 0xd7, - 0x3f, 0x39, 0x02, 0xf2, 0x63, 0x33, 0xc8, 0xbd, 0xcf, 0x96, 0x02, 0x1d, 0x4d, 0x67, 0xed, 0x39, 0x73, 0x8e, 0x7e, - 0x62, 0x03, 0x4c, 0x9d, 0x86, 0xd6, 0x3f, 0xd4, 0xe2, 0x41, 0x19, 0x6e, 0xe4, 0x7d, 0x26, 0x76, 0x32, 0xe3, 0xd7, - 0x10, 0x84, 0xf3, 0x5b, 0x09, 0xf2, 0xe2, 0x6e, 0xf4, 0xe0, 0xfc, 0xcf, 0xa5, 0x07, 0x7d, 0xbd, 0x5f, 0xd1, 0xa3, - 0x16, 0x71, 0xa7, 0x88, 0x01, 0x39, 0xfa, 0x07, 0xf4, 0xee, 0xd8, 0x5b, 0x0c, 0xdf, 0x0a, 0x5b, 0xe4, 0x03, 0xbe, - 0xfb, 0x9c, 0xd3, 0x01, 0x91, 0x59, 0x1c, 0xda, 0x32, 0x00, 0x33, 0xc3, 0xef, 0xd3, 0xaa, 0x97, 0x33, 0x71, 0x7b, - 0x25, 0xa4, 0xab, 0x67, 0x3b, 0x3a, 0x78, 0x83, 0xc9, 0xde, 0xe1, 0x22, 0xe3, 0x11, 0xfe, 0x02, 0x24, 0x1e, 0xf3, - 0x04, 0x2f, 0xc6, 0xca, 0x4b, 0x64, 0x98, 0x9b, 0xfc, 0x1d, 0xe6, 0x55, 0xab, 0x43, 0x82, 0x29, 0x06, 0x0c, 0x5e, - 0xb1, 0x51, 0x1c, 0x39, 0xb6, 0x33, 0x83, 0x1d, 0x0b, 0x63, 0xb6, 0x6a, 0x49, 0xcd, 0x94, 0xcf, 0xec, 0xda, 0xea, - 0x77, 0xee, 0xe4, 0xf8, 0x0d, 0xb3, 0xf0, 0x48, 0x06, 0x18, 0x6d, 0xe9, 0x01, 0xc0, 0xf8, 0xaa, 0x24, 0x47, 0x61, - 0x5f, 0x59, 0x0d, 0xb6, 0x30, 0x1b, 0x3a, 0x7e, 0x17, 0xdc, 0x08, 0x2a, 0xc6, 0x6f, 0x42, 0xfd, 0xe8, 0xb4, 0xb6, - 0xc1, 0xac, 0x31, 0xba, 0xe9, 0x81, 0x06, 0x4b, 0x61, 0x24, 0x11, 0x1c, 0x68, 0x94, 0x7a, 0xfa, 0x97, 0x90, 0x55, - 0xe1, 0xa2, 0xe2, 0xf1, 0xe5, 0xa1, 0xbc, 0xf7, 0x6d, 0x63, 0xe4, 0x96, 0x22, 0xf6, 0xd5, 0x37, 0xa7, 0x36, 0x41, - 0x5d, 0xd0, 0x6f, 0x82, 0xa4, 0x73, 0x6f, 0xd4, 0x08, 0x98, 0x76, 0x6d, 0x49, 0xcf, 0x21, 0xb4, 0x85, 0x3e, 0x18, - 0xb1, 0xb3, 0x78, 0x28, 0xc5, 0xba, 0x67, 0xc9, 0xeb, 0x22, 0x2d, 0xc2, 0x22, 0xec, 0x78, 0xc2, 0x77, 0x86, 0x17, - 0xd4, 0x6a, 0x61, 0x9a, 0xd9, 0x7b, 0xf7, 0x7a, 0x1a, 0x92, 0x7a, 0xc6, 0xba, 0x8d, 0xbf, 0x96, 0xf2, 0x08, 0x7c, - 0xb5, 0x7f, 0x0c, 0xef, 0xe1, 0x2f, 0xa6, 0xbc, 0x37, 0xb0, 0x5d, 0x9f, 0x84, 0xe2, 0xbd, 0xea, 0xb7, 0x53, 0xa2, - 0x44, 0xd8, 0x04, 0xfd, 0xe5, 0xdd, 0x55, 0x91, 0x49, 0xa5, 0xd5, 0xdd, 0xa9, 0x94, 0x16, 0x3c, 0x1b, 0x52, 0x0a, - 0x04, 0x68, 0xd7, 0xdf, 0x31, 0x44, 0xe1, 0x59, 0x0b, 0x7f, 0xd6, 0x84, 0xe1, 0x7d, 0x68, 0xa0, 0xa4, 0xe1, 0x4b, - 0x68, 0xbe, 0x2d, 0x04, 0x2f, 0xf4, 0xfb, 0x89, 0x44, 0x95, 0x10, 0x53, 0x75, 0x8e, 0x59, 0x71, 0x88, 0x24, 0x72, - 0x04, 0x6c, 0xcf, 0x88, 0x37, 0x09, 0x76, 0x95, 0xd1, 0x94, 0xa7, 0xd0, 0xd7, 0xd1, 0x9f, 0x72, 0x5e, 0x55, 0xe7, - 0xd5, 0x76, 0xce, 0x8a, 0x29, 0x90, 0xe1, 0x1b, 0x07, 0x55, 0x74, 0x7d, 0x41, 0x7c, 0xd2, 0x4c, 0x6c, 0xe3, 0xea, - 0xa3, 0x6f, 0x2b, 0x32, 0x70, 0xcd, 0x4d, 0xc1, 0x2a, 0xa6, 0xa1, 0x7d, 0x81, 0x69, 0x33, 0xf8, 0xb3, 0x2a, 0x56, - 0x0f, 0x92, 0xa1, 0xfc, 0x24, 0xc2, 0xdf, 0xc8, 0x42, 0x3f, 0xca, 0x6a, 0x03, 0x72, 0xfa, 0x66, 0x25, 0x41, 0xfa, - 0x62, 0x54, 0x36, 0x91, 0x00, 0x7b, 0x01, 0x7f, 0xc1, 0x5f, 0x75, 0x5d, 0x42, 0xde, 0x83, 0xc4, 0x9c, 0x82, 0x51, - 0x9c, 0xd3, 0xf5, 0x5a, 0x85, 0x7f, 0x2d, 0xa2, 0x59, 0x91, 0x9a, 0x76, 0x25, 0x2b, 0xfa, 0x36, 0x16, 0xd9, 0x81, - 0x4c, 0x48, 0x33, 0x3f, 0xda, 0x6c, 0xde, 0x7f, 0x1c, 0x89, 0x5c, 0x34, 0xfc, 0xa8, 0xbd, 0x2d, 0x88, 0x6c, 0x83, - 0x18, 0xbb, 0x16, 0x27, 0x32, 0x6e, 0xf0, 0xda, 0x60, 0xf5, 0x5b, 0x8a, 0xcc, 0x0d, 0x6f, 0x9b, 0xab, 0xa5, 0xc7, - 0xa5, 0x75, 0x70, 0x65, 0xfc, 0xee, 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0x4f, 0x72, 0x0c, 0xb1, 0xe0, 0x75, 0xd8, - 0xb6, 0x5b, 0x82, 0xe4, 0x31, 0x7e, 0x8d, 0x93, 0x20, 0xbd, 0x0f, 0x85, 0x55, 0xd2, 0xd6, 0xee, 0xa4, 0xbb, 0xf7, - 0xe6, 0x70, 0xdf, 0x12, 0xbb, 0x79, 0x77, 0x0b, 0x5e, 0x77, 0xc9, 0x1d, 0x16, 0xf9, 0x19, 0xa1, 0xc8, 0xcf, 0xb0, - 0x44, 0x52, 0x57, 0x68, 0x6f, 0x09, 0x34, 0x6d, 0x8b, 0xa5, 0x43, 0x11, 0xc3, 0x9b, 0x82, 0xbb, 0x10, 0xe3, 0x87, - 0xbd, 0xb6, 0xb0, 0x5b, 0x0b, 0x57, 0x1a, 0xb6, 0x80, 0x08, 0xf8, 0x29, 0x03, 0x81, 0xa7, 0x2a, 0xe2, 0x07, 0x6a, - 0x9d, 0xa9, 0x64, 0x17, 0x39, 0x94, 0xce, 0x4b, 0x5d, 0x6e, 0x5d, 0xcc, 0x4f, 0xa6, 0x20, 0x87, 0x54, 0x82, 0xca, - 0x7b, 0xd9, 0x61, 0x97, 0xa6, 0xa2, 0x0c, 0x94, 0xa5, 0x88, 0xe4, 0xa4, 0xb3, 0xb7, 0x1b, 0x49, 0x7b, 0x07, 0xf7, - 0x6e, 0x01, 0x9b, 0x17, 0xd4, 0x1c, 0x1a, 0x15, 0x7e, 0x9c, 0x6d, 0x9d, 0xb3, 0x93, 0x56, 0x34, 0x8b, 0xab, 0xf0, - 0x1f, 0x6a, 0xbf, 0xbd, 0x5d, 0xa5, 0x08, 0x65, 0xaa, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, 0x1c, 0x31, 0x68, - 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, 0xb4, 0xf7, 0xfe, - 0xf6, 0x3f, 0xfe, 0x97, 0x0c, 0x2b, 0x02, 0x59, 0x01, 0x2c, 0x4c, 0x83, 0xa9, 0x4e, 0x18, 0xd9, 0x05, 0x38, 0xa2, - 0xf1, 0xa8, 0x35, 0x89, 0x92, 0x31, 0x40, 0x50, 0x30, 0x71, 0x9d, 0x49, 0xd6, 0x03, 0x17, 0x48, 0xb0, 0xcc, 0xc3, - 0x79, 0x09, 0x5e, 0xbd, 0x08, 0x57, 0xec, 0x0f, 0xe5, 0xae, 0xaa, 0x9c, 0x61, 0x62, 0x68, 0x23, 0x93, 0xd5, 0xe0, - 0xb9, 0x5a, 0x36, 0xab, 0xfa, 0x25, 0x49, 0x52, 0x78, 0xb0, 0x5a, 0x2a, 0x2b, 0xb4, 0xd4, 0x07, 0x21, 0xff, 0xf2, - 0xcf, 0xff, 0xf1, 0xbf, 0xaa, 0x57, 0x3c, 0xdf, 0xf8, 0xdb, 0x3f, 0xfd, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x13, 0x26, - 0x07, 0xcb, 0x33, 0x10, 0xda, 0x4a, 0x56, 0x75, 0x00, 0x22, 0xf6, 0x94, 0x55, 0x39, 0x1c, 0xf5, 0x94, 0xd7, 0x03, - 0x9a, 0x90, 0x78, 0x53, 0x42, 0x47, 0x7c, 0x4d, 0x29, 0xd2, 0x44, 0xb5, 0x1b, 0xc8, 0x07, 0x4b, 0x69, 0xd1, 0xb1, - 0xbe, 0xbd, 0xd3, 0xb6, 0xab, 0xe5, 0xdd, 0x33, 0xfa, 0x6e, 0xe1, 0xc2, 0xdc, 0x29, 0x03, 0xc7, 0xd7, 0xcb, 0xb6, - 0x50, 0x61, 0x2c, 0x2c, 0x29, 0xab, 0x72, 0x0b, 0xe3, 0xcb, 0x0b, 0x7c, 0x0d, 0xba, 0x46, 0x31, 0xad, 0x72, 0xad, - 0x4f, 0xef, 0xd7, 0x39, 0x20, 0x3a, 0xc6, 0xa5, 0x11, 0xc1, 0x32, 0x3a, 0x3b, 0x6d, 0xa1, 0x75, 0x92, 0x5c, 0x96, - 0x34, 0x8a, 0xf0, 0x66, 0xee, 0x3f, 0xfa, 0x87, 0xf2, 0x2f, 0x53, 0xb4, 0x0a, 0x2c, 0x67, 0x1a, 0x5d, 0x48, 0x1f, - 0xe7, 0x41, 0xbb, 0x3d, 0xbb, 0x70, 0x17, 0xd5, 0x0c, 0xde, 0x75, 0x93, 0x51, 0x80, 0xcd, 0x1c, 0x90, 0x0e, 0x5d, - 0x75, 0x2c, 0x0f, 0xcc, 0xfa, 0x36, 0x86, 0x7e, 0xca, 0xf2, 0xcb, 0x05, 0x85, 0x93, 0xe2, 0xdf, 0xf1, 0x70, 0x54, - 0x46, 0xde, 0xa0, 0xc4, 0xc0, 0x62, 0x61, 0xf4, 0xea, 0x8a, 0x5e, 0x93, 0xce, 0x72, 0x6e, 0x8a, 0x79, 0xb8, 0x6b, - 0x1e, 0xcb, 0xde, 0xc7, 0x83, 0xd6, 0x49, 0xc7, 0x9b, 0x74, 0x17, 0x7a, 0x78, 0xce, 0xb3, 0xa9, 0x79, 0x9a, 0xcb, - 0x22, 0x36, 0x64, 0x63, 0x15, 0xb1, 0x94, 0xf5, 0xe2, 0xa4, 0xb6, 0xfc, 0x02, 0xb7, 0x1b, 0xd0, 0x36, 0x8b, 0x78, - 0x40, 0x4c, 0xdb, 0x33, 0xcf, 0x7b, 0x23, 0x3c, 0x49, 0xcf, 0x16, 0xc6, 0x5c, 0x3d, 0xd1, 0x14, 0xe3, 0x82, 0xf5, - 0xbc, 0x9f, 0xd2, 0xa7, 0xee, 0xe6, 0x50, 0x22, 0xac, 0xf0, 0x42, 0x1e, 0xa3, 0xbe, 0xab, 0xf9, 0xe3, 0x52, 0x14, - 0x83, 0x0b, 0xbc, 0xb2, 0x5e, 0xa8, 0x45, 0x51, 0xfb, 0x02, 0xac, 0x1d, 0x02, 0xd3, 0x6e, 0xb6, 0xa2, 0x42, 0x6c, - 0xf5, 0x2e, 0x7c, 0xa1, 0x6d, 0xef, 0x68, 0x36, 0xa3, 0x86, 0x2e, 0x70, 0x23, 0xd9, 0xd0, 0x28, 0x29, 0x28, 0x45, - 0x40, 0x9c, 0xc8, 0xcb, 0x36, 0x92, 0x6d, 0xc5, 0x93, 0x3c, 0xab, 0xa7, 0xdf, 0xb9, 0xfd, 0xff, 0x00, 0x52, 0x99, - 0xa6, 0xd8, 0x89, 0x7b, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xd9, 0x76, 0xdb, 0xc8, 0x92, 0xe0, 0xf3, + 0x9c, 0x33, 0x7f, 0x30, 0x2f, 0x30, 0x4a, 0x6d, 0x03, 0x25, 0x10, 0x22, 0x29, 0xcb, 0x76, 0x81, 0x02, 0x79, 0xe5, + 0xa5, 0xae, 0x5d, 0xe5, 0xad, 0x2c, 0xd9, 0x75, 0xab, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, + 0x92, 0x5a, 0x8a, 0x42, 0x9f, 0x7e, 0xea, 0xa7, 0x39, 0x67, 0xd6, 0x87, 0x7e, 0x99, 0xd3, 0xfd, 0x30, 0x1f, 0x31, + 0xcf, 0xfd, 0x29, 0xf7, 0x07, 0xa6, 0x3f, 0x61, 0x22, 0x22, 0x17, 0x24, 0x40, 0x6a, 0x71, 0x75, 0xcd, 0x3d, 0x5e, + 0x04, 0xe4, 0x1a, 0x11, 0x19, 0x19, 0x5b, 0x46, 0x42, 0xbb, 0x77, 0xc6, 0xd9, 0x88, 0x5f, 0xcc, 0x99, 0x35, 0xe5, + 0xb3, 0xa4, 0xbf, 0x2b, 0xff, 0x67, 0xd1, 0xb8, 0xbf, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x8f, 0xb2, + 0xd4, 0x9a, 0xe6, 0x6c, 0x12, 0x8e, 0x23, 0x1e, 0x05, 0xf1, 0x2c, 0x3a, 0x61, 0xd6, 0x56, 0x7f, 0x77, 0xc6, 0x78, + 0x64, 0x8d, 0xa6, 0x51, 0x5e, 0x30, 0x1e, 0xbe, 0x3f, 0xf8, 0xba, 0xf5, 0xa8, 0xbf, 0x5b, 0x8c, 0xf2, 0x78, 0xce, + 0x2d, 0x1c, 0x32, 0x9c, 0x65, 0xe3, 0x45, 0xc2, 0xfa, 0xa7, 0x51, 0x6e, 0x9d, 0xb3, 0xf0, 0xcd, 0xf1, 0x2f, 0x6c, + 0xc4, 0xfd, 0x31, 0x9b, 0xc4, 0x29, 0x7b, 0x9b, 0x67, 0x73, 0x96, 0xf3, 0x0b, 0xef, 0xd9, 0xfa, 0x8a, 0x98, 0x15, + 0xde, 0xbe, 0xae, 0x3a, 0x61, 0xfc, 0xcd, 0x59, 0xaa, 0xfa, 0x3c, 0x65, 0x62, 0x92, 0x2c, 0x2f, 0x3c, 0x7e, 0x45, + 0x9b, 0xfd, 0x8b, 0xd9, 0x71, 0x96, 0x14, 0xde, 0x27, 0x5d, 0x3f, 0xcf, 0x33, 0x9e, 0x21, 0x58, 0xfe, 0x34, 0x2a, + 0x8c, 0x96, 0xde, 0x93, 0x35, 0x4d, 0xe6, 0xb2, 0xf2, 0x45, 0xf1, 0x2c, 0x5d, 0xcc, 0x58, 0x1e, 0x1d, 0x27, 0xcc, + 0x2b, 0x58, 0xe8, 0x30, 0x8f, 0x7b, 0xb1, 0x1b, 0xf6, 0xb9, 0x15, 0xa7, 0x16, 0x1b, 0x9c, 0x33, 0x2a, 0x59, 0x32, + 0xdd, 0x2a, 0xb8, 0xd3, 0xf6, 0x80, 0x5c, 0x93, 0xf8, 0x64, 0xa1, 0xdf, 0xcf, 0xf2, 0x98, 0xab, 0xe7, 0xd3, 0x28, + 0x59, 0xb0, 0x20, 0x2e, 0xdd, 0x80, 0x1d, 0xf2, 0x61, 0x18, 0x7b, 0x4f, 0x68, 0x50, 0x18, 0x72, 0x39, 0xc9, 0x72, + 0x07, 0x69, 0x15, 0xe3, 0xd8, 0xfc, 0xf2, 0xd2, 0xe1, 0xe1, 0xb2, 0x74, 0xdd, 0x4f, 0xcc, 0x1f, 0x45, 0x49, 0xe2, + 0xe0, 0xc4, 0x77, 0xef, 0x16, 0x38, 0x63, 0xec, 0xf1, 0xc3, 0x78, 0xe8, 0xf6, 0xe2, 0x89, 0xc3, 0x99, 0x5b, 0xf5, + 0xcb, 0x26, 0x16, 0x67, 0x0e, 0x77, 0xdd, 0x27, 0x57, 0xf7, 0xc9, 0x19, 0x5f, 0xe4, 0x00, 0x7b, 0xe9, 0xbd, 0x51, + 0x33, 0x3f, 0xc3, 0xfa, 0x7d, 0xea, 0xd8, 0x03, 0xd8, 0x0b, 0x6e, 0x7d, 0x08, 0xcf, 0xe2, 0x74, 0x9c, 0x9d, 0xf9, + 0xfb, 0xd3, 0x08, 0x7e, 0xbc, 0xcb, 0x32, 0x7e, 0xf7, 0xae, 0x73, 0x9a, 0xc5, 0x63, 0xab, 0x1d, 0x86, 0x66, 0xe5, + 0xc5, 0x93, 0xfd, 0xfd, 0xcb, 0xcb, 0x46, 0x81, 0x9f, 0x46, 0x3c, 0x3e, 0x65, 0xa2, 0x33, 0x00, 0x60, 0xc3, 0xcf, + 0x39, 0x67, 0xe3, 0x7d, 0x7e, 0x91, 0x40, 0x29, 0x63, 0xbc, 0xb0, 0x01, 0xc7, 0xa7, 0xd9, 0x08, 0xc8, 0x96, 0x1a, + 0x84, 0x87, 0xa6, 0x39, 0x9b, 0x27, 0xd1, 0x88, 0x61, 0x3d, 0x8c, 0x54, 0xf5, 0xa8, 0x1a, 0x79, 0x5f, 0x87, 0x62, + 0x79, 0x1d, 0xd7, 0x8b, 0x59, 0x98, 0xb2, 0x33, 0xeb, 0x55, 0x34, 0xef, 0x8d, 0x92, 0xa8, 0x28, 0x80, 0x5f, 0x97, + 0x84, 0x42, 0xbe, 0x18, 0x01, 0x83, 0x10, 0x82, 0x4b, 0x24, 0xd3, 0x34, 0x2e, 0xfc, 0x8f, 0x1b, 0xa3, 0xa2, 0x78, + 0xc7, 0x8a, 0x45, 0xc2, 0x37, 0x42, 0x58, 0x0b, 0x7e, 0x27, 0x0c, 0xbf, 0x76, 0xf9, 0x34, 0xcf, 0xce, 0xac, 0x67, + 0x79, 0x0e, 0xcd, 0x6d, 0x98, 0x52, 0x34, 0xb0, 0xe2, 0xc2, 0x4a, 0x33, 0x6e, 0xe9, 0xc1, 0x70, 0x01, 0x7d, 0xeb, + 0x7d, 0xc1, 0xac, 0xa3, 0x45, 0x5a, 0x44, 0x13, 0x06, 0x4d, 0x8f, 0xac, 0x2c, 0xb7, 0x8e, 0x60, 0xd0, 0x23, 0x58, + 0xb2, 0x82, 0xc3, 0xae, 0xf1, 0x6d, 0xb7, 0x47, 0x73, 0x41, 0xe1, 0x01, 0x3b, 0xe7, 0x21, 0x2b, 0x81, 0x31, 0xad, + 0x42, 0xa3, 0xe1, 0xb8, 0xcb, 0x04, 0x0a, 0x58, 0x18, 0x33, 0x64, 0x59, 0xc7, 0x6c, 0xac, 0x17, 0xe7, 0xc3, 0xdd, + 0xbb, 0x9a, 0xd6, 0x40, 0x13, 0x07, 0xda, 0x16, 0x8d, 0xb6, 0x9e, 0x40, 0xbc, 0x46, 0x22, 0xd7, 0x63, 0xbe, 0x24, + 0xdf, 0xfe, 0x45, 0x3a, 0xaa, 0x8f, 0x0d, 0x95, 0x25, 0xcf, 0xf6, 0x79, 0x1e, 0xa7, 0x27, 0x00, 0x84, 0x9c, 0xc9, + 0x6c, 0x52, 0x96, 0x62, 0xf1, 0xdf, 0xb0, 0x90, 0x85, 0x7d, 0x1c, 0x3d, 0x67, 0x8e, 0x5d, 0x50, 0x0f, 0x3b, 0x0c, + 0x91, 0xf4, 0xc0, 0x60, 0x6c, 0xc0, 0x02, 0xb6, 0x69, 0xdb, 0xde, 0xd7, 0xae, 0x77, 0x81, 0x1c, 0xe4, 0xfb, 0x3e, + 0xb1, 0xaf, 0xe8, 0x1c, 0x87, 0x1d, 0x04, 0xda, 0x4f, 0x58, 0x7a, 0xc2, 0xa7, 0x03, 0x76, 0xd8, 0x1e, 0x06, 0x1c, + 0xa0, 0x1a, 0x2f, 0x46, 0xcc, 0x41, 0x7e, 0xf4, 0x72, 0xdc, 0x3e, 0x9b, 0x0e, 0x4c, 0x81, 0x0b, 0x73, 0x87, 0x70, + 0xac, 0x2d, 0x8d, 0xab, 0x58, 0x54, 0x01, 0x86, 0x7c, 0x6e, 0xc3, 0x0e, 0x3b, 0x66, 0xb9, 0x01, 0x87, 0x6e, 0xd6, + 0xab, 0xad, 0xe0, 0x02, 0x56, 0x08, 0xfa, 0x59, 0x93, 0x45, 0x3a, 0xe2, 0x31, 0x08, 0x2e, 0x7b, 0x13, 0xc0, 0x15, + 0x2b, 0xa7, 0x17, 0xce, 0x76, 0x4b, 0xd7, 0x89, 0xdd, 0x4d, 0x76, 0x98, 0x6f, 0x76, 0x86, 0x1e, 0x42, 0xa9, 0x89, + 0x2f, 0x11, 0x8f, 0x01, 0xc1, 0xd2, 0x7b, 0xcb, 0xf4, 0xf6, 0xfc, 0x30, 0x60, 0xfe, 0x2a, 0x1f, 0x87, 0xdc, 0x9f, + 0x45, 0x73, 0xc4, 0x86, 0x11, 0x0f, 0x44, 0xe9, 0x08, 0xa1, 0xab, 0xad, 0x0b, 0x52, 0xcc, 0xaf, 0x58, 0xc0, 0x05, + 0x82, 0xc0, 0x9e, 0x7d, 0x16, 0x8d, 0xa6, 0xb0, 0xc5, 0x2b, 0xc2, 0x8d, 0xd5, 0x76, 0x18, 0xe5, 0x2c, 0xe2, 0xec, + 0x59, 0xc2, 0xf0, 0x0d, 0x57, 0x00, 0x7a, 0xda, 0xae, 0x97, 0xab, 0x7d, 0x97, 0xc4, 0xfc, 0x75, 0x06, 0xf3, 0xf4, + 0x04, 0x93, 0x00, 0x17, 0xe7, 0x77, 0xef, 0xc6, 0xc8, 0x22, 0x7b, 0x1c, 0x56, 0xeb, 0x78, 0x01, 0x42, 0xc0, 0x4e, + 0xb1, 0x85, 0x0d, 0xd4, 0xf6, 0x62, 0x9f, 0x03, 0x11, 0x9f, 0x64, 0x29, 0x87, 0xe1, 0x00, 0x5e, 0xcd, 0x41, 0x7e, + 0x34, 0x9f, 0xb3, 0x74, 0xfc, 0x64, 0x1a, 0x27, 0x63, 0xa0, 0x46, 0x09, 0xf8, 0x66, 0x2c, 0x04, 0x3c, 0x01, 0x99, + 0xe0, 0x7a, 0x8c, 0x68, 0xf9, 0x90, 0x91, 0x79, 0x68, 0xdb, 0x3d, 0x94, 0x40, 0x12, 0x0b, 0x94, 0x41, 0xb4, 0x70, + 0xef, 0x40, 0xf4, 0x17, 0x2e, 0xdf, 0x0c, 0x63, 0xbd, 0x8c, 0x92, 0xc0, 0x6f, 0x50, 0xd2, 0x00, 0xfd, 0x19, 0xc8, + 0xc0, 0x1e, 0x0a, 0xae, 0xef, 0xa5, 0xd4, 0x49, 0x99, 0xc2, 0x10, 0x08, 0x30, 0x42, 0x09, 0x22, 0x69, 0xf0, 0x36, + 0x4b, 0x2e, 0x26, 0x71, 0x92, 0xec, 0x2f, 0xe6, 0xf3, 0x2c, 0xe7, 0xde, 0x37, 0xe1, 0x92, 0x67, 0x15, 0xae, 0xb4, + 0xc9, 0x8b, 0xb3, 0x98, 0x23, 0x41, 0xdd, 0xe5, 0x28, 0x82, 0xa5, 0x7e, 0x9c, 0x65, 0x09, 0x8b, 0x52, 0x40, 0x83, + 0x0d, 0x6c, 0x3b, 0x48, 0x17, 0x49, 0xd2, 0x3b, 0x86, 0x61, 0x3f, 0xf5, 0xa8, 0x5a, 0x48, 0xfc, 0x80, 0x9e, 0xf7, + 0xf2, 0x3c, 0xba, 0x80, 0x86, 0xd8, 0x06, 0x78, 0x11, 0x56, 0xeb, 0x9b, 0xfd, 0x37, 0xaf, 0x7d, 0xc1, 0xf8, 0xf1, + 0xe4, 0x02, 0x00, 0x2d, 0x2b, 0xa9, 0x39, 0xc9, 0xb3, 0x59, 0x63, 0x6a, 0xa4, 0x43, 0x1c, 0xb2, 0xde, 0x15, 0x20, + 0xc4, 0x34, 0x32, 0xac, 0x12, 0x33, 0x21, 0x78, 0x4d, 0xfc, 0x2c, 0x2b, 0x71, 0x0f, 0x0c, 0xf0, 0x21, 0x10, 0xc5, + 0x30, 0xe5, 0xf5, 0xd0, 0xf2, 0xfc, 0x62, 0x19, 0x87, 0x04, 0xe7, 0x1c, 0xf5, 0x2f, 0xc2, 0x38, 0x8a, 0x60, 0xf6, + 0xa5, 0x18, 0xb0, 0x54, 0x10, 0xc7, 0x65, 0xe9, 0x25, 0x9a, 0x89, 0x51, 0xe2, 0xa1, 0x40, 0xe1, 0xb0, 0x8d, 0x2e, + 0x2f, 0x19, 0xbc, 0xb8, 0xde, 0xb7, 0xe1, 0x32, 0x52, 0xf8, 0xa0, 0x86, 0xc2, 0xfd, 0x15, 0x08, 0x39, 0x81, 0x9a, + 0xec, 0x14, 0xf4, 0x20, 0xc0, 0xf9, 0x8d, 0x07, 0xfa, 0x3f, 0x41, 0x28, 0xee, 0x74, 0x3c, 0xd0, 0xa0, 0x4f, 0xa6, + 0x51, 0x7a, 0xc2, 0xc6, 0x41, 0xc2, 0x4a, 0x29, 0x79, 0xf7, 0x2c, 0x58, 0x63, 0x60, 0xa7, 0xc2, 0x7a, 0x7e, 0xf0, + 0xea, 0xa5, 0x5c, 0xb9, 0x9a, 0x30, 0x86, 0x45, 0x5a, 0x80, 0x5a, 0x05, 0xb1, 0x2d, 0xc5, 0xf1, 0x33, 0xae, 0xa4, + 0xb7, 0x28, 0x89, 0x8b, 0xf7, 0x73, 0x30, 0x31, 0xd8, 0x5b, 0x18, 0x06, 0xa6, 0x0f, 0x61, 0x2a, 0x2a, 0x87, 0xf9, + 0x44, 0xc5, 0x58, 0x17, 0x41, 0x67, 0x81, 0xa9, 0x78, 0xcd, 0x1c, 0xb7, 0x04, 0x56, 0xe5, 0xf1, 0xc8, 0x8a, 0xc6, + 0xe3, 0x17, 0x69, 0xcc, 0xe3, 0x28, 0x89, 0x7f, 0x23, 0x4a, 0x2e, 0x91, 0xc7, 0x78, 0x4f, 0x2e, 0x02, 0xe0, 0x4e, + 0x3d, 0x12, 0x57, 0x09, 0xd9, 0x3b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x70, 0x28, 0xc1, 0x4b, 0xfc, 0xf9, 0xa2, + 0x98, 0x22, 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0xe3, 0x82, 0xe5, 0xa7, 0x6c, 0xac, 0x39, 0xa0, 0x00, 0xac, 0xa8, + 0x39, 0x18, 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, + 0xa5, 0x80, 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x05, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, + 0x93, 0x3d, 0x5b, 0x90, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, + 0x0a, 0x29, 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, + 0xc2, 0x4a, 0xbc, 0x2b, 0x44, 0xbc, 0x2b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x3b, 0xdc, 0x4f, 0xb3, 0xbd, 0xd1, + 0x88, 0x15, 0x45, 0x06, 0xb0, 0xdd, 0xa1, 0xf6, 0x57, 0x19, 0x5a, 0x40, 0x49, 0x57, 0xcb, 0x3a, 0xbb, 0x20, 0x0d, + 0x6e, 0xaa, 0x15, 0xa5, 0xd3, 0x03, 0xfb, 0xe3, 0x47, 0x90, 0xd9, 0x9e, 0x24, 0x03, 0x50, 0x7d, 0xd5, 0xf0, 0x13, + 0xf6, 0x4c, 0x9d, 0x32, 0x6b, 0xed, 0x4b, 0xa7, 0x0e, 0x92, 0x07, 0xc3, 0xba, 0xa5, 0xb1, 0xa0, 0x6b, 0x87, 0xc6, + 0xd5, 0x90, 0x0a, 0x72, 0x79, 0x42, 0x2a, 0xdb, 0x58, 0x46, 0xb0, 0xda, 0x4a, 0x8f, 0x48, 0xaf, 0xb0, 0x29, 0x08, + 0xd0, 0x43, 0x36, 0xec, 0xc9, 0xfa, 0x30, 0x17, 0x94, 0xcb, 0xd9, 0xaf, 0x0b, 0x56, 0x70, 0xc1, 0xba, 0x30, 0x6e, + 0x01, 0xe3, 0x96, 0x2b, 0xd6, 0x61, 0xcd, 0x76, 0x5c, 0x07, 0xdb, 0x9b, 0x39, 0xea, 0xb1, 0x02, 0x39, 0xf9, 0x7a, + 0x76, 0x42, 0x58, 0x99, 0x7b, 0x79, 0xf9, 0xad, 0x1a, 0xa4, 0x5a, 0x4a, 0x6d, 0x03, 0x35, 0xd6, 0xc4, 0x56, 0x4d, + 0xc6, 0xb6, 0x2b, 0x15, 0xea, 0x9d, 0x4e, 0xaf, 0xc6, 0x07, 0xb0, 0xe7, 0xda, 0x9a, 0xa5, 0x2b, 0x63, 0xfb, 0xad, + 0xa2, 0xe9, 0x1b, 0x31, 0x32, 0x59, 0xa3, 0xec, 0x66, 0xee, 0x51, 0x3b, 0x1e, 0xda, 0xae, 0xd4, 0x55, 0x82, 0x61, + 0x51, 0x17, 0x0c, 0x4d, 0xa8, 0xe7, 0xba, 0x8b, 0xad, 0x99, 0x8a, 0x85, 0x6a, 0xad, 0x95, 0x03, 0xc1, 0xc3, 0x43, + 0x30, 0x4e, 0xd6, 0xfa, 0x07, 0xaf, 0xa3, 0x19, 0x43, 0x8a, 0x7a, 0x57, 0x35, 0x90, 0x0e, 0x04, 0x34, 0x19, 0x36, + 0xd5, 0x1b, 0x77, 0x85, 0xd5, 0x54, 0xdf, 0x5f, 0x31, 0x58, 0x11, 0x60, 0x5f, 0x97, 0x6b, 0x96, 0x88, 0xf4, 0xa6, + 0xe0, 0x12, 0x4d, 0x1f, 0x51, 0x26, 0xd6, 0x84, 0x14, 0x3c, 0x20, 0x0f, 0xcb, 0xdf, 0x58, 0x38, 0xd9, 0x8a, 0x29, + 0x1c, 0x39, 0xca, 0x14, 0xa0, 0x33, 0x29, 0x01, 0x10, 0x97, 0xf4, 0xb3, 0xb6, 0xb1, 0x90, 0x6c, 0xfb, 0xc8, 0x07, + 0xfe, 0x24, 0x89, 0xb8, 0xd3, 0xd9, 0x6a, 0xbb, 0xc0, 0x87, 0x20, 0xc4, 0x41, 0x47, 0x80, 0x79, 0x5f, 0xa1, 0xc2, + 0x10, 0x95, 0xd8, 0xe5, 0x3e, 0x18, 0x45, 0xd3, 0x78, 0xc2, 0x9d, 0x0c, 0x95, 0x88, 0x5b, 0xb2, 0x04, 0x94, 0x8c, + 0xde, 0x57, 0x20, 0x25, 0xb8, 0x90, 0x2e, 0xa2, 0x5a, 0x0b, 0x34, 0x05, 0x29, 0x49, 0x29, 0xd2, 0x82, 0x0a, 0x02, + 0x43, 0xa8, 0xf4, 0x14, 0x47, 0x81, 0x7e, 0x8b, 0x07, 0x62, 0xd0, 0x60, 0xc5, 0xa2, 0x8c, 0x07, 0xf1, 0x6a, 0x21, + 0xa8, 0x61, 0x9f, 0x67, 0x2f, 0xb3, 0x33, 0x96, 0x3f, 0x89, 0x10, 0xf6, 0x40, 0x74, 0x2f, 0x41, 0xd2, 0x93, 0x40, + 0x67, 0x3d, 0xc5, 0x2b, 0xa7, 0x84, 0x34, 0x2c, 0xc4, 0x2c, 0x46, 0x45, 0x08, 0x5a, 0x8e, 0x68, 0x9f, 0xe2, 0x96, + 0xa2, 0xbd, 0x87, 0xaa, 0x84, 0x69, 0xde, 0xda, 0x7b, 0x59, 0xe7, 0x2d, 0x18, 0x61, 0xae, 0xb8, 0xb5, 0xbe, 0x63, + 0x5d, 0x4f, 0xea, 0x66, 0x47, 0xf2, 0x96, 0xa1, 0xcc, 0x40, 0x7f, 0x5c, 0x5e, 0x56, 0x46, 0x3a, 0x28, 0x53, 0x2d, + 0xcd, 0xd1, 0x72, 0x12, 0x5b, 0xc2, 0x2d, 0x41, 0x19, 0xa1, 0xe1, 0x95, 0x67, 0x49, 0x62, 0xe8, 0x22, 0x2f, 0xee, + 0x39, 0x0d, 0x75, 0x04, 0x50, 0xcc, 0x6a, 0x1a, 0x69, 0xc0, 0x03, 0x5d, 0x81, 0x4a, 0x49, 0x69, 0x23, 0xaf, 0x6a, + 0x22, 0x20, 0x4e, 0xc7, 0x2c, 0x17, 0x0e, 0x9a, 0xd4, 0xa1, 0x30, 0x61, 0x0a, 0x0c, 0xcd, 0xc6, 0x20, 0xe1, 0x15, + 0x02, 0x60, 0x9e, 0xf8, 0xd3, 0xac, 0xe0, 0xba, 0xce, 0x84, 0x3e, 0xbe, 0xbc, 0x8c, 0x85, 0xbf, 0x88, 0x0c, 0x90, + 0xb3, 0x59, 0x76, 0xca, 0xd6, 0x40, 0xdd, 0x53, 0x83, 0x99, 0x20, 0x1b, 0xc3, 0x80, 0x12, 0x05, 0xd5, 0x32, 0x4f, + 0xe2, 0x11, 0xd3, 0x5a, 0x6a, 0xe6, 0x83, 0x41, 0xc7, 0xce, 0x41, 0x46, 0x30, 0xb7, 0xdf, 0xef, 0xb7, 0xbd, 0x8e, + 0x5b, 0x0a, 0x82, 0x2f, 0x57, 0x28, 0x7a, 0x8d, 0x7e, 0x94, 0x26, 0xf8, 0x3a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, + 0xe1, 0x27, 0x79, 0x52, 0x10, 0xbb, 0xde, 0x18, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, + 0xc9, 0xa6, 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, + 0x03, 0xaf, 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x56, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, + 0x5c, 0x47, 0x82, 0x7d, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x1c, 0x47, 0xa3, 0x4f, 0x1a, + 0xdc, 0x8a, 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, + 0xc8, 0x56, 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0xd7, 0xb0, 0x1c, 0x57, + 0xd2, 0x80, 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe3, 0xe2, 0x6a, 0x12, 0xfc, 0xa1, 0x60, + 0x3e, 0x35, 0x66, 0xba, 0x11, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, + 0xa1, 0x42, 0x1c, 0x7f, 0x22, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0xad, 0x8a, 0x16, 0x29, + 0x53, 0x63, 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, + 0x30, 0x29, 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, + 0x99, 0xbc, 0x5d, 0x0c, 0x8a, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, + 0x92, 0x4d, 0xd2, 0x81, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, + 0xdd, 0x86, 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0xae, 0xa3, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, + 0x85, 0xb1, 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x6b, 0xec, 0x29, 0x00, + 0x25, 0x81, 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x3b, 0x44, 0xc7, 0x4a, 0x84, 0xd6, 0x44, 0xbe, 0xd6, + 0x67, 0xb1, 0x5f, 0x73, 0x09, 0x4d, 0x4a, 0xe6, 0x83, 0x3c, 0xb0, 0x55, 0x20, 0xa2, 0xd2, 0x6d, 0xc9, 0x20, 0x21, + 0x87, 0x74, 0x95, 0xe8, 0xb5, 0x91, 0x0c, 0x5a, 0xa7, 0x42, 0xa2, 0xa5, 0xc3, 0x30, 0x72, 0xd0, 0x71, 0xa7, 0xb5, + 0x58, 0x21, 0x64, 0xd3, 0xde, 0x24, 0x56, 0x44, 0xe7, 0x34, 0x47, 0x13, 0xce, 0xd4, 0xe9, 0x8e, 0x03, 0xe8, 0x80, + 0xd8, 0x5f, 0x61, 0xbd, 0xb5, 0x66, 0xa7, 0xeb, 0x57, 0x0e, 0xdf, 0xe5, 0x65, 0x82, 0xfc, 0x20, 0x0c, 0x5e, 0x58, + 0xb3, 0x81, 0x92, 0xbd, 0x7b, 0x2f, 0xb1, 0x15, 0xd9, 0x9f, 0x55, 0x49, 0xe5, 0x29, 0xd4, 0x38, 0xb7, 0xbe, 0x4e, + 0xcc, 0x0c, 0x2d, 0xaa, 0x8a, 0x7d, 0x43, 0xaa, 0xef, 0x2b, 0x85, 0x5d, 0xa1, 0xbc, 0x2f, 0x87, 0x8e, 0x5d, 0xd7, + 0x0d, 0x72, 0x72, 0x5e, 0xee, 0xac, 0x73, 0x21, 0xef, 0xde, 0x35, 0x7d, 0xa6, 0x53, 0x3d, 0xfc, 0x13, 0x07, 0x95, + 0x73, 0x71, 0x91, 0x92, 0x05, 0xf3, 0x44, 0xa9, 0xa3, 0x15, 0x07, 0xb4, 0xdd, 0x43, 0x4f, 0x3b, 0x3a, 0x8b, 0x62, + 0x6e, 0xe9, 0x51, 0x84, 0xa7, 0x8d, 0xf2, 0x49, 0x1a, 0x1d, 0x80, 0x17, 0x9a, 0x90, 0xe4, 0x84, 0x9b, 0xb6, 0x68, + 0x31, 0x9a, 0x32, 0x0c, 0x81, 0x2b, 0x7b, 0xc2, 0x94, 0x3d, 0x77, 0x10, 0x6f, 0x31, 0x30, 0x5b, 0x0f, 0x7b, 0xd9, + 0xec, 0x5e, 0x33, 0xff, 0x61, 0x8d, 0x40, 0xb6, 0xcd, 0x54, 0x5d, 0xd9, 0x78, 0x97, 0x22, 0x12, 0x23, 0x6c, 0xeb, + 0xc6, 0x96, 0xb6, 0x7e, 0xaf, 0xe1, 0x5e, 0x57, 0x8e, 0x79, 0x4d, 0xa9, 0x36, 0xf4, 0xb0, 0x72, 0x73, 0x98, 0xe9, + 0xc8, 0x8b, 0x15, 0x74, 0x7b, 0x22, 0x28, 0x04, 0x4e, 0x84, 0xb6, 0x07, 0x15, 0x37, 0x10, 0x29, 0xb9, 0xd2, 0xaa, + 0xd9, 0x22, 0x19, 0x4b, 0x60, 0xc1, 0x85, 0xe5, 0x92, 0x8f, 0xce, 0xe2, 0x24, 0xa9, 0x4a, 0xff, 0x50, 0x01, 0x2f, + 0x86, 0xbd, 0x49, 0xb4, 0x0b, 0x8c, 0x16, 0x0a, 0x04, 0x57, 0x1b, 0x61, 0xef, 0x1d, 0xb7, 0x5a, 0x77, 0x11, 0x71, + 0xe4, 0x66, 0x34, 0x02, 0xea, 0x31, 0xc2, 0xaa, 0x59, 0x7b, 0xef, 0x19, 0x86, 0xd4, 0x0c, 0x7c, 0x50, 0x9d, 0x51, + 0xf1, 0x67, 0xd9, 0x53, 0x9f, 0x89, 0xde, 0x8d, 0xaa, 0xab, 0x19, 0x50, 0x51, 0x81, 0x0f, 0x33, 0xc4, 0xd2, 0x56, + 0x81, 0x80, 0x5c, 0x0f, 0x8b, 0x52, 0xc0, 0x24, 0x0d, 0x16, 0x94, 0x02, 0x6b, 0xad, 0xec, 0x5e, 0xde, 0x14, 0xcc, + 0xa1, 0x50, 0xb8, 0xe8, 0xff, 0x24, 0x9b, 0xcd, 0xd1, 0x32, 0x6b, 0x30, 0x35, 0x34, 0x78, 0xdf, 0xa8, 0x2f, 0xd7, + 0x94, 0xd5, 0xfa, 0xd0, 0x8e, 0xac, 0xf1, 0x93, 0x76, 0x94, 0xc1, 0xa1, 0x5a, 0xe8, 0xa2, 0xba, 0xdd, 0xdc, 0x14, + 0x31, 0xeb, 0x78, 0xdc, 0x27, 0xbd, 0xad, 0xad, 0x49, 0x4f, 0xd3, 0x80, 0x64, 0x92, 0x64, 0x78, 0x93, 0x01, 0xca, + 0x8a, 0x38, 0xcb, 0xb2, 0x41, 0xbe, 0x65, 0x59, 0xe2, 0xfa, 0x7d, 0xd7, 0xdb, 0xab, 0x79, 0xd6, 0xde, 0xde, 0xd5, + 0x2e, 0x72, 0x55, 0x27, 0x3d, 0xc8, 0xc3, 0x21, 0x14, 0xad, 0xd8, 0x94, 0xe1, 0x72, 0x96, 0x8d, 0x59, 0x60, 0x43, + 0xf7, 0xd4, 0x2e, 0x95, 0x56, 0x86, 0xcd, 0x91, 0x32, 0x67, 0xf9, 0xae, 0x1e, 0x49, 0x0d, 0xf6, 0x80, 0x05, 0xb4, + 0xb9, 0xf0, 0x7d, 0x78, 0x92, 0x64, 0xc7, 0x51, 0x72, 0x20, 0x14, 0x78, 0xad, 0xe5, 0x07, 0x70, 0x19, 0xc9, 0x62, + 0x35, 0x94, 0xd4, 0xf7, 0x83, 0xef, 0x83, 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, 0x8b, 0x58, 0xf8, 0x72, 0xf0, 0x52, + 0xb9, 0xd4, 0x19, 0xb8, 0x10, 0xe0, 0xe4, 0x27, 0x31, 0x6f, 0xe1, 0x79, 0xa4, 0xed, 0x2d, 0x45, 0x05, 0xc6, 0x15, + 0x29, 0x2e, 0x5d, 0x2a, 0x6f, 0xd0, 0xfb, 0x18, 0x1e, 0x41, 0xb3, 0x8d, 0x8d, 0xa5, 0xf3, 0x2a, 0xe2, 0x53, 0x3f, + 0x8f, 0xd2, 0x71, 0x36, 0x73, 0xdc, 0x4d, 0xdb, 0x76, 0xfd, 0x82, 0x3c, 0x91, 0xaf, 0xdc, 0x72, 0xe3, 0xc8, 0x9b, + 0xb2, 0xd0, 0x1e, 0xd8, 0x9b, 0x1f, 0xbd, 0xf7, 0x2c, 0x3c, 0xda, 0xdd, 0x58, 0x4e, 0x59, 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, 0x6f, 0xc4, 0xc2, 0x9a, 0xa7, 0xee, 0x1d, 0xd0, 0xd1, 0x4a, 0x7d, 0x23, 0x8f, 0x18, 0x29, + 0xcd, 0x55, 0x3b, 0xc1, 0x31, 0xb6, 0xf8, 0xfa, 0x6d, 0x7d, 0x28, 0xa2, 0x14, 0x7e, 0x0c, 0xd6, 0x4b, 0x04, 0xea, + 0x1b, 0x1c, 0x1c, 0xef, 0x20, 0xdc, 0xda, 0x75, 0x06, 0x81, 0x73, 0xa7, 0xd5, 0xba, 0xfc, 0x69, 0xeb, 0xf0, 0xe7, + 0xa8, 0xf5, 0xdb, 0x5e, 0xeb, 0xc7, 0xa1, 0x7b, 0xe9, 0xfc, 0xb4, 0x35, 0x38, 0x94, 0x6f, 0x87, 0x3f, 0xf7, 0x7f, + 0x2a, 0x86, 0x5f, 0x8a, 0xc2, 0x0d, 0xd7, 0xdd, 0x3a, 0x01, 0x4f, 0x29, 0xdc, 0x6a, 0xb5, 0xfa, 0xf0, 0xb4, 0x80, + 0x27, 0xfc, 0x79, 0x06, 0x3f, 0x2e, 0x0f, 0xad, 0xff, 0xf0, 0x53, 0xfa, 0x1f, 0x7f, 0xca, 0x87, 0x38, 0xe6, 0xe1, + 0xcf, 0x3f, 0x15, 0xf6, 0xbd, 0x7e, 0xb8, 0x35, 0xdc, 0x74, 0x1d, 0x5d, 0xf3, 0x65, 0x58, 0x3d, 0x42, 0xab, 0xc3, + 0x9f, 0xe5, 0x9b, 0x7d, 0xef, 0x68, 0xb7, 0x1f, 0x0e, 0x2f, 0x1d, 0xfb, 0xf2, 0x9e, 0x7b, 0xe9, 0xba, 0x97, 0x1b, + 0x38, 0xcf, 0x1c, 0x46, 0xbf, 0x07, 0x3f, 0x4f, 0xe0, 0xa7, 0x0d, 0x3f, 0x4f, 0xe1, 0xe7, 0xcf, 0xd0, 0x4d, 0xc4, + 0xdf, 0x2e, 0x29, 0x16, 0x72, 0x89, 0x07, 0x16, 0x11, 0xac, 0x82, 0xbb, 0xb1, 0x15, 0x7b, 0x13, 0x22, 0x1a, 0xec, + 0x43, 0xdf, 0xf7, 0x31, 0x4c, 0xea, 0x2c, 0x3f, 0x6e, 0xc0, 0xa2, 0x23, 0xe7, 0x6c, 0x04, 0xcc, 0x13, 0x91, 0x83, + 0x22, 0xe0, 0xe2, 0x6c, 0xb5, 0xc0, 0xc3, 0x55, 0x6f, 0x1c, 0x4e, 0x98, 0x03, 0x46, 0xc1, 0x73, 0x86, 0x0f, 0x5d, + 0xd7, 0x7b, 0x26, 0xcf, 0x0c, 0x71, 0x9f, 0x0b, 0xd6, 0x4a, 0x33, 0x61, 0xd2, 0xd8, 0xae, 0x37, 0x5f, 0x53, 0x09, + 0xdb, 0x3a, 0x3d, 0x81, 0xba, 0x0d, 0x71, 0xd0, 0xf6, 0x3d, 0x8b, 0x3e, 0xe1, 0x96, 0x7c, 0x6d, 0x1c, 0x02, 0xaf, + 0x58, 0xf2, 0x4d, 0xa3, 0xd1, 0xb0, 0x11, 0x85, 0x3b, 0xf6, 0x98, 0xc1, 0x0c, 0x2b, 0x26, 0x22, 0x27, 0xa5, 0x29, + 0x2c, 0x5b, 0x98, 0xfc, 0x6d, 0x94, 0xf3, 0x8d, 0xca, 0xb0, 0x0d, 0x6b, 0x96, 0x6c, 0xd3, 0xd2, 0xbf, 0xc5, 0x14, + 0x68, 0x5a, 0xd2, 0xf9, 0x87, 0x39, 0x7e, 0x98, 0x12, 0x5a, 0xaf, 0x1d, 0x0e, 0x1e, 0x7a, 0x01, 0x72, 0x47, 0xf4, + 0x73, 0xde, 0xa2, 0x1a, 0x83, 0xbf, 0x32, 0xcc, 0xe0, 0x89, 0xf9, 0x30, 0x44, 0xb3, 0x2c, 0x75, 0x70, 0x2b, 0x45, + 0x71, 0xff, 0x02, 0x77, 0x46, 0x5a, 0x7a, 0xfb, 0xa1, 0xda, 0x31, 0x07, 0x39, 0x63, 0xdf, 0x47, 0xc9, 0x27, 0x96, + 0x3b, 0xe7, 0x5e, 0xa7, 0xfb, 0x15, 0x75, 0xf6, 0xd0, 0x36, 0x7b, 0x55, 0x1d, 0xa3, 0x29, 0xb3, 0x40, 0x1d, 0x11, + 0xb6, 0x3a, 0x5e, 0x8e, 0x51, 0x2d, 0x24, 0x41, 0xe1, 0x65, 0x61, 0x97, 0x38, 0xdc, 0xde, 0x2d, 0x4e, 0x4f, 0xfa, + 0x76, 0x60, 0xdb, 0x60, 0xf1, 0x1f, 0x50, 0xd8, 0x4a, 0x18, 0x16, 0x60, 0x90, 0xed, 0xc6, 0x3d, 0xbe, 0xb9, 0x59, + 0x05, 0x9c, 0xf0, 0x20, 0x9d, 0xba, 0x27, 0x5e, 0xe4, 0x4d, 0x43, 0x18, 0x70, 0x04, 0xcd, 0xb0, 0x4b, 0x6f, 0xb4, + 0x1b, 0xcb, 0x69, 0x30, 0x16, 0xe2, 0x27, 0x51, 0xc1, 0x5f, 0x60, 0x3c, 0x22, 0x1c, 0xa1, 0xb1, 0xef, 0xb3, 0x73, + 0x36, 0x52, 0x76, 0x06, 0x10, 0x2a, 0x72, 0x7b, 0xee, 0x28, 0x34, 0x9a, 0xc1, 0xdc, 0x61, 0x78, 0x30, 0xb0, 0x61, + 0x2f, 0xc1, 0xae, 0x0c, 0xa3, 0xc3, 0xce, 0x70, 0x90, 0x86, 0x20, 0x6b, 0x35, 0x6d, 0x65, 0xd1, 0xa2, 0x56, 0xd4, + 0x1d, 0x0e, 0x9c, 0x53, 0x30, 0xd2, 0xc1, 0x16, 0x77, 0xf0, 0x0d, 0x23, 0x14, 0x45, 0xf8, 0x8e, 0x9d, 0x3c, 0x3b, + 0x9f, 0x3b, 0xf6, 0xee, 0x96, 0xbd, 0x89, 0xa5, 0x9e, 0x0d, 0xec, 0x05, 0x73, 0x87, 0x67, 0xae, 0xd9, 0x79, 0x7b, + 0x88, 0xa0, 0x62, 0x21, 0x4e, 0x7e, 0x36, 0xb0, 0xfb, 0x62, 0xea, 0x36, 0x0c, 0x9a, 0xca, 0xe5, 0xc7, 0x15, 0x3d, + 0x20, 0x54, 0x55, 0x57, 0x05, 0x1d, 0x94, 0x75, 0x03, 0x67, 0x6a, 0x22, 0xd1, 0xc2, 0xc9, 0x24, 0x15, 0xc0, 0xe1, + 0xc1, 0x66, 0x30, 0xa9, 0xd1, 0x6d, 0x7b, 0x38, 0x38, 0x0b, 0xee, 0xd9, 0xf7, 0xd4, 0xcb, 0x09, 0x0b, 0xc0, 0xbb, + 0xa0, 0xe9, 0x4f, 0x50, 0x8b, 0xc0, 0xcf, 0x39, 0x03, 0x24, 0xcf, 0xa8, 0x68, 0x2c, 0x8b, 0x16, 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, 0xc7, 0x1c, 0x54, 0xc2, 0x56, 0xdf, 0x76, 0x07, 0xb6, 0x85, 0x4b, 0xdb, 0xcb, 0x36, + 0x43, 0x41, 0xe1, 0x78, 0xf3, 0x3d, 0x0b, 0xa6, 0xfd, 0xb0, 0x3d, 0x70, 0x72, 0xa1, 0x3a, 0x12, 0x3c, 0xb7, 0x14, + 0x12, 0xbc, 0xed, 0x4d, 0x41, 0xa0, 0x23, 0xe7, 0x6e, 0xd8, 0x9b, 0xaa, 0x10, 0x8a, 0x3e, 0x6e, 0x8e, 0xdd, 0x20, + 0x86, 0x1f, 0x4e, 0x0b, 0x99, 0x66, 0xaa, 0xfb, 0x6a, 0xcd, 0xec, 0x06, 0x63, 0x65, 0x91, 0x27, 0x61, 0xb6, 0xe9, + 0x60, 0x84, 0x16, 0x24, 0xed, 0xee, 0x00, 0x60, 0xd8, 0x74, 0x14, 0xa7, 0x6d, 0x29, 0x56, 0x53, 0xf6, 0xf9, 0x61, + 0xb5, 0x1c, 0x6c, 0x10, 0x31, 0xbf, 0xd2, 0x3e, 0x00, 0x56, 0x90, 0x78, 0xf9, 0x50, 0x9d, 0x79, 0x3d, 0xaf, 0x9d, + 0x6f, 0x2d, 0x95, 0x28, 0x62, 0x9e, 0x21, 0xa1, 0x78, 0xa9, 0xdd, 0x30, 0x61, 0x6e, 0xcf, 0x91, 0x18, 0x9a, 0xe5, + 0xc3, 0x36, 0x30, 0xbd, 0x0a, 0xb0, 0xa7, 0xe6, 0xb6, 0x48, 0xc2, 0xaa, 0xb9, 0x77, 0x08, 0xac, 0x3d, 0x0c, 0x5f, + 0x89, 0x13, 0xc7, 0x9e, 0x8a, 0xe6, 0xb3, 0x24, 0x7c, 0xde, 0x38, 0x2e, 0x8e, 0xf0, 0x44, 0x68, 0xdf, 0x1f, 0x2d, + 0x72, 0x90, 0x07, 0xfc, 0x35, 0x58, 0x06, 0xa1, 0x6c, 0x8a, 0x8e, 0x1e, 0x1e, 0x01, 0x7b, 0x84, 0x78, 0x23, 0x6c, + 0x6e, 0x54, 0xa3, 0x45, 0x49, 0xc6, 0x0b, 0x1d, 0x0c, 0xf7, 0xb8, 0x74, 0xed, 0x51, 0x30, 0xc8, 0x13, 0x63, 0x07, + 0xcf, 0xfc, 0xfd, 0x11, 0x56, 0xe3, 0x04, 0x85, 0x5b, 0xd2, 0x6e, 0xab, 0xc4, 0xdf, 0xbe, 0x9f, 0x82, 0x04, 0xc7, + 0x3a, 0xf0, 0xb3, 0xee, 0xde, 0x4d, 0x24, 0x52, 0xbb, 0x69, 0x8f, 0x4e, 0x22, 0x30, 0x1e, 0x9c, 0xfb, 0x29, 0x54, + 0x23, 0x89, 0xa8, 0x28, 0x47, 0x0b, 0xd4, 0x3c, 0x55, 0xab, 0xe0, 0x3b, 0x34, 0x23, 0xf0, 0x1c, 0xc3, 0xd6, 0xe4, + 0xa7, 0xea, 0xc6, 0x22, 0x96, 0xef, 0xba, 0x74, 0xb4, 0x85, 0x07, 0x90, 0x82, 0xd1, 0x04, 0xc3, 0xb8, 0x14, 0x94, + 0xac, 0xf8, 0xef, 0xa3, 0x11, 0x2b, 0x9f, 0x1e, 0x66, 0x9b, 0x9b, 0x43, 0x71, 0x6e, 0x41, 0x8c, 0xc3, 0x8d, 0xe8, + 0x6a, 0x5c, 0x01, 0x50, 0x9f, 0xce, 0x89, 0xeb, 0x81, 0x69, 0xc5, 0x9a, 0x2e, 0xc5, 0x3e, 0x39, 0xcc, 0x00, 0x14, + 0xdc, 0x72, 0x0e, 0xfd, 0xc1, 0x9f, 0x86, 0xe0, 0x1e, 0xfb, 0x5f, 0xba, 0x5b, 0x4a, 0xd0, 0xf4, 0xe4, 0x99, 0xe2, + 0x92, 0xce, 0x58, 0x3b, 0x1e, 0xc5, 0x46, 0x83, 0xc2, 0x4b, 0x01, 0x03, 0xd0, 0xe6, 0x20, 0x13, 0x2a, 0x0e, 0x42, + 0x8e, 0x0a, 0x6c, 0x1f, 0x37, 0x3f, 0xc7, 0x9d, 0xfd, 0x14, 0x2c, 0xbc, 0x81, 0x7e, 0x7b, 0x0c, 0x6f, 0x7f, 0xd2, + 0x6f, 0x2f, 0x59, 0xf0, 0x4b, 0x29, 0x43, 0xf7, 0xb5, 0x29, 0x1e, 0xa8, 0x29, 0x4a, 0xb1, 0x44, 0x06, 0x0d, 0x99, + 0x9b, 0xaf, 0xc4, 0x6c, 0xb8, 0x5b, 0xa2, 0xda, 0x91, 0xa2, 0x2b, 0xf7, 0x79, 0x74, 0x82, 0xc4, 0x75, 0x4d, 0x52, + 0x18, 0xb9, 0x04, 0x26, 0xc2, 0x15, 0xdf, 0x12, 0x73, 0xf6, 0xdb, 0x60, 0x83, 0xd7, 0xf2, 0x0e, 0xd0, 0xbe, 0x63, + 0xb3, 0x39, 0xbf, 0xd8, 0x27, 0x45, 0x1f, 0xc8, 0xb4, 0x01, 0x71, 0x76, 0xde, 0xee, 0xc5, 0xbb, 0xbc, 0x17, 0x83, + 0x54, 0xcf, 0x15, 0x8b, 0xe1, 0x5e, 0xf5, 0xde, 0x62, 0x94, 0xd2, 0x64, 0x26, 0xaf, 0x86, 0x5e, 0x57, 0xa2, 0xb7, + 0xb9, 0x09, 0x08, 0xf6, 0x8c, 0xae, 0x5c, 0x74, 0x2d, 0x4b, 0x41, 0x13, 0x80, 0xe8, 0x51, 0x9d, 0xe5, 0x88, 0xe3, + 0x30, 0x9b, 0x0d, 0x05, 0x07, 0x73, 0xd7, 0x8e, 0x8a, 0x63, 0x62, 0x77, 0x99, 0xb0, 0x03, 0x98, 0x11, 0x97, 0xb7, + 0x3a, 0x22, 0x3a, 0x2c, 0xfa, 0xeb, 0xf8, 0xf6, 0x47, 0x8f, 0x6d, 0x76, 0x5c, 0xd0, 0x20, 0xb5, 0xb1, 0x1e, 0x56, + 0x63, 0x41, 0x7d, 0xf8, 0x51, 0x53, 0xa9, 0x2c, 0x36, 0x37, 0xcb, 0xfa, 0x51, 0xad, 0xda, 0xc1, 0xb5, 0xd3, 0x94, + 0xf3, 0x66, 0x36, 0x08, 0x07, 0x22, 0x26, 0x50, 0xa0, 0xa5, 0x95, 0x15, 0x03, 0x0c, 0x29, 0xcb, 0x51, 0x3e, 0x85, + 0xcc, 0x8b, 0xcb, 0x52, 0xa7, 0xbe, 0xc8, 0x78, 0x64, 0x88, 0xa7, 0x9e, 0x64, 0xac, 0x80, 0x82, 0xf5, 0x52, 0x2f, + 0xa1, 0x25, 0x02, 0xcc, 0x9f, 0xa9, 0x1c, 0x1a, 0x61, 0x81, 0x44, 0xa1, 0x61, 0x96, 0x28, 0xe3, 0xb3, 0x08, 0x63, + 0xd0, 0xf6, 0x4f, 0x6a, 0xb1, 0xaf, 0x42, 0x19, 0x1d, 0xc5, 0x61, 0x3e, 0x0c, 0xa8, 0x7e, 0x21, 0x25, 0xd8, 0x34, + 0x7c, 0x0f, 0x6c, 0x54, 0x39, 0x9e, 0x24, 0x08, 0x9f, 0xc6, 0x39, 0x23, 0x4f, 0x61, 0x43, 0xc2, 0x2c, 0x4d, 0xdb, + 0x48, 0xb5, 0x8b, 0xcc, 0x20, 0x94, 0x0b, 0xf3, 0x4f, 0x8d, 0xb3, 0x8b, 0x2c, 0x5c, 0x69, 0x0d, 0xe6, 0xc7, 0x1b, + 0x13, 0xa0, 0xec, 0xf2, 0x32, 0x13, 0x3e, 0x6e, 0x44, 0xf6, 0x86, 0xae, 0x98, 0x0e, 0x14, 0x52, 0x81, 0x13, 0x91, + 0xc5, 0x43, 0x67, 0x28, 0x34, 0xc2, 0x01, 0x9d, 0x22, 0xe7, 0xae, 0xb1, 0xe9, 0xf3, 0x81, 0xf6, 0x8d, 0xd2, 0xd0, + 0x49, 0x40, 0x08, 0x08, 0xdc, 0x0d, 0x6b, 0x2a, 0x1d, 0xa4, 0x41, 0x42, 0xa5, 0xe8, 0xe7, 0x00, 0xfe, 0x61, 0x24, + 0x29, 0x00, 0xf6, 0x43, 0x35, 0x52, 0x44, 0x59, 0x16, 0xb8, 0x00, 0x34, 0xd7, 0x3e, 0xae, 0x84, 0x2f, 0x0c, 0x54, + 0x98, 0x9e, 0x66, 0xe5, 0xa5, 0x50, 0x22, 0xef, 0xd6, 0xa4, 0xac, 0x91, 0x4c, 0x3e, 0x45, 0x87, 0x4f, 0x79, 0xd7, + 0xaf, 0x25, 0x1e, 0xba, 0xe0, 0x29, 0x2c, 0xab, 0x7a, 0x7e, 0x15, 0x72, 0x72, 0xae, 0x41, 0x57, 0x48, 0xa1, 0xbf, + 0xe2, 0x24, 0xef, 0xbd, 0xf2, 0xab, 0x5a, 0x6a, 0x0c, 0x65, 0xef, 0xd7, 0x35, 0xc3, 0xf2, 0x72, 0x5e, 0x85, 0x29, + 0x08, 0xb8, 0x25, 0x4b, 0x82, 0xa5, 0xd4, 0x10, 0x60, 0x61, 0x7b, 0xa4, 0x95, 0x82, 0xbc, 0xd4, 0xe1, 0x9d, 0xa7, + 0x60, 0x05, 0x18, 0x87, 0x5a, 0x2a, 0x99, 0x46, 0x12, 0x5f, 0x2a, 0x51, 0x60, 0xca, 0xfd, 0x11, 0xf8, 0xa9, 0xcd, + 0x93, 0xae, 0x73, 0xd7, 0x8f, 0x67, 0x98, 0xda, 0x43, 0xa0, 0xc7, 0xde, 0x1d, 0x30, 0x25, 0xea, 0x3a, 0xac, 0x20, + 0x0e, 0xcd, 0x6a, 0x9a, 0x05, 0xcc, 0x98, 0x36, 0x68, 0xc9, 0x36, 0xd8, 0x72, 0x39, 0xd8, 0x47, 0x62, 0x7b, 0x56, + 0x2b, 0x20, 0x74, 0x0d, 0x1a, 0x18, 0x72, 0x97, 0x0a, 0x2d, 0xcc, 0x7b, 0x5d, 0x2a, 0xc2, 0xfd, 0x39, 0xe0, 0xd2, + 0x0a, 0xce, 0xbc, 0x8c, 0x06, 0xde, 0x8f, 0x8f, 0x13, 0x4c, 0x7c, 0x41, 0xac, 0xc0, 0x0e, 0x0e, 0x3a, 0xcd, 0xa6, + 0xc0, 0xa9, 0xb8, 0x48, 0x19, 0x2c, 0x2b, 0x4a, 0x6d, 0xf8, 0x21, 0x45, 0xb6, 0xee, 0xf2, 0x40, 0x77, 0x21, 0x16, + 0xc0, 0x4e, 0xbf, 0x60, 0xe4, 0x5b, 0xd6, 0xcb, 0x80, 0xc1, 0xa9, 0xd6, 0x38, 0x08, 0xfc, 0xe6, 0x66, 0x32, 0x2c, + 0x53, 0x62, 0xbb, 0x26, 0xab, 0x0b, 0xc8, 0x61, 0xa8, 0x26, 0xee, 0x20, 0x2c, 0x95, 0x3d, 0x5e, 0x94, 0x33, 0x5c, + 0x2e, 0x65, 0x21, 0x37, 0xcf, 0xab, 0x69, 0x3e, 0xb7, 0xd2, 0x6c, 0x3a, 0xde, 0x8a, 0x2f, 0x0a, 0xfe, 0x81, 0x13, + 0x4b, 0xab, 0x9e, 0x52, 0x2b, 0x3c, 0xca, 0xdc, 0x92, 0x75, 0x4a, 0x6a, 0x75, 0xdd, 0x40, 0x35, 0xc2, 0xd3, 0x34, + 0x6c, 0x04, 0x42, 0x4c, 0x70, 0xf1, 0xeb, 0x26, 0x13, 0xd3, 0xde, 0x12, 0x52, 0x47, 0xd8, 0x3d, 0x94, 0x13, 0xdc, + 0xd5, 0x3c, 0xfb, 0x3c, 0x9c, 0x5f, 0xcd, 0xdc, 0x7b, 0x06, 0x73, 0x3f, 0x0e, 0xb9, 0xc1, 0xe8, 0xb1, 0x4c, 0xf8, + 0x91, 0xb1, 0x8f, 0x5c, 0x55, 0x3d, 0x39, 0x09, 0x2b, 0x91, 0x25, 0x9e, 0x8c, 0xa3, 0x0e, 0xe3, 0x54, 0xb4, 0x26, + 0xc8, 0x2e, 0x2f, 0x0b, 0x73, 0x2f, 0x50, 0xd0, 0xd4, 0xe3, 0xf5, 0x38, 0x6d, 0xc5, 0xce, 0x46, 0x24, 0x72, 0xef, + 0x55, 0x2d, 0x12, 0x59, 0xf1, 0x39, 0x8e, 0x74, 0xc5, 0x41, 0xee, 0x93, 0x93, 0xd5, 0x4d, 0x2a, 0x74, 0x8b, 0x46, + 0xdb, 0xd8, 0xa3, 0xfa, 0x40, 0x52, 0xcf, 0xa8, 0xc0, 0xaa, 0xc6, 0xbe, 0x7b, 0xb7, 0x23, 0xd2, 0x2d, 0x95, 0x62, + 0x83, 0xa5, 0x85, 0xd1, 0x8c, 0x51, 0x30, 0x28, 0x29, 0x32, 0x50, 0xa3, 0xfc, 0x0a, 0xc1, 0xb0, 0x47, 0x0d, 0x40, + 0x71, 0xae, 0xaf, 0x7e, 0x5c, 0x4a, 0xb6, 0x10, 0x90, 0xb8, 0x4b, 0x06, 0x62, 0x4d, 0x30, 0x33, 0xf2, 0xc9, 0x7b, + 0xe0, 0xbc, 0x01, 0x43, 0x1f, 0x01, 0xfc, 0x02, 0xb1, 0xe9, 0xc1, 0xc4, 0xb6, 0x89, 0x28, 0xfa, 0x6c, 0xe0, 0x39, + 0x00, 0x3b, 0xaf, 0x42, 0xa3, 0xef, 0xaa, 0x14, 0x30, 0x64, 0x03, 0x37, 0x60, 0x55, 0x58, 0x6e, 0xef, 0x39, 0xb8, + 0x0d, 0xf0, 0xfa, 0x4c, 0x36, 0xdf, 0xc0, 0x3c, 0xc1, 0xea, 0xec, 0xc2, 0xaf, 0x2c, 0x6b, 0x71, 0xee, 0x74, 0xd0, + 0xa8, 0x57, 0x94, 0x10, 0xb5, 0xfb, 0x58, 0x7b, 0x80, 0x11, 0x16, 0xf1, 0xfe, 0x0a, 0xdf, 0xf5, 0xb8, 0xe5, 0x9e, + 0x46, 0x8b, 0x30, 0x5d, 0x25, 0x8d, 0x41, 0xc9, 0xba, 0x9f, 0x8c, 0xb8, 0x97, 0xfb, 0x22, 0x16, 0x5c, 0xe1, 0xc8, + 0xaa, 0x90, 0x62, 0x03, 0x49, 0x7a, 0xda, 0xa3, 0x03, 0xf6, 0x8d, 0x66, 0x2f, 0xa0, 0xcc, 0xfb, 0x8a, 0x54, 0x12, + 0x52, 0x9a, 0xdd, 0x10, 0x49, 0xc2, 0x5a, 0x91, 0xa7, 0xce, 0xfb, 0x8e, 0xf6, 0xb9, 0x95, 0x44, 0x30, 0x82, 0x93, + 0x30, 0x1d, 0x2b, 0x0f, 0x9a, 0x02, 0x5c, 0x45, 0x47, 0x4c, 0xdf, 0x04, 0xe4, 0x37, 0x03, 0xb9, 0xbd, 0x92, 0x5c, + 0x9b, 0x6b, 0x18, 0x9e, 0x20, 0xc1, 0xaa, 0x48, 0x04, 0x1e, 0x51, 0x03, 0x8e, 0xf9, 0x3a, 0xcf, 0x03, 0x4c, 0xf8, + 0xda, 0xde, 0x04, 0x80, 0x72, 0x72, 0x55, 0x9c, 0x95, 0x40, 0x37, 0x60, 0xb9, 0x3e, 0x4e, 0x8d, 0x8a, 0xc4, 0xc5, + 0x8d, 0xe9, 0xea, 0x96, 0xfe, 0x0c, 0x2d, 0x67, 0x32, 0xc4, 0x74, 0x10, 0x04, 0x64, 0xea, 0x3b, 0xe6, 0x08, 0x99, + 0x2b, 0xac, 0xcf, 0xb9, 0x53, 0x9b, 0xba, 0xc7, 0xa8, 0x9b, 0x27, 0xa9, 0xc5, 0xeb, 0xb4, 0x29, 0x25, 0x62, 0x52, + 0x62, 0x6e, 0x88, 0x54, 0x6c, 0xa6, 0xc4, 0x9d, 0x5b, 0xdf, 0x68, 0x21, 0x6d, 0xb4, 0x0d, 0x91, 0x83, 0xcd, 0x2a, + 0x79, 0x4f, 0x60, 0x3c, 0x17, 0x84, 0x2f, 0x5f, 0x51, 0x92, 0x0e, 0x73, 0x4c, 0x04, 0xab, 0x17, 0x53, 0x91, 0xbf, + 0x73, 0x74, 0x9a, 0xbd, 0x41, 0x0f, 0x52, 0x6f, 0x20, 0x31, 0x6b, 0xe2, 0xbb, 0x90, 0x86, 0x3a, 0x42, 0xa0, 0x32, + 0xaa, 0x65, 0x3a, 0x4e, 0xac, 0xc2, 0x37, 0x82, 0xaf, 0xde, 0xea, 0xe3, 0x7c, 0xe3, 0xb9, 0xb1, 0x1a, 0x41, 0x0c, + 0xde, 0x42, 0x3e, 0xf4, 0xa4, 0x08, 0x07, 0xc2, 0xe5, 0x9b, 0x9b, 0xbd, 0x7c, 0x97, 0x57, 0x21, 0x92, 0x0a, 0xc6, + 0x18, 0x33, 0x8a, 0x71, 0x4f, 0xd4, 0xd4, 0x62, 0x0e, 0x03, 0xcb, 0xd6, 0x61, 0x8e, 0x07, 0x00, 0xd0, 0xd2, 0x94, + 0x5e, 0x35, 0x15, 0x2a, 0xcf, 0x73, 0x09, 0x9f, 0xea, 0x10, 0x55, 0x35, 0x7e, 0xbb, 0x3e, 0x03, 0x85, 0xe0, 0xbe, + 0xd3, 0xf1, 0xf0, 0x10, 0x02, 0x56, 0x51, 0xc8, 0x02, 0xbd, 0x41, 0x7b, 0x55, 0x22, 0x14, 0x33, 0x27, 0xeb, 0x31, + 0xc3, 0x49, 0x05, 0x5b, 0xa8, 0x84, 0xa5, 0xd2, 0x02, 0xbf, 0xda, 0x08, 0xcd, 0x53, 0xc6, 0xbd, 0x57, 0x15, 0xce, + 0xa0, 0x3f, 0x98, 0xb7, 0xca, 0xa8, 0x6f, 0x57, 0x4e, 0x64, 0x2a, 0x30, 0x71, 0x33, 0x4b, 0xed, 0xf7, 0xcb, 0x3a, + 0xed, 0xe7, 0x15, 0x72, 0x9f, 0x93, 0xe6, 0xeb, 0xdc, 0x42, 0xf3, 0xc9, 0x70, 0xbf, 0x52, 0x7e, 0x68, 0x61, 0xd4, + 0x94, 0x5f, 0x5e, 0x57, 0x7e, 0x85, 0xa7, 0xc2, 0x5b, 0xfd, 0x2e, 0x0a, 0x5d, 0xd4, 0xe7, 0x60, 0x08, 0xe9, 0x47, + 0x70, 0x0d, 0x0d, 0x1e, 0x14, 0xc9, 0x62, 0xb1, 0x76, 0x41, 0x5c, 0x1f, 0x73, 0xaa, 0x1d, 0xca, 0x18, 0x23, 0x9e, + 0x96, 0x1c, 0x24, 0x19, 0x1c, 0x8c, 0xdf, 0xc0, 0x80, 0x98, 0x94, 0x84, 0x74, 0x08, 0x9d, 0xb5, 0x99, 0x88, 0xca, + 0x5d, 0xbc, 0xd9, 0xb8, 0xac, 0x29, 0x14, 0x61, 0x27, 0x98, 0xa9, 0x94, 0x0a, 0x02, 0x69, 0xf2, 0xdd, 0xe9, 0xd4, + 0x82, 0xa1, 0x85, 0x6b, 0x2a, 0x20, 0xaf, 0xed, 0x7a, 0xd0, 0xe4, 0x3d, 0xc5, 0xd0, 0xd7, 0xa9, 0x11, 0x2f, 0x33, + 0xf8, 0x1a, 0x36, 0x7f, 0x4d, 0x94, 0xe4, 0x21, 0x13, 0xb1, 0x57, 0xf0, 0x89, 0x90, 0x4d, 0xc1, 0xce, 0x04, 0xfa, + 0xa1, 0x5d, 0xd9, 0x4b, 0x77, 0x8b, 0xca, 0xa5, 0x45, 0x63, 0x2b, 0x51, 0xb3, 0xe6, 0x87, 0xf1, 0x66, 0x0a, 0xfb, + 0xd9, 0xa3, 0x04, 0x02, 0xd2, 0x54, 0x4e, 0x52, 0xcd, 0x7b, 0x98, 0x0e, 0x01, 0x24, 0xd8, 0xfd, 0x04, 0x16, 0xfa, + 0x4d, 0x89, 0x09, 0x16, 0x55, 0x63, 0xb7, 0x39, 0x68, 0xcd, 0x39, 0x69, 0xbe, 0x39, 0x6a, 0xed, 0x4d, 0x65, 0x3d, + 0x63, 0x76, 0x80, 0x6d, 0xbb, 0x9b, 0xc5, 0x61, 0xba, 0xd9, 0x19, 0x1a, 0x82, 0x0b, 0x8f, 0xff, 0x93, 0x12, 0xd3, + 0x40, 0x72, 0xa9, 0x1b, 0x3f, 0xa1, 0x0e, 0xc3, 0xff, 0x96, 0xa4, 0x80, 0x07, 0xb5, 0xd5, 0x58, 0x71, 0xee, 0x15, + 0x47, 0xc9, 0x65, 0x55, 0xed, 0x6a, 0x09, 0x1a, 0xba, 0x91, 0x8c, 0x89, 0x62, 0x9e, 0x13, 0x00, 0xa3, 0xd8, 0xfc, + 0x29, 0xd3, 0x49, 0xde, 0xbf, 0xac, 0x4d, 0xed, 0xf6, 0x7d, 0x3f, 0xca, 0x4f, 0xe8, 0x48, 0x45, 0x65, 0x73, 0x12, + 0xf3, 0x6f, 0x0b, 0x30, 0xcd, 0x89, 0x0f, 0xf5, 0x5c, 0xc3, 0x50, 0x80, 0xaf, 0x6c, 0x28, 0x35, 0xdb, 0xe3, 0xdf, + 0x3b, 0xdb, 0x7d, 0x49, 0x14, 0xc1, 0x02, 0x0d, 0xba, 0x5c, 0x81, 0x2f, 0x60, 0x19, 0xdc, 0x92, 0x7e, 0x0a, 0xbe, + 0x97, 0x57, 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, 0xe7, 0xae, 0xe1, 0xc6, 0xf7, 0x1a, 0xf9, 0xe3, + 0x7b, 0xcf, 0x41, 0xb7, 0x13, 0x69, 0xef, 0xde, 0xcd, 0xef, 0x90, 0x85, 0x86, 0xf7, 0xc2, 0xe6, 0xd0, 0x16, 0xe9, + 0x92, 0xab, 0x67, 0x2c, 0xc6, 0xdb, 0x22, 0x54, 0x86, 0x0f, 0x58, 0x30, 0x07, 0x0c, 0xc1, 0x63, 0xa7, 0x32, 0xf9, + 0x0c, 0x1b, 0x4d, 0xb1, 0x6b, 0x2e, 0x0c, 0x3e, 0x50, 0x95, 0x85, 0xe4, 0xc5, 0x3a, 0xd9, 0x9e, 0x9d, 0xc2, 0xf3, + 0xcb, 0xb8, 0x00, 0xea, 0x00, 0xfa, 0x15, 0x95, 0xc5, 0x06, 0x72, 0x71, 0x53, 0xd6, 0x7a, 0x45, 0xe3, 0xf1, 0xb5, + 0x5d, 0x58, 0x5d, 0x81, 0x4f, 0xa3, 0x74, 0x9c, 0x88, 0x49, 0xcc, 0xa4, 0xca, 0x35, 0xb9, 0x36, 0xba, 0x97, 0xb6, + 0x68, 0x9e, 0x0b, 0x09, 0x5e, 0x11, 0xb8, 0x21, 0xf4, 0x95, 0xbe, 0x5c, 0x6f, 0xa0, 0xe0, 0x51, 0x7b, 0x73, 0x11, + 0x4c, 0x4c, 0x3c, 0x66, 0x48, 0x4d, 0xbf, 0x0e, 0xa7, 0x56, 0x16, 0x2b, 0x0e, 0xbf, 0xce, 0x19, 0x6b, 0x28, 0x00, + 0xe2, 0x93, 0x07, 0x57, 0xbb, 0x49, 0xaf, 0x94, 0x76, 0x50, 0x1a, 0x21, 0xbe, 0xad, 0xf0, 0x75, 0x97, 0x8a, 0xaf, + 0x5c, 0x75, 0xef, 0x6b, 0xc6, 0x8c, 0x0b, 0x46, 0xcf, 0xf9, 0x2c, 0x69, 0x5c, 0xbb, 0xa1, 0xbb, 0x3a, 0x3f, 0x7a, + 0x3f, 0xc8, 0xbc, 0x85, 0x19, 0xb0, 0x09, 0xa8, 0x82, 0xe7, 0xde, 0x6b, 0xe3, 0x44, 0xf9, 0x3b, 0xf3, 0x88, 0x57, + 0x0e, 0xb3, 0xee, 0x24, 0xf9, 0xbb, 0xc1, 0x77, 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, 0x75, 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, 0x98, 0xad, 0x1f, 0xbc, + 0xb9, 0x7b, 0x05, 0x2c, 0xc7, 0x80, 0xdd, 0x31, 0x73, 0x1a, 0x43, 0x55, 0x1b, 0xf8, 0x87, 0xf5, 0x83, 0xad, 0xdb, + 0xc3, 0x3f, 0x0c, 0x7e, 0x08, 0xae, 0x6d, 0x6c, 0x6c, 0xe3, 0xed, 0x5a, 0x22, 0xc8, 0x2b, 0x3c, 0xd0, 0xc7, 0xab, + 0x8f, 0x82, 0x96, 0xeb, 0xc4, 0xf6, 0xc0, 0xa1, 0xb0, 0x35, 0xc8, 0x37, 0x29, 0x93, 0x46, 0x8b, 0x82, 0x67, 0x33, + 0x39, 0x43, 0x21, 0xaf, 0xf9, 0x38, 0x68, 0x3b, 0xc2, 0xdf, 0xc0, 0xa9, 0x1d, 0x2f, 0x2f, 0x3f, 0x41, 0x1f, 0xf0, + 0x74, 0xa5, 0x34, 0x15, 0x71, 0x4a, 0xb9, 0x45, 0x57, 0xeb, 0x3c, 0x18, 0x29, 0x2e, 0xa6, 0xa8, 0x74, 0xdc, 0xe5, + 0xb5, 0xb3, 0x91, 0xd3, 0x5f, 0xe2, 0xd5, 0x45, 0xba, 0x7c, 0x24, 0xb2, 0x55, 0x4b, 0xef, 0x37, 0x7d, 0xba, 0x6d, + 0xcf, 0x18, 0x9f, 0x66, 0x63, 0x3a, 0x98, 0xf1, 0x71, 0x22, 0xbc, 0x3e, 0x31, 0xd6, 0x77, 0x8b, 0xc0, 0x74, 0x73, + 0x6c, 0xf2, 0xc3, 0xf1, 0x7a, 0xb3, 0x59, 0xe3, 0x0e, 0xde, 0x38, 0x4f, 0x9c, 0x65, 0x89, 0x11, 0x95, 0xa5, 0x86, + 0x07, 0xb4, 0x42, 0xdc, 0xbc, 0x67, 0x02, 0xe3, 0xb2, 0x0b, 0x92, 0xda, 0x6e, 0x20, 0x70, 0xb1, 0x27, 0x31, 0x4b, + 0xc6, 0xb6, 0x07, 0xe5, 0x81, 0xbe, 0x18, 0x4d, 0xb7, 0x80, 0x69, 0x79, 0xed, 0xec, 0x2c, 0xb5, 0xbd, 0x6a, 0xaa, + 0x00, 0x66, 0xc9, 0xf2, 0xf8, 0x04, 0x59, 0xf7, 0x5b, 0xe8, 0x22, 0x06, 0x8c, 0x8d, 0x2b, 0x73, 0xee, 0x72, 0xdd, + 0x8a, 0xf8, 0x46, 0x13, 0x69, 0x52, 0x1f, 0x52, 0xdf, 0x61, 0x58, 0xab, 0xab, 0x1c, 0x24, 0x70, 0x8f, 0xbc, 0x5b, + 0xe2, 0xd2, 0xd3, 0x67, 0x16, 0x93, 0x2a, 0x7d, 0x4b, 0x5d, 0x8b, 0x6b, 0x86, 0xbd, 0xe2, 0x01, 0xd8, 0x1f, 0x18, + 0xb7, 0x88, 0x45, 0xbc, 0x9d, 0xd7, 0x52, 0x58, 0x1b, 0x73, 0xa0, 0xb9, 0xe1, 0x06, 0xbf, 0xb1, 0x6a, 0xcd, 0xc0, + 0x0c, 0x33, 0xce, 0x48, 0x7e, 0x33, 0xee, 0x55, 0x4d, 0x1c, 0xb9, 0x0a, 0x20, 0xfa, 0x96, 0x74, 0x49, 0x0e, 0xaf, + 0x64, 0xb9, 0xea, 0x0c, 0xf9, 0x57, 0x58, 0x67, 0xbd, 0x38, 0x01, 0x33, 0x69, 0xca, 0x4b, 0x4c, 0x4c, 0x11, 0x97, + 0x9b, 0x65, 0xcc, 0xd3, 0xf4, 0x59, 0xb4, 0x83, 0x93, 0x1b, 0x09, 0x1c, 0xb1, 0x6f, 0x2c, 0x43, 0x33, 0x61, 0x23, + 0x26, 0xd2, 0xa8, 0x94, 0x12, 0x3e, 0x90, 0x4b, 0x2d, 0xf9, 0xcb, 0x5c, 0x5e, 0x7d, 0xb9, 0x4d, 0x70, 0x40, 0x5e, + 0x03, 0xcb, 0xa1, 0x71, 0xdc, 0x32, 0x90, 0x88, 0xc5, 0x80, 0x18, 0xb5, 0x2a, 0x57, 0x93, 0x51, 0x9d, 0xcc, 0x57, + 0xc8, 0x85, 0x8a, 0x3c, 0xb8, 0x25, 0x50, 0xf2, 0xe7, 0x98, 0x3a, 0x98, 0x95, 0xda, 0x4d, 0x8b, 0x4d, 0x92, 0xf7, + 0xcc, 0x80, 0xe4, 0xfa, 0x6b, 0x78, 0x68, 0xfc, 0xe2, 0x95, 0x39, 0x25, 0x7c, 0x51, 0xc6, 0xd2, 0xd2, 0x98, 0x4b, + 0xff, 0x42, 0xde, 0xa7, 0x95, 0x80, 0xfd, 0x0a, 0x62, 0xca, 0xc0, 0x25, 0x36, 0x2e, 0x48, 0xca, 0x6b, 0x79, 0xca, + 0xee, 0x6b, 0x28, 0xdf, 0x15, 0x93, 0xae, 0x52, 0x59, 0x57, 0x58, 0x75, 0xbf, 0x2e, 0x58, 0x7e, 0xb1, 0xcf, 0x30, + 0x37, 0x19, 0x0d, 0xb2, 0x15, 0x33, 0x9b, 0xf2, 0xab, 0xbd, 0x6b, 0xbf, 0xf2, 0x50, 0xd2, 0xa1, 0x5a, 0xa5, 0x9b, + 0x57, 0x6e, 0x38, 0xc6, 0x8d, 0x1b, 0x8e, 0x00, 0x36, 0x86, 0x9d, 0x2a, 0x52, 0xeb, 0xfc, 0xf7, 0xd5, 0xf0, 0x13, + 0xed, 0xb5, 0xa1, 0xde, 0x75, 0xc3, 0xb5, 0xe9, 0xe9, 0xd7, 0xa0, 0x6a, 0x64, 0x09, 0x5d, 0x87, 0x2a, 0x26, 0x23, + 0x51, 0x62, 0xba, 0x4a, 0x79, 0xd4, 0xd7, 0x88, 0x73, 0x10, 0x37, 0x94, 0xbf, 0xf8, 0xe7, 0xf0, 0xe2, 0x28, 0x40, + 0x23, 0x6a, 0x39, 0xc9, 0x52, 0xde, 0x9a, 0x44, 0xb3, 0x38, 0xb9, 0x08, 0x16, 0x71, 0x6b, 0x96, 0xa5, 0x59, 0x31, + 0x07, 0xae, 0xf4, 0x8a, 0x0b, 0xb0, 0xe1, 0x67, 0xad, 0x45, 0xec, 0x3d, 0x67, 0xc9, 0x29, 0xe3, 0xf1, 0x28, 0xf2, + 0xec, 0xbd, 0x1c, 0xc4, 0x83, 0xf5, 0x3a, 0xca, 0xf3, 0xec, 0xcc, 0xf6, 0xde, 0x65, 0xc7, 0xc0, 0xb4, 0xde, 0x9b, + 0xf3, 0x8b, 0x13, 0x96, 0x7a, 0xef, 0x8f, 0x17, 0x29, 0x5f, 0x78, 0x45, 0x94, 0x16, 0xad, 0x82, 0xe5, 0xf1, 0x04, + 0xd4, 0x44, 0x92, 0xe5, 0x2d, 0xcc, 0x7f, 0x9e, 0xb1, 0x20, 0x89, 0x4f, 0xa6, 0xdc, 0x1a, 0x47, 0xf9, 0xa7, 0x5e, + 0xab, 0x35, 0xcf, 0xe3, 0x59, 0x94, 0x5f, 0xb4, 0xa8, 0x45, 0xf0, 0x45, 0x7b, 0x3b, 0xfa, 0x6a, 0x72, 0xbf, 0xc7, + 0x73, 0xe8, 0x1b, 0x23, 0x15, 0x03, 0x10, 0x3e, 0xd6, 0xf6, 0x4e, 0x7b, 0x56, 0xdc, 0x11, 0x27, 0x4a, 0x51, 0xca, + 0xcb, 0x23, 0xef, 0x23, 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, 0x9d, + 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, + 0xc3, 0x55, 0x1f, 0xb1, 0xf9, 0xfa, 0xc6, 0x2f, 0xd5, 0x78, 0x67, 0x2a, 0x6f, 0x50, 0xa8, 0x08, 0xf5, 0xcd, 0x16, + 0xcc, 0x78, 0xcb, 0xfb, 0x1d, 0x7d, 0x50, 0x35, 0xf8, 0x9a, 0x91, 0xd6, 0x0b, 0xb8, 0x67, 0xe6, 0x02, 0xf5, 0xd2, + 0x3e, 0x86, 0xa4, 0x5a, 0x2d, 0x17, 0xf4, 0x06, 0xc3, 0x10, 0x12, 0x1d, 0x08, 0x3a, 0xf9, 0xa0, 0xa0, 0x6f, 0x6a, + 0x64, 0x6e, 0x50, 0x38, 0x99, 0x0b, 0x5b, 0x3e, 0xd3, 0x72, 0x1d, 0x94, 0x34, 0x78, 0xd9, 0x1f, 0x98, 0x6c, 0x00, + 0xd2, 0x9b, 0x42, 0x5d, 0x7f, 0x09, 0x85, 0x2b, 0xa5, 0x1c, 0xa9, 0xd9, 0x75, 0x57, 0xf4, 0x61, 0x55, 0x62, 0xca, + 0x48, 0x3e, 0x1c, 0xfe, 0x3b, 0x0c, 0x7b, 0x47, 0x3b, 0x96, 0x45, 0xb6, 0xc8, 0x47, 0x14, 0xa9, 0x5b, 0xf5, 0xf8, + 0x6d, 0x52, 0xb8, 0xb6, 0xc7, 0xb4, 0x9c, 0x47, 0x37, 0xb8, 0xf6, 0x91, 0x03, 0x4e, 0x87, 0x20, 0xe2, 0x8e, 0x81, + 0x8c, 0x72, 0x28, 0x08, 0x51, 0x75, 0x8d, 0x29, 0xdf, 0x8d, 0xee, 0x5f, 0xfa, 0x8b, 0x34, 0x06, 0x49, 0xf7, 0x31, + 0x1e, 0xd3, 0xbd, 0x93, 0x78, 0x4c, 0x07, 0x11, 0x2d, 0x4a, 0x3c, 0xc2, 0xc8, 0x36, 0x14, 0xa8, 0xef, 0xb0, 0xc0, + 0xb3, 0x4c, 0x64, 0xb1, 0x5b, 0x36, 0x1e, 0x26, 0x18, 0xaa, 0x72, 0x9c, 0xcd, 0xa2, 0x38, 0x0d, 0xf0, 0xfb, 0x20, + 0x9e, 0x1e, 0x31, 0xc0, 0x2e, 0x1e, 0xfc, 0x64, 0x32, 0x17, 0xad, 0xe3, 0xfa, 0xbf, 0x80, 0x1c, 0xa1, 0xfe, 0xa5, + 0xf4, 0xc3, 0x34, 0x5c, 0xea, 0x98, 0xb7, 0x5e, 0x0a, 0xb2, 0x87, 0x2b, 0x9b, 0x95, 0x51, 0x9c, 0x63, 0x97, 0xd3, + 0x8f, 0x41, 0xab, 0x13, 0x74, 0xb4, 0xeb, 0x5a, 0xbb, 0x8d, 0x2a, 0x72, 0x59, 0xe4, 0x8d, 0x46, 0x82, 0x41, 0x3f, + 0x0b, 0x38, 0xab, 0x77, 0x0d, 0xab, 0x27, 0xf9, 0x12, 0x03, 0x38, 0x27, 0xa9, 0x53, 0x03, 0x82, 0xce, 0x02, 0xae, + 0x98, 0xca, 0x2d, 0x23, 0x52, 0x4a, 0x8f, 0x69, 0x03, 0xd7, 0xef, 0x12, 0xe1, 0xbd, 0xa1, 0x7a, 0x0a, 0x94, 0x62, + 0xb9, 0xf1, 0xd1, 0xae, 0xd8, 0xf1, 0x16, 0xf1, 0x58, 0x68, 0xc3, 0x16, 0xb4, 0xad, 0x3f, 0x8d, 0x80, 0x4a, 0x9f, + 0x42, 0x7b, 0x63, 0xe9, 0xa8, 0xc4, 0xfa, 0x1c, 0xe6, 0xda, 0x13, 0x5a, 0x8f, 0x6e, 0xe4, 0xdb, 0xfd, 0x8d, 0x25, + 0x2f, 0x77, 0xb7, 0x44, 0xef, 0xfe, 0x51, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, + 0xf4, 0x5d, 0x1c, 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, + 0x84, 0x94, 0x42, 0xe4, 0x60, 0xf4, 0x56, 0xa1, 0x38, 0x42, 0x08, 0x06, 0x1b, 0xcb, 0xb8, 0x0c, 0x37, 0x96, 0x59, + 0x79, 0x04, 0x96, 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, + 0x6d, 0x6b, 0x20, 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xfb, 0x30, 0xd0, + 0xee, 0x96, 0x68, 0x04, 0x78, 0xe5, 0x08, 0x72, 0x85, 0x16, 0xa6, 0xe3, 0x98, 0xa8, 0x8d, 0xe3, 0x53, 0x4d, 0x3a, + 0xca, 0x4d, 0xee, 0xef, 0x26, 0xd1, 0x31, 0x4b, 0x60, 0xc8, 0xe2, 0xf2, 0xb2, 0x0d, 0x23, 0x89, 0x57, 0x6b, 0x37, + 0x4e, 0xe7, 0x0b, 0xf9, 0x99, 0x2d, 0x98, 0xb8, 0x83, 0x27, 0x9f, 0x78, 0x0b, 0x60, 0xa0, 0x8e, 0xf2, 0x02, 0x39, + 0x00, 0x80, 0x48, 0xa7, 0x08, 0x08, 0x5d, 0xc5, 0x16, 0x50, 0x1a, 0x8f, 0x57, 0xcb, 0x60, 0x27, 0xce, 0xb1, 0x34, + 0x85, 0xe7, 0x59, 0x9c, 0xe2, 0x63, 0x81, 0x8f, 0xd1, 0x39, 0x3e, 0x66, 0xf0, 0xa8, 0x71, 0xcf, 0x4b, 0xfb, 0x6f, + 0xb2, 0x02, 0x96, 0x26, 0x40, 0x76, 0x79, 0x09, 0xf2, 0x5e, 0x93, 0x60, 0x77, 0x0b, 0x88, 0x85, 0x9c, 0x22, 0x3e, + 0xba, 0xc2, 0x4c, 0x32, 0xb2, 0x62, 0xde, 0x12, 0xe5, 0x16, 0x69, 0xd5, 0x10, 0x9c, 0xae, 0xdc, 0x69, 0x18, 0x0f, + 0x9e, 0x4c, 0x2f, 0x79, 0x82, 0x2f, 0xae, 0x6d, 0x89, 0xaf, 0x62, 0x08, 0xa2, 0xd0, 0x23, 0x62, 0xa8, 0xcb, 0xb8, + 0xfc, 0xde, 0x4d, 0x1c, 0xda, 0x38, 0x0b, 0xd8, 0x6f, 0xa8, 0x05, 0x78, 0x14, 0x27, 0xa2, 0xf1, 0x1a, 0x7c, 0x1a, + 0x79, 0x82, 0x84, 0xce, 0xee, 0x56, 0x05, 0x1b, 0x00, 0x3f, 0x12, 0xb7, 0xac, 0x1d, 0x91, 0x03, 0x69, 0x8b, 0x72, + 0x3a, 0x3b, 0x97, 0x5b, 0x5a, 0x46, 0x76, 0x45, 0xac, 0x5c, 0xa3, 0x4a, 0x39, 0x8b, 0xf6, 0x24, 0x4a, 0xd7, 0x35, + 0x05, 0xe8, 0xe7, 0x8c, 0x8d, 0x3d, 0xdb, 0x02, 0x59, 0x2a, 0x9e, 0x3f, 0x26, 0xec, 0x94, 0xc9, 0x2f, 0xa5, 0xe8, + 0x41, 0x74, 0xe5, 0x08, 0x54, 0x32, 0x97, 0x97, 0x38, 0x25, 0x7b, 0x2a, 0x1c, 0x25, 0x25, 0xea, 0x88, 0x78, 0xb6, + 0x31, 0x68, 0x73, 0x8e, 0x76, 0x7d, 0x58, 0xaf, 0x03, 0xd6, 0xae, 0x2d, 0xe0, 0x25, 0x3b, 0xee, 0x66, 0xe4, 0x60, + 0x00, 0x36, 0x99, 0xc0, 0x76, 0x51, 0x91, 0x65, 0x2d, 0x0b, 0x04, 0x54, 0xe0, 0x94, 0x7a, 0xb6, 0x68, 0x61, 0x57, + 0x6d, 0xf5, 0x93, 0x24, 0x4e, 0x92, 0x8d, 0x3e, 0xad, 0x99, 0x0b, 0xb8, 0x63, 0x43, 0x44, 0x5a, 0x1b, 0xf2, 0xcd, + 0xfe, 0xb7, 0x7f, 0xfe, 0x9f, 0xff, 0x15, 0x06, 0xa6, 0x7e, 0x6e, 0x69, 0x5d, 0xdd, 0xea, 0x7f, 0x40, 0xab, 0x45, + 0x7a, 0x43, 0xbb, 0xbf, 0xfe, 0xe3, 0x7f, 0x83, 0x66, 0x74, 0x23, 0xc7, 0x2d, 0x8f, 0x08, 0xa2, 0x11, 0x1a, 0x41, + 0x9f, 0x05, 0x52, 0x6d, 0x90, 0x2b, 0x67, 0xfa, 0x27, 0x04, 0xbb, 0xe0, 0xd9, 0xfc, 0x5a, 0x70, 0x10, 0xea, 0x51, + 0x92, 0x15, 0x4c, 0xc3, 0x23, 0x64, 0xed, 0xe7, 0x01, 0x44, 0x73, 0xcd, 0x81, 0xcb, 0x0b, 0x4b, 0x8f, 0x23, 0x96, + 0x67, 0xdd, 0x38, 0x8d, 0xd5, 0x2b, 0x18, 0x27, 0x74, 0x28, 0xae, 0x00, 0xeb, 0x25, 0x9e, 0xe0, 0x81, 0x04, 0x82, + 0x5b, 0xff, 0xca, 0xd7, 0xfa, 0xc1, 0x34, 0x7f, 0x8a, 0xb1, 0x44, 0x28, 0x45, 0x8d, 0x00, 0x3f, 0x41, 0x68, 0x7d, + 0xd4, 0xcf, 0xd1, 0xb9, 0x7e, 0x46, 0xc1, 0x26, 0x26, 0x00, 0x5d, 0x34, 0x43, 0x33, 0xc3, 0x9c, 0x41, 0xa4, 0x01, + 0x54, 0x7e, 0xa4, 0x91, 0x4d, 0x22, 0x84, 0xd7, 0x47, 0x4c, 0xba, 0xc4, 0x2b, 0x36, 0x8b, 0x9c, 0x7d, 0x4c, 0xb2, + 0x33, 0x0c, 0x4e, 0x21, 0x91, 0xae, 0xaa, 0x2f, 0xff, 0xf5, 0x5f, 0x7c, 0xff, 0x5f, 0xff, 0xe5, 0x8a, 0x06, 0x53, + 0xd8, 0x07, 0x60, 0x4d, 0xf2, 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, 0x20, 0xfd, 0x85, 0x7e, 0x78, 0x22, + 0xa4, 0x7b, 0x20, 0x96, 0x41, 0xca, 0xfa, 0x1a, 0x40, 0x9e, 0x75, 0x40, 0x13, 0x50, 0x93, 0xb8, 0xd2, 0xad, 0x64, + 0x8a, 0x1c, 0xe7, 0xfd, 0x57, 0x78, 0xab, 0xce, 0xc2, 0xde, 0xa8, 0x59, 0x0b, 0x32, 0x04, 0x38, 0x19, 0x02, 0x52, + 0x03, 0x99, 0x3a, 0x18, 0x3d, 0x8c, 0x6c, 0xbd, 0xae, 0xfd, 0x88, 0xdd, 0x6b, 0xda, 0x92, 0x4b, 0x6d, 0x19, 0x4b, + 0x4b, 0x56, 0x6a, 0x4b, 0xfc, 0x76, 0x4a, 0x43, 0x5b, 0xc6, 0x57, 0x6a, 0x4b, 0xa4, 0xdc, 0x00, 0x47, 0x0e, 0xed, + 0x4d, 0x0c, 0xa3, 0x18, 0xba, 0x99, 0xa3, 0x5d, 0x02, 0xbe, 0xf3, 0xe8, 0x93, 0x34, 0x4b, 0x08, 0x01, 0x8c, 0x23, + 0x68, 0x43, 0x4b, 0x60, 0x00, 0x2a, 0xf6, 0xa8, 0xd4, 0x9b, 0x1e, 0x1f, 0x8d, 0x09, 0xb8, 0xbb, 0x9c, 0x30, 0x14, + 0xc9, 0x47, 0x5b, 0x38, 0x84, 0xd8, 0x2b, 0x25, 0x3d, 0x03, 0x52, 0x5b, 0x38, 0xce, 0x91, 0xb7, 0x14, 0x01, 0xa9, + 0xc0, 0x7e, 0xfb, 0x66, 0xff, 0xc0, 0xf6, 0x8e, 0xb3, 0xf1, 0x45, 0x60, 0x83, 0x33, 0x01, 0x86, 0x87, 0xeb, 0xf3, + 0x29, 0x4b, 0x1d, 0x65, 0xce, 0x67, 0x09, 0xb8, 0x33, 0xd9, 0x89, 0xf8, 0x7e, 0x42, 0x33, 0x98, 0x0e, 0x34, 0xa5, + 0x0f, 0x2c, 0xf6, 0x77, 0xb9, 0xf8, 0xf6, 0x28, 0xcf, 0xf1, 0xb1, 0x8f, 0xe9, 0x04, 0xbb, 0x5b, 0xf0, 0x80, 0x2f, + 0xfb, 0xa8, 0x8a, 0xf4, 0x9b, 0x80, 0xb3, 0x10, 0xef, 0x5b, 0xd8, 0x7e, 0x4b, 0xf5, 0x45, 0x28, 0xfa, 0x92, 0xd1, + 0xb4, 0xc5, 0x5d, 0x99, 0x71, 0x34, 0xf6, 0x18, 0xad, 0x34, 0xb2, 0xb8, 0x81, 0x1a, 0x7c, 0xac, 0x4b, 0x84, 0xe6, + 0x37, 0x8a, 0x68, 0x94, 0x4a, 0x4f, 0xcb, 0x2a, 0x9c, 0x90, 0x2c, 0x3b, 0x31, 0x19, 0xfc, 0x24, 0xf0, 0x8f, 0xcc, + 0x6f, 0x85, 0x89, 0xcf, 0xfa, 0x68, 0x24, 0x0f, 0xff, 0xec, 0x7d, 0x64, 0xde, 0xc5, 0x11, 0xb5, 0x54, 0x8e, 0x29, + 0x46, 0x4c, 0xd0, 0x81, 0x6f, 0xab, 0x08, 0x04, 0x98, 0x26, 0x49, 0x34, 0x2f, 0x58, 0xa0, 0x1e, 0xa4, 0x8f, 0x8a, + 0xae, 0xee, 0x6a, 0x50, 0xc0, 0x34, 0x61, 0x4a, 0x3e, 0x5d, 0x9a, 0x4e, 0xec, 0x03, 0x70, 0x62, 0x31, 0x01, 0xbf, + 0x15, 0x81, 0xe2, 0x4d, 0x83, 0x84, 0x4d, 0x78, 0xc9, 0xf1, 0x86, 0xf7, 0x52, 0x45, 0x0d, 0xfc, 0xee, 0x0e, 0x38, + 0xb6, 0x96, 0x8f, 0xff, 0xdf, 0x34, 0xf6, 0x38, 0x48, 0xc1, 0x11, 0xa5, 0x2b, 0x1f, 0x78, 0x9d, 0x0e, 0x20, 0x32, + 0xdf, 0x97, 0xc6, 0x44, 0x23, 0x86, 0x11, 0x95, 0x92, 0xe7, 0x20, 0xb2, 0x3d, 0x9e, 0x9b, 0xed, 0x40, 0xd4, 0xae, + 0x84, 0x55, 0x56, 0x1d, 0xfb, 0x6d, 0x57, 0x9a, 0xff, 0xab, 0x8d, 0x55, 0x74, 0xa4, 0xfe, 0xb6, 0x42, 0x21, 0x23, + 0x8e, 0x53, 0x0a, 0x2d, 0xb3, 0x14, 0x3d, 0x4c, 0x9c, 0x56, 0x23, 0x3c, 0x37, 0x1a, 0x89, 0x25, 0xed, 0xf8, 0x43, + 0xda, 0xf1, 0x24, 0xc1, 0x86, 0x4b, 0x31, 0xf7, 0x28, 0x4a, 0x46, 0x0e, 0x02, 0x60, 0xb5, 0xac, 0x47, 0x40, 0x4d, + 0x57, 0x45, 0x19, 0xfc, 0x87, 0x48, 0xdc, 0x52, 0xc8, 0xbb, 0x35, 0x54, 0x3a, 0x1a, 0x96, 0x65, 0xef, 0x8c, 0x39, + 0x87, 0xbf, 0xc9, 0x0b, 0x03, 0xe2, 0x9e, 0xa8, 0xfe, 0xd6, 0x5e, 0xbb, 0x74, 0x87, 0xde, 0x5f, 0x8c, 0x0f, 0x98, + 0xd9, 0x8a, 0xa1, 0x6d, 0x0f, 0x96, 0xe1, 0x2f, 0x21, 0xf6, 0x7d, 0xe5, 0xd8, 0x68, 0x55, 0x52, 0xcd, 0x45, 0x8b, + 0xf8, 0xcb, 0xc6, 0x6e, 0x22, 0xdc, 0xfd, 0xfd, 0x55, 0x51, 0x8b, 0x6f, 0x6e, 0x8e, 0x5a, 0xb0, 0x5b, 0x46, 0x2d, + 0xbe, 0xf9, 0x83, 0xa3, 0x16, 0xdf, 0x37, 0xa3, 0x16, 0xbf, 0x7e, 0x4e, 0xd4, 0x22, 0xcf, 0xce, 0x8a, 0xb0, 0x23, + 0x4f, 0xc9, 0x41, 0xe6, 0xfc, 0x6d, 0xc2, 0x17, 0x30, 0x51, 0x23, 0x78, 0x41, 0xd1, 0x0a, 0x91, 0xd8, 0x07, 0x92, + 0x5d, 0xc6, 0x0a, 0xda, 0x3a, 0x83, 0xae, 0x75, 0x5f, 0x5d, 0x19, 0x02, 0x6f, 0xcd, 0xd5, 0x97, 0xdd, 0xba, 0x2a, + 0x9a, 0x10, 0xd0, 0x37, 0x3f, 0x75, 0xc7, 0xee, 0xa6, 0x4a, 0xdd, 0x32, 0x47, 0xe8, 0xa9, 0x88, 0xbc, 0x60, 0x9f, + 0xa5, 0xfd, 0x9f, 0x0e, 0x3b, 0xbd, 0xed, 0xce, 0x0c, 0x7a, 0x83, 0x0e, 0x85, 0xb7, 0x76, 0x6f, 0x7b, 0x1b, 0xdf, + 0xce, 0xd4, 0x5b, 0x17, 0xdf, 0x62, 0xf5, 0xb6, 0x83, 0x6f, 0x23, 0xf5, 0xf6, 0x00, 0xdf, 0xc6, 0xea, 0xed, 0x21, + 0xbe, 0x9d, 0xda, 0xe5, 0x21, 0xd7, 0xc0, 0x3d, 0x04, 0xbe, 0x22, 0x43, 0x3f, 0x50, 0x65, 0xb0, 0x69, 0xf1, 0xaa, + 0x5d, 0x74, 0x12, 0xc4, 0x9e, 0x70, 0x88, 0x82, 0xdc, 0x3b, 0x03, 0xc9, 0x1f, 0x50, 0x66, 0xd9, 0x53, 0xfc, 0xe6, + 0x02, 0xf8, 0x0f, 0x07, 0xf1, 0x8c, 0xa9, 0x8f, 0xcf, 0x2a, 0xac, 0xc1, 0x86, 0x3c, 0x6c, 0x0f, 0xcb, 0x9e, 0x5e, + 0x27, 0x11, 0x2c, 0x51, 0x27, 0xf7, 0xb4, 0x72, 0x55, 0x9d, 0x98, 0xae, 0xa5, 0x57, 0xf8, 0x0a, 0x3d, 0x62, 0xb8, + 0xd0, 0x13, 0xb0, 0x8d, 0x5a, 0xe7, 0xe0, 0x74, 0xad, 0xd5, 0x2d, 0x08, 0x91, 0xd6, 0x26, 0x84, 0x93, 0x7e, 0x3b, + 0x88, 0x4e, 0xf4, 0xf3, 0x2b, 0x30, 0x76, 0xa3, 0x13, 0x76, 0x93, 0x9e, 0x21, 0x10, 0x4d, 0x1d, 0xa3, 0x80, 0x20, + 0x6b, 0x08, 0x96, 0x06, 0x9d, 0x3f, 0xa9, 0x63, 0x90, 0x3a, 0x75, 0xad, 0x43, 0xd3, 0xd7, 0x8b, 0x80, 0xa2, 0x55, + 0xc1, 0x2e, 0xd8, 0xdc, 0x54, 0x2a, 0x28, 0x0c, 0x15, 0x58, 0x70, 0xad, 0x2a, 0xd2, 0xfe, 0xf1, 0x95, 0x0a, 0xc9, + 0x52, 0xba, 0xc8, 0x8c, 0xe4, 0xeb, 0x30, 0xfe, 0xaa, 0x78, 0xfc, 0xa2, 0x33, 0xc2, 0x3f, 0x52, 0xf8, 0x7e, 0x31, + 0x99, 0x4c, 0xae, 0xd5, 0x4d, 0x5f, 0x8c, 0x27, 0xac, 0xcb, 0x76, 0x7a, 0x18, 0xe5, 0x6d, 0x49, 0x71, 0xd8, 0x29, + 0x89, 0x76, 0xcb, 0xdb, 0x35, 0x46, 0xc9, 0x09, 0xea, 0xea, 0xf6, 0x4a, 0xac, 0x04, 0xaa, 0x2c, 0x41, 0x78, 0x9f, + 0xc4, 0x69, 0xd0, 0x2e, 0xfd, 0x53, 0x29, 0xf5, 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, 0x54, 0xfc, 0xb0, 0x0d, 0x95, 0x82, 0xd1, 0x96, + 0xe8, 0xe0, 0x89, 0xc7, 0x20, 0x5a, 0xf0, 0x0c, 0x6c, 0xab, 0xb2, 0xc7, 0x40, 0x3e, 0x4f, 0xa4, 0x6c, 0x17, 0xdf, + 0x76, 0x45, 0x89, 0xfe, 0xab, 0x29, 0xd1, 0x91, 0x99, 0x49, 0x9a, 0x33, 0xd2, 0x03, 0xcd, 0x6a, 0xe4, 0x2c, 0xaa, + 0xfe, 0x35, 0x64, 0x95, 0xb0, 0x47, 0x69, 0x83, 0x2d, 0x85, 0x8c, 0xff, 0xf6, 0x2a, 0x19, 0xff, 0xdd, 0xcd, 0x32, + 0xfe, 0xf8, 0x76, 0x22, 0xfe, 0xbb, 0x3f, 0x58, 0xc4, 0x7f, 0x6b, 0x8a, 0x78, 0x21, 0xc4, 0x2e, 0xc0, 0x7a, 0x25, + 0xb3, 0xf5, 0x38, 0x3b, 0x6f, 0xe1, 0x96, 0xc8, 0x6d, 0x92, 0x9e, 0x1b, 0xb7, 0x12, 0xfe, 0x6b, 0x72, 0x7f, 0xd4, + 0x60, 0xc6, 0x87, 0x62, 0x79, 0x76, 0x72, 0x92, 0x30, 0x25, 0xe3, 0x8d, 0x0a, 0xb2, 0x88, 0xdf, 0xa4, 0xa1, 0xfd, + 0x06, 0x9c, 0x53, 0xa3, 0x64, 0x32, 0x81, 0xa2, 0xc9, 0xc4, 0x56, 0xb9, 0xb1, 0x20, 0xcf, 0xa8, 0xd5, 0xeb, 0x5a, + 0x09, 0xb5, 0xfa, 0xfa, 0x6b, 0xb3, 0xcc, 0x2c, 0x90, 0x51, 0x28, 0xd3, 0x98, 0x90, 0x35, 0xe3, 0xb8, 0xc0, 0x3d, + 0x58, 0x7d, 0xd8, 0x16, 0xed, 0x95, 0x19, 0x28, 0x95, 0x78, 0x84, 0x5f, 0x4c, 0x69, 0x7e, 0x44, 0x44, 0xe4, 0x31, + 0xaf, 0x22, 0x57, 0x9d, 0x75, 0x1a, 0xdf, 0xab, 0xab, 0xce, 0x37, 0x61, 0xf1, 0x65, 0x2e, 0xc3, 0xe3, 0x8b, 0x17, + 0x63, 0xe7, 0x02, 0xec, 0xd8, 0xb8, 0x78, 0x93, 0x36, 0x72, 0xc4, 0x04, 0xd8, 0x61, 0x68, 0x62, 0x5a, 0x0a, 0x82, + 0x55, 0xc9, 0xf2, 0x55, 0x65, 0xcf, 0xe8, 0x24, 0x53, 0x89, 0x70, 0xc8, 0x41, 0x8d, 0x2c, 0x81, 0x39, 0x98, 0xd4, + 0x85, 0xf4, 0xe1, 0x72, 0x91, 0x60, 0x71, 0x2a, 0xbf, 0x70, 0x4d, 0x91, 0xff, 0xa5, 0xd4, 0x1f, 0xf2, 0xe8, 0xbd, + 0xea, 0x89, 0xc1, 0x76, 0x31, 0xc3, 0xb8, 0x54, 0x01, 0x76, 0x20, 0xdc, 0x1c, 0x3f, 0xc7, 0x23, 0x86, 0x50, 0x71, + 0xec, 0x8a, 0x7a, 0xf8, 0xf9, 0x93, 0xea, 0xab, 0x90, 0xb5, 0x2f, 0x08, 0x36, 0x78, 0x00, 0xbf, 0xec, 0xcf, 0x51, + 0x1b, 0x64, 0x0b, 0xee, 0x38, 0xd4, 0xca, 0x71, 0x4b, 0xaf, 0xbb, 0xd3, 0x06, 0x15, 0xe3, 0x8b, 0x6f, 0xfe, 0x38, + 0xba, 0xb3, 0xc4, 0xf7, 0xaa, 0xb0, 0xf9, 0xca, 0x37, 0xb8, 0x34, 0x89, 0xf1, 0x23, 0x21, 0x02, 0x51, 0xe3, 0x9e, + 0x88, 0x5a, 0xc4, 0xe6, 0xbb, 0xaf, 0xdc, 0x37, 0x83, 0xb0, 0xee, 0x3a, 0x0e, 0x96, 0x31, 0xb2, 0x7a, 0x21, 0xb6, + 0x15, 0x56, 0xcd, 0x2a, 0x38, 0x37, 0xe8, 0xcc, 0xe2, 0xcc, 0x88, 0x39, 0xd7, 0xb6, 0x41, 0xa9, 0x82, 0xce, 0x22, + 0x72, 0x7c, 0x81, 0xf1, 0x51, 0xe1, 0xfb, 0x2a, 0xa0, 0xeb, 0x5e, 0xa7, 0x01, 0x39, 0xfa, 0xa3, 0x9a, 0xd1, 0x55, + 0x95, 0x2a, 0x28, 0xcd, 0x13, 0x02, 0x03, 0x19, 0x0a, 0xfe, 0xc2, 0x1a, 0xa7, 0x42, 0x6f, 0xc1, 0x34, 0x24, 0x80, + 0xb5, 0x47, 0x86, 0x6e, 0x89, 0xad, 0xc0, 0x16, 0xd2, 0x02, 0x94, 0x1e, 0x76, 0xe8, 0x5b, 0x35, 0xd0, 0xd3, 0xd5, + 0x98, 0xf1, 0x75, 0x4e, 0xda, 0xc5, 0x91, 0x5f, 0x9c, 0x79, 0xf0, 0xcf, 0xfa, 0x72, 0x09, 0x52, 0xfe, 0xf8, 0x53, + 0xcc, 0xc1, 0xa6, 0x9e, 0xb7, 0x30, 0x02, 0x42, 0x21, 0x4c, 0xa9, 0x0e, 0xe9, 0xd8, 0x51, 0x5c, 0x0f, 0xea, 0x2d, + 0x0a, 0xf4, 0xe5, 0xc8, 0x69, 0x09, 0xd2, 0x2c, 0x65, 0xbd, 0xfa, 0xf1, 0xb2, 0xe9, 0x37, 0x28, 0x62, 0x0d, 0x97, + 0x19, 0xfa, 0x7e, 0xfc, 0x02, 0x7c, 0x3f, 0xa1, 0x46, 0xdb, 0xca, 0x69, 0x68, 0xaf, 0x6d, 0x1f, 0x48, 0xda, 0x6e, + 0x92, 0xb5, 0x90, 0xaf, 0x3a, 0x47, 0x57, 0x39, 0x37, 0x37, 0x1d, 0xb6, 0x76, 0x77, 0x76, 0x3c, 0xf5, 0xcf, 0x38, + 0xa5, 0x6e, 0x16, 0xd3, 0x61, 0xeb, 0x6d, 0x20, 0x0b, 0xa2, 0x09, 0x7e, 0x4d, 0xef, 0x36, 0x2d, 0x8f, 0x29, 0xd3, + 0x71, 0x89, 0x6a, 0x3d, 0xe8, 0x3c, 0x02, 0x6f, 0xed, 0xd6, 0xc3, 0x5f, 0x8f, 0x7e, 0x29, 0x69, 0xa4, 0x2e, 0xac, + 0xda, 0x76, 0x0f, 0xe5, 0x45, 0x12, 0x5d, 0x80, 0xd3, 0x48, 0x36, 0xc6, 0x31, 0x06, 0x70, 0x7b, 0xf3, 0x4c, 0x66, + 0x0d, 0xe4, 0x2c, 0xa1, 0x5f, 0x59, 0x21, 0x97, 0x62, 0xfb, 0xc1, 0xfc, 0x5c, 0xad, 0x46, 0xa7, 0x91, 0x0d, 0xf0, + 0x87, 0x1e, 0xfa, 0x5f, 0x9d, 0x65, 0x50, 0x3f, 0xb8, 0xde, 0x01, 0x18, 0x84, 0x61, 0xd3, 0xca, 0x05, 0x54, 0x6d, + 0x28, 0x31, 0xd2, 0x1e, 0xaa, 0x81, 0x2c, 0x7f, 0x1b, 0x54, 0x65, 0x54, 0xb0, 0x1e, 0x7e, 0xd6, 0x30, 0x06, 0xd7, + 0x54, 0x1a, 0x4f, 0xb3, 0x78, 0x3c, 0x4e, 0x58, 0x4f, 0xd9, 0x47, 0x56, 0xe7, 0x01, 0x66, 0x0d, 0x98, 0x4b, 0x56, + 0x5f, 0x15, 0x83, 0x78, 0x9a, 0x4e, 0xd1, 0x31, 0xd8, 0x6b, 0xf8, 0x6d, 0xc2, 0xb5, 0xe4, 0x94, 0xc7, 0xe9, 0xed, + 0x8a, 0x78, 0xf4, 0x5c, 0xc7, 0x65, 0x07, 0x8c, 0x45, 0x5a, 0xf0, 0x76, 0x8f, 0x67, 0xf3, 0xa0, 0xb5, 0x5d, 0x47, + 0x04, 0xab, 0x34, 0x0a, 0xde, 0x1a, 0xb4, 0x3c, 0xb4, 0x0e, 0x84, 0x96, 0xb3, 0xfc, 0x8e, 0x2c, 0xa3, 0x01, 0xf0, + 0xfb, 0x77, 0xba, 0xa8, 0xac, 0x23, 0xf3, 0xff, 0x67, 0xb7, 0x7c, 0xb5, 0x7e, 0xb7, 0x7c, 0xa5, 0x76, 0xcb, 0xf5, + 0x1c, 0xfb, 0xc5, 0xa4, 0x83, 0x7f, 0x7a, 0x15, 0x42, 0xb0, 0x2a, 0x40, 0x0e, 0x0b, 0xed, 0xe2, 0x56, 0x17, 0xfe, + 0xa3, 0xa1, 0xdb, 0x1e, 0xfe, 0xf1, 0xc1, 0x02, 0x6c, 0x5b, 0x58, 0x88, 0xff, 0xda, 0xb5, 0xaa, 0xce, 0x7d, 0xac, + 0xc3, 0x5e, 0x3b, 0xab, 0x75, 0xdd, 0xeb, 0x37, 0x2d, 0xc8, 0x2b, 0xee, 0x04, 0x4a, 0x18, 0x83, 0xab, 0x16, 0x1d, + 0x1f, 0x43, 0xe9, 0x24, 0x1b, 0x2d, 0x8a, 0xbf, 0x97, 0xf0, 0x4b, 0x22, 0x5e, 0xbb, 0xa5, 0x1b, 0xe3, 0xa8, 0xae, + 0x22, 0x05, 0x45, 0x8d, 0xb0, 0xd4, 0xeb, 0x14, 0x14, 0xc0, 0x98, 0xcc, 0xe9, 0xfa, 0xf7, 0xd7, 0x6c, 0x82, 0xbf, + 0xc9, 0xda, 0xac, 0x45, 0xe6, 0xdf, 0x4b, 0x8c, 0x6b, 0x89, 0xf0, 0x59, 0x34, 0x30, 0xd7, 0xb0, 0xfd, 0x68, 0x3d, + 0xb8, 0x87, 0x6a, 0xa6, 0xa1, 0x52, 0x0a, 0x52, 0xef, 0x80, 0x17, 0x10, 0x2d, 0x12, 0x7e, 0xfd, 0xa8, 0x57, 0x71, + 0xc6, 0xca, 0xa8, 0xd7, 0x08, 0xf4, 0xaa, 0xed, 0x2d, 0xa5, 0xf4, 0x17, 0x5f, 0xdd, 0xc7, 0x3f, 0x22, 0xf0, 0x75, + 0x5c, 0xf9, 0x46, 0x22, 0x36, 0x80, 0xbe, 0xd1, 0x46, 0xcd, 0xf9, 0x11, 0x1a, 0x9c, 0xfc, 0x9f, 0xdb, 0xb6, 0x46, + 0x63, 0xfd, 0x56, 0xcd, 0xa5, 0x55, 0xfa, 0x59, 0xad, 0x3f, 0x6f, 0xf0, 0x5b, 0xb6, 0x1d, 0x09, 0x87, 0xa0, 0xde, + 0x56, 0xfe, 0xfa, 0x90, 0x95, 0xc6, 0x8a, 0xe2, 0xb7, 0x6d, 0x5f, 0x99, 0xc4, 0xd4, 0x63, 0x23, 0x3c, 0xd6, 0x4e, + 0xa4, 0x3c, 0x6f, 0xc6, 0x1e, 0xc2, 0x8f, 0xfc, 0x91, 0x85, 0xf7, 0xf0, 0xcb, 0x5b, 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, 0x3f, 0x98, 0x64, 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, 0x84, 0x74, 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, 0x04, 0x6e, 0xc1, 0xd4, 0x82, 0xe7, 0x57, 0xdd, 0x87, 0xa0, 0xe3, 0x4e, 0x5b, 0xf7, 0x47, 0xed, 0x56, + 0xc7, 0xea, 0xb4, 0xba, 0xfe, 0x43, 0xab, 0x2b, 0xfe, 0x07, 0x19, 0xb9, 0x6d, 0x75, 0xe0, 0x69, 0xdb, 0x82, 0xf7, + 0xd3, 0xfb, 0x22, 0x1d, 0x22, 0xb2, 0xb7, 0xfa, 0xbb, 0xf8, 0xfb, 0x83, 0x00, 0xa9, 0xaf, 0x6c, 0xf1, 0x1b, 0xcf, + 0xec, 0x2f, 0xcc, 0xd2, 0xce, 0xa3, 0xb5, 0xc5, 0xdd, 0x87, 0x6b, 0x8b, 0xb7, 0x1f, 0xac, 0x2d, 0xbe, 0xbf, 0x53, + 0x2f, 0xde, 0x3a, 0x11, 0x55, 0x5a, 0x2e, 0x84, 0xf6, 0x2c, 0x02, 0x46, 0x39, 0x77, 0x3a, 0x00, 0x67, 0xdb, 0x6a, + 0xe1, 0x8f, 0x87, 0x5d, 0x57, 0xf7, 0x3a, 0xc6, 0x5e, 0x1a, 0xcb, 0x87, 0x8f, 0x00, 0xcb, 0xe7, 0xdd, 0x07, 0x23, + 0x6c, 0x47, 0x88, 0xc2, 0xbf, 0xd3, 0xed, 0x47, 0x23, 0xd0, 0x08, 0x16, 0xfe, 0x83, 0x3f, 0xd3, 0x9d, 0xee, 0x48, + 0xbc, 0xb4, 0xb1, 0xfe, 0x43, 0xe7, 0x61, 0x01, 0x4d, 0xf1, 0xcf, 0x6f, 0xda, 0x84, 0x46, 0x03, 0xde, 0x1c, 0xf7, + 0x3e, 0xd0, 0xe8, 0xd1, 0xb4, 0xeb, 0x7f, 0x75, 0xfa, 0xd0, 0x7f, 0x34, 0xed, 0x3c, 0xfc, 0x20, 0xde, 0x12, 0xa0, + 0xe0, 0x57, 0xf8, 0xef, 0xc3, 0x76, 0x7b, 0xda, 0xea, 0xf8, 0x8f, 0x4e, 0xb7, 0xfd, 0xed, 0xa4, 0xf5, 0xc0, 0x7f, + 0x84, 0xff, 0xaa, 0xe1, 0xa6, 0xd9, 0x8c, 0xd9, 0x16, 0xae, 0x77, 0xc3, 0xef, 0x35, 0xe7, 0xe8, 0xde, 0xb7, 0x76, + 0xee, 0x3f, 0x7f, 0x04, 0x6b, 0x34, 0xed, 0x74, 0xe1, 0xff, 0xab, 0x1e, 0x3f, 0x20, 0xe1, 0xe5, 0xc0, 0x11, 0xc3, + 0x54, 0x52, 0x45, 0x38, 0xfa, 0x78, 0xd7, 0x3d, 0xef, 0x87, 0xab, 0x02, 0x20, 0x7f, 0xbe, 0x39, 0x00, 0xf2, 0x97, + 0x5b, 0x06, 0xb9, 0xff, 0xfc, 0x07, 0x47, 0x40, 0x7e, 0x68, 0x06, 0xb9, 0xf7, 0xd8, 0x4a, 0xa0, 0xa3, 0xe9, 0xac, + 0x3d, 0x67, 0xce, 0xe1, 0x8f, 0x6c, 0x88, 0x69, 0xd3, 0xd0, 0xfa, 0x2f, 0xb5, 0x78, 0x50, 0x86, 0x1b, 0x79, 0x8f, + 0x89, 0x9d, 0xcc, 0xf8, 0x15, 0x04, 0xe1, 0xfc, 0x46, 0x82, 0xbc, 0xb8, 0x1d, 0x3d, 0x38, 0xff, 0x63, 0xe9, 0x41, + 0x5f, 0xee, 0x57, 0xf4, 0xa8, 0x45, 0xdc, 0x29, 0x62, 0x40, 0x8e, 0xfe, 0x3e, 0xbd, 0x3b, 0xf6, 0x16, 0xc3, 0xb7, + 0xc2, 0x16, 0xb9, 0x80, 0xef, 0x3e, 0xe7, 0x74, 0x40, 0x64, 0x15, 0x87, 0xb6, 0x0c, 0xc0, 0xcc, 0xf1, 0xdb, 0xb4, + 0xea, 0xe5, 0x54, 0xdc, 0x5c, 0x09, 0xe9, 0xda, 0xd9, 0x8e, 0x0e, 0xde, 0x60, 0xa2, 0x77, 0xb8, 0xcc, 0x78, 0x84, + 0xbf, 0xfc, 0x88, 0xc7, 0x3c, 0xc1, 0x4b, 0xb1, 0xf2, 0x02, 0x19, 0xe6, 0x25, 0x7f, 0x87, 0x39, 0xd5, 0xea, 0x90, + 0x60, 0x86, 0x01, 0x83, 0x57, 0x6c, 0x1c, 0x47, 0x8e, 0xed, 0xcc, 0x61, 0xc7, 0xc2, 0x98, 0xad, 0x5a, 0x42, 0x33, + 0xe5, 0x32, 0xbb, 0xb6, 0xfa, 0x7d, 0x3b, 0x39, 0x7e, 0xbf, 0x2c, 0x3c, 0x94, 0x01, 0x46, 0x5b, 0x7a, 0x00, 0x30, + 0xbe, 0x2a, 0xc9, 0x51, 0xd8, 0x57, 0x56, 0x83, 0x2d, 0xcc, 0x86, 0x8e, 0xdf, 0x05, 0x37, 0x82, 0x8a, 0xf1, 0x7b, + 0x50, 0x3f, 0x38, 0xad, 0x6d, 0x30, 0x6b, 0x8c, 0x6e, 0x7a, 0xa0, 0xe1, 0x4a, 0x18, 0x49, 0x04, 0x07, 0x1a, 0xa5, + 0x9e, 0xfe, 0x05, 0x64, 0x55, 0xb8, 0xa8, 0x78, 0x7c, 0x71, 0x20, 0xef, 0x7c, 0xdb, 0x18, 0xb9, 0xa5, 0x88, 0x7d, + 0xf5, 0xbd, 0xa9, 0x4d, 0x50, 0x17, 0xf4, 0x5b, 0x20, 0xe9, 0xdc, 0x1b, 0x35, 0x02, 0xa6, 0x5c, 0x5b, 0xd2, 0x73, + 0x08, 0x6d, 0xa1, 0x0f, 0xc6, 0xec, 0x34, 0x1e, 0x49, 0xb1, 0xee, 0x59, 0xf2, 0xaa, 0x48, 0x8b, 0xb0, 0x08, 0x3b, + 0x9e, 0xf0, 0x9d, 0xe1, 0x05, 0xb5, 0x5a, 0x98, 0x66, 0x76, 0xff, 0x5e, 0x4f, 0x43, 0x52, 0xcf, 0x56, 0xb7, 0xf1, + 0x57, 0x52, 0x1e, 0x82, 0xaf, 0xf6, 0xf7, 0xe1, 0x3d, 0xfc, 0xa5, 0x94, 0xf7, 0x86, 0xb6, 0xeb, 0x93, 0x50, 0xbc, + 0x57, 0xfd, 0x66, 0x4a, 0x94, 0x08, 0x9b, 0xa0, 0xbf, 0xbc, 0xdb, 0x2a, 0x32, 0xa9, 0xb4, 0xba, 0x3b, 0x95, 0xd2, + 0x82, 0x67, 0x43, 0x4a, 0x81, 0x00, 0xed, 0xfa, 0x3b, 0x86, 0x28, 0x3c, 0x6d, 0xe1, 0xcf, 0x9a, 0x30, 0xbc, 0x0f, + 0x0d, 0x94, 0x34, 0x7c, 0x09, 0xcd, 0xb7, 0x85, 0xe0, 0x85, 0x7e, 0x3f, 0x92, 0xa8, 0x12, 0x62, 0xaa, 0xce, 0x31, + 0x6b, 0x0e, 0x91, 0x44, 0x8e, 0x80, 0xed, 0x19, 0xf1, 0x26, 0xc1, 0xae, 0x32, 0x9a, 0xf2, 0x14, 0xfa, 0x3a, 0xfa, + 0x33, 0xce, 0xeb, 0xea, 0xbc, 0xda, 0xce, 0x59, 0x33, 0x05, 0x32, 0x7c, 0xe3, 0xa0, 0x8a, 0xae, 0x2e, 0x88, 0xcf, + 0x99, 0x89, 0x6d, 0x5c, 0x7d, 0xf0, 0x6d, 0x4d, 0xf6, 0xad, 0xb9, 0x29, 0x58, 0xc5, 0x34, 0xb4, 0x2f, 0x30, 0x65, + 0x06, 0x7f, 0x56, 0xc5, 0xea, 0x41, 0x32, 0x94, 0x9f, 0x44, 0xf8, 0xdb, 0x58, 0xe8, 0x47, 0x59, 0x6d, 0x40, 0x4e, + 0xdf, 0xab, 0x24, 0x48, 0x5f, 0x8c, 0xcb, 0x26, 0x12, 0x60, 0x2f, 0xe0, 0x2f, 0xf7, 0xab, 0xae, 0x4a, 0xc8, 0x3b, + 0x90, 0x98, 0x53, 0x30, 0x8e, 0x73, 0xba, 0x5a, 0xab, 0xf0, 0xaf, 0x45, 0x34, 0x2b, 0x52, 0xd3, 0xae, 0x64, 0xc5, + 0xc0, 0xc6, 0x22, 0x3b, 0x90, 0xc9, 0x68, 0xe6, 0x07, 0x9b, 0xcd, 0xbb, 0x8f, 0x63, 0x91, 0x87, 0x86, 0x1f, 0xb4, + 0xb7, 0x05, 0x91, 0x6d, 0x10, 0x63, 0x57, 0xe2, 0x44, 0xc6, 0x0d, 0x5e, 0x19, 0xac, 0x7e, 0x43, 0x91, 0xb9, 0xe1, + 0x6d, 0x73, 0xb5, 0xf4, 0xb8, 0xb4, 0x0e, 0xae, 0x8c, 0xdf, 0x1d, 0xb3, 0x88, 0xfb, 0x51, 0x4a, 0xb9, 0x49, 0x8e, + 0x21, 0x16, 0xbc, 0x0e, 0xdb, 0x76, 0x4b, 0x90, 0x3c, 0xc6, 0xaf, 0x70, 0x12, 0xa4, 0xf7, 0xa1, 0xb0, 0x4a, 0xd8, + 0xda, 0x9d, 0x76, 0xfb, 0x6f, 0x0e, 0xf6, 0x2c, 0xb1, 0x9b, 0x77, 0xb7, 0xe0, 0x75, 0x97, 0xdc, 0x61, 0x91, 0x9f, + 0x11, 0x8a, 0xfc, 0x0c, 0x4b, 0x24, 0x74, 0x85, 0xf6, 0x96, 0x40, 0xd3, 0xb6, 0x58, 0x3a, 0x12, 0x31, 0xbc, 0x19, + 0xb8, 0x0b, 0x31, 0x7e, 0xd4, 0x6b, 0x0b, 0xbb, 0xb5, 0x70, 0xa5, 0x6d, 0x95, 0xe1, 0xa2, 0x0c, 0x04, 0x9e, 0xaa, + 0x88, 0x1f, 0xa8, 0x75, 0xa6, 0x92, 0x5d, 0xe4, 0x50, 0x3a, 0x27, 0x75, 0xb5, 0x75, 0xb1, 0x38, 0x9e, 0x81, 0x1c, + 0x52, 0x09, 0x2a, 0xef, 0x65, 0x87, 0x5d, 0x9a, 0x0a, 0x93, 0x62, 0x57, 0x23, 0x92, 0xd3, 0x4e, 0x7f, 0x37, 0x92, + 0xf6, 0x0e, 0xee, 0xdd, 0x02, 0x36, 0x2f, 0xa8, 0x39, 0x34, 0x2a, 0xfc, 0x38, 0xdb, 0x3a, 0x63, 0xc7, 0xad, 0x68, + 0x1e, 0x57, 0xe1, 0x3f, 0xd4, 0x7e, 0xfd, 0x5d, 0xa5, 0x08, 0x65, 0x9a, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, + 0x1c, 0x31, 0x68, 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, + 0xb4, 0xfb, 0x7f, 0xfd, 0x5f, 0xff, 0x5b, 0x86, 0x15, 0x81, 0xac, 0x00, 0x16, 0xa6, 0xc1, 0x54, 0x27, 0x8c, 0xec, + 0x1c, 0x1c, 0xd1, 0x78, 0xdc, 0x9a, 0x46, 0xc9, 0x04, 0x20, 0x28, 0x98, 0xb8, 0xca, 0x24, 0xeb, 0x81, 0x0b, 0x24, + 0x58, 0xe6, 0xe1, 0xbc, 0x04, 0xaf, 0x5e, 0x84, 0x2b, 0xf6, 0xbb, 0xf2, 0x56, 0x55, 0xbe, 0x30, 0x31, 0xb4, 0x91, + 0xc5, 0x6a, 0xf0, 0x5c, 0x2d, 0x93, 0x55, 0xfd, 0x82, 0x24, 0x29, 0x3c, 0x58, 0x2d, 0x8d, 0x15, 0x5a, 0xea, 0x83, + 0x90, 0x7f, 0xfb, 0xe7, 0xff, 0xfc, 0xdf, 0xd5, 0x2b, 0x9e, 0x6f, 0xfc, 0xf5, 0x9f, 0xfe, 0xe1, 0xff, 0xfe, 0x9f, + 0xff, 0x82, 0x59, 0xc2, 0xf2, 0x0c, 0x84, 0xb6, 0x92, 0x55, 0x1d, 0x80, 0x88, 0x3d, 0x65, 0x55, 0x0e, 0x47, 0x3d, + 0xdd, 0x75, 0x9f, 0x26, 0x24, 0xde, 0x94, 0xd0, 0x11, 0x5f, 0x53, 0x7a, 0x34, 0x51, 0xed, 0x1a, 0xf2, 0xc1, 0x52, + 0x5a, 0x74, 0xac, 0x6f, 0xef, 0xb4, 0xed, 0x6a, 0x79, 0xfb, 0x46, 0xdf, 0x2d, 0x5c, 0x98, 0x5b, 0x65, 0xe0, 0xf8, + 0x7a, 0xd9, 0x96, 0x2a, 0x8c, 0x85, 0x25, 0x65, 0x55, 0x6e, 0x61, 0x7c, 0x79, 0x89, 0xaf, 0x41, 0xd7, 0x28, 0xa6, + 0x55, 0xae, 0xf5, 0xe9, 0xfd, 0xb2, 0x00, 0x44, 0x27, 0xb8, 0x34, 0x22, 0x58, 0x46, 0x67, 0xa7, 0x2d, 0xb4, 0x4e, + 0x92, 0x8b, 0x92, 0x46, 0x11, 0xde, 0xcc, 0xfd, 0x47, 0x7f, 0x57, 0xfe, 0x69, 0x86, 0x56, 0x81, 0xe5, 0xcc, 0xa2, + 0x73, 0xe9, 0xe3, 0x3c, 0x68, 0xb7, 0xe7, 0xe7, 0xee, 0xb2, 0x9a, 0xc1, 0xbb, 0x6a, 0x32, 0x0a, 0xb0, 0x99, 0x03, + 0xd2, 0xa1, 0xab, 0x8e, 0xe5, 0x81, 0x59, 0xdf, 0xc6, 0xd0, 0x4f, 0x59, 0x7e, 0xb9, 0xa4, 0x70, 0x52, 0xfc, 0x1b, + 0x1e, 0x8e, 0xca, 0xc8, 0x1b, 0x94, 0x18, 0x58, 0x2c, 0x8d, 0x5e, 0x5d, 0xd1, 0x6b, 0xda, 0x59, 0xcd, 0x4d, 0x31, + 0x0f, 0x77, 0xcd, 0x63, 0xd9, 0xfb, 0x78, 0xd0, 0x3a, 0xed, 0x78, 0xd3, 0xee, 0x52, 0x0f, 0xcf, 0x79, 0x36, 0x33, + 0x4f, 0x73, 0x59, 0xc4, 0x46, 0x6c, 0xa2, 0x22, 0x96, 0xb2, 0x5e, 0x9c, 0xd4, 0x96, 0x5f, 0xe0, 0x76, 0x03, 0xda, + 0x66, 0x11, 0x0f, 0x88, 0x69, 0x7b, 0xe6, 0x79, 0x6f, 0x84, 0x27, 0xe9, 0xd9, 0xd2, 0x98, 0xab, 0x27, 0x9a, 0x62, + 0x5c, 0xb0, 0x9e, 0xf7, 0x53, 0xfa, 0xd4, 0xdd, 0x1c, 0x4a, 0x84, 0x15, 0x5e, 0xc8, 0x63, 0xd4, 0x77, 0x35, 0x7f, + 0x5c, 0x8a, 0x62, 0x70, 0x81, 0xd7, 0xd6, 0x0b, 0xb5, 0x28, 0x6a, 0x5f, 0x80, 0xb5, 0x43, 0x60, 0xda, 0xcd, 0x56, + 0x54, 0x88, 0xad, 0xde, 0x85, 0x2f, 0xb4, 0xed, 0x1d, 0xcd, 0xe7, 0xd4, 0xd0, 0x05, 0x6e, 0x24, 0x1b, 0x1a, 0x25, + 0x05, 0xa5, 0x08, 0x88, 0x13, 0x79, 0xd9, 0x46, 0xb2, 0xad, 0x78, 0x92, 0x67, 0xf5, 0xf4, 0xfb, 0xb6, 0xff, 0x1f, + 0x22, 0x28, 0x4d, 0x5d, 0x85, 0x7b, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 5c06a97d47..30ac959e43 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -360,9 +360,14 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM } std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) { return json::build_json([obj, value, start_config](JsonObject root) { - std::string state = value_accuracy_to_string(value, obj->get_accuracy_decimals()); - if (!obj->get_unit_of_measurement().empty()) - state += " " + obj->get_unit_of_measurement(); + std::string state; + if (isnan(value)) { + state = "NA"; + } else { + state = value_accuracy_to_string(value, obj->get_accuracy_decimals()); + if (!obj->get_unit_of_measurement().empty()) + state += " " + obj->get_unit_of_measurement(); + } set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); }); } @@ -719,12 +724,15 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["step"] = obj->traits.get_step(); root["mode"] = (int) obj->traits.get_mode(); } - std::string state = str_sprintf("%f", value); - root["state"] = state; if (isnan(value)) { root["value"] = "\"NaN\""; + root["state"] = "NA"; } else { root["value"] = value; + std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); + if (!obj->traits.get_unit_of_measurement().empty()) + state += " " + obj->traits.get_unit_of_measurement(); + root["state"] = state; } }); } diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index b03d890ad8..d82e452c3d 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -258,6 +258,19 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { return std::string(tmp); } +int8_t step_to_accuracy_decimals(float step) { + // use printf %g to find number of digits based on temperature step + char buf[32]; + sprintf(buf, "%.5g", step); + + std::string str{buf}; + size_t dot_pos = str.find('.'); + if (dot_pos == std::string::npos) + return 0; + + return str.length() - dot_pos - 1; +} + // Colors float gamma_correct(float value, float gamma) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index dab829ab93..6bed743010 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -415,6 +415,9 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch /// Create a string from a value and an accuracy in decimals. std::string value_accuracy_to_string(float value, int8_t accuracy_decimals); +/// Derive accuracy in decimals from an increment step. +int8_t step_to_accuracy_decimals(float step); + ///@} /// @name Colors From fb8846bb455c4d8a8b84776ebb71e43eeb359c99 Mon Sep 17 00:00:00 2001 From: buxtronix Date: Mon, 15 Aug 2022 13:06:05 +1000 Subject: [PATCH 20/69] Only trigger ble_client on_connect after discovering services (#3710) --- esphome/components/ble_client/automation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 12c22345b4..828cbca662 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -15,10 +15,10 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode { void loop() 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 { - if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK) - this->trigger(); - if (event == ESP_GATTC_SEARCH_CMPL_EVT) + if (event == ESP_GATTC_SEARCH_CMPL_EVT) { this->node_state = espbt::ClientState::ESTABLISHED; + this->trigger(); + } } }; From 8cfcd5904c7a3b88310227ce8947f0117627868c Mon Sep 17 00:00:00 2001 From: rbaron Date: Mon, 15 Aug 2022 05:09:33 +0200 Subject: [PATCH 21/69] Fixes BLE remote address type when connecting (#3702) --- esphome/components/ble_client/ble_client.cpp | 3 ++- esphome/components/ble_client/ble_client.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index d06c3c4cad..5f58d8273f 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -54,6 +54,7 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { this->remote_bda[3] = (addr >> 16) & 0xFF; this->remote_bda[4] = (addr >> 8) & 0xFF; this->remote_bda[5] = (addr >> 0) & 0xFF; + this->remote_addr_type = device.get_address_type(); return true; } @@ -83,7 +84,7 @@ void BLEClient::set_enabled(bool enabled) { void BLEClient::connect() { ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str()); - auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true); + auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true); if (ret) { ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); this->set_states_(espbt::ClientState::IDLE); diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index b122bfd11e..5ed8f219d1 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -115,6 +115,7 @@ class BLEClient : public espbt::ESPBTClient, public Component { int gattc_if; esp_bd_addr_t remote_bda; + esp_ble_addr_type_t remote_addr_type; uint16_t conn_id; uint64_t address; bool enabled; From d07a6704d53c79eaf6bbe40eeab920e21f226ebc Mon Sep 17 00:00:00 2001 From: rbaron Date: Tue, 16 Aug 2022 04:40:58 +0200 Subject: [PATCH 22/69] Makes ble_client.ble_write's action value templatable (#3715) --- esphome/components/ble_client/__init__.py | 10 +++++-- esphome/components/ble_client/automation.cpp | 9 +++--- esphome/components/ble_client/automation.h | 30 ++++++++++++++++---- tests/test2.yaml | 6 ++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 6c13e7fcf5..4b7d5f5b8a 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -82,7 +82,7 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( cv.Required(CONF_ID): cv.use_id(BLEClient), cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, - cv.Required(CONF_VALUE): cv.ensure_list(cv.hex_uint8_t), + cv.Required(CONF_VALUE): cv.templatable(cv.ensure_list(cv.hex_uint8_t)), } ) @@ -93,8 +93,14 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( async def ble_write_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) + value = config[CONF_VALUE] - cg.add(var.set_value(value)) + if cg.is_template(value): + templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_value_template(templ)) + else: + cg.add(var.set_value_simple(value)) + serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(serv_uuid128)) char_uuid128 = esp32_ble_tracker.as_reversed_hex_array( diff --git a/esphome/components/ble_client/automation.cpp b/esphome/components/ble_client/automation.cpp index 8d5fe96570..6918ab31b4 100644 --- a/esphome/components/ble_client/automation.cpp +++ b/esphome/components/ble_client/automation.cpp @@ -10,7 +10,7 @@ namespace esphome { namespace ble_client { static const char *const TAG = "ble_client.automation"; -void BLEWriterClientNode::write() { +void BLEWriterClientNode::write(const std::vector &value) { if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected"); return; @@ -29,9 +29,10 @@ void BLEWriterClientNode::write() { ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str()); return; } - ESP_LOGVV(TAG, "Will write %d bytes: %s", this->value_.size(), format_hex_pretty(this->value_).c_str()); - esp_err_t err = esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, - value_.size(), value_.data(), write_type, ESP_GATT_AUTH_REQ_NONE); + ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str()); + esp_err_t err = + esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(), + const_cast(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE); if (err != ESP_OK) { ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err)); } diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 828cbca662..38e64ebd76 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -42,10 +42,8 @@ class BLEWriterClientNode : public BLEClientNode { ble_client_ = ble_client; } - void set_value(std::vector value) { value_ = std::move(value); } - - // Attempts to write the contents of value_ to char_uuid_. - void write(); + // Attempts to write the contents of value to char_uuid_. + void write(const std::vector &value); void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } @@ -60,14 +58,34 @@ class BLEWriterClientNode : public BLEClientNode { esp_gatt_char_prop_t char_props_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; - std::vector value_; }; template class BLEClientWriteAction : public Action, public BLEWriterClientNode { public: BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {} - void play(Ts... x) override { return write(); } + void play(Ts... x) override { + if (has_simple_value_) { + return write(this->value_simple_); + } else { + return write(this->value_template_(x...)); + } + } + + void set_value_template(std::function(Ts...)> func) { + this->value_template_ = std::move(func); + has_simple_value_ = false; + } + + void set_value_simple(const std::vector &value) { + this->value_simple_ = value; + has_simple_value_ = true; + } + + private: + bool has_simple_value_ = true; + std::vector value_simple_; + std::function(Ts...)> value_template_{}; }; } // namespace ble_client diff --git a/tests/test2.yaml b/tests/test2.yaml index 5dd9e76a2f..110e8e6625 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -615,3 +615,9 @@ switch: service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC value: [0x01, 0xab, 0xff] + - ble_client.ble_write: + id: airthings01 + service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE + characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC + value: !lambda |- + return {0x13, 0x37}; From c943d84036fd77a89d8ba1f3c3a0156aa3ffd59a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:47:48 +1200 Subject: [PATCH 23/69] Bump version to 2022.8.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 59fa466d11..00d155e66f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.0b2" +__version__ = "2022.8.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 3a82f500d4d1a8122aeb8ba4dd1a228a36ed2101 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Aug 2022 12:58:30 +1200 Subject: [PATCH 24/69] Bump version to 2022.8.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 00d155e66f..981ee773d4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.0b3" +__version__ = "2022.8.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 3acc8e7479843998bdc9def5ea0a0c7397906b57 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 16 Aug 2022 20:41:46 -0700 Subject: [PATCH 25/69] fix grow password setting (#3722) Co-authored-by: Samuel Sieb --- esphome/components/fingerprint_grow/fingerprint_grow.cpp | 8 ++++---- esphome/components/fingerprint_grow/fingerprint_grow.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 3b8c52fea2..1d6cb776b7 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -51,7 +51,7 @@ void FingerprintGrowComponent::update() { void FingerprintGrowComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader..."); if (this->check_password_()) { - if (this->new_password_ != nullptr) { + if (this->new_password_ != -1) { if (this->set_password_()) return; } else { @@ -202,9 +202,9 @@ bool FingerprintGrowComponent::check_password_() { } bool FingerprintGrowComponent::set_password_() { - ESP_LOGI(TAG, "Setting new password: %d", *this->new_password_); - this->data_ = {SET_PASSWORD, (uint8_t)(*this->new_password_ >> 24), (uint8_t)(*this->new_password_ >> 16), - (uint8_t)(*this->new_password_ >> 8), (uint8_t)(*this->new_password_ & 0xFF)}; + ESP_LOGI(TAG, "Setting new password: %d", this->new_password_); + this->data_ = {SET_PASSWORD, (uint8_t)(this->new_password_ >> 24), (uint8_t)(this->new_password_ >> 16), + (uint8_t)(this->new_password_ >> 8), (uint8_t)(this->new_password_ & 0xFF)}; if (this->send_command_() == OK) { ESP_LOGI(TAG, "New password successfully set"); ESP_LOGI(TAG, "Define the new password in your configuration and reflash now"); diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 7ec253ff3a..e50418a768 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -96,7 +96,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic } void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; } void set_password(uint32_t password) { this->password_ = password; } - void set_new_password(uint32_t new_password) { this->new_password_ = &new_password; } + void set_new_password(uint32_t new_password) { this->new_password_ = new_password; } void set_fingerprint_count_sensor(sensor::Sensor *fingerprint_count_sensor) { this->fingerprint_count_sensor_ = fingerprint_count_sensor; } @@ -153,7 +153,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF}; uint16_t capacity_ = 64; uint32_t password_ = 0x0; - uint32_t *new_password_{nullptr}; + uint32_t new_password_ = -1; GPIOPin *sensing_pin_{nullptr}; uint8_t enrollment_image_ = 0; uint16_t enrollment_slot_ = 0; From 22eb4f9cb92ee2a22397f187c15d279c46e2552a Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 18 Aug 2022 14:55:10 -0500 Subject: [PATCH 26/69] Fix SPI HW selection for ESP32 variants (#3728) --- esphome/components/spi/spi.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 95280f23b9..864f6ae39d 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -76,7 +76,11 @@ void SPIComponent::setup() { if (spi_bus_num == 0) { this->hw_spi_ = &SPI; } else { +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + this->hw_spi_ = new SPIClass(FSPI); // NOLINT(cppcoreguidelines-owning-memory) +#else this->hw_spi_ = new SPIClass(VSPI); // NOLINT(cppcoreguidelines-owning-memory) +#endif // USE_ESP32_VARIANT } spi_bus_num++; this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); From aa2eb29274a7c3c7be4bf97dfe13e4a070cb81a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:26:52 +1200 Subject: [PATCH 27/69] Bump version to 2022.8.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 981ee773d4..3091f55f12 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.0" +__version__ = "2022.8.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c8eb30ef27934329f54a1fce50444400239acda8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 25 Aug 2022 07:13:44 +1200 Subject: [PATCH 28/69] Initial bluetooth_proxy support (#3736) --- CODEOWNERS | 1 + esphome/components/api/api.proto | 28 ++++ esphome/components/api/api_connection.cpp | 3 + esphome/components/api/api_connection.h | 15 ++ esphome/components/api/api_pb2.cpp | 146 ++++++++++++++++++ esphome/components/api/api_pb2.h | 40 +++++ esphome/components/api/api_pb2_service.cpp | 29 ++++ esphome/components/api/api_pb2_service.h | 7 + esphome/components/api/api_server.cpp | 7 + esphome/components/api/api_server.h | 3 + esphome/components/api/proto.h | 2 +- .../components/bluetooth_proxy/__init__.py | 27 ++++ .../bluetooth_proxy/bluetooth_proxy.cpp | 58 +++++++ .../bluetooth_proxy/bluetooth_proxy.h | 26 ++++ esphome/core/defines.h | 1 + tests/test1.yaml | 2 + 16 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 esphome/components/bluetooth_proxy/__init__.py create mode 100644 esphome/components/bluetooth_proxy/bluetooth_proxy.cpp create mode 100644 esphome/components/bluetooth_proxy/bluetooth_proxy.h diff --git a/CODEOWNERS b/CODEOWNERS index 62aae6d3cb..7f7562dec4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,6 +34,7 @@ esphome/components/binary_sensor/* @esphome/core esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- esphome/components/ble_client/* @buxtronix +esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme680_bsec/* @trvrnrth esphome/components/bmp3xx/* @martgras esphome/components/button/* @esphome/core diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 88a74540d0..c6cde8b038 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -27,6 +27,7 @@ service APIConnection { rpc subscribe_logs (SubscribeLogsRequest) returns (void) {} rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {} rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {} + rpc subscribe_bluetooth_le_advertisements (SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc get_time (GetTimeRequest) returns (GetTimeResponse) { option (needs_authentication) = false; } @@ -190,6 +191,8 @@ message DeviceInfoResponse { string project_version = 9; uint32 webserver_port = 10; + + bool has_bluetooth_proxy = 11; } message ListEntitiesRequest { @@ -1099,3 +1102,28 @@ message MediaPlayerCommandRequest { bool has_media_url = 6; string media_url = 7; } + +// ==================== BLUETOOTH ==================== +message SubscribeBluetoothLEAdvertisementsRequest { + option (id) = 66; + option (source) = SOURCE_CLIENT; +} + +message BluetoothServiceData { + string uuid = 1; + repeated uint32 data = 2 [packed=false]; +} +message BluetoothLEAdvertisementResponse { + option (id) = 67; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (no_delay) = true; + + uint64 address = 1; + string name = 2; + sint32 rssi = 3; + + repeated string service_uuids = 4; + repeated BluetoothServiceData service_data = 5; + repeated BluetoothServiceData manufacturer_data = 6; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9028034c90..a3b000c778 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -886,6 +886,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { #endif #ifdef USE_WEBSERVER resp.webserver_port = USE_WEBSERVER_PORT; +#endif +#ifdef USE_BLUETOOTH_PROXY + resp.has_bluetooth_proxy = true; #endif return resp; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 0787d2f7eb..dcf8bacad2 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -7,6 +7,10 @@ #include "api_server.h" #include "api_frame_helper.h" +#ifdef USE_BLUETOOTH_PROXY +#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" +#endif + namespace esphome { namespace api { @@ -94,6 +98,13 @@ class APIConnection : public APIServerConnection { return; this->send_homeassistant_service_response(call); } +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) { + if (!this->bluetooth_le_advertisement_subscription_) + return false; + return this->send_bluetooth_le_advertisement_response(call); + } +#endif #ifdef USE_HOMEASSISTANT_TIME void send_time_request() { GetTimeRequest req; @@ -134,6 +145,9 @@ class APIConnection : public APIServerConnection { return {}; } void execute_service(const ExecuteServiceRequest &msg) override; + void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override { + this->bluetooth_le_advertisement_subscription_ = true; + } bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } bool is_connection_setup() override { return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated(); @@ -176,6 +190,7 @@ class APIConnection : public APIServerConnection { uint32_t last_traffic_; bool sent_ping_{false}; bool service_call_subscription_{false}; + bool bluetooth_le_advertisement_subscription_{true}; bool next_close_ = false; APIServer *parent_; InitialStateIterator initial_state_iterator_; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index b91c9bd600..13969fce76 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -495,6 +495,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { this->webserver_port = value.as_uint32(); return true; } + case 11: { + this->has_bluetooth_proxy = value.as_bool(); + return true; + } default: return false; } @@ -544,6 +548,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(8, this->project_name); buffer.encode_string(9, this->project_version); buffer.encode_uint32(10, this->webserver_port); + buffer.encode_bool(11, this->has_bluetooth_proxy); } #ifdef HAS_PROTO_MESSAGE_DUMP void DeviceInfoResponse::dump_to(std::string &out) const { @@ -589,6 +594,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const { sprintf(buffer, "%u", this->webserver_port); out.append(buffer); out.append("\n"); + + out.append(" has_bluetooth_proxy: "); + out.append(YESNO(this->has_bluetooth_proxy)); + out.append("\n"); out.append("}"); } #endif @@ -4854,6 +4863,143 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { out.append("}"); } #endif +void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { + out.append("SubscribeBluetoothLEAdvertisementsRequest {}"); +} +#endif +bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->data.push_back(value.as_uint32()); + return true; + } + default: + return false; + } +} +bool BluetoothServiceData::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->uuid = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->uuid); + for (auto &it : this->data) { + buffer.encode_uint32(2, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothServiceData::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothServiceData {\n"); + out.append(" uuid: "); + out.append("'").append(this->uuid).append("'"); + out.append("\n"); + + for (const auto &it : this->data) { + out.append(" data: "); + sprintf(buffer, "%u", it); + out.append(buffer); + out.append("\n"); + } + out.append("}"); +} +#endif +bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 3: { + this->rssi = value.as_sint32(); + return true; + } + default: + return false; + } +} +bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->name = value.as_string(); + return true; + } + case 4: { + this->service_uuids.push_back(value.as_string()); + return true; + } + case 5: { + this->service_data.push_back(value.as_message()); + return true; + } + case 6: { + this->manufacturer_data.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_string(2, this->name); + buffer.encode_sint32(3, this->rssi); + for (auto &it : this->service_uuids) { + buffer.encode_string(4, it, true); + } + for (auto &it : this->service_data) { + buffer.encode_message(5, it, true); + } + for (auto &it : this->manufacturer_data) { + buffer.encode_message(6, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothLEAdvertisementResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" rssi: "); + sprintf(buffer, "%d", this->rssi); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->service_uuids) { + out.append(" service_uuids: "); + out.append("'").append(it).append("'"); + out.append("\n"); + } + + for (const auto &it : this->service_data) { + out.append(" service_data: "); + it.dump_to(out); + out.append("\n"); + } + + for (const auto &it : this->manufacturer_data) { + out.append(" manufacturer_data: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index f9981fdbb7..2093f93ee7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -263,6 +263,7 @@ class DeviceInfoResponse : public ProtoMessage { std::string project_name{}; std::string project_version{}; uint32_t webserver_port{0}; + bool has_bluetooth_proxy{false}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1214,6 +1215,45 @@ class MediaPlayerCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: +}; +class BluetoothServiceData : public ProtoMessage { + public: + std::string uuid{}; + std::vector data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothLEAdvertisementResponse : public ProtoMessage { + public: + uint64_t address{0}; + std::string name{}; + int32_t rssi{0}; + std::vector service_uuids{}; + std::vector service_data{}; + std::vector manufacturer_data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index bd146cb54d..6932675c41 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -328,6 +328,14 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer #endif #ifdef USE_MEDIA_PLAYER #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 67); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -595,6 +603,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, #endif break; } + case 66: { + SubscribeBluetoothLEAdvertisementsRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str()); +#endif + this->on_subscribe_bluetooth_le_advertisements_request(msg); + break; + } default: return false; } @@ -691,6 +708,18 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc } this->subscribe_home_assistant_states(msg); } +void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( + const SubscribeBluetoothLEAdvertisementsRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_bluetooth_le_advertisements(msg); +} void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { if (!this->is_connection_setup()) { this->on_no_setup_connection(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 28ad3fbd15..49426d1bbc 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -153,6 +153,11 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_MEDIA_PLAYER virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; +#endif + virtual void on_subscribe_bluetooth_le_advertisements_request( + const SubscribeBluetoothLEAdvertisementsRequest &value){}; +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -170,6 +175,7 @@ class APIServerConnection : public APIServerConnectionBase { virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0; virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; + virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; virtual void execute_service(const ExecuteServiceRequest &msg) = 0; #ifdef USE_COVER @@ -216,6 +222,7 @@ class APIServerConnection : public APIServerConnectionBase { void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override; void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override; void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; + void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; void on_get_time_request(const GetTimeRequest &msg) override; void on_execute_service_request(const ExecuteServiceRequest &msg) override; #ifdef USE_COVER diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 8375a82313..5851594955 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -291,6 +291,13 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon client->send_homeassistant_service_call(call); } } +#ifdef USE_BLUETOOTH_PROXY +void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_le_advertisement(call); + } +} +#endif APIServer::APIServer() { global_api_server = this; } void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, std::function f) { diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 6997e23cac..dc892b2088 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -73,6 +73,9 @@ class APIServer : public Component, public Controller { void on_media_player_update(media_player::MediaPlayer *obj) override; #endif void send_homeassistant_service_call(const HomeassistantServiceResponse &call); +#ifdef USE_BLUETOOTH_PROXY + void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call); +#endif void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #ifdef USE_HOMEASSISTANT_TIME void request_time(); diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 32f525990d..11cd4330ce 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -70,7 +70,7 @@ class ProtoVarInt { } } void encode(std::vector &out) { - uint32_t val = this->value_; + uint64_t val = this->value_; if (val <= 0x7F) { out.push_back(val); return; diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py new file mode 100644 index 0000000000..a6a5a4391b --- /dev/null +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -0,0 +1,27 @@ +from esphome.components import esp32_ble_tracker +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import CONF_ID + +DEPENDENCIES = ["esp32", "esp32_ble_tracker"] +CODEOWNERS = ["@jesserockz"] + + +bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy") + +BluetoothProxy = bluetooth_proxy_ns.class_("BluetoothProxy", cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BluetoothProxy), + } +).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + await esp32_ble_tracker.register_ble_device(var, config) + + cg.add_define("USE_BLUETOOTH_PROXY") diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp new file mode 100644 index 0000000000..41871295ce --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -0,0 +1,58 @@ +#include "bluetooth_proxy.h" + +#ifdef USE_API +#include "esphome/components/api/api_pb2.h" +#include "esphome/components/api/api_server.h" +#endif // USE_API +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace bluetooth_proxy { + +static const char *const TAG = "bluetooth_proxy"; + +bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(), + device.get_rssi()); + this->send_api_packet_(device); + return true; +} + +void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { +#ifndef USE_API + return; +#else + api::BluetoothLEAdvertisementResponse resp; + resp.address = device.address_uint64(); + if (!device.get_name().empty()) + resp.name = device.get_name(); + resp.rssi = device.get_rssi(); + for (auto uuid : device.get_service_uuids()) { + resp.service_uuids.push_back(uuid.to_string()); + } + for (auto &data : device.get_service_datas()) { + api::BluetoothServiceData service_data; + service_data.uuid = data.uuid.to_string(); + for (auto d : data.data) + service_data.data.push_back(d); + resp.service_data.push_back(service_data); + } + for (auto &data : device.get_manufacturer_datas()) { + api::BluetoothServiceData manufacturer_data; + manufacturer_data.uuid = data.uuid.to_string(); + for (auto d : data.data) + manufacturer_data.data.push_back(d); + resp.manufacturer_data.push_back(manufacturer_data); + } + api::global_api_server->send_bluetooth_le_advertisement(resp); +#endif +} + +void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h new file mode 100644 index 0000000000..9a936747c0 --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef USE_ESP32 + +#include + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace bluetooth_proxy { + +class BluetoothProxy : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + void dump_config() override; + + protected: + void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); +}; + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 5b1d567aa4..51da2ceb66 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -70,6 +70,7 @@ #define USE_ESP32_IGNORE_EFUSE_MAC_CRC #define USE_IMPROV #define USE_SOCKET_IMPL_BSD_SOCKETS +#define USE_BLUETOOTH_PROXY #ifdef USE_ARDUINO #define USE_ARDUINO_VERSION_CODE VERSION_CODE(1, 0, 6) diff --git a/tests/test1.yaml b/tests/test1.yaml index 5897639ead..15ede4d05e 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -288,6 +288,8 @@ adalight: esp32_ble_tracker: +bluetooth_proxy: + ble_client: - mac_address: AA:BB:CC:DD:EE:FF id: ble_foo From d2ce62aa13714c8f76bd74d9b03c93985205a343 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Sun, 4 Sep 2022 10:21:17 +0300 Subject: [PATCH 29/69] Ignore NaN states in the integration component (#3767) --- esphome/components/integration/integration_sensor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 65fa42dd0d..2ac7caca21 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -23,6 +23,8 @@ void IntegrationSensor::setup() { } void IntegrationSensor::dump_config() { LOG_SENSOR("", "Integration Sensor", this); } void IntegrationSensor::process_sensor_value_(float value) { + if (std::isnan(value)) + return; const uint32_t now = millis(); const double old_value = this->last_value_; const double new_value = value; From 39d493c2786d301797ec19a46fa45914710d226c Mon Sep 17 00:00:00 2001 From: Avirsaam <107193364+Avirsaam@users.noreply.github.com> Date: Mon, 5 Sep 2022 03:50:27 +0300 Subject: [PATCH 30/69] Update modbus_controller.cpp (#3768) --- 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 bd2fec3ece..e60b016a17 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -236,7 +236,7 @@ size_t ModbusController::create_register_ranges_() { } } - if (curr->start_address == r.start_address) { + if (curr->start_address == r.start_address && curr->register_type == r.register_type) { // use the lowest non zero value for the whole range // Because zero is the default value for skip_updates it is excluded from getting the min value. if (curr->skip_updates != 0) { From 5bf0c92318b463968bdf070835070fb2112e43a9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 13:12:00 +1200 Subject: [PATCH 31/69] Bump version to 2022.8.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 3091f55f12..7d916e30af 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.1" +__version__ = "2022.8.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From acd0b50b40115d63cc345b3211ad551cee764307 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 16:41:23 +1200 Subject: [PATCH 32/69] Fix HA addon auth using HA credentials (#3758) --- esphome/dashboard/dashboard.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 1fadac968d..21730033a5 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -816,14 +816,17 @@ class LoginHandler(BaseHandler): import requests headers = { - "Authentication": f"Bearer {os.getenv('SUPERVISOR_TOKEN')}", + "X-Supervisor-Token": os.getenv("SUPERVISOR_TOKEN"), } + data = { "username": self.get_argument("username", ""), "password": self.get_argument("password", ""), } try: - req = requests.post("http://supervisor/auth", headers=headers, data=data) + req = requests.post( + "http://supervisor/auth", headers=headers, json=data, timeout=30 + ) if req.status_code == 200: self.set_secure_cookie("authenticated", cookie_authenticated_yes) self.redirect("/") From 733a84df7515019c20cf660139737c3a4818966f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 16:50:17 +1200 Subject: [PATCH 33/69] Bump version to 2022.8.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7d916e30af..d65aea7159 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.8.2" +__version__ = "2022.8.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From e557dc7208abda7e1e23ec23c743637491f8fcae Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Sep 2022 17:02:22 +1200 Subject: [PATCH 34/69] Bump version to 2022.9.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c9d46fc82c..608a61466b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0-dev" +__version__ = "2022.9.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c61abf6aca6be646320bd39d1b8fd7cdf348e339 Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Wed, 14 Sep 2022 15:01:28 +0200 Subject: [PATCH 35/69] null initialize total sensor for pulse counter (#3803) * null initialize total sensor. * pedantic styling fix Co-authored-by: Guillermo Ruffino Co-authored-by: Guillermo Ruffino --- esphome/components/pulse_counter/pulse_counter_sensor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index d9be79e403..ef944f106f 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -81,7 +81,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { PulseCounterStorageBase &storage_; uint32_t last_time_{0}; uint32_t current_total_{0}; - sensor::Sensor *total_sensor_; + sensor::Sensor *total_sensor_{nullptr}; }; } // namespace pulse_counter From cea7deab912f664635cb28678690c72fc452c80d Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Wed, 14 Sep 2022 16:43:03 -0300 Subject: [PATCH 36/69] Sim800l add calls, multiline sms and ussd (#3630) Co-authored-by: Matus Ivanecky Co-authored-by: Matus Ivanecky --- esphome/components/sim800l/__init__.py | 109 ++++++++++ esphome/components/sim800l/sim800l.cpp | 282 ++++++++++++++++++++----- esphome/components/sim800l/sim800l.h | 121 +++++++++-- 3 files changed, 442 insertions(+), 70 deletions(-) diff --git a/esphome/components/sim800l/__init__.py b/esphome/components/sim800l/__init__.py index 564b685b37..698e3cda9e 100644 --- a/esphome/components/sim800l/__init__.py +++ b/esphome/components/sim800l/__init__.py @@ -18,15 +18,42 @@ Sim800LReceivedMessageTrigger = sim800l_ns.class_( "Sim800LReceivedMessageTrigger", automation.Trigger.template(cg.std_string, cg.std_string), ) +Sim800LIncomingCallTrigger = sim800l_ns.class_( + "Sim800LIncomingCallTrigger", + automation.Trigger.template(cg.std_string), +) +Sim800LCallConnectedTrigger = sim800l_ns.class_( + "Sim800LCallConnectedTrigger", + automation.Trigger.template(), +) +Sim800LCallDisconnectedTrigger = sim800l_ns.class_( + "Sim800LCallDisconnectedTrigger", + automation.Trigger.template(), +) + +Sim800LReceivedUssdTrigger = sim800l_ns.class_( + "Sim800LReceivedUssdTrigger", + automation.Trigger.template(cg.std_string), +) # Actions Sim800LSendSmsAction = sim800l_ns.class_("Sim800LSendSmsAction", automation.Action) +Sim800LSendUssdAction = sim800l_ns.class_("Sim800LSendUssdAction", automation.Action) Sim800LDialAction = sim800l_ns.class_("Sim800LDialAction", automation.Action) +Sim800LConnectAction = sim800l_ns.class_("Sim800LConnectAction", automation.Action) +Sim800LDisconnectAction = sim800l_ns.class_( + "Sim800LDisconnectAction", automation.Action +) CONF_SIM800L_ID = "sim800l_id" CONF_ON_SMS_RECEIVED = "on_sms_received" +CONF_ON_USSD_RECEIVED = "on_ussd_received" +CONF_ON_INCOMING_CALL = "on_incoming_call" +CONF_ON_CALL_CONNECTED = "on_call_connected" +CONF_ON_CALL_DISCONNECTED = "on_call_disconnected" CONF_RECIPIENT = "recipient" CONF_MESSAGE = "message" +CONF_USSD = "ussd" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -39,6 +66,34 @@ CONFIG_SCHEMA = cv.All( ), } ), + cv.Optional(CONF_ON_INCOMING_CALL): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LIncomingCallTrigger + ), + } + ), + cv.Optional(CONF_ON_CALL_CONNECTED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LCallConnectedTrigger + ), + } + ), + cv.Optional(CONF_ON_CALL_DISCONNECTED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LCallDisconnectedTrigger + ), + } + ), + cv.Optional(CONF_ON_USSD_RECEIVED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LReceivedUssdTrigger + ), + } + ), } ) .extend(cv.polling_component_schema("5s")) @@ -59,6 +114,19 @@ async def to_code(config): await automation.build_automation( trigger, [(cg.std_string, "message"), (cg.std_string, "sender")], conf ) + for conf in config.get(CONF_ON_INCOMING_CALL, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.std_string, "caller_id")], conf) + for conf in config.get(CONF_ON_CALL_CONNECTED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_CALL_DISCONNECTED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_USSD_RECEIVED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.std_string, "ussd")], conf) SIM800L_SEND_SMS_SCHEMA = cv.Schema( @@ -98,3 +166,44 @@ async def sim800l_dial_to_code(config, action_id, template_arg, args): template_ = await cg.templatable(config[CONF_RECIPIENT], args, cg.std_string) cg.add(var.set_recipient(template_)) return var + + +@automation.register_action( + "sim800l.connect", + Sim800LConnectAction, + cv.Schema({cv.GenerateID(): cv.use_id(Sim800LComponent)}), +) +async def sim800l_connect_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var + + +SIM800L_SEND_USSD_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(Sim800LComponent), + cv.Required(CONF_USSD): cv.templatable(cv.string_strict), + } +) + + +@automation.register_action( + "sim800l.send_ussd", Sim800LSendUssdAction, SIM800L_SEND_USSD_SCHEMA +) +async def sim800l_send_ussd_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_USSD], args, cg.std_string) + cg.add(var.set_ussd(template_)) + return var + + +@automation.register_action( + "sim800l.disconnect", + Sim800LDisconnectAction, + cv.Schema({cv.GenerateID(): cv.use_id(Sim800LComponent)}), +) +async def sim800l_disconnect_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index a935978747..3ae5de491a 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -16,20 +16,38 @@ void Sim800LComponent::update() { this->write(26); } + if (this->expect_ack_) + return; + if (state_ == STATE_INIT) { if (this->registered_ && this->send_pending_) { this->send_cmd_("AT+CSCS=\"GSM\""); - this->state_ = STATE_SENDINGSMS1; + this->state_ = STATE_SENDING_SMS_1; } else if (this->registered_ && this->dial_pending_) { this->send_cmd_("AT+CSCS=\"GSM\""); this->state_ = STATE_DIALING1; + } else if (this->registered_ && this->connect_pending_) { + this->connect_pending_ = false; + ESP_LOGI(TAG, "Connecting..."); + this->send_cmd_("ATA"); + this->state_ = STATE_ATA_SENT; + } else if (this->registered_ && this->send_ussd_pending_) { + this->send_cmd_("AT+CSCS=\"GSM\""); + this->state_ = STATE_SEND_USSD1; + } else if (this->registered_ && this->disconnect_pending_) { + this->disconnect_pending_ = false; + ESP_LOGI(TAG, "Disconnecting..."); + this->send_cmd_("ATH"); + } else if (this->registered_ && this->call_state_ != 6) { + send_cmd_("AT+CLCC"); + this->state_ = STATE_CHECK_CALL; + return; } else { this->send_cmd_("AT"); - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; } this->expect_ack_ = true; - } - if (state_ == STATE_RECEIVEDSMS) { + } else if (state_ == STATE_RECEIVED_SMS) { // Serial Buffer should have flushed. // Send cmd to delete received sms char delete_cmd[20]; @@ -49,16 +67,29 @@ void Sim800LComponent::send_cmd_(const std::string &message) { } void Sim800LComponent::parse_cmd_(std::string message) { - ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_); - if (message.empty()) return; + ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_); + + if (this->state_ != STATE_RECEIVE_SMS) { + if (message == "RING") { + // Incoming call... + this->state_ = STATE_PARSE_CLIP; + this->expect_ack_ = false; + } else if (message == "NO CARRIER") { + if (this->call_state_ != 6) { + this->call_state_ = 6; + this->call_disconnected_callback_.call(); + } + } + } + + bool ok = message == "OK"; if (this->expect_ack_) { - bool ok = message == "OK"; this->expect_ack_ = false; if (!ok) { - if (this->state_ == STATE_CHECK_AT && message == "AT") { + if (this->state_ == STATE_SETUP_CMGF && message == "AT") { // Expected ack but AT echo received this->state_ = STATE_DISABLE_ECHO; this->expect_ack_ = true; @@ -68,6 +99,10 @@ void Sim800LComponent::parse_cmd_(std::string message) { return; } } + } else if (ok && (this->state_ != STATE_PARSE_SMS_RESPONSE && this->state_ != STATE_CHECK_CALL && + this->state_ != STATE_RECEIVE_SMS && this->state_ != STATE_DIALING2)) { + ESP_LOGW(TAG, "Received unexpected OK. Ignoring"); + return; } switch (this->state_) { @@ -75,30 +110,88 @@ void Sim800LComponent::parse_cmd_(std::string message) { // While we were waiting for update to check for messages, this notifies a message // is available. bool message_available = message.compare(0, 6, "+CMTI:") == 0; - if (!message_available) + if (!message_available) { + if (message == "RING") { + // Incoming call... + this->state_ = STATE_PARSE_CLIP; + } else if (message == "NO CARRIER") { + if (this->call_state_ != 6) { + this->call_state_ = 6; + this->call_disconnected_callback_.call(); + } + } else if (message.compare(0, 6, "+CUSD:") == 0) { + // Incoming USSD MESSAGE + this->state_ = STATE_CHECK_USSD; + } break; + } + // Else fall thru ... } case STATE_CHECK_SMS: send_cmd_("AT+CMGL=\"ALL\""); - this->state_ = STATE_PARSE_SMS; + this->state_ = STATE_PARSE_SMS_RESPONSE; this->parse_index_ = 0; break; case STATE_DISABLE_ECHO: send_cmd_("ATE0"); - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; this->expect_ack_ = true; break; - case STATE_CHECK_AT: + case STATE_SETUP_CMGF: send_cmd_("AT+CMGF=1"); + this->state_ = STATE_SETUP_CLIP; + this->expect_ack_ = true; + break; + case STATE_SETUP_CLIP: + send_cmd_("AT+CLIP=1"); this->state_ = STATE_CREG; this->expect_ack_ = true; break; + case STATE_SETUP_USSD: + send_cmd_("AT+CUSD=1"); + this->state_ = STATE_CREG; + this->expect_ack_ = true; + break; + case STATE_SEND_USSD1: + this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\""); + this->state_ = STATE_SEND_USSD2; + break; + case STATE_SEND_USSD2: + ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str()); + if (message == "OK") { + // Dialing + ESP_LOGD(TAG, "Dialing ussd code: '%s' done.", this->ussd_.c_str()); + this->state_ = STATE_CHECK_USSD; + this->send_ussd_pending_ = false; + } else { + this->set_registered_(false); + this->state_ = STATE_INIT; + this->send_cmd_("AT+CMEE=2"); + this->write(26); + } + break; + case STATE_CHECK_USSD: + ESP_LOGD(TAG, "Check ussd code: '%s'", message.c_str()); + if (message.compare(0, 6, "+CUSD:") == 0) { + this->state_ = STATE_RECEIVED_USSD; + this->ussd_ = ""; + size_t start = 10; + size_t end = message.find_last_of(','); + if (end > start) { + this->ussd_ = message.substr(start + 1, end - start - 2); + this->ussd_received_callback_.call(this->ussd_); + } + } + // Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS + if (message == "OK") + this->state_ = STATE_INIT; + break; case STATE_CREG: send_cmd_("AT+CREG?"); - this->state_ = STATE_CREGWAIT; + this->state_ = STATE_CREG_WAIT; break; - case STATE_CREGWAIT: { + case STATE_CREG_WAIT: { // Response: "+CREG: 0,1" -- the one there means registered ok // "+CREG: -,-" means not registered ok bool registered = message.compare(0, 6, "+CREG:") == 0 && (message[9] == '1' || message[9] == '5'); @@ -112,10 +205,10 @@ void Sim800LComponent::parse_cmd_(std::string message) { if (message[7] == '0') { // Network registration is disable, enable it send_cmd_("AT+CREG=1"); this->expect_ack_ = true; - this->state_ = STATE_CHECK_AT; + this->state_ = STATE_SETUP_CMGF; } else { // Keep waiting registration - this->state_ = STATE_CREG; + this->state_ = STATE_INIT; } } set_registered_(registered); @@ -145,9 +238,6 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->expect_ack_ = true; this->state_ = STATE_CHECK_SMS; break; - case STATE_PARSE_SMS: - this->state_ = STATE_PARSE_SMS_RESPONSE; - break; case STATE_PARSE_SMS_RESPONSE: if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) { size_t start = 7; @@ -158,10 +248,11 @@ void Sim800LComponent::parse_cmd_(std::string message) { if (item == 1) { // Slot Index this->parse_index_ = parse_number(message.substr(start, end - start)).value_or(0); } - // item 2 = STATUS, usually "REC UNERAD" + // item 2 = STATUS, usually "REC UNREAD" if (item == 3) { // recipient // Add 1 and remove 2 from substring to get rid of "quotes" this->sender_ = message.substr(start + 1, end - start - 2); + this->message_.clear(); break; } // item 4 = "" @@ -174,42 +265,83 @@ void Sim800LComponent::parse_cmd_(std::string message) { ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str()); return; } - this->state_ = STATE_RECEIVESMS; + this->state_ = STATE_RECEIVE_SMS; + } + // Otherwise we receive another OK + if (ok) { + send_cmd_("AT+CLCC"); + this->state_ = STATE_CHECK_CALL; } - // Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS - if (message == "OK") - this->state_ = STATE_INIT; break; - case STATE_RECEIVESMS: + case STATE_CHECK_CALL: + if (message.compare(0, 6, "+CLCC:") == 0 && this->parse_index_ == 0) { + this->expect_ack_ = true; + size_t start = 7; + size_t end = message.find(',', start); + uint8_t item = 0; + while (end != start) { + item++; + // item 1 call index for +CHLD + // item 2 dir 0 Mobile originated; 1 Mobile terminated + if (item == 3) { // stat + uint8_t current_call_state = parse_number(message.substr(start, end - start)).value_or(6); + if (current_call_state != this->call_state_) { + ESP_LOGD(TAG, "Call state is now: %d", current_call_state); + if (current_call_state == 0) + this->call_connected_callback_.call(); + } + this->call_state_ = current_call_state; + break; + } + // item 4 = "" + // item 5 = Received timestamp + start = end + 1; + end = message.find(',', start); + } + + if (item < 2) { + ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str()); + return; + } + } else if (ok) { + if (this->call_state_ != 6) { + // no call in progress + this->call_state_ = 6; // Disconnect + this->call_disconnected_callback_.call(); + } + } + this->state_ = STATE_INIT; + break; + case STATE_RECEIVE_SMS: /* Our recipient is set and the message body is in message kick ESPHome callback now */ - ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); - ESP_LOGD(TAG, "%s", message.c_str()); - this->callback_.call(message, this->sender_); - /* If the message is multiline, next lines will contain message data. - If there were other messages in the list, next line will be +CMGL: ... - At the end of the list the new line and the OK should be received. - To keep this simple just first line of message if considered, then - the next state will swallow all received data and in next poll event - this message index is marked for deletion. - */ - this->state_ = STATE_RECEIVEDSMS; + if (ok || message.compare(0, 6, "+CMGL:") == 0) { + ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); + ESP_LOGD(TAG, "%s", this->message_.c_str()); + this->sms_received_callback_.call(this->message_, this->sender_); + this->state_ = STATE_RECEIVED_SMS; + } else { + if (this->message_.length() > 0) + this->message_ += "\n"; + this->message_ += message; + } break; - case STATE_RECEIVEDSMS: + case STATE_RECEIVED_SMS: + case STATE_RECEIVED_USSD: // Let the buffer flush. Next poll will request to delete the parsed index message. break; - case STATE_SENDINGSMS1: + case STATE_SENDING_SMS_1: this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\""); - this->state_ = STATE_SENDINGSMS2; + this->state_ = STATE_SENDING_SMS_2; break; - case STATE_SENDINGSMS2: + case STATE_SENDING_SMS_2: if (message == ">") { // Send sms body - ESP_LOGD(TAG, "Sending message: '%s'", this->outgoing_message_.c_str()); + ESP_LOGI(TAG, "Sending to %s message: '%s'", this->recipient_.c_str(), this->outgoing_message_.c_str()); this->write_str(this->outgoing_message_.c_str()); this->write(26); - this->state_ = STATE_SENDINGSMS3; + this->state_ = STATE_SENDING_SMS_3; } else { set_registered_(false); this->state_ = STATE_INIT; @@ -217,7 +349,7 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->write(26); } break; - case STATE_SENDINGSMS3: + case STATE_SENDING_SMS_3: if (message.compare(0, 6, "+CMGS:") == 0) { ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str()); this->send_pending_ = false; @@ -230,23 +362,55 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->state_ = STATE_DIALING2; break; case STATE_DIALING2: - if (message == "OK") { - // Dialing - ESP_LOGD(TAG, "Dialing: '%s'", this->recipient_.c_str()); - this->state_ = STATE_INIT; + if (ok) { + ESP_LOGI(TAG, "Dialing: '%s'", this->recipient_.c_str()); this->dial_pending_ = false; } else { this->set_registered_(false); - this->state_ = STATE_INIT; this->send_cmd_("AT+CMEE=2"); this->write(26); } + this->state_ = STATE_INIT; + break; + case STATE_PARSE_CLIP: + if (message.compare(0, 6, "+CLIP:") == 0) { + std::string caller_id; + size_t start = 7; + size_t end = message.find(',', start); + uint8_t item = 0; + while (end != start) { + item++; + if (item == 1) { // Slot Index + // Add 1 and remove 2 from substring to get rid of "quotes" + caller_id = message.substr(start + 1, end - start - 2); + break; + } + // item 4 = "" + // item 5 = Received timestamp + start = end + 1; + end = message.find(',', start); + } + if (this->call_state_ != 4) { + this->call_state_ = 4; + ESP_LOGI(TAG, "Incoming call from %s", caller_id.c_str()); + incoming_call_callback_.call(caller_id); + } + this->state_ = STATE_INIT; + } + break; + case STATE_ATA_SENT: + ESP_LOGI(TAG, "Call connected"); + if (this->call_state_ != 0) { + this->call_state_ = 0; + this->call_connected_callback_.call(); + } + this->state_ = STATE_INIT; break; default: - ESP_LOGD(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); + ESP_LOGW(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); break; } -} +} // namespace sim800l void Sim800LComponent::loop() { // Read message @@ -265,7 +429,7 @@ void Sim800LComponent::loop() { byte = '?'; // need to be valid utf8 string for log functions. this->read_buffer_[this->read_pos_] = byte; - if (this->state_ == STATE_SENDINGSMS2 && this->read_pos_ == 0 && byte == '>') + if (this->state_ == STATE_SENDING_SMS_2 && this->read_pos_ == 0 && byte == '>') this->read_buffer_[++this->read_pos_] = ASCII_LF; if (this->read_buffer_[this->read_pos_] == ASCII_LF) { @@ -276,13 +440,23 @@ void Sim800LComponent::loop() { this->read_pos_++; } } + if (state_ == STATE_INIT && this->registered_ && + (this->call_state_ != 6 // A call is in progress + || this->send_pending_ || this->dial_pending_ || this->connect_pending_ || this->disconnect_pending_)) { + this->update(); + } } void Sim800LComponent::send_sms(const std::string &recipient, const std::string &message) { - ESP_LOGD(TAG, "Sending to %s: %s", recipient.c_str(), message.c_str()); this->recipient_ = recipient; this->outgoing_message_ = message; this->send_pending_ = true; +} + +void Sim800LComponent::send_ussd(const std::string &ussd_code) { + ESP_LOGD(TAG, "Sending USSD code: %s", ussd_code.c_str()); + this->ussd_ = ussd_code; + this->send_ussd_pending_ = true; this->update(); } void Sim800LComponent::dump_config() { @@ -295,11 +469,11 @@ void Sim800LComponent::dump_config() { #endif } void Sim800LComponent::dial(const std::string &recipient) { - ESP_LOGD(TAG, "Dialing %s", recipient.c_str()); this->recipient_ = recipient; this->dial_pending_ = true; - this->update(); } +void Sim800LComponent::connect() { this->connect_pending_ = true; } +void Sim800LComponent::disconnect() { this->disconnect_pending_ = true; } void Sim800LComponent::set_registered_(bool registered) { this->registered_ = registered; diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index 3535b96283..bf7efd6915 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -16,31 +16,35 @@ namespace esphome { namespace sim800l { -const uint8_t SIM800L_READ_BUFFER_LENGTH = 255; +const uint16_t SIM800L_READ_BUFFER_LENGTH = 1024; enum State { STATE_IDLE = 0, STATE_INIT, - STATE_CHECK_AT, + STATE_SETUP_CMGF, + STATE_SETUP_CLIP, STATE_CREG, - STATE_CREGWAIT, + STATE_CREG_WAIT, STATE_CSQ, STATE_CSQ_RESPONSE, - STATE_IDLEWAIT, - STATE_SENDINGSMS1, - STATE_SENDINGSMS2, - STATE_SENDINGSMS3, + STATE_SENDING_SMS_1, + STATE_SENDING_SMS_2, + STATE_SENDING_SMS_3, STATE_CHECK_SMS, - STATE_PARSE_SMS, STATE_PARSE_SMS_RESPONSE, - STATE_RECEIVESMS, - STATE_READSMS, - STATE_RECEIVEDSMS, - STATE_DELETEDSMS, + STATE_RECEIVE_SMS, + STATE_RECEIVED_SMS, STATE_DISABLE_ECHO, - STATE_PARSE_SMS_OK, STATE_DIALING1, - STATE_DIALING2 + STATE_DIALING2, + STATE_PARSE_CLIP, + STATE_ATA_SENT, + STATE_CHECK_CALL, + STATE_SETUP_USSD, + STATE_SEND_USSD1, + STATE_SEND_USSD2, + STATE_CHECK_USSD, + STATE_RECEIVED_USSD }; class Sim800LComponent : public uart::UARTDevice, public PollingComponent { @@ -58,10 +62,25 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { void set_rssi_sensor(sensor::Sensor *rssi_sensor) { rssi_sensor_ = rssi_sensor; } #endif void add_on_sms_received_callback(std::function callback) { - this->callback_.add(std::move(callback)); + this->sms_received_callback_.add(std::move(callback)); + } + void add_on_incoming_call_callback(std::function callback) { + this->incoming_call_callback_.add(std::move(callback)); + } + void add_on_call_connected_callback(std::function callback) { + this->call_connected_callback_.add(std::move(callback)); + } + void add_on_call_disconnected_callback(std::function callback) { + this->call_disconnected_callback_.add(std::move(callback)); + } + void add_on_ussd_received_callback(std::function callback) { + this->ussd_received_callback_.add(std::move(callback)); } void send_sms(const std::string &recipient, const std::string &message); + void send_ussd(const std::string &ussd_code); void dial(const std::string &recipient); + void connect(); + void disconnect(); protected: void send_cmd_(const std::string &message); @@ -76,6 +95,7 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { sensor::Sensor *rssi_sensor_{nullptr}; #endif std::string sender_; + std::string message_; char read_buffer_[SIM800L_READ_BUFFER_LENGTH]; size_t read_pos_{0}; uint8_t parse_index_{0}; @@ -86,10 +106,19 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { std::string recipient_; std::string outgoing_message_; + std::string ussd_; bool send_pending_; bool dial_pending_; + bool connect_pending_; + bool disconnect_pending_; + bool send_ussd_pending_; + uint8_t call_state_{6}; - CallbackManager callback_; + CallbackManager sms_received_callback_; + CallbackManager incoming_call_callback_; + CallbackManager call_connected_callback_; + CallbackManager call_disconnected_callback_; + CallbackManager ussd_received_callback_; }; class Sim800LReceivedMessageTrigger : public Trigger { @@ -100,6 +129,33 @@ class Sim800LReceivedMessageTrigger : public Trigger { } }; +class Sim800LIncomingCallTrigger : public Trigger { + public: + explicit Sim800LIncomingCallTrigger(Sim800LComponent *parent) { + parent->add_on_incoming_call_callback([this](const std::string &caller_id) { this->trigger(caller_id); }); + } +}; + +class Sim800LCallConnectedTrigger : public Trigger<> { + public: + explicit Sim800LCallConnectedTrigger(Sim800LComponent *parent) { + parent->add_on_call_connected_callback([this]() { this->trigger(); }); + } +}; + +class Sim800LCallDisconnectedTrigger : public Trigger<> { + public: + explicit Sim800LCallDisconnectedTrigger(Sim800LComponent *parent) { + parent->add_on_call_disconnected_callback([this]() { this->trigger(); }); + } +}; +class Sim800LReceivedUssdTrigger : public Trigger { + public: + explicit Sim800LReceivedUssdTrigger(Sim800LComponent *parent) { + parent->add_on_ussd_received_callback([this](const std::string &ussd) { this->trigger(ussd); }); + } +}; + template class Sim800LSendSmsAction : public Action { public: Sim800LSendSmsAction(Sim800LComponent *parent) : parent_(parent) {} @@ -116,6 +172,20 @@ template class Sim800LSendSmsAction : public Action { Sim800LComponent *parent_; }; +template class Sim800LSendUssdAction : public Action { + public: + Sim800LSendUssdAction(Sim800LComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(std::string, ussd) + + void play(Ts... x) { + auto ussd_code = this->ussd_.value(x...); + this->parent_->send_ussd(ussd_code); + } + + protected: + Sim800LComponent *parent_; +}; + template class Sim800LDialAction : public Action { public: Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {} @@ -129,6 +199,25 @@ template class Sim800LDialAction : public Action { protected: Sim800LComponent *parent_; }; +template class Sim800LConnectAction : public Action { + public: + Sim800LConnectAction(Sim800LComponent *parent) : parent_(parent) {} + + void play(Ts... x) { this->parent_->connect(); } + + protected: + Sim800LComponent *parent_; +}; + +template class Sim800LDisconnectAction : public Action { + public: + Sim800LDisconnectAction(Sim800LComponent *parent) : parent_(parent) {} + + void play(Ts... x) { this->parent_->disconnect(); } + + protected: + Sim800LComponent *parent_; +}; } // namespace sim800l } // namespace esphome From 6b23b7cad7cecdd892dd36f8e9a5fce0e9d3cb29 Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Thu, 15 Sep 2022 01:53:02 +0200 Subject: [PATCH 37/69] Unify 'nullptr' initalization of class members; (#3805) --- esphome/components/ade7953/ade7953.h | 2 +- esphome/components/api/api_frame_helper.h | 6 +++--- esphome/components/hydreon_rgxx/hydreon_rgxx.h | 6 +++--- esphome/components/mlx90393/sensor_mlx90393.h | 2 +- esphome/components/pid/pid_climate.h | 4 ++-- esphome/components/pulse_meter/pulse_meter_sensor.h | 4 ++-- .../components/pvvx_mithermometer/display/pvvx_display.h | 2 +- esphome/core/automation.h | 2 +- esphome/core/component.h | 2 +- esphome/core/gpio.h | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index bb160cd8eb..418ad1c9e7 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -82,7 +82,7 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { return i2c::ERROR_OK; } - InternalGPIOPin *irq_pin_ = nullptr; + InternalGPIOPin *irq_pin_{nullptr}; bool is_setup_{false}; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_a_sensor_{nullptr}; diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 57e3c961d5..348a9b574f 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -116,9 +116,9 @@ class APINoiseFrameHelper : public APIFrameHelper { std::vector prologue_; std::shared_ptr ctx_; - NoiseHandshakeState *handshake_ = nullptr; - NoiseCipherState *send_cipher_ = nullptr; - NoiseCipherState *recv_cipher_ = nullptr; + NoiseHandshakeState *handshake_{nullptr}; + NoiseCipherState *send_cipher_{nullptr}; + NoiseCipherState *recv_cipher_{nullptr}; NoiseProtocolId nid_; enum class State { diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.h b/esphome/components/hydreon_rgxx/hydreon_rgxx.h index 1697060d28..34b9bd8d5e 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.h +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.h @@ -58,9 +58,9 @@ class HydreonRGxxComponent : public PollingComponent, public uart::UARTDevice { sensor::Sensor *sensors_[NUM_SENSORS] = {nullptr}; #ifdef USE_BINARY_SENSOR - binary_sensor::BinarySensor *too_cold_sensor_ = nullptr; - binary_sensor::BinarySensor *lens_bad_sensor_ = nullptr; - binary_sensor::BinarySensor *em_sat_sensor_ = nullptr; + binary_sensor::BinarySensor *too_cold_sensor_{nullptr}; + binary_sensor::BinarySensor *lens_bad_sensor_{nullptr}; + binary_sensor::BinarySensor *em_sat_sensor_{nullptr}; #endif int16_t boot_count_ = 0; diff --git a/esphome/components/mlx90393/sensor_mlx90393.h b/esphome/components/mlx90393/sensor_mlx90393.h index fc33ad1aa8..8dfb7e6a13 100644 --- a/esphome/components/mlx90393/sensor_mlx90393.h +++ b/esphome/components/mlx90393/sensor_mlx90393.h @@ -52,7 +52,7 @@ class MLX90393Cls : public PollingComponent, public i2c::I2CDevice, public MLX90 uint8_t temperature_oversampling_ = 0; uint8_t filter_; uint8_t resolutions_[3] = {0}; - GPIOPin *drdy_pin_ = nullptr; + GPIOPin *drdy_pin_{nullptr}; }; } // namespace mlx90393 diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index ff301386b6..095c00eb49 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -59,8 +59,8 @@ class PIDClimate : public climate::Climate, public Component { /// The sensor used for getting the current temperature sensor::Sensor *sensor_; - output::FloatOutput *cool_output_ = nullptr; - output::FloatOutput *heat_output_ = nullptr; + output::FloatOutput *cool_output_{nullptr}; + output::FloatOutput *heat_output_{nullptr}; PIDController controller_; /// Output value as reported by the PID controller, for PIDClimateSensor float output_value_; diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.h b/esphome/components/pulse_meter/pulse_meter_sensor.h index cf08f8c92d..bf50eab6ff 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.h +++ b/esphome/components/pulse_meter/pulse_meter_sensor.h @@ -31,11 +31,11 @@ class PulseMeterSensor : public sensor::Sensor, public Component { protected: static void gpio_intr(PulseMeterSensor *sensor); - InternalGPIOPin *pin_ = nullptr; + InternalGPIOPin *pin_{nullptr}; ISRInternalGPIOPin isr_pin_; uint32_t filter_us_ = 0; uint32_t timeout_us_ = 1000000UL * 60UL * 5UL; - sensor::Sensor *total_sensor_ = nullptr; + sensor::Sensor *total_sensor_{nullptr}; InternalFilterMode filter_mode_{FILTER_EDGE}; Deduplicator pulse_width_dedupe_; diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.h b/esphome/components/pvvx_mithermometer/display/pvvx_display.h index 4de1ab7ba6..c7e7cc04fb 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.h +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.h @@ -114,7 +114,7 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent { void delayed_disconnect_(); #ifdef USE_TIME void sync_time_(); - time::RealTimeClock *time_ = nullptr; + time::RealTimeClock *time_{nullptr}; #endif uint16_t char_handle_ = 0; bool connection_established_ = false; diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 92bc32247b..84c754e081 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -176,7 +176,7 @@ template class Action { return this->next_->is_running(); } - Action *next_ = nullptr; + Action *next_{nullptr}; /// The number of instances of this sequence in the list of actions /// that is currently being executed. diff --git a/esphome/core/component.h b/esphome/core/component.h index e394736653..cb97a93d21 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -254,7 +254,7 @@ class Component { uint32_t component_state_{0x0000}; ///< State of this component. float setup_priority_override_{NAN}; - const char *component_source_ = nullptr; + const char *component_source_{nullptr}; }; /** This class simplifies creating components that periodically check a state. diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h index b953a95664..1b6f2ba1e6 100644 --- a/esphome/core/gpio.h +++ b/esphome/core/gpio.h @@ -73,7 +73,7 @@ class ISRInternalGPIOPin { void pin_mode(gpio::Flags flags); protected: - void *arg_ = nullptr; + void *arg_{nullptr}; }; class InternalGPIOPin : public GPIOPin { From df4d0da221f8c1296ea3c53d3667d50c44a663df Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 15 Sep 2022 11:53:22 +1200 Subject: [PATCH 38/69] Initialize all child sensors to nullptr (#3808) --- esphome/components/aht10/aht10.h | 4 ++-- esphome/components/am2320/am2320.h | 4 ++-- esphome/components/as3935/as3935.h | 6 +++--- esphome/components/bl0939/bl0939.h | 16 ++++++++-------- esphome/components/bl0940/bl0940.h | 12 ++++++------ esphome/components/bl0942/bl0942.h | 10 +++++----- esphome/components/bme280/bme280.h | 6 +++--- esphome/components/bme680/bme680.h | 8 ++++---- esphome/components/bme680_bsec/bme680_bsec.h | 18 +++++++++--------- esphome/components/bmp280/bmp280.h | 4 ++-- esphome/components/bmp3xx/bmp3xx.h | 4 ++-- esphome/components/dht12/dht12.h | 4 ++-- esphome/components/dps310/dps310.h | 4 ++-- esphome/components/ens210/ens210.h | 4 ++-- esphome/components/hdc1080/hdc1080.h | 4 ++-- esphome/components/hmc5883l/hmc5883l.h | 8 ++++---- esphome/components/honeywellabp/honeywellabp.h | 4 ++-- esphome/components/ms5611/ms5611.h | 4 ++-- esphome/components/pzem004t/pzem004t.h | 8 ++++---- esphome/components/pzemac/pzemac.h | 12 ++++++------ esphome/components/pzemdc/pzemdc.h | 10 +++++----- esphome/components/qmc5883l/qmc5883l.h | 8 ++++---- esphome/components/qmp6988/qmp6988.h | 4 ++-- esphome/components/sht3xd/sht3xd.h | 4 ++-- esphome/components/shtcx/shtcx.h | 4 ++-- esphome/components/tsl2591/tsl2591.h | 8 ++++---- esphome/components/tx20/tx20.h | 4 ++-- 27 files changed, 93 insertions(+), 93 deletions(-) diff --git a/esphome/components/aht10/aht10.h b/esphome/components/aht10/aht10.h index bfb6b07a7a..4d0eaa5919 100644 --- a/esphome/components/aht10/aht10.h +++ b/esphome/components/aht10/aht10.h @@ -18,8 +18,8 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice { void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } protected: - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace aht10 diff --git a/esphome/components/am2320/am2320.h b/esphome/components/am2320/am2320.h index 33e1d30aa0..da1e87cf65 100644 --- a/esphome/components/am2320/am2320.h +++ b/esphome/components/am2320/am2320.h @@ -21,8 +21,8 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice { bool read_data_(uint8_t *data); bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace am2320 diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index 2e65aab4d1..2cba9b11a0 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -92,9 +92,9 @@ class AS3935Component : public Component { virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0; - sensor::Sensor *distance_sensor_; - sensor::Sensor *energy_sensor_; - binary_sensor::BinarySensor *thunder_alert_binary_sensor_; + sensor::Sensor *distance_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; + binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr}; GPIOPin *irq_pin_; bool indoor_; diff --git a/esphome/components/bl0939/bl0939.h b/esphome/components/bl0939/bl0939.h index d3dab67cc1..673d4ff351 100644 --- a/esphome/components/bl0939/bl0939.h +++ b/esphome/components/bl0939/bl0939.h @@ -75,16 +75,16 @@ class BL0939 : public PollingComponent, public uart::UARTDevice { void dump_config() override; protected: - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_1_; - sensor::Sensor *current_sensor_2_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_1_{nullptr}; + sensor::Sensor *current_sensor_2_{nullptr}; // NB This may be negative as the circuits is seemingly able to measure // power in both directions - sensor::Sensor *power_sensor_1_; - sensor::Sensor *power_sensor_2_; - sensor::Sensor *energy_sensor_1_; - sensor::Sensor *energy_sensor_2_; - sensor::Sensor *energy_sensor_sum_; + sensor::Sensor *power_sensor_1_{nullptr}; + sensor::Sensor *power_sensor_2_{nullptr}; + sensor::Sensor *energy_sensor_1_{nullptr}; + sensor::Sensor *energy_sensor_2_{nullptr}; + sensor::Sensor *energy_sensor_sum_{nullptr}; // Divide by this to turn into Watt float power_reference_ = BL0939_PREF; diff --git a/esphome/components/bl0940/bl0940.h b/esphome/components/bl0940/bl0940.h index 49c8e50595..2d4e7ccaac 100644 --- a/esphome/components/bl0940/bl0940.h +++ b/esphome/components/bl0940/bl0940.h @@ -75,14 +75,14 @@ class BL0940 : public PollingComponent, public uart::UARTDevice { void dump_config() override; protected: - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; // NB This may be negative as the circuits is seemingly able to measure // power in both directions - sensor::Sensor *power_sensor_; - sensor::Sensor *energy_sensor_; - sensor::Sensor *internal_temperature_sensor_; - sensor::Sensor *external_temperature_sensor_; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; + sensor::Sensor *internal_temperature_sensor_{nullptr}; + sensor::Sensor *external_temperature_sensor_{nullptr}; // Max difference between two measurements of the temperature. Used to avoid noise. float max_temperature_diff_{0}; diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h index 8149b7493b..12489915e1 100644 --- a/esphome/components/bl0942/bl0942.h +++ b/esphome/components/bl0942/bl0942.h @@ -43,13 +43,13 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { void dump_config() override; protected: - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; // NB This may be negative as the circuits is seemingly able to measure // power in both directions - sensor::Sensor *power_sensor_; - sensor::Sensor *energy_sensor_; - sensor::Sensor *frequency_sensor_; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; + sensor::Sensor *frequency_sensor_{nullptr}; // Divide by this to turn into Watt float power_reference_ = BL0942_PREF; diff --git a/esphome/components/bme280/bme280.h b/esphome/components/bme280/bme280.h index 8511f73382..50d398c40f 100644 --- a/esphome/components/bme280/bme280.h +++ b/esphome/components/bme280/bme280.h @@ -96,9 +96,9 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice { BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X}; BME280Oversampling humidity_oversampling_{BME280_OVERSAMPLING_16X}; BME280IIRFilter iir_filter_{BME280_IIR_FILTER_OFF}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, diff --git a/esphome/components/bme680/bme680.h b/esphome/components/bme680/bme680.h index 0671cd990e..6446449742 100644 --- a/esphome/components/bme680/bme680.h +++ b/esphome/components/bme680/bme680.h @@ -129,10 +129,10 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice { uint16_t heater_temperature_{320}; uint16_t heater_duration_{150}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; - sensor::Sensor *humidity_sensor_; - sensor::Sensor *gas_resistance_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *gas_resistance_sensor_{nullptr}; }; } // namespace bme680 diff --git a/esphome/components/bme680_bsec/bme680_bsec.h b/esphome/components/bme680_bsec/bme680_bsec.h index 650b4d2413..6fe8f8fef7 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.h +++ b/esphome/components/bme680_bsec/bme680_bsec.h @@ -100,15 +100,15 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice { SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT}; SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; - sensor::Sensor *humidity_sensor_; - sensor::Sensor *gas_resistance_sensor_; - sensor::Sensor *iaq_sensor_; - text_sensor::TextSensor *iaq_accuracy_text_sensor_; - sensor::Sensor *iaq_accuracy_sensor_; - sensor::Sensor *co2_equivalent_sensor_; - sensor::Sensor *breath_voc_equivalent_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *gas_resistance_sensor_{nullptr}; + sensor::Sensor *iaq_sensor_{nullptr}; + text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr}; + sensor::Sensor *iaq_accuracy_sensor_{nullptr}; + sensor::Sensor *co2_equivalent_sensor_{nullptr}; + sensor::Sensor *breath_voc_equivalent_sensor_{nullptr}; }; #endif } // namespace bme680_bsec diff --git a/esphome/components/bmp280/bmp280.h b/esphome/components/bmp280/bmp280.h index f8646fb547..96eb470155 100644 --- a/esphome/components/bmp280/bmp280.h +++ b/esphome/components/bmp280/bmp280.h @@ -81,8 +81,8 @@ class BMP280Component : public PollingComponent, public i2c::I2CDevice { BMP280Oversampling temperature_oversampling_{BMP280_OVERSAMPLING_16X}; BMP280Oversampling pressure_oversampling_{BMP280_OVERSAMPLING_16X}; BMP280IIRFilter iir_filter_{BMP280_IIR_FILTER_OFF}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, diff --git a/esphome/components/bmp3xx/bmp3xx.h b/esphome/components/bmp3xx/bmp3xx.h index ab20abfe9b..d3b15f601d 100644 --- a/esphome/components/bmp3xx/bmp3xx.h +++ b/esphome/components/bmp3xx/bmp3xx.h @@ -125,8 +125,8 @@ class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice { Oversampling pressure_oversampling_{OVERSAMPLING_X16}; IIRFilter iir_filter_{IIR_FILTER_OFF}; OperationMode operation_mode_{FORCED_MODE}; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; enum ErrorCode { NONE = 0, ERROR_COMMUNICATION_FAILED, diff --git a/esphome/components/dht12/dht12.h b/esphome/components/dht12/dht12.h index ae4d4fd607..2a706039ba 100644 --- a/esphome/components/dht12/dht12.h +++ b/esphome/components/dht12/dht12.h @@ -20,8 +20,8 @@ class DHT12Component : public PollingComponent, public i2c::I2CDevice { protected: bool read_data_(uint8_t *data); - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace dht12 diff --git a/esphome/components/dps310/dps310.h b/esphome/components/dps310/dps310.h index 7aca2c3d10..50e7d93c8a 100644 --- a/esphome/components/dps310/dps310.h +++ b/esphome/components/dps310/dps310.h @@ -53,8 +53,8 @@ class DPS310Component : public PollingComponent, public i2c::I2CDevice { void calculate_values_(int32_t raw_temperature, int32_t raw_pressure); static int32_t twos_complement(int32_t val, uint8_t bits); - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; int32_t raw_pressure_, raw_temperature_, c00_, c10_; int16_t c0_, c1_, c01_, c11_, c20_, c21_, c30_; uint8_t prod_rev_id_; diff --git a/esphome/components/ens210/ens210.h b/esphome/components/ens210/ens210.h index 342be04799..0fb6ff634d 100644 --- a/esphome/components/ens210/ens210.h +++ b/esphome/components/ens210/ens210.h @@ -31,8 +31,8 @@ class ENS210Component : public PollingComponent, public i2c::I2CDevice { bool set_low_power_(bool enable); void extract_measurement_(uint32_t val, int *data, int *status); - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace ens210 diff --git a/esphome/components/hdc1080/hdc1080.h b/esphome/components/hdc1080/hdc1080.h index 9cb87cdb8b..2ff7b6dc33 100644 --- a/esphome/components/hdc1080/hdc1080.h +++ b/esphome/components/hdc1080/hdc1080.h @@ -21,8 +21,8 @@ class HDC1080Component : public PollingComponent, public i2c::I2CDevice { float get_setup_priority() const override; protected: - sensor::Sensor *temperature_; - sensor::Sensor *humidity_; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *humidity_{nullptr}; }; } // namespace hdc1080 diff --git a/esphome/components/hmc5883l/hmc5883l.h b/esphome/components/hmc5883l/hmc5883l.h index 41d41baa22..3481f45dc8 100644 --- a/esphome/components/hmc5883l/hmc5883l.h +++ b/esphome/components/hmc5883l/hmc5883l.h @@ -54,10 +54,10 @@ class HMC5883LComponent : public PollingComponent, public i2c::I2CDevice { HMC5883LOversampling oversampling_{HMC5883L_OVERSAMPLING_1}; HMC5883LDatarate datarate_{HMC5883L_DATARATE_15_0_HZ}; HMC5883LRange range_{HMC5883L_RANGE_130_UT}; - sensor::Sensor *x_sensor_; - sensor::Sensor *y_sensor_; - sensor::Sensor *z_sensor_; - sensor::Sensor *heading_sensor_; + sensor::Sensor *x_sensor_{nullptr}; + sensor::Sensor *y_sensor_{nullptr}; + sensor::Sensor *z_sensor_{nullptr}; + sensor::Sensor *heading_sensor_{nullptr}; enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, diff --git a/esphome/components/honeywellabp/honeywellabp.h b/esphome/components/honeywellabp/honeywellabp.h index 44d5952ca6..98f6f08c4a 100644 --- a/esphome/components/honeywellabp/honeywellabp.h +++ b/esphome/components/honeywellabp/honeywellabp.h @@ -29,8 +29,8 @@ class HONEYWELLABPSensor : public PollingComponent, uint8_t status_ = 0; // byte to hold status information. int pressure_count_ = 0; // hold raw pressure data (14 - bit, 0 - 16384) int temperature_count_ = 0; // hold raw temperature data (11 - bit, 0 - 2048) - sensor::Sensor *pressure_sensor_; - sensor::Sensor *temperature_sensor_; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; uint8_t readsensor_(); uint8_t readstatus_(); int rawpressure_(); diff --git a/esphome/components/ms5611/ms5611.h b/esphome/components/ms5611/ms5611.h index b5663ad736..476db79612 100644 --- a/esphome/components/ms5611/ms5611.h +++ b/esphome/components/ms5611/ms5611.h @@ -22,8 +22,8 @@ class MS5611Component : public PollingComponent, public i2c::I2CDevice { void read_pressure_(uint32_t raw_temperature); void calculate_values_(uint32_t raw_temperature, uint32_t raw_pressure); - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; uint16_t prom_[6]; }; diff --git a/esphome/components/pzem004t/pzem004t.h b/esphome/components/pzem004t/pzem004t.h index f4f9f29b4d..e18413f35c 100644 --- a/esphome/components/pzem004t/pzem004t.h +++ b/esphome/components/pzem004t/pzem004t.h @@ -23,10 +23,10 @@ class PZEM004T : public PollingComponent, public uart::UARTDevice { void dump_config() override; protected: - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_; - sensor::Sensor *power_sensor_; - sensor::Sensor *energy_sensor_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; enum PZEM004TReadState { SET_ADDRESS = 0xB4, diff --git a/esphome/components/pzemac/pzemac.h b/esphome/components/pzemac/pzemac.h index e9f76972a3..8f2cf1460d 100644 --- a/esphome/components/pzemac/pzemac.h +++ b/esphome/components/pzemac/pzemac.h @@ -27,12 +27,12 @@ class PZEMAC : public PollingComponent, public modbus::ModbusDevice { protected: template friend class ResetEnergyAction; - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_; - sensor::Sensor *power_sensor_; - sensor::Sensor *energy_sensor_; - sensor::Sensor *frequency_sensor_; - sensor::Sensor *power_factor_sensor_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; + sensor::Sensor *frequency_sensor_{nullptr}; + sensor::Sensor *power_factor_sensor_{nullptr}; void reset_energy_(); }; diff --git a/esphome/components/pzemdc/pzemdc.h b/esphome/components/pzemdc/pzemdc.h index d838eb4167..a78a48a6fb 100644 --- a/esphome/components/pzemdc/pzemdc.h +++ b/esphome/components/pzemdc/pzemdc.h @@ -22,11 +22,11 @@ class PZEMDC : public PollingComponent, public modbus::ModbusDevice { void dump_config() override; protected: - sensor::Sensor *voltage_sensor_; - sensor::Sensor *current_sensor_; - sensor::Sensor *power_sensor_; - sensor::Sensor *frequency_sensor_; - sensor::Sensor *power_factor_sensor_; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *frequency_sensor_{nullptr}; + sensor::Sensor *power_factor_sensor_{nullptr}; }; } // namespace pzemdc diff --git a/esphome/components/qmc5883l/qmc5883l.h b/esphome/components/qmc5883l/qmc5883l.h index 01697ecbd0..15ef435ce5 100644 --- a/esphome/components/qmc5883l/qmc5883l.h +++ b/esphome/components/qmc5883l/qmc5883l.h @@ -45,10 +45,10 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice { QMC5883LDatarate datarate_{QMC5883L_DATARATE_10_HZ}; QMC5883LRange range_{QMC5883L_RANGE_200_UT}; QMC5883LOversampling oversampling_{QMC5883L_SAMPLING_512}; - sensor::Sensor *x_sensor_; - sensor::Sensor *y_sensor_; - sensor::Sensor *z_sensor_; - sensor::Sensor *heading_sensor_; + sensor::Sensor *x_sensor_{nullptr}; + sensor::Sensor *y_sensor_{nullptr}; + sensor::Sensor *z_sensor_{nullptr}; + sensor::Sensor *heading_sensor_{nullptr}; enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, diff --git a/esphome/components/qmp6988/qmp6988.h b/esphome/components/qmp6988/qmp6988.h index ef944ba4ff..f0c11adf43 100644 --- a/esphome/components/qmp6988/qmp6988.h +++ b/esphome/components/qmp6988/qmp6988.h @@ -91,8 +91,8 @@ class QMP6988Component : public PollingComponent, public i2c::I2CDevice { protected: qmp6988_data_t qmp6988_data_; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; QMP6988Oversampling temperature_oversampling_{QMP6988_OVERSAMPLING_16X}; QMP6988Oversampling pressure_oversampling_{QMP6988_OVERSAMPLING_16X}; diff --git a/esphome/components/sht3xd/sht3xd.h b/esphome/components/sht3xd/sht3xd.h index 3164aa0687..41ca3c5d6e 100644 --- a/esphome/components/sht3xd/sht3xd.h +++ b/esphome/components/sht3xd/sht3xd.h @@ -19,8 +19,8 @@ class SHT3XDComponent : public PollingComponent, public sensirion_common::Sensir void update() override; protected: - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace sht3xd diff --git a/esphome/components/shtcx/shtcx.h b/esphome/components/shtcx/shtcx.h index c44fb9d9c1..084d3bfc35 100644 --- a/esphome/components/shtcx/shtcx.h +++ b/esphome/components/shtcx/shtcx.h @@ -26,8 +26,8 @@ class SHTCXComponent : public PollingComponent, public sensirion_common::Sensiri protected: SHTCXType type_; uint16_t sensor_id_; - sensor::Sensor *temperature_sensor_; - sensor::Sensor *humidity_sensor_; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; }; } // namespace shtcx diff --git a/esphome/components/tsl2591/tsl2591.h b/esphome/components/tsl2591/tsl2591.h index d82dbc395f..5b7eea35ec 100644 --- a/esphome/components/tsl2591/tsl2591.h +++ b/esphome/components/tsl2591/tsl2591.h @@ -245,10 +245,10 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice { protected: const char *name_; - sensor::Sensor *full_spectrum_sensor_; - sensor::Sensor *infrared_sensor_; - sensor::Sensor *visible_sensor_; - sensor::Sensor *calculated_lux_sensor_; + sensor::Sensor *full_spectrum_sensor_{nullptr}; + sensor::Sensor *infrared_sensor_{nullptr}; + sensor::Sensor *visible_sensor_{nullptr}; + sensor::Sensor *calculated_lux_sensor_{nullptr}; TSL2591IntegrationTime integration_time_; TSL2591ComponentGain component_gain_; TSL2591Gain gain_; diff --git a/esphome/components/tx20/tx20.h b/esphome/components/tx20/tx20.h index 1c617d0674..95a9517227 100644 --- a/esphome/components/tx20/tx20.h +++ b/esphome/components/tx20/tx20.h @@ -43,8 +43,8 @@ class Tx20Component : public Component { std::string wind_cardinal_direction_; InternalGPIOPin *pin_; - sensor::Sensor *wind_speed_sensor_; - sensor::Sensor *wind_direction_degrees_sensor_; + sensor::Sensor *wind_speed_sensor_{nullptr}; + sensor::Sensor *wind_direction_degrees_sensor_{nullptr}; Tx20ComponentStore store_; }; From 625a575e49f362578a9ea6acef7d83538fa66b9d Mon Sep 17 00:00:00 2001 From: Azimath Date: Wed, 14 Sep 2022 19:54:33 -0400 Subject: [PATCH 39/69] Remove floating point calculation from ac_dimmer ISR (#3770) --- esphome/components/ac_dimmer/ac_dimmer.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 1d0cd8d0ab..16101a1c2c 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -121,11 +121,8 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() { // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic // also take into account min_power auto min_us = this->cycle_time_us * this->min_power / 1000; - // calculate required value to provide a true RMS voltage output - this->enable_time_us = - std::max((uint32_t) 1, (uint32_t)((65535 - (acos(1 - (2 * this->value / 65535.0)) / 3.14159 * 65535)) * - (this->cycle_time_us - min_us)) / - 65535); + this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); + if (this->method == DIM_METHOD_LEADING_PULSE) { // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone // this is for brightness near 99% @@ -206,6 +203,7 @@ void AcDimmer::setup() { #endif } void AcDimmer::write_state(float state) { + state = std::acos(1 - (2 * state)) / 3.14159; // RMS power compensation auto new_value = static_cast(roundf(state * 65535)); if (new_value != 0 && this->store_.value == 0) this->store_.init_cycle = this->init_with_half_cycle_; From 7deabbb51232da5d0ab85744cd6e61431e5c3666 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:02:40 +1200 Subject: [PATCH 40/69] Bump version to 2022.9.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 608a61466b..5ce1edb8cd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0b1" +__version__ = "2022.9.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From fb9984e21f54dabab4563b83c5f14dce4ac3128e Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Thu, 15 Sep 2022 12:27:50 -0700 Subject: [PATCH 41/69] split pronto codes if they are too long (#3812) Co-authored-by: Samuel Sieb --- esphome/components/remote_base/pronto_protocol.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index d434744e49..d8798d4ab9 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -227,7 +227,18 @@ optional ProntoProtocol::decode(RemoteReceiveData src) { return out; } -void ProntoProtocol::dump(const ProntoData &data) { ESP_LOGD(TAG, "Received Pronto: data=%s", data.data.c_str()); } +void ProntoProtocol::dump(const ProntoData &data) { + std::string first, rest; + if (data.data.size() < 230) { + first = data.data; + } else { + first = data.data.substr(0, 229); + rest = data.data.substr(230); + } + ESP_LOGD(TAG, "Received Pronto: data=%s", first.c_str()); + if (!rest.empty()) + ESP_LOGD(TAG, "%s", rest.c_str()); +} } // namespace remote_base } // namespace esphome From 47a7a239aef236351ba547ab44d4f299f3c691ab Mon Sep 17 00:00:00 2001 From: h3ndrik Date: Sun, 18 Sep 2022 21:25:59 +0200 Subject: [PATCH 42/69] [BME280] raise standby time (#3804) --- esphome/components/bme280/bme280.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280/bme280.cpp index 345b24a36e..d8124f5dc3 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280/bme280.cpp @@ -163,7 +163,7 @@ void BME280Component::setup() { return; } config_register &= ~0b11111100; - config_register |= 0b000 << 5; // 0.5 ms standby time + config_register |= 0b101 << 5; // 1000 ms standby time config_register |= (this->iir_filter_ & 0b111) << 2; if (!this->write_byte(BME280_REGISTER_CONFIG, config_register)) { this->mark_failed(); From 91f1c25fccea0f7d120ead79dcdd609b2236616a Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 19 Sep 2022 20:02:55 -0500 Subject: [PATCH 43/69] Make sprinkler reset_resume() method public (#3824) --- esphome/components/sprinkler/sprinkler.cpp | 12 ++++++------ esphome/components/sprinkler/sprinkler.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index ab694c8412..2be71a08d0 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -769,7 +769,7 @@ void Sprinkler::resume() { ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0), this->resume_duration_.value_or(0)); this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value()); - this->reset_resume_(); + this->reset_resume(); } else { ESP_LOGD(TAG, "No valve to resume!"); } @@ -783,6 +783,11 @@ void Sprinkler::resume_or_start_full_cycle() { } } +void Sprinkler::reset_resume() { + this->paused_valve_.reset(); + this->resume_duration_.reset(); +} + const char *Sprinkler::valve_name(const size_t valve_number) { if (this->is_a_valid_valve(valve_number)) { return this->valve_[valve_number].controller_switch->get_name().c_str(); @@ -1101,11 +1106,6 @@ void Sprinkler::reset_cycle_states_() { } } -void Sprinkler::reset_resume_() { - this->paused_valve_.reset(); - this->resume_duration_.reset(); -} - void Sprinkler::fsm_request_(size_t requested_valve, uint32_t requested_run_duration) { this->next_req_.set_valve(requested_valve); this->next_req_.set_run_duration(requested_run_duration); diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index 1243a844fa..acd168d791 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -308,6 +308,9 @@ class Sprinkler : public Component, public EntityBase { /// if a cycle was suspended using pause(), resumes it. otherwise calls start_full_cycle() void resume_or_start_full_cycle(); + /// resets resume state + void reset_resume(); + /// returns a pointer to a valve's name string object; returns nullptr if valve_number is invalid const char *valve_name(size_t valve_number); @@ -401,9 +404,6 @@ class Sprinkler : public Component, public EntityBase { /// resets the cycle state for all valves void reset_cycle_states_(); - /// resets resume state - void reset_resume_(); - /// make a request of the state machine void fsm_request_(size_t requested_valve, uint32_t requested_run_duration = 0); From 5c4e83ebdc1f8685b0dc7a675416d75d4552f221 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 20 Sep 2022 01:13:59 -0400 Subject: [PATCH 44/69] Bump dashboard to 20220919.1 (#3828) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bd0c70bb72..a81c38c44f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 -esphome-dashboard==20220508.0 +esphome-dashboard==20220919.1 aioesphomeapi==10.13.0 zeroconf==0.39.1 From 5f56cf312848c5e20f0c25463e768f90a3a372da Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 20 Sep 2022 17:14:51 +1200 Subject: [PATCH 45/69] Bump version to 2022.9.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5ce1edb8cd..02cbe02ba2 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0b2" +__version__ = "2022.9.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 33f296e05b11786c48bfbe8cbb1bed9a6c559572 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 20 Sep 2022 02:23:55 -0300 Subject: [PATCH 46/69] Fix-esphome-validation-line-number (#3815) --- esphome/config.py | 22 +++++++++++++++------- esphome/core/config.py | 6 +++++- esphome/vscode.py | 4 +++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index 56fe75a4c7..04717be6f5 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -165,15 +165,19 @@ class Config(OrderedDict, fv.FinalValidateConfig): return err return None - def get_deepest_document_range_for_path(self, path): - # type: (ConfigPath) -> Optional[ESPHomeDataBase] + def get_deepest_document_range_for_path(self, path, get_key=False): + # type: (ConfigPath, bool) -> Optional[ESPHomeDataBase] data = self doc_range = None - for item_index in path: + for index, path_item in enumerate(path): try: - if item_index in data: - doc_range = [x for x in data.keys() if x == item_index][0].esp_range - data = data[item_index] + if path_item in data: + key_data = [x for x in data.keys() if x == path_item][0] + if isinstance(key_data, ESPHomeDataBase): + doc_range = key_data.esp_range + if get_key and index == len(path) - 1: + return doc_range + data = data[path_item] except (KeyError, IndexError, TypeError, AttributeError): return doc_range if isinstance(data, core.ID): @@ -281,7 +285,7 @@ class ConfigValidationStep(abc.ABC): class LoadValidationStep(ConfigValidationStep): """Load step, this step is called once for each domain config fragment. - Responsibilties: + Responsibilities: - Load component code - Ensure all AUTO_LOADs are added - Set output paths of result @@ -738,6 +742,10 @@ def validate_config(config, command_line_substitutions) -> Config: result.add_validation_step(LoadValidationStep(key, config[key])) result.run_validation_steps() + if result.errors: + # do not try to validate further as we don't know what the target is + return result + for domain, conf in config.items(): result.add_validation_step(LoadValidationStep(domain, conf)) result.add_validation_step(IDPassValidationStep()) diff --git a/esphome/core/config.py b/esphome/core/config.py index f1337be04b..82cf37d44d 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -179,7 +179,11 @@ def preload_core_config(config, result): ] if not has_oldstyle and not newstyle_found: - raise cv.Invalid("Platform missing for core options!", [CONF_ESPHOME]) + raise cv.Invalid( + "Platform missing. You must include one of the available platform keys: " + + ", ".join(TARGET_PLATFORMS), + [CONF_ESPHOME], + ) if has_oldstyle and newstyle_found: raise cv.Invalid( f"Please remove the `platform` key from the [esphome] block. You're already using the new style with the [{conf[CONF_PLATFORM]}] block", diff --git a/esphome/vscode.py b/esphome/vscode.py index 68d59abd02..6a43a654ed 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -12,7 +12,9 @@ from typing import Optional def _get_invalid_range(res, invalid): # type: (Config, cv.Invalid) -> Optional[DocumentRange] - return res.get_deepest_document_range_for_path(invalid.path) + return res.get_deepest_document_range_for_path( + invalid.path, invalid.error_message == "extra keys not allowed" + ) def _dump_range(range): From d56107e97fe72de58c26b2667cc42e9d08bc8fbc Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 20 Sep 2022 15:35:46 -0400 Subject: [PATCH 47/69] Bump dashboard to 20220920.0 (#3831) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a81c38c44f..0a667bef10 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 -esphome-dashboard==20220919.1 +esphome-dashboard==20220920.0 aioesphomeapi==10.13.0 zeroconf==0.39.1 From 63b42f36080c17ab51fc57325e2fd61dc71eac4a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Sep 2022 07:36:39 +1200 Subject: [PATCH 48/69] Bump version to 2022.9.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 02cbe02ba2..0fe271cd5a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0b3" +__version__ = "2022.9.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From fd6135aebb1199dfd4029bfe53f3343c7dcb14f7 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 20 Sep 2022 21:32:53 -0300 Subject: [PATCH 49/69] Bump dashboard to 20220920.1 (#3834) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0a667bef10..55544e442e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 -esphome-dashboard==20220920.0 +esphome-dashboard==20220920.1 aioesphomeapi==10.13.0 zeroconf==0.39.1 From 91560ae4e9613e78c3265c6d637e6f7ee530f2af Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Sep 2022 12:38:55 +1200 Subject: [PATCH 50/69] Bump version to 2022.9.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 0fe271cd5a..290e5e2994 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0b4" +__version__ = "2022.9.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 9655362f238bacc6e24a4c5446fb345f4d8afe8d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Sep 2022 15:34:02 +1200 Subject: [PATCH 51/69] Bump version to 2022.9.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 290e5e2994..41057f3d29 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0b5" +__version__ = "2022.9.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 34bef2f2ca73f5e73796ea62671dccb97de48e60 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Sep 2022 07:38:31 +1200 Subject: [PATCH 52/69] Revert "fix spi timing issues" (#3838) --- esphome/components/spi/spi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 6c92321ac8..7f0b0f481a 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -195,11 +195,6 @@ class SPIComponent : public Component { template void enable(GPIOPin *cs) { - if (cs != nullptr) { - this->active_cs_ = cs; - this->active_cs_->digital_write(false); - } - #ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { uint8_t data_mode = SPI_MODE0; @@ -220,6 +215,11 @@ class SPIComponent : public Component { #ifdef USE_SPI_ARDUINO_BACKEND } #endif // USE_SPI_ARDUINO_BACKEND + + if (cs != nullptr) { + this->active_cs_ = cs; + this->active_cs_->digital_write(false); + } } void disable(); From 66226abb4877de61261b4bc5553ea7caf5664a4f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 22 Sep 2022 07:40:15 +1200 Subject: [PATCH 53/69] Bump version to 2022.9.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 41057f3d29..ce5a51aa71 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.0" +__version__ = "2022.9.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From efdb3d1f40eb0750b15ff2e3894aea0ac8ba28d3 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 25 Sep 2022 18:16:30 -0300 Subject: [PATCH 54/69] Bump dashboard to 20220925.0 (#3846) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 55544e442e..7f86acdbef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 -esphome-dashboard==20220920.1 +esphome-dashboard==20220925.0 aioesphomeapi==10.13.0 zeroconf==0.39.1 From d2c1c7507cbb42dd1bfa857006b3db9f6432f4d1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:00:55 +1300 Subject: [PATCH 55/69] Bump version to 2022.9.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ce5a51aa71..a272f518df 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.1" +__version__ = "2022.9.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 1001d9c04e738d619c8a76796a55abc1a3549367 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 4 Oct 2022 10:45:06 +1300 Subject: [PATCH 56/69] Bluetooth Proxy active connections (#3817) --- CODEOWNERS | 1 + .../airthings_wave_mini.cpp | 6 +- .../airthings_wave_plus.cpp | 6 +- esphome/components/am43/am43.cpp | 10 +- esphome/components/am43/cover/am43_cover.cpp | 21 +- esphome/components/anova/anova.cpp | 22 +- esphome/components/api/api.proto | 177 +++- esphome/components/api/api_connection.cpp | 72 +- esphome/components/api/api_connection.h | 31 +- esphome/components/api/api_pb2.cpp | 764 +++++++++++++++++- esphome/components/api/api_pb2.h | 231 +++++- esphome/components/api/api_pb2_service.cpp | 285 ++++++- esphome/components/api/api_pb2_service.h | 95 ++- esphome/components/api/api_server.cpp | 45 ++ esphome/components/api/api_server.h | 6 + esphome/components/bedjet/bedjet_hub.cpp | 48 +- esphome/components/bedjet/bedjet_hub.h | 2 - esphome/components/ble_client/__init__.py | 7 +- esphome/components/ble_client/automation.cpp | 6 +- esphome/components/ble_client/automation.h | 3 +- esphome/components/ble_client/ble_client.cpp | 409 +--------- esphome/components/ble_client/ble_client.h | 86 +- .../components/ble_client/sensor/automation.h | 3 +- .../ble_client/sensor/ble_sensor.cpp | 12 +- .../ble_client/text_sensor/automation.h | 3 +- .../text_sensor/ble_text_sensor.cpp | 12 +- .../components/bluetooth_proxy/__init__.py | 16 +- .../bluetooth_proxy/bluetooth_proxy.cpp | 323 +++++++- .../bluetooth_proxy/bluetooth_proxy.h | 39 +- .../components/esp32_ble_client/__init__.py | 12 + .../esp32_ble_client/ble_characteristic.cpp | 83 ++ .../esp32_ble_client/ble_characteristic.h | 35 + .../esp32_ble_client/ble_client_base.cpp | 324 ++++++++ .../esp32_ble_client/ble_client_base.h | 72 ++ .../esp32_ble_client/ble_descriptor.h | 25 + .../esp32_ble_client/ble_service.cpp | 66 ++ .../components/esp32_ble_client/ble_service.h | 32 + .../components/esp32_ble_tracker/__init__.py | 2 +- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 37 +- .../esp32_ble_tracker/esp32_ble_tracker.h | 5 +- esphome/components/esp32_ble_tracker/queue.h | 10 +- .../display/pvvx_display.cpp | 5 +- .../radon_eye_rd200/radon_eye_rd200.cpp | 12 +- esphome/const.py | 1 + 44 files changed, 2818 insertions(+), 644 deletions(-) create mode 100644 esphome/components/esp32_ble_client/__init__.py create mode 100644 esphome/components/esp32_ble_client/ble_characteristic.cpp create mode 100644 esphome/components/esp32_ble_client/ble_characteristic.h create mode 100644 esphome/components/esp32_ble_client/ble_client_base.cpp create mode 100644 esphome/components/esp32_ble_client/ble_client_base.h create mode 100644 esphome/components/esp32_ble_client/ble_descriptor.h create mode 100644 esphome/components/esp32_ble_client/ble_service.cpp create mode 100644 esphome/components/esp32_ble_client/ble_service.h diff --git a/CODEOWNERS b/CODEOWNERS index 69e30027a9..d95ebcce59 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -70,6 +70,7 @@ esphome/components/ektf2232/* @jesserockz esphome/components/ens210/* @itn3rd77 esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @jesserockz +esphome/components/esp32_ble_client/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_can/* @Sympatron diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp index 2e7a1fb024..40873ec005 100644 --- a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp @@ -38,7 +38,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->conn_id) + 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); @@ -88,8 +88,8 @@ void AirthingsWaveMini::update() { } void AirthingsWaveMini::request_read_values_() { - auto status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE); + 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); } diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 02ed33b87a..11f86307fe 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -38,7 +38,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->conn_id) + 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); @@ -109,8 +109,8 @@ void AirthingsWavePlus::update() { } void AirthingsWavePlus::request_read_values_() { - auto status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE); + 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); } diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index 1c4bad64c3..9a0e5999d2 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -76,9 +76,9 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i if (this->current_sensor_ > 0) { if (this->illuminance_ != nullptr) { auto *packet = this->encoder_->get_light_level_request(); - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, - packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), + this->char_handle_, packet->length, packet->data, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); @@ -102,8 +102,8 @@ void Am43::update() { if (this->battery_ != nullptr) { auto *packet = this->encoder_->get_battery_level_request(); auto status = - esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, - packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); } diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 39089e73c0..ba8e350732 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -27,8 +27,8 @@ void Am43Component::loop() { if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) { auto *packet = this->encoder_->get_send_pin_request(this->pin_); auto status = - esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, - packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str()); if (status) { ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status); @@ -54,8 +54,8 @@ void Am43Component::control(const CoverCall &call) { if (call.get_stop()) { auto *packet = this->encoder_->get_stop_request(); auto status = - esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, - packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status); } @@ -66,8 +66,8 @@ void Am43Component::control(const CoverCall &call) { pos = 1 - pos; auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100)); auto status = - esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, - packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status); } @@ -92,7 +92,8 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } this->char_handle_ = chr->handle; - auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle); + auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), + chr->handle); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status); } @@ -122,9 +123,9 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ if (this->decoder_->pin_ok_) { ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str()); auto *packet = this->encoder_->get_position_request(); - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, - packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), + this->char_handle_, packet->length, packet->data, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status); } else { diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index 6c8316d338..cafd30149d 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -34,15 +34,17 @@ void Anova::control(const ClimateCall &call) { ESP_LOGW(TAG, "Unsupported mode: %d", mode); return; } - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, - pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + auto status = + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); } if (call.get_target_temperature().has_value()) { auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature()); - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, - pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + auto status = + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); } @@ -65,7 +67,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ } this->char_handle_ = chr->handle; - auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle); + auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), + chr->handle); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status); } @@ -112,8 +115,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ } if (pkt != nullptr) { auto status = - esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, - pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); @@ -137,8 +140,9 @@ void Anova::update() { auto *pkt = this->codec_->get_read_device_status_request(); if (this->current_request_ == 0) this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c'); - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, - pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + auto status = + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); this->current_request_++; diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index c6cde8b038..9fa77d2daa 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -27,7 +27,6 @@ service APIConnection { rpc subscribe_logs (SubscribeLogsRequest) returns (void) {} rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {} rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {} - rpc subscribe_bluetooth_le_advertisements (SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc get_time (GetTimeRequest) returns (GetTimeResponse) { option (needs_authentication) = false; } @@ -44,6 +43,16 @@ service APIConnection { rpc button_command (ButtonCommandRequest) returns (void) {} rpc lock_command (LockCommandRequest) returns (void) {} rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} + + rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} + rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {} + rpc bluetooth_gatt_get_services(BluetoothGATTGetServicesRequest) returns (void) {} + rpc bluetooth_gatt_read(BluetoothGATTReadRequest) returns (void) {} + rpc bluetooth_gatt_write(BluetoothGATTWriteRequest) returns (void) {} + rpc bluetooth_gatt_read_descriptor(BluetoothGATTReadDescriptorRequest) returns (void) {} + rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {} + rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {} + rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {} } @@ -78,6 +87,8 @@ message HelloRequest { // Not strictly necessary to send but nice for debugging // purposes. string client_info = 1; + uint32 api_version_major = 2; + uint32 api_version_minor = 3; } // Confirmation of successful connection request. @@ -192,7 +203,7 @@ message DeviceInfoResponse { uint32 webserver_port = 10; - bool has_bluetooth_proxy = 11; + uint32 bluetooth_proxy_version = 11; } message ListEntitiesRequest { @@ -1111,7 +1122,8 @@ message SubscribeBluetoothLEAdvertisementsRequest { message BluetoothServiceData { string uuid = 1; - repeated uint32 data = 2 [packed=false]; + repeated uint32 legacy_data = 2 [deprecated = true]; + bytes data = 3; // Changed in proto version 1.7 } message BluetoothLEAdvertisementResponse { option (id) = 67; @@ -1127,3 +1139,162 @@ message BluetoothLEAdvertisementResponse { repeated BluetoothServiceData service_data = 5; repeated BluetoothServiceData manufacturer_data = 6; } + +enum BluetoothDeviceRequestType { + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0; + BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1; + BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2; + BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; +} + +message BluetoothDeviceRequest { + option (id) = 68; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + BluetoothDeviceRequestType request_type = 2; +} + +message BluetoothDeviceConnectionResponse { + option (id) = 69; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + bool connected = 2; + uint32 mtu = 3; + int32 error = 4; +} + +message BluetoothGATTGetServicesRequest { + option (id) = 70; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; +} + +message BluetoothGATTDescriptor { + repeated uint64 uuid = 1; + uint32 handle = 2; +} + +message BluetoothGATTCharacteristic { + repeated uint64 uuid = 1; + uint32 handle = 2; + uint32 properties = 3; + repeated BluetoothGATTDescriptor descriptors = 4; +} + +message BluetoothGATTService { + repeated uint64 uuid = 1; + uint32 handle = 2; + repeated BluetoothGATTCharacteristic characteristics = 3; +} + +message BluetoothGATTGetServicesResponse { + option (id) = 71; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + repeated BluetoothGATTService services = 2; +} + +message BluetoothGATTGetServicesDoneResponse { + option (id) = 72; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; +} + +message BluetoothGATTReadRequest { + option (id) = 73; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; +} + +message BluetoothGATTReadResponse { + option (id) = 74; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + + bytes data = 3; + +} + +message BluetoothGATTWriteRequest { + option (id) = 75; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + bool response = 3; + + bytes data = 4; +} + +message BluetoothGATTReadDescriptorRequest { + option (id) = 76; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; +} + +message BluetoothGATTWriteDescriptorRequest { + option (id) = 77; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + + bytes data = 3; +} + +message BluetoothGATTNotifyRequest { + option (id) = 78; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + bool enable = 3; +} + +message BluetoothGATTNotifyDataResponse { + option (id) = 79; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + + bytes data = 3; +} + +message SubscribeBluetoothConnectionsFreeRequest { + option (id) = 80; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_BLUETOOTH_PROXY"; +} + +message BluetoothConnectionsFreeResponse { + option (id) = 81; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint32 free = 1; + uint32 limit = 2; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index a3b000c778..1dbf0abec4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,10 +1,10 @@ #include "api_connection.h" -#include "esphome/core/entity_base.h" -#include "esphome/core/log.h" -#include "esphome/components/network/util.h" -#include "esphome/core/version.h" -#include "esphome/core/hal.h" #include +#include "esphome/components/network/util.h" +#include "esphome/core/entity_base.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/version.h" #ifdef USE_DEEP_SLEEP #include "esphome/components/deep_sleep/deep_sleep_component.h" @@ -12,6 +12,9 @@ #ifdef USE_HOMEASSISTANT_TIME #include "esphome/components/homeassistant/time/homeassistant_time.h" #endif +#ifdef USE_BLUETOOTH_PROXY +#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" +#endif namespace esphome { namespace api { @@ -823,6 +826,56 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) { } #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) { + if (!this->bluetooth_le_advertisement_subscription_) + return false; + if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) { + BluetoothLEAdvertisementResponse resp = msg; + for (auto &service : resp.service_data) { + service.legacy_data.assign(service.data.begin(), service.data.end()); + service.data.clear(); + } + for (auto &manufacturer_data : resp.manufacturer_data) { + manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end()); + manufacturer_data.data.clear(); + } + return this->send_bluetooth_le_advertisement_response(resp); + } + return this->send_bluetooth_le_advertisement_response(msg); +} +void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg); +} +void APIConnection::bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read(msg); +} +void APIConnection::bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write(msg); +} +void APIConnection::bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read_descriptor(msg); +} +void APIConnection::bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write_descriptor(msg); +} +void APIConnection::bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_send_services(msg); +} + +void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg); +} + +BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free( + const SubscribeBluetoothConnectionsFreeRequest &msg) { + BluetoothConnectionsFreeResponse resp; + resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free(); + resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit(); + return resp; +} +#endif + bool APIConnection::send_log_message(int level, const char *tag, const char *line) { if (this->log_subscription_ < level) return false; @@ -840,11 +893,14 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin HelloResponse APIConnection::hello(const HelloRequest &msg) { this->client_info_ = msg.client_info + " (" + this->helper_->getpeername() + ")"; this->helper_->set_log_info(client_info_); - ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str()); + this->client_api_version_major_ = msg.api_version_major; + this->client_api_version_minor_ = msg.api_version_minor; + ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(), + this->client_api_version_major_, this->client_api_version_minor_); HelloResponse resp; resp.api_version_major = 1; - resp.api_version_minor = 6; + resp.api_version_minor = 7; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.name = App.get_name(); @@ -888,7 +944,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.webserver_port = USE_WEBSERVER_PORT; #endif #ifdef USE_BLUETOOTH_PROXY - resp.has_bluetooth_proxy = true; + resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 2 : 1; #endif return resp; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index dcf8bacad2..028fb80175 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -1,15 +1,11 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/application.h" +#include "api_frame_helper.h" #include "api_pb2.h" #include "api_pb2_service.h" #include "api_server.h" -#include "api_frame_helper.h" - -#ifdef USE_BLUETOOTH_PROXY -#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" -#endif +#include "esphome/core/application.h" +#include "esphome/core/component.h" namespace esphome { namespace api { @@ -99,11 +95,18 @@ class APIConnection : public APIServerConnection { this->send_homeassistant_service_response(call); } #ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) { - if (!this->bluetooth_le_advertisement_subscription_) - return false; - return this->send_bluetooth_le_advertisement_response(call); - } + bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg); + + void bluetooth_device_request(const BluetoothDeviceRequest &msg) override; + void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override; + void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) override; + void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) override; + void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override; + void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override; + void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override; + BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( + const SubscribeBluetoothConnectionsFreeRequest &msg) override; + #endif #ifdef USE_HOMEASSISTANT_TIME void send_time_request() { @@ -181,6 +184,8 @@ class APIConnection : public APIServerConnection { std::unique_ptr helper_; std::string client_info_; + uint32_t client_api_version_major_{0}; + uint32_t client_api_version_minor_{0}; #ifdef USE_ESP32_CAMERA esp32_camera::CameraImageReader image_reader_; #endif @@ -190,7 +195,7 @@ class APIConnection : public APIServerConnection { uint32_t last_traffic_; bool sent_ping_{false}; bool service_call_subscription_{false}; - bool bluetooth_le_advertisement_subscription_{true}; + bool bluetooth_le_advertisement_subscription_{false}; bool next_close_ = false; APIServer *parent_; InitialStateIterator initial_state_iterator_; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 13969fce76..73d8044cde 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -340,6 +340,35 @@ template<> const char *proto_enum_to_string(enums::Me return "UNKNOWN"; } } +template<> +const char *proto_enum_to_string(enums::BluetoothDeviceRequestType value) { + switch (value) { + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR"; + default: + return "UNKNOWN"; + } +} +bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->api_version_major = value.as_uint32(); + return true; + } + case 3: { + this->api_version_minor = value.as_uint32(); + return true; + } + default: + return false; + } +} bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { @@ -350,7 +379,11 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) return false; } } -void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); } +void HelloRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->client_info); + buffer.encode_uint32(2, this->api_version_major); + buffer.encode_uint32(3, this->api_version_minor); +} #ifdef HAS_PROTO_MESSAGE_DUMP void HelloRequest::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; @@ -358,6 +391,16 @@ void HelloRequest::dump_to(std::string &out) const { out.append(" client_info: "); out.append("'").append(this->client_info).append("'"); out.append("\n"); + + out.append(" api_version_major: "); + sprintf(buffer, "%u", this->api_version_major); + out.append(buffer); + out.append("\n"); + + out.append(" api_version_minor: "); + sprintf(buffer, "%u", this->api_version_minor); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -496,7 +539,7 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 11: { - this->has_bluetooth_proxy = value.as_bool(); + this->bluetooth_proxy_version = value.as_uint32(); return true; } default: @@ -548,7 +591,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(8, this->project_name); buffer.encode_string(9, this->project_version); buffer.encode_uint32(10, this->webserver_port); - buffer.encode_bool(11, this->has_bluetooth_proxy); + buffer.encode_uint32(11, this->bluetooth_proxy_version); } #ifdef HAS_PROTO_MESSAGE_DUMP void DeviceInfoResponse::dump_to(std::string &out) const { @@ -595,8 +638,9 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append(buffer); out.append("\n"); - out.append(" has_bluetooth_proxy: "); - out.append(YESNO(this->has_bluetooth_proxy)); + out.append(" bluetooth_proxy_version: "); + sprintf(buffer, "%u", this->bluetooth_proxy_version); + out.append(buffer); out.append("\n"); out.append("}"); } @@ -4872,7 +4916,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->data.push_back(value.as_uint32()); + this->legacy_data.push_back(value.as_uint32()); return true; } default: @@ -4885,15 +4929,20 @@ bool BluetoothServiceData::decode_length(uint32_t field_id, ProtoLengthDelimited this->uuid = value.as_string(); return true; } + case 3: { + this->data = value.as_string(); + return true; + } default: return false; } } void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->uuid); - for (auto &it : this->data) { + for (auto &it : this->legacy_data) { buffer.encode_uint32(2, it, true); } + buffer.encode_string(3, this->data); } #ifdef HAS_PROTO_MESSAGE_DUMP void BluetoothServiceData::dump_to(std::string &out) const { @@ -4903,12 +4952,16 @@ void BluetoothServiceData::dump_to(std::string &out) const { out.append("'").append(this->uuid).append("'"); out.append("\n"); - for (const auto &it : this->data) { - out.append(" data: "); + for (const auto &it : this->legacy_data) { + out.append(" legacy_data: "); sprintf(buffer, "%u", it); out.append(buffer); out.append("\n"); } + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -5000,6 +5053,699 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->request_type = value.as_enum(); + return true; + } + default: + return false; + } +} +void BluetoothDeviceRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_enum(2, this->request_type); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothDeviceRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothDeviceRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" request_type: "); + out.append(proto_enum_to_string(this->request_type)); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothDeviceConnectionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->connected = value.as_bool(); + return true; + } + case 3: { + this->mtu = value.as_uint32(); + return true; + } + case 4: { + this->error = value.as_int32(); + return true; + } + default: + return false; + } +} +void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_bool(2, this->connected); + buffer.encode_uint32(3, this->mtu); + buffer.encode_int32(4, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothDeviceConnectionResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" connected: "); + out.append(YESNO(this->connected)); + out.append("\n"); + + out.append(" mtu: "); + sprintf(buffer, "%u", this->mtu); + out.append(buffer); + out.append("\n"); + + out.append(" error: "); + sprintf(buffer, "%d", this->error); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + default: + return false; + } +} +void BluetoothGATTGetServicesRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); } +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTGetServicesRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTGetServicesRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTDescriptor::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->uuid.push_back(value.as_uint64()); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTDescriptor::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->uuid) { + buffer.encode_uint64(1, it, true); + } + buffer.encode_uint32(2, this->handle); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTDescriptor::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTDescriptor {\n"); + for (const auto &it : this->uuid) { + out.append(" uuid: "); + sprintf(buffer, "%llu", it); + out.append(buffer); + out.append("\n"); + } + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTCharacteristic::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->uuid.push_back(value.as_uint64()); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + case 3: { + this->properties = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTCharacteristic::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 4: { + this->descriptors.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->uuid) { + buffer.encode_uint64(1, it, true); + } + buffer.encode_uint32(2, this->handle); + buffer.encode_uint32(3, this->properties); + for (auto &it : this->descriptors) { + buffer.encode_message(4, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTCharacteristic::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTCharacteristic {\n"); + for (const auto &it : this->uuid) { + out.append(" uuid: "); + sprintf(buffer, "%llu", it); + out.append(buffer); + out.append("\n"); + } + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" properties: "); + sprintf(buffer, "%u", this->properties); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->descriptors) { + out.append(" descriptors: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +#endif +bool BluetoothGATTService::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->uuid.push_back(value.as_uint64()); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTService::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 3: { + this->characteristics.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->uuid) { + buffer.encode_uint64(1, it, true); + } + buffer.encode_uint32(2, this->handle); + for (auto &it : this->characteristics) { + buffer.encode_message(3, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTService::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTService {\n"); + for (const auto &it : this->uuid) { + out.append(" uuid: "); + sprintf(buffer, "%llu", it); + out.append(buffer); + out.append("\n"); + } + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->characteristics) { + out.append(" characteristics: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +#endif +bool BluetoothGATTGetServicesResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTGetServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->services.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + for (auto &it : this->services) { + buffer.encode_message(2, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTGetServicesResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTGetServicesResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + for (const auto &it : this->services) { + out.append(" services: "); + it.dump_to(out); + out.append("\n"); + } + out.append("}"); +} +#endif +bool BluetoothGATTGetServicesDoneResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + default: + return false; + } +} +void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTGetServicesDoneResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTGetServicesDoneResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTReadRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTReadRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTReadRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTReadResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTReadResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 3: { + this->data = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_string(3, this->data); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTReadResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTReadResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + case 3: { + this->response = value.as_bool(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTWriteRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 4: { + this->data = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothGATTWriteRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_bool(3, this->response); + buffer.encode_string(4, this->data); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTWriteRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTWriteRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" response: "); + out.append(YESNO(this->response)); + out.append("\n"); + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTReadDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTReadDescriptorRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTReadDescriptorRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTWriteDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTWriteDescriptorRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 3: { + this->data = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothGATTWriteDescriptorRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_string(3, this->data); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTWriteDescriptorRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + case 3: { + this->enable = value.as_bool(); + return true; + } + default: + return false; + } +} +void BluetoothGATTNotifyRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_bool(3, this->enable); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTNotifyRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTNotifyRequest {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" enable: "); + out.append(YESNO(this->enable)); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTNotifyDataResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool BluetoothGATTNotifyDataResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 3: { + this->data = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_string(3, this->data); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTNotifyDataResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" data: "); + out.append("'").append(this->data).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +void SubscribeBluetoothConnectionsFreeRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const { + out.append("SubscribeBluetoothConnectionsFreeRequest {}"); +} +#endif +bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->free = value.as_uint32(); + return true; + } + case 2: { + this->limit = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->free); + buffer.encode_uint32(2, this->limit); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothConnectionsFreeResponse {\n"); + out.append(" free: "); + sprintf(buffer, "%u", this->free); + out.append(buffer); + out.append("\n"); + + out.append(" limit: "); + sprintf(buffer, "%u", this->limit); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 2093f93ee7..325e9a23c3 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -155,12 +155,20 @@ enum MediaPlayerCommand : uint32_t { MEDIA_PLAYER_COMMAND_MUTE = 3, MEDIA_PLAYER_COMMAND_UNMUTE = 4, }; +enum BluetoothDeviceRequestType : uint32_t { + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0, + BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1, + BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2, + BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, +}; } // namespace enums class HelloRequest : public ProtoMessage { public: std::string client_info{}; + uint32_t api_version_major{0}; + uint32_t api_version_minor{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -168,6 +176,7 @@ class HelloRequest : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class HelloResponse : public ProtoMessage { public: @@ -263,7 +272,7 @@ class DeviceInfoResponse : public ProtoMessage { std::string project_name{}; std::string project_version{}; uint32_t webserver_port{0}; - bool has_bluetooth_proxy{false}; + uint32_t bluetooth_proxy_version{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1227,7 +1236,8 @@ class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { class BluetoothServiceData : public ProtoMessage { public: std::string uuid{}; - std::vector data{}; + std::vector legacy_data{}; + std::string data{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1254,6 +1264,223 @@ class BluetoothLEAdvertisementResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class BluetoothDeviceRequest : public ProtoMessage { + public: + uint64_t address{0}; + enums::BluetoothDeviceRequestType request_type{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothDeviceConnectionResponse : public ProtoMessage { + public: + uint64_t address{0}; + bool connected{false}; + uint32_t mtu{0}; + int32_t error{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTGetServicesRequest : public ProtoMessage { + public: + uint64_t address{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTDescriptor : public ProtoMessage { + public: + std::vector uuid{}; + uint32_t handle{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTCharacteristic : public ProtoMessage { + public: + std::vector uuid{}; + uint32_t handle{0}; + uint32_t properties{0}; + std::vector descriptors{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTService : public ProtoMessage { + public: + std::vector uuid{}; + uint32_t handle{0}; + std::vector characteristics{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTGetServicesResponse : public ProtoMessage { + public: + uint64_t address{0}; + std::vector services{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { + public: + uint64_t address{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTReadRequest : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTReadResponse : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + std::string data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTWriteRequest : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + bool response{false}; + std::string data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTReadDescriptorRequest : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + std::string data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTNotifyRequest : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + bool enable{false}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTNotifyDataResponse : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + std::string data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: +}; +class BluetoothConnectionsFreeResponse : public ProtoMessage { + public: + uint32_t free{0}; + uint32_t limit{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 6932675c41..7bfe1acf48 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -336,6 +336,71 @@ bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const Blu return this->send_message_(msg, 67); } #endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 69); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 71); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response( + const BluetoothGATTGetServicesDoneResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 72); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 74); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 79); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 81); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -612,6 +677,94 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_subscribe_bluetooth_le_advertisements_request(msg); break; } + case 68: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothDeviceRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_device_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_device_request(msg); +#endif + break; + } + case 70: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTGetServicesRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_get_services_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_get_services_request(msg); +#endif + break; + } + case 73: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTReadRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_read_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_read_request(msg); +#endif + break; + } + case 75: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTWriteRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_write_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_write_request(msg); +#endif + break; + } + case 76: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTReadDescriptorRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_read_descriptor_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_read_descriptor_request(msg); +#endif + break; + } + case 77: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTWriteDescriptorRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_write_descriptor_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_write_descriptor_request(msg); +#endif + break; + } + case 78: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothGATTNotifyRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_gatt_notify_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_gatt_notify_request(msg); +#endif + break; + } + case 80: { +#ifdef USE_BLUETOOTH_PROXY + SubscribeBluetoothConnectionsFreeRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str()); +#endif + this->on_subscribe_bluetooth_connections_free_request(msg); +#endif + break; + } default: return false; } @@ -708,18 +861,6 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc } this->subscribe_home_assistant_states(msg); } -void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( - const SubscribeBluetoothLEAdvertisementsRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_bluetooth_le_advertisements(msg); -} void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { if (!this->is_connection_setup()) { this->on_no_setup_connection(); @@ -884,6 +1025,126 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma this->media_player_command(msg); } #endif +void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( + const SubscribeBluetoothLEAdvertisementsRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_bluetooth_le_advertisements(msg); +} +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_device_request(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_get_services(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_read(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_write(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_read_descriptor(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_write_descriptor(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_gatt_notify(msg); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_subscribe_bluetooth_connections_free_request( + const SubscribeBluetoothConnectionsFreeRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg); + if (!this->send_bluetooth_connections_free_response(ret)) { + this->on_fatal_error(); + } +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 49426d1bbc..c7ef1468d8 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -158,6 +158,48 @@ class APIServerConnectionBase : public ProtoService { const SubscribeBluetoothLEAdvertisementsRequest &value){}; #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){}; +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -175,7 +217,6 @@ class APIServerConnection : public APIServerConnectionBase { virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0; virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; - virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; virtual void execute_service(const ExecuteServiceRequest &msg) = 0; #ifdef USE_COVER @@ -210,6 +251,32 @@ class APIServerConnection : public APIServerConnectionBase { #endif #ifdef USE_MEDIA_PLAYER virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0; +#endif + virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0; +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( + const SubscribeBluetoothConnectionsFreeRequest &msg) = 0; #endif protected: void on_hello_request(const HelloRequest &msg) override; @@ -222,7 +289,6 @@ class APIServerConnection : public APIServerConnectionBase { void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override; void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override; void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; - void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; void on_get_time_request(const GetTimeRequest &msg) override; void on_execute_service_request(const ExecuteServiceRequest &msg) override; #ifdef USE_COVER @@ -257,6 +323,31 @@ class APIServerConnection : public APIServerConnectionBase { #endif #ifdef USE_MEDIA_PLAYER void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override; +#endif + void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override; +#endif +#ifdef USE_BLUETOOTH_PROXY + void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override; #endif }; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 5851594955..965f08aa15 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -297,6 +297,51 @@ void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementRe client->send_bluetooth_le_advertisement(call); } } +void APIServer::send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) { + BluetoothDeviceConnectionResponse call; + call.address = address; + call.connected = connected; + call.mtu = mtu; + call.error = error; + + for (auto &client : this->clients_) { + client->send_bluetooth_device_connection_response(call); + } +} + +void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) { + BluetoothConnectionsFreeResponse call; + call.free = free; + call.limit = limit; + + for (auto &client : this->clients_) { + client->send_bluetooth_connections_free_response(call); + } +} + +void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_read_response(call); + } +} +void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_notify_data_response(call); + } +} +void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_get_services_response(call); + } +} +void APIServer::send_bluetooth_gatt_services_done(uint64_t address) { + BluetoothGATTGetServicesDoneResponse call; + call.address = address; + + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_get_services_done_response(call); + } +} #endif APIServer::APIServer() { global_api_server = this; } void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index dc892b2088..72ab55432c 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -75,6 +75,12 @@ class APIServer : public Component, public Controller { void send_homeassistant_service_call(const HomeassistantServiceResponse &call); #ifdef USE_BLUETOOTH_PROXY void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call); + void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error = ESP_OK); + void send_bluetooth_connections_free(uint8_t free, uint8_t limit); + void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); + void send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call); + void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call); + void send_bluetooth_gatt_services_done(uint64_t address); #endif void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #ifdef USE_HOMEASSISTANT_TIME diff --git a/esphome/components/bedjet/bedjet_hub.cpp b/esphome/components/bedjet/bedjet_hub.cpp index fd383eb6be..f90ca5cf54 100644 --- a/esphome/components/bedjet/bedjet_hub.cpp +++ b/esphome/components/bedjet/bedjet_hub.cpp @@ -128,9 +128,9 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) { } return -1; } - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_cmd_, - pkt->data_length + 1, (uint8_t *) &pkt->command, ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), + this->char_handle_cmd_, pkt->data_length + 1, (uint8_t *) &pkt->command, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); return status; } @@ -138,13 +138,13 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) { uint8_t BedJetHub::set_notify_(const bool enable) { uint8_t status; if (enable) { - status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, + status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), this->char_handle_status_); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status); } } else { - status = esp_ble_gattc_unregister_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, + status = esp_ble_gattc_unregister_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), this->char_handle_status_); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->get_name().c_str(), status); @@ -204,8 +204,8 @@ bool BedJetHub::discover_characteristics_() { result = false; } else { this->char_handle_name_ = chr->handle; - auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_name_, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), + this->char_handle_name_, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGI(TAG, "[%s] Unable to read name characteristic: %d", this->get_name().c_str(), status); } @@ -230,22 +230,6 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga this->dispatch_state_(false); break; } - case ESP_GATTC_OPEN_EVT: { - // FIXME: bug in BLEClient - this->parent_->conn_id = param->open.conn_id; - this->open_conn_id_ = param->open.conn_id; - break; - } - - case ESP_GATTC_CONNECT_EVT: { - if (this->parent_->conn_id != param->connect.conn_id && this->open_conn_id_ != 0xff) { - // FIXME: bug in BLEClient - ESP_LOGW(TAG, "[%s] CONNECT_EVT unexpected conn_id; open=%d, parent=%d, param=%d", this->get_name().c_str(), - this->open_conn_id_, this->parent_->conn_id, param->connect.conn_id); - this->parent_->conn_id = this->open_conn_id_; - } - break; - } case ESP_GATTC_SEARCH_CMPL_EVT: { auto result = this->discover_characteristics_(); @@ -301,7 +285,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent_->conn_id) + 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); @@ -358,9 +342,9 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga if (this->processing_) break; - if (param->notify.conn_id != this->parent_->conn_id) { + if (param->notify.conn_id != this->parent_->get_conn_id()) { ESP_LOGW(TAG, "[%s] Received notify event for unexpected parent conn: expect %x, got %x", - this->get_name().c_str(), this->parent_->conn_id, param->notify.conn_id); + this->get_name().c_str(), this->parent_->get_conn_id(), param->notify.conn_id); // FIXME: bug in BLEClient holding wrong conn_id. } @@ -394,7 +378,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga if (needs_extra) { // This means the packet was partial, so read the status characteristic to get the second part. // Ideally this will complete quickly. We won't process additional notification events until it does. - auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id, + auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_status_, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGI(TAG, "[%s] Unable to read extended status packet", this->get_name().c_str()); @@ -438,15 +422,15 @@ uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) { // NOTE: BLEClient uses `uint8_t*` of length 1, but BLE spec requires 16 bits. uint16_t notify_en = enable ? 1 : 0; - auto status = - esp_ble_gattc_write_char_descr(this->parent_->gattc_if, this->parent_->conn_id, handle, sizeof(notify_en), - (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_write_char_descr(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), handle, + sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status); return status; } ESP_LOGD(TAG, "[%s] wrote notify=%s to status config 0x%04x, for conn %d", this->get_name().c_str(), - enable ? "true" : "false", handle, this->parent_->conn_id); + enable ? "true" : "false", handle, this->parent_->get_conn_id()); return ESP_GATT_OK; } @@ -500,7 +484,7 @@ void BedJetHub::update() { this->dispatch_status_(); } void BedJetHub::dump_config() { ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str()); ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id); - ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->conn_id); + ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id()); LOG_UPDATE_INTERVAL(this) ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size()); for (auto *child : this->children_) { diff --git a/esphome/components/bedjet/bedjet_hub.h b/esphome/components/bedjet/bedjet_hub.h index c7583b78e9..f1479710a7 100644 --- a/esphome/components/bedjet/bedjet_hub.h +++ b/esphome/components/bedjet/bedjet_hub.h @@ -167,8 +167,6 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo uint16_t char_handle_status_; uint16_t config_descr_status_; - uint8_t open_conn_id_ = -1; - uint8_t write_notify_config_descriptor_(bool enable); }; diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 4b7d5f5b8a..0f1f60e62b 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import esp32_ble_tracker +from esphome.components import esp32_ble_tracker, esp32_ble_client from esphome.const import ( CONF_CHARACTERISTIC_UUID, CONF_ID, @@ -14,13 +14,12 @@ from esphome.const import ( ) from esphome import automation +AUTO_LOAD = ["esp32_ble_client"] CODEOWNERS = ["@buxtronix"] DEPENDENCIES = ["esp32_ble_tracker"] ble_client_ns = cg.esphome_ns.namespace("ble_client") -BLEClient = ble_client_ns.class_( - "BLEClient", cg.Component, esp32_ble_tracker.ESPBTClient -) +BLEClient = ble_client_ns.class_("BLEClient", esp32_ble_client.BLEClientBase) BLEClientNode = ble_client_ns.class_("BLEClientNode") BLEClientNodeConstRef = BLEClientNode.operator("ref").operator("const") # Triggers diff --git a/esphome/components/ble_client/automation.cpp b/esphome/components/ble_client/automation.cpp index 6918ab31b4..395950dd26 100644 --- a/esphome/components/ble_client/automation.cpp +++ b/esphome/components/ble_client/automation.cpp @@ -1,8 +1,8 @@ #include "automation.h" +#include #include #include -#include #include "esphome/core/log.h" @@ -31,8 +31,8 @@ void BLEWriterClientNode::write(const std::vector &value) { } ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str()); esp_err_t err = - esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(), - const_cast(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->ble_char_handle_, + value.size(), const_cast(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE); if (err != ESP_OK) { ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err)); } diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 38e64ebd76..ba5f78109e 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -28,7 +28,8 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode { void loop() 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 { - if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0) + if (event == ESP_GATTC_DISCONNECT_EVT && + memcmp(param->disconnect.remote_bda, this->parent_->get_remote_bda(), 6) == 0) this->trigger(); if (event == ESP_GATTC_SEARCH_CMPL_EVT) this->node_state = espbt::ClientState::ESTABLISHED; diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 5f58d8273f..3f86df32f5 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -1,8 +1,9 @@ -#include "esphome/core/log.h" +#include "ble_client.h" +#include "esphome/components/esp32_ble_client/ble_client_base.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" -#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#include "ble_client.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 @@ -11,22 +12,13 @@ namespace ble_client { static const char *const TAG = "ble_client"; -float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } - void BLEClient::setup() { - auto ret = esp_ble_gattc_app_register(this->app_id); - if (ret) { - ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret); - this->mark_failed(); - } - this->set_states_(espbt::ClientState::IDLE); + BLEClientBase::setup(); this->enabled = true; } void BLEClient::loop() { - if (this->state() == espbt::ClientState::DISCOVERED) { - this->connect(); - } + BLEClientBase::loop(); for (auto *node : this->nodes_) node->loop(); } @@ -39,34 +31,7 @@ void BLEClient::dump_config() { bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { if (!this->enabled) return false; - if (device.address_uint64() != this->address) - return false; - if (this->state() != espbt::ClientState::IDLE) - return false; - - ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str()); - this->set_states_(espbt::ClientState::DISCOVERED); - - auto addr = device.address_uint64(); - this->remote_bda[0] = (addr >> 40) & 0xFF; - this->remote_bda[1] = (addr >> 32) & 0xFF; - this->remote_bda[2] = (addr >> 24) & 0xFF; - this->remote_bda[3] = (addr >> 16) & 0xFF; - this->remote_bda[4] = (addr >> 8) & 0xFF; - this->remote_bda[5] = (addr >> 0) & 0xFF; - this->remote_addr_type = device.get_address_type(); - return true; -} - -std::string BLEClient::address_str() const { - char buf[20]; - sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (uint8_t)(this->address >> 40) & 0xff, - (uint8_t)(this->address >> 32) & 0xff, (uint8_t)(this->address >> 24) & 0xff, - (uint8_t)(this->address >> 16) & 0xff, (uint8_t)(this->address >> 8) & 0xff, - (uint8_t)(this->address >> 0) & 0xff); - std::string ret; - ret = buf; - return ret; + return BLEClientBase::parse_device(device); } void BLEClient::set_enabled(bool enabled) { @@ -74,7 +39,7 @@ void BLEClient::set_enabled(bool enabled) { return; if (!enabled && this->state() != espbt::ClientState::IDLE) { ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str()); - auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id); + auto ret = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); if (ret) { ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s status=%d", this->address_str().c_str(), ret); } @@ -82,125 +47,12 @@ void BLEClient::set_enabled(bool enabled) { this->enabled = enabled; } -void BLEClient::connect() { - ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str()); - auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true); - if (ret) { - ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); - this->set_states_(espbt::ClientState::IDLE); - } else { - this->set_states_(espbt::ClientState::CONNECTING); - } -} - void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { - if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) - return; - if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if) - return; - bool all_established = this->all_nodes_established_(); - switch (event) { - case ESP_GATTC_REG_EVT: { - if (param->reg.status == ESP_GATT_OK) { - ESP_LOGV(TAG, "gattc registered app id %d", this->app_id); - this->gattc_if = esp_gattc_if; - } else { - ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status); - } - break; - } - case ESP_GATTC_OPEN_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); - this->conn_id = param->open.conn_id; - if (param->open.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); - this->set_states_(espbt::ClientState::IDLE); - break; - } - break; - } - case ESP_GATTC_CONNECT_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str()); - if (this->conn_id != param->connect.conn_id) { - ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d", - this->address_str().c_str(), param->connect.conn_id, this->conn_id); - } - auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id); - if (ret) { - ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret); - } - break; - } - case ESP_GATTC_CFG_MTU_EVT: { - if (param->cfg_mtu.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu, - param->cfg_mtu.status); - this->set_states_(espbt::ClientState::IDLE); - break; - } - ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); - esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); - break; - } - case ESP_GATTC_DISCONNECT_EVT: { - if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) { - return; - } - ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason); - for (auto &svc : this->services_) - delete svc; // NOLINT(cppcoreguidelines-owning-memory) - this->services_.clear(); - this->set_states_(espbt::ClientState::IDLE); - break; - } - case ESP_GATTC_SEARCH_RES_EVT: { - BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory) - ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid); - ble_service->start_handle = param->search_res.start_handle; - ble_service->end_handle = param->search_res.end_handle; - ble_service->client = this; - this->services_.push_back(ble_service); - break; - } - case ESP_GATTC_SEARCH_CMPL_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str()); - for (auto &svc : this->services_) { - ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str()); - ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle); - svc->parse_characteristics(); - } - this->set_states_(espbt::ClientState::CONNECTED); - this->set_state(espbt::ClientState::ESTABLISHED); - break; - } - case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - auto *descr = this->get_config_descriptor(param->reg_for_notify.handle); - if (descr == nullptr) { - ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle); - break; - } - if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 || - descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { - ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle, - descr->uuid.to_string().c_str()); - break; - } - uint16_t notify_en = 1; - auto status = - esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en), - (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) { - ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status); - } - break; - } + BLEClientBase::gattc_event_handler(event, esp_gattc_if, param); - default: - break; - } for (auto *node : this->nodes_) node->gattc_event_handler(event, esp_gattc_if, param); @@ -212,237 +64,20 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es } } -void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - switch (event) { - // This event is sent by the server when it requests security - case ESP_GAP_BLE_SEC_REQ_EVT: - ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event); - esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); - break; - // This event is sent once authentication has completed - case ESP_GAP_BLE_AUTH_CMPL_EVT: - esp_bd_addr_t bd_addr; - memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t)); - ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str()); - if (!param->ble_security.auth_cmpl.success) { - ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason); - } else { - ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type, - param->ble_security.auth_cmpl.auth_mode); - } - break; - // There are other events we'll want to implement at some point to support things like pass key - // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md - default: - break; +void BLEClient::set_state(espbt::ClientState state) { + BLEClientBase::set_state(state); + for (auto &node : nodes_) + node->node_state = state; +} + +bool BLEClient::all_nodes_established_() { + if (this->state() != espbt::ClientState::ESTABLISHED) + return false; + for (auto &node : nodes_) { + if (node->node_state != espbt::ClientState::ESTABLISHED) + return false; } -} - -// Parse GATT values into a float for a sensor. -// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/ -float BLEClient::parse_char_value(uint8_t *value, uint16_t length) { - // A length of one means a single octet value. - if (length == 0) - return 0; - if (length == 1) - return (float) ((uint8_t) value[0]); - - switch (value[0]) { - case 0x1: // boolean. - case 0x2: // 2bit. - case 0x3: // nibble. - case 0x4: // uint8. - return (float) ((uint8_t) value[1]); - case 0x5: // uint12. - case 0x6: // uint16. - if (length > 2) { - return (float) ((uint16_t)(value[1] << 8) + (uint16_t) value[2]); - } - case 0x7: // uint24. - if (length > 3) { - return (float) ((uint32_t)(value[1] << 16) + (uint32_t)(value[2] << 8) + (uint32_t)(value[3])); - } - case 0x8: // uint32. - if (length > 4) { - return (float) ((uint32_t)(value[1] << 24) + (uint32_t)(value[2] << 16) + (uint32_t)(value[3] << 8) + - (uint32_t)(value[4])); - } - case 0xC: // int8. - return (float) ((int8_t) value[1]); - case 0xD: // int12. - case 0xE: // int16. - if (length > 2) { - return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]); - } - case 0xF: // int24. - if (length > 3) { - return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3])); - } - case 0x10: // int32. - if (length > 4) { - return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) + - (int32_t)(value[4])); - } - } - ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length); - return NAN; -} - -BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) { - for (auto *svc : this->services_) { - if (svc->uuid == uuid) - return svc; - } - return nullptr; -} - -BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); } - -BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) { - auto *svc = this->get_service(service); - if (svc == nullptr) - return nullptr; - return svc->get_characteristic(chr); -} - -BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr) { - return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr)); -} - -BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) { - for (auto &svc : this->services_) { - for (auto &chr : svc->characteristics) { - if (chr->handle == handle) { - for (auto &desc : chr->descriptors) { - if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902)) - return desc; - } - } - } - } - return nullptr; -} - -BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { - for (auto &chr : this->characteristics) { - if (chr->uuid == uuid) - return chr; - } - return nullptr; -} - -BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) { - return this->get_characteristic(espbt::ESPBTUUID::from_uint16(uuid)); -} - -BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) { - auto *svc = this->get_service(service); - if (svc == nullptr) - return nullptr; - auto *ch = svc->get_characteristic(chr); - if (ch == nullptr) - return nullptr; - return ch->get_descriptor(descr); -} - -BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) { - return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr), - espbt::ESPBTUUID::from_uint16(descr)); -} - -BLEService::~BLEService() { - for (auto &chr : this->characteristics) - delete chr; // NOLINT(cppcoreguidelines-owning-memory) -} - -void BLEService::parse_characteristics() { - uint16_t offset = 0; - esp_gattc_char_elem_t result; - - while (true) { - uint16_t count = 1; - esp_gatt_status_t status = esp_ble_gattc_get_all_char( - this->client->gattc_if, this->client->conn_id, this->start_handle, this->end_handle, &result, &count, offset); - if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { - break; - } - if (status != ESP_GATT_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status); - break; - } - if (count == 0) { - break; - } - - BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory) - characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); - characteristic->properties = result.properties; - characteristic->handle = result.char_handle; - characteristic->service = this; - this->characteristics.push_back(characteristic); - ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(), - characteristic->handle, characteristic->properties); - characteristic->parse_descriptors(); - offset++; - } -} - -BLECharacteristic::~BLECharacteristic() { - for (auto &desc : this->descriptors) - delete desc; // NOLINT(cppcoreguidelines-owning-memory) -} - -void BLECharacteristic::parse_descriptors() { - uint16_t offset = 0; - esp_gattc_descr_elem_t result; - - while (true) { - uint16_t count = 1; - esp_gatt_status_t status = esp_ble_gattc_get_all_descr( - this->service->client->gattc_if, this->service->client->conn_id, this->handle, &result, &count, offset); - if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { - break; - } - if (status != ESP_GATT_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status); - break; - } - if (count == 0) { - break; - } - - BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory) - desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); - desc->handle = result.handle; - desc->characteristic = this; - this->descriptors.push_back(desc); - ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle); - offset++; - } -} - -BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) { - for (auto &desc : this->descriptors) { - if (desc->uuid == uuid) - return desc; - } - return nullptr; -} -BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { - return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); -} - -void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) { - auto *client = this->service->client; - auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val, - write_type, ESP_GATT_AUTH_REQ_NONE); - if (status) { - ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); - } -} - -void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { - write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP); + return true; } } // namespace ble_client diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 5ed8f219d1..0f8373ab1f 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -1,8 +1,9 @@ #pragma once +#include "esphome/components/esp32_ble_client/ble_client_base.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #ifdef USE_ESP32 @@ -18,9 +19,9 @@ namespace ble_client { namespace espbt = esphome::esp32_ble_tracker; +using namespace esp32_ble_client; + class BLEClient; -class BLEService; -class BLECharacteristic; class BLEClientNode { public: @@ -42,57 +43,15 @@ class BLEClientNode { uint64_t address_; }; -class BLEDescriptor { - public: - espbt::ESPBTUUID uuid; - uint16_t handle; - - BLECharacteristic *characteristic; -}; - -class BLECharacteristic { - public: - ~BLECharacteristic(); - espbt::ESPBTUUID uuid; - uint16_t handle; - esp_gatt_char_prop_t properties; - std::vector descriptors; - void parse_descriptors(); - BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); - BLEDescriptor *get_descriptor(uint16_t uuid); - void write_value(uint8_t *new_val, int16_t new_val_size); - void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type); - BLEService *service; -}; - -class BLEService { - public: - ~BLEService(); - espbt::ESPBTUUID uuid; - uint16_t start_handle; - uint16_t end_handle; - std::vector characteristics; - BLEClient *client; - void parse_characteristics(); - BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid); - BLECharacteristic *get_characteristic(uint16_t uuid); -}; - -class BLEClient : public espbt::ESPBTClient, public Component { +class BLEClient : public BLEClientBase { public: void setup() override; void dump_config() override; void loop() override; - float get_setup_priority() const 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 gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; bool parse_device(const espbt::ESPBTDevice &device) override; - void on_scan_end() override {} - void connect() override; - - void set_address(uint64_t address) { this->address = address; } void set_enabled(bool enabled); @@ -102,43 +61,14 @@ class BLEClient : public espbt::ESPBTClient, public Component { this->nodes_.push_back(node); } - BLEService *get_service(espbt::ESPBTUUID uuid); - BLEService *get_service(uint16_t uuid); - BLECharacteristic *get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr); - BLECharacteristic *get_characteristic(uint16_t service, uint16_t chr); - BLEDescriptor *get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr); - BLEDescriptor *get_descriptor(uint16_t service, uint16_t chr, uint16_t descr); - // Get the configuration descriptor for the given characteristic handle. - BLEDescriptor *get_config_descriptor(uint16_t handle); - - float parse_char_value(uint8_t *value, uint16_t length); - - int gattc_if; - esp_bd_addr_t remote_bda; - esp_ble_addr_type_t remote_addr_type; - uint16_t conn_id; - uint64_t address; bool enabled; - std::string address_str() const; + + void set_state(espbt::ClientState state) override; protected: - void set_states_(espbt::ClientState st) { - this->set_state(st); - for (auto &node : nodes_) - node->node_state = st; - } - bool all_nodes_established_() { - if (this->state() != espbt::ClientState::ESTABLISHED) - return false; - for (auto &node : nodes_) { - if (node->node_state != espbt::ClientState::ESTABLISHED) - return false; - } - return true; - } + bool all_nodes_established_(); std::vector nodes_; - std::vector services_; }; } // namespace ble_client diff --git a/esphome/components/ble_client/sensor/automation.h b/esphome/components/ble_client/sensor/automation.h index 2baaafe2ec..d830165d30 100644 --- a/esphome/components/ble_client/sensor/automation.h +++ b/esphome/components/ble_client/sensor/automation.h @@ -19,7 +19,8 @@ class BLESensorNotifyTrigger : public Trigger, public BLESensor { break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle) + if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() || + param->notify.handle != this->sensor_->handle) break; this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len)); } diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index ee5afd3f7b..a05efad60b 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -63,8 +63,8 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga this->handle = descr->handle; } if (this->notify_) { - auto status = - esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle); + auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(), + this->parent()->get_remote_bda(), chr->handle); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); } @@ -74,7 +74,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->conn_id) + 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); @@ -87,7 +87,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle) + if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle) break; ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), param->notify.handle, param->notify.value[0]); @@ -122,8 +122,8 @@ void BLESensor::update() { return; } - auto status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE); + 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) { this->status_set_warning(); this->publish_state(NAN); diff --git a/esphome/components/ble_client/text_sensor/automation.h b/esphome/components/ble_client/text_sensor/automation.h index be85892c5a..c504c35a58 100644 --- a/esphome/components/ble_client/text_sensor/automation.h +++ b/esphome/components/ble_client/text_sensor/automation.h @@ -19,7 +19,8 @@ class BLETextSensorNotifyTrigger : public Trigger, public BLETextSe break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle) + if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() || + param->notify.handle != this->sensor_->handle) break; this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len)); } diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index 1a71cd6cd8..1a304593c7 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -66,8 +66,8 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ this->handle = descr->handle; } if (this->notify_) { - auto status = - esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle); + auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(), + this->parent()->get_remote_bda(), chr->handle); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); } @@ -77,7 +77,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->conn_id) + 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); @@ -90,7 +90,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle) + if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle) break; ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), param->notify.handle, param->notify.value[0]); @@ -121,8 +121,8 @@ void BLETextSensor::update() { return; } - auto status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE); + 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) { this->status_set_warning(); this->publish_state(EMPTY); diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index a6a5a4391b..b5acea89dd 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -1,19 +1,23 @@ -from esphome.components import esp32_ble_tracker +from esphome.components import esp32_ble_tracker, esp32_ble_client import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID +from esphome.const import CONF_ACTIVE, CONF_ID -DEPENDENCIES = ["esp32", "esp32_ble_tracker"] +AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] +DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz"] bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy") -BluetoothProxy = bluetooth_proxy_ns.class_("BluetoothProxy", cg.Component) +BluetoothProxy = bluetooth_proxy_ns.class_( + "BluetoothProxy", esp32_ble_client.BLEClientBase +) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(BluetoothProxy), + cv.Optional(CONF_ACTIVE, default=False): cv.boolean, } ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -22,6 +26,8 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - await esp32_ble_tracker.register_ble_device(var, config) + cg.add(var.set_active(config[CONF_ACTIVE])) + + await esp32_ble_tracker.register_client(var, config) cg.add_define("USE_BLUETOOTH_PROXY") diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 41871295ce..96fee39f95 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -1,22 +1,39 @@ #include "bluetooth_proxy.h" -#ifdef USE_API -#include "esphome/components/api/api_pb2.h" -#include "esphome/components/api/api_server.h" -#endif // USE_API #include "esphome/core/log.h" #ifdef USE_ESP32 +#ifdef USE_API +#include "esphome/components/api/api_server.h" +#endif + namespace esphome { namespace bluetooth_proxy { static const char *const TAG = "bluetooth_proxy"; +BluetoothProxy::BluetoothProxy() { + global_bluetooth_proxy = this; + this->address_ = 0; +} + bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(), device.get_rssi()); this->send_api_packet_(device); + + this->address_type_map_[device.address_uint64()] = device.get_address_type(); + + if (this->address_ == 0) + return true; + + if (this->state_ == espbt::ClientState::DISCOVERED) { + ESP_LOGV(TAG, "Connecting to address %s", this->address_str().c_str()); + return true; + } + + BLEClientBase::parse_device(device); return true; } @@ -35,23 +52,309 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi for (auto &data : device.get_service_datas()) { api::BluetoothServiceData service_data; service_data.uuid = data.uuid.to_string(); - for (auto d : data.data) - service_data.data.push_back(d); - resp.service_data.push_back(service_data); + service_data.data.assign(data.data.begin(), data.data.end()); + resp.service_data.push_back(std::move(service_data)); } for (auto &data : device.get_manufacturer_datas()) { api::BluetoothServiceData manufacturer_data; manufacturer_data.uuid = data.uuid.to_string(); - for (auto d : data.data) - manufacturer_data.data.push_back(d); - resp.manufacturer_data.push_back(manufacturer_data); + manufacturer_data.data.assign(data.data.begin(), data.data.end()); + resp.manufacturer_data.push_back(std::move(manufacturer_data)); } api::global_api_server->send_bluetooth_le_advertisement(resp); #endif } +void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + BLEClientBase::gattc_event_handler(event, gattc_if, param); + switch (event) { + case ESP_GATTC_DISCONNECT_EVT: { +#ifdef USE_API + api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, + param->disconnect.reason); + api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), + this->get_bluetooth_connections_limit()); +#endif + this->address_ = 0; + } + case ESP_GATTC_OPEN_EVT: { + if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { +#ifdef USE_API + api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status); + +#endif + break; + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: { +#ifdef USE_API + api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); + api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), + this->get_bluetooth_connections_limit()); +#endif + break; + } + case ESP_GATTC_READ_DESCR_EVT: + case ESP_GATTC_READ_CHAR_EVT: { + if (param->read.conn_id != this->conn_id_) + break; + if (param->read.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error reading char/descriptor at handle %d, status=%d", param->read.handle, param->read.status); + break; + } +#ifdef USE_API + api::BluetoothGATTReadResponse resp; + resp.address = this->address_; + resp.handle = param->read.handle; + resp.data.reserve(param->read.value_len); + for (uint16_t i = 0; i < param->read.value_len; i++) { + resp.data.push_back(param->read.value[i]); + } + api::global_api_server->send_bluetooth_gatt_read_response(resp); +#endif + break; + } + case ESP_GATTC_NOTIFY_EVT: { + if (param->notify.conn_id != this->conn_id_) + break; + ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%x", param->notify.handle); +#ifdef USE_API + api::BluetoothGATTNotifyDataResponse resp; + resp.address = this->address_; + resp.handle = param->notify.handle; + resp.data.reserve(param->notify.value_len); + for (uint16_t i = 0; i < param->notify.value_len; i++) { + resp.data.push_back(param->notify.value[i]); + } + api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); +#endif + break; + } + default: + break; + } +} + void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } +void BluetoothProxy::loop() { + BLEClientBase::loop(); +#ifdef USE_API + if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) { + ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); + auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err); + } + } + + if (this->send_service_ == this->services_.size()) { + this->send_service_ = -1; + api::global_api_server->send_bluetooth_gatt_services_done(this->address_); + } else if (this->send_service_ >= 0) { + auto &service = this->services_[this->send_service_]; + api::BluetoothGATTGetServicesResponse resp; + resp.address = this->address_; + api::BluetoothGATTService service_resp; + service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()}; + service_resp.handle = service->start_handle; + for (auto &characteristic : service->characteristics) { + api::BluetoothGATTCharacteristic characteristic_resp; + characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()}; + characteristic_resp.handle = characteristic->handle; + characteristic_resp.properties = characteristic->properties; + for (auto &descriptor : characteristic->descriptors) { + api::BluetoothGATTDescriptor descriptor_resp; + descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()}; + descriptor_resp.handle = descriptor->handle; + characteristic_resp.descriptors.push_back(std::move(descriptor_resp)); + } + service_resp.characteristics.push_back(std::move(characteristic_resp)); + } + resp.services.push_back(std::move(service_resp)); + api::global_api_server->send_bluetooth_gatt_services(resp); + this->send_service_++; + } +#endif +} + +#ifdef USE_API +void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { + switch (msg.request_type) { + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { + this->address_ = msg.address; + if (this->address_type_map_.find(this->address_) != this->address_type_map_.end()) { + // Utilise the address type cache + this->remote_addr_type_ = this->address_type_map_[this->address_]; + } else { + this->remote_addr_type_ = BLE_ADDR_TYPE_PUBLIC; + } + this->remote_bda_[0] = (this->address_ >> 40) & 0xFF; + this->remote_bda_[1] = (this->address_ >> 32) & 0xFF; + this->remote_bda_[2] = (this->address_ >> 24) & 0xFF; + this->remote_bda_[3] = (this->address_ >> 16) & 0xFF; + this->remote_bda_[4] = (this->address_ >> 8) & 0xFF; + this->remote_bda_[5] = (this->address_ >> 0) & 0xFF; + this->set_state(espbt::ClientState::DISCOVERED); + esp_ble_gap_stop_scanning(); + break; + } + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: { + if (this->state() != espbt::ClientState::IDLE) { + ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); + auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err); + } + } + break; + } + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR: + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR: + break; + } +} + +void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected."); + return; + } + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request"); + return; + } + + auto *characteristic = this->get_characteristic(msg.handle); + if (characteristic == nullptr) { + ESP_LOGW(TAG, "Cannot read GATT characteristic, not found."); + return; + } + + ESP_LOGV(TAG, "Reading GATT characteristic %s", characteristic->uuid.to_string().c_str()); + + esp_err_t err = + esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + } +} + +void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected."); + return; + } + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request"); + return; + } + + auto *characteristic = this->get_characteristic(msg.handle); + if (characteristic == nullptr) { + ESP_LOGW(TAG, "Cannot write GATT characteristic, not found."); + return; + } + + ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str()); + characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(), + msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); +} + +void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not connected."); + return; + } + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request"); + return; + } + + auto *descriptor = this->get_descriptor(msg.handle); + if (descriptor == nullptr) { + ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found."); + return; + } + + ESP_LOGV(TAG, "Reading GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(), + descriptor->uuid.to_string().c_str()); + + esp_err_t err = + esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + } +} + +void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not connected."); + return; + } + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request"); + return; + } + + auto *descriptor = this->get_descriptor(msg.handle); + if (descriptor == nullptr) { + ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found."); + return; + } + + ESP_LOGV(TAG, "Writing GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(), + descriptor->uuid.to_string().c_str()); + + esp_err_t err = + esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, msg.data.size(), + (uint8_t *) msg.data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, err=%d", err); + } +} + +void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) { + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for service list request"); + return; + } + this->send_service_ = 0; +} + +void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) { + if (this->address_ != msg.address) { + ESP_LOGW(TAG, "Address mismatch for notify"); + return; + } + + auto *characteristic = this->get_characteristic(msg.handle); + + if (characteristic == nullptr) { + ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found."); + return; + } + + esp_err_t err; + if (msg.enable) { + err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, err=%d", err); + } + } else { + err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_unregister_for_notify failed, err=%d", err); + } + } +} + +#endif + +BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + } // namespace bluetooth_proxy } // namespace esphome diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 9a936747c0..8ff43aff3f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -4,22 +4,59 @@ #include +#include "esphome/components/esp32_ble_client/ble_client_base.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" + +#include + +#ifdef USE_API +#include "esphome/components/api/api_pb2.h" +#endif // USE_API namespace esphome { namespace bluetooth_proxy { -class BluetoothProxy : public Component, public esp32_ble_tracker::ESPBTDeviceListener { +using namespace esp32_ble_client; + +class BluetoothProxy : public BLEClientBase { public: + BluetoothProxy(); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; + void loop() 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; + +#ifdef USE_API + void bluetooth_device_request(const api::BluetoothDeviceRequest &msg); + void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg); + void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg); + void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg); + void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg); + void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); + void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg); +#endif + + int get_bluetooth_connections_free() { return this->state_ == espbt::ClientState::IDLE ? 1 : 0; } + int get_bluetooth_connections_limit() { return 1; } + + void set_active(bool active) { this->active_ = active; } + bool has_active() { return this->active_; } protected: void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); + + std::map address_type_map_; + int16_t send_service_{-1}; + bool active_; }; +extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + } // namespace bluetooth_proxy } // namespace esphome diff --git a/esphome/components/esp32_ble_client/__init__.py b/esphome/components/esp32_ble_client/__init__.py new file mode 100644 index 0000000000..94a5576d0b --- /dev/null +++ b/esphome/components/esp32_ble_client/__init__.py @@ -0,0 +1,12 @@ +import esphome.codegen as cg + +from esphome.components import esp32_ble_tracker + +AUTO_LOAD = ["esp32_ble_tracker"] +CODEOWNERS = ["@jesserockz"] +DEPENDENCIES = ["esp32"] + +esp32_ble_client_ns = cg.esphome_ns.namespace("esp32_ble_client") +BLEClientBase = esp32_ble_client_ns.class_( + "BLEClientBase", esp32_ble_tracker.ESPBTClient, cg.Component +) diff --git a/esphome/components/esp32_ble_client/ble_characteristic.cpp b/esphome/components/esp32_ble_client/ble_characteristic.cpp new file mode 100644 index 0000000000..873833368c --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_characteristic.cpp @@ -0,0 +1,83 @@ +#include "ble_characteristic.h" +#include "ble_client_base.h" +#include "ble_service.h" + +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32_ble_client { + +static const char *const TAG = "esp32_ble_client.characteristic"; + +BLECharacteristic::~BLECharacteristic() { + for (auto &desc : this->descriptors) + delete desc; // NOLINT(cppcoreguidelines-owning-memory) +} + +void BLECharacteristic::parse_descriptors() { + uint16_t offset = 0; + esp_gattc_descr_elem_t result; + + while (true) { + uint16_t count = 1; + esp_gatt_status_t status = + esp_ble_gattc_get_all_descr(this->service->client->get_gattc_if(), this->service->client->get_conn_id(), + this->handle, &result, &count, offset); + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { + break; + } + if (status != ESP_GATT_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status); + break; + } + if (count == 0) { + break; + } + + BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory) + desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); + desc->handle = result.handle; + desc->characteristic = this; + this->descriptors.push_back(desc); + ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle); + offset++; + } +} + +BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) { + for (auto &desc : this->descriptors) { + if (desc->uuid == uuid) + return desc; + } + return nullptr; +} +BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { + return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); +} +BLEDescriptor *BLECharacteristic::get_descriptor_by_handle(uint16_t handle) { + for (auto &desc : this->descriptors) { + if (desc->handle == handle) + return desc; + } + return nullptr; +} + +void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) { + auto *client = this->service->client; + auto status = esp_ble_gattc_write_char(client->get_gattc_if(), client->get_conn_id(), this->handle, new_val_size, + new_val, write_type, ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); + } +} + +void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { + write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP); +} + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_characteristic.h b/esphome/components/esp32_ble_client/ble_characteristic.h new file mode 100644 index 0000000000..ffa9227cc4 --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_characteristic.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#include "ble_descriptor.h" + +namespace esphome { +namespace esp32_ble_client { + +namespace espbt = esphome::esp32_ble_tracker; + +class BLEService; + +class BLECharacteristic { + public: + ~BLECharacteristic(); + espbt::ESPBTUUID uuid; + uint16_t handle; + esp_gatt_char_prop_t properties; + std::vector descriptors; + void parse_descriptors(); + BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); + BLEDescriptor *get_descriptor(uint16_t uuid); + BLEDescriptor *get_descriptor_by_handle(uint16_t handle); + void write_value(uint8_t *new_val, int16_t new_val_size); + void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type); + BLEService *service; +}; + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp new file mode 100644 index 0000000000..0e81c9aca8 --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -0,0 +1,324 @@ +#include "ble_client_base.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32_ble_client { + +static const char *const TAG = "esp32_ble_client"; + +void BLEClientBase::setup() { + auto ret = esp_ble_gattc_app_register(this->app_id); + if (ret) { + ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret); + this->mark_failed(); + } + this->set_state(espbt::ClientState::IDLE); +} + +void BLEClientBase::loop() { + if (this->state_ == espbt::ClientState::DISCOVERED) { + this->connect(); + } +} + +float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } + +bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) + return false; + if (this->state_ != espbt::ClientState::IDLE) + return false; + + ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str()); + this->set_state(espbt::ClientState::DISCOVERED); + + auto addr = device.address_uint64(); + this->remote_bda_[0] = (addr >> 40) & 0xFF; + this->remote_bda_[1] = (addr >> 32) & 0xFF; + this->remote_bda_[2] = (addr >> 24) & 0xFF; + this->remote_bda_[3] = (addr >> 16) & 0xFF; + this->remote_bda_[4] = (addr >> 8) & 0xFF; + this->remote_bda_[5] = (addr >> 0) & 0xFF; + this->remote_addr_type_ = device.get_address_type(); + return true; +} + +std::string BLEClientBase::address_str() const { + return str_snprintf("%02x:%02x:%02x:%02x:%02x:%02x", 17, (uint8_t)(this->address_ >> 40) & 0xff, + (uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff, + (uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff, + (uint8_t)(this->address_ >> 0) & 0xff); +} + +void BLEClientBase::connect() { + ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str()); + auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true); + if (ret) { + ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); + this->set_state(espbt::ClientState::IDLE); + } else { + this->set_state(espbt::ClientState::CONNECTING); + } +} + +void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, + esp_ble_gattc_cb_param_t *param) { + if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) + return; + if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if_) + return; + + switch (event) { + case ESP_GATTC_REG_EVT: { + if (param->reg.status == ESP_GATT_OK) { + ESP_LOGV(TAG, "gattc registered app id %d", this->app_id); + this->gattc_if_ = esp_gattc_if; + } else { + ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status); + } + break; + } + case ESP_GATTC_OPEN_EVT: { + ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); + this->conn_id_ = param->open.conn_id; + if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { + ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); + this->set_state(espbt::ClientState::IDLE); + break; + } + break; + } + case ESP_GATTC_CONNECT_EVT: { + ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str()); + if (this->conn_id_ != param->connect.conn_id) { + ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d", + this->address_str().c_str(), param->connect.conn_id, this->conn_id_); + } + auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->connect.conn_id); + if (ret) { + ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret); + } + break; + } + case ESP_GATTC_CFG_MTU_EVT: { + if (param->cfg_mtu.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu, + param->cfg_mtu.status); + this->set_state(espbt::ClientState::IDLE); + break; + } + ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); + this->mtu_ = param->cfg_mtu.mtu; + esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); + break; + } + case ESP_GATTC_DISCONNECT_EVT: { + if (memcmp(param->disconnect.remote_bda, this->remote_bda_, 6) != 0) { + return; + } + ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason); + for (auto &svc : this->services_) + delete svc; // NOLINT(cppcoreguidelines-owning-memory) + this->services_.clear(); + this->set_state(espbt::ClientState::IDLE); + break; + } + case ESP_GATTC_SEARCH_RES_EVT: { + BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory) + ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid); + ble_service->start_handle = param->search_res.start_handle; + ble_service->end_handle = param->search_res.end_handle; + ble_service->client = this; + this->services_.push_back(ble_service); + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: { + ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str()); + for (auto &svc : this->services_) { + ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str()); + ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle); + svc->parse_characteristics(); + } + this->set_state(espbt::ClientState::CONNECTED); + this->state_ = espbt::ClientState::ESTABLISHED; + break; + } + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + auto *descr = this->get_config_descriptor(param->reg_for_notify.handle); + if (descr == nullptr) { + ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle); + break; + } + if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 || + descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { + ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle, + descr->uuid.to_string().c_str()); + break; + } + uint16_t notify_en = 1; + auto status = + esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descr->handle, sizeof(notify_en), + (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status); + } + break; + } + + default: + break; + } +} + +void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { + switch (event) { + // This event is sent by the server when it requests security + case ESP_GAP_BLE_SEC_REQ_EVT: + ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event); + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + break; + // This event is sent once authentication has completed + case ESP_GAP_BLE_AUTH_CMPL_EVT: + esp_bd_addr_t bd_addr; + memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t)); + ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str()); + if (!param->ble_security.auth_cmpl.success) { + ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason); + } else { + ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type, + param->ble_security.auth_cmpl.auth_mode); + } + break; + // There are other events we'll want to implement at some point to support things like pass key + // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md + default: + break; + } +} + +// Parse GATT values into a float for a sensor. +// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/ +float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { + // A length of one means a single octet value. + if (length == 0) + return 0; + if (length == 1) + return (float) ((uint8_t) value[0]); + + switch (value[0]) { + case 0x1: // boolean. + case 0x2: // 2bit. + case 0x3: // nibble. + case 0x4: // uint8. + return (float) ((uint8_t) value[1]); + case 0x5: // uint12. + case 0x6: // uint16. + if (length > 2) { + return (float) encode_uint16(value[1], value[2]); + } + case 0x7: // uint24. + if (length > 3) { + return (float) encode_uint24(value[1], value[2], value[3]); + } + case 0x8: // uint32. + if (length > 4) { + return (float) encode_uint32(value[1], value[2], value[3], value[4]); + } + case 0xC: // int8. + return (float) ((int8_t) value[1]); + case 0xD: // int12. + case 0xE: // int16. + if (length > 2) { + return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]); + } + case 0xF: // int24. + if (length > 3) { + return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3])); + } + case 0x10: // int32. + if (length > 4) { + return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) + + (int32_t)(value[4])); + } + } + ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length); + return NAN; +} + +BLEService *BLEClientBase::get_service(espbt::ESPBTUUID uuid) { + for (auto *svc : this->services_) { + if (svc->uuid == uuid) + return svc; + } + return nullptr; +} + +BLEService *BLEClientBase::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); } + +BLECharacteristic *BLEClientBase::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) { + auto *svc = this->get_service(service); + if (svc == nullptr) + return nullptr; + return svc->get_characteristic(chr); +} + +BLECharacteristic *BLEClientBase::get_characteristic(uint16_t service, uint16_t chr) { + return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr)); +} + +BLECharacteristic *BLEClientBase::get_characteristic(uint16_t handle) { + for (auto *svc : this->services_) { + for (auto *chr : svc->characteristics) { + if (chr->handle == handle) + return chr; + } + } + return nullptr; +} + +BLEDescriptor *BLEClientBase::get_config_descriptor(uint16_t handle) { + auto *chr = this->get_characteristic(handle); + if (chr != nullptr) { + for (auto &desc : chr->descriptors) { + if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902)) + return desc; + } + } + return nullptr; +} + +BLEDescriptor *BLEClientBase::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) { + auto *svc = this->get_service(service); + if (svc == nullptr) + return nullptr; + auto *ch = svc->get_characteristic(chr); + if (ch == nullptr) + return nullptr; + return ch->get_descriptor(descr); +} + +BLEDescriptor *BLEClientBase::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) { + return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr), + espbt::ESPBTUUID::from_uint16(descr)); +} + +BLEDescriptor *BLEClientBase::get_descriptor(uint16_t handle) { + for (auto *svc : this->services_) { + for (auto *chr : svc->characteristics) { + for (auto *desc : chr->descriptors) { + if (desc->handle == handle) + return desc; + } + } + } + return nullptr; +} + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h new file mode 100644 index 0000000000..eba70fa571 --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -0,0 +1,72 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/core/component.h" + +#include "ble_service.h" + +#include +#include + +#include +#include +#include +#include + +namespace esphome { +namespace esp32_ble_client { + +namespace espbt = esphome::esp32_ble_tracker; + +class BLEClientBase : public espbt::ESPBTClient, public Component { + public: + void setup() override; + void loop() override; + float get_setup_priority() const override; + + bool parse_device(const espbt::ESPBTDevice &device) override; + void on_scan_end() 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 gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; + void connect() override; + + void set_address(uint64_t address) { this->address_ = address; } + std::string address_str() const; + + BLEService *get_service(espbt::ESPBTUUID uuid); + BLEService *get_service(uint16_t uuid); + BLECharacteristic *get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr); + BLECharacteristic *get_characteristic(uint16_t service, uint16_t chr); + BLECharacteristic *get_characteristic(uint16_t handle); + BLEDescriptor *get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr); + BLEDescriptor *get_descriptor(uint16_t service, uint16_t chr, uint16_t descr); + BLEDescriptor *get_descriptor(uint16_t handle); + // Get the configuration descriptor for the given characteristic handle. + BLEDescriptor *get_config_descriptor(uint16_t handle); + + float parse_char_value(uint8_t *value, uint16_t length); + + int get_gattc_if() const { return this->gattc_if_; } + uint8_t *get_remote_bda() { return this->remote_bda_; } + esp_ble_addr_type_t get_remote_addr_type() const { return this->remote_addr_type_; } + uint16_t get_conn_id() const { return this->conn_id_; } + uint64_t get_address() const { return this->address_; } + + protected: + int gattc_if_; + esp_bd_addr_t remote_bda_; + esp_ble_addr_type_t remote_addr_type_; + uint16_t conn_id_; + uint64_t address_; + uint16_t mtu_{23}; + + std::vector services_; +}; + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_descriptor.h b/esphome/components/esp32_ble_client/ble_descriptor.h new file mode 100644 index 0000000000..c05430144f --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_descriptor.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +namespace esphome { +namespace esp32_ble_client { + +namespace espbt = esphome::esp32_ble_tracker; + +class BLECharacteristic; + +class BLEDescriptor { + public: + espbt::ESPBTUUID uuid; + uint16_t handle; + + BLECharacteristic *characteristic; +}; + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_service.cpp b/esphome/components/esp32_ble_client/ble_service.cpp new file mode 100644 index 0000000000..1d81eaa556 --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_service.cpp @@ -0,0 +1,66 @@ +#include "ble_service.h" +#include "ble_client_base.h" + +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32_ble_client { + +static const char *const TAG = "esp32_ble_client.service"; + +BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { + for (auto &chr : this->characteristics) { + if (chr->uuid == uuid) + return chr; + } + return nullptr; +} + +BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) { + return this->get_characteristic(espbt::ESPBTUUID::from_uint16(uuid)); +} + +BLEService::~BLEService() { + for (auto &chr : this->characteristics) + delete chr; // NOLINT(cppcoreguidelines-owning-memory) +} + +void BLEService::parse_characteristics() { + uint16_t offset = 0; + esp_gattc_char_elem_t result; + + while (true) { + uint16_t count = 1; + esp_gatt_status_t status = + esp_ble_gattc_get_all_char(this->client->get_gattc_if(), this->client->get_conn_id(), this->start_handle, + this->end_handle, &result, &count, offset); + if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) { + break; + } + if (status != ESP_GATT_OK) { + ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status); + break; + } + if (count == 0) { + break; + } + + BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory) + characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); + characteristic->properties = result.properties; + characteristic->handle = result.char_handle; + characteristic->service = this; + this->characteristics.push_back(characteristic); + ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(), + characteristic->handle, characteristic->properties); + characteristic->parse_descriptors(); + offset++; + } +} + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_client/ble_service.h b/esphome/components/esp32_ble_client/ble_service.h new file mode 100644 index 0000000000..04f2212e0e --- /dev/null +++ b/esphome/components/esp32_ble_client/ble_service.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#include "ble_characteristic.h" + +namespace esphome { +namespace esp32_ble_client { + +namespace espbt = esphome::esp32_ble_tracker; + +class BLEClientBase; + +class BLEService { + public: + ~BLEService(); + espbt::ESPBTUUID uuid; + uint16_t start_handle; + uint16_t end_handle; + std::vector characteristics; + BLEClientBase *client; + void parse_characteristics(); + BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid); + BLECharacteristic *get_characteristic(uint16_t uuid); +}; + +} // namespace esp32_ble_client +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 6d7868d4c5..0d7abe32f9 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -3,6 +3,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.const import ( + CONF_ACTIVE, CONF_ID, CONF_INTERVAL, CONF_DURATION, @@ -22,7 +23,6 @@ DEPENDENCIES = ["esp32"] CONF_ESP32_BLE_ID = "esp32_ble_id" CONF_SCAN_PARAMETERS = "scan_parameters" CONF_WINDOW = "window" -CONF_ACTIVE = "active" CONF_CONTINUOUS = "continuous" CONF_ON_SCAN_END = "on_scan_end" esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index f7e51a8ab3..d4512b52b0 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -518,28 +518,39 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const { } esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; } std::string ESPBTUUID::to_string() const { - char sbuf[64]; switch (this->uuid_.len) { case ESP_UUID_LEN_16: - sprintf(sbuf, "0x%02X%02X", this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff); - break; + return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff); case ESP_UUID_LEN_32: - sprintf(sbuf, "0x%02X%02X%02X%02X", this->uuid_.uuid.uuid32 >> 24, (this->uuid_.uuid.uuid32 >> 16 & 0xff), - (this->uuid_.uuid.uuid32 >> 8 & 0xff), this->uuid_.uuid.uuid32 & 0xff); - break; + return str_snprintf("0x%02X%02X%02X%02X", 10, this->uuid_.uuid.uuid32 >> 24, + (this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff), + this->uuid_.uuid.uuid32 & 0xff); default: case ESP_UUID_LEN_128: - char *bpos = sbuf; + std::string buf; for (int8_t i = 15; i >= 0; i--) { - sprintf(bpos, "%02X", this->uuid_.uuid.uuid128[i]); - bpos += 2; + buf += str_snprintf("%02X", 2, this->uuid_.uuid.uuid128[i]); if (i == 6 || i == 8 || i == 10 || i == 12) - sprintf(bpos++, "-"); + buf += "-"; } - sbuf[47] = '\0'; - break; + return buf; } - return sbuf; + return ""; +} + +uint64_t ESPBTUUID::get_128bit_high() const { + esp_bt_uuid_t uuid = this->as_128bit().get_uuid(); + return ((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) | + ((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) | + ((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) | + ((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]); +} +uint64_t ESPBTUUID::get_128bit_low() const { + esp_bt_uuid_t uuid = this->as_128bit().get_uuid(); + return ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) | + ((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) | + ((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) | + ((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0]); } ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 29d0c81542..45abcd63fa 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -41,6 +41,9 @@ class ESPBTUUID { std::string to_string() const; + uint64_t get_128bit_high() const; + uint64_t get_128bit_low() const; + protected: esp_bt_uuid_t uuid_; }; @@ -158,7 +161,7 @@ class ESPBTClient : public ESPBTDeviceListener { esp_ble_gattc_cb_param_t *param) = 0; virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; virtual void connect() = 0; - void set_state(ClientState st) { this->state_ = st; } + virtual void set_state(ClientState st) { this->state_ = st; } ClientState state() const { return state_; } int app_id; diff --git a/esphome/components/esp32_ble_tracker/queue.h b/esphome/components/esp32_ble_tracker/queue.h index f09b2ca8d7..f3a2b3cb3c 100644 --- a/esphome/components/esp32_ble_tracker/queue.h +++ b/esphome/components/esp32_ble_tracker/queue.h @@ -72,13 +72,13 @@ class BLEEvent { // Need to also make a copy of relevant event data. switch (e) { case ESP_GATTC_NOTIFY_EVT: - memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len); - this->event_.gattc.gattc_param.notify.value = this->event_.gattc.data; + this->data.assign(p->notify.value, p->notify.value + p->notify.value_len); + this->event_.gattc.gattc_param.notify.value = this->data.data(); break; case ESP_GATTC_READ_CHAR_EVT: case ESP_GATTC_READ_DESCR_EVT: - memcpy(this->event_.gattc.data, p->read.value, p->read.value_len); - this->event_.gattc.gattc_param.read.value = this->event_.gattc.data; + this->data.assign(p->read.value, p->read.value + p->read.value_len); + this->event_.gattc.gattc_param.read.value = this->data.data(); break; default: break; @@ -96,9 +96,9 @@ class BLEEvent { esp_gattc_cb_event_t gattc_event; esp_gatt_if_t gattc_if; esp_ble_gattc_cb_param_t gattc_param; - uint8_t data[64]; } gattc; } event_; + std::vector data{}; uint8_t type_; // 0=gap 1=gattc }; diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp index 21638ef7e4..384537e5d7 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -102,8 +102,9 @@ void PVVXDisplay::send_to_setup_char_(uint8_t *blk, size_t size) { ESP_LOGW(TAG, "[%s] Not connected to BLE client.", this->parent_->address_str().c_str()); return; } - auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, size, blk, - ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + auto status = + esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, size, + blk, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); } else { diff --git a/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp b/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp index 6bb17f0508..3959178b94 100644 --- a/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp +++ b/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp @@ -50,7 +50,7 @@ void RadonEyeRD200::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->parent()->conn_id) + 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); @@ -146,17 +146,17 @@ void RadonEyeRD200::update() { void RadonEyeRD200::write_query_message_() { ESP_LOGV(TAG, "writing 0x50 to write service"); int request = 0x50; - auto status = esp_ble_gattc_write_char_descr(this->parent()->gattc_if, this->parent()->conn_id, this->write_handle_, - sizeof(request), (uint8_t *) &request, ESP_GATT_WRITE_TYPE_NO_RSP, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_write_char_descr(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), + this->write_handle_, sizeof(request), (uint8_t *) &request, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "Error sending write request for sensor, status=%d", status); } } void RadonEyeRD200::request_read_values_() { - auto status = esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->read_handle_, - ESP_GATT_AUTH_REQ_NONE); + auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), + this->read_handle_, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); } diff --git a/esphome/const.py b/esphome/const.py index a272f518df..e9a6975ca2 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -23,6 +23,7 @@ CONF_ACCURACY = "accuracy" CONF_ACCURACY_DECIMALS = "accuracy_decimals" CONF_ACTION_ID = "action_id" CONF_ACTION_STATE_TOPIC = "action_state_topic" +CONF_ACTIVE = "active" CONF_ACTIVE_POWER = "active_power" CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" From 6b52f62531519451d934fe8cfbe74bbcf0b8564e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 6 Oct 2022 09:22:01 +1300 Subject: [PATCH 57/69] Bump version to 2022.9.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e9a6975ca2..ff59f2b8bf 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.2" +__version__ = "2022.9.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 5df0e82c372362a310b332720e63e47e8284ab01 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 7 Oct 2022 14:42:28 +1300 Subject: [PATCH 58/69] Add network type to mdns service message (#3880) --- esphome/components/mdns/mdns_component.cpp | 6 ++++++ esphome/dashboard/dashboard.py | 1 + esphome/zeroconf.py | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 31858c0d3c..e1fcf320ed 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -45,6 +45,12 @@ void MDNSComponent::compile_records_() { service.txt_records.push_back({"board", ESPHOME_BOARD}); +#if defined(USE_WIFI) + service.txt_records.push_back({"network", "wifi"}); +#elif defined(USE_ETHERNET) + service.txt_records.push_back({"network", "ethernet"}); +#endif + #ifdef ESPHOME_PROJECT_NAME service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME}); service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION}); diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 9a8f072237..c812ec4b49 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -613,6 +613,7 @@ class ListDevicesHandler(BaseHandler): "package_import_url": res.package_import_url, "project_name": res.project_name, "project_version": res.project_version, + "network": res.network, } for res in IMPORT_RESULT.values() if res.device_name not in configured diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index 1fbdf7e93f..089ae4f1c2 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -118,6 +118,7 @@ ESPHOME_SERVICE_TYPE = "_esphomelib._tcp.local." TXT_RECORD_PACKAGE_IMPORT_URL = b"package_import_url" TXT_RECORD_PROJECT_NAME = b"project_name" TXT_RECORD_PROJECT_VERSION = b"project_version" +TXT_RECORD_NETWORK = b"network" @dataclass @@ -126,6 +127,7 @@ class DiscoveredImport: package_import_url: str project_name: str project_version: str + network: str class DashboardImportDiscovery: @@ -134,7 +136,7 @@ class DashboardImportDiscovery: self.service_browser = ServiceBrowser( self.zc, ESPHOME_SERVICE_TYPE, [self._on_update] ) - self.import_state = {} + self.import_state: dict[str, DiscoveredImport] = {} def _on_update( self, @@ -171,12 +173,14 @@ class DashboardImportDiscovery: import_url = info.properties[TXT_RECORD_PACKAGE_IMPORT_URL].decode() project_name = info.properties[TXT_RECORD_PROJECT_NAME].decode() project_version = info.properties[TXT_RECORD_PROJECT_VERSION].decode() + network = info.properties.get(TXT_RECORD_NETWORK, b"wifi").decode() self.import_state[name] = DiscoveredImport( device_name=node_name, package_import_url=import_url, project_name=project_name, project_version=project_version, + network=network, ) def cancel(self) -> None: From 5e96b8ef7c903d5c4a15a1e66384e0c7ff8dfa0f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:20:13 +1300 Subject: [PATCH 59/69] Bump esphome-dashboard to 20221007.0 (#3881) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7f86acdbef..00e6e0ba67 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 -esphome-dashboard==20220925.0 +esphome-dashboard==20221007.0 aioesphomeapi==10.13.0 zeroconf==0.39.1 From 3b83f967e4859ae800ff9448cc47034ef045a011 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:35:48 +1300 Subject: [PATCH 60/69] Dont add wifi block to yaml if discovered device uses ethernet (#3882) --- esphome/components/dashboard_import/__init__.py | 15 ++++++++++----- esphome/dashboard/dashboard.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py index 41b4a8bed1..b795c85b12 100644 --- a/esphome/components/dashboard_import/__init__.py +++ b/esphome/components/dashboard_import/__init__.py @@ -3,6 +3,7 @@ from pathlib import Path import esphome.codegen as cg import esphome.config_validation as cv from esphome.components.packages import validate_source_shorthand +from esphome.const import CONF_WIFI from esphome.wizard import wizard_file from esphome.yaml_util import dump @@ -43,7 +44,9 @@ async def to_code(config): cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL])) -def import_config(path: str, name: str, project_name: str, import_url: str) -> None: +def import_config( + path: str, name: str, project_name: str, import_url: str, network: str = CONF_WIFI +) -> None: p = Path(path) if p.exists(): @@ -69,7 +72,9 @@ def import_config(path: str, name: str, project_name: str, import_url: str) -> N "name_add_mac_suffix": False, }, } - p.write_text( - dump(config) + WIFI_CONFIG, - encoding="utf8", - ) + output = dump(config) + + if network == CONF_WIFI: + output += WIFI_CONFIG + + p.write_text(output, encoding="utf8") diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index c812ec4b49..07ee151949 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -395,11 +395,22 @@ class ImportRequestHandler(BaseHandler): args = json.loads(self.request.body.decode()) try: name = args["name"] + + imported_device = next( + (res for res in IMPORT_RESULT.values() if res.device_name == name), None + ) + + if imported_device is not None: + network = imported_device.network + else: + network = const.CONF_WIFI + import_config( settings.rel_path(f"{name}.yaml"), name, args["project_name"], args["package_import_url"], + network, ) except FileExistsError: self.set_status(500) From 3c320c4c836c4611ac8171b0b69be3cdbfc40ee0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:34:39 +1300 Subject: [PATCH 61/69] Bump version to 2022.9.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ff59f2b8bf..e93808e6d4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.9.3" +__version__ = "2022.9.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From f422fabab415ab39e78aab874bc19f75ca461ff0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Oct 2022 09:18:46 +1300 Subject: [PATCH 62/69] Bump version to 2022.10.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7dcb3c823c..e0f3bc77b5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.10.0-dev" +__version__ = "2022.10.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From be914f2c1593453ba8ed7736e699ba5c16c9e80e Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Wed, 12 Oct 2022 18:11:59 -0300 Subject: [PATCH 63/69] fix never calling preset change trigger (#3864) Co-authored-by: Keith Burzinski --- .../thermostat/thermostat_climate.cpp | 73 +++++++++++++------ .../thermostat/thermostat_climate.h | 3 +- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 54e9f1687c..61e279d4a6 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -974,9 +974,19 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { auto config = this->preset_config_.find(preset); if (config != this->preset_config_.end()) { - ESP_LOGI(TAG, "Switching to preset %s", LOG_STR_ARG(climate::climate_preset_to_string(preset))); - this->change_preset_internal_(config->second); + ESP_LOGI(TAG, "Preset %s requested", LOG_STR_ARG(climate::climate_preset_to_string(preset))); + if (this->change_preset_internal_(config->second) || (!this->preset.has_value()) || + this->preset.value() != preset) { + // Fire any preset changed trigger if defined + Trigger<> *trig = this->preset_change_trigger_; + assert(trig != nullptr); + trig->trigger(); + this->refresh(); + ESP_LOGI(TAG, "Preset %s applied", LOG_STR_ARG(climate::climate_preset_to_string(preset))); + } else { + ESP_LOGI(TAG, "No changes required to apply preset %s", LOG_STR_ARG(climate::climate_preset_to_string(preset))); + } this->custom_preset.reset(); this->preset = preset; } else { @@ -988,9 +998,19 @@ void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) auto config = this->custom_preset_config_.find(custom_preset); if (config != this->custom_preset_config_.end()) { - ESP_LOGI(TAG, "Switching to custom preset %s", custom_preset.c_str()); - this->change_preset_internal_(config->second); + ESP_LOGI(TAG, "Custom preset %s requested", custom_preset.c_str()); + if (this->change_preset_internal_(config->second) || (!this->custom_preset.has_value()) || + this->custom_preset.value() != custom_preset) { + // Fire any preset changed trigger if defined + Trigger<> *trig = this->preset_change_trigger_; + assert(trig != nullptr); + trig->trigger(); + this->refresh(); + ESP_LOGI(TAG, "Custom preset %s applied", custom_preset.c_str()); + } else { + ESP_LOGI(TAG, "No changes required to apply custom preset %s", custom_preset.c_str()); + } this->preset.reset(); this->custom_preset = custom_preset; } else { @@ -998,39 +1018,46 @@ void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) } } -void ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTempConfig &config) { +bool ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTempConfig &config) { + bool something_changed = false; + if (this->supports_two_points_) { - this->target_temperature_low = config.default_temperature_low; - this->target_temperature_high = config.default_temperature_high; + if (this->target_temperature_low != config.default_temperature_low) { + this->target_temperature_low = config.default_temperature_low; + something_changed = true; + } + if (this->target_temperature_high != config.default_temperature_high) { + this->target_temperature_high = config.default_temperature_high; + something_changed = true; + } } else { - this->target_temperature = config.default_temperature; + if (this->target_temperature != config.default_temperature) { + this->target_temperature = config.default_temperature; + something_changed = true; + } } - // Note: The mode, fan_mode, and swing_mode can all be defined on the preset but if the climate.control call - // also specifies them then the control's version will override these for that call - if (config.mode_.has_value()) { - this->mode = *config.mode_; + // Note: The mode, fan_mode and swing_mode can all be defined in the preset but if the climate.control call + // also specifies them then the climate.control call's values will override the preset's values for that call + if (config.mode_.has_value() && (this->mode != config.mode_.value())) { ESP_LOGV(TAG, "Setting mode to %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_))); + this->mode = *config.mode_; + something_changed = true; } - if (config.fan_mode_.has_value()) { - this->fan_mode = *config.fan_mode_; + if (config.fan_mode_.has_value() && (this->fan_mode != config.fan_mode_.value())) { ESP_LOGV(TAG, "Setting fan mode to %s", LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_))); + this->fan_mode = *config.fan_mode_; + something_changed = true; } - if (config.swing_mode_.has_value()) { + if (config.swing_mode_.has_value() && (this->swing_mode != config.swing_mode_.value())) { ESP_LOGV(TAG, "Setting swing mode to %s", LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_))); this->swing_mode = *config.swing_mode_; + something_changed = true; } - // Fire any preset changed trigger if defined - if (this->preset != preset) { - Trigger<> *trig = this->preset_change_trigger_; - assert(trig != nullptr); - trig->trigger(); - } - - this->refresh(); + return something_changed; } void ThermostatClimate::set_preset_config(climate::ClimatePreset preset, diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index a738ba4986..68cbb17e34 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -168,7 +168,8 @@ class ThermostatClimate : public climate::Climate, public Component { /// Applies the temperature, mode, fan, and swing modes of the provided config. /// This is agnostic of custom vs built in preset - void change_preset_internal_(const ThermostatClimateTargetTempConfig &config); + /// Returns true if something was changed + bool change_preset_internal_(const ThermostatClimateTargetTempConfig &config); /// Return the traits of this controller. climate::ClimateTraits traits() override; From 8bf34e09f43a50c641c9e67892d99c4ed84ae7da Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Thu, 13 Oct 2022 03:50:45 +0400 Subject: [PATCH 64/69] Modbus QWORD fix (#3856) --- .../modbus_controller/modbus_controller.cpp | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index e60b016a17..57f714f233 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -571,24 +571,16 @@ int64_t payload_to_number(const std::vector &data, SensorValueType sens static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); } break; case SensorValueType::U_QWORD: - // Ignore bitmask for U_QWORD - value = get_data(data, offset); - break; case SensorValueType::S_QWORD: - // Ignore bitmask for S_QWORD - value = get_data(data, offset); + // Ignore bitmask for QWORD + value = get_data(data, offset); break; case SensorValueType::U_QWORD_R: - // Ignore bitmask for U_QWORD - value = get_data(data, offset); - value = static_cast(value & 0xFFFF) << 48 | (value & 0xFFFF000000000000) >> 48 | - static_cast(value & 0xFFFF0000) << 32 | (value & 0x0000FFFF00000000) >> 32 | - static_cast(value & 0xFFFF00000000) << 16 | (value & 0x00000000FFFF0000) >> 16; - break; - case SensorValueType::S_QWORD_R: - // Ignore bitmask for S_QWORD - value = get_data(data, offset); - break; + case SensorValueType::S_QWORD_R: { + // Ignore bitmask for QWORD + uint64_t tmp = get_data(data, offset); + value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + } break; case SensorValueType::RAW: default: break; From b2d91ac5de0f001b9d058e097714d5f1a6c08f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Jouault?= Date: Fri, 14 Oct 2022 01:47:05 +0200 Subject: [PATCH 65/69] Send true and not RSSI in ble_presence (#3904) --- esphome/components/ble_presence/ble_presence_device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index dcccf844d2..1689c9ba3f 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -58,7 +58,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, case MATCH_BY_SERVICE_UUID: for (auto uuid : device.get_service_uuids()) { if (this->uuid_ == uuid) { - this->publish_state(device.get_rssi()); + this->publish_state(true); this->found_ = true; return true; } @@ -83,7 +83,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, return false; } - this->publish_state(device.get_rssi()); + this->publish_state(true); this->found_ = true; return true; } From b6073408f4a820b2c38da517f9092a1f8abdfe1c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 15 Oct 2022 08:35:35 +1300 Subject: [PATCH 66/69] Remove address type map from bluetooth proxy (#3905) --- .../bluetooth_proxy/bluetooth_proxy.cpp | 21 ------------------- .../bluetooth_proxy/bluetooth_proxy.h | 1 - 2 files changed, 22 deletions(-) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index a08c58bd9e..cc1c178b08 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -26,16 +26,9 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) device.get_rssi()); this->send_api_packet_(device); - this->address_type_map_[device.address_uint64()] = device.get_address_type(); - if (this->address_ == 0) return true; - if (this->state_ == espbt::ClientState::DISCOVERED) { - ESP_LOGV(TAG, "Connecting to address %s", this->address_str().c_str()); - return true; - } - BLEClientBase::parse_device(device); return true; } @@ -216,20 +209,6 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest switch (msg.request_type) { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { this->address_ = msg.address; - if (this->address_type_map_.find(this->address_) != this->address_type_map_.end()) { - // Utilise the address type cache - this->remote_addr_type_ = this->address_type_map_[this->address_]; - } else { - this->remote_addr_type_ = BLE_ADDR_TYPE_PUBLIC; - } - this->remote_bda_[0] = (this->address_ >> 40) & 0xFF; - this->remote_bda_[1] = (this->address_ >> 32) & 0xFF; - this->remote_bda_[2] = (this->address_ >> 24) & 0xFF; - this->remote_bda_[3] = (this->address_ >> 16) & 0xFF; - this->remote_bda_[4] = (this->address_ >> 8) & 0xFF; - this->remote_bda_[5] = (this->address_ >> 0) & 0xFF; - this->set_state(espbt::ClientState::DISCOVERED); - esp_ble_gap_stop_scanning(); break; } case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: { diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 97047c118b..9529b99f73 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -45,7 +45,6 @@ class BluetoothProxy : public BLEClientBase { protected: void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); - std::map address_type_map_; int16_t send_service_{-1}; bool active_; }; From a84378c6c26aed20537810bee5ceb0386339c903 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 15 Oct 2022 08:37:11 +1300 Subject: [PATCH 67/69] Bump version to 2022.10.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e0f3bc77b5..9d608ebf9b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.10.0b1" +__version__ = "2022.10.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From d1263e583b2b5c41fe73fa3668cae4a5b960a088 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Oct 2022 10:30:22 +1300 Subject: [PATCH 68/69] Bump version to 2022.10.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 9d608ebf9b..d2bac8f9df 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.10.0b2" +__version__ = "2022.10.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From 58cd754e0733c22b456e2472e730e9d84b6f12f4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Oct 2022 10:50:32 +1300 Subject: [PATCH 69/69] Fix bad merge --- .../bluetooth_proxy/bluetooth_proxy.cpp | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 3f06cd5c25..cc1c178b08 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -164,78 +164,6 @@ void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if } } -void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - BLEClientBase::gattc_event_handler(event, gattc_if, param); - switch (event) { - case ESP_GATTC_DISCONNECT_EVT: { -#ifdef USE_API - api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, - param->disconnect.reason); - api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), - this->get_bluetooth_connections_limit()); -#endif - this->address_ = 0; - } - case ESP_GATTC_OPEN_EVT: { - if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { -#ifdef USE_API - api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status); - -#endif - break; - } - break; - } - case ESP_GATTC_SEARCH_CMPL_EVT: { -#ifdef USE_API - api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); - api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), - this->get_bluetooth_connections_limit()); -#endif - break; - } - case ESP_GATTC_READ_DESCR_EVT: - case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->conn_id_) - break; - if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char/descriptor at handle %d, status=%d", param->read.handle, param->read.status); - break; - } -#ifdef USE_API - api::BluetoothGATTReadResponse resp; - resp.address = this->address_; - resp.handle = param->read.handle; - resp.data.reserve(param->read.value_len); - for (uint16_t i = 0; i < param->read.value_len; i++) { - resp.data.push_back(param->read.value[i]); - } - api::global_api_server->send_bluetooth_gatt_read_response(resp); -#endif - break; - } - case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->conn_id_) - break; - ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%x", param->notify.handle); -#ifdef USE_API - api::BluetoothGATTNotifyDataResponse resp; - resp.address = this->address_; - resp.handle = param->notify.handle; - resp.data.reserve(param->notify.value_len); - for (uint16_t i = 0; i < param->notify.value_len; i++) { - resp.data.push_back(param->notify.value[i]); - } - api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); -#endif - break; - } - default: - break; - } -} - void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } void BluetoothProxy::loop() {