Merge branch 'esphome:dev' into optolink

This commit is contained in:
j0ta29 2023-06-05 15:44:16 +02:00 committed by GitHub
commit 8133ab1f39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 396 additions and 64 deletions

View file

@ -56,7 +56,7 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v3.5.2
- name: Run yamllint - name: Run yamllint
uses: frenck/action-yamllint@v1.4.0 uses: frenck/action-yamllint@v1.4.1
black: black:
name: Check black name: Check black

View file

@ -980,7 +980,7 @@ def run_esphome(argv):
_LOGGER.error(e, exc_info=args.verbose) _LOGGER.error(e, exc_info=args.verbose)
return 1 return 1
safe_print(f"ESPHome {const.__version__}") _LOGGER.info("ESPHome %s", const.__version__)
for conf_path in args.configuration: for conf_path in args.configuration:
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES): if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):

View file

@ -1397,6 +1397,7 @@ message VoiceAssistantRequest {
option (ifdef) = "USE_VOICE_ASSISTANT"; option (ifdef) = "USE_VOICE_ASSISTANT";
bool start = 1; bool start = 1;
string conversation_id = 2;
} }
message VoiceAssistantResponse { message VoiceAssistantResponse {

View file

@ -895,11 +895,12 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIConnection::request_voice_assistant(bool start) { bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id) {
if (!this->voice_assistant_subscription_) if (!this->voice_assistant_subscription_)
return false; return false;
VoiceAssistantRequest msg; VoiceAssistantRequest msg;
msg.start = start; msg.start = start;
msg.conversation_id = conversation_id;
return this->send_voice_assistant_request(msg); return this->send_voice_assistant_request(msg);
} }
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {

View file

@ -128,7 +128,7 @@ class APIConnection : public APIServerConnection {
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
this->voice_assistant_subscription_ = msg.subscribe; this->voice_assistant_subscription_ = msg.subscribe;
} }
bool request_voice_assistant(bool start); bool request_voice_assistant(bool start, const std::string &conversation_id);
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
#endif #endif

View file

@ -6187,7 +6187,20 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
return false; return false;
} }
} }
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); } bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->conversation_id = value.as_string();
return true;
}
default:
return false;
}
}
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id);
}
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantRequest::dump_to(std::string &out) const { void VoiceAssistantRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
@ -6195,6 +6208,10 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
out.append(" start: "); out.append(" start: ");
out.append(YESNO(this->start)); out.append(YESNO(this->start));
out.append("\n"); out.append("\n");
out.append(" conversation_id: ");
out.append("'").append(this->conversation_id).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif

View file

@ -1604,12 +1604,14 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage {
class VoiceAssistantRequest : public ProtoMessage { class VoiceAssistantRequest : public ProtoMessage {
public: public:
bool start{false}; bool start{false};
std::string conversation_id{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
#endif #endif
protected: protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class VoiceAssistantResponse : public ProtoMessage { class VoiceAssistantResponse : public ProtoMessage {

View file

@ -428,16 +428,16 @@ void APIServer::on_shutdown() {
} }
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIServer::start_voice_assistant() { bool APIServer::start_voice_assistant(const std::string &conversation_id) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(true)) if (c->request_voice_assistant(true, conversation_id))
return true; return true;
} }
return false; return false;
} }
void APIServer::stop_voice_assistant() { void APIServer::stop_voice_assistant() {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(false)) if (c->request_voice_assistant(false, ""))
return; return;
} }
} }

View file

@ -96,7 +96,7 @@ class APIServer : public Component, public Controller {
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool start_voice_assistant(); bool start_voice_assistant(const std::string &conversation_id);
void stop_voice_assistant(); void stop_voice_assistant();
#endif #endif

View file

@ -42,6 +42,39 @@ ESP32_BASE_PINS = {
} }
ESP32_BOARD_PINS = { ESP32_BOARD_PINS = {
"adafruit_feather_esp32s2_tft": {
"BUTTON": 0,
"A0": 18,
"A1": 17,
"A2": 16,
"A3": 15,
"A4": 14,
"A5": 8,
"SCK": 36,
"MOSI": 35,
"MISO": 37,
"RX": 2,
"TX": 1,
"D13": 13,
"D12": 12,
"D11": 11,
"D10": 10,
"D9": 9,
"D6": 6,
"D5": 5,
"NEOPIXEL": 33,
"PIN_NEOPIXEL": 33,
"NEOPIXEL_POWER": 34,
"SCL": 41,
"SDA": 42,
"TFT_I2C_POWER": 21,
"TFT_CS": 7,
"TFT_DC": 39,
"TFT_RESET": 40,
"TFT_BACKLIGHT": 45,
"LED": 13,
"LED_BUILTIN": 13,
},
"adafruit_qtpy_esp32c3": { "adafruit_qtpy_esp32c3": {
"A0": 4, "A0": 4,
"A1": 3, "A1": 3,

View file

@ -1,3 +1,4 @@
#include <cinttypes>
#include "led_strip.h" #include "led_strip.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
@ -195,7 +196,7 @@ void ESP32RMTLEDStripLightOutput::dump_config() {
break; break;
} }
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
ESP_LOGCONFIG(TAG, " Max refresh rate: %u", *this->max_refresh_rate_); ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
} }

View file

@ -62,7 +62,7 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(I2SAudioMicrophone), cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS), cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS),
cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All( cv.Optional(CONF_BITS_PER_SAMPLE, default="32bit"): cv.All(
_validate_bits, cv.enum(BITS_PER_SAMPLE) _validate_bits, cv.enum(BITS_PER_SAMPLE)
), ),
} }

View file

@ -1,3 +1,4 @@
#include <cinttypes>
#include "light_call.h" #include "light_call.h"
#include "light_state.h" #include "light_state.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -283,7 +284,7 @@ LightColorValues LightCall::validate_() {
// validate effect index // validate effect index
if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) { if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) {
ESP_LOGW(TAG, "'%s' - Invalid effect index %u!", name, *this->effect_); ESP_LOGW(TAG, "'%s' - Invalid effect index %" PRIu32 "!", name, *this->effect_);
this->effect_.reset(); this->effect_.reset();
} }

View file

@ -21,25 +21,35 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION;
} }
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// The following function takes longer than the 5 seconds timeout of WDT // The following function takes longer than the 5 seconds timeout of WDT
#if ESP_IDF_VERSION_MAJOR >= 5 #if ESP_IDF_VERSION_MAJOR >= 5
esp_task_wdt_config_t wdtc; esp_task_wdt_config_t wdtc;
wdtc.timeout_ms = 15000;
wdtc.idle_core_mask = 0; wdtc.idle_core_mask = 0;
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
wdtc.idle_core_mask |= (1 << 0);
#endif
#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
wdtc.idle_core_mask |= (1 << 1);
#endif
wdtc.timeout_ms = 15000;
wdtc.trigger_panic = false; wdtc.trigger_panic = false;
esp_task_wdt_reconfigure(&wdtc); esp_task_wdt_reconfigure(&wdtc);
#else #else
esp_task_wdt_init(15, false); esp_task_wdt_init(15, false);
#endif
#endif #endif
esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_); esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_);
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// Set the WDT back to the configured timeout // Set the WDT back to the configured timeout
#if ESP_IDF_VERSION_MAJOR >= 5 #if ESP_IDF_VERSION_MAJOR >= 5
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S; wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000;
esp_task_wdt_reconfigure(&wdtc); esp_task_wdt_reconfigure(&wdtc);
#else #else
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false); esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false);
#endif
#endif #endif
if (err != ESP_OK) { if (err != ESP_OK) {

View file

@ -99,7 +99,7 @@ void OTAComponent::dump_config() {
#endif #endif
if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 &&
this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) {
ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %d restarts", ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %" PRIu32 " restarts",
this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_); this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_);
} }
} }
@ -191,7 +191,7 @@ void OTAComponent::handle_() {
this->writeall_(buf, 1); this->writeall_(buf, 1);
md5::MD5Digest md5{}; md5::MD5Digest md5{};
md5.init(); md5.init();
sprintf(sbuf, "%08X", random_uint32()); sprintf(sbuf, "%08" PRIx32, random_uint32());
md5.add(sbuf, 8); md5.add(sbuf, 8);
md5.calculate(); md5.calculate();
md5.get_hex(sbuf); md5.get_hex(sbuf);
@ -466,7 +466,7 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_
if (is_manual_safe_mode) { if (is_manual_safe_mode) {
ESP_LOGI(TAG, "Safe mode has been entered manually"); ESP_LOGI(TAG, "Safe mode has been entered manually");
} else { } else {
ESP_LOGCONFIG(TAG, "There have been %u suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_); ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_);
} }
if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) { if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {

View file

@ -5,6 +5,7 @@
#include <map> #include <map>
#include <utility> #include <utility>
#include "esphome/core/entity_base.h"
#include "esphome/components/web_server_base/web_server_base.h" #include "esphome/components/web_server_base/web_server_base.h"
#include "esphome/core/controller.h" #include "esphome/core/controller.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"

View file

@ -157,7 +157,6 @@ async def to_code(config):
"platform_packages", "platform_packages",
[ [
f"earlephilhower/framework-arduinopico@{conf[CONF_SOURCE]}", f"earlephilhower/framework-arduinopico@{conf[CONF_SOURCE]}",
"earlephilhower/tool-pioasm-rp2040-earlephilhower",
], ],
) )

View file

@ -265,3 +265,9 @@ async def to_code(config):
time_to_cycles(config[CONF_BIT1_LOW]), time_to_cycles(config[CONF_BIT1_LOW]),
), ),
) )
cg.add_platformio_option(
"platform_packages",
[
"earlephilhower/tool-pioasm-rp2040-earlephilhower",
],
)

View file

@ -17,6 +17,13 @@ namespace select {
} \ } \
} }
#define SUB_SELECT(name) \
protected: \
select::Select *name##_select_{nullptr}; \
\
public: \
void set_##name##_select(select::Select *select) { this->name##_select_ = select; }
/** Base-class for all selects. /** Base-class for all selects.
* *
* A select can use publish_state to send out a new value. * A select can use publish_state to send out a new value.

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import display, spi from esphome.components import display, spi, power_supply
from esphome.const import ( from esphome.const import (
CONF_BACKLIGHT_PIN, CONF_BACKLIGHT_PIN,
CONF_DC_PIN, CONF_DC_PIN,
@ -11,6 +11,7 @@ from esphome.const import (
CONF_MODEL, CONF_MODEL,
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_WIDTH, CONF_WIDTH,
CONF_POWER_SUPPLY,
) )
from . import st7789v_ns from . import st7789v_ns
@ -32,6 +33,7 @@ MODELS = {
"TTGO_TDISPLAY_135X240": ST7789VModel.ST7789V_MODEL_TTGO_TDISPLAY_135_240, "TTGO_TDISPLAY_135X240": ST7789VModel.ST7789V_MODEL_TTGO_TDISPLAY_135_240,
"ADAFRUIT_FUNHOUSE_240X240": ST7789VModel.ST7789V_MODEL_ADAFRUIT_FUNHOUSE_240_240, "ADAFRUIT_FUNHOUSE_240X240": ST7789VModel.ST7789V_MODEL_ADAFRUIT_FUNHOUSE_240_240,
"ADAFRUIT_RR_280X240": ST7789VModel.ST7789V_MODEL_ADAFRUIT_RR_280_240, "ADAFRUIT_RR_280X240": ST7789VModel.ST7789V_MODEL_ADAFRUIT_RR_280_240,
"ADAFRUIT_S2_TFT_FEATHER_240X135": ST7789VModel.ST7789V_MODEL_ADAFRUIT_S2_TFT_FEATHER_240_135,
"CUSTOM": ST7789VModel.ST7789V_MODEL_CUSTOM, "CUSTOM": ST7789VModel.ST7789V_MODEL_CUSTOM,
} }
@ -58,6 +60,14 @@ def validate_st7789v(config):
raise cv.Invalid( raise cv.Invalid(
f'Do not specify {CONF_HEIGHT}, {CONF_WIDTH}, {CONF_OFFSET_HEIGHT} or {CONF_OFFSET_WIDTH} when using {CONF_MODEL} that is not "CUSTOM"' f'Do not specify {CONF_HEIGHT}, {CONF_WIDTH}, {CONF_OFFSET_HEIGHT} or {CONF_OFFSET_WIDTH} when using {CONF_MODEL} that is not "CUSTOM"'
) )
if (
config[CONF_MODEL].upper() == "ADAFRUIT_S2_TFT_FEATHER_240X135"
and CONF_POWER_SUPPLY not in config
):
raise cv.Invalid(
f'{CONF_POWER_SUPPLY} must be specified when {CONF_MODEL} is "ADAFRUIT_S2_TFT_FEATHER_240X135"'
)
return config return config
@ -69,6 +79,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
cv.Optional(CONF_EIGHTBITCOLOR, default=False): cv.boolean, cv.Optional(CONF_EIGHTBITCOLOR, default=False): cv.boolean,
cv.Optional(CONF_HEIGHT): cv.int_, cv.Optional(CONF_HEIGHT): cv.int_,
cv.Optional(CONF_WIDTH): cv.int_, cv.Optional(CONF_WIDTH): cv.int_,
@ -113,3 +124,7 @@ async def to_code(config):
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
) )
cg.add(var.set_writer(lambda_)) cg.add(var.set_writer(lambda_))
if CONF_POWER_SUPPLY in config:
ps = await cg.get_variable(config[CONF_POWER_SUPPLY])
cg.add(var.set_power_supply(ps))

View file

@ -8,6 +8,10 @@ static const char *const TAG = "st7789v";
void ST7789V::setup() { void ST7789V::setup() {
ESP_LOGCONFIG(TAG, "Setting up SPI ST7789V..."); ESP_LOGCONFIG(TAG, "Setting up SPI ST7789V...");
#ifdef USE_POWER_SUPPLY
this->power_.request();
// the PowerSupply component takes care of post turn-on delay
#endif
this->spi_setup(); this->spi_setup();
this->dc_pin_->setup(); // OUTPUT this->dc_pin_->setup(); // OUTPUT
@ -128,6 +132,9 @@ void ST7789V::dump_config() {
LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" B/L Pin: ", this->backlight_pin_); LOG_PIN(" B/L Pin: ", this->backlight_pin_);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
#ifdef USE_POWER_SUPPLY
ESP_LOGCONFIG(TAG, " Power Supply Configured: yes");
#endif
} }
float ST7789V::get_setup_priority() const { return setup_priority::PROCESSOR; } float ST7789V::get_setup_priority() const { return setup_priority::PROCESSOR; }
@ -162,6 +169,13 @@ void ST7789V::set_model(ST7789VModel model) {
this->offset_width_ = 20; this->offset_width_ = 20;
break; break;
case ST7789V_MODEL_ADAFRUIT_S2_TFT_FEATHER_240_135:
this->height_ = 240;
this->width_ = 135;
this->offset_height_ = 52;
this->offset_width_ = 40;
break;
default: default:
break; break;
} }
@ -323,6 +337,8 @@ const char *ST7789V::model_str_() {
return "Adafruit Funhouse 240x240"; return "Adafruit Funhouse 240x240";
case ST7789V_MODEL_ADAFRUIT_RR_280_240: case ST7789V_MODEL_ADAFRUIT_RR_280_240:
return "Adafruit Round-Rectangular 280x240"; return "Adafruit Round-Rectangular 280x240";
case ST7789V_MODEL_ADAFRUIT_S2_TFT_FEATHER_240_135:
return "Adafruit ESP32-S2 TFT Feather";
default: default:
return "Custom"; return "Custom";
} }

View file

@ -3,6 +3,9 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/spi/spi.h" #include "esphome/components/spi/spi.h"
#include "esphome/components/display/display_buffer.h" #include "esphome/components/display/display_buffer.h"
#ifdef USE_POWER_SUPPLY
#include "esphome/components/power_supply/power_supply.h"
#endif
namespace esphome { namespace esphome {
namespace st7789v { namespace st7789v {
@ -11,6 +14,7 @@ enum ST7789VModel {
ST7789V_MODEL_TTGO_TDISPLAY_135_240, ST7789V_MODEL_TTGO_TDISPLAY_135_240,
ST7789V_MODEL_ADAFRUIT_FUNHOUSE_240_240, ST7789V_MODEL_ADAFRUIT_FUNHOUSE_240_240,
ST7789V_MODEL_ADAFRUIT_RR_280_240, ST7789V_MODEL_ADAFRUIT_RR_280_240,
ST7789V_MODEL_ADAFRUIT_S2_TFT_FEATHER_240_135,
ST7789V_MODEL_CUSTOM ST7789V_MODEL_CUSTOM
}; };
@ -120,6 +124,9 @@ class ST7789V : public PollingComponent,
void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; } void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; }
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
void set_backlight_pin(GPIOPin *backlight_pin) { this->backlight_pin_ = backlight_pin; } void set_backlight_pin(GPIOPin *backlight_pin) { this->backlight_pin_ = backlight_pin; }
#ifdef USE_POWER_SUPPLY
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); }
#endif
void set_eightbitcolor(bool eightbitcolor) { this->eightbitcolor_ = eightbitcolor; } void set_eightbitcolor(bool eightbitcolor) { this->eightbitcolor_ = eightbitcolor; }
void set_height(uint32_t height) { this->height_ = height; } void set_height(uint32_t height) { this->height_ = height; }
@ -143,6 +150,9 @@ class ST7789V : public PollingComponent,
GPIOPin *dc_pin_{nullptr}; GPIOPin *dc_pin_{nullptr};
GPIOPin *reset_pin_{nullptr}; GPIOPin *reset_pin_{nullptr};
GPIOPin *backlight_pin_{nullptr}; GPIOPin *backlight_pin_{nullptr};
#ifdef USE_POWER_SUPPLY
power_supply::PowerSupplyRequester power_;
#endif
bool eightbitcolor_{false}; bool eightbitcolor_{false};
uint16_t height_{0}; uint16_t height_{0};

View file

@ -8,6 +8,13 @@
namespace esphome { namespace esphome {
namespace switch_ { namespace switch_ {
#define SUB_SWITCH(name) \
protected: \
switch_::Switch *name##_switch_{nullptr}; \
\
public: \
void set_##name##_switch(switch_::Switch *s) { this->name##_switch_ = s; }
// bit0: on/off. bit1: persistent. bit2: inverted. bit3: disabled // bit0: on/off. bit1: persistent. bit2: inverted. bit3: disabled
const int RESTORE_MODE_ON_MASK = 0x01; const int RESTORE_MODE_ON_MASK = 0x01;
const int RESTORE_MODE_PERSISTENT_MASK = 0x02; const int RESTORE_MODE_PERSISTENT_MASK = 0x02;

View file

@ -1,16 +1,23 @@
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_ID, CONF_MICROPHONE, CONF_SPEAKER from esphome.const import (
CONF_ID,
CONF_MICROPHONE,
CONF_SPEAKER,
CONF_MEDIA_PLAYER,
)
from esphome import automation from esphome import automation
from esphome.automation import register_action from esphome.automation import register_action, register_condition
from esphome.components import microphone, speaker from esphome.components import microphone, speaker, media_player
AUTO_LOAD = ["socket"] AUTO_LOAD = ["socket"]
DEPENDENCIES = ["api", "microphone"] DEPENDENCIES = ["api", "microphone"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
CONF_SILENCE_DETECTION = "silence_detection"
CONF_ON_LISTENING = "on_listening"
CONF_ON_START = "on_start" CONF_ON_START = "on_start"
CONF_ON_STT_END = "on_stt_end" CONF_ON_STT_END = "on_stt_end"
CONF_ON_TTS_START = "on_tts_start" CONF_ON_TTS_START = "on_tts_start"
@ -25,16 +32,25 @@ VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
StartAction = voice_assistant_ns.class_( StartAction = voice_assistant_ns.class_(
"StartAction", automation.Action, cg.Parented.template(VoiceAssistant) "StartAction", automation.Action, cg.Parented.template(VoiceAssistant)
) )
StartContinuousAction = voice_assistant_ns.class_(
"StartContinuousAction", automation.Action, cg.Parented.template(VoiceAssistant)
)
StopAction = voice_assistant_ns.class_( StopAction = voice_assistant_ns.class_(
"StopAction", automation.Action, cg.Parented.template(VoiceAssistant) "StopAction", automation.Action, cg.Parented.template(VoiceAssistant)
) )
IsRunningCondition = voice_assistant_ns.class_(
"IsRunningCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
)
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(VoiceAssistant), cv.GenerateID(): cv.declare_id(VoiceAssistant),
cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone),
cv.Optional(CONF_SPEAKER): cv.use_id(speaker.Speaker), cv.Exclusive(CONF_SPEAKER, "output"): cv.use_id(speaker.Speaker),
cv.Exclusive(CONF_MEDIA_PLAYER, "output"): cv.use_id(media_player.MediaPlayer),
cv.Optional(CONF_SILENCE_DETECTION, default=True): cv.boolean,
cv.Optional(CONF_ON_LISTENING): automation.validate_automation(single=True),
cv.Optional(CONF_ON_START): automation.validate_automation(single=True), cv.Optional(CONF_ON_START): automation.validate_automation(single=True),
cv.Optional(CONF_ON_STT_END): automation.validate_automation(single=True), cv.Optional(CONF_ON_STT_END): automation.validate_automation(single=True),
cv.Optional(CONF_ON_TTS_START): automation.validate_automation(single=True), cv.Optional(CONF_ON_TTS_START): automation.validate_automation(single=True),
@ -56,6 +72,17 @@ async def to_code(config):
spkr = await cg.get_variable(config[CONF_SPEAKER]) spkr = await cg.get_variable(config[CONF_SPEAKER])
cg.add(var.set_speaker(spkr)) cg.add(var.set_speaker(spkr))
if CONF_MEDIA_PLAYER in config:
mp = await cg.get_variable(config[CONF_MEDIA_PLAYER])
cg.add(var.set_media_player(mp))
cg.add(var.set_silence_detection(config[CONF_SILENCE_DETECTION]))
if CONF_ON_LISTENING in config:
await automation.build_automation(
var.get_listening_trigger(), [], config[CONF_ON_LISTENING]
)
if CONF_ON_START in config: if CONF_ON_START in config:
await automation.build_automation( await automation.build_automation(
var.get_start_trigger(), [], config[CONF_ON_START] var.get_start_trigger(), [], config[CONF_ON_START]
@ -96,6 +123,11 @@ async def to_code(config):
VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssistant)}) VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssistant)})
@register_action(
"voice_assistant.start_continuous",
StartContinuousAction,
VOICE_ASSISTANT_ACTION_SCHEMA,
)
@register_action("voice_assistant.start", StartAction, VOICE_ASSISTANT_ACTION_SCHEMA) @register_action("voice_assistant.start", StartAction, VOICE_ASSISTANT_ACTION_SCHEMA)
async def voice_assistant_listen_to_code(config, action_id, template_arg, args): async def voice_assistant_listen_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
@ -108,3 +140,12 @@ async def voice_assistant_stop_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID]) await cg.register_parented(var, config[CONF_ID])
return var return var
@register_condition(
"voice_assistant.is_running", IsRunningCondition, VOICE_ASSISTANT_ACTION_SCHEMA
)
async def voice_assistant_is_running_to_code(config, condition_id, template_arg, args):
var = cg.new_Pvariable(condition_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var

View file

@ -69,17 +69,42 @@ void VoiceAssistant::setup() {
void VoiceAssistant::loop() { void VoiceAssistant::loop() {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ == nullptr) { if (this->speaker_ != nullptr) {
return;
}
uint8_t buf[1024]; uint8_t buf[1024];
auto len = this->socket_->read(buf, sizeof(buf)); auto len = this->socket_->read(buf, sizeof(buf));
if (len == -1) { if (len == -1) {
return; return;
} }
this->speaker_->play(buf, len); this->speaker_->play(buf, len);
this->set_timeout("data-incoming", 200, [this]() {
if (this->continuous_) {
this->request_start(true);
}
});
return;
}
#endif #endif
#ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) {
if (!this->playing_tts_ ||
this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING) {
return;
}
this->set_timeout("playing-media", 1000, [this]() {
this->playing_tts_ = false;
if (this->continuous_) {
this->request_start(true);
}
});
return;
}
#endif
// Set a 1 second timeout to start the voice assistant again.
this->set_timeout("continuous-no-sound", 1000, [this]() {
if (this->continuous_) {
this->request_start(true);
}
});
} }
void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) { void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) {
@ -100,14 +125,19 @@ void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) {
} }
this->running_ = true; this->running_ = true;
this->mic_->start(); this->mic_->start();
this->listening_trigger_->trigger();
} }
void VoiceAssistant::request_start() { void VoiceAssistant::request_start(bool continuous) {
ESP_LOGD(TAG, "Requesting start..."); ESP_LOGD(TAG, "Requesting start...");
if (!api::global_api_server->start_voice_assistant()) { if (!api::global_api_server->start_voice_assistant(this->conversation_id_)) {
ESP_LOGW(TAG, "Could not request start."); ESP_LOGW(TAG, "Could not request start.");
this->error_trigger_->trigger("not-connected", "Could not request start."); this->error_trigger_->trigger("not-connected", "Could not request start.");
this->continuous_ = false;
return;
} }
this->continuous_ = continuous;
this->set_timeout("reset-conversation_id", 5 * 60 * 1000, [this]() { this->conversation_id_ = ""; });
} }
void VoiceAssistant::signal_stop() { void VoiceAssistant::signal_stop() {
@ -136,9 +166,18 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
return; return;
} }
ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str()); ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
this->signal_stop();
this->stt_end_trigger_->trigger(text); this->stt_end_trigger_->trigger(text);
break; break;
} }
case api::enums::VOICE_ASSISTANT_INTENT_END: {
for (auto arg : msg.data) {
if (arg.name == "conversation_id") {
this->conversation_id_ = std::move(arg.value);
}
}
break;
}
case api::enums::VOICE_ASSISTANT_TTS_START: { case api::enums::VOICE_ASSISTANT_TTS_START: {
std::string text; std::string text;
for (auto arg : msg.data) { for (auto arg : msg.data) {
@ -166,6 +205,12 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
return; return;
} }
ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str()); ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str());
#ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) {
this->playing_tts_ = true;
this->media_player_->make_call().set_media_url(url).perform();
}
#endif
this->tts_end_trigger_->trigger(url); this->tts_end_trigger_->trigger(url);
break; break;
} }
@ -184,6 +229,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
} }
} }
ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str()); ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str());
this->continuous_ = false;
this->signal_stop();
this->error_trigger_->trigger(code, message); this->error_trigger_->trigger(code, message);
} }
default: default:

View file

@ -15,6 +15,9 @@
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
#include "esphome/components/speaker/speaker.h" #include "esphome/components/speaker/speaker.h"
#endif #endif
#ifdef USE_MEDIA_PLAYER
#include "esphome/components/media_player/media_player.h"
#endif
#include "esphome/components/socket/socket.h" #include "esphome/components/socket/socket.h"
namespace esphome { namespace esphome {
@ -22,8 +25,10 @@ namespace voice_assistant {
// Version 1: Initial version // Version 1: Initial version
// Version 2: Adds raw speaker support // Version 2: Adds raw speaker support
// Version 3: Adds continuous support
static const uint32_t INITIAL_VERSION = 1; static const uint32_t INITIAL_VERSION = 1;
static const uint32_t SPEAKER_SUPPORT = 2; static const uint32_t SPEAKER_SUPPORT = 2;
static const uint32_t SILENCE_DETECTION_SUPPORT = 3;
class VoiceAssistant : public Component { class VoiceAssistant : public Component {
public: public:
@ -36,20 +41,34 @@ class VoiceAssistant : public Component {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; }
#endif #endif
#ifdef USE_MEDIA_PLAYER
void set_media_player(media_player::MediaPlayer *media_player) { this->media_player_ = media_player; }
#endif
uint32_t get_version() const { uint32_t get_version() const {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ != nullptr) if (this->speaker_ != nullptr) {
if (this->silence_detection_) {
return SILENCE_DETECTION_SUPPORT;
}
return SPEAKER_SUPPORT; return SPEAKER_SUPPORT;
}
#endif #endif
return INITIAL_VERSION; return INITIAL_VERSION;
} }
void request_start(); void request_start(bool continuous = false);
void signal_stop(); void signal_stop();
void on_event(const api::VoiceAssistantEventResponse &msg); void on_event(const api::VoiceAssistantEventResponse &msg);
bool is_running() const { return this->running_; }
void set_continuous(bool continuous) { this->continuous_ = continuous; }
bool is_continuous() const { return this->continuous_; }
void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; }
Trigger<> *get_listening_trigger() const { return this->listening_trigger_; }
Trigger<> *get_start_trigger() const { return this->start_trigger_; } Trigger<> *get_start_trigger() const { return this->start_trigger_; }
Trigger<std::string> *get_stt_end_trigger() const { return this->stt_end_trigger_; } Trigger<std::string> *get_stt_end_trigger() const { return this->stt_end_trigger_; }
Trigger<std::string> *get_tts_start_trigger() const { return this->tts_start_trigger_; } Trigger<std::string> *get_tts_start_trigger() const { return this->tts_start_trigger_; }
@ -61,6 +80,7 @@ class VoiceAssistant : public Component {
std::unique_ptr<socket::Socket> socket_ = nullptr; std::unique_ptr<socket::Socket> socket_ = nullptr;
struct sockaddr_storage dest_addr_; struct sockaddr_storage dest_addr_;
Trigger<> *listening_trigger_ = new Trigger<>();
Trigger<> *start_trigger_ = new Trigger<>(); Trigger<> *start_trigger_ = new Trigger<>();
Trigger<std::string> *stt_end_trigger_ = new Trigger<std::string>(); Trigger<std::string> *stt_end_trigger_ = new Trigger<std::string>();
Trigger<std::string> *tts_start_trigger_ = new Trigger<std::string>(); Trigger<std::string> *tts_start_trigger_ = new Trigger<std::string>();
@ -72,8 +92,16 @@ class VoiceAssistant : public Component {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
speaker::Speaker *speaker_{nullptr}; speaker::Speaker *speaker_{nullptr};
#endif #endif
#ifdef USE_MEDIA_PLAYER
media_player::MediaPlayer *media_player_{nullptr};
bool playing_tts_{false};
#endif
std::string conversation_id_{""};
bool running_{false}; bool running_{false};
bool continuous_{false};
bool silence_detection_;
}; };
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<VoiceAssistant> { template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<VoiceAssistant> {
@ -81,9 +109,22 @@ template<typename... Ts> class StartAction : public Action<Ts...>, public Parent
void play(Ts... x) override { this->parent_->request_start(); } void play(Ts... x) override { this->parent_->request_start(); }
}; };
template<typename... Ts> class StartContinuousAction : public Action<Ts...>, public Parented<VoiceAssistant> {
public:
void play(Ts... x) override { this->parent_->request_start(true); }
};
template<typename... Ts> class StopAction : public Action<Ts...>, public Parented<VoiceAssistant> { template<typename... Ts> class StopAction : public Action<Ts...>, public Parented<VoiceAssistant> {
public: public:
void play(Ts... x) override { this->parent_->signal_stop(); } void play(Ts... x) override {
this->parent_->set_continuous(false);
this->parent_->signal_stop();
}
};
template<typename... Ts> class IsRunningCondition : public Condition<Ts...>, public Parented<VoiceAssistant> {
public:
bool check(Ts... x) override { return this->parent_->is_running() || this->parent_->is_continuous(); }
}; };
extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View file

@ -53,6 +53,9 @@ WIFI_POWER_SAVE_MODES = {
"HIGH": WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, "HIGH": WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH,
} }
WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition)
WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition)
WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action)
WiFiDisableAction = wifi_ns.class_("WiFiDisableAction", automation.Action)
def validate_password(value): def validate_password(value):
@ -253,6 +256,7 @@ def _validate(config):
CONF_OUTPUT_POWER = "output_power" CONF_OUTPUT_POWER = "output_power"
CONF_PASSIVE_SCAN = "passive_scan" CONF_PASSIVE_SCAN = "passive_scan"
CONF_ENABLE_ON_BOOT = "enable_on_boot"
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
@ -286,6 +290,7 @@ CONFIG_SCHEMA = cv.All(
"This option has been removed. Please use the [disabled] option under the " "This option has been removed. Please use the [disabled] option under the "
"new mdns component instead." "new mdns component instead."
), ),
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
} }
), ),
_validate, _validate,
@ -385,6 +390,8 @@ async def to_code(config):
if CONF_OUTPUT_POWER in config: if CONF_OUTPUT_POWER in config:
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER])) cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
if CORE.is_esp8266: if CORE.is_esp8266:
cg.add_library("ESP8266WiFi", None) cg.add_library("ESP8266WiFi", None)
elif CORE.is_esp32 and CORE.using_arduino: elif CORE.is_esp32 and CORE.using_arduino:
@ -410,3 +417,18 @@ async def to_code(config):
@automation.register_condition("wifi.connected", WiFiConnectedCondition, cv.Schema({})) @automation.register_condition("wifi.connected", WiFiConnectedCondition, cv.Schema({}))
async def wifi_connected_to_code(config, condition_id, template_arg, args): async def wifi_connected_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg) return cg.new_Pvariable(condition_id, template_arg)
@automation.register_condition("wifi.enabled", WiFiEnabledCondition, cv.Schema({}))
async def wifi_enabled_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg)
@automation.register_action("wifi.enable", WiFiEnableAction, cv.Schema({}))
async def wifi_enable_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg)
@automation.register_action("wifi.disable", WiFiDisableAction, cv.Schema({}))
async def wifi_disable_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg)

View file

@ -36,9 +36,18 @@ float WiFiComponent::get_setup_priority() const { return setup_priority::WIFI; }
void WiFiComponent::setup() { void WiFiComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up WiFi..."); ESP_LOGCONFIG(TAG, "Setting up WiFi...");
this->wifi_pre_setup_();
if (this->enable_on_boot_) {
this->start();
} else {
this->state_ = WIFI_COMPONENT_STATE_DISABLED;
}
}
void WiFiComponent::start() {
ESP_LOGCONFIG(TAG, "Starting WiFi...");
ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str()); ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
this->last_connected_ = millis(); this->last_connected_ = millis();
this->wifi_pre_setup_();
uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL;
@ -135,6 +144,8 @@ void WiFiComponent::loop() {
case WIFI_COMPONENT_STATE_OFF: case WIFI_COMPONENT_STATE_OFF:
case WIFI_COMPONENT_STATE_AP: case WIFI_COMPONENT_STATE_AP:
break; break;
case WIFI_COMPONENT_STATE_DISABLED:
return;
} }
if (this->has_ap() && !this->ap_setup_) { if (this->has_ap() && !this->ap_setup_) {
@ -387,6 +398,28 @@ void WiFiComponent::print_connect_params_() {
#endif #endif
} }
void WiFiComponent::enable() {
if (this->state_ != WIFI_COMPONENT_STATE_DISABLED)
return;
ESP_LOGD(TAG, "Enabling WIFI...");
this->error_from_callback_ = false;
this->state_ = WIFI_COMPONENT_STATE_OFF;
this->start();
}
void WiFiComponent::disable() {
if (this->state_ == WIFI_COMPONENT_STATE_DISABLED)
return;
ESP_LOGD(TAG, "Disabling WIFI...");
this->state_ = WIFI_COMPONENT_STATE_DISABLED;
this->wifi_disconnect_();
this->wifi_mode_(false, false);
}
bool WiFiComponent::is_disabled() { return this->state_ == WIFI_COMPONENT_STATE_DISABLED; }
void WiFiComponent::start_scanning() { void WiFiComponent::start_scanning() {
this->action_started_ = millis(); this->action_started_ = millis();
ESP_LOGD(TAG, "Starting scan..."); ESP_LOGD(TAG, "Starting scan...");
@ -608,7 +641,7 @@ void WiFiComponent::retry_connect() {
} }
bool WiFiComponent::can_proceed() { bool WiFiComponent::can_proceed() {
if (!this->has_sta()) { if (!this->has_sta() || this->state_ == WIFI_COMPONENT_STATE_DISABLED) {
return true; return true;
} }
return this->is_connected(); return this->is_connected();

View file

@ -47,6 +47,8 @@ struct SavedWifiSettings {
enum WiFiComponentState { enum WiFiComponentState {
/** Nothing has been initialized yet. Internal AP, if configured, is disabled at this point. */ /** Nothing has been initialized yet. Internal AP, if configured, is disabled at this point. */
WIFI_COMPONENT_STATE_OFF = 0, WIFI_COMPONENT_STATE_OFF = 0,
/** WiFi is disabled. */
WIFI_COMPONENT_STATE_DISABLED,
/** WiFi is in cooldown mode because something went wrong, scanning will begin after a short period of time. */ /** WiFi is in cooldown mode because something went wrong, scanning will begin after a short period of time. */
WIFI_COMPONENT_STATE_COOLDOWN, WIFI_COMPONENT_STATE_COOLDOWN,
/** WiFi is in STA-only mode and currently scanning for APs. */ /** WiFi is in STA-only mode and currently scanning for APs. */
@ -198,6 +200,9 @@ class WiFiComponent : public Component {
void set_ap(const WiFiAP &ap); void set_ap(const WiFiAP &ap);
WiFiAP get_ap() { return this->ap_; } WiFiAP get_ap() { return this->ap_; }
void enable();
void disable();
bool is_disabled();
void start_scanning(); void start_scanning();
void check_scanning_finished(); void check_scanning_finished();
void start_connecting(const WiFiAP &ap, bool two); void start_connecting(const WiFiAP &ap, bool two);
@ -224,6 +229,7 @@ class WiFiComponent : public Component {
// (In most use cases you won't need these) // (In most use cases you won't need these)
/// Setup WiFi interface. /// Setup WiFi interface.
void setup() override; void setup() override;
void start();
void dump_config() override; void dump_config() override;
/// WIFI setup_priority. /// WIFI setup_priority.
float get_setup_priority() const override; float get_setup_priority() const override;
@ -282,6 +288,8 @@ class WiFiComponent : public Component {
int8_t wifi_rssi(); int8_t wifi_rssi();
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
protected: protected:
static std::string format_mac_addr(const uint8_t mac[6]); static std::string format_mac_addr(const uint8_t mac[6]);
void setup_ap_config_(); void setup_ap_config_();
@ -359,18 +367,30 @@ class WiFiComponent : public Component {
bool btm_{false}; bool btm_{false};
bool rrm_{false}; bool rrm_{false};
#endif #endif
bool enable_on_boot_;
}; };
extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
template<typename... Ts> class WiFiConnectedCondition : public Condition<Ts...> { template<typename... Ts> class WiFiConnectedCondition : public Condition<Ts...> {
public: public:
bool check(Ts... x) override; bool check(Ts... x) override { return global_wifi_component->is_connected(); }
}; };
template<typename... Ts> bool WiFiConnectedCondition<Ts...>::check(Ts... x) { template<typename... Ts> class WiFiEnabledCondition : public Condition<Ts...> {
return global_wifi_component->is_connected(); public:
} bool check(Ts... x) override { return !global_wifi_component->is_disabled(); }
};
template<typename... Ts> class WiFiEnableAction : public Action<Ts...> {
public:
void play(Ts... x) override { global_wifi_component->enable(); }
};
template<typename... Ts> class WiFiDisableAction : public Action<Ts...> {
public:
void play(Ts... x) override { global_wifi_component->disable(); }
};
} // namespace wifi } // namespace wifi
} // namespace esphome } // namespace esphome

View file

@ -399,6 +399,7 @@ CONF_MAX_VOLTAGE = "max_voltage"
CONF_MDNS = "mdns" CONF_MDNS = "mdns"
CONF_MEASUREMENT_DURATION = "measurement_duration" CONF_MEASUREMENT_DURATION = "measurement_duration"
CONF_MEASUREMENT_SEQUENCE_NUMBER = "measurement_sequence_number" CONF_MEASUREMENT_SEQUENCE_NUMBER = "measurement_sequence_number"
CONF_MEDIA_PLAYER = "media_player"
CONF_MEDIUM = "medium" CONF_MEDIUM = "medium"
CONF_MEMORY_BLOCKS = "memory_blocks" CONF_MEMORY_BLOCKS = "memory_blocks"
CONF_METHOD = "method" CONF_METHOD = "method"

View file

@ -7,11 +7,11 @@ tzlocal==5.0.1 # from time
tzdata>=2021.1 # from time tzdata>=2021.1 # from time
pyserial==3.5 pyserial==3.5
platformio==6.1.7 # When updating platformio, also update Dockerfile platformio==6.1.7 # When updating platformio, also update Dockerfile
esptool==4.5.1 esptool==4.6
click==8.1.3 click==8.1.3
esphome-dashboard==20230516.0 esphome-dashboard==20230516.0
aioesphomeapi==13.7.5 aioesphomeapi==13.9.0
zeroconf==0.62.0 zeroconf==0.63.0
# esp-idf requires this, but doesn't bundle it by default # esp-idf requires this, but doesn't bundle it by default
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24

View file

@ -6,7 +6,7 @@ pre-commit
# Unit tests # Unit tests
pytest==7.3.1 pytest==7.3.1
pytest-cov==4.0.0 pytest-cov==4.1.0
pytest-mock==3.10.0 pytest-mock==3.10.0
pytest-asyncio==0.21.0 pytest-asyncio==0.21.0
asyncmock==0.4.2 asyncmock==0.4.2

View file

@ -37,24 +37,24 @@ switch:
output: pin_4 output: pin_4
id: pin_4_switch id: pin_4_switch
light: #light:
- platform: rp2040_pio_led_strip # - platform: rp2040_pio_led_strip
id: led_strip # id: led_strip
pin: GPIO13 # pin: GPIO13
num_leds: 60 # num_leds: 60
pio: 0 # pio: 0
rgb_order: GRB # rgb_order: GRB
chipset: WS2812 # chipset: WS2812
- platform: rp2040_pio_led_strip # - platform: rp2040_pio_led_strip
id: led_strip_custom_timings # id: led_strip_custom_timings
pin: GPIO13 # pin: GPIO13
num_leds: 60 # num_leds: 60
pio: 1 # pio: 1
rgb_order: GRB # rgb_order: GRB
bit0_high: .1us # bit0_high: .1us
bit0_low: 1.2us # bit0_low: 1.2us
bit1_high: .69us # bit1_high: .69us
bit1_low: .4us # bit1_low: .4us
sensor: sensor: