mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
commit
ab48e4a466
12 changed files with 180 additions and 107 deletions
|
@ -33,7 +33,7 @@ class AQICalculator : public AbstractAQICalculator {
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||||
for (int i = 0; i < AMOUNT_OF_LEVELS - 1; i++) {
|
for (int i = 0; i < AMOUNT_OF_LEVELS; i++) {
|
||||||
if (value >= array[i][0] && value <= array[i][1]) {
|
if (value >= array[i][0] && value <= array[i][1]) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,17 +102,18 @@ class LightTurnOnTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
LightTurnOnTrigger(LightState *a_light) {
|
LightTurnOnTrigger(LightState *a_light) {
|
||||||
a_light->add_new_remote_values_callback([this, a_light]() {
|
a_light->add_new_remote_values_callback([this, a_light]() {
|
||||||
auto is_on = a_light->current_values.is_on();
|
// using the remote value because of transitions we need to trigger as early as possible
|
||||||
|
auto is_on = a_light->remote_values.is_on();
|
||||||
// only trigger when going from off to on
|
// only trigger when going from off to on
|
||||||
auto should_trigger = is_on && !last_on_;
|
auto should_trigger = is_on && !this->last_on_;
|
||||||
// Set new state immediately so that trigger() doesn't devolve
|
// Set new state immediately so that trigger() doesn't devolve
|
||||||
// into infinite loop
|
// into infinite loop
|
||||||
last_on_ = is_on;
|
this->last_on_ = is_on;
|
||||||
if (should_trigger) {
|
if (should_trigger) {
|
||||||
this->trigger();
|
this->trigger();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
last_on_ = a_light->current_values.is_on();
|
this->last_on_ = a_light->current_values.is_on();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -122,22 +123,14 @@ class LightTurnOnTrigger : public Trigger<> {
|
||||||
class LightTurnOffTrigger : public Trigger<> {
|
class LightTurnOffTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
LightTurnOffTrigger(LightState *a_light) {
|
LightTurnOffTrigger(LightState *a_light) {
|
||||||
a_light->add_new_remote_values_callback([this, a_light]() {
|
a_light->add_new_target_state_reached_callback([this, a_light]() {
|
||||||
auto is_on = a_light->current_values.is_on();
|
auto is_on = a_light->current_values.is_on();
|
||||||
// only trigger when going from on to off
|
// only trigger when going from on to off
|
||||||
auto should_trigger = !is_on && last_on_;
|
if (!is_on) {
|
||||||
// Set new state immediately so that trigger() doesn't devolve
|
|
||||||
// into infinite loop
|
|
||||||
last_on_ = is_on;
|
|
||||||
if (should_trigger) {
|
|
||||||
this->trigger();
|
this->trigger();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
last_on_ = a_light->current_values.is_on();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
bool last_on_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
||||||
|
|
|
@ -145,6 +145,7 @@ void LightState::loop() {
|
||||||
if (this->transformer_ != nullptr) {
|
if (this->transformer_ != nullptr) {
|
||||||
if (this->transformer_->is_finished()) {
|
if (this->transformer_->is_finished()) {
|
||||||
this->remote_values = this->current_values = this->transformer_->get_end_values();
|
this->remote_values = this->current_values = this->transformer_->get_end_values();
|
||||||
|
this->target_state_reached_callback_.call();
|
||||||
if (this->transformer_->publish_at_end())
|
if (this->transformer_->publish_at_end())
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
this->transformer_ = nullptr;
|
this->transformer_ = nullptr;
|
||||||
|
@ -336,6 +337,9 @@ void LightCall::perform() {
|
||||||
this->parent_->set_immediately_(v, this->publish_);
|
this->parent_->set_immediately_(v, this->publish_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this->has_transition_()) {
|
||||||
|
this->parent_->target_state_reached_callback_.call();
|
||||||
|
}
|
||||||
if (this->publish_) {
|
if (this->publish_) {
|
||||||
this->parent_->publish_state();
|
this->parent_->publish_state();
|
||||||
}
|
}
|
||||||
|
@ -395,13 +399,13 @@ LightColorValues LightCall::validate_() {
|
||||||
|
|
||||||
// sets RGB to 100% if only White specified
|
// sets RGB to 100% if only White specified
|
||||||
if (this->white_.has_value()) {
|
if (this->white_.has_value()) {
|
||||||
if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) {
|
|
||||||
this->red_ = optional<float>(1.0f);
|
|
||||||
this->green_ = optional<float>(1.0f);
|
|
||||||
this->blue_ = optional<float>(1.0f);
|
|
||||||
}
|
|
||||||
// make white values binary aka 0.0f or 1.0f...this allows brightness to do its job
|
|
||||||
if (traits.get_supports_color_interlock()) {
|
if (traits.get_supports_color_interlock()) {
|
||||||
|
if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) {
|
||||||
|
this->red_ = optional<float>(1.0f);
|
||||||
|
this->green_ = optional<float>(1.0f);
|
||||||
|
this->blue_ = optional<float>(1.0f);
|
||||||
|
}
|
||||||
|
// make white values binary aka 0.0f or 1.0f...this allows brightness to do its job
|
||||||
if (*this->white_ > 0.0f) {
|
if (*this->white_ > 0.0f) {
|
||||||
this->white_ = optional<float>(1.0f);
|
this->white_ = optional<float>(1.0f);
|
||||||
} else {
|
} else {
|
||||||
|
@ -411,11 +415,13 @@ LightColorValues LightCall::validate_() {
|
||||||
}
|
}
|
||||||
// White to 0% if (exclusively) setting any RGB value that isn't 255,255,255
|
// White to 0% if (exclusively) setting any RGB value that isn't 255,255,255
|
||||||
else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||||
if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f && traits.get_supports_rgb_white_value() &&
|
if (traits.get_supports_color_interlock()) {
|
||||||
traits.get_supports_color_interlock()) {
|
if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f &&
|
||||||
this->white_ = optional<float>(1.0f);
|
traits.get_supports_rgb_white_value() && traits.get_supports_color_interlock()) {
|
||||||
} else if (!this->white_.has_value() || !traits.get_supports_rgb_white_value()) {
|
this->white_ = optional<float>(1.0f);
|
||||||
this->white_ = optional<float>(0.0f);
|
} else if (!this->white_.has_value() || !traits.get_supports_rgb_white_value()) {
|
||||||
|
this->white_ = optional<float>(0.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if changing Kelvin alone, change to white light
|
// if changing Kelvin alone, change to white light
|
||||||
|
@ -752,6 +758,10 @@ void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bo
|
||||||
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
||||||
this->remote_values_callback_.add(std::move(send_callback));
|
this->remote_values_callback_.add(std::move(send_callback));
|
||||||
}
|
}
|
||||||
|
void LightState::add_new_target_state_reached_callback(std::function<void()> &&send_callback) {
|
||||||
|
this->target_state_reached_callback_.add(std::move(send_callback));
|
||||||
|
}
|
||||||
|
|
||||||
LightEffect *LightState::get_active_effect_() {
|
LightEffect *LightState::get_active_effect_() {
|
||||||
if (this->active_effect_index_ == 0)
|
if (this->active_effect_index_ == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -242,6 +242,13 @@ class LightState : public Nameable, public Component {
|
||||||
*/
|
*/
|
||||||
void add_new_remote_values_callback(std::function<void()> &&send_callback);
|
void add_new_remote_values_callback(std::function<void()> &&send_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback is called once the state of current_values and remote_values are equal
|
||||||
|
*
|
||||||
|
* @param send_callback
|
||||||
|
*/
|
||||||
|
void add_new_target_state_reached_callback(std::function<void()> &&send_callback);
|
||||||
|
|
||||||
/// Return whether the light has any effects that meet the trait requirements.
|
/// Return whether the light has any effects that meet the trait requirements.
|
||||||
bool supports_effects();
|
bool supports_effects();
|
||||||
|
|
||||||
|
@ -318,6 +325,12 @@ class LightState : public Nameable, public Component {
|
||||||
* starting with the beginning of the transition.
|
* starting with the beginning of the transition.
|
||||||
*/
|
*/
|
||||||
CallbackManager<void()> remote_values_callback_{};
|
CallbackManager<void()> remote_values_callback_{};
|
||||||
|
|
||||||
|
/** Callback to call when the state of current_values and remote_values are equal
|
||||||
|
* This should be called once the state of current_values changed and equals the state of remote_values
|
||||||
|
*/
|
||||||
|
CallbackManager<void()> target_state_reached_callback_{};
|
||||||
|
|
||||||
LightOutput *output_; ///< Store the output to allow effects to have more access.
|
LightOutput *output_; ///< Store the output to allow effects to have more access.
|
||||||
/// Whether the light value should be written in the next cycle.
|
/// Whether the light value should be written in the next cycle.
|
||||||
bool next_write_{true};
|
bool next_write_{true};
|
||||||
|
|
|
@ -229,7 +229,7 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||||
b = pixels[col];
|
b = pixels[col];
|
||||||
} else if (this->orientation_ == 2) {
|
} else if (this->orientation_ == 2) {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
b |= ((pixels[i] >> (7 - col)) << (7 - i));
|
b |= ((pixels[i] >> (7 - col)) & 1) << i;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b = pixels[7 - col];
|
b = pixels[7 - col];
|
||||||
|
|
|
@ -12,6 +12,75 @@ namespace xiaomi_ble {
|
||||||
|
|
||||||
static const char *TAG = "xiaomi_ble";
|
static const char *TAG = "xiaomi_ble";
|
||||||
|
|
||||||
|
bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result) {
|
||||||
|
// motion detection, 1 byte, 8-bit unsigned integer
|
||||||
|
if ((value_type == 0x03) && (value_length == 1)) {
|
||||||
|
result.has_motion = (data[0]) ? true : false;
|
||||||
|
}
|
||||||
|
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
||||||
|
else if ((value_type == 0x04) && (value_length == 2)) {
|
||||||
|
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.temperature = temperature / 10.0f;
|
||||||
|
}
|
||||||
|
// humidity, 2 bytes, 16-bit signed integer (LE), 0.1 %
|
||||||
|
else if ((value_type == 0x06) && (value_length == 2)) {
|
||||||
|
const int16_t humidity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.humidity = humidity / 10.0f;
|
||||||
|
}
|
||||||
|
// illuminance (+ motion), 3 bytes, 24-bit unsigned integer (LE), 1 lx
|
||||||
|
else if (((value_type == 0x07) || (value_type == 0x0F)) && (value_length == 3)) {
|
||||||
|
const uint32_t illuminance = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);
|
||||||
|
result.illuminance = illuminance;
|
||||||
|
result.is_light = (illuminance == 100) ? true : false;
|
||||||
|
if (value_type == 0x0F)
|
||||||
|
result.has_motion = true;
|
||||||
|
}
|
||||||
|
// soil moisture, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x08) && (value_length == 1)) {
|
||||||
|
result.moisture = data[0];
|
||||||
|
}
|
||||||
|
// conductivity, 2 bytes, 16-bit unsigned integer (LE), 1 µS/cm
|
||||||
|
else if ((value_type == 0x09) && (value_length == 2)) {
|
||||||
|
const uint16_t conductivity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.conductivity = conductivity;
|
||||||
|
}
|
||||||
|
// battery, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x0A) && (value_length == 1)) {
|
||||||
|
result.battery_level = data[0];
|
||||||
|
}
|
||||||
|
// temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 %
|
||||||
|
else if ((value_type == 0x0D) && (value_length == 4)) {
|
||||||
|
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
const int16_t humidity = uint16_t(data[2]) | (uint16_t(data[3]) << 8);
|
||||||
|
result.temperature = temperature / 10.0f;
|
||||||
|
result.humidity = humidity / 10.0f;
|
||||||
|
}
|
||||||
|
// formaldehyde, 2 bytes, 16-bit unsigned integer (LE), 0.01 mg / m3
|
||||||
|
else if ((value_type == 0x10) && (value_length == 2)) {
|
||||||
|
const uint16_t formaldehyde = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.formaldehyde = formaldehyde / 100.0f;
|
||||||
|
}
|
||||||
|
// on/off state, 1 byte, 8-bit unsigned integer
|
||||||
|
else if ((value_type == 0x12) && (value_length == 1)) {
|
||||||
|
result.is_active = (data[0]) ? true : false;
|
||||||
|
}
|
||||||
|
// mosquito tablet, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x13) && (value_length == 1)) {
|
||||||
|
result.tablet = data[0];
|
||||||
|
}
|
||||||
|
// idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min
|
||||||
|
else if ((value_type == 0x17) && (value_length == 4)) {
|
||||||
|
const uint32_t idle_time =
|
||||||
|
uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24);
|
||||||
|
result.idle_time = idle_time / 60.0f;
|
||||||
|
result.has_motion = (idle_time) ? false : true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result) {
|
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result) {
|
||||||
result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status
|
result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status
|
||||||
if (result.has_encryption) {
|
if (result.has_encryption) {
|
||||||
|
@ -25,81 +94,39 @@ bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult
|
||||||
// Byte 2: length
|
// Byte 2: length
|
||||||
// Byte 3..3+len-1: data point value
|
// Byte 3..3+len-1: data point value
|
||||||
|
|
||||||
const uint8_t *raw = message.data() + result.raw_offset;
|
const uint8_t *payload = message.data() + result.raw_offset;
|
||||||
const uint8_t *data = raw + 3;
|
uint8_t payload_length = message.size() - result.raw_offset;
|
||||||
const uint8_t data_length = raw[2];
|
uint8_t payload_offset = 0;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
if ((data_length < 1) || (data_length > 4)) {
|
if (payload_length < 4) {
|
||||||
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", data_length);
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", payload_length);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// motion detection, 1 byte, 8-bit unsigned integer
|
while (payload_length > 0) {
|
||||||
if ((raw[0] == 0x03) && (data_length == 1)) {
|
if (payload[payload_offset + 1] != 0x10) {
|
||||||
result.has_motion = (data[0]) ? true : false;
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
|
||||||
}
|
break;
|
||||||
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
}
|
||||||
else if ((raw[0] == 0x04) && (data_length == 2)) {
|
|
||||||
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
const uint8_t value_length = payload[payload_offset + 2];
|
||||||
result.temperature = temperature / 10.0f;
|
if ((value_length < 1) || (value_length > 4) || (payload_length < (3 + value_length))) {
|
||||||
}
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): value has wrong size (%d)!", value_length);
|
||||||
// humidity, 2 bytes, 16-bit signed integer (LE), 0.1 %
|
break;
|
||||||
else if ((raw[0] == 0x06) && (data_length == 2)) {
|
}
|
||||||
const int16_t humidity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.humidity = humidity / 10.0f;
|
const uint8_t value_type = payload[payload_offset + 0];
|
||||||
}
|
const uint8_t *data = &payload[payload_offset + 3];
|
||||||
// illuminance (+ motion), 3 bytes, 24-bit unsigned integer (LE), 1 lx
|
|
||||||
else if (((raw[0] == 0x07) || (raw[0] == 0x0F)) && (data_length == 3)) {
|
if (parse_xiaomi_value(value_type, data, value_length, result))
|
||||||
const uint32_t illuminance = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);
|
success = true;
|
||||||
result.illuminance = illuminance;
|
|
||||||
result.is_light = (illuminance == 100) ? true : false;
|
payload_length -= 3 + value_length;
|
||||||
if (raw[0] == 0x0F)
|
payload_offset += 3 + value_length;
|
||||||
result.has_motion = true;
|
|
||||||
}
|
|
||||||
// soil moisture, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x08) && (data_length == 1)) {
|
|
||||||
result.moisture = data[0];
|
|
||||||
}
|
|
||||||
// conductivity, 2 bytes, 16-bit unsigned integer (LE), 1 µS/cm
|
|
||||||
else if ((raw[0] == 0x09) && (data_length == 2)) {
|
|
||||||
const uint16_t conductivity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.conductivity = conductivity;
|
|
||||||
}
|
|
||||||
// battery, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x0A) && (data_length == 1)) {
|
|
||||||
result.battery_level = data[0];
|
|
||||||
}
|
|
||||||
// temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 %
|
|
||||||
else if ((raw[0] == 0x0D) && (data_length == 4)) {
|
|
||||||
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
const int16_t humidity = uint16_t(data[2]) | (uint16_t(data[3]) << 8);
|
|
||||||
result.temperature = temperature / 10.0f;
|
|
||||||
result.humidity = humidity / 10.0f;
|
|
||||||
}
|
|
||||||
// formaldehyde, 2 bytes, 16-bit unsigned integer (LE), 0.01 mg / m3
|
|
||||||
else if ((raw[0] == 0x10) && (data_length == 2)) {
|
|
||||||
const uint16_t formaldehyde = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.formaldehyde = formaldehyde / 100.0f;
|
|
||||||
}
|
|
||||||
// on/off state, 1 byte, 8-bit unsigned integer
|
|
||||||
else if ((raw[0] == 0x12) && (data_length == 1)) {
|
|
||||||
result.is_active = (data[0]) ? true : false;
|
|
||||||
}
|
|
||||||
// mosquito tablet, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x13) && (data_length == 1)) {
|
|
||||||
result.tablet = data[0];
|
|
||||||
}
|
|
||||||
// idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min
|
|
||||||
else if ((raw[0] == 0x17) && (data_length == 4)) {
|
|
||||||
const uint32_t idle_time =
|
|
||||||
uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24);
|
|
||||||
result.idle_time = idle_time / 60.0f;
|
|
||||||
result.has_motion = (idle_time) ? false : true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {
|
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct XiaomiAESVector {
|
||||||
size_t ivsize;
|
size_t ivsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result);
|
||||||
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result);
|
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result);
|
||||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data);
|
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data);
|
||||||
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address);
|
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address);
|
||||||
|
|
|
@ -11,10 +11,11 @@ from string import ascii_letters, digits
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from esphome import core
|
from esphome import core
|
||||||
from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
|
from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, \
|
||||||
CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, \
|
CONF_DISCOVERY, CONF_ID, CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, \
|
||||||
CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \
|
CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \
|
||||||
CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE
|
CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, \
|
||||||
|
CONF_TYPE, CONF_PACKAGES
|
||||||
from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
||||||
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
|
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
|
||||||
from esphome.helpers import list_starts_with, add_class_to_obj
|
from esphome.helpers import list_starts_with, add_class_to_obj
|
||||||
|
@ -1167,9 +1168,12 @@ class OnlyWith(Optional):
|
||||||
@property
|
@property
|
||||||
def default(self):
|
def default(self):
|
||||||
# pylint: disable=unsupported-membership-test
|
# pylint: disable=unsupported-membership-test
|
||||||
if self._component not in CORE.raw_config:
|
if (self._component in CORE.raw_config or
|
||||||
return vol.UNDEFINED
|
(CONF_PACKAGES in CORE.raw_config and
|
||||||
return self._default
|
self._component in
|
||||||
|
{list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})):
|
||||||
|
return self._default
|
||||||
|
return vol.UNDEFINED
|
||||||
|
|
||||||
@default.setter
|
@default.setter
|
||||||
def default(self, value):
|
def default(self, value):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
MAJOR_VERSION = 1
|
MAJOR_VERSION = 1
|
||||||
MINOR_VERSION = 15
|
MINOR_VERSION = 15
|
||||||
PATCH_VERSION = '2'
|
PATCH_VERSION = '3'
|
||||||
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
|
__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}'
|
||||||
__version__ = f'{__short_version__}.{PATCH_VERSION}'
|
__version__ = f'{__short_version__}.{PATCH_VERSION}'
|
||||||
|
|
||||||
|
|
|
@ -178,8 +178,8 @@ void delay_microseconds_accurate(uint32_t usec) {
|
||||||
if (usec <= 16383UL) {
|
if (usec <= 16383UL) {
|
||||||
delayMicroseconds(usec);
|
delayMicroseconds(usec);
|
||||||
} else {
|
} else {
|
||||||
delay(usec / 16383UL);
|
delay(usec / 1000UL);
|
||||||
delayMicroseconds(usec % 16383UL);
|
delayMicroseconds(usec % 1000UL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace esphome {
|
||||||
static const char *TAG = "scheduler";
|
static const char *TAG = "scheduler";
|
||||||
|
|
||||||
static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL;
|
static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL;
|
||||||
|
static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
|
||||||
|
|
||||||
// Uncomment to debug scheduler
|
// Uncomment to debug scheduler
|
||||||
// #define ESPHOME_DEBUG_SCHEDULER
|
// #define ESPHOME_DEBUG_SCHEDULER
|
||||||
|
@ -107,6 +108,26 @@ void ICACHE_RAM_ATTR HOT Scheduler::call() {
|
||||||
}
|
}
|
||||||
#endif // ESPHOME_DEBUG_SCHEDULER
|
#endif // ESPHOME_DEBUG_SCHEDULER
|
||||||
|
|
||||||
|
auto to_remove_was = to_remove_;
|
||||||
|
auto items_was = items_.size();
|
||||||
|
// If we have too many items to remove
|
||||||
|
if (to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
|
||||||
|
std::vector<std::unique_ptr<SchedulerItem>> valid_items;
|
||||||
|
while (!this->empty_()) {
|
||||||
|
auto item = std::move(this->items_[0]);
|
||||||
|
this->pop_raw_();
|
||||||
|
valid_items.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
this->items_ = std::move(valid_items);
|
||||||
|
|
||||||
|
// The following should not happen unless I'm missing something
|
||||||
|
if (to_remove_ != 0) {
|
||||||
|
ESP_LOGW(TAG, "to_remove_ was %u now: %u items where %zu now %zu. Please report this", to_remove_was, to_remove_,
|
||||||
|
items_was, items_.size());
|
||||||
|
to_remove_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (!this->empty_()) {
|
while (!this->empty_()) {
|
||||||
// use scoping to indicate visibility of `item` variable
|
// use scoping to indicate visibility of `item` variable
|
||||||
{
|
{
|
||||||
|
@ -147,6 +168,7 @@ void ICACHE_RAM_ATTR HOT Scheduler::call() {
|
||||||
|
|
||||||
if (item->remove) {
|
if (item->remove) {
|
||||||
// We were removed/cancelled in the function call, stop
|
// We were removed/cancelled in the function call, stop
|
||||||
|
to_remove_--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +204,7 @@ void HOT Scheduler::cleanup_() {
|
||||||
if (!item->remove)
|
if (!item->remove)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
to_remove_--;
|
||||||
this->pop_raw_();
|
this->pop_raw_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +216,8 @@ void HOT Scheduler::push_(std::unique_ptr<Scheduler::SchedulerItem> item) { this
|
||||||
bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, Scheduler::SchedulerItem::Type type) {
|
bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, Scheduler::SchedulerItem::Type type) {
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
for (auto &it : this->items_)
|
for (auto &it : this->items_)
|
||||||
if (it->component == component && it->name == name && it->type == type) {
|
if (it->component == component && it->name == name && it->type == type && !it->remove) {
|
||||||
|
to_remove_++;
|
||||||
it->remove = true;
|
it->remove = true;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ class Scheduler {
|
||||||
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
|
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
|
||||||
uint32_t last_millis_{0};
|
uint32_t last_millis_{0};
|
||||||
uint8_t millis_major_{0};
|
uint8_t millis_major_{0};
|
||||||
|
uint32_t to_remove_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
Loading…
Reference in a new issue