mirror of
https://github.com/esphome/esphome.git
synced 2025-11-06 01:51:49 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab48e4a466 | ||
|
|
8ef0f5b047 | ||
|
|
2c14d134be | ||
|
|
9cd21bb5a0 | ||
|
|
bd061ac2ee | ||
|
|
dd3e821857 | ||
|
|
b38b7019ea | ||
|
|
2c71ee7853 | ||
|
|
540c62061d | ||
|
|
221ef07c8b | ||
|
|
29fc7ea154 | ||
|
|
a3b2d384f5 | ||
|
|
50238f8d72 | ||
|
|
704470d606 | ||
|
|
f7e6195466 | ||
|
|
a0bb7c3ed0 | ||
|
|
c4edd3047f | ||
|
|
c50da1593a | ||
|
|
1d06426281 | ||
|
|
9c5b693dd5 | ||
|
|
5fecc70db1 |
@@ -3,7 +3,8 @@ import esphome.config_validation as cv
|
|||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition
|
from esphome.automation import Condition
|
||||||
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
|
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
|
||||||
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT
|
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT, \
|
||||||
|
CONF_TAG
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import coroutine_with_priority
|
||||||
|
|
||||||
DEPENDENCIES = ['network']
|
DEPENDENCIES = ['network']
|
||||||
@@ -137,6 +138,23 @@ def homeassistant_event_to_code(config, action_id, template_arg, args):
|
|||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({
|
||||||
|
cv.GenerateID(): cv.use_id(APIServer),
|
||||||
|
cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
|
||||||
|
}, key=CONF_TAG)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action('homeassistant.tag_scanned', HomeAssistantServiceCallAction,
|
||||||
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA)
|
||||||
|
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = yield cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||||
|
cg.add(var.set_service('esphome.tag_scanned'))
|
||||||
|
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||||
|
cg.add(var.add_data('tag_id', templ))
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
||||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
def api_connected_to_code(config, condition_id, template_arg, args):
|
||||||
yield cg.new_Pvariable(condition_id, template_arg)
|
yield cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ static const char *TAG = "remote_receiver.esp32";
|
|||||||
|
|
||||||
void RemoteReceiverComponent::setup() {
|
void RemoteReceiverComponent::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
|
ESP_LOGCONFIG(TAG, "Setting up Remote Receiver...");
|
||||||
|
this->pin_->setup();
|
||||||
rmt_config_t rmt{};
|
rmt_config_t rmt{};
|
||||||
this->config_rmt(rmt);
|
this->config_rmt(rmt);
|
||||||
rmt.gpio_num = gpio_num_t(this->pin_->get_pin());
|
rmt.gpio_num = gpio_num_t(this->pin_->get_pin());
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ bool parse_ruuvi_data_byte(const esp32_ble_tracker::adv_data_t &adv_data, RuuviP
|
|||||||
const float acceleration_y = (int16_t(data[8] << 8) + int16_t(data[9])) / 1000.0f;
|
const float acceleration_y = (int16_t(data[8] << 8) + int16_t(data[9])) / 1000.0f;
|
||||||
const float acceleration_z = (int16_t(data[10] << 8) + int16_t(data[11])) / 1000.0f;
|
const float acceleration_z = (int16_t(data[10] << 8) + int16_t(data[11])) / 1000.0f;
|
||||||
|
|
||||||
const uint8_t power_info = (data[12] << 8) | data[13];
|
const uint16_t power_info = (uint16_t(data[12] << 8) | data[13]);
|
||||||
const float battery_voltage = ((power_info >> 5) + 1600.0f) / 1000.0f;
|
const float battery_voltage = ((power_info >> 5) + 1600.0f) / 1000.0f;
|
||||||
const float tx_power = ((power_info & 0x1F) * 2.0f) - 40.0f;
|
const float tx_power = ((power_info & 0x1F) * 2.0f) - 40.0f;
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,6 @@ void SNTPComponent::setup() {
|
|||||||
sntp_setservername(2, strdup(this->server_3_.c_str()));
|
sntp_setservername(2, strdup(this->server_3_.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
|
||||||
// let localtime/gmtime handle timezones, not sntp
|
|
||||||
sntp_set_timezone(0);
|
|
||||||
#endif
|
|
||||||
sntp_init();
|
sntp_init();
|
||||||
}
|
}
|
||||||
void SNTPComponent::dump_config() {
|
void SNTPComponent::dump_config() {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ from .. import tuya_ns, CONF_TUYA_ID, Tuya
|
|||||||
DEPENDENCIES = ['tuya']
|
DEPENDENCIES = ['tuya']
|
||||||
CODEOWNERS = ['@jesserockz']
|
CODEOWNERS = ['@jesserockz']
|
||||||
|
|
||||||
CONF_TARGET_TEMPERATURE_DATAPOINT = "target_temperature_datapoint"
|
CONF_TARGET_TEMPERATURE_DATAPOINT = 'target_temperature_datapoint'
|
||||||
CONF_CURRENT_TEMPERATURE_DATAPOINT = "current_temperature_datapoint"
|
CONF_CURRENT_TEMPERATURE_DATAPOINT = 'current_temperature_datapoint'
|
||||||
# CONF_ECO_MODE_DATAPOINT = "eco_mode_datapoint"
|
CONF_TEMPERATURE_MULTIPLIER = 'temperature_multiplier'
|
||||||
|
|
||||||
TuyaClimate = tuya_ns.class_('TuyaClimate', climate.Climate, cg.Component)
|
TuyaClimate = tuya_ns.class_('TuyaClimate', climate.Climate, cg.Component)
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
|||||||
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t,
|
||||||
# cv.Optional(CONF_ECO_MODE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_TEMPERATURE_MULTIPLIER, default=1): cv.positive_float,
|
||||||
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(
|
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(
|
||||||
CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT))
|
CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT))
|
||||||
|
|
||||||
@@ -38,5 +38,4 @@ def to_code(config):
|
|||||||
cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT]))
|
cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT]))
|
||||||
if CONF_CURRENT_TEMPERATURE_DATAPOINT in config:
|
if CONF_CURRENT_TEMPERATURE_DATAPOINT in config:
|
||||||
cg.add(var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT]))
|
cg.add(var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT]))
|
||||||
# if CONF_ECO_MODE_DATAPOINT in config:
|
cg.add(var.set_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER]))
|
||||||
# cg.add(var.set_eco_mode_id(config[CONF_ECO_MODE_DATAPOINT]))
|
|
||||||
|
|||||||
@@ -21,28 +21,20 @@ void TuyaClimate::setup() {
|
|||||||
}
|
}
|
||||||
if (this->target_temperature_id_.has_value()) {
|
if (this->target_temperature_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->target_temperature_id_, [this](TuyaDatapoint datapoint) {
|
this->parent_->register_listener(*this->target_temperature_id_, [this](TuyaDatapoint datapoint) {
|
||||||
this->target_temperature = datapoint.value_int;
|
this->target_temperature = datapoint.value_int * this->temperature_multiplier_;
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
ESP_LOGD(TAG, "MCU reported target temperature is: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "MCU reported target temperature is: %.1f", this->target_temperature);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this->current_temperature_id_.has_value()) {
|
if (this->current_temperature_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->current_temperature_id_, [this](TuyaDatapoint datapoint) {
|
this->parent_->register_listener(*this->current_temperature_id_, [this](TuyaDatapoint datapoint) {
|
||||||
this->current_temperature = datapoint.value_int;
|
this->current_temperature = datapoint.value_int * this->temperature_multiplier_;
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
ESP_LOGD(TAG, "MCU reported current temperature is: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "MCU reported current temperature is: %.1f", this->current_temperature);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// if (this->eco_mode_id_.has_value()) {
|
|
||||||
// this->parent_->register_listener(*this->eco_mode_id_, [this](TuyaDatapoint datapoint) {
|
|
||||||
// this->eco_mode = datapoint.value_bool;
|
|
||||||
// this->compute_state_();
|
|
||||||
// this->publish_state();
|
|
||||||
// ESP_LOGD(TAG, "MCU reported eco mode of: %s", ONOFF(datapoint.value_bool));
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaClimate::control(const climate::ClimateCall &call) {
|
void TuyaClimate::control(const climate::ClimateCall &call) {
|
||||||
@@ -56,30 +48,17 @@ void TuyaClimate::control(const climate::ClimateCall &call) {
|
|||||||
this->parent_->set_datapoint_value(datapoint);
|
this->parent_->set_datapoint_value(datapoint);
|
||||||
ESP_LOGD(TAG, "Setting switch: %s", ONOFF(datapoint.value_bool));
|
ESP_LOGD(TAG, "Setting switch: %s", ONOFF(datapoint.value_bool));
|
||||||
}
|
}
|
||||||
if (call.get_target_temperature_low().has_value())
|
|
||||||
this->target_temperature_low = *call.get_target_temperature_low();
|
|
||||||
if (call.get_target_temperature_high().has_value())
|
|
||||||
this->target_temperature_high = *call.get_target_temperature_high();
|
|
||||||
if (call.get_target_temperature().has_value()) {
|
if (call.get_target_temperature().has_value()) {
|
||||||
this->target_temperature = *call.get_target_temperature();
|
this->target_temperature = *call.get_target_temperature();
|
||||||
|
|
||||||
TuyaDatapoint datapoint{};
|
TuyaDatapoint datapoint{};
|
||||||
datapoint.id = *this->target_temperature_id_;
|
datapoint.id = *this->target_temperature_id_;
|
||||||
datapoint.type = TuyaDatapointType::INTEGER;
|
datapoint.type = TuyaDatapointType::INTEGER;
|
||||||
datapoint.value_int = (int) this->target_temperature;
|
datapoint.value_int = (int) (this->target_temperature / this->temperature_multiplier_);
|
||||||
this->parent_->set_datapoint_value(datapoint);
|
this->parent_->set_datapoint_value(datapoint);
|
||||||
ESP_LOGD(TAG, "Setting target temperature: %d", datapoint.value_int);
|
ESP_LOGD(TAG, "Setting target temperature: %.1f", this->target_temperature);
|
||||||
}
|
}
|
||||||
// if (call.get_eco_mode().has_value()) {
|
|
||||||
// this->eco_mode = *call.get_eco_mode();
|
|
||||||
|
|
||||||
// TuyaDatapoint datapoint{};
|
|
||||||
// datapoint.id = *this->eco_mode_id_;
|
|
||||||
// datapoint.type = TuyaDatapointType::BOOLEAN;
|
|
||||||
// datapoint.value_bool = this->eco_mode;
|
|
||||||
// this->parent_->set_datapoint_value(datapoint);
|
|
||||||
// ESP_LOGD(TAG, "Setting eco mode: %s", ONOFF(datapoint.value_bool));
|
|
||||||
// }
|
|
||||||
|
|
||||||
this->compute_state_();
|
this->compute_state_();
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
@@ -89,7 +68,6 @@ climate::ClimateTraits TuyaClimate::traits() {
|
|||||||
auto traits = climate::ClimateTraits();
|
auto traits = climate::ClimateTraits();
|
||||||
traits.set_supports_current_temperature(this->current_temperature_id_.has_value());
|
traits.set_supports_current_temperature(this->current_temperature_id_.has_value());
|
||||||
traits.set_supports_heat_mode(true);
|
traits.set_supports_heat_mode(true);
|
||||||
// traits.set_supports_eco_mode(this->eco_mode_id_.has_value());
|
|
||||||
traits.set_supports_action(true);
|
traits.set_supports_action(true);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
@@ -102,8 +80,6 @@ void TuyaClimate::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_);
|
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_);
|
||||||
if (this->current_temperature_id_.has_value())
|
if (this->current_temperature_id_.has_value())
|
||||||
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_);
|
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_);
|
||||||
// if (this->eco_mode_id_.has_value())
|
|
||||||
// ESP_LOGCONFIG(TAG, " Eco Mode has datapoint ID %u", *this->mode_id_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaClimate::compute_state_() {
|
void TuyaClimate::compute_state_() {
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ class TuyaClimate : public climate::Climate, public Component {
|
|||||||
void set_current_temperature_id(uint8_t current_temperature_id) {
|
void set_current_temperature_id(uint8_t current_temperature_id) {
|
||||||
this->current_temperature_id_ = current_temperature_id;
|
this->current_temperature_id_ = current_temperature_id;
|
||||||
}
|
}
|
||||||
// void set_eco_mode_id(uint8_t eco_mode_id) { this->eco_mode_id_ = eco_mode_id; }
|
void set_temperature_multiplier(float temperature_multiplier) {
|
||||||
|
this->temperature_multiplier_ = temperature_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ class TuyaClimate : public climate::Climate, public Component {
|
|||||||
optional<uint8_t> switch_id_{};
|
optional<uint8_t> switch_id_{};
|
||||||
optional<uint8_t> target_temperature_id_{};
|
optional<uint8_t> target_temperature_id_{};
|
||||||
optional<uint8_t> current_temperature_id_{};
|
optional<uint8_t> current_temperature_id_{};
|
||||||
// optional<uint8_t> eco_mode_id_{};
|
float temperature_multiplier_{1.0f};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tuya
|
} // namespace tuya
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, esp32_ble_tracker
|
from esphome.components import sensor, esp32_ble_tracker
|
||||||
from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
|
from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
|
||||||
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, CONF_ID, \
|
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \
|
||||||
CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \
|
CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \
|
||||||
UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER
|
UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ CONFIG_SCHEMA = cv.Schema({
|
|||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
||||||
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0),
|
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0),
|
||||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0),
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0),
|
||||||
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0),
|
||||||
cv.Optional(CONF_CONDUCTIVITY):
|
cv.Optional(CONF_CONDUCTIVITY):
|
||||||
sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0),
|
sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0),
|
||||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
||||||
@@ -43,3 +44,6 @@ def to_code(config):
|
|||||||
if CONF_CONDUCTIVITY in config:
|
if CONF_CONDUCTIVITY in config:
|
||||||
sens = yield sensor.new_sensor(config[CONF_CONDUCTIVITY])
|
sens = yield sensor.new_sensor(config[CONF_CONDUCTIVITY])
|
||||||
cg.add(var.set_conductivity(sens))
|
cg.add(var.set_conductivity(sens))
|
||||||
|
if CONF_BATTERY_LEVEL in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||||
|
cg.add(var.set_battery_level(sens))
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ void XiaomiHHCCJCY01::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Moisture", this->moisture_);
|
LOG_SENSOR(" ", "Moisture", this->moisture_);
|
||||||
LOG_SENSOR(" ", "Conductivity", this->conductivity_);
|
LOG_SENSOR(" ", "Conductivity", this->conductivity_);
|
||||||
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||||
|
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
@@ -50,6 +51,8 @@ bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||||||
this->conductivity_->publish_state(*res->conductivity);
|
this->conductivity_->publish_state(*res->conductivity);
|
||||||
if (res->illuminance.has_value() && this->illuminance_ != nullptr)
|
if (res->illuminance.has_value() && this->illuminance_ != nullptr)
|
||||||
this->illuminance_->publish_state(*res->illuminance);
|
this->illuminance_->publish_state(*res->illuminance);
|
||||||
|
if (res->battery_level.has_value() && this->battery_level_ != nullptr)
|
||||||
|
this->battery_level_->publish_state(*res->battery_level);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class XiaomiHHCCJCY01 : public Component, public esp32_ble_tracker::ESPBTDeviceL
|
|||||||
void set_moisture(sensor::Sensor *moisture) { moisture_ = moisture; }
|
void set_moisture(sensor::Sensor *moisture) { moisture_ = moisture; }
|
||||||
void set_conductivity(sensor::Sensor *conductivity) { conductivity_ = conductivity; }
|
void set_conductivity(sensor::Sensor *conductivity) { conductivity_ = conductivity; }
|
||||||
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||||
|
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t address_;
|
uint64_t address_;
|
||||||
@@ -29,6 +30,7 @@ class XiaomiHHCCJCY01 : public Component, public esp32_ble_tracker::ESPBTDeviceL
|
|||||||
sensor::Sensor *moisture_{nullptr};
|
sensor::Sensor *moisture_{nullptr};
|
||||||
sensor::Sensor *conductivity_{nullptr};
|
sensor::Sensor *conductivity_{nullptr};
|
||||||
sensor::Sensor *illuminance_{nullptr};
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
sensor::Sensor *battery_level_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xiaomi_hhccjcy01
|
} // namespace xiaomi_hhccjcy01
|
||||||
|
|||||||
@@ -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 = '0'
|
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
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ sensor:
|
|||||||
name: "Xiaomi HHCCJCY01 Illuminance"
|
name: "Xiaomi HHCCJCY01 Illuminance"
|
||||||
conductivity:
|
conductivity:
|
||||||
name: "Xiaomi HHCCJCY01 Soil Conductivity"
|
name: "Xiaomi HHCCJCY01 Soil Conductivity"
|
||||||
|
battery_level:
|
||||||
|
name: "Xiaomi HHCCJCY01 Battery Level"
|
||||||
- platform: xiaomi_lywsdcgq
|
- platform: xiaomi_lywsdcgq
|
||||||
mac_address: 7A:80:8E:19:36:BA
|
mac_address: 7A:80:8E:19:36:BA
|
||||||
temperature:
|
temperature:
|
||||||
@@ -280,6 +282,9 @@ text_sensor:
|
|||||||
service: light.turn_on
|
service: light.turn_on
|
||||||
data:
|
data:
|
||||||
entity_id: light.my_light
|
entity_id: light.my_light
|
||||||
|
- homeassistant.tag_scanned:
|
||||||
|
tag: 1234-abcd
|
||||||
|
- homeassistant.tag_scanned: 1234-abcd
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Template Text Sensor"
|
name: "Template Text Sensor"
|
||||||
lambda: |-
|
lambda: |-
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ climate:
|
|||||||
id: tuya_climate
|
id: tuya_climate
|
||||||
switch_datapoint: 1
|
switch_datapoint: 1
|
||||||
target_temperature_datapoint: 3
|
target_temperature_datapoint: 3
|
||||||
|
temperature_multiplier: 0.5
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: tuya
|
- platform: tuya
|
||||||
|
|||||||
Reference in New Issue
Block a user