Merge branch 'dev' into nvds-status-info

This commit is contained in:
NP v/d Spek 2024-02-19 00:12:15 +01:00 committed by GitHub
commit 97878b187c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
428 changed files with 10738 additions and 116 deletions

View file

@ -199,6 +199,7 @@ esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core esphome/components/mdns/* @esphome/core
esphome/components/media_player/* @jesserockz esphome/components/media_player/* @jesserockz
esphome/components/micro_wake_word/* @jesserockz @kahrendt
esphome/components/micronova/* @jorre05 esphome/components/micronova/* @jorre05
esphome/components/microphone/* @jesserockz esphome/components/microphone/* @jesserockz
esphome/components/mics_4514/* @jesserockz esphome/components/mics_4514/* @jesserockz

View file

@ -35,7 +35,7 @@ RUN \
iputils-ping=3:20221126-1 \ iputils-ping=3:20221126-1 \
git=1:2.39.2-1.1 \ git=1:2.39.2-1.1 \
curl=7.88.1-10+deb12u5 \ curl=7.88.1-10+deb12u5 \
openssh-client=1:9.2p1-2+deb12u1 \ openssh-client=1:9.2p1-2+deb12u2 \
python3-cffi=1.15.1-5 \ python3-cffi=1.15.1-5 \
libcairo2=1.16.0-7 \ libcairo2=1.16.0-7 \
libmagic1=1:5.44-3 \ libmagic1=1:5.44-3 \

View file

@ -11,6 +11,7 @@ MULTI_CONF = True
CONF_BME680_BSEC_ID = "bme680_bsec_id" CONF_BME680_BSEC_ID = "bme680_bsec_id"
CONF_TEMPERATURE_OFFSET = "temperature_offset" CONF_TEMPERATURE_OFFSET = "temperature_offset"
CONF_IAQ_MODE = "iaq_mode" CONF_IAQ_MODE = "iaq_mode"
CONF_SUPPLY_VOLTAGE = "supply_voltage"
CONF_SAMPLE_RATE = "sample_rate" CONF_SAMPLE_RATE = "sample_rate"
CONF_STATE_SAVE_INTERVAL = "state_save_interval" CONF_STATE_SAVE_INTERVAL = "state_save_interval"
@ -22,6 +23,12 @@ IAQ_MODE_OPTIONS = {
"MOBILE": IAQMode.IAQ_MODE_MOBILE, "MOBILE": IAQMode.IAQ_MODE_MOBILE,
} }
SupplyVoltage = bme680_bsec_ns.enum("SupplyVoltage")
SUPPLY_VOLTAGE_OPTIONS = {
"1.8V": SupplyVoltage.SUPPLY_VOLTAGE_1V8,
"3.3V": SupplyVoltage.SUPPLY_VOLTAGE_3V3,
}
SampleRate = bme680_bsec_ns.enum("SampleRate") SampleRate = bme680_bsec_ns.enum("SampleRate")
SAMPLE_RATE_OPTIONS = { SAMPLE_RATE_OPTIONS = {
"LP": SampleRate.SAMPLE_RATE_LP, "LP": SampleRate.SAMPLE_RATE_LP,
@ -40,6 +47,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
IAQ_MODE_OPTIONS, upper=True IAQ_MODE_OPTIONS, upper=True
), ),
cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum(
SUPPLY_VOLTAGE_OPTIONS, upper=True
),
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
SAMPLE_RATE_OPTIONS, upper=True SAMPLE_RATE_OPTIONS, upper=True
), ),
@ -67,6 +77,7 @@ async def to_code(config):
cg.add(var.set_device_id(str(config[CONF_ID]))) cg.add(var.set_device_id(str(config[CONF_ID])))
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET])) cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE])) cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
cg.add(var.set_supply_voltage(config[CONF_SUPPLY_VOLTAGE]))
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
cg.add( cg.add(
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds) var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)

View file

@ -52,17 +52,33 @@ void BME680BSECComponent::setup() {
void BME680BSECComponent::set_config_() { void BME680BSECComponent::set_config_() {
if (this->sample_rate_ == SAMPLE_RATE_ULP) { if (this->sample_rate_ == SAMPLE_RATE_ULP) {
if (this->supply_voltage_ == SUPPLY_VOLTAGE_3V3) {
const uint8_t config[] = { const uint8_t config[] = {
#include "config/generic_33v_300s_28d/bsec_iaq.txt" #include "config/generic_33v_300s_28d/bsec_iaq.txt"
}; };
this->bsec_status_ = this->bsec_status_ =
bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_)); bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
} else { } else { // SUPPLY_VOLTAGE_1V8
const uint8_t config[] = {
#include "config/generic_18v_300s_28d/bsec_iaq.txt"
};
this->bsec_status_ =
bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
}
} else { // SAMPLE_RATE_LP
if (this->supply_voltage_ == SUPPLY_VOLTAGE_3V3) {
const uint8_t config[] = { const uint8_t config[] = {
#include "config/generic_33v_3s_28d/bsec_iaq.txt" #include "config/generic_33v_3s_28d/bsec_iaq.txt"
}; };
this->bsec_status_ = this->bsec_status_ =
bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_)); bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
} else { // SUPPLY_VOLTAGE_1V8
const uint8_t config[] = {
#include "config/generic_18v_3s_28d/bsec_iaq.txt"
};
this->bsec_status_ =
bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
}
} }
} }
@ -145,6 +161,7 @@ void BME680BSECComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_); ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile"); ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
ESP_LOGCONFIG(TAG, " Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8");
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_)); ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_); ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_);

View file

@ -21,6 +21,11 @@ enum IAQMode {
IAQ_MODE_MOBILE = 1, IAQ_MODE_MOBILE = 1,
}; };
enum SupplyVoltage {
SUPPLY_VOLTAGE_3V3 = 0,
SUPPLY_VOLTAGE_1V8 = 1,
};
enum SampleRate { enum SampleRate {
SAMPLE_RATE_LP = 0, SAMPLE_RATE_LP = 0,
SAMPLE_RATE_ULP = 1, SAMPLE_RATE_ULP = 1,
@ -35,6 +40,7 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
void set_temperature_offset(float offset) { this->temperature_offset_ = offset; } void set_temperature_offset(float offset) { this->temperature_offset_ = offset; }
void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; } void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; }
void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; } void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; }
void set_supply_voltage(SupplyVoltage supply_voltage) { this->supply_voltage_ = supply_voltage; }
void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; } void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; } void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; }
@ -109,6 +115,7 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
std::string device_id_; std::string device_id_;
float temperature_offset_{0}; float temperature_offset_{0};
IAQMode iaq_mode_{IAQ_MODE_STATIC}; IAQMode iaq_mode_{IAQ_MODE_STATIC};
SupplyVoltage supply_voltage_;
SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate
SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT}; SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};

View file

@ -1,6 +1,8 @@
#include "cse7766.h" #include "cse7766.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <cinttypes> #include <cinttypes>
#include <iomanip>
#include <sstream>
namespace esphome { namespace esphome {
namespace cse7766 { namespace cse7766 {
@ -68,20 +70,26 @@ bool CSE7766Component::check_byte_() {
return true; return true;
} }
void CSE7766Component::parse_data_() { void CSE7766Component::parse_data_() {
ESP_LOGVV(TAG, "CSE7766 Data: "); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
{
std::stringstream ss;
ss << "Raw data:" << std::hex << std::uppercase << std::setfill('0');
for (uint8_t i = 0; i < 23; i++) { for (uint8_t i = 0; i < 23; i++) {
ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]), ss << ' ' << std::setw(2) << static_cast<unsigned>(this->raw_data_[i]);
this->raw_data_[i]);
} }
ESP_LOGVV(TAG, "%s", ss.str().c_str());
}
#endif
// Parse header
uint8_t header1 = this->raw_data_[0]; uint8_t header1 = this->raw_data_[0];
if (header1 == 0xAA) { if (header1 == 0xAA) {
ESP_LOGE(TAG, "CSE7766 not calibrated!"); ESP_LOGE(TAG, "CSE7766 not calibrated!");
return; return;
} }
bool power_cycle_exceeds_range = false; bool power_cycle_exceeds_range = false;
if ((header1 & 0xF0) == 0xF0) { if ((header1 & 0xF0) == 0xF0) {
if (header1 & 0xD) { if (header1 & 0xD) {
ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1); ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
@ -94,76 +102,108 @@ void CSE7766Component::parse_data_() {
if (header1 & (1 << 0)) { if (header1 & (1 << 0)) {
ESP_LOGE(TAG, " Coefficient storage area is abnormal."); ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
} }
// Datasheet: voltage or current cycle exceeding range means invalid values
return; return;
} }
power_cycle_exceeds_range = header1 & (1 << 1); power_cycle_exceeds_range = header1 & (1 << 1);
} }
uint32_t voltage_calib = this->get_24_bit_uint_(2); // Parse data frame
uint32_t voltage_coeff = this->get_24_bit_uint_(2);
uint32_t voltage_cycle = this->get_24_bit_uint_(5); uint32_t voltage_cycle = this->get_24_bit_uint_(5);
uint32_t current_calib = this->get_24_bit_uint_(8); uint32_t current_coeff = this->get_24_bit_uint_(8);
uint32_t current_cycle = this->get_24_bit_uint_(11); uint32_t current_cycle = this->get_24_bit_uint_(11);
uint32_t power_calib = this->get_24_bit_uint_(14); uint32_t power_coeff = this->get_24_bit_uint_(14);
uint32_t power_cycle = this->get_24_bit_uint_(17); uint32_t power_cycle = this->get_24_bit_uint_(17);
uint8_t adj = this->raw_data_[20]; uint8_t adj = this->raw_data_[20];
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22]; uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
bool have_power = adj & 0x10;
bool have_current = adj & 0x20;
bool have_voltage = adj & 0x40; bool have_voltage = adj & 0x40;
float voltage = 0.0f;
if (have_voltage) { if (have_voltage) {
// voltage cycle of serial port outputted is a complete cycle; voltage = voltage_coeff / float(voltage_cycle);
float voltage = voltage_calib / float(voltage_cycle); if (this->voltage_sensor_ != nullptr) {
if (this->voltage_sensor_ != nullptr)
this->voltage_sensor_->publish_state(voltage); this->voltage_sensor_->publish_state(voltage);
} }
bool have_power = adj & 0x10;
float power = 0.0f;
if (have_power) {
// power cycle of serial port outputted is a complete cycle;
// According to the user manual, power cycle exceeding range means the measured power is 0
if (!power_cycle_exceeds_range) {
power = power_calib / float(power_cycle);
} }
if (this->power_sensor_ != nullptr)
this->power_sensor_->publish_state(power);
uint32_t difference; float power = 0.0f;
float energy = 0.0f;
if (power_cycle_exceeds_range) {
// Datasheet: power cycle exceeding range means active power is 0
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(0.0f);
}
} else if (have_power) {
power = power_coeff / float(power_cycle);
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(power);
}
// Add CF pulses to the total energy only if we have Power coefficient to multiply by
if (this->cf_pulses_last_ == 0) { if (this->cf_pulses_last_ == 0) {
this->cf_pulses_last_ = cf_pulses; this->cf_pulses_last_ = cf_pulses;
} }
uint32_t cf_diff;
if (cf_pulses < this->cf_pulses_last_) { if (cf_pulses < this->cf_pulses_last_) {
difference = cf_pulses + (0x10000 - this->cf_pulses_last_); cf_diff = cf_pulses + (0x10000 - this->cf_pulses_last_);
} else { } else {
difference = cf_pulses - this->cf_pulses_last_; cf_diff = cf_pulses - this->cf_pulses_last_;
} }
this->cf_pulses_last_ = cf_pulses; this->cf_pulses_last_ = cf_pulses;
this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
energy = cf_diff * float(power_coeff) / 1000000.0f / 3600.0f;
this->energy_total_ += energy;
if (this->energy_sensor_ != nullptr) if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(this->energy_total_); this->energy_sensor_->publish_state(this->energy_total_);
} else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) { } else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) {
this->energy_sensor_->publish_state(0); this->energy_sensor_->publish_state(0);
} }
if (adj & 0x20) {
// indicates current cycle of serial port outputted is a complete cycle;
float current = 0.0f; float current = 0.0f;
if (have_voltage && !have_power) { float calculated_current = 0.0f;
// Testing has shown that when we have voltage and current but not power, that means the power is 0. if (have_current) {
// We report a power of 0, which in turn means we should report a current of 0. // Assumption: if we don't have power measurement, then current is likely below 50mA
if (this->power_sensor_ != nullptr) if (have_power && voltage > 1.0f) {
this->power_sensor_->publish_state(0); calculated_current = power / voltage;
} else if (power != 0.0f) {
current = current_calib / float(current_cycle);
} }
if (this->current_sensor_ != nullptr) // Datasheet: minimum measured current is 50mA
if (calculated_current > 0.05f) {
current = current_coeff / float(current_cycle);
}
if (this->current_sensor_ != nullptr) {
this->current_sensor_->publish_state(current); this->current_sensor_->publish_state(current);
} }
} }
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
{
std::stringstream ss;
ss << "Parsed:";
if (have_voltage) {
ss << " V=" << voltage << "V";
}
if (have_current) {
ss << " I=" << current * 1000.0f << "mA (~" << calculated_current * 1000.0f << "mA)";
}
if (have_power) {
ss << " P=" << power << "W";
}
if (energy != 0.0f) {
ss << " E=" << energy << "kWh (" << cf_pulses << ")";
}
ESP_LOGVV(TAG, "%s", ss.str().c_str());
}
#endif
}
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) | return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) |
uint32_t(this->raw_data_[start_index + 2]); uint32_t(this->raw_data_[start_index + 2]);

View file

@ -1,7 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome import core
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.const import CONF_ID from esphome.const import CONF_ID
from esphome.components import uart from esphome.components import uart
@ -101,7 +100,7 @@ def range_segment_list(input):
largest_distance = -1 largest_distance = -1
for distance in input: for distance in input:
if isinstance(distance, core.Lambda): if isinstance(distance, cv.Lambda):
continue continue
m = cv.distance(distance) m = cv.distance(distance)
if m > 9: if m > 9:
@ -128,14 +127,14 @@ MMWAVE_SETTINGS_SCHEMA = cv.Schema(
cv.Optional(CONF_OUTPUT_LATENCY): { cv.Optional(CONF_OUTPUT_LATENCY): {
cv.Required(CONF_DELAY_AFTER_DETECT): cv.templatable( cv.Required(CONF_DELAY_AFTER_DETECT): cv.templatable(
cv.All( cv.All(
cv.positive_time_period, cv.positive_time_period_milliseconds,
cv.Range(max=core.TimePeriod(seconds=1638.375)), cv.Range(max=cv.TimePeriod(seconds=1638.375)),
) )
), ),
cv.Required(CONF_DELAY_AFTER_DISAPPEAR): cv.templatable( cv.Required(CONF_DELAY_AFTER_DISAPPEAR): cv.templatable(
cv.All( cv.All(
cv.positive_time_period, cv.positive_time_period_milliseconds,
cv.Range(max=core.TimePeriod(seconds=1638.375)), cv.Range(max=cv.TimePeriod(seconds=1638.375)),
) )
), ),
}, },

View file

@ -50,7 +50,7 @@ class DfrobotSen0395SettingsAction : public Action<Ts...>, public Parented<Dfrob
float detect = this->delay_after_detect_.value(x...); float detect = this->delay_after_detect_.value(x...);
float disappear = this->delay_after_disappear_.value(x...); float disappear = this->delay_after_disappear_.value(x...);
if (detect >= 0 && disappear >= 0) { if (detect >= 0 && disappear >= 0) {
this->parent_->enqueue(make_unique<OutputLatencyCommand>(detect, disappear)); this->parent_->enqueue(make_unique<SetLatencyCommand>(detect, disappear));
} }
} }
if (this->start_after_power_on_.has_value()) { if (this->start_after_power_on_.has_value()) {

View file

@ -1,5 +1,7 @@
#include "commands.h" #include "commands.h"
#include <cmath>
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "dfrobot_sen0395.h" #include "dfrobot_sen0395.h"
@ -194,32 +196,22 @@ uint8_t DetRangeCfgCommand::on_message(std::string &message) {
return 0; // Command not done yet. return 0; // Command not done yet.
} }
OutputLatencyCommand::OutputLatencyCommand(float delay_after_detection, float delay_after_disappear) { SetLatencyCommand::SetLatencyCommand(float delay_after_detection, float delay_after_disappear) {
delay_after_detection = round(delay_after_detection / 0.025) * 0.025; delay_after_detection = std::round(delay_after_detection / 0.025f) * 0.025f;
delay_after_disappear = round(delay_after_disappear / 0.025) * 0.025; delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f;
if (delay_after_detection < 0) this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f);
delay_after_detection = 0; this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f);
if (delay_after_detection > 1638.375) this->cmd_ = str_sprintf("setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
delay_after_detection = 1638.375;
if (delay_after_disappear < 0)
delay_after_disappear = 0;
if (delay_after_disappear > 1638.375)
delay_after_disappear = 1638.375;
this->delay_after_detection_ = delay_after_detection;
this->delay_after_disappear_ = delay_after_disappear;
this->cmd_ = str_sprintf("outputLatency -1 %.0f %.0f", delay_after_detection / 0.025, delay_after_disappear / 0.025);
}; };
uint8_t OutputLatencyCommand::on_message(std::string &message) { uint8_t SetLatencyCommand::on_message(std::string &message) {
if (message == "sensor is not stopped") { if (message == "sensor is not stopped") {
ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!"); ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!");
return 1; // Command done return 1; // Command done
} else if (message == "Done") { } else if (message == "Done") {
ESP_LOGI(TAG, "Updated output latency config:"); ESP_LOGI(TAG, "Updated output latency config:");
ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.02fs.", this->delay_after_detection_); ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.03f s.", this->delay_after_detection_);
ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.02fs.", this->delay_after_disappear_); ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.03f s.", this->delay_after_disappear_);
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
return 1; // Command done return 1; // Command done
} }

View file

@ -62,9 +62,9 @@ class DetRangeCfgCommand : public Command {
// TODO: Set min max values in component, so they can be published as sensor. // TODO: Set min max values in component, so they can be published as sensor.
}; };
class OutputLatencyCommand : public Command { class SetLatencyCommand : public Command {
public: public:
OutputLatencyCommand(float delay_after_detection, float delay_after_disappear); SetLatencyCommand(float delay_after_detection, float delay_after_disappear);
uint8_t on_message(std::string &message) override; uint8_t on_message(std::string &message) override;
protected: protected:

View file

@ -160,11 +160,13 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index
b = 0; b = 0;
break; break;
} }
uint8_t multiplier = this->is_rgbw_ ? 4 : 3; uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
return {this->buf_ + (index * multiplier) + r, uint8_t white = this->is_wrgb_ ? 0 : 3;
this->buf_ + (index * multiplier) + g,
this->buf_ + (index * multiplier) + b, return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
this->is_rgbw_ ? this->buf_ + (index * multiplier) + 3 : nullptr, this->buf_ + (index * multiplier) + g + this->is_wrgb_,
this->buf_ + (index * multiplier) + b + this->is_wrgb_,
this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
&this->effect_data_[index], &this->effect_data_[index],
&this->correction_}; &this->correction_};
} }

View file

@ -33,7 +33,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
int32_t size() const override { return this->num_leds_; } int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override { light::LightTraits get_traits() override {
auto traits = light::LightTraits(); auto traits = light::LightTraits();
if (this->is_rgbw_) { if (this->is_rgbw_ || this->is_wrgb_) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE}); traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else { } else {
traits.set_supported_color_modes({light::ColorMode::RGB}); traits.set_supported_color_modes({light::ColorMode::RGB});
@ -44,6 +44,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
void set_pin(uint8_t pin) { this->pin_ = pin; } void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; } void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; } void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
/// Set a maximum refresh rate in µs as some lights do not like being updated too often. /// Set a maximum refresh rate in µs as some lights do not like being updated too often.
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
@ -72,6 +73,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
uint8_t pin_; uint8_t pin_;
uint16_t num_leds_; uint16_t num_leds_;
bool is_rgbw_; bool is_rgbw_;
bool is_wrgb_;
rmt_item32_t bit0_, bit1_; rmt_item32_t bit0_, bit1_;
RGBOrder rgb_order_; RGBOrder rgb_order_;

View file

@ -52,6 +52,7 @@ CHIPSETS = {
CONF_IS_RGBW = "is_rgbw" CONF_IS_RGBW = "is_rgbw"
CONF_IS_WRGB = "is_wrgb"
CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_HIGH = "bit0_high"
CONF_BIT0_LOW = "bit0_low" CONF_BIT0_LOW = "bit0_low"
CONF_BIT1_HIGH = "bit1_high" CONF_BIT1_HIGH = "bit1_high"
@ -90,6 +91,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
cv.Optional(CONF_IS_WRGB, default=False): cv.boolean,
cv.Inclusive( cv.Inclusive(
CONF_BIT0_HIGH, CONF_BIT0_HIGH,
"custom", "custom",
@ -145,6 +147,7 @@ async def to_code(config):
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER])) cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW])) cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
cg.add( cg.add(
var.set_rmt_channel( var.set_rmt_channel(

View file

@ -119,4 +119,4 @@ def to_code(config):
cg.add_library("tonia/HeatpumpIR", "1.0.23") cg.add_library("tonia/HeatpumpIR", "1.0.23")
if CORE.is_esp8266 or CORE.is_esp32: if CORE.is_esp8266 or CORE.is_esp32:
cg.add_library("crankyoldgit/IRremoteESP8266", "2.7.12") cg.add_library("crankyoldgit/IRremoteESP8266", "2.8.4")

View file

@ -4,6 +4,7 @@ from esphome.const import (
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_HOST, PLATFORM_HOST,
CONF_MAC_ADDRESS,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.helpers import IS_MACOS from esphome.helpers import IS_MACOS
@ -28,13 +29,18 @@ def set_core_data(config):
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema({}), cv.Schema(
{
cv.Optional(CONF_MAC_ADDRESS, default="98:35:69:ab:f6:79"): cv.mac_address,
}
),
set_core_data, set_core_data,
) )
async def to_code(config): async def to_code(config):
cg.add_build_flag("-DUSE_HOST") cg.add_build_flag("-DUSE_HOST")
cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts)
cg.add_build_flag("-std=c++17") cg.add_build_flag("-std=c++17")
cg.add_build_flag("-lsodium") cg.add_build_flag("-lsodium")
if IS_MACOS: if IS_MACOS:

View file

@ -120,6 +120,7 @@ void LightState::loop() {
// Apply transformer (if any) // Apply transformer (if any)
if (this->transformer_ != nullptr) { if (this->transformer_ != nullptr) {
auto values = this->transformer_->apply(); auto values = this->transformer_->apply();
this->is_transformer_active_ = true;
if (values.has_value()) { if (values.has_value()) {
this->current_values = *values; this->current_values = *values;
this->output_->update_state(this); this->output_->update_state(this);
@ -131,6 +132,7 @@ void LightState::loop() {
this->current_values = this->transformer_->get_target_values(); this->current_values = this->transformer_->get_target_values();
this->transformer_->stop(); this->transformer_->stop();
this->is_transformer_active_ = false;
this->transformer_ = nullptr; this->transformer_ = nullptr;
this->target_state_reached_callback_.call(); this->target_state_reached_callback_.call();
} }
@ -214,6 +216,8 @@ void LightState::current_values_as_ct(float *color_temperature, float *white_bri
this->gamma_correct_); this->gamma_correct_);
} }
bool LightState::is_transformer_active() { return this->is_transformer_active_; }
void LightState::start_effect_(uint32_t effect_index) { void LightState::start_effect_(uint32_t effect_index) {
this->stop_effect_(); this->stop_effect_();
if (effect_index == 0) if (effect_index == 0)
@ -263,6 +267,7 @@ void LightState::start_flash_(const LightColorValues &target, uint32_t length, b
} }
void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
this->is_transformer_active_ = false;
this->transformer_ = nullptr; this->transformer_ = nullptr;
this->current_values = target; this->current_values = target;
if (set_remote_values) { if (set_remote_values) {

View file

@ -144,6 +144,17 @@ class LightState : public EntityBase, public Component {
void current_values_as_ct(float *color_temperature, float *white_brightness); void current_values_as_ct(float *color_temperature, float *white_brightness);
/**
* Indicator if a transformer (e.g. transition) is active. This is useful
* for effects e.g. at the start of the apply() method, add a check like:
*
* if (this->state_->is_transformer_active()) {
* // Something is already running.
* return;
* }
*/
bool is_transformer_active();
protected: protected:
friend LightOutput; friend LightOutput;
friend LightCall; friend LightCall;
@ -203,6 +214,9 @@ class LightState : public EntityBase, public Component {
LightRestoreMode restore_mode_; LightRestoreMode restore_mode_;
/// List of effects for this light. /// List of effects for this light.
std::vector<LightEffect *> effects_; std::vector<LightEffect *> effects_;
// for effects, true if a transformer (transition) is active.
bool is_transformer_active_ = false;
}; };
} // namespace light } // namespace light

View file

@ -3,6 +3,8 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include <vector>
namespace esphome { namespace esphome {
namespace lightwaverf { namespace lightwaverf {

View file

@ -212,6 +212,14 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
return; return;
#endif #endif
#ifdef USE_HOST #ifdef USE_HOST
time_t rawtime;
struct tm *timeinfo;
char buffer[80];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, sizeof buffer, "[%H:%M:%S]", timeinfo);
fputs(buffer, stdout);
puts(msg); puts(msg);
#endif #endif

View file

@ -0,0 +1,367 @@
import logging
import json
import hashlib
from urllib.parse import urljoin
from pathlib import Path
import requests
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.core import CORE, HexInt, EsphomeError
from esphome.components import esp32, microphone
from esphome import automation, git, external_files
from esphome.automation import register_action, register_condition
from esphome.const import (
__version__,
CONF_ID,
CONF_MICROPHONE,
CONF_MODEL,
CONF_URL,
CONF_FILE,
CONF_PATH,
CONF_REF,
CONF_REFRESH,
CONF_TYPE,
CONF_USERNAME,
CONF_PASSWORD,
CONF_RAW_DATA_ID,
TYPE_GIT,
TYPE_LOCAL,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@kahrendt", "@jesserockz"]
DEPENDENCIES = ["microphone"]
DOMAIN = "micro_wake_word"
CONF_PROBABILITY_CUTOFF = "probability_cutoff"
CONF_SLIDING_WINDOW_AVERAGE_SIZE = "sliding_window_average_size"
CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected"
TYPE_HTTP = "http"
micro_wake_word_ns = cg.esphome_ns.namespace("micro_wake_word")
MicroWakeWord = micro_wake_word_ns.class_("MicroWakeWord", cg.Component)
StartAction = micro_wake_word_ns.class_("StartAction", automation.Action)
StopAction = micro_wake_word_ns.class_("StopAction", automation.Action)
IsRunningCondition = micro_wake_word_ns.class_(
"IsRunningCondition", automation.Condition
)
def _validate_json_filename(value):
value = cv.string(value)
if not value.endswith(".json"):
raise cv.Invalid("Manifest filename must end with .json")
return value
def _process_git_source(config):
repo_dir, _ = git.clone_or_update(
url=config[CONF_URL],
ref=config.get(CONF_REF),
refresh=config[CONF_REFRESH],
domain=DOMAIN,
username=config.get(CONF_USERNAME),
password=config.get(CONF_PASSWORD),
)
if not (repo_dir / config[CONF_FILE]).exists():
raise cv.Invalid("File does not exist in repository")
return config
CV_GIT_SCHEMA = cv.GIT_SCHEMA
if isinstance(CV_GIT_SCHEMA, dict):
CV_GIT_SCHEMA = cv.Schema(CV_GIT_SCHEMA)
GIT_SCHEMA = cv.All(
CV_GIT_SCHEMA.extend(
{
cv.Required(CONF_FILE): _validate_json_filename,
cv.Optional(CONF_REFRESH, default="1d"): cv.All(
cv.string, cv.source_refresh
),
}
),
_process_git_source,
)
KEY_WAKE_WORD = "wake_word"
KEY_AUTHOR = "author"
KEY_WEBSITE = "website"
KEY_VERSION = "version"
KEY_MICRO = "micro"
KEY_MINIMUM_ESPHOME_VERSION = "minimum_esphome_version"
MANIFEST_SCHEMA_V1 = cv.Schema(
{
cv.Required(CONF_TYPE): "micro",
cv.Required(KEY_WAKE_WORD): cv.string,
cv.Required(KEY_AUTHOR): cv.string,
cv.Required(KEY_WEBSITE): cv.url,
cv.Required(KEY_VERSION): cv.All(cv.int_, 1),
cv.Required(CONF_MODEL): cv.string,
cv.Required(KEY_MICRO): cv.Schema(
{
cv.Required(CONF_PROBABILITY_CUTOFF): cv.float_,
cv.Required(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int,
cv.Optional(KEY_MINIMUM_ESPHOME_VERSION): cv.All(
cv.version_number, cv.validate_esphome_version
),
}
),
}
)
def _compute_local_file_path(config: dict) -> Path:
url = config[CONF_URL]
h = hashlib.new("sha256")
h.update(url.encode())
key = h.hexdigest()[:8]
base_dir = external_files.compute_local_file_dir(DOMAIN)
return base_dir / key
def _download_file(url: str, path: Path) -> bytes:
if not external_files.has_remote_file_changed(url, path):
_LOGGER.debug("Remote file has not changed, skipping download")
return path.read_bytes()
try:
req = requests.get(
url,
timeout=external_files.NETWORK_TIMEOUT,
headers={"User-agent": f"ESPHome/{__version__} (https://esphome.io)"},
)
req.raise_for_status()
except requests.exceptions.RequestException as e:
raise cv.Invalid(f"Could not download file from {url}: {e}") from e
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(req.content)
return req.content
def _process_http_source(config):
url = config[CONF_URL]
path = _compute_local_file_path(config)
json_path = path / "manifest.json"
json_contents = _download_file(url, json_path)
manifest_data = json.loads(json_contents)
if not isinstance(manifest_data, dict):
raise cv.Invalid("Manifest file must contain a JSON object")
try:
MANIFEST_SCHEMA_V1(manifest_data)
except cv.Invalid as e:
raise cv.Invalid(f"Invalid manifest file: {e}") from e
model = manifest_data[CONF_MODEL]
model_url = urljoin(url, model)
model_path = path / model
_download_file(str(model_url), model_path)
return config
HTTP_SCHEMA = cv.All(
{
cv.Required(CONF_URL): cv.url,
},
_process_http_source,
)
LOCAL_SCHEMA = cv.Schema(
{
cv.Required(CONF_PATH): cv.All(_validate_json_filename, cv.file_),
}
)
def _validate_source_model_name(value):
if not isinstance(value, str):
raise cv.Invalid("Model name must be a string")
if value.endswith(".json"):
raise cv.Invalid("Model name must not end with .json")
return MODEL_SOURCE_SCHEMA(
{
CONF_TYPE: TYPE_HTTP,
CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/{value}.json",
}
)
def _validate_source_shorthand(value):
if not isinstance(value, str):
raise cv.Invalid("Shorthand only for strings")
try: # Test for model name
return _validate_source_model_name(value)
except cv.Invalid:
pass
try: # Test for local path
return MODEL_SOURCE_SCHEMA({CONF_TYPE: TYPE_LOCAL, CONF_PATH: value})
except cv.Invalid:
pass
try: # Test for http url
return MODEL_SOURCE_SCHEMA({CONF_TYPE: TYPE_HTTP, CONF_URL: value})
except cv.Invalid:
pass
git_file = git.GitFile.from_shorthand(value)
conf = {
CONF_TYPE: TYPE_GIT,
CONF_URL: git_file.git_url,
CONF_FILE: git_file.filename,
}
if git_file.ref:
conf[CONF_REF] = git_file.ref
try:
return MODEL_SOURCE_SCHEMA(conf)
except cv.Invalid as e:
raise cv.Invalid(
f"Could not find file '{git_file.filename}' in the repository. Please make sure it exists."
) from e
MODEL_SOURCE_SCHEMA = cv.Any(
_validate_source_shorthand,
cv.typed_schema(
{
TYPE_GIT: GIT_SCHEMA,
TYPE_LOCAL: LOCAL_SCHEMA,
TYPE_HTTP: HTTP_SCHEMA,
}
),
msg="Not a valid model name, local path, http(s) url, or github shorthand",
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MicroWakeWord),
cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone),
cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage,
cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int,
cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation(
single=True
),
cv.Required(CONF_MODEL): MODEL_SOURCE_SCHEMA,
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
).extend(cv.COMPONENT_SCHEMA),
cv.only_with_esp_idf,
)
def _load_model_data(manifest_path: Path):
with open(manifest_path, encoding="utf-8") as f:
manifest = json.load(f)
try:
MANIFEST_SCHEMA_V1(manifest)
except cv.Invalid as e:
raise EsphomeError(f"Invalid manifest file: {e}") from e
model_path = urljoin(str(manifest_path), manifest[CONF_MODEL])
with open(model_path, "rb") as f:
model = f.read()
return manifest, model
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
mic = await cg.get_variable(config[CONF_MICROPHONE])
cg.add(var.set_microphone(mic))
if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED):
await automation.build_automation(
var.get_wake_word_detected_trigger(),
[(cg.std_string, "wake_word")],
on_wake_word_detection_config,
)
esp32.add_idf_component(
name="esp-tflite-micro",
repo="https://github.com/espressif/esp-tflite-micro",
)
cg.add_build_flag("-DTF_LITE_STATIC_MEMORY")
cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON")
cg.add_build_flag("-DESP_NN")
model_config = config.get(CONF_MODEL)
data = []
if model_config[CONF_TYPE] == TYPE_GIT:
# compute path to model file
key = f"{model_config[CONF_URL]}@{model_config.get(CONF_REF)}"
base_dir = Path(CORE.data_dir) / DOMAIN
h = hashlib.new("sha256")
h.update(key.encode())
file: Path = base_dir / h.hexdigest()[:8] / model_config[CONF_FILE]
elif model_config[CONF_TYPE] == TYPE_LOCAL:
file = model_config[CONF_PATH]
elif model_config[CONF_TYPE] == TYPE_HTTP:
file = _compute_local_file_path(model_config) / "manifest.json"
manifest, data = _load_model_data(file)
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_model_start(prog_arr))
probability_cutoff = config.get(
CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF]
)
cg.add(var.set_probability_cutoff(probability_cutoff))
sliding_window_average_size = config.get(
CONF_SLIDING_WINDOW_AVERAGE_SIZE,
manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE],
)
cg.add(var.set_sliding_window_average_size(sliding_window_average_size))
cg.add(var.set_wake_word(manifest[KEY_WAKE_WORD]))
MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)})
@register_action("micro_wake_word.start", StartAction, MICRO_WAKE_WORD_ACTION_SCHEMA)
@register_action("micro_wake_word.stop", StopAction, MICRO_WAKE_WORD_ACTION_SCHEMA)
@register_condition(
"micro_wake_word.is_running", IsRunningCondition, MICRO_WAKE_WORD_ACTION_SCHEMA
)
async def micro_wake_word_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var

View file

@ -0,0 +1,493 @@
#pragma once
#ifdef USE_ESP_IDF
// Converted audio_preprocessor_int8.tflite
// From https://github.com/tensorflow/tflite-micro/tree/main/tensorflow/lite/micro/examples/micro_speech/models accessed
// January 2024
//
// Copyright 2023 The TensorFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace esphome {
namespace micro_wake_word {
const unsigned char G_AUDIO_PREPROCESSOR_INT8_TFLITE[] = {
0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00,
0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xeb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67,
0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff,
0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x5f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc2, 0xf5, 0xff, 0xff,
0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65,
0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x2d, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f,
0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00,
0x08, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e,
0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x2e, 0x00,
0x00, 0x00, 0x9c, 0x0d, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xc4, 0x09, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00, 0x48,
0x09, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0xf8, 0x08, 0x00, 0x00,
0xec, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00, 0xc0, 0x06, 0x00, 0x00, 0x38, 0x04, 0x00,
0x00, 0xb0, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x60, 0x01,
0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c,
0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00,
0x04, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00,
0x00, 0xdc, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xbc, 0x00,
0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94,
0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff,
0x04, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04,
0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00,
0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x32, 0x2e, 0x30, 0x00,
0x00, 0x56, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x38, 0x2e, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xe1, 0xff, 0xff, 0xd8, 0xe1, 0xff, 0xff, 0xdc,
0xe1, 0xff, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe4, 0xe1, 0xff, 0xff, 0xe8, 0xe1, 0xff, 0xff, 0xec, 0xe1, 0xff, 0xff,
0xf0, 0xe1, 0xff, 0xff, 0xf4, 0xe1, 0xff, 0xff, 0xf8, 0xe1, 0xff, 0xff, 0xfc, 0xe1, 0xff, 0xff, 0x00, 0xe2, 0xff,
0xff, 0x04, 0xe2, 0xff, 0xff, 0x08, 0xe2, 0xff, 0xff, 0x0c, 0xe2, 0xff, 0xff, 0x10, 0xe2, 0xff, 0xff, 0x14, 0xe2,
0xff, 0xff, 0x18, 0xe2, 0xff, 0xff, 0x1c, 0xe2, 0xff, 0xff, 0x20, 0xe2, 0xff, 0xff, 0x24, 0xe2, 0xff, 0xff, 0x28,
0xe2, 0xff, 0xff, 0x2c, 0xe2, 0xff, 0xff, 0x30, 0xe2, 0xff, 0xff, 0xd2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0xe2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0xf2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xff,
0xff, 0xff, 0x02, 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x12,
0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x22, 0xf8, 0xff, 0xff,
0x04, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0b, 0x41,
0x01, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0e, 0x80, 0x05,
0x00, 0x00, 0x00, 0x00, 0xd1, 0x0c, 0x63, 0x04, 0x00, 0x00, 0x00, 0x00, 0x34, 0x0c, 0x3f, 0x04, 0x00, 0x00, 0x00,
0x00, 0x81, 0x0c, 0xf7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0d, 0x77, 0x06, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x0f,
0xa9, 0x08, 0x01, 0x02, 0x7f, 0x0b, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0e, 0xd1, 0x08, 0xdb, 0x02, 0x00,
0x00, 0x00, 0x00, 0x03, 0x0d, 0x4a, 0x07, 0xad, 0x01, 0x2c, 0x0c, 0xc6, 0x06, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00,
0x45, 0x0c, 0x29, 0x07, 0x23, 0x02, 0x34, 0x0d, 0x5b, 0x08, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0e, 0x48,
0x0a, 0xbd, 0x05, 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x0c, 0x88, 0x08, 0x43, 0x04,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0b, 0xd3, 0x07, 0xcb, 0x03, 0xd2, 0x0f, 0xe7,
0x0b, 0x09, 0x08, 0x39, 0x04, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0c, 0x14, 0x09,
0x75, 0x05, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0x0a, 0x6b, 0x07, 0x03,
0x04, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x0d, 0x09, 0x0a, 0xc9, 0x06, 0x93, 0x03, 0x65, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0d, 0x25, 0x0a, 0x12, 0x07, 0x07, 0x04, 0x05, 0x01, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x0e, 0x17, 0x0b, 0x2c, 0x08, 0x49, 0x05, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x98, 0x0f, 0xcb, 0x0c, 0x04, 0x0a, 0x44, 0x07, 0x8b, 0x04, 0xd8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x0f, 0x87,
0x0c, 0xe7, 0x09, 0x4e, 0x07, 0xba, 0x04, 0x2d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x0f, 0x23, 0x0d, 0xa7, 0x0a,
0x30, 0x08, 0xbe, 0x05, 0x52, 0x03, 0xeb, 0x00, 0x89, 0x0e, 0x2c, 0x0c, 0xd4, 0x09, 0x81, 0x07, 0x33, 0x05, 0xe9,
0x02, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x29, 0x0c, 0xf1, 0x09, 0xbe, 0x07, 0x90, 0x05, 0x65, 0x03,
0x3f, 0x01, 0x1d, 0x0f, 0xff, 0x0c, 0xe5, 0x0a, 0xcf, 0x08, 0xbc, 0x06, 0xae, 0x04, 0xa3, 0x02, 0x9c, 0x00, 0x99,
0x0e, 0x99, 0x0c, 0x9d, 0x0a, 0xa4, 0x08, 0xaf, 0x06, 0xbd, 0x04, 0xcf, 0x02, 0xe4, 0x00, 0xfc, 0x0e, 0x17, 0x0d,
0x36, 0x0b, 0x57, 0x09, 0x7c, 0x07, 0xa4, 0x05, 0xcf, 0x03, 0xfd, 0x01, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x62, 0x0e, 0x98, 0x0c, 0xd2, 0x0a, 0x0e, 0x09, 0x4d, 0x07, 0x8f, 0x05, 0xd4, 0x03, 0x1b, 0x02,
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x0e, 0x00, 0x0d, 0x52, 0x0b, 0xa6, 0x09, 0xfd, 0x07, 0x56, 0x06, 0xb1,
0x04, 0x0f, 0x03, 0x6f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x0f, 0x37, 0x0e, 0x9e, 0x0c,
0x08, 0x0b, 0x73, 0x09, 0xe1, 0x07, 0x52, 0x06, 0xc4, 0x04, 0x38, 0x03, 0xaf, 0x01, 0x28, 0x00, 0xa3, 0x0e, 0x1f,
0x0d, 0x9e, 0x0b, 0x1f, 0x0a, 0xa2, 0x08, 0x27, 0x07, 0xae, 0x05, 0x37, 0x04, 0xc2, 0x02, 0x4e, 0x01, 0x00, 0x00,
0x00, 0x00, 0xdd, 0x0f, 0x6d, 0x0e, 0xff, 0x0c, 0x93, 0x0b, 0x29, 0x0a, 0xc1, 0x08, 0x5a, 0x07, 0xf5, 0x05, 0x92,
0x04, 0x30, 0x03, 0xd1, 0x01, 0x73, 0x00, 0x16, 0x0f, 0xbc, 0x0d, 0x62, 0x0c, 0x0b, 0x0b, 0xb5, 0x09, 0x61, 0x08,
0x0e, 0x07, 0xbd, 0x05, 0x6d, 0x04, 0x1f, 0x03, 0xd3, 0x01, 0x88, 0x00, 0x3e, 0x0f, 0xf6, 0x0d, 0xaf, 0x0c, 0x6a,
0x0b, 0x27, 0x0a, 0xe4, 0x08, 0xa3, 0x07, 0x64, 0x06, 0x26, 0x05, 0xe9, 0x03, 0xae, 0x02, 0x74, 0x01, 0x3b, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0f, 0xce, 0x0d, 0x99, 0x0c, 0x66, 0x0b, 0x34, 0x0a, 0x03,
0x09, 0xd3, 0x07, 0xa5, 0x06, 0x78, 0x05, 0x4c, 0x04, 0x22, 0x03, 0xf8, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,
0xa9, 0x0f, 0x83, 0x0e, 0x5f, 0x0d, 0x3b, 0x0c, 0x19, 0x0b, 0xf8, 0x09, 0xd8, 0x08, 0xb9, 0x07, 0x9b, 0x06, 0x7e,
0x05, 0x63, 0x04, 0x48, 0x03, 0x2f, 0x02, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfa, 0xff, 0xff, 0x04, 0x00,
0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x04, 0xbe, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x7f, 0x0a, 0x00, 0x00,
0x00, 0x00, 0x2e, 0x03, 0x9c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x03, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x7e,
0x03, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x56, 0x07,
0xfe, 0x0d, 0x80, 0x04, 0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x16, 0x01, 0x2e, 0x07, 0x24, 0x0d, 0x00, 0x00, 0x00,
0x00, 0xfc, 0x02, 0xb5, 0x08, 0x52, 0x0e, 0xd3, 0x03, 0x39, 0x09, 0x86, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xba, 0x03,
0xd6, 0x08, 0xdc, 0x0d, 0xcb, 0x02, 0xa4, 0x07, 0x69, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0xb7, 0x05, 0x42,
0x0a, 0xba, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x03, 0x77, 0x07, 0xbc, 0x0b, 0xf1, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x2c, 0x08, 0x34, 0x0c, 0x2d, 0x00, 0x18, 0x04, 0xf6,
0x07, 0xc6, 0x0b, 0x89, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0xeb, 0x06, 0x8a, 0x0a,
0x1d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x01, 0x22, 0x05, 0x94, 0x08, 0xfc, 0x0b, 0x59,
0x0f, 0x00, 0x00, 0x00, 0x00, 0xac, 0x02, 0xf6, 0x05, 0x36, 0x09, 0x6c, 0x0c, 0x9a, 0x0f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xbe, 0x02, 0xda, 0x05, 0xed, 0x08, 0xf8, 0x0b, 0xfa, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xf5,
0x01, 0xe8, 0x04, 0xd3, 0x07, 0xb6, 0x0a, 0x92, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00,
0x34, 0x03, 0xfb, 0x05, 0xbb, 0x08, 0x74, 0x0b, 0x27, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x78, 0x03, 0x18,
0x06, 0xb1, 0x08, 0x45, 0x0b, 0xd2, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0xdc, 0x02, 0x58, 0x05, 0xcf, 0x07,
0x41, 0x0a, 0xad, 0x0c, 0x14, 0x0f, 0x76, 0x01, 0xd3, 0x03, 0x2b, 0x06, 0x7e, 0x08, 0xcc, 0x0a, 0x16, 0x0d, 0x5a,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x01, 0xd6, 0x03, 0x0e, 0x06, 0x41, 0x08, 0x6f, 0x0a, 0x9a, 0x0c, 0xc0, 0x0e,
0xe2, 0x00, 0x00, 0x03, 0x1a, 0x05, 0x30, 0x07, 0x43, 0x09, 0x51, 0x0b, 0x5c, 0x0d, 0x63, 0x0f, 0x66, 0x01, 0x66,
0x03, 0x62, 0x05, 0x5b, 0x07, 0x50, 0x09, 0x42, 0x0b, 0x30, 0x0d, 0x1b, 0x0f, 0x03, 0x01, 0xe8, 0x02, 0xc9, 0x04,
0xa8, 0x06, 0x83, 0x08, 0x5b, 0x0a, 0x30, 0x0c, 0x02, 0x0e, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x9d, 0x01, 0x67, 0x03, 0x2d, 0x05, 0xf1, 0x06, 0xb2, 0x08, 0x70, 0x0a, 0x2b, 0x0c, 0xe4, 0x0d, 0x9a, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 0xff, 0x02, 0xad, 0x04, 0x59, 0x06, 0x02, 0x08, 0xa9, 0x09, 0x4e, 0x0b, 0xf0,
0x0c, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xc8, 0x01, 0x61, 0x03, 0xf7, 0x04,
0x8c, 0x06, 0x1e, 0x08, 0xad, 0x09, 0x3b, 0x0b, 0xc7, 0x0c, 0x50, 0x0e, 0xd7, 0x0f, 0x5c, 0x01, 0xe0, 0x02, 0x61,
0x04, 0xe0, 0x05, 0x5d, 0x07, 0xd8, 0x08, 0x51, 0x0a, 0xc8, 0x0b, 0x3d, 0x0d, 0xb1, 0x0e, 0x00, 0x00, 0x00, 0x00,
0x22, 0x00, 0x92, 0x01, 0x00, 0x03, 0x6c, 0x04, 0xd6, 0x05, 0x3e, 0x07, 0xa5, 0x08, 0x0a, 0x0a, 0x6d, 0x0b, 0xcf,
0x0c, 0x2e, 0x0e, 0x8c, 0x0f, 0xe9, 0x00, 0x43, 0x02, 0x9d, 0x03, 0xf4, 0x04, 0x4a, 0x06, 0x9e, 0x07, 0xf1, 0x08,
0x42, 0x0a, 0x92, 0x0b, 0xe0, 0x0c, 0x2c, 0x0e, 0x77, 0x0f, 0xc1, 0x00, 0x09, 0x02, 0x50, 0x03, 0x95, 0x04, 0xd8,
0x05, 0x1b, 0x07, 0x5c, 0x08, 0x9b, 0x09, 0xd9, 0x0a, 0x16, 0x0c, 0x51, 0x0d, 0x8b, 0x0e, 0xc4, 0x0f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x31, 0x02, 0x66, 0x03, 0x99, 0x04, 0xcb, 0x05, 0xfc, 0x06, 0x2c,
0x08, 0x5a, 0x09, 0x87, 0x0a, 0xb3, 0x0b, 0xdd, 0x0c, 0x07, 0x0e, 0x2f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00,
0x7c, 0x01, 0xa0, 0x02, 0xc4, 0x03, 0xe6, 0x04, 0x07, 0x06, 0x27, 0x07, 0x46, 0x08, 0x64, 0x09, 0x81, 0x0a, 0x9c,
0x0b, 0xb7, 0x0c, 0xd0, 0x0d, 0xe8, 0x0e, 0x00, 0x10, 0x00, 0x00, 0x2a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10,
0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x24, 0x00, 0x26, 0x00, 0x2a, 0x00,
0x2e, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x5a,
0x00, 0x62, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, 0x92, 0x00, 0x9a, 0x00, 0xa6, 0x00,
0xb0, 0x00, 0xbc, 0x00, 0xc8, 0x00, 0xd4, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x8a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00,
0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00,
0x1c, 0x00, 0x20, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x30, 0x00, 0x34, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x44,
0x00, 0x4c, 0x00, 0x50, 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00,
0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xc4, 0x00, 0xd0, 0x00, 0xdc, 0x00, 0xe8,
0x00, 0xf4, 0x00, 0x00, 0x01, 0x0c, 0x01, 0x1c, 0x01, 0x2c, 0x01, 0x00, 0x00, 0xea, 0xfd, 0xff, 0xff, 0x04, 0x00,
0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04,
0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00,
0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08,
0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00,
0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4a, 0xfe, 0xff, 0xff, 0x04,
0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x7c, 0x7f, 0x79, 0x7f, 0x76, 0x7f, 0xfa, 0xff, 0x00, 0x00, 0x00, 0x00,
0x70, 0x7f, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7f, 0xe9, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x4b, 0x7f, 0xd0,
0xff, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x7f, 0xa0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x7e, 0x42, 0xff, 0x00, 0x00,
0x00, 0x00, 0xfd, 0x7d, 0x86, 0xfe, 0x04, 0x00, 0x00, 0x00, 0x87, 0x7c, 0x1d, 0xfd, 0x12, 0x00, 0x00, 0x00, 0xb6,
0x79, 0x7f, 0xfa, 0x3e, 0x00, 0x00, 0x00, 0x73, 0x74, 0xf9, 0xf5, 0xca, 0x00, 0x00, 0x00, 0x36, 0x6b, 0x33, 0xef,
0x32, 0x02, 0x00, 0x00, 0x9b, 0x5c, 0x87, 0xe7, 0xce, 0x04, 0x00, 0x00, 0xf0, 0x48, 0xde, 0xe2, 0xa0, 0x07, 0x00,
0x00, 0x6e, 0x33, 0x8a, 0xe4, 0xa4, 0x08, 0x00, 0x00, 0x9c, 0x20, 0x22, 0xeb, 0x4c, 0x07, 0x00, 0x00, 0x0a, 0x13,
0x7d, 0xf2, 0x02, 0x05, 0x00, 0x00, 0x89, 0x0a, 0x17, 0xf8, 0x06, 0x03, 0x00, 0x00, 0xa6, 0x05, 0xa0, 0xfb, 0xb4,
0x01, 0x00, 0x00, 0xfa, 0x02, 0xac, 0xfd, 0xe8, 0x00, 0x00, 0x00, 0x8e, 0x01, 0xc7, 0xfe, 0x7a, 0x00, 0x00, 0x00,
0xcf, 0x00, 0x5c, 0xff, 0x40, 0x00, 0x00, 0x00, 0x6b, 0x00, 0xab, 0xff, 0x22, 0x00, 0x00, 0x00, 0x38, 0x00, 0xd3,
0xff, 0x12, 0x00, 0x00, 0x00, 0x1d, 0x00, 0xea, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xf3, 0xff, 0x06, 0x00,
0x00, 0x00, 0x08, 0x00, 0xf8, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00,
0x92, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x02, 0x00, 0x04, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x13, 0x00, 0x17, 0x00,
0x1b, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x49, 0x00, 0x51,
0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8d, 0x00, 0x97, 0x00, 0xa1, 0x00,
0xac, 0x00, 0xb7, 0x00, 0xc2, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xe5, 0x00, 0xf2, 0x00, 0xff, 0x00, 0x0c, 0x01, 0x19,
0x01, 0x27, 0x01, 0x35, 0x01, 0x43, 0x01, 0x52, 0x01, 0x61, 0x01, 0x70, 0x01, 0x7f, 0x01, 0x8f, 0x01, 0x9f, 0x01,
0xaf, 0x01, 0xc0, 0x01, 0xd1, 0x01, 0xe2, 0x01, 0xf3, 0x01, 0x05, 0x02, 0x17, 0x02, 0x29, 0x02, 0x3c, 0x02, 0x4e,
0x02, 0x61, 0x02, 0x75, 0x02, 0x88, 0x02, 0x9c, 0x02, 0xb0, 0x02, 0xc4, 0x02, 0xd8, 0x02, 0xed, 0x02, 0x02, 0x03,
0x17, 0x03, 0x2c, 0x03, 0x41, 0x03, 0x57, 0x03, 0x6d, 0x03, 0x83, 0x03, 0x99, 0x03, 0xb0, 0x03, 0xc7, 0x03, 0xdd,
0x03, 0xf4, 0x03, 0x0c, 0x04, 0x23, 0x04, 0x3b, 0x04, 0x52, 0x04, 0x6a, 0x04, 0x82, 0x04, 0x9a, 0x04, 0xb3, 0x04,
0xcb, 0x04, 0xe4, 0x04, 0xfd, 0x04, 0x16, 0x05, 0x2f, 0x05, 0x48, 0x05, 0x61, 0x05, 0x7a, 0x05, 0x94, 0x05, 0xad,
0x05, 0xc7, 0x05, 0xe1, 0x05, 0xfb, 0x05, 0x15, 0x06, 0x2f, 0x06, 0x49, 0x06, 0x63, 0x06, 0x7e, 0x06, 0x98, 0x06,
0xb2, 0x06, 0xcd, 0x06, 0xe7, 0x06, 0x02, 0x07, 0x1d, 0x07, 0x37, 0x07, 0x52, 0x07, 0x6d, 0x07, 0x87, 0x07, 0xa2,
0x07, 0xbd, 0x07, 0xd8, 0x07, 0xf3, 0x07, 0x0d, 0x08, 0x28, 0x08, 0x43, 0x08, 0x5e, 0x08, 0x79, 0x08, 0x93, 0x08,
0xae, 0x08, 0xc9, 0x08, 0xe3, 0x08, 0xfe, 0x08, 0x19, 0x09, 0x33, 0x09, 0x4e, 0x09, 0x68, 0x09, 0x82, 0x09, 0x9d,
0x09, 0xb7, 0x09, 0xd1, 0x09, 0xeb, 0x09, 0x05, 0x0a, 0x1f, 0x0a, 0x39, 0x0a, 0x53, 0x0a, 0x6c, 0x0a, 0x86, 0x0a,
0x9f, 0x0a, 0xb8, 0x0a, 0xd1, 0x0a, 0xea, 0x0a, 0x03, 0x0b, 0x1c, 0x0b, 0x35, 0x0b, 0x4d, 0x0b, 0x66, 0x0b, 0x7e,
0x0b, 0x96, 0x0b, 0xae, 0x0b, 0xc5, 0x0b, 0xdd, 0x0b, 0xf4, 0x0b, 0x0c, 0x0c, 0x23, 0x0c, 0x39, 0x0c, 0x50, 0x0c,
0x67, 0x0c, 0x7d, 0x0c, 0x93, 0x0c, 0xa9, 0x0c, 0xbf, 0x0c, 0xd4, 0x0c, 0xe9, 0x0c, 0xfe, 0x0c, 0x13, 0x0d, 0x28,
0x0d, 0x3c, 0x0d, 0x50, 0x0d, 0x64, 0x0d, 0x78, 0x0d, 0x8b, 0x0d, 0x9f, 0x0d, 0xb2, 0x0d, 0xc4, 0x0d, 0xd7, 0x0d,
0xe9, 0x0d, 0xfb, 0x0d, 0x0d, 0x0e, 0x1e, 0x0e, 0x2f, 0x0e, 0x40, 0x0e, 0x51, 0x0e, 0x61, 0x0e, 0x71, 0x0e, 0x81,
0x0e, 0x90, 0x0e, 0x9f, 0x0e, 0xae, 0x0e, 0xbd, 0x0e, 0xcb, 0x0e, 0xd9, 0x0e, 0xe7, 0x0e, 0xf4, 0x0e, 0x01, 0x0f,
0x0e, 0x0f, 0x1b, 0x0f, 0x27, 0x0f, 0x33, 0x0f, 0x3e, 0x0f, 0x49, 0x0f, 0x54, 0x0f, 0x5f, 0x0f, 0x69, 0x0f, 0x73,
0x0f, 0x7d, 0x0f, 0x86, 0x0f, 0x8f, 0x0f, 0x98, 0x0f, 0xa0, 0x0f, 0xa8, 0x0f, 0xaf, 0x0f, 0xb7, 0x0f, 0xbe, 0x0f,
0xc4, 0x0f, 0xcb, 0x0f, 0xd0, 0x0f, 0xd6, 0x0f, 0xdb, 0x0f, 0xe0, 0x0f, 0xe5, 0x0f, 0xe9, 0x0f, 0xed, 0x0f, 0xf0,
0x0f, 0xf3, 0x0f, 0xf6, 0x0f, 0xf9, 0x0f, 0xfb, 0x0f, 0xfc, 0x0f, 0xfe, 0x0f, 0xff, 0x0f, 0x00, 0x10, 0x00, 0x10,
0x00, 0x10, 0x00, 0x10, 0xff, 0x0f, 0xfe, 0x0f, 0xfc, 0x0f, 0xfb, 0x0f, 0xf9, 0x0f, 0xf6, 0x0f, 0xf3, 0x0f, 0xf0,
0x0f, 0xed, 0x0f, 0xe9, 0x0f, 0xe5, 0x0f, 0xe0, 0x0f, 0xdb, 0x0f, 0xd6, 0x0f, 0xd0, 0x0f, 0xcb, 0x0f, 0xc4, 0x0f,
0xbe, 0x0f, 0xb7, 0x0f, 0xaf, 0x0f, 0xa8, 0x0f, 0xa0, 0x0f, 0x98, 0x0f, 0x8f, 0x0f, 0x86, 0x0f, 0x7d, 0x0f, 0x73,
0x0f, 0x69, 0x0f, 0x5f, 0x0f, 0x54, 0x0f, 0x49, 0x0f, 0x3e, 0x0f, 0x33, 0x0f, 0x27, 0x0f, 0x1b, 0x0f, 0x0e, 0x0f,
0x01, 0x0f, 0xf4, 0x0e, 0xe7, 0x0e, 0xd9, 0x0e, 0xcb, 0x0e, 0xbd, 0x0e, 0xae, 0x0e, 0x9f, 0x0e, 0x90, 0x0e, 0x81,
0x0e, 0x71, 0x0e, 0x61, 0x0e, 0x51, 0x0e, 0x40, 0x0e, 0x2f, 0x0e, 0x1e, 0x0e, 0x0d, 0x0e, 0xfb, 0x0d, 0xe9, 0x0d,
0xd7, 0x0d, 0xc4, 0x0d, 0xb2, 0x0d, 0x9f, 0x0d, 0x8b, 0x0d, 0x78, 0x0d, 0x64, 0x0d, 0x50, 0x0d, 0x3c, 0x0d, 0x28,
0x0d, 0x13, 0x0d, 0xfe, 0x0c, 0xe9, 0x0c, 0xd4, 0x0c, 0xbf, 0x0c, 0xa9, 0x0c, 0x93, 0x0c, 0x7d, 0x0c, 0x67, 0x0c,
0x50, 0x0c, 0x39, 0x0c, 0x23, 0x0c, 0x0c, 0x0c, 0xf4, 0x0b, 0xdd, 0x0b, 0xc5, 0x0b, 0xae, 0x0b, 0x96, 0x0b, 0x7e,
0x0b, 0x66, 0x0b, 0x4d, 0x0b, 0x35, 0x0b, 0x1c, 0x0b, 0x03, 0x0b, 0xea, 0x0a, 0xd1, 0x0a, 0xb8, 0x0a, 0x9f, 0x0a,
0x86, 0x0a, 0x6c, 0x0a, 0x53, 0x0a, 0x39, 0x0a, 0x1f, 0x0a, 0x05, 0x0a, 0xeb, 0x09, 0xd1, 0x09, 0xb7, 0x09, 0x9d,
0x09, 0x82, 0x09, 0x68, 0x09, 0x4e, 0x09, 0x33, 0x09, 0x19, 0x09, 0xfe, 0x08, 0xe3, 0x08, 0xc9, 0x08, 0xae, 0x08,
0x93, 0x08, 0x79, 0x08, 0x5e, 0x08, 0x43, 0x08, 0x28, 0x08, 0x0d, 0x08, 0xf3, 0x07, 0xd8, 0x07, 0xbd, 0x07, 0xa2,
0x07, 0x87, 0x07, 0x6d, 0x07, 0x52, 0x07, 0x37, 0x07, 0x1d, 0x07, 0x02, 0x07, 0xe7, 0x06, 0xcd, 0x06, 0xb2, 0x06,
0x98, 0x06, 0x7e, 0x06, 0x63, 0x06, 0x49, 0x06, 0x2f, 0x06, 0x15, 0x06, 0xfb, 0x05, 0xe1, 0x05, 0xc7, 0x05, 0xad,
0x05, 0x94, 0x05, 0x7a, 0x05, 0x61, 0x05, 0x48, 0x05, 0x2f, 0x05, 0x16, 0x05, 0xfd, 0x04, 0xe4, 0x04, 0xcb, 0x04,
0xb3, 0x04, 0x9a, 0x04, 0x82, 0x04, 0x6a, 0x04, 0x52, 0x04, 0x3b, 0x04, 0x23, 0x04, 0x0c, 0x04, 0xf4, 0x03, 0xdd,
0x03, 0xc7, 0x03, 0xb0, 0x03, 0x99, 0x03, 0x83, 0x03, 0x6d, 0x03, 0x57, 0x03, 0x41, 0x03, 0x2c, 0x03, 0x17, 0x03,
0x02, 0x03, 0xed, 0x02, 0xd8, 0x02, 0xc4, 0x02, 0xb0, 0x02, 0x9c, 0x02, 0x88, 0x02, 0x75, 0x02, 0x61, 0x02, 0x4e,
0x02, 0x3c, 0x02, 0x29, 0x02, 0x17, 0x02, 0x05, 0x02, 0xf3, 0x01, 0xe2, 0x01, 0xd1, 0x01, 0xc0, 0x01, 0xaf, 0x01,
0x9f, 0x01, 0x8f, 0x01, 0x7f, 0x01, 0x70, 0x01, 0x61, 0x01, 0x52, 0x01, 0x43, 0x01, 0x35, 0x01, 0x27, 0x01, 0x19,
0x01, 0x0c, 0x01, 0xff, 0x00, 0xf2, 0x00, 0xe5, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xac, 0x00,
0xa1, 0x00, 0x97, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x68, 0x00, 0x60, 0x00, 0x58, 0x00, 0x51,
0x00, 0x49, 0x00, 0x42, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2a, 0x00, 0x25, 0x00, 0x20, 0x00, 0x1b, 0x00,
0x17, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c,
0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xf4, 0x05, 0x00, 0x00, 0xf8, 0x05, 0x00,
0x00, 0xfc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0xc8, 0x04, 0x00, 0x00, 0x70,
0x04, 0x00, 0x00, 0x4c, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x00,
0x4c, 0x03, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00,
0x00, 0x48, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x78, 0x00,
0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf6, 0xfa, 0xff, 0xff, 0x0c,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x16, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3a, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0xa6, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, 0x00,
0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x68, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd6, 0xfc, 0xff, 0xff, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x98, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00,
0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xc8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x25,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xfd, 0xff, 0xff,
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00,
0x00, 0xf8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x84, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
0x00, 0x30, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c,
0x65, 0x00, 0x02, 0x24, 0x0f, 0x02, 0x01, 0x02, 0x03, 0x40, 0x04, 0x04, 0x04, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00,
0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x73, 0x6e,
0x72, 0x5f, 0x73, 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x01, 0x06, 0x04, 0x02, 0x24, 0x01, 0x01,
0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x20, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00,
0x00, 0x0a, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f,
0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67,
0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e,
0x67, 0x00, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d,
0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73,
0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x73, 0x70, 0x65, 0x63, 0x74,
0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74,
0x73, 0x00, 0x09, 0xa5, 0x88, 0x75, 0x6d, 0x59, 0x4d, 0x3a, 0x31, 0x23, 0x09, 0x00, 0x01, 0x00, 0x09, 0x00, 0x29,
0x3c, 0xd7, 0x03, 0x00, 0x00, 0x33, 0x03, 0x28, 0x00, 0x67, 0x3e, 0x99, 0x01, 0x0a, 0x00, 0x0e, 0x00, 0x05, 0x05,
0x69, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1b, 0x25, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0xfe, 0xff, 0xff, 0x10, 0x00,
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x54, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x28, 0x04, 0x02, 0x24, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff,
0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff,
0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00,
0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
0x00, 0xfe, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x10,
0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x65, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64,
0x65, 0x78, 0x00, 0x02, 0x17, 0x0e, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf1, 0x00, 0x05, 0x00, 0x05, 0x05,
0x06, 0x25, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17,
0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x54, 0x00, 0x66, 0x66, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74,
0x68, 0x00, 0x02, 0x0e, 0x0d, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x02, 0x05, 0x05, 0x06, 0x25,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10,
0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08,
0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00,
0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00,
0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73,
0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x07, 0x01, 0x01, 0x01, 0x0c, 0x04, 0x02, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xc4, 0x0a,
0x00, 0x00, 0x74, 0x0a, 0x00, 0x00, 0x3c, 0x0a, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x88,
0x09, 0x00, 0x00, 0x40, 0x09, 0x00, 0x00, 0xfc, 0x08, 0x00, 0x00, 0xb8, 0x08, 0x00, 0x00, 0x6c, 0x08, 0x00, 0x00,
0x20, 0x08, 0x00, 0x00, 0xd4, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x3c, 0x07, 0x00, 0x00, 0xf8, 0x06, 0x00,
0x00, 0xb8, 0x06, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x50, 0x06, 0x00, 0x00, 0x1c, 0x06, 0x00, 0x00, 0xd8, 0x05,
0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, 0xd8, 0x04, 0x00, 0x00, 0x98,
0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00,
0x6c, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00,
0x00, 0xe4, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x08, 0x01,
0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe,
0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x44, 0xf5, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72,
0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x84, 0xf5, 0xff, 0xff,
0x0d, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x7a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0xc0,
0xf5, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
0x00, 0xbe, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x61,
0x64, 0x64, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x18, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61,
0x74, 0x65, 0x44, 0x69, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2a, 0xf7, 0xff, 0xff, 0x00,
0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x10, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x61, 0x64, 0x64, 0x00, 0x01, 0x00, 0x00,
0x00, 0x28, 0x00, 0x00, 0x00, 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00,
0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xf6, 0xff, 0xff, 0x03,
0x00, 0x00, 0x00, 0x6d, 0x75, 0x6c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x8a, 0xf7, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x14, 0x00, 0x00, 0x00, 0xd0, 0xf6, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x32,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xbe, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14,
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00,
0x04, 0xf7, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x02, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x22,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x48, 0xf7, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
0x00, 0x3a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x73,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73,
0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x31, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, 0x00, 0x00,
0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34,
0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x27, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c,
0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0xe6, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f,
0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x32, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x78, 0xf8, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x72, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x14, 0x00, 0x00, 0x00, 0xb8,
0xf8, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x01, 0x00, 0x00, 0xa6, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xec, 0xf8, 0xff, 0xff, 0x06, 0x00,
0x00, 0x00, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xda,
0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x20, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72,
0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xec, 0x00,
0x00, 0x00, 0x16, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1a,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xf9, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
0x43, 0x61, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4a, 0xfa, 0xff,
0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x90, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x6c, 0x5f, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x86, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x69,
0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x66, 0x66, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0xbe,
0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x04, 0xfa, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67,
0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x31,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, 0x44, 0xfa, 0xff, 0xff,
0x15, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f,
0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x42, 0xfb,
0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x88, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68,
0x61, 0x70, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x76, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00,
0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0x00,
0x00, 0x00, 0xbc, 0xfa, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00,
0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xfc, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, 0x6f,
0x6e, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14,
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00,
0x2c, 0xfb, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x16, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43,
0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00,
0x00, 0x8c, 0xfb, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2f, 0x73, 0x68,
0x61, 0x70, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0xfc, 0xff, 0xff, 0x00,
0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x24, 0x00, 0x00, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79,
0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x2f, 0x79, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc2, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f,
0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x0a, 0xfd,
0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x50, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73,
0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, 0x00, 0x00,
0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28,
0x00, 0x00, 0x00, 0x98, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x9a, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0xe0,
0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x29, 0x00, 0x00, 0x00, 0xe2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00,
0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x28, 0xfd, 0xff, 0xff, 0x1a,
0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61,
0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00,
0x00, 0x2a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x70, 0xfd, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x73,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x6a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00,
0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0xb0, 0xfd,
0xff, 0xff, 0x13, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65,
0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xfe, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x24, 0x00, 0x00, 0x00, 0xf0, 0xfd, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65,
0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xee, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x34, 0xfe, 0xff,
0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f,
0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32,
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x78, 0xfe, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73,
0x74, 0x5f, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xa8,
0xfe, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xdc, 0xfe, 0xff, 0xff, 0x07, 0x00,
0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x5f, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xca,
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e,
0x73, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1c, 0x00,
0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x16,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x2c, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72,
0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f,
0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0,
0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00,
0xa4, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00,
0x00, 0x10, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x50, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x5c, 0xfe, 0xff, 0xff,
0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x68, 0xfe, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2a, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7c, 0xfe, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x12, 0x70, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x13,
0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b,
0x4c, 0x6f, 0x67, 0x00, 0x98, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x0a, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x50, 0x43, 0x41, 0x4e, 0x00, 0x00, 0xb8, 0xfe,
0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x23, 0x00, 0x00, 0x00, 0x53,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x70, 0x65, 0x63,
0x74, 0x72, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xf0, 0xfe, 0xff,
0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x00, 0x53, 0x69,
0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x71, 0x75, 0x61, 0x72,
0x65, 0x52, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x42, 0x61, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x6c, 0xff, 0xff, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x10, 0x00, 0x0f,
0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x35, 0x7c, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00,
0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0a,
0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x52, 0x66, 0x66, 0x74, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff,
0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67,
0x6e, 0x61, 0x6c, 0x46, 0x66, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x0c, 0x00,
0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x16, 0x0c, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67,
0x6e, 0x61, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00};
} // namespace micro_wake_word
} // namespace esphome
#endif // USE_ESP_IDF

View file

@ -0,0 +1,521 @@
#include "micro_wake_word.h"
/**
* This is a workaround until we can figure out a way to get
* the tflite-micro idf component code available in CI
*
* */
//
#ifndef CLANG_TIDY
#ifdef USE_ESP_IDF
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "audio_preprocessor_int8_model_data.h"
#include <tensorflow/lite/core/c/common.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
#include <cmath>
namespace esphome {
namespace micro_wake_word {
static const char *const TAG = "micro_wake_word";
static const size_t SAMPLE_RATE_HZ = 16000; // 16 kHz
static const size_t BUFFER_LENGTH = 500; // 0.5 seconds
static const size_t BUFFER_SIZE = SAMPLE_RATE_HZ / 1000 * BUFFER_LENGTH;
static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
float MicroWakeWord::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
static const LogString *micro_wake_word_state_to_string(State state) {
switch (state) {
case State::IDLE:
return LOG_STR("IDLE");
case State::START_MICROPHONE:
return LOG_STR("START_MICROPHONE");
case State::STARTING_MICROPHONE:
return LOG_STR("STARTING_MICROPHONE");
case State::DETECTING_WAKE_WORD:
return LOG_STR("DETECTING_WAKE_WORD");
case State::STOP_MICROPHONE:
return LOG_STR("STOP_MICROPHONE");
case State::STOPPING_MICROPHONE:
return LOG_STR("STOPPING_MICROPHONE");
default:
return LOG_STR("UNKNOWN");
}
}
void MicroWakeWord::dump_config() {
ESP_LOGCONFIG(TAG, "microWakeWord:");
ESP_LOGCONFIG(TAG, " Wake Word: %s", this->get_wake_word().c_str());
ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_);
ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_average_size_);
}
void MicroWakeWord::setup() {
ESP_LOGCONFIG(TAG, "Setting up microWakeWord...");
if (!this->initialize_models()) {
ESP_LOGE(TAG, "Failed to initialize models");
this->mark_failed();
return;
}
ExternalRAMAllocator<int16_t> allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE);
this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t));
if (this->input_buffer_ == nullptr) {
ESP_LOGW(TAG, "Could not allocate input buffer");
this->mark_failed();
return;
}
this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t));
if (this->ring_buffer_ == nullptr) {
ESP_LOGW(TAG, "Could not allocate ring buffer");
this->mark_failed();
return;
}
ESP_LOGCONFIG(TAG, "Micro Wake Word initialized");
}
int MicroWakeWord::read_microphone_() {
size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t));
if (bytes_read == 0) {
return 0;
}
size_t bytes_written = this->ring_buffer_->write((void *) this->input_buffer_, bytes_read);
if (bytes_written != bytes_read) {
ESP_LOGW(TAG, "Failed to write some data to ring buffer (written=%d, expected=%d)", bytes_written, bytes_read);
}
return bytes_written;
}
void MicroWakeWord::loop() {
switch (this->state_) {
case State::IDLE:
break;
case State::START_MICROPHONE:
ESP_LOGD(TAG, "Starting Microphone");
this->microphone_->start();
this->set_state_(State::STARTING_MICROPHONE);
this->high_freq_.start();
break;
case State::STARTING_MICROPHONE:
if (this->microphone_->is_running()) {
this->set_state_(State::DETECTING_WAKE_WORD);
}
break;
case State::DETECTING_WAKE_WORD:
this->read_microphone_();
if (this->detect_wake_word_()) {
ESP_LOGD(TAG, "Wake Word Detected");
this->detected_ = true;
this->set_state_(State::STOP_MICROPHONE);
}
break;
case State::STOP_MICROPHONE:
ESP_LOGD(TAG, "Stopping Microphone");
this->microphone_->stop();
this->set_state_(State::STOPPING_MICROPHONE);
this->high_freq_.stop();
break;
case State::STOPPING_MICROPHONE:
if (this->microphone_->is_stopped()) {
this->set_state_(State::IDLE);
if (this->detected_) {
this->detected_ = false;
this->wake_word_detected_trigger_->trigger("");
}
}
break;
}
}
void MicroWakeWord::start() {
if (this->is_failed()) {
ESP_LOGW(TAG, "Wake word component is marked as failed. Please check setup logs");
return;
}
if (this->state_ != State::IDLE) {
ESP_LOGW(TAG, "Wake word is already running");
return;
}
this->set_state_(State::START_MICROPHONE);
}
void MicroWakeWord::stop() {
if (this->state_ == State::IDLE) {
ESP_LOGW(TAG, "Wake word is already stopped");
return;
}
if (this->state_ == State::STOPPING_MICROPHONE) {
ESP_LOGW(TAG, "Wake word is already stopping");
return;
}
this->set_state_(State::STOP_MICROPHONE);
}
void MicroWakeWord::set_state_(State state) {
ESP_LOGD(TAG, "State changed from %s to %s", LOG_STR_ARG(micro_wake_word_state_to_string(this->state_)),
LOG_STR_ARG(micro_wake_word_state_to_string(state)));
this->state_ = state;
}
bool MicroWakeWord::initialize_models() {
ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ExternalRAMAllocator<int8_t> features_allocator(ExternalRAMAllocator<int8_t>::ALLOW_FAILURE);
ExternalRAMAllocator<int16_t> audio_samples_allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE);
this->streaming_tensor_arena_ = arena_allocator.allocate(STREAMING_MODEL_ARENA_SIZE);
if (this->streaming_tensor_arena_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena.");
return false;
}
this->streaming_var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE);
if (this->streaming_var_arena_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the streaming model variable's tensor arena.");
return false;
}
this->preprocessor_tensor_arena_ = arena_allocator.allocate(PREPROCESSOR_ARENA_SIZE);
if (this->preprocessor_tensor_arena_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the audio preprocessor model's tensor arena.");
return false;
}
this->new_features_data_ = features_allocator.allocate(PREPROCESSOR_FEATURE_SIZE);
if (this->new_features_data_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the audio features buffer.");
return false;
}
this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(SAMPLE_DURATION_COUNT);
if (this->preprocessor_audio_buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer.");
return false;
}
this->preprocessor_stride_buffer_ = audio_samples_allocator.allocate(HISTORY_SAMPLES_TO_KEEP);
if (this->preprocessor_stride_buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate the audio preprocessor's stride buffer.");
return false;
}
this->preprocessor_model_ = tflite::GetModel(G_AUDIO_PREPROCESSOR_INT8_TFLITE);
if (this->preprocessor_model_->version() != TFLITE_SCHEMA_VERSION) {
ESP_LOGE(TAG, "Wake word's audio preprocessor model's schema is not supported");
return false;
}
this->streaming_model_ = tflite::GetModel(this->model_start_);
if (this->streaming_model_->version() != TFLITE_SCHEMA_VERSION) {
ESP_LOGE(TAG, "Wake word's streaming model's schema is not supported");
return false;
}
static tflite::MicroMutableOpResolver<18> preprocessor_op_resolver;
static tflite::MicroMutableOpResolver<14> streaming_op_resolver;
if (!this->register_preprocessor_ops_(preprocessor_op_resolver))
return false;
if (!this->register_streaming_ops_(streaming_op_resolver))
return false;
tflite::MicroAllocator *ma =
tflite::MicroAllocator::Create(this->streaming_var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE);
this->mrv_ = tflite::MicroResourceVariables::Create(ma, 15);
static tflite::MicroInterpreter static_preprocessor_interpreter(
this->preprocessor_model_, preprocessor_op_resolver, this->preprocessor_tensor_arena_, PREPROCESSOR_ARENA_SIZE);
static tflite::MicroInterpreter static_streaming_interpreter(this->streaming_model_, streaming_op_resolver,
this->streaming_tensor_arena_,
STREAMING_MODEL_ARENA_SIZE, this->mrv_);
this->preprocessor_interperter_ = &static_preprocessor_interpreter;
this->streaming_interpreter_ = &static_streaming_interpreter;
// Allocate tensors for each models.
if (this->preprocessor_interperter_->AllocateTensors() != kTfLiteOk) {
ESP_LOGE(TAG, "Failed to allocate tensors for the audio preprocessor");
return false;
}
if (this->streaming_interpreter_->AllocateTensors() != kTfLiteOk) {
ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model");
return false;
}
// Verify input tensor matches expected values
TfLiteTensor *input = this->streaming_interpreter_->input(0);
if ((input->dims->size != 3) || (input->dims->data[0] != 1) || (input->dims->data[0] != 1) ||
(input->dims->data[1] != 1) || (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) {
ESP_LOGE(TAG, "Wake word detection model tensor input dimensions is not 1x1x%u", input->dims->data[2]);
return false;
}
if (input->type != kTfLiteInt8) {
ESP_LOGE(TAG, "Wake word detection model tensor input is not int8.");
return false;
}
// Verify output tensor matches expected values
TfLiteTensor *output = this->streaming_interpreter_->output(0);
if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) {
ESP_LOGE(TAG, "Wake word detection model tensor output dimensions is not 1x1.");
}
if (output->type != kTfLiteUInt8) {
ESP_LOGE(TAG, "Wake word detection model tensor input is not uint8.");
return false;
}
this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0);
return true;
}
bool MicroWakeWord::update_features_() {
// Retrieve strided audio samples
int16_t *audio_samples = nullptr;
if (!this->stride_audio_samples_(&audio_samples)) {
return false;
}
// Compute the features for the newest audio samples
if (!this->generate_single_feature_(audio_samples, SAMPLE_DURATION_COUNT, this->new_features_data_)) {
return false;
}
return true;
}
float MicroWakeWord::perform_streaming_inference_() {
TfLiteTensor *input = this->streaming_interpreter_->input(0);
size_t bytes_to_copy = input->bytes;
memcpy((void *) (tflite::GetTensorData<int8_t>(input)), (const void *) (this->new_features_data_), bytes_to_copy);
uint32_t prior_invoke = millis();
TfLiteStatus invoke_status = this->streaming_interpreter_->Invoke();
if (invoke_status != kTfLiteOk) {
ESP_LOGW(TAG, "Streaming Interpreter Invoke failed");
return false;
}
ESP_LOGV(TAG, "Streaming Inference Latency=%u ms", (millis() - prior_invoke));
TfLiteTensor *output = this->streaming_interpreter_->output(0);
return static_cast<float>(output->data.uint8[0]) / 255.0;
}
bool MicroWakeWord::detect_wake_word_() {
// Preprocess the newest audio samples into features
if (!this->update_features_()) {
return false;
}
// Perform inference
uint32_t streaming_size = micros();
float streaming_prob = this->perform_streaming_inference_();
// Add the most recent probability to the sliding window
this->recent_streaming_probabilities_[this->last_n_index_] = streaming_prob;
++this->last_n_index_;
if (this->last_n_index_ == this->sliding_window_average_size_)
this->last_n_index_ = 0;
float sum = 0.0;
for (auto &prob : this->recent_streaming_probabilities_) {
sum += prob;
}
float sliding_window_average = sum / static_cast<float>(this->sliding_window_average_size_);
// Ensure we have enough samples since the last positive detection
this->ignore_windows_ = std::min(this->ignore_windows_ + 1, 0);
if (this->ignore_windows_ < 0) {
return false;
}
// Detect the wake word if the sliding window average is above the cutoff
if (sliding_window_average > this->probability_cutoff_) {
this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION;
for (auto &prob : this->recent_streaming_probabilities_) {
prob = 0;
}
return true;
}
return false;
}
void MicroWakeWord::set_sliding_window_average_size(size_t size) {
this->sliding_window_average_size_ = size;
this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0);
}
bool MicroWakeWord::slice_available_() {
size_t available = this->ring_buffer_->available();
size_t free = this->ring_buffer_->free();
if (free < NEW_SAMPLES_TO_GET * sizeof(int16_t)) {
// If the ring buffer is within one audio slice of being full, then wake word detection will have issues.
// If this is constantly occuring, then some possibilities why are
// 1) there are too many other slow components configured
// 2) the ESP32 isn't fast enough; e.g., an ESP32 is much slower than an ESP32-S3 at inferences.
// 3) the model is too large
// 4) the model uses operations that are not optimized
ESP_LOGW(TAG,
"Audio buffer is nearly full. Wake word detection may be less accurate and have slower reponse times. "
#if !defined(USE_ESP32_VARIANT_ESP32S3)
"microWakeWord is designed for the ESP32-S3. The current platform is too slow for this model."
#endif
);
}
return available > (NEW_SAMPLES_TO_GET * sizeof(int16_t));
}
bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) {
if (!this->slice_available_()) {
return false;
}
// Copy 320 bytes (160 samples over 10 ms) into preprocessor_audio_buffer_ from history in
// preprocessor_stride_buffer_
memcpy((void *) (this->preprocessor_audio_buffer_), (void *) (this->preprocessor_stride_buffer_),
HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t));
// Copy 640 bytes (320 samples over 20 ms) from the ring buffer
// The first 320 bytes (160 samples over 10 ms) will be from history
size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_ + HISTORY_SAMPLES_TO_KEEP),
NEW_SAMPLES_TO_GET * sizeof(int16_t), pdMS_TO_TICKS(200));
if (bytes_read == 0) {
ESP_LOGE(TAG, "Could not read data from Ring Buffer");
} else if (bytes_read < NEW_SAMPLES_TO_GET * sizeof(int16_t)) {
ESP_LOGD(TAG, "Partial Read of Data by Model");
ESP_LOGD(TAG, "Could only read %d bytes when required %d bytes ", bytes_read,
(int) (NEW_SAMPLES_TO_GET * sizeof(int16_t)));
return false;
}
// Copy the last 320 bytes (160 samples over 10 ms) from the audio buffer into history stride buffer for the next
// iteration
memcpy((void *) (this->preprocessor_stride_buffer_), (void *) (this->preprocessor_audio_buffer_ + NEW_SAMPLES_TO_GET),
HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t));
*audio_samples = this->preprocessor_audio_buffer_;
return true;
}
bool MicroWakeWord::generate_single_feature_(const int16_t *audio_data, const int audio_data_size,
int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]) {
TfLiteTensor *input = this->preprocessor_interperter_->input(0);
TfLiteTensor *output = this->preprocessor_interperter_->output(0);
std::copy_n(audio_data, audio_data_size, tflite::GetTensorData<int16_t>(input));
if (this->preprocessor_interperter_->Invoke() != kTfLiteOk) {
ESP_LOGE(TAG, "Failed to preprocess audio for local wake word.");
return false;
}
std::memcpy(feature_output, tflite::GetTensorData<int8_t>(output), PREPROCESSOR_FEATURE_SIZE * sizeof(int8_t));
return true;
}
bool MicroWakeWord::register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver) {
if (op_resolver.AddReshape() != kTfLiteOk)
return false;
if (op_resolver.AddCast() != kTfLiteOk)
return false;
if (op_resolver.AddStridedSlice() != kTfLiteOk)
return false;
if (op_resolver.AddConcatenation() != kTfLiteOk)
return false;
if (op_resolver.AddMul() != kTfLiteOk)
return false;
if (op_resolver.AddAdd() != kTfLiteOk)
return false;
if (op_resolver.AddDiv() != kTfLiteOk)
return false;
if (op_resolver.AddMinimum() != kTfLiteOk)
return false;
if (op_resolver.AddMaximum() != kTfLiteOk)
return false;
if (op_resolver.AddWindow() != kTfLiteOk)
return false;
if (op_resolver.AddFftAutoScale() != kTfLiteOk)
return false;
if (op_resolver.AddRfft() != kTfLiteOk)
return false;
if (op_resolver.AddEnergy() != kTfLiteOk)
return false;
if (op_resolver.AddFilterBank() != kTfLiteOk)
return false;
if (op_resolver.AddFilterBankSquareRoot() != kTfLiteOk)
return false;
if (op_resolver.AddFilterBankSpectralSubtraction() != kTfLiteOk)
return false;
if (op_resolver.AddPCAN() != kTfLiteOk)
return false;
if (op_resolver.AddFilterBankLog() != kTfLiteOk)
return false;
return true;
}
bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<14> &op_resolver) {
if (op_resolver.AddCallOnce() != kTfLiteOk)
return false;
if (op_resolver.AddVarHandle() != kTfLiteOk)
return false;
if (op_resolver.AddReshape() != kTfLiteOk)
return false;
if (op_resolver.AddReadVariable() != kTfLiteOk)
return false;
if (op_resolver.AddStridedSlice() != kTfLiteOk)
return false;
if (op_resolver.AddConcatenation() != kTfLiteOk)
return false;
if (op_resolver.AddAssignVariable() != kTfLiteOk)
return false;
if (op_resolver.AddConv2D() != kTfLiteOk)
return false;
if (op_resolver.AddMul() != kTfLiteOk)
return false;
if (op_resolver.AddAdd() != kTfLiteOk)
return false;
if (op_resolver.AddMean() != kTfLiteOk)
return false;
if (op_resolver.AddFullyConnected() != kTfLiteOk)
return false;
if (op_resolver.AddLogistic() != kTfLiteOk)
return false;
if (op_resolver.AddQuantize() != kTfLiteOk)
return false;
return true;
}
} // namespace micro_wake_word
} // namespace esphome
#endif // USE_ESP_IDF
#endif // CLANG_TIDY

View file

@ -0,0 +1,207 @@
#pragma once
/**
* This is a workaround until we can figure out a way to get
* the tflite-micro idf component code available in CI
*
* */
//
#ifndef CLANG_TIDY
#ifdef USE_ESP_IDF
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/ring_buffer.h"
#include "esphome/components/microphone/microphone.h"
#include <tensorflow/lite/core/c/common.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
namespace esphome {
namespace micro_wake_word {
// The following are dictated by the preprocessor model
//
// The number of features the audio preprocessor generates per slice
static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40;
// How frequently the preprocessor generates a new set of features
static const uint8_t FEATURE_STRIDE_MS = 20;
// Duration of each slice used as input into the preprocessor
static const uint8_t FEATURE_DURATION_MS = 30;
// Audio sample frequency in hertz
static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000;
// The number of old audio samples that are saved to be part of the next feature window
static const uint16_t HISTORY_SAMPLES_TO_KEEP =
((FEATURE_DURATION_MS - FEATURE_STRIDE_MS) * (AUDIO_SAMPLE_FREQUENCY / 1000));
// The number of new audio samples to receive to be included with the next feature window
static const uint16_t NEW_SAMPLES_TO_GET = (FEATURE_STRIDE_MS * (AUDIO_SAMPLE_FREQUENCY / 1000));
// The total number of audio samples included in the feature window
static const uint16_t SAMPLE_DURATION_COUNT = FEATURE_DURATION_MS * AUDIO_SAMPLE_FREQUENCY / 1000;
// Number of bytes in memory needed for the preprocessor arena
static const uint32_t PREPROCESSOR_ARENA_SIZE = 9528;
// The following configure the streaming wake word model
//
// The number of audio slices to process before accepting a positive detection
static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74;
// Number of bytes in memory needed for the streaming wake word model
static const uint32_t STREAMING_MODEL_ARENA_SIZE = 64000;
static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024;
enum State {
IDLE,
START_MICROPHONE,
STARTING_MICROPHONE,
DETECTING_WAKE_WORD,
STOP_MICROPHONE,
STOPPING_MICROPHONE,
};
class MicroWakeWord : public Component {
public:
void setup() override;
void loop() override;
float get_setup_priority() const override;
void dump_config() override;
void start();
void stop();
bool is_running() const { return this->state_ != State::IDLE; }
bool initialize_models();
std::string get_wake_word() { return this->wake_word_; }
// Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate
void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; }
void set_sliding_window_average_size(size_t size);
void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; }
Trigger<std::string> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; }
void set_model_start(const uint8_t *model_start) { this->model_start_ = model_start; }
void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
protected:
void set_state_(State state);
int read_microphone_();
const uint8_t *model_start_;
std::string wake_word_;
microphone::Microphone *microphone_{nullptr};
Trigger<std::string> *wake_word_detected_trigger_ = new Trigger<std::string>();
State state_{State::IDLE};
HighFrequencyLoopRequester high_freq_;
std::unique_ptr<RingBuffer> ring_buffer_;
int16_t *input_buffer_;
const tflite::Model *preprocessor_model_{nullptr};
const tflite::Model *streaming_model_{nullptr};
tflite::MicroInterpreter *streaming_interpreter_{nullptr};
tflite::MicroInterpreter *preprocessor_interperter_{nullptr};
std::vector<float> recent_streaming_probabilities_;
size_t last_n_index_{0};
float probability_cutoff_{0.5};
size_t sliding_window_average_size_{10};
// When the wake word detection first starts or after the word has been detected once, we ignore this many audio
// feature slices before accepting a positive detection again
int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION};
uint8_t *streaming_var_arena_{nullptr};
uint8_t *streaming_tensor_arena_{nullptr};
uint8_t *preprocessor_tensor_arena_{nullptr};
int8_t *new_features_data_{nullptr};
tflite::MicroResourceVariables *mrv_{nullptr};
// Stores audio fed into feature generator preprocessor
int16_t *preprocessor_audio_buffer_;
int16_t *preprocessor_stride_buffer_;
bool detected_{false};
/** Detects if wake word has been said
*
* If enough audio samples are available, it will generate one slice of new features.
* If the streaming model predicts the wake word, then the nonstreaming model confirms it.
* @param ring_Buffer Ring buffer containing raw audio samples
* @return True if the wake word is detected, false otherwise
*/
bool detect_wake_word_();
/// @brief Returns true if there are enough audio samples in the buffer to generate another slice of features
bool slice_available_();
/** Shifts previous feature slices over by one and generates a new slice of features
*
* @param ring_buffer ring buffer containing raw audio samples
* @return True if a new slice of features was generated, false otherwise
*/
bool update_features_();
/** Generates features from audio samples
*
* Adapted from TFLite micro speech example
* @param audio_data Pointer to array with the audio samples
* @param audio_data_size The number of samples to use as input to the preprocessor model
* @param feature_output Array that will store the features
* @return True if successful, false otherwise.
*/
bool generate_single_feature_(const int16_t *audio_data, int audio_data_size,
int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]);
/** Performs inference over the most recent feature slice with the streaming model
*
* @return Probability of the wake word between 0.0 and 1.0
*/
float perform_streaming_inference_();
/** Strides the audio samples by keeping the last 10 ms of the previous slice
*
* Adapted from the TFLite micro speech example
* @param ring_buffer Ring buffer containing raw audio samples
* @param audio_samples Pointer to an array that will store the strided audio samples
* @return True if successful, false otherwise
*/
bool stride_audio_samples_(int16_t **audio_samples);
/// @brief Returns true if successfully registered the preprocessor's TensorFlow operations
bool register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver);
/// @brief Returns true if successfully registered the streaming model's TensorFlow operations
bool register_streaming_ops_(tflite::MicroMutableOpResolver<14> &op_resolver);
};
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<MicroWakeWord> {
public:
void play(Ts... x) override { this->parent_->start(); }
};
template<typename... Ts> class StopAction : public Action<Ts...>, public Parented<MicroWakeWord> {
public:
void play(Ts... x) override { this->parent_->stop(); }
};
template<typename... Ts> class IsRunningCondition : public Condition<Ts...>, public Parented<MicroWakeWord> {
public:
bool check(Ts... x) override { return this->parent_->is_running(); }
};
} // namespace micro_wake_word
} // namespace esphome
#endif // USE_ESP_IDF
#endif // CLANG_TIDY

View file

@ -62,6 +62,7 @@ from esphome.const import (
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME,
DEVICE_CLASS_VOLUME_FLOW_RATE,
DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_VOLUME_STORAGE,
DEVICE_CLASS_WATER, DEVICE_CLASS_WATER,
DEVICE_CLASS_WEIGHT, DEVICE_CLASS_WEIGHT,
@ -117,6 +118,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME,
DEVICE_CLASS_VOLUME_FLOW_RATE,
DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_VOLUME_STORAGE,
DEVICE_CLASS_WATER, DEVICE_CLASS_WATER,
DEVICE_CLASS_WEIGHT, DEVICE_CLASS_WEIGHT,

View file

@ -14,7 +14,7 @@ static const uint8_t NBITS_ADDRESS = 16;
static const uint8_t NBITS_CHANNEL = 5; static const uint8_t NBITS_CHANNEL = 5;
static const uint8_t NBITS_COMMAND = 7; static const uint8_t NBITS_COMMAND = 7;
static const uint8_t NDATABITS = NBITS_ADDRESS + NBITS_CHANNEL + NBITS_COMMAND; static const uint8_t NDATABITS = NBITS_ADDRESS + NBITS_CHANNEL + NBITS_COMMAND;
static const uint8_t MIN_RX_SRC = (NDATABITS * 2 + NBITS_SYNC / 2); static const uint8_t MIN_RX_SRC = (NDATABITS + NBITS_SYNC / 2);
static const uint8_t CMD_ON = 0x41; static const uint8_t CMD_ON = 0x41;
static const uint8_t CMD_OFF = 0x02; static const uint8_t CMD_OFF = 0x02;
@ -135,7 +135,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
.command = 0, .command = 0,
}; };
while (src.size() - src.get_index() > MIN_RX_SRC) { while (src.size() - src.get_index() >= MIN_RX_SRC) {
ESP_LOGVV(TAG, ESP_LOGVV(TAG,
"Decode Drayton: %" PRId32 ", %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 "Decode Drayton: %" PRId32 ", %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
" %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
@ -150,7 +150,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
} }
// Look for sync pulse, after. If sucessful index points to space of sync symbol // Look for sync pulse, after. If sucessful index points to space of sync symbol
while (src.size() - src.get_index() >= NDATABITS) { while (src.size() - src.get_index() >= MIN_RX_SRC) {
ESP_LOGVV(TAG, "Decode Drayton: sync search %d, %" PRId32 " %" PRId32, src.size() - src.get_index(), src.peek(), ESP_LOGVV(TAG, "Decode Drayton: sync search %d, %" PRId32 " %" PRId32, src.size() - src.get_index(), src.peek(),
src.peek(1)); src.peek(1));
if (src.peek_mark(2 * BIT_TIME_US) && if (src.peek_mark(2 * BIT_TIME_US) &&

View file

@ -82,6 +82,7 @@ from esphome.const import (
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME,
DEVICE_CLASS_VOLUME_FLOW_RATE,
DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_VOLUME_STORAGE,
DEVICE_CLASS_WATER, DEVICE_CLASS_WATER,
DEVICE_CLASS_WEIGHT, DEVICE_CLASS_WEIGHT,
@ -141,6 +142,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_VOLUME, DEVICE_CLASS_VOLUME,
DEVICE_CLASS_VOLUME_FLOW_RATE,
DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_VOLUME_STORAGE,
DEVICE_CLASS_WATER, DEVICE_CLASS_WATER,
DEVICE_CLASS_WEIGHT, DEVICE_CLASS_WEIGHT,

View file

@ -29,7 +29,8 @@ from esphome.const import (
from esphome.core import HexInt, CORE from esphome.core import HexInt, CORE
DOMAIN = "shelly_dimmer" DOMAIN = "shelly_dimmer"
DEPENDENCIES = ["sensor", "uart", "esp8266"] AUTO_LOAD = ["sensor"]
DEPENDENCIES = ["uart", "esp8266"]
shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer") shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer")
ShellyDimmer = shelly_dimmer_ns.class_( ShellyDimmer = shelly_dimmer_ns.class_(

View file

@ -25,6 +25,7 @@ namespace sntp {
static const char *const TAG = "sntp"; static const char *const TAG = "sntp";
void SNTPComponent::setup() { void SNTPComponent::setup() {
#ifndef USE_HOST
ESP_LOGCONFIG(TAG, "Setting up SNTP..."); ESP_LOGCONFIG(TAG, "Setting up SNTP...");
#if defined(USE_ESP32) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_LIBRETINY)
if (sntp_enabled()) { if (sntp_enabled()) {
@ -48,6 +49,7 @@ void SNTPComponent::setup() {
#endif #endif
sntp_init(); sntp_init();
#endif
} }
void SNTPComponent::dump_config() { void SNTPComponent::dump_config() {
ESP_LOGCONFIG(TAG, "SNTP Time:"); ESP_LOGCONFIG(TAG, "SNTP Time:");
@ -57,7 +59,7 @@ void SNTPComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
} }
void SNTPComponent::update() { void SNTPComponent::update() {
#ifndef USE_ESP_IDF #if !defined(USE_ESP_IDF) && !defined(USE_HOST)
// force resync // force resync
if (sntp_enabled()) { if (sntp_enabled()) {
sntp_stop(); sntp_stop();

View file

@ -28,7 +28,7 @@ class Filter {
* @param value The new value. * @param value The new value.
* @return An optional string, the new value that should be pushed out. * @return An optional string, the new value that should be pushed out.
*/ */
virtual optional<std::string> new_value(std::string value); virtual optional<std::string> new_value(std::string value) = 0;
/// Initialize this filter, please note this can be called more than once. /// Initialize this filter, please note this can be called more than once.
virtual void initialize(TextSensor *parent, Filter *next); virtual void initialize(TextSensor *parent, Filter *next);

View file

@ -8,6 +8,8 @@ wled_ns = cg.esphome_ns.namespace("wled")
WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect) WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect)
CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino) CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino)
CONF_SYNC_GROUP_MASK = "sync_group_mask"
CONF_BLANK_ON_START = "blank_on_start"
@register_addressable_effect( @register_addressable_effect(
@ -16,10 +18,13 @@ CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino)
"WLED", "WLED",
{ {
cv.Optional(CONF_PORT, default=21324): cv.port, cv.Optional(CONF_PORT, default=21324): cv.port,
cv.Optional(CONF_SYNC_GROUP_MASK, default=0): cv.int_range(min=0, max=255),
cv.Optional(CONF_BLANK_ON_START, default=True): cv.boolean,
}, },
) )
async def wled_light_effect_to_code(config, effect_id): async def wled_light_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(effect.set_port(config[CONF_PORT])) cg.add(effect.set_port(config[CONF_PORT]))
cg.add(effect.set_sync_group_mask(config[CONF_SYNC_GROUP_MASK]))
cg.add(effect.set_blank_on_start(config[CONF_BLANK_ON_START]))
return effect return effect

View file

@ -13,6 +13,10 @@
#include <WiFiUdp.h> #include <WiFiUdp.h>
#endif #endif
#ifdef USE_BK72XX
#include <WiFiUdp.h>
#endif
namespace esphome { namespace esphome {
namespace wled { namespace wled {
@ -29,7 +33,11 @@ WLEDLightEffect::WLEDLightEffect(const std::string &name) : AddressableLightEffe
void WLEDLightEffect::start() { void WLEDLightEffect::start() {
AddressableLightEffect::start(); AddressableLightEffect::start();
blank_at_ = 0; if (this->blank_on_start_) {
this->blank_at_ = 0;
} else {
this->blank_at_ = UINT32_MAX;
}
} }
void WLEDLightEffect::stop() { void WLEDLightEffect::stop() {
@ -101,8 +109,11 @@ bool WLEDLightEffect::parse_frame_(light::AddressableLight &it, const uint8_t *p
if (!parse_drgb_frame_(it, payload, size)) if (!parse_drgb_frame_(it, payload, size))
return false; return false;
} else { } else {
if (!parse_notifier_frame_(it, payload, size)) if (!parse_notifier_frame_(it, payload, size)) {
return false; return false;
} else {
timeout = UINT8_MAX;
}
} }
break; break;
@ -143,8 +154,32 @@ bool WLEDLightEffect::parse_frame_(light::AddressableLight &it, const uint8_t *p
} }
bool WLEDLightEffect::parse_notifier_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) { bool WLEDLightEffect::parse_notifier_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) {
// Packet needs to be empty // Receive at least RGBW and Brightness for all LEDs from WLED Sync Notification
return size == 0; // https://kno.wled.ge/interfaces/udp-notifier/
// https://github.com/Aircoookie/WLED/blob/main/wled00/udp.cpp
if (size < 34) {
return false;
}
uint8_t payload_sync_group_mask = payload[34];
if (this->sync_group_mask_ && !(payload_sync_group_mask & this->sync_group_mask_)) {
ESP_LOGD(TAG, "sync group mask does not match");
return false;
}
uint8_t bri = payload[0];
uint8_t r = esp_scale8(payload[1], bri);
uint8_t g = esp_scale8(payload[2], bri);
uint8_t b = esp_scale8(payload[3], bri);
uint8_t w = esp_scale8(payload[8], bri);
for (auto &&led : it) {
led.set(Color(r, g, b, w));
}
return true;
} }
bool WLEDLightEffect::parse_warls_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) { bool WLEDLightEffect::parse_warls_frame_(light::AddressableLight &it, const uint8_t *payload, uint16_t size) {

View file

@ -21,6 +21,8 @@ class WLEDLightEffect : public light::AddressableLightEffect {
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const Color &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
void set_port(uint16_t port) { this->port_ = port; } void set_port(uint16_t port) { this->port_ = port; }
void set_sync_group_mask(uint8_t mask) { this->sync_group_mask_ = mask; }
void set_blank_on_start(bool blank) { this->blank_on_start_ = blank; }
protected: protected:
void blank_all_leds_(light::AddressableLight &it); void blank_all_leds_(light::AddressableLight &it);
@ -35,6 +37,8 @@ class WLEDLightEffect : public light::AddressableLightEffect {
std::unique_ptr<UDP> udp_; std::unique_ptr<UDP> udp_;
uint32_t blank_at_{0}; uint32_t blank_at_{0};
uint32_t dropped_{0}; uint32_t dropped_{0};
uint8_t sync_group_mask_{0};
bool blank_on_start_{true};
}; };
} // namespace wled } // namespace wled

View file

@ -57,6 +57,7 @@ from esphome.const import (
TYPE_GIT, TYPE_GIT,
TYPE_LOCAL, TYPE_LOCAL,
VALID_SUBSTITUTIONS_CHARACTERS, VALID_SUBSTITUTIONS_CHARACTERS,
__version__ as ESPHOME_VERSION,
) )
from esphome.core import ( from esphome.core import (
CORE, CORE,
@ -1895,6 +1896,16 @@ def version_number(value):
raise Invalid("Not a valid version number") from e raise Invalid("Not a valid version number") from e
def validate_esphome_version(value: str):
min_version = Version.parse(value)
current_version = Version.parse(ESPHOME_VERSION)
if current_version < min_version:
raise Invalid(
f"Your ESPHome version is too old. Please update to at least {min_version}"
)
return value
def platformio_version_constraint(value): def platformio_version_constraint(value):
# for documentation on valid version constraints: # for documentation on valid version constraints:
# https://docs.platformio.org/en/latest/core/userguide/platforms/cmd_install.html#cmd-platform-install # https://docs.platformio.org/en/latest/core/userguide/platforms/cmd_install.html#cmd-platform-install

View file

@ -1,6 +1,6 @@
"""Constants used by esphome.""" """Constants used by esphome."""
__version__ = "2024.1.0-dev" __version__ = "2024.3.0-dev"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = ( VALID_SUBSTITUTIONS_CHARACTERS = (
@ -1082,6 +1082,7 @@ DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
DEVICE_CLASS_VOLTAGE = "voltage" DEVICE_CLASS_VOLTAGE = "voltage"
DEVICE_CLASS_VOLUME = "volume" DEVICE_CLASS_VOLUME = "volume"
DEVICE_CLASS_VOLUME_FLOW_RATE = "volume_flow_rate"
DEVICE_CLASS_VOLUME_STORAGE = "volume_storage" DEVICE_CLASS_VOLUME_STORAGE = "volume_storage"
DEVICE_CLASS_WATER = "water" DEVICE_CLASS_WATER = "water"
DEVICE_CLASS_WEIGHT = "weight" DEVICE_CLASS_WEIGHT = "weight"

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <string>
#include <functional>
#include <cmath> #include <cmath>
#include <cstdint>
#include <functional>
#include <string>
#include "esphome/core/optional.h" #include "esphome/core/optional.h"

View file

@ -102,16 +102,6 @@ def valid_project_name(value: str):
return value return value
def validate_version(value: str):
min_version = cv.Version.parse(value)
current_version = cv.Version.parse(ESPHOME_VERSION)
if current_version < min_version:
raise cv.Invalid(
f"Your ESPHome version is too old. Please update to at least {min_version}"
)
return value
if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ:
_compile_process_limit_default = min( _compile_process_limit_default = min(
int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]), int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]),
@ -164,7 +154,7 @@ CONFIG_SCHEMA = cv.All(
} }
), ),
cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All(
cv.version_number, validate_version cv.version_number, cv.validate_esphome_version
), ),
cv.Optional( cv.Optional(
CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default

View file

@ -11,6 +11,12 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#ifdef USE_HOST
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#if defined(USE_ESP8266) #if defined(USE_ESP8266)
#include <osapi.h> #include <osapi.h>
#include <user_interface.h> #include <user_interface.h>
@ -415,7 +421,7 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
int8_t step_to_accuracy_decimals(float step) { int8_t step_to_accuracy_decimals(float step) {
// use printf %g to find number of digits based on temperature step // use printf %g to find number of digits based on temperature step
char buf[32]; char buf[32];
sprintf(buf, "%.5g", step); snprintf(buf, sizeof buf, "%.5g", step);
std::string str{buf}; std::string str{buf};
size_t dot_pos = str.find('.'); size_t dot_pos = str.find('.');
@ -551,7 +557,10 @@ void HighFrequencyLoopRequester::stop() {
bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; } bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; }
void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter)
#if defined(USE_ESP32) #if defined(USE_HOST)
static const uint8_t esphome_host_mac_address[6] = USE_ESPHOME_HOST_MAC_ADDRESS;
memcpy(mac, esphome_host_mac_address, sizeof(esphome_host_mac_address));
#elif defined(USE_ESP32)
#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) #if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
// When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default // When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default
// returns the 802.15.4 EUI-64 address. Read directly from eFuse instead. // returns the 802.15.4 EUI-64 address. Read directly from eFuse instead.
@ -569,6 +578,8 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame
WiFi.macAddress(mac); WiFi.macAddress(mac);
#elif defined(USE_LIBRETINY) #elif defined(USE_LIBRETINY)
WiFi.macAddress(mac); WiFi.macAddress(mac);
#else
// this should be an error, but that messes with CI checks. #error No mac address method defined
#endif #endif
} }
std::string get_mac_address() { std::string get_mac_address() {

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <ctime> #include <ctime>
#include <string> #include <string>

View file

@ -478,7 +478,7 @@ def variable(
:param type_: Manually define a type for the variable, only use this when it's not possible :param type_: Manually define a type for the variable, only use this when it's not possible
to do so during config validation phase (for example because of template arguments). to do so during config validation phase (for example because of template arguments).
:returns The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
assert isinstance(id_, ID) assert isinstance(id_, ID)
rhs = safe_exp(rhs) rhs = safe_exp(rhs)
@ -526,7 +526,7 @@ def new_variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj
:param type_: Manually define a type for the variable, only use this when it's not possible :param type_: Manually define a type for the variable, only use this when it's not possible
to do so during config validation phase (for example because of template arguments). to do so during config validation phase (for example because of template arguments).
:returns The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
assert isinstance(id_, ID) assert isinstance(id_, ID)
rhs = safe_exp(rhs) rhs = safe_exp(rhs)
@ -549,7 +549,7 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj":
:param type_: Manually define a type for the variable, only use this when it's not possible :param type_: Manually define a type for the variable, only use this when it's not possible
to do so during config validation phase (for example because of template arguments). to do so during config validation phase (for example because of template arguments).
:returns The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
rhs = safe_exp(rhs) rhs = safe_exp(rhs)
obj = MockObj(id_, "->") obj = MockObj(id_, "->")
@ -570,7 +570,7 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable:
:param id_: The ID used to declare the variable (also specifies the type). :param id_: The ID used to declare the variable (also specifies the type).
:param args: The values to pass to the constructor. :param args: The values to pass to the constructor.
:returns The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
if args and isinstance(args[0], TemplateArguments): if args and isinstance(args[0], TemplateArguments):
id_ = id_.copy() id_ = id_.copy()

View file

@ -93,7 +93,7 @@ lib_deps =
ESP8266HTTPClient ; http_request (Arduino built-in) ESP8266HTTPClient ; http_request (Arduino built-in)
ESP8266mDNS ; mdns (Arduino built-in) ESP8266mDNS ; mdns (Arduino built-in)
DNSServer ; captive_portal (Arduino built-in) DNSServer ; captive_portal (Arduino built-in)
crankyoldgit/IRremoteESP8266@2.7.12 ; heatpumpir crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
build_flags = build_flags =
${common:arduino.build_flags} ${common:arduino.build_flags}
-Wno-nonnull-compare -Wno-nonnull-compare
@ -122,7 +122,7 @@ lib_deps =
ESPmDNS ; mdns (Arduino built-in) ESPmDNS ; mdns (Arduino built-in)
DNSServer ; captive_portal (Arduino built-in) DNSServer ; captive_portal (Arduino built-in)
esphome/ESP32-audioI2S@2.0.7 ; i2s_audio esphome/ESP32-audioI2S@2.0.7 ; i2s_audio
crankyoldgit/IRremoteESP8266@2.7.12 ; heatpumpir crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
droscy/esp_wireguard@0.3.2 ; wireguard droscy/esp_wireguard@0.3.2 ; wireguard
build_flags = build_flags =
${common:arduino.build_flags} ${common:arduino.build_flags}

View file

@ -1,4 +1,5 @@
async_timeout==4.0.3; python_version <= "3.10" async_timeout==4.0.3; python_version <= "3.10"
cryptography==42.0.2
voluptuous==0.14.1 voluptuous==0.14.1
PyYAML==6.0.1 PyYAML==6.0.1
paho-mqtt==1.6.1 paho-mqtt==1.6.1
@ -12,7 +13,7 @@ platformio==6.1.13 # When updating platformio, also update Dockerfile
esptool==4.7.0 esptool==4.7.0
click==8.1.7 click==8.1.7
esphome-dashboard==20231107.0 esphome-dashboard==20231107.0
aioesphomeapi==21.0.1 aioesphomeapi==21.0.2
zeroconf==0.131.0 zeroconf==0.131.0
python-magic==0.4.27 python-magic==0.4.27

View file

@ -1,3 +1,2 @@
pillow==10.2.0 pillow==10.2.0
cairosvg==2.7.1 cairosvg==2.7.1
cryptography==41.0.4

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 17
rx_pin:
number: 16
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 17
rx_pin:
number: 16
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a01nyub
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a01nyub
id: a01nyub_sensor
name: a01nyub Distance
uart_id: uart_a01nyub

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 17
rx_pin:
number: 16
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 17
rx_pin:
number: 16
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,13 @@
uart:
- id: uart_a02yyuw
tx_pin:
number: 4
rx_pin:
number: 5
baud_rate: 9600
sensor:
- platform: a02yyuw
id: a02yyuw_sensor
name: a02yyuw Distance
uart_id: uart_a02yyuw

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 2
dir_pin:
number: 3
sleep_pin:
number: 5
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 2
dir_pin:
number: 3
sleep_pin:
number: 5
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 22
dir_pin:
number: 23
sleep_pin:
number: 25
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 22
dir_pin:
number: 23
sleep_pin:
number: 25
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 1
dir_pin:
number: 2
sleep_pin:
number: 5
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,12 @@
stepper:
- platform: a4988
id: a4988_stepper
step_pin:
number: 2
dir_pin:
number: 3
sleep_pin:
number: 5
max_speed: 250 steps/s
acceleration: 100 steps/s^2
deceleration: 200 steps/s^2

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,21 @@
sensor:
- platform: absolute_humidity
name: Absolute Humidity
temperature: template_temperature
humidity: template_humidity
- platform: template
id: template_humidity
lambda: |-
if (millis() > 10000) {
return 0.6;
} else {
return 0.0;
}
- platform: template
id: template_temperature
lambda: |-
if (millis() > 10000) {
return 42.0;
} else {
return 0.0;
}

View file

@ -0,0 +1,7 @@
output:
- platform: ac_dimmer
id: ac_dimmer_1
gate_pin:
number: 5
zero_cross_pin:
number: 6

View file

@ -0,0 +1,7 @@
output:
- platform: ac_dimmer
id: ac_dimmer_1
gate_pin:
number: 12
zero_cross_pin:
number: 13

View file

@ -0,0 +1,7 @@
output:
- platform: ac_dimmer
id: ac_dimmer_1
gate_pin:
number: 5
zero_cross_pin:
number: 4

View file

@ -0,0 +1,7 @@
output:
- platform: ac_dimmer
id: ac_dimmer_1
gate_pin:
number: 5
zero_cross_pin:
number: 6

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 6
mosi_pin: 7
miso_pin: 5
adc128s102:
cs_pin: 8
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 6
mosi_pin: 7
miso_pin: 5
adc128s102:
cs_pin: 8
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 16
mosi_pin: 17
miso_pin: 15
adc128s102:
cs_pin: 12
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 16
mosi_pin: 17
miso_pin: 15
adc128s102:
cs_pin: 12
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 14
mosi_pin: 13
miso_pin: 12
adc128s102:
cs_pin: 15
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,14 @@
spi:
- id: spi_adc128s102
clk_pin: 2
mosi_pin: 3
miso_pin: 4
adc128s102:
cs_pin: 5
id: adc128s102_adc
sensor:
- platform: adc128s102
id: adc128s102_channel_0
channel: 0

View file

@ -0,0 +1,31 @@
light:
- platform: esp32_rmt_led_strip
id: led_matrix_32x8
default_transition_length: 500ms
chipset: ws2812
rgb_order: GRB
num_leds: 256
pin: 2
rmt_channel: 0
display:
- platform: addressable_light
id: led_matrix_32x8_display
addressable_light_id: led_matrix_32x8
width: 32
height: 8
pixel_mapper: |-
if (x % 2 == 0) {
return (x * 8) + y;
}
return (x * 8) + (7 - y);
lambda: |-
Color red = Color(0xFF0000);
Color green = Color(0x00FF00);
Color blue = Color(0x0000FF);
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
rotation: 0°
update_interval: 16ms

View file

@ -0,0 +1,31 @@
light:
- platform: esp32_rmt_led_strip
id: led_matrix_32x8
default_transition_length: 500ms
chipset: ws2812
rgb_order: GRB
num_leds: 256
pin: 2
rmt_channel: 0
display:
- platform: addressable_light
id: led_matrix_32x8_display
addressable_light_id: led_matrix_32x8
width: 32
height: 8
pixel_mapper: |-
if (x % 2 == 0) {
return (x * 8) + y;
}
return (x * 8) + (7 - y);
lambda: |-
Color red = Color(0xFF0000);
Color green = Color(0x00FF00);
Color blue = Color(0x0000FF);
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
rotation: 0°
update_interval: 16ms

View file

@ -0,0 +1,31 @@
light:
- platform: esp32_rmt_led_strip
id: led_matrix_32x8
default_transition_length: 500ms
chipset: ws2812
rgb_order: GRB
num_leds: 256
pin: 2
rmt_channel: 0
display:
- platform: addressable_light
id: led_matrix_32x8_display
addressable_light_id: led_matrix_32x8
width: 32
height: 8
pixel_mapper: |-
if (x % 2 == 0) {
return (x * 8) + y;
}
return (x * 8) + (7 - y);
lambda: |-
Color red = Color(0xFF0000);
Color green = Color(0x00FF00);
Color blue = Color(0x0000FF);
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
rotation: 0°
update_interval: 16ms

View file

@ -0,0 +1,32 @@
light:
- platform: fastled_clockless
id: led_matrix_32x8
name: led_matrix_32x8
chipset: WS2812B
pin: 2
num_leds: 256
rgb_order: GRB
default_transition_length: 0s
color_correct: [50%, 50%, 50%]
display:
- platform: addressable_light
id: led_matrix_32x8_display
addressable_light_id: led_matrix_32x8
width: 32
height: 8
pixel_mapper: |-
if (x % 2 == 0) {
return (x * 8) + y;
}
return (x * 8) + (7 - y);
lambda: |-
Color red = Color(0xFF0000);
Color green = Color(0x00FF00);
Color blue = Color(0x0000FF);
it.rectangle(0, 0, it.get_width(), it.get_height(), red);
it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green);
it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue);
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
rotation: 0°
update_interval: 16ms

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 5
sda: 4
sensor:
- platform: ade7953_i2c
irq_pin: 6
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 5
sda: 4
sensor:
- platform: ade7953_i2c
irq_pin: 6
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 16
sda: 17
sensor:
- platform: ade7953_i2c
irq_pin: 15
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 16
sda: 17
sensor:
- platform: ade7953_i2c
irq_pin: 15
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 5
sda: 4
sensor:
- platform: ade7953_i2c
irq_pin: 15
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,34 @@
i2c:
- id: i2c_ade7953
scl: 5
sda: 4
sensor:
- platform: ade7953_i2c
irq_pin: 6
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 6
mosi_pin: 7
miso_pin: 5
sensor:
- platform: ade7953_spi
cs_pin: 8
irq_pin: 9
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 6
mosi_pin: 7
miso_pin: 5
sensor:
- platform: ade7953_spi
cs_pin: 8
irq_pin: 9
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 16
mosi_pin: 17
miso_pin: 15
sensor:
- platform: ade7953_spi
cs_pin: 5
irq_pin: 13
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 16
mosi_pin: 17
miso_pin: 15
sensor:
- platform: ade7953_spi
cs_pin: 5
irq_pin: 13
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 14
mosi_pin: 13
miso_pin: 12
sensor:
- platform: ade7953_spi
cs_pin: 15
irq_pin: 5
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,36 @@
spi:
- id: spi_ade7953
clk_pin: 2
mosi_pin: 3
miso_pin: 4
sensor:
- platform: ade7953_spi
cs_pin: 5
irq_pin: 6
voltage:
name: ADE7953 Voltage
id: ade7953_voltage
current_a:
name: ADE7953 Current A
id: ade7953_current_a
current_b:
name: ADE7953 Current B
id: ade7953_current_b
power_factor_a:
name: ADE7953 Power Factor A
power_factor_b:
name: ADE7953 Power Factor B
apparent_power_a:
name: ADE7953 Apparent Power A
apparent_power_b:
name: ADE7953 Apparent Power B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
reactive_power_a:
name: ADE7953 Reactive Power A
reactive_power_b:
name: ADE7953 Reactive Power B
update_interval: 1s

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 5
sda: 4
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 5
sda: 4
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 16
sda: 17
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 16
sda: 17
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 5
sda: 4
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,13 @@
i2c:
- id: i2c_ads1115
scl: 5
sda: 4
ads1115:
address: 0x48
sensor:
- platform: ads1115
multiplexer: A0_A1
gain: 1.024
id: ads1115_sensor

View file

@ -0,0 +1,11 @@
i2c:
- id: i2c_aht10
scl: 5
sda: 4
sensor:
- platform: aht10
temperature:
name: Temperature
humidity:
name: Humidity

View file

@ -0,0 +1,11 @@
i2c:
- id: i2c_aht10
scl: 5
sda: 4
sensor:
- platform: aht10
temperature:
name: Temperature
humidity:
name: Humidity

Some files were not shown because too many files have changed in this diff Show more