mirror of
https://github.com/esphome/esphome.git
synced 2025-10-31 23:21:54 +00:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -96,8 +96,11 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
|||||||
BLEClientWriteAction(BLEClient *ble_client) {
|
BLEClientWriteAction(BLEClient *ble_client) {
|
||||||
ble_client->register_ble_node(this);
|
ble_client->register_ble_node(this);
|
||||||
ble_client_ = ble_client;
|
ble_client_ = ble_client;
|
||||||
|
this->construct_simple_value_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~BLEClientWriteAction() { this->destroy_simple_value_(); }
|
||||||
|
|
||||||
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||||
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||||
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||||
@@ -106,14 +109,18 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
|||||||
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||||
|
|
||||||
void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) {
|
void set_value_template(std::vector<uint8_t> (*func)(Ts...)) {
|
||||||
this->value_template_ = std::move(func);
|
this->destroy_simple_value_();
|
||||||
has_simple_value_ = false;
|
this->value_.template_func = func;
|
||||||
|
this->has_simple_value_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_value_simple(const std::vector<uint8_t> &value) {
|
void set_value_simple(const std::vector<uint8_t> &value) {
|
||||||
this->value_simple_ = value;
|
if (!this->has_simple_value_) {
|
||||||
has_simple_value_ = true;
|
this->construct_simple_value_();
|
||||||
|
}
|
||||||
|
this->value_.simple = value;
|
||||||
|
this->has_simple_value_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void play(Ts... x) override {}
|
void play(Ts... x) override {}
|
||||||
@@ -121,7 +128,7 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
|||||||
void play_complex(Ts... x) override {
|
void play_complex(Ts... x) override {
|
||||||
this->num_running_++;
|
this->num_running_++;
|
||||||
this->var_ = std::make_tuple(x...);
|
this->var_ = std::make_tuple(x...);
|
||||||
auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...);
|
auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...);
|
||||||
// on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work.
|
// on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work.
|
||||||
if (!write(value))
|
if (!write(value))
|
||||||
this->play_next_(x...);
|
this->play_next_(x...);
|
||||||
@@ -194,10 +201,22 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void construct_simple_value_() { new (&this->value_.simple) std::vector<uint8_t>(); }
|
||||||
|
|
||||||
|
void destroy_simple_value_() {
|
||||||
|
if (this->has_simple_value_) {
|
||||||
|
this->value_.simple.~vector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BLEClient *ble_client_;
|
BLEClient *ble_client_;
|
||||||
bool has_simple_value_ = true;
|
bool has_simple_value_ = true;
|
||||||
std::vector<uint8_t> value_simple_;
|
union Value {
|
||||||
std::function<std::vector<uint8_t>(Ts...)> value_template_{};
|
std::vector<uint8_t> simple;
|
||||||
|
std::vector<uint8_t> (*template_func)(Ts...);
|
||||||
|
Value() {} // trivial constructor
|
||||||
|
~Value() {} // trivial destructor - we manage lifetime via discriminator
|
||||||
|
} value_;
|
||||||
espbt::ESPBTUUID service_uuid_;
|
espbt::ESPBTUUID service_uuid_;
|
||||||
espbt::ESPBTUUID char_uuid_;
|
espbt::ESPBTUUID char_uuid_;
|
||||||
std::tuple<Ts...> var_{};
|
std::tuple<Ts...> var_{};
|
||||||
@@ -213,9 +232,9 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...
|
|||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
uint32_t passkey;
|
uint32_t passkey;
|
||||||
if (has_simple_value_) {
|
if (has_simple_value_) {
|
||||||
passkey = this->value_simple_;
|
passkey = this->value_.simple;
|
||||||
} else {
|
} else {
|
||||||
passkey = this->value_template_(x...);
|
passkey = this->value_.template_func(x...);
|
||||||
}
|
}
|
||||||
if (passkey > 999999)
|
if (passkey > 999999)
|
||||||
return;
|
return;
|
||||||
@@ -224,21 +243,23 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...
|
|||||||
esp_ble_passkey_reply(remote_bda, true, passkey);
|
esp_ble_passkey_reply(remote_bda, true, passkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_value_template(std::function<uint32_t(Ts...)> func) {
|
void set_value_template(uint32_t (*func)(Ts...)) {
|
||||||
this->value_template_ = std::move(func);
|
this->value_.template_func = func;
|
||||||
has_simple_value_ = false;
|
this->has_simple_value_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_value_simple(const uint32_t &value) {
|
void set_value_simple(const uint32_t &value) {
|
||||||
this->value_simple_ = value;
|
this->value_.simple = value;
|
||||||
has_simple_value_ = true;
|
this->has_simple_value_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BLEClient *parent_{nullptr};
|
BLEClient *parent_{nullptr};
|
||||||
bool has_simple_value_ = true;
|
bool has_simple_value_ = true;
|
||||||
uint32_t value_simple_{0};
|
union {
|
||||||
std::function<uint32_t(Ts...)> value_template_{};
|
uint32_t simple;
|
||||||
|
uint32_t (*template_func)(Ts...);
|
||||||
|
} value_{.simple = 0};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
|
template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
|
||||||
@@ -249,27 +270,29 @@ template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Ac
|
|||||||
esp_bd_addr_t remote_bda;
|
esp_bd_addr_t remote_bda;
|
||||||
memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
|
memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
|
||||||
if (has_simple_value_) {
|
if (has_simple_value_) {
|
||||||
esp_ble_confirm_reply(remote_bda, this->value_simple_);
|
esp_ble_confirm_reply(remote_bda, this->value_.simple);
|
||||||
} else {
|
} else {
|
||||||
esp_ble_confirm_reply(remote_bda, this->value_template_(x...));
|
esp_ble_confirm_reply(remote_bda, this->value_.template_func(x...));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_value_template(std::function<bool(Ts...)> func) {
|
void set_value_template(bool (*func)(Ts...)) {
|
||||||
this->value_template_ = std::move(func);
|
this->value_.template_func = func;
|
||||||
has_simple_value_ = false;
|
this->has_simple_value_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_value_simple(const bool &value) {
|
void set_value_simple(const bool &value) {
|
||||||
this->value_simple_ = value;
|
this->value_.simple = value;
|
||||||
has_simple_value_ = true;
|
this->has_simple_value_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BLEClient *parent_{nullptr};
|
BLEClient *parent_{nullptr};
|
||||||
bool has_simple_value_ = true;
|
bool has_simple_value_ = true;
|
||||||
bool value_simple_{false};
|
union {
|
||||||
std::function<bool(Ts...)> value_template_{};
|
bool simple;
|
||||||
|
bool (*template_func)(Ts...);
|
||||||
|
} value_{.simple = false};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {
|
template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
|||||||
}
|
}
|
||||||
|
|
||||||
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
|
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
|
||||||
if (this->data_to_value_func_.has_value()) {
|
if (this->has_data_to_value_) {
|
||||||
std::vector<uint8_t> data(value, value + value_len);
|
std::vector<uint8_t> data(value, value + value_len);
|
||||||
return (*this->data_to_value_func_)(data);
|
return this->data_to_value_func_(data);
|
||||||
} else {
|
} else {
|
||||||
return value[0];
|
return value[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ namespace ble_client {
|
|||||||
|
|
||||||
namespace espbt = esphome::esp32_ble_tracker;
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
using data_to_value_t = std::function<float(std::vector<uint8_t>)>;
|
|
||||||
|
|
||||||
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
|
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
|
||||||
public:
|
public:
|
||||||
void loop() override;
|
void loop() override;
|
||||||
@@ -33,13 +31,17 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
|
|||||||
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||||
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||||
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||||
void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; }
|
void set_data_to_value(float (*lambda)(const std::vector<uint8_t> &)) {
|
||||||
|
this->data_to_value_func_ = lambda;
|
||||||
|
this->has_data_to_value_ = true;
|
||||||
|
}
|
||||||
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float parse_data_(uint8_t *value, uint16_t value_len);
|
float parse_data_(uint8_t *value, uint16_t value_len);
|
||||||
optional<data_to_value_t> data_to_value_func_{};
|
bool has_data_to_value_{false};
|
||||||
|
float (*data_to_value_func_)(const std::vector<uint8_t> &){};
|
||||||
bool notify_;
|
bool notify_;
|
||||||
espbt::ESPBTUUID service_uuid_;
|
espbt::ESPBTUUID service_uuid_;
|
||||||
espbt::ESPBTUUID char_uuid_;
|
espbt::ESPBTUUID char_uuid_;
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class HttpRequestComponent : public Component {
|
|||||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
void set_useragent(const char *useragent) { this->useragent_ = useragent; }
|
void set_useragent(const char *useragent) { this->useragent_ = useragent; }
|
||||||
void set_timeout(uint16_t timeout) { this->timeout_ = timeout; }
|
void set_timeout(uint32_t timeout) { this->timeout_ = timeout; }
|
||||||
void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; }
|
void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; }
|
||||||
uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; }
|
uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; }
|
||||||
void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
|
void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
|
||||||
@@ -173,7 +173,7 @@ class HttpRequestComponent : public Component {
|
|||||||
const char *useragent_{nullptr};
|
const char *useragent_{nullptr};
|
||||||
bool follow_redirects_{};
|
bool follow_redirects_{};
|
||||||
uint16_t redirect_limit_{};
|
uint16_t redirect_limit_{};
|
||||||
uint16_t timeout_{4500};
|
uint32_t timeout_{4500};
|
||||||
uint32_t watchdog_timeout_{0};
|
uint32_t watchdog_timeout_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from esphome.const import (
|
|||||||
CONF_FAMILY,
|
CONF_FAMILY,
|
||||||
CONF_GROUP,
|
CONF_GROUP,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_INDEX,
|
||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_LEVEL,
|
CONF_LEVEL,
|
||||||
CONF_MAGNITUDE,
|
CONF_MAGNITUDE,
|
||||||
@@ -616,6 +617,49 @@ async def dooya_action(var, config, args):
|
|||||||
cg.add(var.set_check(template_))
|
cg.add(var.set_check(template_))
|
||||||
|
|
||||||
|
|
||||||
|
# Dyson
|
||||||
|
DysonData, DysonBinarySensor, DysonTrigger, DysonAction, DysonDumper = declare_protocol(
|
||||||
|
"Dyson"
|
||||||
|
)
|
||||||
|
DYSON_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_CODE): cv.hex_uint16_t,
|
||||||
|
cv.Optional(CONF_INDEX, default=0xFF): cv.hex_uint8_t,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register_binary_sensor("dyson", DysonBinarySensor, DYSON_SCHEMA)
|
||||||
|
def dyson_binary_sensor(var, config):
|
||||||
|
cg.add(
|
||||||
|
var.set_data(
|
||||||
|
cg.StructInitializer(
|
||||||
|
DysonData,
|
||||||
|
("code", config[CONF_CODE]),
|
||||||
|
("index", config[CONF_INDEX]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register_trigger("dyson", DysonTrigger, DysonData)
|
||||||
|
def dyson_trigger(var, config):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@register_dumper("dyson", DysonDumper)
|
||||||
|
def dyson_dumper(var, config):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@register_action("dyson", DysonAction, DYSON_SCHEMA)
|
||||||
|
async def dyson_action(var, config, args):
|
||||||
|
template_ = await cg.templatable(config[CONF_CODE], args, cg.uint16)
|
||||||
|
cg.add(var.set_code(template_))
|
||||||
|
template_ = await cg.templatable(config[CONF_INDEX], args, cg.uint8)
|
||||||
|
cg.add(var.set_index(template_))
|
||||||
|
|
||||||
|
|
||||||
# JVC
|
# JVC
|
||||||
JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC")
|
JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC")
|
||||||
JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t})
|
JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t})
|
||||||
|
|||||||
71
esphome/components/remote_base/dyson_protocol.cpp
Normal file
71
esphome/components/remote_base/dyson_protocol.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include "dyson_protocol.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace remote_base {
|
||||||
|
|
||||||
|
static const char *const TAG = "remote.dyson";
|
||||||
|
|
||||||
|
// pulsewidth [µs]
|
||||||
|
constexpr uint32_t PW_MARK_US = 780;
|
||||||
|
constexpr uint32_t PW_SHORT_US = 720;
|
||||||
|
constexpr uint32_t PW_LONG_US = 1500;
|
||||||
|
constexpr uint32_t PW_START_US = 2280;
|
||||||
|
|
||||||
|
// MSB of 15 bit dyson code
|
||||||
|
constexpr uint16_t MSB_DYSON = (1 << 14);
|
||||||
|
|
||||||
|
// required symbols in transmit buffer = (start_symbol + 15 data_symbols)
|
||||||
|
constexpr uint32_t N_SYMBOLS_REQ = 2u * (1 + 15);
|
||||||
|
|
||||||
|
void DysonProtocol::encode(RemoteTransmitData *dst, const DysonData &data) {
|
||||||
|
uint32_t raw_code = (data.code << 2) + (data.index & 3);
|
||||||
|
dst->set_carrier_frequency(36000);
|
||||||
|
dst->reserve(N_SYMBOLS_REQ + 1);
|
||||||
|
dst->item(PW_START_US, PW_SHORT_US);
|
||||||
|
for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) {
|
||||||
|
if (mask == (mask & raw_code)) {
|
||||||
|
dst->item(PW_MARK_US, PW_LONG_US);
|
||||||
|
} else {
|
||||||
|
dst->item(PW_MARK_US, PW_SHORT_US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst->mark(PW_MARK_US); // final carrier pulse
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<DysonData> DysonProtocol::decode(RemoteReceiveData src) {
|
||||||
|
uint32_t n_received = static_cast<uint32_t>(src.size());
|
||||||
|
uint16_t raw_code = 0;
|
||||||
|
DysonData data{
|
||||||
|
.code = 0,
|
||||||
|
.index = 0,
|
||||||
|
};
|
||||||
|
if (n_received < N_SYMBOLS_REQ)
|
||||||
|
return {}; // invalid frame length
|
||||||
|
if (!src.expect_item(PW_START_US, PW_SHORT_US))
|
||||||
|
return {}; // start not found
|
||||||
|
for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) {
|
||||||
|
if (src.expect_item(PW_MARK_US, PW_SHORT_US)) {
|
||||||
|
raw_code &= ~mask; // zero detected
|
||||||
|
} else if (src.expect_item(PW_MARK_US, PW_LONG_US)) {
|
||||||
|
raw_code |= mask; // one detected
|
||||||
|
} else {
|
||||||
|
return {}; // invalid data item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.code = raw_code >> 2; // extract button code
|
||||||
|
data.index = raw_code & 3; // extract rolling index
|
||||||
|
if (src.expect_mark(PW_MARK_US)) { // check total length
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return {}; // frame not complete
|
||||||
|
}
|
||||||
|
|
||||||
|
void DysonProtocol::dump(const DysonData &data) {
|
||||||
|
ESP_LOGI(TAG, "Dyson: code=0x%x rolling index=%d", data.code, data.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace remote_base
|
||||||
|
} // namespace esphome
|
||||||
46
esphome/components/remote_base/dyson_protocol.h
Normal file
46
esphome/components/remote_base/dyson_protocol.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "remote_base.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace remote_base {
|
||||||
|
|
||||||
|
static constexpr uint8_t IGNORE_INDEX = 0xFF;
|
||||||
|
|
||||||
|
struct DysonData {
|
||||||
|
uint16_t code; // the button, e.g. power, swing, fan++, ...
|
||||||
|
uint8_t index; // the rolling index counter
|
||||||
|
bool operator==(const DysonData &rhs) const {
|
||||||
|
if (IGNORE_INDEX == index || IGNORE_INDEX == rhs.index) {
|
||||||
|
return code == rhs.code;
|
||||||
|
}
|
||||||
|
return code == rhs.code && index == rhs.index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DysonProtocol : public RemoteProtocol<DysonData> {
|
||||||
|
public:
|
||||||
|
void encode(RemoteTransmitData *dst, const DysonData &data) override;
|
||||||
|
optional<DysonData> decode(RemoteReceiveData src) override;
|
||||||
|
void dump(const DysonData &data) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_REMOTE_PROTOCOL(Dyson)
|
||||||
|
|
||||||
|
template<typename... Ts> class DysonAction : public RemoteTransmitterActionBase<Ts...> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(uint16_t, code)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, index)
|
||||||
|
|
||||||
|
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||||
|
DysonData data{};
|
||||||
|
data.code = this->code_.value(x...);
|
||||||
|
data.index = this->index_.value(x...);
|
||||||
|
DysonProtocol().encode(dst, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace remote_base
|
||||||
|
} // namespace esphome
|
||||||
@@ -48,7 +48,6 @@ import sys
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from helpers import (
|
from helpers import (
|
||||||
BASE_BUS_COMPONENTS,
|
|
||||||
CPP_FILE_EXTENSIONS,
|
CPP_FILE_EXTENSIONS,
|
||||||
PYTHON_FILE_EXTENSIONS,
|
PYTHON_FILE_EXTENSIONS,
|
||||||
changed_files,
|
changed_files,
|
||||||
@@ -453,7 +452,7 @@ def detect_memory_impact_config(
|
|||||||
# Get actually changed files (not dependencies)
|
# Get actually changed files (not dependencies)
|
||||||
files = changed_files(branch)
|
files = changed_files(branch)
|
||||||
|
|
||||||
# Find all changed components (excluding core and base bus components)
|
# Find all changed components (excluding core)
|
||||||
# Also collect platform hints from platform-specific filenames
|
# Also collect platform hints from platform-specific filenames
|
||||||
changed_component_set: set[str] = set()
|
changed_component_set: set[str] = set()
|
||||||
has_core_cpp_changes = False
|
has_core_cpp_changes = False
|
||||||
@@ -462,13 +461,13 @@ def detect_memory_impact_config(
|
|||||||
for file in files:
|
for file in files:
|
||||||
component = get_component_from_path(file)
|
component = get_component_from_path(file)
|
||||||
if component:
|
if component:
|
||||||
# Skip base bus components as they're used across many builds
|
# Add all changed components, including base bus components
|
||||||
if component not in BASE_BUS_COMPONENTS:
|
# Base bus components (uart, i2c, spi, etc.) should still be analyzed
|
||||||
changed_component_set.add(component)
|
# when directly changed, even though they're also used as dependencies
|
||||||
# Check if this is a platform-specific file
|
changed_component_set.add(component)
|
||||||
platform_hint = _detect_platform_hint_from_filename(file)
|
# Check if this is a platform-specific file
|
||||||
if platform_hint:
|
if platform_hint := _detect_platform_hint_from_filename(file):
|
||||||
platform_hints.append(platform_hint)
|
platform_hints.append(platform_hint)
|
||||||
elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS):
|
elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS):
|
||||||
# Core ESPHome C++ files changed (not component-specific)
|
# Core ESPHome C++ files changed (not component-specific)
|
||||||
# Only C++ files affect memory usage
|
# Only C++ files affect memory usage
|
||||||
|
|||||||
@@ -3,3 +3,52 @@ esp32_ble_tracker:
|
|||||||
ble_client:
|
ble_client:
|
||||||
- mac_address: 01:02:03:04:05:06
|
- mac_address: 01:02:03:04:05:06
|
||||||
id: test_blec
|
id: test_blec
|
||||||
|
on_connect:
|
||||||
|
- ble_client.ble_write:
|
||||||
|
id: test_blec
|
||||||
|
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||||
|
characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678"
|
||||||
|
value: !lambda |-
|
||||||
|
return std::vector<uint8_t>{0x01, 0x02, 0x03};
|
||||||
|
- ble_client.ble_write:
|
||||||
|
id: test_blec
|
||||||
|
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||||
|
characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678"
|
||||||
|
value: [0x04, 0x05, 0x06]
|
||||||
|
on_passkey_request:
|
||||||
|
- ble_client.passkey_reply:
|
||||||
|
id: test_blec
|
||||||
|
passkey: !lambda |-
|
||||||
|
return 123456;
|
||||||
|
- ble_client.passkey_reply:
|
||||||
|
id: test_blec
|
||||||
|
passkey: 654321
|
||||||
|
on_numeric_comparison_request:
|
||||||
|
- ble_client.numeric_comparison_reply:
|
||||||
|
id: test_blec
|
||||||
|
accept: !lambda |-
|
||||||
|
return true;
|
||||||
|
- ble_client.numeric_comparison_reply:
|
||||||
|
id: test_blec
|
||||||
|
accept: false
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: ble_client
|
||||||
|
ble_client_id: test_blec
|
||||||
|
type: characteristic
|
||||||
|
id: test_sensor_lambda
|
||||||
|
name: "BLE Sensor with Lambda"
|
||||||
|
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||||
|
characteristic_uuid: "abcd1236-abcd-1234-abcd-abcd12345678"
|
||||||
|
lambda: |-
|
||||||
|
if (x.size() >= 2) {
|
||||||
|
return (float)(x[0] | (x[1] << 8)) / 100.0;
|
||||||
|
}
|
||||||
|
return NAN;
|
||||||
|
- platform: ble_client
|
||||||
|
ble_client_id: test_blec
|
||||||
|
type: characteristic
|
||||||
|
id: test_sensor_no_lambda
|
||||||
|
name: "BLE Sensor without Lambda"
|
||||||
|
service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678"
|
||||||
|
characteristic_uuid: "abcd1237-abcd-1234-abcd-abcd12345678"
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ on_drayton:
|
|||||||
- logger.log:
|
- logger.log:
|
||||||
format: "on_drayton: %u %u %u"
|
format: "on_drayton: %u %u %u"
|
||||||
args: ["x.address", "x.channel", "x.command"]
|
args: ["x.address", "x.channel", "x.command"]
|
||||||
|
on_dyson:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "on_dyson: %u %u"
|
||||||
|
args: ["x.code", "x.index"]
|
||||||
on_gobox:
|
on_gobox:
|
||||||
then:
|
then:
|
||||||
- logger.log:
|
- logger.log:
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ button:
|
|||||||
remote_transmitter.transmit_beo4:
|
remote_transmitter.transmit_beo4:
|
||||||
source: 0x01
|
source: 0x01
|
||||||
command: 0x0C
|
command: 0x0C
|
||||||
|
- platform: template
|
||||||
|
name: Dyson fan up
|
||||||
|
id: dyson_fan_up
|
||||||
|
on_press:
|
||||||
|
remote_transmitter.transmit_dyson:
|
||||||
|
code: 0x1215
|
||||||
|
index: 0x0
|
||||||
- platform: template
|
- platform: template
|
||||||
name: JVC Off
|
name: JVC Off
|
||||||
id: living_room_lights_on
|
id: living_room_lights_on
|
||||||
|
|||||||
@@ -19,3 +19,41 @@ uart:
|
|||||||
|
|
||||||
packet_transport:
|
packet_transport:
|
||||||
- platform: uart
|
- platform: uart
|
||||||
|
|
||||||
|
switch:
|
||||||
|
# Test uart switch with single state (array)
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Single Array"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: [0x01, 0x02, 0x03]
|
||||||
|
# Test uart switch with single state (string)
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Single String"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: "ON"
|
||||||
|
# Test uart switch with turn_on/turn_off (arrays)
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Dual Array"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data:
|
||||||
|
turn_on: [0xA0, 0xA1, 0xA2]
|
||||||
|
turn_off: [0xB0, 0xB1, 0xB2]
|
||||||
|
# Test uart switch with turn_on/turn_off (strings)
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Dual String"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data:
|
||||||
|
turn_on: "TURN_ON"
|
||||||
|
turn_off: "TURN_OFF"
|
||||||
|
|
||||||
|
button:
|
||||||
|
# Test uart button with array data
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Button Array"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: [0xFF, 0xEE, 0xDD]
|
||||||
|
# Test uart button with string data
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Button String"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: "BUTTON_PRESS"
|
||||||
|
|||||||
@@ -13,3 +13,21 @@ uart:
|
|||||||
rx_buffer_size: 512
|
rx_buffer_size: 512
|
||||||
parity: EVEN
|
parity: EVEN
|
||||||
stop_bits: 2
|
stop_bits: 2
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Array"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: [0x01, 0x02, 0x03]
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Switch Dual"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data:
|
||||||
|
turn_on: [0xA0, 0xA1]
|
||||||
|
turn_off: [0xB0, 0xB1]
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: uart
|
||||||
|
name: "UART Button"
|
||||||
|
uart_id: uart_uart
|
||||||
|
data: [0xFF, 0xEE]
|
||||||
|
|||||||
@@ -849,39 +849,47 @@ def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) ->
|
|||||||
assert result["should_run"] == "false"
|
assert result["should_run"] == "false"
|
||||||
|
|
||||||
|
|
||||||
def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) -> None:
|
def test_detect_memory_impact_config_includes_base_bus_components(
|
||||||
"""Test that base bus components (i2c, spi, uart) are skipped."""
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""Test that base bus components (i2c, spi, uart) are included when directly changed.
|
||||||
|
|
||||||
|
Base bus components should be analyzed for memory impact when they are directly
|
||||||
|
changed, even though they are often used as dependencies. This ensures that
|
||||||
|
optimizations to base components (like using move semantics or initializer_list)
|
||||||
|
are properly measured.
|
||||||
|
"""
|
||||||
# Create test directory structure
|
# Create test directory structure
|
||||||
tests_dir = tmp_path / "tests" / "components"
|
tests_dir = tmp_path / "tests" / "components"
|
||||||
|
|
||||||
# i2c component (should be skipped as it's a base bus component)
|
# uart component (base bus component that should be included)
|
||||||
i2c_dir = tests_dir / "i2c"
|
uart_dir = tests_dir / "uart"
|
||||||
i2c_dir.mkdir(parents=True)
|
uart_dir.mkdir(parents=True)
|
||||||
(i2c_dir / "test.esp32-idf.yaml").write_text("test: i2c")
|
(uart_dir / "test.esp32-idf.yaml").write_text("test: uart")
|
||||||
|
|
||||||
# wifi component (should not be skipped)
|
# wifi component (regular component)
|
||||||
wifi_dir = tests_dir / "wifi"
|
wifi_dir = tests_dir / "wifi"
|
||||||
wifi_dir.mkdir(parents=True)
|
wifi_dir.mkdir(parents=True)
|
||||||
(wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi")
|
(wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi")
|
||||||
|
|
||||||
# Mock changed_files to return both i2c and wifi
|
# Mock changed_files to return both uart and wifi
|
||||||
with (
|
with (
|
||||||
patch.object(determine_jobs, "root_path", str(tmp_path)),
|
patch.object(determine_jobs, "root_path", str(tmp_path)),
|
||||||
patch.object(helpers, "root_path", str(tmp_path)),
|
patch.object(helpers, "root_path", str(tmp_path)),
|
||||||
patch.object(determine_jobs, "changed_files") as mock_changed_files,
|
patch.object(determine_jobs, "changed_files") as mock_changed_files,
|
||||||
):
|
):
|
||||||
mock_changed_files.return_value = [
|
mock_changed_files.return_value = [
|
||||||
"esphome/components/i2c/i2c.cpp",
|
"esphome/components/uart/automation.h", # Header file with inline code
|
||||||
"esphome/components/wifi/wifi.cpp",
|
"esphome/components/wifi/wifi.cpp",
|
||||||
]
|
]
|
||||||
determine_jobs._component_has_tests.cache_clear()
|
determine_jobs._component_has_tests.cache_clear()
|
||||||
|
|
||||||
result = determine_jobs.detect_memory_impact_config()
|
result = determine_jobs.detect_memory_impact_config()
|
||||||
|
|
||||||
# Should only include wifi, not i2c
|
# Should include both uart and wifi
|
||||||
assert result["should_run"] == "true"
|
assert result["should_run"] == "true"
|
||||||
assert result["components"] == ["wifi"]
|
assert set(result["components"]) == {"uart", "wifi"}
|
||||||
assert "i2c" not in result["components"]
|
assert result["platform"] == "esp32-idf" # Common platform
|
||||||
|
|
||||||
|
|
||||||
def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None:
|
def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user