Merge branch 'dev' into webserver-sorting

This commit is contained in:
RFDarter 2024-05-15 00:32:59 +02:00 committed by GitHub
commit a991a83833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1163 additions and 416 deletions

View File

@ -34,6 +34,16 @@ runs:
echo $l >> $GITHUB_OUTPUT echo $l >> $GITHUB_OUTPUT
done done
# set cache-to only if dev branch
- id: cache-to
shell: bash
run: |-
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
echo "value=type=gha,mode=max" >> $GITHUB_OUTPUT
else
echo "value=" >> $GITHUB_OUTPUT
fi
- name: Build and push to ghcr by digest - name: Build and push to ghcr by digest
id: build-ghcr id: build-ghcr
uses: docker/build-push-action@v5.3.0 uses: docker/build-push-action@v5.3.0
@ -43,7 +53,7 @@ runs:
platforms: ${{ inputs.platform }} platforms: ${{ inputs.platform }}
target: ${{ inputs.target }} target: ${{ inputs.target }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: ${{ steps.cache-to.outputs.value }}
build-args: | build-args: |
BASEIMGTYPE=${{ inputs.baseimg }} BASEIMGTYPE=${{ inputs.baseimg }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}
@ -66,7 +76,7 @@ runs:
platforms: ${{ inputs.platform }} platforms: ${{ inputs.platform }}
target: ${{ inputs.target }} target: ${{ inputs.target }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: ${{ steps.cache-to.outputs.value }}
build-args: | build-args: |
BASEIMGTYPE=${{ inputs.baseimg }} BASEIMGTYPE=${{ inputs.baseimg }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}

View File

@ -364,12 +364,20 @@ jobs:
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio - name: Cache platformio
if: github.ref == 'refs/heads/dev'
uses: actions/cache@v4.0.2 uses: actions/cache@v4.0.2
with: with:
path: ~/.platformio path: ~/.platformio
# yamllint disable-line rule:line-length key: platformio-${{ matrix.pio_cache_key }}
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@v4.0.2
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}
- name: Install clang-tidy - name: Install clang-tidy
run: sudo apt-get install clang-tidy-14 run: sudo apt-get install clang-tidy-14

View File

@ -40,3 +40,10 @@ repos:
hooks: hooks:
- id: clang-format - id: clang-format
types_or: [c, c++] types_or: [c, c++]
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]

View File

@ -135,6 +135,7 @@ esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio esphome/components/ft63x6/* @gpambrozio
esphome/components/gcja5/* @gcormier esphome/components/gcja5/* @gcormier
esphome/components/gdk101/* @Szewcson
esphome/components/globals/* @esphome/core esphome/components/globals/* @esphome/core
esphome/components/gp8403/* @jesserockz esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core esphome/components/gpio/* @esphome/core

View File

@ -65,7 +65,7 @@ def choose_prompt(options, purpose: str = None):
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:' f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
) )
for i, (desc, _) in enumerate(options): for i, (desc, _) in enumerate(options):
safe_print(f" [{i+1}] {desc}") safe_print(f" [{i + 1}] {desc}")
while True: while True:
opt = input("(number): ") opt = input("(number): ")

View File

@ -157,7 +157,7 @@ async def to_code(config):
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width: if len(pixels) != height * width:
raise core.EsphomeError( raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})" f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
) )
for pix, a in pixels: for pix, a in pixels:
if transparent: if transparent:
@ -180,7 +180,7 @@ async def to_code(config):
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width: if len(pixels) != height * width:
raise core.EsphomeError( raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})" f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
) )
for pix in pixels: for pix in pixels:
data[pos] = pix[0] data[pos] = pix[0]
@ -203,7 +203,7 @@ async def to_code(config):
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width: if len(pixels) != height * width:
raise core.EsphomeError( raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})" f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
) )
for r, g, b, a in pixels: for r, g, b, a in pixels:
if transparent: if transparent:
@ -232,7 +232,7 @@ async def to_code(config):
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width: if len(pixels) != height * width:
raise core.EsphomeError( raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})" f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
) )
for r, g, b, a in pixels: for r, g, b, a in pixels:
R = r >> 3 R = r >> 3

View File

@ -1147,6 +1147,9 @@ message MediaPlayerCommandRequest {
bool has_media_url = 6; bool has_media_url = 6;
string media_url = 7; string media_url = 7;
bool has_announcement = 8;
bool announcement = 9;
} }
// ==================== BLUETOOTH ==================== // ==================== BLUETOOTH ====================

View File

@ -1002,7 +1002,11 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla
MediaPlayerStateResponse resp{}; MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash(); resp.key = media_player->get_object_id_hash();
resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
? media_player::MEDIA_PLAYER_STATE_PLAYING
: media_player->state;
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume; resp.volume = media_player->volume;
resp.muted = media_player->is_muted(); resp.muted = media_player->is_muted();
return this->send_media_player_state_response(resp); return this->send_media_player_state_response(resp);
@ -1038,6 +1042,9 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
if (msg.has_media_url) { if (msg.has_media_url) {
call.set_media_url(msg.media_url); call.set_media_url(msg.media_url);
} }
if (msg.has_announcement) {
call.set_announcement(msg.announcement);
}
call.perform(); call.perform();
} }
#endif #endif

View File

@ -5253,6 +5253,14 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val
this->has_media_url = value.as_bool(); this->has_media_url = value.as_bool();
return true; return true;
} }
case 8: {
this->has_announcement = value.as_bool();
return true;
}
case 9: {
this->announcement = value.as_bool();
return true;
}
default: default:
return false; return false;
} }
@ -5289,6 +5297,8 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(5, this->volume); buffer.encode_float(5, this->volume);
buffer.encode_bool(6, this->has_media_url); buffer.encode_bool(6, this->has_media_url);
buffer.encode_string(7, this->media_url); buffer.encode_string(7, this->media_url);
buffer.encode_bool(8, this->has_announcement);
buffer.encode_bool(9, this->announcement);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void MediaPlayerCommandRequest::dump_to(std::string &out) const { void MediaPlayerCommandRequest::dump_to(std::string &out) const {
@ -5323,6 +5333,14 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
out.append(" media_url: "); out.append(" media_url: ");
out.append("'").append(this->media_url).append("'"); out.append("'").append(this->media_url).append("'");
out.append("\n"); out.append("\n");
out.append(" has_announcement: ");
out.append(YESNO(this->has_announcement));
out.append("\n");
out.append(" announcement: ");
out.append(YESNO(this->announcement));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif

View File

@ -1298,6 +1298,8 @@ class MediaPlayerCommandRequest : public ProtoMessage {
float volume{0.0f}; float volume{0.0f};
bool has_media_url{false}; bool has_media_url{false};
std::string media_url{}; std::string media_url{};
bool has_announcement{false};
bool announcement{false};
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;

View File

@ -15,6 +15,7 @@ void CST816Touchscreen::continue_setup_() {
} }
switch (this->chip_id_) { switch (this->chip_id_) {
case CST820_CHIP_ID: case CST820_CHIP_ID:
case CST826_CHIP_ID:
case CST716_CHIP_ID: case CST716_CHIP_ID:
case CST816S_CHIP_ID: case CST816S_CHIP_ID:
case CST816D_CHIP_ID: case CST816D_CHIP_ID:
@ -90,6 +91,9 @@ void CST816Touchscreen::dump_config() {
case CST820_CHIP_ID: case CST820_CHIP_ID:
name = "CST820"; name = "CST820";
break; break;
case CST826_CHIP_ID:
name = "CST826";
break;
case CST816S_CHIP_ID: case CST816S_CHIP_ID:
name = "CST816S"; name = "CST816S";
break; break;

View File

@ -24,6 +24,7 @@ static const uint8_t REG_SLEEP = 0xE5;
static const uint8_t REG_IRQ_CTL = 0xFA; static const uint8_t REG_IRQ_CTL = 0xFA;
static const uint8_t IRQ_EN_MOTION = 0x70; static const uint8_t IRQ_EN_MOTION = 0x70;
static const uint8_t CST826_CHIP_ID = 0x11;
static const uint8_t CST820_CHIP_ID = 0xB7; static const uint8_t CST820_CHIP_ID = 0xB7;
static const uint8_t CST816S_CHIP_ID = 0xB4; static const uint8_t CST816S_CHIP_ID = 0xB4;
static const uint8_t CST816D_CHIP_ID = 0xB6; static const uint8_t CST816D_CHIP_ID = 0xB6;

View File

@ -8,62 +8,16 @@
#include <cinttypes> #include <cinttypes>
#include <climits> #include <climits>
#ifdef USE_ESP32
#include <esp_heap_caps.h>
#include <esp_system.h>
#include <esp_chip_info.h>
#if defined(USE_ESP32_VARIANT_ESP32)
#include <esp32/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C3)
#include <esp32c3/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C6)
#include <esp32c6/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S2)
#include <esp32s2/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S3)
#include <esp32s3/rom/rtc.h>
#endif
#endif // USE_ESP32
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include <Arduino.h>
#elif defined(USE_ESP32) || defined(USE_ESP8266)
#include <Esp.h>
#endif
#endif
namespace esphome { namespace esphome {
namespace debug { namespace debug {
static const char *const TAG = "debug"; static const char *const TAG = "debug";
static uint32_t get_free_heap() {
#if defined(USE_ESP8266)
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
#elif defined(USE_ESP32)
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#elif defined(USE_RP2040)
return rp2040.getFreeHeap();
#elif defined(USE_LIBRETINY)
return lt_heap_get_free();
#elif defined(USE_HOST)
return INT_MAX;
#endif
}
void DebugComponent::dump_config() { void DebugComponent::dump_config() {
#ifndef ESPHOME_LOG_HAS_DEBUG #ifndef ESPHOME_LOG_HAS_DEBUG
return; // Can't log below if debug logging is disabled return; // Can't log below if debug logging is disabled
#endif #endif
std::string device_info;
std::string reset_reason;
device_info.reserve(256);
ESP_LOGCONFIG(TAG, "Debug component:"); ESP_LOGCONFIG(TAG, "Debug component:");
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_); LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
@ -76,305 +30,15 @@ void DebugComponent::dump_config() {
#endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2) #endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#endif // USE_SENSOR #endif // USE_SENSOR
std::string device_info;
device_info.reserve(256);
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
device_info += ESPHOME_VERSION; device_info += ESPHOME_VERSION;
this->free_heap_ = get_free_heap(); this->free_heap_ = get_free_heap_();
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_); ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
#if defined(USE_ARDUINO) && (defined(USE_ESP32) || defined(USE_ESP8266)) get_device_info_(device_info);
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
#ifdef USE_ESP32
case FM_FAST_READ:
flash_mode = "FAST_READ";
break;
case FM_SLOW_READ:
flash_mode = "SLOW_READ";
break;
#endif
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#endif // USE_ARDUINO && (USE_ESP32 || USE_ESP8266)
#ifdef USE_ESP32
esp_chip_info_t info;
esp_chip_info(&info);
const char *model;
#if defined(USE_ESP32_VARIANT_ESP32)
model = "ESP32";
#elif defined(USE_ESP32_VARIANT_ESP32C3)
model = "ESP32-C3";
#elif defined(USE_ESP32_VARIANT_ESP32C6)
model = "ESP32-C6";
#elif defined(USE_ESP32_VARIANT_ESP32S2)
model = "ESP32-S2";
#elif defined(USE_ESP32_VARIANT_ESP32S3)
model = "ESP32-S3";
#elif defined(USE_ESP32_VARIANT_ESP32H2)
model = "ESP32-H2";
#else
model = "UNKNOWN";
#endif
std::string features;
if (info.features & CHIP_FEATURE_EMB_FLASH) {
features += "EMB_FLASH,";
info.features &= ~CHIP_FEATURE_EMB_FLASH;
}
if (info.features & CHIP_FEATURE_WIFI_BGN) {
features += "WIFI_BGN,";
info.features &= ~CHIP_FEATURE_WIFI_BGN;
}
if (info.features & CHIP_FEATURE_BLE) {
features += "BLE,";
info.features &= ~CHIP_FEATURE_BLE;
}
if (info.features & CHIP_FEATURE_BT) {
features += "BT,";
info.features &= ~CHIP_FEATURE_BT;
}
if (info.features & CHIP_FEATURE_EMB_PSRAM) {
features += "EMB_PSRAM,";
info.features &= ~CHIP_FEATURE_EMB_PSRAM;
}
if (info.features)
features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
info.revision);
device_info += "|Chip: ";
device_info += model;
device_info += " Features:";
device_info += features;
device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision);
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version();
std::string mac = get_mac_address_pretty();
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
device_info += "|EFuse MAC: ";
device_info += mac;
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET:
reset_reason = "Power On Reset";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_SYS_RESET:
#endif
reset_reason = "Software Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core";
break;
#endif
case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core";
break;
#endif
case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break;
case TG1WDT_SYS_RESET:
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
break;
case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core";
break;
#if !defined(USE_ESP32_VARIANT_ESP32C6)
case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU";
break;
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG0WDT_CPU_RESET:
reset_reason = "Timer Group 0 Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_CPU_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_CPU_RESET:
#endif
reset_reason = "Software Reset CPU";
break;
case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET:
reset_reason = "External CPU Reset";
break;
#endif
case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset";
break;
case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
break;
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG1WDT_CPU_RESET:
reset_reason = "Timer Group 1 Reset CPU";
break;
case SUPER_WDT_RESET:
reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
break;
case GLITCH_RTC_RESET:
reset_reason = "Glitch Reset Digital Core And RTC Module";
break;
case EFUSE_RESET:
reset_reason = "eFuse Reset Digital Core";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
case USB_UART_CHIP_RESET:
reset_reason = "USB UART Reset Digital Core";
break;
case USB_JTAG_CHIP_RESET:
reset_reason = "USB JTAG Reset Digital Core";
break;
case POWER_GLITCH_RESET:
reset_reason = "Power Glitch Reset Digital Core And RTC Module";
break;
#endif
default:
reset_reason = "Unknown Reset Reason";
}
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
device_info += "|Reset: ";
device_info += reset_reason;
const char *wakeup_reason;
switch (rtc_get_wakeup_cause()) {
case NO_SLEEP:
wakeup_reason = "No Sleep";
break;
case EXT_EVENT0_TRIG:
wakeup_reason = "External Event 0";
break;
case EXT_EVENT1_TRIG:
wakeup_reason = "External Event 1";
break;
case GPIO_TRIG:
wakeup_reason = "GPIO";
break;
case TIMER_EXPIRE:
wakeup_reason = "Wakeup Timer";
break;
case SDIO_TRIG:
wakeup_reason = "SDIO";
break;
case MAC_TRIG:
wakeup_reason = "MAC";
break;
case UART0_TRIG:
wakeup_reason = "UART0";
break;
case UART1_TRIG:
wakeup_reason = "UART1";
break;
case TOUCH_TRIG:
wakeup_reason = "Touch";
break;
case SAR_TRIG:
wakeup_reason = "SAR";
break;
case BT_TRIG:
wakeup_reason = "BT";
break;
default:
wakeup_reason = "Unknown";
}
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
device_info += "|Wakeup: ";
device_info += wakeup_reason;
#endif
#if defined(USE_ESP8266) && !defined(CLANG_TIDY)
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
device_info += ESP.getSdkVersion();
device_info += "|Core: ";
device_info += ESP.getCoreVersion().c_str();
device_info += "|Boot: ";
device_info += to_string(ESP.getBootVersion());
device_info += "|Mode: " + to_string(ESP.getBootMode());
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
device_info += "|Reset: ";
device_info += ESP.getResetReason().c_str();
device_info += "|";
device_info += ESP.getResetInfo().c_str();
reset_reason = ESP.getResetReason().c_str();
#endif
#ifdef USE_RP2040
ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
#endif // USE_RP2040
#ifdef USE_LIBRETINY
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
ESP_LOGD(TAG, "Reset Reason: %s", lt_get_reboot_reason_name(lt_get_reboot_reason()));
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;
device_info += "|Reset Reason: ";
device_info += lt_get_reboot_reason_name(lt_get_reboot_reason());
device_info += "|Chip Name: ";
device_info += lt_cpu_get_model_name();
device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
reset_reason = lt_get_reboot_reason_name(lt_get_reboot_reason());
#endif // USE_LIBRETINY
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->device_info_ != nullptr) { if (this->device_info_ != nullptr) {
@ -383,14 +47,14 @@ void DebugComponent::dump_config() {
this->device_info_->publish_state(device_info); this->device_info_->publish_state(device_info);
} }
if (this->reset_reason_ != nullptr) { if (this->reset_reason_ != nullptr) {
this->reset_reason_->publish_state(reset_reason); this->reset_reason_->publish_state(get_reset_reason_());
} }
#endif // USE_TEXT_SENSOR #endif // USE_TEXT_SENSOR
} }
void DebugComponent::loop() { void DebugComponent::loop() {
// log when free heap space has halved // log when free heap space has halved
uint32_t new_free_heap = get_free_heap(); uint32_t new_free_heap = get_free_heap_();
if (new_free_heap < this->free_heap_ / 2) { if (new_free_heap < this->free_heap_ / 2) {
this->free_heap_ = new_free_heap; this->free_heap_ = new_free_heap;
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_); ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
@ -411,38 +75,16 @@ void DebugComponent::loop() {
void DebugComponent::update() { void DebugComponent::update() {
#ifdef USE_SENSOR #ifdef USE_SENSOR
if (this->free_sensor_ != nullptr) { if (this->free_sensor_ != nullptr) {
this->free_sensor_->publish_state(get_free_heap()); this->free_sensor_->publish_state(get_free_heap_());
} }
if (this->block_sensor_ != nullptr) {
#if defined(USE_ESP8266)
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
#elif defined(USE_ESP32)
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
#elif defined(USE_LIBRETINY)
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
#endif
}
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
}
#endif
if (this->loop_time_sensor_ != nullptr) { if (this->loop_time_sensor_ != nullptr) {
this->loop_time_sensor_->publish_state(this->max_loop_time_); this->loop_time_sensor_->publish_state(this->max_loop_time_);
this->max_loop_time_ = 0; this->max_loop_time_ = 0;
} }
#ifdef USE_ESP32
if (this->psram_sensor_ != nullptr) {
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
#endif // USE_ESP32
#endif // USE_SENSOR #endif // USE_SENSOR
update_platform_();
} }
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; } float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }

View File

@ -59,6 +59,11 @@ class DebugComponent : public PollingComponent {
text_sensor::TextSensor *device_info_{nullptr}; text_sensor::TextSensor *device_info_{nullptr};
text_sensor::TextSensor *reset_reason_{nullptr}; text_sensor::TextSensor *reset_reason_{nullptr};
#endif // USE_TEXT_SENSOR #endif // USE_TEXT_SENSOR
std::string get_reset_reason_();
uint32_t get_free_heap_();
void get_device_info_(std::string &device_info);
void update_platform_();
}; };
} // namespace debug } // namespace debug

View File

@ -0,0 +1,287 @@
#include "debug_component.h"
#ifdef USE_ESP32
#include "esphome/core/log.h"
#include <esp_heap_caps.h>
#include <esp_system.h>
#include <esp_chip_info.h>
#if defined(USE_ESP32_VARIANT_ESP32)
#include <esp32/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C3)
#include <esp32c3/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C6)
#include <esp32c6/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S2)
#include <esp32s2/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S3)
#include <esp32s3/rom/rtc.h>
#endif
#ifdef USE_ARDUINO
#include <Esp.h>
#endif
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() {
std::string reset_reason;
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET:
reset_reason = "Power On Reset";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_SYS_RESET:
#endif
reset_reason = "Software Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core";
break;
#endif
case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core";
break;
#endif
case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break;
case TG1WDT_SYS_RESET:
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
break;
case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core";
break;
#if !defined(USE_ESP32_VARIANT_ESP32C6)
case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU";
break;
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG0WDT_CPU_RESET:
reset_reason = "Timer Group 0 Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_CPU_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_CPU_RESET:
#endif
reset_reason = "Software Reset CPU";
break;
case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET:
reset_reason = "External CPU Reset";
break;
#endif
case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset";
break;
case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
break;
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG1WDT_CPU_RESET:
reset_reason = "Timer Group 1 Reset CPU";
break;
case SUPER_WDT_RESET:
reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
break;
case GLITCH_RTC_RESET:
reset_reason = "Glitch Reset Digital Core And RTC Module";
break;
case EFUSE_RESET:
reset_reason = "eFuse Reset Digital Core";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
case USB_UART_CHIP_RESET:
reset_reason = "USB UART Reset Digital Core";
break;
case USB_JTAG_CHIP_RESET:
reset_reason = "USB JTAG Reset Digital Core";
break;
case POWER_GLITCH_RESET:
reset_reason = "Power Glitch Reset Digital Core And RTC Module";
break;
#endif
default:
reset_reason = "Unknown Reset Reason";
}
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
return reset_reason;
}
uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); }
void DebugComponent::get_device_info_(std::string &device_info) {
#if defined(USE_ARDUINO)
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
case FM_FAST_READ:
flash_mode = "FAST_READ";
break;
case FM_SLOW_READ:
flash_mode = "SLOW_READ";
break;
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#endif
esp_chip_info_t info;
esp_chip_info(&info);
const char *model;
#if defined(USE_ESP32_VARIANT_ESP32)
model = "ESP32";
#elif defined(USE_ESP32_VARIANT_ESP32C3)
model = "ESP32-C3";
#elif defined(USE_ESP32_VARIANT_ESP32C6)
model = "ESP32-C6";
#elif defined(USE_ESP32_VARIANT_ESP32S2)
model = "ESP32-S2";
#elif defined(USE_ESP32_VARIANT_ESP32S3)
model = "ESP32-S3";
#elif defined(USE_ESP32_VARIANT_ESP32H2)
model = "ESP32-H2";
#else
model = "UNKNOWN";
#endif
std::string features;
if (info.features & CHIP_FEATURE_EMB_FLASH) {
features += "EMB_FLASH,";
info.features &= ~CHIP_FEATURE_EMB_FLASH;
}
if (info.features & CHIP_FEATURE_WIFI_BGN) {
features += "WIFI_BGN,";
info.features &= ~CHIP_FEATURE_WIFI_BGN;
}
if (info.features & CHIP_FEATURE_BLE) {
features += "BLE,";
info.features &= ~CHIP_FEATURE_BLE;
}
if (info.features & CHIP_FEATURE_BT) {
features += "BT,";
info.features &= ~CHIP_FEATURE_BT;
}
if (info.features & CHIP_FEATURE_EMB_PSRAM) {
features += "EMB_PSRAM,";
info.features &= ~CHIP_FEATURE_EMB_PSRAM;
}
if (info.features)
features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
info.revision);
device_info += "|Chip: ";
device_info += model;
device_info += " Features:";
device_info += features;
device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision);
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version();
std::string mac = get_mac_address_pretty();
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
device_info += "|EFuse MAC: ";
device_info += mac;
device_info += "|Reset: ";
device_info += get_reset_reason_();
const char *wakeup_reason;
switch (rtc_get_wakeup_cause()) {
case NO_SLEEP:
wakeup_reason = "No Sleep";
break;
case EXT_EVENT0_TRIG:
wakeup_reason = "External Event 0";
break;
case EXT_EVENT1_TRIG:
wakeup_reason = "External Event 1";
break;
case GPIO_TRIG:
wakeup_reason = "GPIO";
break;
case TIMER_EXPIRE:
wakeup_reason = "Wakeup Timer";
break;
case SDIO_TRIG:
wakeup_reason = "SDIO";
break;
case MAC_TRIG:
wakeup_reason = "MAC";
break;
case UART0_TRIG:
wakeup_reason = "UART0";
break;
case UART1_TRIG:
wakeup_reason = "UART1";
break;
case TOUCH_TRIG:
wakeup_reason = "Touch";
break;
case SAR_TRIG:
wakeup_reason = "SAR";
break;
case BT_TRIG:
wakeup_reason = "BT";
break;
default:
wakeup_reason = "Unknown";
}
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
device_info += "|Wakeup: ";
device_info += wakeup_reason;
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
}
if (this->psram_sensor_ != nullptr) {
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@ -0,0 +1,94 @@
#include "debug_component.h"
#ifdef USE_ESP8266
#include "esphome/core/log.h"
#include <Esp.h>
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() {
#if !defined(CLANG_TIDY)
return ESP.getResetReason().c_str();
#else
return "";
#endif
}
uint32_t DebugComponent::get_free_heap_() {
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
}
void DebugComponent::get_device_info_(std::string &device_info) {
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#if !defined(CLANG_TIDY)
auto reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
device_info += ESP.getSdkVersion();
device_info += "|Core: ";
device_info += ESP.getCoreVersion().c_str();
device_info += "|Boot: ";
device_info += to_string(ESP.getBootVersion());
device_info += "|Mode: " + to_string(ESP.getBootMode());
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
device_info += "|Reset: ";
device_info += reset_reason;
device_info += "|";
device_info += ESP.getResetInfo().c_str();
#endif
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
}
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
}
#endif
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@ -0,0 +1,18 @@
#include "debug_component.h"
#ifdef USE_HOST
#include <climits>
namespace esphome {
namespace debug {
std::string DebugComponent::get_reset_reason_() { return ""; }
uint32_t DebugComponent::get_free_heap_() { return INT_MAX; }
void DebugComponent::get_device_info_(std::string &device_info) {}
void DebugComponent::update_platform_() {}
} // namespace debug
} // namespace esphome
#endif

View File

@ -0,0 +1,44 @@
#include "debug_component.h"
#ifdef USE_LIBRETINY
#include "esphome/core/log.h"
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() { return lt_get_reboot_reason_name(lt_get_reboot_reason()); }
uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
void DebugComponent::get_device_info_(std::string &device_info) {
reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;
device_info += "|Reset Reason: ";
device_info += reset_reason;
device_info += "|Chip Name: ";
device_info += lt_cpu_get_model_name();
device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
}
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@ -0,0 +1,23 @@
#include "debug_component.h"
#ifdef USE_RP2040
#include "esphome/core/log.h"
#include <Arduino.h>
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() { return ""; }
uint32_t DebugComponent::get_free_heap_() { return rp2040.getFreeHeap(); }
void DebugComponent::get_device_info_(std::string &device_info) {
ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
}
void DebugComponent::update_platform_() {}
} // namespace debug
} // namespace esphome
#endif

View File

@ -1,6 +1,11 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include "ble.h" #include "ble.h"
#ifdef USE_ESP32_VARIANT_ESP32C6
#include "const_esp32c6.h"
#endif // USE_ESP32_VARIANT_ESP32C6
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -114,7 +119,11 @@ bool ESP32BLE::ble_setup_() {
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
// start bt controller // start bt controller
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
#ifdef USE_ESP32_VARIANT_ESP32C6
esp_bt_controller_config_t cfg = BT_CONTROLLER_CONFIG;
#else
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#endif
err = esp_bt_controller_init(&cfg); err = esp_bt_controller_init(&cfg);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));

View File

@ -0,0 +1,67 @@
#pragma once
#ifdef USE_ESP32_VARIANT_ESP32C6
#include <esp_bt.h>
namespace esphome {
namespace esp32_ble {
static const esp_bt_controller_config_t BT_CONTROLLER_CONFIG = {
.config_version = CONFIG_VERSION,
.ble_ll_resolv_list_size = CONFIG_BT_LE_LL_RESOLV_LIST_SIZE,
.ble_hci_evt_hi_buf_count = DEFAULT_BT_LE_HCI_EVT_HI_BUF_COUNT,
.ble_hci_evt_lo_buf_count = DEFAULT_BT_LE_HCI_EVT_LO_BUF_COUNT,
.ble_ll_sync_list_cnt = DEFAULT_BT_LE_MAX_PERIODIC_ADVERTISER_LIST,
.ble_ll_sync_cnt = DEFAULT_BT_LE_MAX_PERIODIC_SYNCS,
.ble_ll_rsp_dup_list_count = CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT,
.ble_ll_adv_dup_list_count = CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT,
.ble_ll_tx_pwr_dbm = BLE_LL_TX_PWR_DBM_N,
.rtc_freq = RTC_FREQ_N,
.ble_ll_sca = CONFIG_BT_LE_LL_SCA,
.ble_ll_scan_phy_number = BLE_LL_SCAN_PHY_NUMBER_N,
.ble_ll_conn_def_auth_pyld_tmo = BLE_LL_CONN_DEF_AUTH_PYLD_TMO_N,
.ble_ll_jitter_usecs = BLE_LL_JITTER_USECS_N,
.ble_ll_sched_max_adv_pdu_usecs = BLE_LL_SCHED_MAX_ADV_PDU_USECS_N,
.ble_ll_sched_direct_adv_max_usecs = BLE_LL_SCHED_DIRECT_ADV_MAX_USECS_N,
.ble_ll_sched_adv_max_usecs = BLE_LL_SCHED_ADV_MAX_USECS_N,
.ble_scan_rsp_data_max_len = DEFAULT_BT_LE_SCAN_RSP_DATA_MAX_LEN_N,
.ble_ll_cfg_num_hci_cmd_pkts = BLE_LL_CFG_NUM_HCI_CMD_PKTS_N,
.ble_ll_ctrl_proc_timeout_ms = BLE_LL_CTRL_PROC_TIMEOUT_MS_N,
.nimble_max_connections = DEFAULT_BT_LE_MAX_CONNECTIONS,
.ble_whitelist_size = DEFAULT_BT_NIMBLE_WHITELIST_SIZE, // NOLINT
.ble_acl_buf_size = DEFAULT_BT_LE_ACL_BUF_SIZE,
.ble_acl_buf_count = DEFAULT_BT_LE_ACL_BUF_COUNT,
.ble_hci_evt_buf_size = DEFAULT_BT_LE_HCI_EVT_BUF_SIZE,
.ble_multi_adv_instances = DEFAULT_BT_LE_MAX_EXT_ADV_INSTANCES,
.ble_ext_adv_max_size = DEFAULT_BT_LE_EXT_ADV_MAX_SIZE,
.controller_task_stack_size = NIMBLE_LL_STACK_SIZE,
.controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO,
.controller_run_cpu = 0,
.enable_qa_test = RUN_QA_TEST,
.enable_bqb_test = RUN_BQB_TEST,
.enable_uart_hci = HCI_UART_EN,
.ble_hci_uart_port = DEFAULT_BT_LE_HCI_UART_PORT,
.ble_hci_uart_baud = DEFAULT_BT_LE_HCI_UART_BAUD,
.ble_hci_uart_data_bits = DEFAULT_BT_LE_HCI_UART_DATA_BITS,
.ble_hci_uart_stop_bits = DEFAULT_BT_LE_HCI_UART_STOP_BITS,
.ble_hci_uart_flow_ctrl = DEFAULT_BT_LE_HCI_UART_FLOW_CTRL,
.ble_hci_uart_uart_parity = DEFAULT_BT_LE_HCI_UART_PARITY,
.enable_tx_cca = DEFAULT_BT_LE_TX_CCA_ENABLED,
.cca_rssi_thresh = 256 - DEFAULT_BT_LE_CCA_RSSI_THRESH,
.sleep_en = NIMBLE_SLEEP_ENABLE,
.coex_phy_coded_tx_rx_time_limit = DEFAULT_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF,
.dis_scan_backoff = NIMBLE_DISABLE_SCAN_BACKOFF,
.ble_scan_classify_filter_enable = 1,
.main_xtal_freq = CONFIG_XTAL_FREQ,
.version_num = (uint8_t) efuse_hal_chip_revision(),
.cpu_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
.ignore_wl_for_direct_adv = 0,
.enable_pcl = DEFAULT_BT_LE_POWER_CONTROL_ENABLED,
.config_magic = CONFIG_MAGIC,
};
} // namespace esp32_ble
} // namespace esphome
#endif // USE_ESP32_VARIANT_ESP32C6

View File

@ -0,0 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
CODEOWNERS = ["@Szewcson"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
CONF_GDK101_ID = "gdk101_id"
gdk101_ns = cg.esphome_ns.namespace("gdk101")
GDK101Component = gdk101_ns.class_(
"GDK101Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GDK101Component),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x18))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@ -0,0 +1,29 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
CONF_VIBRATIONS,
DEVICE_CLASS_VIBRATION,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_VIBRATE,
)
from . import CONF_GDK101_ID, GDK101Component
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Required(CONF_VIBRATIONS): binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
icon=ICON_VIBRATE,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
var = await binary_sensor.new_binary_sensor(config[CONF_VIBRATIONS])
cg.add(hub.set_vibration_binary_sensor(var))

View File

@ -0,0 +1,189 @@
#include "gdk101.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace gdk101 {
static const char *const TAG = "gdk101";
static const uint8_t NUMBER_OF_READ_RETRIES = 5;
void GDK101Component::update() {
uint8_t data[2];
if (!this->read_dose_1m_(data)) {
this->status_set_warning("Failed to read dose 1m");
return;
}
if (!this->read_dose_10m_(data)) {
this->status_set_warning("Failed to read dose 10m");
return;
}
if (!this->read_status_(data)) {
this->status_set_warning("Failed to read status");
return;
}
if (!this->read_measurement_duration_(data)) {
this->status_set_warning("Failed to read measurement duration");
return;
}
this->status_clear_warning();
}
void GDK101Component::setup() {
uint8_t data[2];
ESP_LOGCONFIG(TAG, "Setting up GDK101...");
// first, reset the sensor
if (!this->reset_sensor_(data)) {
this->status_set_error("Reset failed!");
this->mark_failed();
return;
}
// sensor should acknowledge success of the reset procedure
if (data[0] != 1) {
this->status_set_error("Reset not acknowledged!");
this->mark_failed();
return;
}
delay(10);
// read firmware version
if (!this->read_fw_version_(data)) {
this->status_set_error("Failed to read firmware version");
this->mark_failed();
return;
}
}
void GDK101Component::dump_config() {
ESP_LOGCONFIG(TAG, "GDK101:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with GDK101 failed!");
}
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_);
LOG_SENSOR(" ", "Status", this->status_sensor_);
LOG_SENSOR(" ", "Measurement Duration", this->measurement_duration_sensor_);
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_);
#endif // USE_BINARY_SENSOR
}
float GDK101Component::get_setup_priority() const { return setup_priority::DATA; }
bool GDK101Component::read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len) {
uint8_t retry = NUMBER_OF_READ_RETRIES;
bool status = false;
while (!status && retry) {
status = this->read_bytes(a_register, data, len);
retry--;
}
return status;
}
bool GDK101Component::reset_sensor_(uint8_t *data) {
// It looks like reset is not so well designed in that sensor
// After sending reset command it looks that sensor start performing reset and is unresponsible during read
// after a while we can send another reset command and read "0x01" as confirmation
// Documentation not going in to such details unfortunately
if (!this->read_bytes_with_retry_(GDK101_REG_RESET, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
return true;
}
bool GDK101Component::read_dose_1m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_1m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_1MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_1m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_dose_10m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_10m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_10MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_10m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_status_(uint8_t *data) {
if (!this->read_bytes(GDK101_REG_READ_STATUS, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
#ifdef USE_SENSOR
if (this->status_sensor_ != nullptr) {
this->status_sensor_->publish_state(data[0]);
}
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
if (this->vibration_binary_sensor_ != nullptr) {
this->vibration_binary_sensor_->publish_state(data[1]);
}
#endif // USE_BINARY_SENSOR
return true;
}
bool GDK101Component::read_fw_version_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->fw_version_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float fw_version = data[0] + (data[1] / 10.0f);
this->fw_version_sensor_->publish_state(fw_version);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_measurement_duration_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->measurement_duration_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_MEASURING_TIME, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float meas_time = (data[0] * 60) + data[1];
this->measurement_duration_sensor_->publish_state(meas_time);
}
#endif // USE_SENSOR
return true;
}
} // namespace gdk101
} // namespace esphome

View File

@ -0,0 +1,52 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif // USE_BINARY_SENSOR
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace gdk101 {
static const uint8_t GDK101_REG_READ_FIRMWARE = 0xB4; // Firmware version
static const uint8_t GDK101_REG_RESET = 0xA0; // Reset register - reading its value triggers reset
static const uint8_t GDK101_REG_READ_STATUS = 0xB0; // Status register
static const uint8_t GDK101_REG_READ_MEASURING_TIME = 0xB1; // Mesuring time
static const uint8_t GDK101_REG_READ_10MIN_AVG = 0xB2; // Average radiation dose per 10 min
static const uint8_t GDK101_REG_READ_1MIN_AVG = 0xB3; // Average radiation dose per 1 min
class GDK101Component : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(rad_1m)
SUB_SENSOR(rad_10m)
SUB_SENSOR(status)
SUB_SENSOR(fw_version)
SUB_SENSOR(measurement_duration)
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(vibration)
#endif // USE_BINARY_SENSOR
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
bool read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len);
bool reset_sensor_(uint8_t *data);
bool read_dose_1m_(uint8_t *data);
bool read_dose_10m_(uint8_t *data);
bool read_status_(uint8_t *data);
bool read_fw_version_(uint8_t *data);
bool read_measurement_duration_(uint8_t *data);
};
} // namespace gdk101
} // namespace esphome

View File

@ -0,0 +1,83 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_MEASUREMENT_DURATION,
CONF_STATUS,
CONF_VERSION,
ICON_RADIOACTIVE,
ICON_TIMER,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_MICROSILVERTS_PER_HOUR,
UNIT_SECOND,
)
from . import CONF_GDK101_ID, GDK101Component
CONF_RADIATION_DOSE_PER_1M = "radiation_dose_per_1m"
CONF_RADIATION_DOSE_PER_10M = "radiation_dose_per_10m"
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Optional(CONF_RADIATION_DOSE_PER_1M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADIATION_DOSE_PER_10M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VERSION): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=1,
),
cv.Optional(CONF_STATUS): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=0,
),
cv.Optional(CONF_MEASUREMENT_DURATION): sensor.sensor_schema(
unit_of_measurement=UNIT_SECOND,
icon=ICON_TIMER,
accuracy_decimals=0,
state_class=STATE_CLASS_TOTAL_INCREASING,
device_class=DEVICE_CLASS_DURATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
if radiation_dose_per_1m := config.get(CONF_RADIATION_DOSE_PER_1M):
sens = await sensor.new_sensor(radiation_dose_per_1m)
cg.add(hub.set_rad_1m_sensor(sens))
if radiation_dose_per_10m := config.get(CONF_RADIATION_DOSE_PER_10M):
sens = await sensor.new_sensor(radiation_dose_per_10m)
cg.add(hub.set_rad_10m_sensor(sens))
if version_config := config.get(CONF_VERSION):
sens = await sensor.new_sensor(version_config)
cg.add(hub.set_fw_version_sensor(sens))
if status_config := config.get(CONF_STATUS):
sens = await sensor.new_sensor(status_config)
cg.add(hub.set_status_sensor(sens))
if measurement_duration_config := config.get(CONF_MEASUREMENT_DURATION):
sens = await sensor.new_sensor(measurement_duration_config)
cg.add(hub.set_measurement_duration_sensor(sens))

View File

@ -10,6 +10,11 @@ namespace i2s_audio {
static const char *const TAG = "audio"; static const char *const TAG = "audio";
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
media_player::MediaPlayerState play_state = media_player::MEDIA_PLAYER_STATE_PLAYING;
if (call.get_announcement().has_value()) {
play_state = call.get_announcement().value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
: media_player::MEDIA_PLAYER_STATE_PLAYING;
}
if (call.get_media_url().has_value()) { if (call.get_media_url().has_value()) {
this->current_url_ = call.get_media_url(); this->current_url_ = call.get_media_url();
if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) { if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) {
@ -17,7 +22,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
this->audio_->stopSong(); this->audio_->stopSong();
} }
this->audio_->connecttohost(this->current_url_.value().c_str()); this->audio_->connecttohost(this->current_url_.value().c_str());
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = play_state;
} else { } else {
this->start(); this->start();
} }
@ -35,7 +40,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
case media_player::MEDIA_PLAYER_COMMAND_PLAY: case media_player::MEDIA_PLAYER_COMMAND_PLAY:
if (!this->audio_->isRunning()) if (!this->audio_->isRunning())
this->audio_->pauseResume(); this->audio_->pauseResume();
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = play_state;
break; break;
case media_player::MEDIA_PLAYER_COMMAND_PAUSE: case media_player::MEDIA_PLAYER_COMMAND_PAUSE:
if (this->audio_->isRunning()) if (this->audio_->isRunning())
@ -126,7 +131,9 @@ void I2SAudioMediaPlayer::loop() {
void I2SAudioMediaPlayer::play_() { void I2SAudioMediaPlayer::play_() {
this->audio_->loop(); this->audio_->loop();
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) { if ((this->state == media_player::MEDIA_PLAYER_STATE_PLAYING ||
this->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING) &&
!this->audio_->isRunning()) {
this->stop(); this->stop();
} }
} }
@ -164,6 +171,10 @@ void I2SAudioMediaPlayer::start_() {
if (this->current_url_.has_value()) { if (this->current_url_.has_value()) {
this->audio_->connecttohost(this->current_url_.value().c_str()); this->audio_->connecttohost(this->current_url_.value().c_str());
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
if (this->is_announcement_.has_value()) {
this->state = this->is_announcement_.value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
: media_player::MEDIA_PLAYER_STATE_PLAYING;
}
this->publish_state(); this->publish_state();
} }
} }

View File

@ -78,6 +78,7 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer,
HighFrequencyLoopRequester high_freq_; HighFrequencyLoopRequester high_freq_;
optional<std::string> current_url_{}; optional<std::string> current_url_{};
optional<bool> is_announcement_{};
}; };
} // namespace i2s_audio } // namespace i2s_audio

View File

@ -8,6 +8,9 @@ namespace ltr390 {
static const char *const TAG = "ltr390"; static const char *const TAG = "ltr390";
static const uint8_t LTR390_WAKEUP_TIME = 10;
static const uint8_t LTR390_SETTLE_TIME = 5;
static const uint8_t LTR390_MAIN_CTRL = 0x00; static const uint8_t LTR390_MAIN_CTRL = 0x00;
static const uint8_t LTR390_MEAS_RATE = 0x04; static const uint8_t LTR390_MEAS_RATE = 0x04;
static const uint8_t LTR390_GAIN = 0x05; static const uint8_t LTR390_GAIN = 0x05;
@ -101,21 +104,27 @@ void LTR390Component::read_mode_(int mode_index) {
std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get();
ctrl[LTR390_CTRL_MODE] = mode; ctrl[LTR390_CTRL_MODE] = mode;
ctrl[LTR390_CTRL_EN] = true;
this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong();
// After the sensor integration time do the following // After the sensor integration time do the following
this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100, [this, mode_index]() { this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100 + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME,
// Read from the sensor [this, mode_index]() {
std::get<1>(this->mode_funcs_[mode_index])(); // Read from the sensor
std::get<1>(this->mode_funcs_[mode_index])();
// If there are more modes to read then begin the next // If there are more modes to read then begin the next
// otherwise stop // otherwise stop
if (mode_index + 1 < (int) this->mode_funcs_.size()) { if (mode_index + 1 < (int) this->mode_funcs_.size()) {
this->read_mode_(mode_index + 1); this->read_mode_(mode_index + 1);
} else { } else {
this->reading_ = false; // put sensor in standby
} std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get();
}); ctrl[LTR390_CTRL_EN] = false;
this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong();
this->reading_ = false;
}
});
} }
void LTR390Component::setup() { void LTR390Component::setup() {

View File

@ -51,12 +51,16 @@ VolumeSetAction = media_player_ns.class_(
CONF_ON_PLAY = "on_play" CONF_ON_PLAY = "on_play"
CONF_ON_PAUSE = "on_pause" CONF_ON_PAUSE = "on_pause"
CONF_ON_ANNOUNCEMENT = "on_announcement"
CONF_MEDIA_URL = "media_url" CONF_MEDIA_URL = "media_url"
StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template()) StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template())
IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template()) IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template())
PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template()) PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template())
PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template()) PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template())
AnnoucementTrigger = media_player_ns.class_(
"AnnouncementTrigger", automation.Trigger.template()
)
IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition) IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition) IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
@ -75,6 +79,9 @@ async def setup_media_player_core_(var, config):
for conf in config.get(CONF_ON_PAUSE, []): for conf in config.get(CONF_ON_PAUSE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_ANNOUNCEMENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_media_player(var, config): async def register_media_player(var, config):
@ -106,6 +113,11 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
} }
), ),
cv.Optional(CONF_ON_ANNOUNCEMENT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(AnnoucementTrigger),
}
),
} }
) )

View File

@ -52,6 +52,7 @@ class StateTrigger : public Trigger<> {
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(AnnouncementTrigger, ANNOUNCING)
template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> { template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public: public:

View File

@ -15,6 +15,8 @@ const char *media_player_state_to_string(MediaPlayerState state) {
return "PLAYING"; return "PLAYING";
case MEDIA_PLAYER_STATE_PAUSED: case MEDIA_PLAYER_STATE_PAUSED:
return "PAUSED"; return "PAUSED";
case MEDIA_PLAYER_STATE_ANNOUNCING:
return "ANNOUNCING";
case MEDIA_PLAYER_STATE_NONE: case MEDIA_PLAYER_STATE_NONE:
default: default:
return "UNKNOWN"; return "UNKNOWN";
@ -68,6 +70,9 @@ void MediaPlayerCall::perform() {
if (this->volume_.has_value()) { if (this->volume_.has_value()) {
ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value()); ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value());
} }
if (this->announcement_.has_value()) {
ESP_LOGD(TAG, " Announcement: %s", this->announcement_.value() ? "yes" : "no");
}
this->parent_->control(*this); this->parent_->control(*this);
} }
@ -108,6 +113,11 @@ MediaPlayerCall &MediaPlayerCall::set_volume(float volume) {
return *this; return *this;
} }
MediaPlayerCall &MediaPlayerCall::set_announcement(bool announce) {
this->announcement_ = announce;
return *this;
}
void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) { void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback)); this->state_callback_.add(std::move(callback));
} }

View File

@ -10,7 +10,8 @@ enum MediaPlayerState : uint8_t {
MEDIA_PLAYER_STATE_NONE = 0, MEDIA_PLAYER_STATE_NONE = 0,
MEDIA_PLAYER_STATE_IDLE = 1, MEDIA_PLAYER_STATE_IDLE = 1,
MEDIA_PLAYER_STATE_PLAYING = 2, MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3 MEDIA_PLAYER_STATE_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4
}; };
const char *media_player_state_to_string(MediaPlayerState state); const char *media_player_state_to_string(MediaPlayerState state);
@ -51,12 +52,14 @@ class MediaPlayerCall {
MediaPlayerCall &set_media_url(const std::string &url); MediaPlayerCall &set_media_url(const std::string &url);
MediaPlayerCall &set_volume(float volume); MediaPlayerCall &set_volume(float volume);
MediaPlayerCall &set_announcement(bool announce);
void perform(); void perform();
const optional<MediaPlayerCommand> &get_command() const { return command_; } const optional<MediaPlayerCommand> &get_command() const { return command_; }
const optional<std::string> &get_media_url() const { return media_url_; } const optional<std::string> &get_media_url() const { return media_url_; }
const optional<float> &get_volume() const { return volume_; } const optional<float> &get_volume() const { return volume_; }
const optional<bool> &get_announcement() const { return announcement_; }
protected: protected:
void validate_(); void validate_();
@ -64,6 +67,7 @@ class MediaPlayerCall {
optional<MediaPlayerCommand> command_; optional<MediaPlayerCommand> command_;
optional<std::string> media_url_; optional<std::string> media_url_;
optional<float> volume_; optional<float> volume_;
optional<bool> announcement_;
}; };
class MediaPlayer : public EntityBase { class MediaPlayer : public EntityBase {

View File

@ -96,6 +96,9 @@ void TimeBasedCover::control(const CoverCall &call) {
} }
} else { } else {
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
if (this->manual_control_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
this->position = pos == COVER_CLOSED ? COVER_OPEN : COVER_CLOSED;
}
this->target_position_ = pos; this->target_position_ = pos;
this->start_direction_(op); this->start_direction_(op);
} }

View File

@ -152,7 +152,7 @@ void VoiceAssistant::loop() {
} else } else
#endif #endif
{ {
this->set_state_(State::START_PIPELINE, State::START_MICROPHONE); this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
} }
} else { } else {
this->high_freq_.stop(); this->high_freq_.stop();
@ -318,7 +318,7 @@ void VoiceAssistant::loop() {
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) { if (this->media_player_ != nullptr) {
playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING); playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING);
} }
#endif #endif
if (playing) { if (playing) {
@ -514,7 +514,7 @@ void VoiceAssistant::request_start(bool continuous, bool silence_detection) {
} else } else
#endif #endif
{ {
this->set_state_(State::START_PIPELINE, State::START_MICROPHONE); this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
} }
} }
} }
@ -640,7 +640,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
this->defer([this, url]() { this->defer([this, url]() {
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) { if (this->media_player_ != nullptr) {
this->media_player_->make_call().set_media_url(url).perform(); this->media_player_->make_call().set_media_url(url).set_announcement(true).perform();
} }
#endif #endif
this->tts_end_trigger_->trigger(url); this->tts_end_trigger_->trigger(url);

View File

@ -94,10 +94,10 @@ class VoiceAssistant : public Component {
uint32_t get_feature_flags() const { uint32_t get_feature_flags() const {
uint32_t flags = 0; uint32_t flags = 0;
flags |= VoiceAssistantFeature::FEATURE_VOICE_ASSISTANT; flags |= VoiceAssistantFeature::FEATURE_VOICE_ASSISTANT;
flags |= VoiceAssistantFeature::FEATURE_API_AUDIO;
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ != nullptr) { if (this->speaker_ != nullptr) {
flags |= VoiceAssistantFeature::FEATURE_SPEAKER; flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
flags |= VoiceAssistantFeature::FEATURE_API_AUDIO;
} }
#endif #endif
return flags; return flags;

View File

@ -40,19 +40,19 @@ WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller)
def default_url(config): def default_url(config):
config = config.copy() config = config.copy()
if config[CONF_VERSION] == 1: if config[CONF_VERSION] == 1:
if not (CONF_CSS_URL in config): if CONF_CSS_URL not in config:
config[CONF_CSS_URL] = "https://esphome.io/_static/webserver-v1.min.css" config[CONF_CSS_URL] = "https://esphome.io/_static/webserver-v1.min.css"
if not (CONF_JS_URL in config): if CONF_JS_URL not in config:
config[CONF_JS_URL] = "https://esphome.io/_static/webserver-v1.min.js" config[CONF_JS_URL] = "https://esphome.io/_static/webserver-v1.min.js"
if config[CONF_VERSION] == 2: if config[CONF_VERSION] == 2:
if not (CONF_CSS_URL in config): if CONF_CSS_URL not in config:
config[CONF_CSS_URL] = "" config[CONF_CSS_URL] = ""
if not (CONF_JS_URL in config): if CONF_JS_URL not in config:
config[CONF_JS_URL] = "https://oi.esphome.io/v2/www.js" config[CONF_JS_URL] = "https://oi.esphome.io/v2/www.js"
if config[CONF_VERSION] == 3: if config[CONF_VERSION] == 3:
if not (CONF_CSS_URL in config): if CONF_CSS_URL not in config:
config[CONF_CSS_URL] = "" config[CONF_CSS_URL] = ""
if not (CONF_JS_URL in config): if CONF_JS_URL not in config:
config[CONF_JS_URL] = "https://oi.esphome.io/v3/www.js" config[CONF_JS_URL] = "https://oi.esphome.io/v3/www.js"
return config return config

View File

@ -34,7 +34,7 @@ from esphome.voluptuous_schema import ExtraKeysInvalid
from esphome.log import color, Fore from esphome.log import color, Fore
import esphome.final_validate as fv import esphome.final_validate as fv
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.types import ConfigType, ConfigPathType, ConfigFragmentType from esphome.types import ConfigType, ConfigFragmentType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -213,7 +213,7 @@ class Config(OrderedDict, fv.FinalValidateConfig):
return doc_range return doc_range
def get_nested_item( def get_nested_item(
self, path: ConfigPathType, raise_error: bool = False self, path: ConfigPath, raise_error: bool = False
) -> ConfigFragmentType: ) -> ConfigFragmentType:
data = self data = self
for item_index in path: for item_index in path:
@ -244,7 +244,7 @@ class Config(OrderedDict, fv.FinalValidateConfig):
return path return path
raise KeyError(f"ID {id} not found in configuration") raise KeyError(f"ID {id} not found in configuration")
def get_config_for_path(self, path: ConfigPathType) -> ConfigFragmentType: def get_config_for_path(self, path: ConfigPath) -> ConfigFragmentType:
return self.get_nested_item(path, raise_error=True) return self.get_nested_item(path, raise_error=True)
@property @property
@ -885,6 +885,9 @@ def _get_parent_name(path, config):
# Sub-item # Sub-item
break break
return domain return domain
# When processing a list, skip back over the index
while len(path) > 1 and isinstance(path[-1], int):
path = path[:-1]
return path[-1] return path[-1]
@ -1106,7 +1109,14 @@ def read_config(command_line_substitutions):
if errline: if errline:
errstr += f" {errline}" errstr += f" {errline}"
safe_print(errstr) safe_print(errstr)
safe_print(indent(dump_dict(res, path)[0])) split_dump = dump_dict(res, path)[0].splitlines()
# find the last error message
i = len(split_dump) - 1
while i > 10 and "\033[" not in split_dump[i]:
i = i - 1
# discard lines more than 4 beyond the last error
i = min(i + 4, len(split_dump))
safe_print(indent("\n".join(split_dump[:i])))
for err in res.errors: for err in res.errors:
safe_print(color(Fore.BOLD_RED, err.msg)) safe_print(color(Fore.BOLD_RED, err.msg))

View File

@ -884,6 +884,7 @@ CONF_VALUE_FONT = "value_font"
CONF_VARIABLES = "variables" CONF_VARIABLES = "variables"
CONF_VARIANT = "variant" CONF_VARIANT = "variant"
CONF_VERSION = "version" CONF_VERSION = "version"
CONF_VIBRATIONS = "vibrations"
CONF_VISIBLE = "visible" CONF_VISIBLE = "visible"
CONF_VISUAL = "visual" CONF_VISUAL = "visual"
CONF_VOLTAGE = "voltage" CONF_VOLTAGE = "voltage"
@ -985,6 +986,7 @@ ICON_SIGNAL_DISTANCE_VARIANT = "mdi:signal"
ICON_THERMOMETER = "mdi:thermometer" ICON_THERMOMETER = "mdi:thermometer"
ICON_TIMELAPSE = "mdi:timelapse" ICON_TIMELAPSE = "mdi:timelapse"
ICON_TIMER = "mdi:timer-outline" ICON_TIMER = "mdi:timer-outline"
ICON_VIBRATE = "mdi:vibrate"
ICON_WATER = "mdi:water" ICON_WATER = "mdi:water"
ICON_WATER_PERCENT = "mdi:water-percent" ICON_WATER_PERCENT = "mdi:water-percent"
ICON_WEATHER_SUNSET = "mdi:weather-sunset" ICON_WEATHER_SUNSET = "mdi:weather-sunset"
@ -1026,6 +1028,7 @@ UNIT_METER_PER_SECOND_SQUARED = "m/s²"
UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³" UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³"
UNIT_MICROMETER = "µm" UNIT_MICROMETER = "µm"
UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm" UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
UNIT_MICROSILVERTS_PER_HOUR = "µSv/h"
UNIT_MICROTESLA = "µT" UNIT_MICROTESLA = "µT"
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³" UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
UNIT_MILLISECOND = "ms" UNIT_MILLISECOND = "ms"

View File

@ -394,7 +394,7 @@ async def to_code(config):
if project_conf := config.get(CONF_PROJECT): if project_conf := config.get(CONF_PROJECT):
cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME])
cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION]) cg.add_define("ESPHOME_PROJECT_VERSION", project_conf[CONF_VERSION])
cg.add_define("ESPHOME_PROJECT_VERSION_30", project_conf[CONF_VERSION][:30]) cg.add_define("ESPHOME_PROJECT_VERSION_30", project_conf[CONF_VERSION][:29])
for conf in project_conf.get(CONF_ON_UPDATE, []): for conf in project_conf.get(CONF_ON_UPDATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
await cg.register_component(trigger, conf) await cg.register_component(trigger, conf)

View File

@ -2,7 +2,7 @@ import abc
import inspect import inspect
import math import math
import re import re
from collections.abc import Generator, Sequence from collections.abc import Sequence
from typing import Any, Callable, Optional, Union from typing import Any, Callable, Optional, Union
from esphome.core import ( from esphome.core import (
@ -477,6 +477,7 @@ def variable(
:param rhs: The expression to place on the right hand side of the assignment. :param rhs: The expression to place on the right hand side of the assignment.
:param type_: Manually define a type for the variable, only use this when it's not possible :param type_: Manually define a type for the variable, only use this when it's not possible
to do so during config validation phase (for example because of template arguments). to do so during config validation phase (for example because of template arguments).
:param register: If true register the variable with the core
:return: The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
@ -492,9 +493,7 @@ def variable(
return obj return obj
def with_local_variable( def with_local_variable(id_: ID, rhs: SafeExpType, callback: Callable, *args) -> None:
id_: ID, rhs: SafeExpType, callback: Callable[["MockObj"], None], *args
) -> None:
"""Declare a new variable, not pointer type, in the code generation, within a scoped block """Declare a new variable, not pointer type, in the code generation, within a scoped block
The variable is only usable within the callback The variable is only usable within the callback
The callback cannot be async. The callback cannot be async.
@ -599,6 +598,7 @@ def add_library(name: str, version: Optional[str], repository: Optional[str] = N
:param name: The name of the library (for example 'AsyncTCP') :param name: The name of the library (for example 'AsyncTCP')
:param version: The version of the library, may be None. :param version: The version of the library, may be None.
:param repository: The repository for the library
""" """
CORE.add_library(Library(name, version, repository)) CORE.add_library(Library(name, version, repository))
@ -654,7 +654,7 @@ async def process_lambda(
parameters: list[tuple[SafeExpType, str]], parameters: list[tuple[SafeExpType, str]],
capture: str = "=", capture: str = "=",
return_type: SafeExpType = None, return_type: SafeExpType = None,
) -> Generator[LambdaExpression, None, None]: ) -> Union[LambdaExpression, None]:
"""Process the given lambda value into a LambdaExpression. """Process the given lambda value into a LambdaExpression.
This is a coroutine because lambdas can depend on other IDs, This is a coroutine because lambdas can depend on other IDs,
@ -673,7 +673,7 @@ async def process_lambda(
) )
if value is None: if value is None:
return return None
parts = value.parts[:] parts = value.parts[:]
for i, id in enumerate(value.requires_ids): for i, id in enumerate(value.requires_ids):
full_id, var = await get_variable_with_full_id(id) full_id, var = await get_variable_with_full_id(id)
@ -712,7 +712,7 @@ async def templatable(
value: Any, value: Any,
args: list[tuple[SafeExpType, str]], args: list[tuple[SafeExpType, str]],
output_type: Optional[SafeExpType], output_type: Optional[SafeExpType],
to_exp: Any = None, to_exp: Union[Callable, dict] = None,
): ):
"""Generate code for a templatable config option. """Generate code for a templatable config option.

View File

@ -9,7 +9,7 @@ tornado==6.4
tzlocal==5.2 # from time tzlocal==5.2 # from time
tzdata>=2021.1 # from time tzdata>=2021.1 # from time
pyserial==3.5 pyserial==3.5
platformio==6.1.13 # When updating platformio, also update Dockerfile platformio==6.1.15 # When updating platformio, also update Dockerfile
esptool==4.7.0 esptool==4.7.0
click==8.1.7 click==8.1.7
esphome-dashboard==20240412.0 esphome-dashboard==20240412.0

View File

@ -5,7 +5,7 @@ pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating
pre-commit pre-commit
# Unit tests # Unit tests
pytest==8.1.1 pytest==8.2.0
pytest-cov==4.1.0 pytest-cov==4.1.0
pytest-mock==3.14.0 pytest-mock==3.14.0
pytest-asyncio==0.23.6 pytest-asyncio==0.23.6

View File

@ -0,0 +1,28 @@
i2c:
id: i2c_bus
sda: ${i2c_sda}
scl: ${i2c_scl}
gdk101:
id: my_gdk101
i2c_id: i2c_bus
sensor:
- platform: gdk101
gdk101_id: my_gdk101
radiation_dose_per_1m:
name: Radiation Dose @ 1 min
radiation_dose_per_10m:
name: Radiation Dose @ 10 min
status:
name: Status
version:
name: FW Version
measurement_duration:
name: Measuring Time
binary_sensor:
- platform: gdk101
gdk101_id: my_gdk101
vibrations:
name: Vibrations

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO16
i2c_sda: GPIO17
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO16
i2c_sda: GPIO17
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO5
i2c_sda: GPIO4
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO5
i2c_sda: GPIO4
<<: !include common.yaml