mirror of
https://github.com/esphome/esphome.git
synced 2026-01-19 17:46:23 -07:00
Compare commits
2 Commits
mqtt_reduc
...
debug_clea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd33087b3f | ||
|
|
6812654435 |
@@ -1,96 +0,0 @@
|
||||
---
|
||||
name: pr-workflow
|
||||
description: Create pull requests for esphome. Use when creating PRs, submitting changes, or preparing contributions.
|
||||
allowed-tools: Read, Bash, Glob, Grep
|
||||
---
|
||||
|
||||
# ESPHome PR Workflow
|
||||
|
||||
When creating a pull request for esphome, follow these steps:
|
||||
|
||||
## 1. Create Branch from Upstream
|
||||
|
||||
Always base your branch on **upstream** (not origin/fork) to ensure you have the latest code:
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git checkout -b <branch-name> upstream/dev
|
||||
```
|
||||
|
||||
## 2. Read the PR Template
|
||||
|
||||
Before creating a PR, read `.github/PULL_REQUEST_TEMPLATE.md` to understand required fields.
|
||||
|
||||
## 3. Create the PR
|
||||
|
||||
Use `gh pr create` with the **full template** filled in. Never skip or abbreviate sections.
|
||||
|
||||
Required fields:
|
||||
- **What does this implement/fix?**: Brief description of changes
|
||||
- **Types of changes**: Check ONE appropriate box (Bugfix, New feature, Breaking change, etc.)
|
||||
- **Related issue**: Use `fixes <link>` syntax if applicable
|
||||
- **Pull request in esphome-docs**: Link if docs are needed
|
||||
- **Test Environment**: Check platforms you tested on
|
||||
- **Example config.yaml**: Include working example YAML
|
||||
- **Checklist**: Verify code is tested and tests added
|
||||
|
||||
## 4. Example PR Body
|
||||
|
||||
```markdown
|
||||
# What does this implement/fix?
|
||||
|
||||
<describe your changes here>
|
||||
|
||||
## Types of changes
|
||||
|
||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||
- [x] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Developer breaking change (an API change that could break external components)
|
||||
- [ ] Code quality improvements to existing code or addition of tests
|
||||
- [ ] Other
|
||||
|
||||
**Related issue or feature (if applicable):**
|
||||
|
||||
- fixes https://github.com/esphome/esphome/issues/XXX
|
||||
|
||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):**
|
||||
|
||||
- esphome/esphome-docs#XXX
|
||||
|
||||
## Test Environment
|
||||
|
||||
- [x] ESP32
|
||||
- [x] ESP32 IDF
|
||||
- [ ] ESP8266
|
||||
- [ ] RP2040
|
||||
- [ ] BK72xx
|
||||
- [ ] RTL87xx
|
||||
- [ ] LN882x
|
||||
- [ ] nRF52840
|
||||
|
||||
## Example entry for `config.yaml`:
|
||||
|
||||
```yaml
|
||||
# Example config.yaml
|
||||
component_name:
|
||||
id: my_component
|
||||
option: value
|
||||
```
|
||||
|
||||
## Checklist:
|
||||
- [x] The code change is tested and works locally.
|
||||
- [x] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
If user exposed functionality or configuration variables are added/changed:
|
||||
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
||||
```
|
||||
|
||||
## 5. Push and Create PR
|
||||
|
||||
```bash
|
||||
git push -u origin <branch-name>
|
||||
gh pr create --repo esphome/esphome --base dev --title "[component] Brief description"
|
||||
```
|
||||
|
||||
Title should be prefixed with the component name in brackets, e.g. `[safe_mode] Add feature`.
|
||||
2
.github/actions/restore-python/action.yml
vendored
2
.github/actions/restore-python/action.yml
vendored
@@ -22,7 +22,7 @@ runs:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
|
||||
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Save Python virtual environment cache
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||
@@ -193,7 +193,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
- name: Restore components graph cache
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: .temp/components_graph.json
|
||||
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT
|
||||
- name: Save components graph cache
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: .temp/components_graph.json
|
||||
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
||||
@@ -245,7 +245,7 @@ jobs:
|
||||
python-version: "3.13"
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||
@@ -334,14 +334,14 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref != 'refs/heads/dev'
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||
@@ -413,14 +413,14 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref != 'refs/heads/dev'
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||
@@ -502,14 +502,14 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref != 'refs/heads/dev'
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||
@@ -735,7 +735,7 @@ jobs:
|
||||
- name: Restore cached memory analysis
|
||||
id: cache-memory-analysis
|
||||
if: steps.check-script.outputs.skip != 'true'
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: memory-analysis-target.json
|
||||
key: ${{ steps.cache-key.outputs.cache-key }}
|
||||
@@ -759,7 +759,7 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
||||
@@ -800,7 +800,7 @@ jobs:
|
||||
|
||||
- name: Save memory analysis to cache
|
||||
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success'
|
||||
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: memory-analysis-target.json
|
||||
key: ${{ steps.cache-key.outputs.cache-key }}
|
||||
@@ -847,7 +847,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
- name: Cache platformio
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.14.13
|
||||
rev: v0.14.11
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -222,13 +222,8 @@ def choose_upload_log_host(
|
||||
else:
|
||||
resolved.append(device)
|
||||
if not resolved:
|
||||
if CORE.dashboard:
|
||||
hint = "If you know the IP, set 'use_address' in your network config."
|
||||
else:
|
||||
hint = "If you know the IP, try --device <IP>"
|
||||
raise EsphomeError(
|
||||
f"All specified devices {defaults} could not be resolved. "
|
||||
f"Is the device connected to the network? {hint}"
|
||||
f"All specified devices {defaults} could not be resolved. Is the device connected to the network?"
|
||||
)
|
||||
return resolved
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ from esphome.cpp_types import ( # noqa: F401
|
||||
JsonObjectConst,
|
||||
Parented,
|
||||
PollingComponent,
|
||||
StringRef,
|
||||
arduino_json_ns,
|
||||
bool_,
|
||||
const_char_ptr,
|
||||
|
||||
@@ -1712,16 +1712,17 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
|
||||
}
|
||||
|
||||
// Create null-terminated state for callback (parse_number needs null-termination)
|
||||
// HA state max length is 255 characters, but attributes can be much longer
|
||||
// Use stack buffer for common case (states), heap fallback for large attributes
|
||||
size_t state_len = msg.state.size();
|
||||
SmallBufferWithHeapFallback<256> state_buf_alloc(state_len + 1);
|
||||
char *state_buf = reinterpret_cast<char *>(state_buf_alloc.get());
|
||||
if (state_len > 0) {
|
||||
memcpy(state_buf, msg.state.c_str(), state_len);
|
||||
// HA state max length is 255, so 256 byte buffer covers all cases
|
||||
char state_buf[256];
|
||||
size_t copy_len = msg.state.size();
|
||||
if (copy_len >= sizeof(state_buf)) {
|
||||
copy_len = sizeof(state_buf) - 1; // Truncate to leave space for null terminator
|
||||
}
|
||||
state_buf[state_len] = '\0';
|
||||
it.callback(StringRef(state_buf, state_len));
|
||||
if (copy_len > 0) {
|
||||
memcpy(state_buf, msg.state.c_str(), copy_len);
|
||||
}
|
||||
state_buf[copy_len] = '\0';
|
||||
it.callback(StringRef(state_buf, copy_len));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -48,14 +48,14 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
|
||||
}
|
||||
uint32_t field_length = res->as_uint32();
|
||||
ptr += consumed;
|
||||
if (field_length > static_cast<size_t>(end - ptr)) {
|
||||
if (ptr + field_length > end) {
|
||||
return count; // Out of bounds
|
||||
}
|
||||
ptr += field_length;
|
||||
break;
|
||||
}
|
||||
case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes
|
||||
if (end - ptr < 4) {
|
||||
if (ptr + 4 > end) {
|
||||
return count;
|
||||
}
|
||||
ptr += 4;
|
||||
@@ -110,7 +110,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
}
|
||||
uint32_t field_length = res->as_uint32();
|
||||
ptr += consumed;
|
||||
if (field_length > static_cast<size_t>(end - ptr)) {
|
||||
if (ptr + field_length > end) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
break;
|
||||
}
|
||||
case WIRE_TYPE_FIXED32: { // 32-bit
|
||||
if (end - ptr < 4) {
|
||||
if (ptr + 4 > end) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,14 +158,12 @@ void ATM90E32Component::setup() {
|
||||
|
||||
if (this->enable_offset_calibration_) {
|
||||
// Initialize flash storage for offset calibrations
|
||||
uint32_t o_hash = fnv1_hash("_offset_calibration_");
|
||||
o_hash = fnv1_hash_extend(o_hash, this->cs_summary_);
|
||||
uint32_t o_hash = fnv1_hash(std::string("_offset_calibration_") + this->cs_summary_);
|
||||
this->offset_pref_ = global_preferences->make_preference<OffsetCalibration[3]>(o_hash, true);
|
||||
this->restore_offset_calibrations_();
|
||||
|
||||
// Initialize flash storage for power offset calibrations
|
||||
uint32_t po_hash = fnv1_hash("_power_offset_calibration_");
|
||||
po_hash = fnv1_hash_extend(po_hash, this->cs_summary_);
|
||||
uint32_t po_hash = fnv1_hash(std::string("_power_offset_calibration_") + this->cs_summary_);
|
||||
this->power_offset_pref_ = global_preferences->make_preference<PowerOffsetCalibration[3]>(po_hash, true);
|
||||
this->restore_power_offset_calibrations_();
|
||||
} else {
|
||||
@@ -185,8 +183,7 @@ void ATM90E32Component::setup() {
|
||||
|
||||
if (this->enable_gain_calibration_) {
|
||||
// Initialize flash storage for gain calibration
|
||||
uint32_t g_hash = fnv1_hash("_gain_calibration_");
|
||||
g_hash = fnv1_hash_extend(g_hash, this->cs_summary_);
|
||||
uint32_t g_hash = fnv1_hash(std::string("_gain_calibration_") + this->cs_summary_);
|
||||
this->gain_calibration_pref_ = global_preferences->make_preference<GainCalibration[3]>(g_hash, true);
|
||||
this->restore_gain_calibrations_();
|
||||
|
||||
|
||||
@@ -185,16 +185,18 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (str_endswith_ignore_case(url, ".wav")) {
|
||||
std::string url_string = str_lower_case(url);
|
||||
|
||||
if (str_endswith(url_string, ".wav")) {
|
||||
file_type = AudioFileType::WAV;
|
||||
}
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
else if (str_endswith_ignore_case(url, ".mp3")) {
|
||||
else if (str_endswith(url_string, ".mp3")) {
|
||||
file_type = AudioFileType::MP3;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||
else if (str_endswith_ignore_case(url, ".flac")) {
|
||||
else if (str_endswith(url_string, ".flac")) {
|
||||
file_type = AudioFileType::FLAC;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -81,8 +81,8 @@ void CCS811Component::setup() {
|
||||
bootloader_version, application_version);
|
||||
if (this->version_ != nullptr) {
|
||||
char version[20]; // "15.15.15 (0xffff)" is 17 chars, plus NUL, plus wiggle room
|
||||
buf_append_printf(version, sizeof(version), 0, "%d.%d.%d (0x%02x)", (application_version >> 12 & 15),
|
||||
(application_version >> 8 & 15), (application_version >> 4 & 15), application_version);
|
||||
sprintf(version, "%d.%d.%d (0x%02x)", (application_version >> 12 & 15), (application_version >> 8 & 15),
|
||||
(application_version >> 4 & 15), application_version);
|
||||
ESP_LOGD(TAG, "publishing version state: %s", version);
|
||||
this->version_->publish_state(version);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pi
|
||||
|
||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
|
||||
size_t CH422GGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "EXIO%u via CH422G", this->pin_);
|
||||
return snprintf(buffer, len, "EXIO%u via CH422G", this->pin_);
|
||||
}
|
||||
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
|
||||
flags_ = flags;
|
||||
|
||||
@@ -207,24 +207,20 @@ void CSE7766Component::parse_data_() {
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
{
|
||||
// Buffer: 7 + 15 + 33 + 15 + 25 = 95 chars max + null, rounded to 128 for safety margin.
|
||||
// Float sizes with %.4f can be up to 11 chars for large values (e.g., 999999.9999).
|
||||
char buf[128];
|
||||
size_t pos = buf_append_printf(buf, sizeof(buf), 0, "Parsed:");
|
||||
std::string buf = "Parsed:";
|
||||
if (have_voltage) {
|
||||
pos = buf_append_printf(buf, sizeof(buf), pos, " V=%.4fV", voltage);
|
||||
buf += str_sprintf(" V=%fV", voltage);
|
||||
}
|
||||
if (have_current) {
|
||||
pos = buf_append_printf(buf, sizeof(buf), pos, " I=%.4fmA (~%.4fmA)", current * 1000.0f,
|
||||
calculated_current * 1000.0f);
|
||||
buf += str_sprintf(" I=%fmA (~%fmA)", current * 1000.0f, calculated_current * 1000.0f);
|
||||
}
|
||||
if (have_power) {
|
||||
pos = buf_append_printf(buf, sizeof(buf), pos, " P=%.4fW", power);
|
||||
buf += str_sprintf(" P=%fW", power);
|
||||
}
|
||||
if (energy != 0.0f) {
|
||||
buf_append_printf(buf, sizeof(buf), pos, " E=%.4fkWh (%u)", energy, cf_pulses);
|
||||
buf += str_sprintf(" E=%fkWh (%u)", energy, cf_pulses);
|
||||
}
|
||||
ESP_LOGVV(TAG, "%s", buf);
|
||||
ESP_LOGVV(TAG, "%s", buf.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -258,9 +258,8 @@ bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
}
|
||||
|
||||
char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
|
||||
pos = buf_append_printf(buf, sizeof(buf), pos, "%02x ", frame[i]);
|
||||
sprintf(buf, "%s%02x ", buf, frame[i]);
|
||||
}
|
||||
ESP_LOGD(TAG, "FRAME %s", buf);
|
||||
|
||||
@@ -350,9 +349,8 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||
valid_daikin_frame = true;
|
||||
size_t bytes_count = data.size() / 2 / 8;
|
||||
size_t buf_size = bytes_count * 3 + 1;
|
||||
std::unique_ptr<char[]> buf(new char[buf_size]()); // value-initialize (zero-fill)
|
||||
size_t buf_pos = 0;
|
||||
std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
|
||||
buf[0] = '\0';
|
||||
for (size_t i = 0; i < bytes_count; i++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
@@ -363,19 +361,19 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf_pos = buf_append_printf(buf.get(), buf_size, buf_pos, "%02x ", byte);
|
||||
sprintf(buf.get(), "%s%02x ", buf.get(), byte);
|
||||
}
|
||||
ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
|
||||
}
|
||||
if (!valid_daikin_frame) {
|
||||
char sbuf[16 * 10 + 1] = {0};
|
||||
size_t sbuf_pos = 0;
|
||||
char sbuf[16 * 10 + 1];
|
||||
sbuf[0] = '\0';
|
||||
for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
|
||||
if ((j - 2) % 16 == 0) {
|
||||
if (j > 0) {
|
||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
|
||||
}
|
||||
sbuf_pos = 0;
|
||||
sbuf[0] = '\0';
|
||||
}
|
||||
char type_ch = ' ';
|
||||
// debug_tolerance = 25%
|
||||
@@ -403,10 +401,9 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
type_ch = '0';
|
||||
|
||||
if (abs(data[j]) > 100000) {
|
||||
sbuf_pos = buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", data[j] > 0 ? 99999 : -99999, type_ch);
|
||||
sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
|
||||
} else {
|
||||
sbuf_pos =
|
||||
buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", (int) (round(data[j] / 10.) * 10), type_ch);
|
||||
sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
|
||||
}
|
||||
if (j + 1 == static_cast<size_t>(data.size())) {
|
||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
|
||||
|
||||
@@ -30,7 +30,7 @@ void DebugComponent::dump_config() {
|
||||
|
||||
char device_info_buffer[DEVICE_INFO_BUFFER_SIZE];
|
||||
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
|
||||
size_t pos = buf_append(device_info_buffer, DEVICE_INFO_BUFFER_SIZE, 0, "%s", ESPHOME_VERSION);
|
||||
size_t pos = buf_append_printf(device_info_buffer, DEVICE_INFO_BUFFER_SIZE, 0, "%s", ESPHOME_VERSION);
|
||||
|
||||
this->free_heap_ = get_free_heap_();
|
||||
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/macros.h"
|
||||
#include <span>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
#ifdef USE_ESP8266
|
||||
#include <pgmspace.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
@@ -25,40 +19,7 @@ namespace debug {
|
||||
static constexpr size_t DEVICE_INFO_BUFFER_SIZE = 256;
|
||||
static constexpr size_t RESET_REASON_BUFFER_SIZE = 128;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
|
||||
// Format strings must be wrapped with PSTR() macro
|
||||
inline size_t buf_append_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
|
||||
if (pos >= size) {
|
||||
return size;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
|
||||
va_end(args);
|
||||
if (written < 0) {
|
||||
return pos; // encoding error
|
||||
}
|
||||
return std::min(pos + static_cast<size_t>(written), size);
|
||||
}
|
||||
#define buf_append(buf, size, pos, fmt, ...) buf_append_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
/// Safely append formatted string to buffer, returning new position (capped at size)
|
||||
__attribute__((format(printf, 4, 5))) inline size_t buf_append(char *buf, size_t size, size_t pos, const char *fmt,
|
||||
...) {
|
||||
if (pos >= size) {
|
||||
return size;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int written = vsnprintf(buf + pos, size - pos, fmt, args);
|
||||
va_end(args);
|
||||
if (written < 0) {
|
||||
return pos; // encoding error
|
||||
}
|
||||
return std::min(pos + static_cast<size_t>(written), size);
|
||||
}
|
||||
#endif
|
||||
// buf_append_printf is now provided by esphome/core/helpers.h
|
||||
|
||||
class DebugComponent : public PollingComponent {
|
||||
public:
|
||||
|
||||
@@ -173,8 +173,8 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
||||
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
||||
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||
flash_mode);
|
||||
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||
flash_mode);
|
||||
#endif
|
||||
|
||||
esp_chip_info_t info;
|
||||
@@ -182,52 +182,52 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
const char *model = ESPHOME_VARIANT;
|
||||
|
||||
// Build features string
|
||||
pos = buf_append(buf, size, pos, "|Chip: %s Features:", model);
|
||||
pos = buf_append_printf(buf, size, pos, "|Chip: %s Features:", model);
|
||||
bool first_feature = true;
|
||||
for (const auto &feature : CHIP_FEATURES) {
|
||||
if (info.features & feature.bit) {
|
||||
pos = buf_append(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
|
||||
pos = buf_append_printf(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
|
||||
first_feature = false;
|
||||
info.features &= ~feature.bit;
|
||||
}
|
||||
}
|
||||
if (info.features != 0) {
|
||||
pos = buf_append(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
|
||||
pos = buf_append_printf(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
|
||||
}
|
||||
ESP_LOGD(TAG, "Chip: Model=%s, Cores=%u, Revision=%u", model, info.cores, info.revision);
|
||||
pos = buf_append(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
|
||||
pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
|
||||
|
||||
uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
|
||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||
pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||
|
||||
// Framework detection
|
||||
#ifdef USE_ARDUINO
|
||||
ESP_LOGD(TAG, "Framework: Arduino");
|
||||
pos = buf_append(buf, size, pos, "|Framework: Arduino");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
|
||||
#elif defined(USE_ESP32)
|
||||
ESP_LOGD(TAG, "Framework: ESP-IDF");
|
||||
pos = buf_append(buf, size, pos, "|Framework: ESP-IDF");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
|
||||
#else
|
||||
ESP_LOGW(TAG, "Framework: UNKNOWN");
|
||||
pos = buf_append(buf, size, pos, "|Framework: UNKNOWN");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: UNKNOWN");
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
|
||||
pos = buf_append(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
||||
pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
||||
|
||||
uint8_t mac[6];
|
||||
get_mac_address_raw(mac);
|
||||
ESP_LOGD(TAG, "EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
pos = buf_append(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
|
||||
mac[5]);
|
||||
pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5]);
|
||||
|
||||
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
||||
const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||
pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
|
||||
const char *wakeup_cause = get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||
pos = buf_append(buf, size, pos, "|Wakeup: %s", wakeup_cause);
|
||||
pos = buf_append_printf(buf, size, pos, "|Wakeup: %s", wakeup_cause);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
||||
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
||||
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||
flash_mode);
|
||||
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||
flash_mode);
|
||||
|
||||
#if !defined(CLANG_TIDY)
|
||||
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
||||
@@ -77,15 +77,15 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
chip_id, ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), boot_version, boot_mode, cpu_freq, flash_chip_id,
|
||||
reset_reason, ESP.getResetInfo().c_str());
|
||||
|
||||
pos = buf_append(buf, size, pos, "|Chip: 0x%08" PRIX32, chip_id);
|
||||
pos = buf_append(buf, size, pos, "|SDK: %s", ESP.getSdkVersion());
|
||||
pos = buf_append(buf, size, pos, "|Core: %s", ESP.getCoreVersion().c_str());
|
||||
pos = buf_append(buf, size, pos, "|Boot: %u", boot_version);
|
||||
pos = buf_append(buf, size, pos, "|Mode: %u", boot_mode);
|
||||
pos = buf_append(buf, size, pos, "|CPU: %u", cpu_freq);
|
||||
pos = buf_append(buf, size, pos, "|Flash: 0x%08" PRIX32, flash_chip_id);
|
||||
pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
pos = buf_append(buf, size, pos, "|%s", ESP.getResetInfo().c_str());
|
||||
pos = buf_append_printf(buf, size, pos, "|Chip: 0x%08" PRIX32, chip_id);
|
||||
pos = buf_append_printf(buf, size, pos, "|SDK: %s", ESP.getSdkVersion());
|
||||
pos = buf_append_printf(buf, size, pos, "|Core: %s", ESP.getCoreVersion().c_str());
|
||||
pos = buf_append_printf(buf, size, pos, "|Boot: %u", boot_version);
|
||||
pos = buf_append_printf(buf, size, pos, "|Mode: %u", boot_mode);
|
||||
pos = buf_append_printf(buf, size, pos, "|CPU: %u", cpu_freq);
|
||||
pos = buf_append_printf(buf, size, pos, "|Flash: 0x%08" PRIX32, flash_chip_id);
|
||||
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
pos = buf_append_printf(buf, size, pos, "|%s", ESP.getResetInfo().c_str());
|
||||
#endif
|
||||
|
||||
return pos;
|
||||
|
||||
@@ -36,12 +36,12 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), mac_id,
|
||||
lt_get_board_code(), flash_kib, ram_kib, reset_reason);
|
||||
|
||||
pos = buf_append(buf, size, pos, "|Version: %s", LT_BANNER_STR + 10);
|
||||
pos = buf_append(buf, size, pos, "|Reset Reason: %s", reset_reason);
|
||||
pos = buf_append(buf, size, pos, "|Chip Name: %s", lt_cpu_get_model_name());
|
||||
pos = buf_append(buf, size, pos, "|Chip ID: 0x%06" PRIX32, mac_id);
|
||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 " KiB", flash_kib);
|
||||
pos = buf_append(buf, size, pos, "|RAM: %" PRIu32 " KiB", ram_kib);
|
||||
pos = buf_append_printf(buf, size, pos, "|Version: %s", LT_BANNER_STR + 10);
|
||||
pos = buf_append_printf(buf, size, pos, "|Reset Reason: %s", reset_reason);
|
||||
pos = buf_append_printf(buf, size, pos, "|Chip Name: %s", lt_cpu_get_model_name());
|
||||
pos = buf_append_printf(buf, size, pos, "|Chip ID: 0x%06" PRIX32, mac_id);
|
||||
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 " KiB", flash_kib);
|
||||
pos = buf_append_printf(buf, size, pos, "|RAM: %" PRIu32 " KiB", ram_kib);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
|
||||
uint32_t cpu_freq = rp2040.f_cpu();
|
||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq);
|
||||
pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq);
|
||||
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set,
|
||||
return pos;
|
||||
}
|
||||
if (pos > 0) {
|
||||
pos = buf_append(buf, size, pos, ", ");
|
||||
pos = buf_append_printf(buf, size, pos, ", ");
|
||||
}
|
||||
return buf_append(buf, size, pos, "%s", reason);
|
||||
return buf_append_printf(buf, size, pos, "%s", reason);
|
||||
}
|
||||
|
||||
static inline uint32_t read_mem_u32(uintptr_t addr) {
|
||||
@@ -140,7 +140,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
const char *supply_status =
|
||||
(nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage.";
|
||||
ESP_LOGD(TAG, "Main supply status: %s", supply_status);
|
||||
pos = buf_append(buf, size, pos, "|Main supply status: %s", supply_status);
|
||||
pos = buf_append_printf(buf, size, pos, "|Main supply status: %s", supply_status);
|
||||
|
||||
// Regulator stage 0
|
||||
if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
|
||||
@@ -172,16 +172,16 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
reg0_voltage = "???V";
|
||||
}
|
||||
ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
||||
pos = buf_append(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
||||
pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Regulator stage 0: disabled");
|
||||
pos = buf_append(buf, size, pos, "|Regulator stage 0: disabled");
|
||||
pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: disabled");
|
||||
}
|
||||
|
||||
// Regulator stage 1
|
||||
const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO";
|
||||
ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type);
|
||||
pos = buf_append(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
|
||||
pos = buf_append_printf(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
|
||||
|
||||
// USB power state
|
||||
const char *usb_state;
|
||||
@@ -195,7 +195,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
usb_state = "disconnected";
|
||||
}
|
||||
ESP_LOGD(TAG, "USB power state: %s", usb_state);
|
||||
pos = buf_append(buf, size, pos, "|USB power state: %s", usb_state);
|
||||
pos = buf_append_printf(buf, size, pos, "|USB power state: %s", usb_state);
|
||||
|
||||
// Power-fail comparator
|
||||
bool enabled;
|
||||
@@ -300,14 +300,14 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
||||
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage);
|
||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
|
||||
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Power-fail comparator: disabled");
|
||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: disabled");
|
||||
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: disabled");
|
||||
}
|
||||
|
||||
auto package = [](uint32_t value) {
|
||||
|
||||
@@ -127,9 +127,7 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
||||
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
|
||||
this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15);
|
||||
this->cmd_ = buf;
|
||||
this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15);
|
||||
} else if (min3 < 0 || max3 < 0) {
|
||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||
@@ -137,10 +135,7 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
||||
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
|
||||
this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
|
||||
max2 / 0.15);
|
||||
this->cmd_ = buf;
|
||||
this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15);
|
||||
} else if (min4 < 0 || max4 < 0) {
|
||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||
@@ -150,10 +145,9 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
||||
this->max3_ = max3 = round(max3 / 0.15) * 0.15;
|
||||
this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
|
||||
max2 / 0.15, min3 / 0.15, max3 / 0.15);
|
||||
this->cmd_ = buf;
|
||||
this->cmd_ = str_sprintf("detRangeCfg -1 "
|
||||
"%.0f %.0f %.0f %.0f %.0f %.0f",
|
||||
min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15);
|
||||
} else {
|
||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||
@@ -164,10 +158,10 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
||||
this->min4_ = min4 = round(min4 / 0.15) * 0.15;
|
||||
this->max4_ = max4 = round(max4 / 0.15) * 0.15;
|
||||
|
||||
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15,
|
||||
min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15, max4 / 0.15);
|
||||
this->cmd_ = buf;
|
||||
this->cmd_ = str_sprintf("detRangeCfg -1 "
|
||||
"%.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f",
|
||||
min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15,
|
||||
max4 / 0.15);
|
||||
}
|
||||
|
||||
this->min1_ = min1;
|
||||
@@ -209,10 +203,7 @@ SetLatencyCommand::SetLatencyCommand(float delay_after_detection, float delay_af
|
||||
delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f;
|
||||
this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f);
|
||||
this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f);
|
||||
// max 32: "setLatency "(11) + float(8) + " "(1) + float(8) + null, rounded to 32
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
|
||||
this->cmd_ = buf;
|
||||
this->cmd_ = str_sprintf("setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
|
||||
};
|
||||
|
||||
uint8_t SetLatencyCommand::on_message(std::string &message) {
|
||||
|
||||
@@ -75,8 +75,8 @@ class SetLatencyCommand : public Command {
|
||||
class SensorCfgStartCommand : public Command {
|
||||
public:
|
||||
SensorCfgStartCommand(bool startup_mode) : startup_mode_(startup_mode) {
|
||||
char tmp_cmd[20]; // "sensorCfgStart " (15) + "0/1" (1) + null = 17
|
||||
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "sensorCfgStart %d", startup_mode);
|
||||
char tmp_cmd[20] = {0};
|
||||
sprintf(tmp_cmd, "sensorCfgStart %d", startup_mode);
|
||||
cmd_ = std::string(tmp_cmd);
|
||||
}
|
||||
uint8_t on_message(std::string &message) override;
|
||||
@@ -142,8 +142,8 @@ class SensitivityCommand : public Command {
|
||||
SensitivityCommand(uint8_t sensitivity) : sensitivity_(sensitivity) {
|
||||
if (sensitivity > 9)
|
||||
sensitivity_ = sensitivity = 9;
|
||||
char tmp_cmd[20]; // "setSensitivity " (15) + "0-9" (1) + null = 17
|
||||
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "setSensitivity %d", sensitivity);
|
||||
char tmp_cmd[20] = {0};
|
||||
sprintf(tmp_cmd, "setSensitivity %d", sensitivity);
|
||||
cmd_ = std::string(tmp_cmd);
|
||||
};
|
||||
uint8_t on_message(std::string &message) override;
|
||||
|
||||
@@ -69,10 +69,7 @@ void Esp32HostedUpdate::setup() {
|
||||
// Get coprocessor version
|
||||
esp_hosted_coprocessor_fwver_t ver_info;
|
||||
if (esp_hosted_get_coprocessor_fwversion(&ver_info) == ESP_OK) {
|
||||
// 16 bytes: "255.255.255" (11 chars) + null + safety margin
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%d.%d.%d", ver_info.major1, ver_info.minor1, ver_info.patch1);
|
||||
this->update_info_.current_version = buf;
|
||||
this->update_info_.current_version = str_sprintf("%d.%d.%d", ver_info.major1, ver_info.minor1, ver_info.patch1);
|
||||
} else {
|
||||
this->update_info_.current_version = "unknown";
|
||||
}
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "preferences.h"
|
||||
#include <Arduino.h>
|
||||
#include <core_esp8266_features.h>
|
||||
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
}
|
||||
#include <Esp.h>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -20,19 +16,23 @@ void IRAM_ATTR HOT delay(uint32_t ms) { ::delay(ms); }
|
||||
uint32_t IRAM_ATTR HOT micros() { return ::micros(); }
|
||||
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
|
||||
void arch_restart() {
|
||||
system_restart();
|
||||
ESP.restart(); // NOLINT(readability-static-accessed-through-instance)
|
||||
// restart() doesn't always end execution
|
||||
while (true) { // NOLINT(clang-diagnostic-unreachable-code)
|
||||
yield();
|
||||
}
|
||||
}
|
||||
void arch_init() {}
|
||||
void IRAM_ATTR HOT arch_feed_wdt() { system_soft_wdt_feed(); }
|
||||
void IRAM_ATTR HOT arch_feed_wdt() {
|
||||
ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||
return pgm_read_byte(addr); // NOLINT
|
||||
}
|
||||
uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() { return esp_get_cycle_count(); }
|
||||
uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() {
|
||||
return ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
uint32_t arch_get_cpu_freq_hz() { return F_CPU; }
|
||||
|
||||
void force_link_symbols() {
|
||||
|
||||
@@ -99,7 +99,7 @@ void ESP8266GPIOPin::pin_mode(gpio::Flags flags) {
|
||||
}
|
||||
|
||||
size_t ESP8266GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "GPIO%u", this->pin_);
|
||||
return snprintf(buffer, len, "GPIO%u", this->pin_);
|
||||
}
|
||||
|
||||
bool ESP8266GPIOPin::digital_read() {
|
||||
|
||||
@@ -90,7 +90,9 @@ async def setup_event_core_(var, config, *, event_types: list[str]):
|
||||
|
||||
for conf in config.get(CONF_ON_EVENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.StringRef, "event_type")], conf)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "event_type")], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_event_types(event_types))
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public
|
||||
void play(const Ts &...x) override { this->parent_->trigger(this->event_type_.value(x...)); }
|
||||
};
|
||||
|
||||
class EventTrigger : public Trigger<StringRef> {
|
||||
class EventTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
EventTrigger(Event *event) {
|
||||
event->add_on_event_callback([this](StringRef event_type) { this->trigger(event_type); });
|
||||
event->add_on_event_callback([this](const std::string &event_type) { this->trigger(event_type); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ void Event::trigger(const std::string &event_type) {
|
||||
}
|
||||
this->last_event_type_ = found;
|
||||
ESP_LOGD(TAG, "'%s' >> '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
this->event_callback_.call(StringRef(found));
|
||||
this->event_callback_.call(event_type);
|
||||
#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_event(this);
|
||||
#endif
|
||||
@@ -45,7 +45,7 @@ void Event::set_event_types(const std::vector<const char *> &event_types) {
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(StringRef event_type)> &&callback) {
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
this->event_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,10 @@ class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
/// Check if an event has been triggered.
|
||||
bool has_event() const { return this->last_event_type_ != nullptr; }
|
||||
|
||||
void add_on_event_callback(std::function<void(StringRef event_type)> &&callback);
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
LazyCallbackManager<void(StringRef event_type)> event_callback_;
|
||||
LazyCallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
FixedVector<const char *> types_;
|
||||
|
||||
private:
|
||||
|
||||
@@ -160,7 +160,7 @@ void EZOSensor::loop() {
|
||||
this->commands_.pop_front();
|
||||
}
|
||||
|
||||
void EZOSensor::add_command_(const char *command, EzoCommandType command_type, uint16_t delay_ms) {
|
||||
void EZOSensor::add_command_(const std::string &command, EzoCommandType command_type, uint16_t delay_ms) {
|
||||
std::unique_ptr<EzoCommand> ezo_command(new EzoCommand);
|
||||
ezo_command->command = command;
|
||||
ezo_command->command_type = command_type;
|
||||
@@ -169,17 +169,13 @@ void EZOSensor::add_command_(const char *command, EzoCommandType command_type, u
|
||||
}
|
||||
|
||||
void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) {
|
||||
// max 21: "Cal,"(4) + type(4) + ","(1) + float(11) + null; use 24 for safety
|
||||
char payload[24];
|
||||
snprintf(payload, sizeof(payload), "Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);
|
||||
std::string payload = str_sprintf("Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
|
||||
}
|
||||
|
||||
void EZOSensor::set_address(uint8_t address) {
|
||||
if (address > 0 && address < 128) {
|
||||
// max 8: "I2C,"(4) + uint8(3) + null
|
||||
char payload[8];
|
||||
snprintf(payload, sizeof(payload), "I2C,%u", address);
|
||||
std::string payload = str_sprintf("I2C,%u", address);
|
||||
this->new_address_ = address;
|
||||
this->add_command_(payload, EzoCommandType::EZO_I2C);
|
||||
} else {
|
||||
@@ -198,9 +194,7 @@ void EZOSensor::get_slope() { this->add_command_("Slope,?", EzoCommandType::EZO_
|
||||
void EZOSensor::get_t() { this->add_command_("T,?", EzoCommandType::EZO_T); }
|
||||
|
||||
void EZOSensor::set_t(float value) {
|
||||
// max 14 bytes: "T,"(2) + float with "%0.2f" (up to 11 chars) + null(1); use 16 for alignment
|
||||
char payload[16];
|
||||
snprintf(payload, sizeof(payload), "T,%0.2f", value);
|
||||
std::string payload = str_sprintf("T,%0.2f", value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_T);
|
||||
}
|
||||
|
||||
@@ -221,9 +215,7 @@ void EZOSensor::set_calibration_point_high(float value) {
|
||||
}
|
||||
|
||||
void EZOSensor::set_calibration_generic(float value) {
|
||||
// exact 16 bytes: "Cal," (4) + float with "%0.2f" (up to 11 chars, e.g. "-9999999.99") + null (1) = 16
|
||||
char payload[16];
|
||||
snprintf(payload, sizeof(payload), "Cal,%0.2f", value);
|
||||
std::string payload = str_sprintf("Cal,%0.2f", value);
|
||||
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
|
||||
}
|
||||
|
||||
@@ -231,11 +223,13 @@ void EZOSensor::clear_calibration() { this->add_command_("Cal,clear", EzoCommand
|
||||
|
||||
void EZOSensor::get_led_state() { this->add_command_("L,?", EzoCommandType::EZO_LED); }
|
||||
|
||||
void EZOSensor::set_led_state(bool on) { this->add_command_(on ? "L,1" : "L,0", EzoCommandType::EZO_LED); }
|
||||
|
||||
void EZOSensor::send_custom(const std::string &to_send) {
|
||||
this->add_command_(to_send.c_str(), EzoCommandType::EZO_CUSTOM);
|
||||
void EZOSensor::set_led_state(bool on) {
|
||||
std::string to_send = "L,";
|
||||
to_send += on ? "1" : "0";
|
||||
this->add_command_(to_send, EzoCommandType::EZO_LED);
|
||||
}
|
||||
|
||||
void EZOSensor::send_custom(const std::string &to_send) { this->add_command_(to_send, EzoCommandType::EZO_CUSTOM); }
|
||||
|
||||
} // namespace ezo
|
||||
} // namespace esphome
|
||||
|
||||
@@ -92,7 +92,7 @@ class EZOSensor : public sensor::Sensor, public PollingComponent, public i2c::I2
|
||||
std::deque<std::unique_ptr<EzoCommand>> commands_;
|
||||
int new_address_;
|
||||
|
||||
void add_command_(const char *command, EzoCommandType command_type, uint16_t delay_ms = 300);
|
||||
void add_command_(const std::string &command, EzoCommandType command_type, uint16_t delay_ms = 300);
|
||||
|
||||
void set_calibration_point_(EzoCalibrationType type, float value);
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ FanSpeedSetTrigger = fan_ns.class_(
|
||||
"FanSpeedSetTrigger", automation.Trigger.template(cg.int_)
|
||||
)
|
||||
FanPresetSetTrigger = fan_ns.class_(
|
||||
"FanPresetSetTrigger", automation.Trigger.template(cg.StringRef)
|
||||
"FanPresetSetTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template())
|
||||
@@ -287,7 +287,7 @@ async def setup_fan_core_(var, config):
|
||||
await automation.build_automation(trigger, [(cg.int_, "x")], conf)
|
||||
for conf in config.get(CONF_ON_PRESET_SET, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.StringRef, "x")], conf)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
|
||||
async def register_fan(var, config):
|
||||
|
||||
@@ -208,7 +208,7 @@ class FanSpeedSetTrigger : public Trigger<int> {
|
||||
int last_speed_;
|
||||
};
|
||||
|
||||
class FanPresetSetTrigger : public Trigger<StringRef> {
|
||||
class FanPresetSetTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
FanPresetSetTrigger(Fan *state) {
|
||||
state->add_on_state_callback([this, state]() {
|
||||
@@ -216,7 +216,7 @@ class FanPresetSetTrigger : public Trigger<StringRef> {
|
||||
auto should_trigger = preset_mode != this->last_preset_mode_;
|
||||
this->last_preset_mode_ = preset_mode;
|
||||
if (should_trigger) {
|
||||
this->trigger(preset_mode);
|
||||
this->trigger(std::string(preset_mode));
|
||||
}
|
||||
});
|
||||
this->last_preset_mode_ = state->get_preset_mode();
|
||||
|
||||
@@ -163,10 +163,9 @@ bool GDK101Component::read_fw_version_(uint8_t *data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// max 8: "255.255" (7 chars) + null
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%d.%d", data[0], data[1]);
|
||||
this->fw_version_text_sensor_->publish_state(buf);
|
||||
const std::string fw_version_str = str_sprintf("%d.%d", data[0], data[1]);
|
||||
|
||||
this->fw_version_text_sensor_->publish_state(fw_version_str);
|
||||
}
|
||||
#endif // USE_TEXT_SENSOR
|
||||
return true;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "hmac_sha256.h"
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST)
|
||||
@@ -25,7 +26,9 @@ void HmacSHA256::calculate() { mbedtls_md_hmac_finish(&this->ctx_, this->digest_
|
||||
void HmacSHA256::get_bytes(uint8_t *output) { memcpy(output, this->digest_, SHA256_DIGEST_SIZE); }
|
||||
|
||||
void HmacSHA256::get_hex(char *output) {
|
||||
format_hex_to(output, SHA256_DIGEST_SIZE * 2 + 1, this->digest_, SHA256_DIGEST_SIZE);
|
||||
for (size_t i = 0; i < SHA256_DIGEST_SIZE; i++) {
|
||||
sprintf(output + (i * 2), "%02x", this->digest_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool HmacSHA256::equals_bytes(const uint8_t *expected) {
|
||||
|
||||
@@ -97,7 +97,7 @@ void HomeassistantNumber::control(float value) {
|
||||
entity_value.key = VALUE_KEY;
|
||||
// Stack buffer - no heap allocation; %g produces shortest representation
|
||||
char value_buf[16];
|
||||
buf_append_printf(value_buf, sizeof(value_buf), 0, "%g", value);
|
||||
snprintf(value_buf, sizeof(value_buf), "%g", value);
|
||||
entity_value.value = StringRef(value_buf);
|
||||
|
||||
api::global_api_server->send_homeassistant_action(resp);
|
||||
|
||||
@@ -242,7 +242,9 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t max_length = this->max_response_buffer_size_;
|
||||
size_t content_length = container->content_length;
|
||||
size_t max_length = std::min(content_length, this->max_response_buffer_size_);
|
||||
|
||||
#ifdef USE_HTTP_REQUEST_RESPONSE
|
||||
if (this->capture_response_.value(x...)) {
|
||||
std::string response_body;
|
||||
|
||||
@@ -213,12 +213,18 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
||||
const uint32_t start = millis();
|
||||
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
||||
|
||||
this->feed_wdt();
|
||||
int read_len = esp_http_client_read(this->client_, (char *) buf, max_len);
|
||||
this->feed_wdt();
|
||||
if (read_len > 0) {
|
||||
this->bytes_read_ += read_len;
|
||||
int bufsize = std::min(max_len, this->content_length - this->bytes_read_);
|
||||
|
||||
if (bufsize == 0) {
|
||||
this->duration_ms += (millis() - start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->feed_wdt();
|
||||
int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
|
||||
this->feed_wdt();
|
||||
this->bytes_read_ += read_len;
|
||||
|
||||
this->duration_ms += (millis() - start);
|
||||
|
||||
return read_len;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from esphome import automation, pins
|
||||
@@ -19,16 +18,13 @@ from esphome.const import (
|
||||
CONF_ROTATION,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID, EnumValue
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
import esphome.final_validate as fv
|
||||
from esphome.helpers import add_class_to_obj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import boards, hub75_ns
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ["esp32"]
|
||||
CODEOWNERS = ["@stuartparmenter"]
|
||||
|
||||
@@ -124,51 +120,13 @@ PANEL_LAYOUTS = {
|
||||
}
|
||||
|
||||
Hub75ScanWiring = cg.global_ns.enum("Hub75ScanWiring", is_class=True)
|
||||
SCAN_WIRINGS = {
|
||||
SCAN_PATTERNS = {
|
||||
"STANDARD_TWO_SCAN": Hub75ScanWiring.STANDARD_TWO_SCAN,
|
||||
"SCAN_1_4_16PX_HIGH": Hub75ScanWiring.SCAN_1_4_16PX_HIGH,
|
||||
"SCAN_1_8_32PX_HIGH": Hub75ScanWiring.SCAN_1_8_32PX_HIGH,
|
||||
"SCAN_1_8_40PX_HIGH": Hub75ScanWiring.SCAN_1_8_40PX_HIGH,
|
||||
"SCAN_1_8_64PX_HIGH": Hub75ScanWiring.SCAN_1_8_64PX_HIGH,
|
||||
"FOUR_SCAN_16PX_HIGH": Hub75ScanWiring.FOUR_SCAN_16PX_HIGH,
|
||||
"FOUR_SCAN_32PX_HIGH": Hub75ScanWiring.FOUR_SCAN_32PX_HIGH,
|
||||
"FOUR_SCAN_64PX_HIGH": Hub75ScanWiring.FOUR_SCAN_64PX_HIGH,
|
||||
}
|
||||
|
||||
# Deprecated scan wiring names - mapped to new names
|
||||
DEPRECATED_SCAN_WIRINGS = {
|
||||
"FOUR_SCAN_16PX_HIGH": "SCAN_1_4_16PX_HIGH",
|
||||
"FOUR_SCAN_32PX_HIGH": "SCAN_1_8_32PX_HIGH",
|
||||
"FOUR_SCAN_64PX_HIGH": "SCAN_1_8_64PX_HIGH",
|
||||
}
|
||||
|
||||
|
||||
def _validate_scan_wiring(value):
|
||||
"""Validate scan_wiring with deprecation warnings for old names."""
|
||||
value = cv.string(value).upper().replace(" ", "_")
|
||||
|
||||
# Check if using deprecated name
|
||||
# Remove deprecated names in 2026.7.0
|
||||
if value in DEPRECATED_SCAN_WIRINGS:
|
||||
new_name = DEPRECATED_SCAN_WIRINGS[value]
|
||||
_LOGGER.warning(
|
||||
"Scan wiring '%s' is deprecated and will be removed in ESPHome 2026.7.0. "
|
||||
"Please use '%s' instead.",
|
||||
value,
|
||||
new_name,
|
||||
)
|
||||
value = new_name
|
||||
|
||||
# Validate against allowed values
|
||||
if value not in SCAN_WIRINGS:
|
||||
raise cv.Invalid(
|
||||
f"Unknown scan wiring '{value}'. "
|
||||
f"Valid options are: {', '.join(sorted(SCAN_WIRINGS.keys()))}"
|
||||
)
|
||||
|
||||
# Return as EnumValue like cv.enum does
|
||||
result = add_class_to_obj(value, EnumValue)
|
||||
result.enum_value = SCAN_WIRINGS[value]
|
||||
return result
|
||||
|
||||
|
||||
Hub75ClockSpeed = cg.global_ns.enum("Hub75ClockSpeed", is_class=True)
|
||||
CLOCK_SPEEDS = {
|
||||
"8MHZ": Hub75ClockSpeed.HZ_8M,
|
||||
@@ -424,7 +382,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_LAYOUT_COLS): cv.positive_int,
|
||||
cv.Optional(CONF_LAYOUT): cv.enum(PANEL_LAYOUTS, upper=True, space="_"),
|
||||
# Panel hardware configuration
|
||||
cv.Optional(CONF_SCAN_WIRING): _validate_scan_wiring,
|
||||
cv.Optional(CONF_SCAN_WIRING): cv.enum(
|
||||
SCAN_PATTERNS, upper=True, space="_"
|
||||
),
|
||||
cv.Optional(CONF_SHIFT_DRIVER): cv.enum(SHIFT_DRIVERS, upper=True),
|
||||
# Display configuration
|
||||
cv.Optional(CONF_DOUBLE_BUFFER): cv.boolean,
|
||||
@@ -587,7 +547,7 @@ def _build_config_struct(
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
add_idf_component(
|
||||
name="esphome/esp-hub75",
|
||||
ref="0.3.0",
|
||||
ref="0.2.2",
|
||||
)
|
||||
|
||||
# Set compile-time configuration via build flags (so external library sees them)
|
||||
|
||||
@@ -42,8 +42,8 @@ ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t
|
||||
}
|
||||
|
||||
ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const {
|
||||
SmallBufferWithHeapFallback<17> buffer_alloc(len + 1); // Most I2C writes are <= 16 bytes
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
SmallBufferWithHeapFallback<17> buffer_alloc; // Most I2C writes are <= 16 bytes
|
||||
uint8_t *buffer = buffer_alloc.get(len + 1);
|
||||
|
||||
buffer[0] = a_register;
|
||||
std::copy(data, data + len, buffer + 1);
|
||||
@@ -51,8 +51,8 @@ ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, siz
|
||||
}
|
||||
|
||||
ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const {
|
||||
SmallBufferWithHeapFallback<18> buffer_alloc(len + 2); // Most I2C writes are <= 16 bytes + 2 for register
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
SmallBufferWithHeapFallback<18> buffer_alloc; // Most I2C writes are <= 16 bytes + 2 for register
|
||||
uint8_t *buffer = buffer_alloc.get(len + 2);
|
||||
|
||||
buffer[0] = a_register >> 8;
|
||||
buffer[1] = a_register;
|
||||
|
||||
@@ -11,6 +11,22 @@
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
|
||||
/// @brief Helper class for efficient buffer allocation - uses stack for small sizes, heap for large
|
||||
template<size_t STACK_SIZE> class SmallBufferWithHeapFallback {
|
||||
public:
|
||||
uint8_t *get(size_t size) {
|
||||
if (size <= STACK_SIZE) {
|
||||
return this->stack_buffer_;
|
||||
}
|
||||
this->heap_buffer_ = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
|
||||
return this->heap_buffer_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t stack_buffer_[STACK_SIZE];
|
||||
std::unique_ptr<uint8_t[]> heap_buffer_;
|
||||
};
|
||||
|
||||
/// @brief Error codes returned by I2CBus and I2CDevice methods
|
||||
enum ErrorCode {
|
||||
NO_ERROR = 0, ///< No error found during execution of method
|
||||
@@ -76,8 +92,8 @@ class I2CBus {
|
||||
total_len += read_buffers[i].len;
|
||||
}
|
||||
|
||||
SmallBufferWithHeapFallback<128> buffer_alloc(total_len); // Most I2C reads are small
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
SmallBufferWithHeapFallback<128> buffer_alloc; // Most I2C reads are small
|
||||
uint8_t *buffer = buffer_alloc.get(total_len);
|
||||
|
||||
auto err = this->write_readv(address, nullptr, 0, buffer, total_len);
|
||||
if (err != ERROR_OK)
|
||||
@@ -100,8 +116,8 @@ class I2CBus {
|
||||
total_len += write_buffers[i].len;
|
||||
}
|
||||
|
||||
SmallBufferWithHeapFallback<128> buffer_alloc(total_len); // Most I2C writes are small
|
||||
uint8_t *buffer = buffer_alloc.get();
|
||||
SmallBufferWithHeapFallback<128> buffer_alloc; // Most I2C writes are small
|
||||
uint8_t *buffer = buffer_alloc.get(total_len);
|
||||
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i != count; i++) {
|
||||
|
||||
@@ -19,12 +19,12 @@ InfraredCall &InfraredCall::set_carrier_frequency(uint32_t frequency) {
|
||||
InfraredCall &InfraredCall::set_raw_timings(const std::vector<int32_t> &timings) {
|
||||
this->raw_timings_ = &timings;
|
||||
this->packed_data_ = nullptr;
|
||||
this->base64url_ptr_ = nullptr;
|
||||
this->base85_ptr_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
InfraredCall &InfraredCall::set_raw_timings_base64url(const std::string &base64url) {
|
||||
this->base64url_ptr_ = &base64url;
|
||||
InfraredCall &InfraredCall::set_raw_timings_base85(const std::string &base85) {
|
||||
this->base85_ptr_ = &base85;
|
||||
this->raw_timings_ = nullptr;
|
||||
this->packed_data_ = nullptr;
|
||||
return *this;
|
||||
@@ -35,7 +35,7 @@ InfraredCall &InfraredCall::set_raw_timings_packed(const uint8_t *data, uint16_t
|
||||
this->packed_length_ = length;
|
||||
this->packed_count_ = count;
|
||||
this->raw_timings_ = nullptr;
|
||||
this->base64url_ptr_ = nullptr;
|
||||
this->base85_ptr_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -101,22 +101,13 @@ void Infrared::control(const InfraredCall &call) {
|
||||
call.get_packed_count());
|
||||
ESP_LOGD(TAG, "Transmitting packed raw timings: count=%u, repeat=%u", call.get_packed_count(),
|
||||
call.get_repeat_count());
|
||||
} else if (call.is_base64url()) {
|
||||
// Decode base64url (URL-safe) into transmit buffer
|
||||
if (!transmit_data->set_data_from_base64url(call.get_base64url_data())) {
|
||||
ESP_LOGE(TAG, "Invalid base64url data");
|
||||
} else if (call.is_base85()) {
|
||||
// Decode base85 directly into transmit buffer (zero heap allocations)
|
||||
if (!transmit_data->set_data_from_base85(call.get_base85_data())) {
|
||||
ESP_LOGE(TAG, "Invalid base85 data");
|
||||
return;
|
||||
}
|
||||
// Sanity check: validate timing values are within reasonable bounds
|
||||
constexpr int32_t max_timing_us = 500000; // 500ms absolute max
|
||||
for (int32_t timing : transmit_data->get_data()) {
|
||||
int32_t abs_timing = timing < 0 ? -timing : timing;
|
||||
if (abs_timing > max_timing_us) {
|
||||
ESP_LOGE(TAG, "Invalid timing value: %d µs (max %d)", timing, max_timing_us);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Transmitting base64url raw timings: count=%zu, repeat=%u", transmit_data->get_data().size(),
|
||||
ESP_LOGD(TAG, "Transmitting base85 raw timings: count=%zu, repeat=%u", transmit_data->get_data().size(),
|
||||
call.get_repeat_count());
|
||||
} else {
|
||||
// From vector (lambdas/automations)
|
||||
|
||||
@@ -40,11 +40,11 @@ class InfraredCall {
|
||||
/// @note Usage: Primarily for lambdas/automations where the vector is in scope.
|
||||
InfraredCall &set_raw_timings(const std::vector<int32_t> &timings);
|
||||
|
||||
/// Set the raw timings from base64url-encoded little-endian int32 data
|
||||
/// Set the raw timings from base85-encoded int32 data
|
||||
/// @note Lifetime: Stores a pointer to the string. The string must outlive perform().
|
||||
/// @note Usage: For web_server - base64url is fully URL-safe (uses '-' and '_').
|
||||
/// @note Usage: For web_server where the encoded string is on the stack.
|
||||
/// @note Decoding happens at perform() time, directly into the transmit buffer.
|
||||
InfraredCall &set_raw_timings_base64url(const std::string &base64url);
|
||||
InfraredCall &set_raw_timings_base85(const std::string &base85);
|
||||
|
||||
/// Set the raw timings from packed protobuf sint32 data (zigzag + varint encoded)
|
||||
/// @note Lifetime: Stores a pointer to the buffer. The buffer must outlive perform().
|
||||
@@ -59,18 +59,18 @@ class InfraredCall {
|
||||
|
||||
/// Get the carrier frequency
|
||||
const optional<uint32_t> &get_carrier_frequency() const { return this->carrier_frequency_; }
|
||||
/// Get the raw timings (only valid if set via set_raw_timings)
|
||||
/// Get the raw timings (only valid if set via set_raw_timings, not packed or base85)
|
||||
const std::vector<int32_t> &get_raw_timings() const { return *this->raw_timings_; }
|
||||
/// Check if raw timings have been set (any format)
|
||||
/// Check if raw timings have been set (vector, packed, or base85)
|
||||
bool has_raw_timings() const {
|
||||
return this->raw_timings_ != nullptr || this->packed_data_ != nullptr || this->base64url_ptr_ != nullptr;
|
||||
return this->raw_timings_ != nullptr || this->packed_data_ != nullptr || this->base85_ptr_ != nullptr;
|
||||
}
|
||||
/// Check if using packed data format
|
||||
bool is_packed() const { return this->packed_data_ != nullptr; }
|
||||
/// Check if using base64url data format
|
||||
bool is_base64url() const { return this->base64url_ptr_ != nullptr; }
|
||||
/// Get the base64url data string
|
||||
const std::string &get_base64url_data() const { return *this->base64url_ptr_; }
|
||||
/// Check if using base85 data format
|
||||
bool is_base85() const { return this->base85_ptr_ != nullptr; }
|
||||
/// Get the base85 data string
|
||||
const std::string &get_base85_data() const { return *this->base85_ptr_; }
|
||||
/// Get packed data (only valid if set via set_raw_timings_packed)
|
||||
const uint8_t *get_packed_data() const { return this->packed_data_; }
|
||||
uint16_t get_packed_length() const { return this->packed_length_; }
|
||||
@@ -84,8 +84,8 @@ class InfraredCall {
|
||||
optional<uint32_t> carrier_frequency_;
|
||||
// Pointer to vector-based timings (caller-owned, must outlive perform())
|
||||
const std::vector<int32_t> *raw_timings_{nullptr};
|
||||
// Pointer to base64url-encoded string (caller-owned, must outlive perform())
|
||||
const std::string *base64url_ptr_{nullptr};
|
||||
// Pointer to base85-encoded string (caller-owned, must outlive perform())
|
||||
const std::string *base85_ptr_{nullptr};
|
||||
// Pointer to packed protobuf buffer (caller-owned, must outlive perform())
|
||||
const uint8_t *packed_data_{nullptr};
|
||||
uint16_t packed_length_{0};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "light_json_schema.h"
|
||||
#include "color_mode.h"
|
||||
#include "light_output.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
@@ -9,32 +8,29 @@ namespace esphome::light {
|
||||
|
||||
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
|
||||
|
||||
// Get JSON string for color mode.
|
||||
// ColorMode enum values are sparse bitmasks (0, 1, 3, 7, 11, 19, 35, 39, 47, 51) which would
|
||||
// generate a large jump table. Converting to bit index (0-9) allows a compact switch.
|
||||
static ProgmemStr get_color_mode_json_str(ColorMode mode) {
|
||||
switch (ColorModeBitPolicy::to_bit(mode)) {
|
||||
case 1:
|
||||
return ESPHOME_F("onoff");
|
||||
case 2:
|
||||
return ESPHOME_F("brightness");
|
||||
case 3:
|
||||
return ESPHOME_F("white");
|
||||
case 4:
|
||||
return ESPHOME_F("color_temp");
|
||||
case 5:
|
||||
return ESPHOME_F("cwww");
|
||||
case 6:
|
||||
return ESPHOME_F("rgb");
|
||||
case 7:
|
||||
return ESPHOME_F("rgbw");
|
||||
case 8:
|
||||
return ESPHOME_F("rgbct");
|
||||
case 9:
|
||||
return ESPHOME_F("rgbww");
|
||||
default:
|
||||
return nullptr;
|
||||
// Get JSON string for color mode using linear search (avoids large switch jump table)
|
||||
static const char *get_color_mode_json_str(ColorMode mode) {
|
||||
// Parallel arrays: mode values and their corresponding strings
|
||||
// Uses less RAM than a switch jump table on sparse enum values
|
||||
static constexpr ColorMode MODES[] = {
|
||||
ColorMode::ON_OFF,
|
||||
ColorMode::BRIGHTNESS,
|
||||
ColorMode::WHITE,
|
||||
ColorMode::COLOR_TEMPERATURE,
|
||||
ColorMode::COLD_WARM_WHITE,
|
||||
ColorMode::RGB,
|
||||
ColorMode::RGB_WHITE,
|
||||
ColorMode::RGB_COLOR_TEMPERATURE,
|
||||
ColorMode::RGB_COLD_WARM_WHITE,
|
||||
};
|
||||
static constexpr const char *STRINGS[] = {
|
||||
"onoff", "brightness", "white", "color_temp", "cwww", "rgb", "rgbw", "rgbct", "rgbww",
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(MODES) / sizeof(MODES[0]); i++) {
|
||||
if (MODES[i] == mode)
|
||||
return STRINGS[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||
@@ -48,7 +44,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||
auto values = state.remote_values;
|
||||
|
||||
const auto color_mode = values.get_color_mode();
|
||||
const auto *mode_str = get_color_mode_json_str(color_mode);
|
||||
const char *mode_str = get_color_mode_json_str(color_mode);
|
||||
if (mode_str != nullptr) {
|
||||
root[ESPHOME_F("color_mode")] = mode_str;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ void MAX6956GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this-
|
||||
bool MAX6956GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void MAX6956GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t MAX6956GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via Max6956", this->pin_);
|
||||
return snprintf(buffer, len, "%u via Max6956", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace max6956
|
||||
|
||||
@@ -100,7 +100,7 @@ void MCP23016GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this
|
||||
bool MCP23016GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void MCP23016GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t MCP23016GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via MCP23016", this->pin_);
|
||||
return snprintf(buffer, len, "%u via MCP23016", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace mcp23016
|
||||
|
||||
@@ -17,7 +17,7 @@ template<uint8_t N> void MCP23XXXGPIOPin<N>::digital_write(bool value) {
|
||||
this->parent_->digital_write(this->pin_, value != this->inverted_);
|
||||
}
|
||||
template<uint8_t N> size_t MCP23XXXGPIOPin<N>::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via MCP23XXX", this->pin_);
|
||||
return snprintf(buffer, len, "%u via MCP23XXX", this->pin_);
|
||||
}
|
||||
|
||||
template class MCP23XXXGPIOPin<8>;
|
||||
|
||||
@@ -101,225 +101,4 @@ DriverChip(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x00),
|
||||
]
|
||||
)
|
||||
|
||||
# jc8012P4A1 Driver Configuration (jd9365)
|
||||
# Using parameters from esp_lcd_jd9365.h and the working full init sequence
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# * Resolution: 800x1280
|
||||
# * PCLK Frequency: 60 MHz
|
||||
# * DSI Lane Bit Rate: 1 Gbps (using 2-Lane DSI configuration)
|
||||
# * Horizontal Timing (hsync_pulse_width=20, hsync_back_porch=20, hsync_front_porch=40)
|
||||
# * Vertical Timing (vsync_pulse_width=4, vsync_back_porch=8, vsync_front_porch=20)
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
DriverChip(
|
||||
"JC8012P4A1",
|
||||
width=800,
|
||||
height=1280,
|
||||
hsync_back_porch=20,
|
||||
hsync_pulse_width=20,
|
||||
hsync_front_porch=40,
|
||||
vsync_back_porch=8,
|
||||
vsync_pulse_width=4,
|
||||
vsync_front_porch=20,
|
||||
pclk_frequency="60MHz",
|
||||
lane_bit_rate="1Gbps",
|
||||
swap_xy=cv.UNDEFINED,
|
||||
color_order="RGB",
|
||||
reset_pin=27,
|
||||
initsequence=[
|
||||
(0xE0, 0x00),
|
||||
(0xE1, 0x93),
|
||||
(0xE2, 0x65),
|
||||
(0xE3, 0xF8),
|
||||
(0x80, 0x01),
|
||||
(0xE0, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x01, 0x39),
|
||||
(0x03, 0x10),
|
||||
(0x04, 0x41),
|
||||
(0x0C, 0x74),
|
||||
(0x17, 0x00),
|
||||
(0x18, 0xD7),
|
||||
(0x19, 0x00),
|
||||
(0x1A, 0x00),
|
||||
(0x1B, 0xD7),
|
||||
(0x1C, 0x00),
|
||||
(0x24, 0xFE),
|
||||
(0x35, 0x26),
|
||||
(0x37, 0x69),
|
||||
(0x38, 0x05),
|
||||
(0x39, 0x06),
|
||||
(0x3A, 0x08),
|
||||
(0x3C, 0x78),
|
||||
(0x3D, 0xFF),
|
||||
(0x3E, 0xFF),
|
||||
(0x3F, 0xFF),
|
||||
(0x40, 0x06),
|
||||
(0x41, 0xA0),
|
||||
(0x43, 0x14),
|
||||
(0x44, 0x0B),
|
||||
(0x45, 0x30),
|
||||
(0x4B, 0x04),
|
||||
(0x55, 0x02),
|
||||
(0x57, 0x89),
|
||||
(0x59, 0x0A),
|
||||
(0x5A, 0x28),
|
||||
(0x5B, 0x15),
|
||||
(0x5D, 0x50),
|
||||
(0x5E, 0x37),
|
||||
(0x5F, 0x29),
|
||||
(0x60, 0x1E),
|
||||
(0x61, 0x1D),
|
||||
(0x62, 0x12),
|
||||
(0x63, 0x1A),
|
||||
(0x64, 0x08),
|
||||
(0x65, 0x25),
|
||||
(0x66, 0x26),
|
||||
(0x67, 0x28),
|
||||
(0x68, 0x49),
|
||||
(0x69, 0x3A),
|
||||
(0x6A, 0x43),
|
||||
(0x6B, 0x3A),
|
||||
(0x6C, 0x3B),
|
||||
(0x6D, 0x32),
|
||||
(0x6E, 0x1F),
|
||||
(0x6F, 0x0E),
|
||||
(0x70, 0x50),
|
||||
(0x71, 0x37),
|
||||
(0x72, 0x29),
|
||||
(0x73, 0x1E),
|
||||
(0x74, 0x1D),
|
||||
(0x75, 0x12),
|
||||
(0x76, 0x1A),
|
||||
(0x77, 0x08),
|
||||
(0x78, 0x25),
|
||||
(0x79, 0x26),
|
||||
(0x7A, 0x28),
|
||||
(0x7B, 0x49),
|
||||
(0x7C, 0x3A),
|
||||
(0x7D, 0x43),
|
||||
(0x7E, 0x3A),
|
||||
(0x7F, 0x3B),
|
||||
(0x80, 0x32),
|
||||
(0x81, 0x1F),
|
||||
(0x82, 0x0E),
|
||||
(0xE0, 0x02),
|
||||
(0x00, 0x1F),
|
||||
(0x01, 0x1F),
|
||||
(0x02, 0x52),
|
||||
(0x03, 0x51),
|
||||
(0x04, 0x50),
|
||||
(0x05, 0x4B),
|
||||
(0x06, 0x4A),
|
||||
(0x07, 0x49),
|
||||
(0x08, 0x48),
|
||||
(0x09, 0x47),
|
||||
(0x0A, 0x46),
|
||||
(0x0B, 0x45),
|
||||
(0x0C, 0x44),
|
||||
(0x0D, 0x40),
|
||||
(0x0E, 0x41),
|
||||
(0x0F, 0x1F),
|
||||
(0x10, 0x1F),
|
||||
(0x11, 0x1F),
|
||||
(0x12, 0x1F),
|
||||
(0x13, 0x1F),
|
||||
(0x14, 0x1F),
|
||||
(0x15, 0x1F),
|
||||
(0x16, 0x1F),
|
||||
(0x17, 0x1F),
|
||||
(0x18, 0x52),
|
||||
(0x19, 0x51),
|
||||
(0x1A, 0x50),
|
||||
(0x1B, 0x4B),
|
||||
(0x1C, 0x4A),
|
||||
(0x1D, 0x49),
|
||||
(0x1E, 0x48),
|
||||
(0x1F, 0x47),
|
||||
(0x20, 0x46),
|
||||
(0x21, 0x45),
|
||||
(0x22, 0x44),
|
||||
(0x23, 0x40),
|
||||
(0x24, 0x41),
|
||||
(0x25, 0x1F),
|
||||
(0x26, 0x1F),
|
||||
(0x27, 0x1F),
|
||||
(0x28, 0x1F),
|
||||
(0x29, 0x1F),
|
||||
(0x2A, 0x1F),
|
||||
(0x2B, 0x1F),
|
||||
(0x2C, 0x1F),
|
||||
(0x2D, 0x1F),
|
||||
(0x2E, 0x52),
|
||||
(0x2F, 0x40),
|
||||
(0x30, 0x41),
|
||||
(0x31, 0x48),
|
||||
(0x32, 0x49),
|
||||
(0x33, 0x4A),
|
||||
(0x34, 0x4B),
|
||||
(0x35, 0x44),
|
||||
(0x36, 0x45),
|
||||
(0x37, 0x46),
|
||||
(0x38, 0x47),
|
||||
(0x39, 0x51),
|
||||
(0x3A, 0x50),
|
||||
(0x3B, 0x1F),
|
||||
(0x3C, 0x1F),
|
||||
(0x3D, 0x1F),
|
||||
(0x3E, 0x1F),
|
||||
(0x3F, 0x1F),
|
||||
(0x40, 0x1F),
|
||||
(0x41, 0x1F),
|
||||
(0x42, 0x1F),
|
||||
(0x43, 0x1F),
|
||||
(0x44, 0x52),
|
||||
(0x45, 0x40),
|
||||
(0x46, 0x41),
|
||||
(0x47, 0x48),
|
||||
(0x48, 0x49),
|
||||
(0x49, 0x4A),
|
||||
(0x4A, 0x4B),
|
||||
(0x4B, 0x44),
|
||||
(0x4C, 0x45),
|
||||
(0x4D, 0x46),
|
||||
(0x4E, 0x47),
|
||||
(0x4F, 0x51),
|
||||
(0x50, 0x50),
|
||||
(0x51, 0x1F),
|
||||
(0x52, 0x1F),
|
||||
(0x53, 0x1F),
|
||||
(0x54, 0x1F),
|
||||
(0x55, 0x1F),
|
||||
(0x56, 0x1F),
|
||||
(0x57, 0x1F),
|
||||
(0x58, 0x40),
|
||||
(0x59, 0x00),
|
||||
(0x5A, 0x00),
|
||||
(0x5B, 0x10),
|
||||
(0x5C, 0x05),
|
||||
(0x5D, 0x50),
|
||||
(0x5E, 0x01),
|
||||
(0x5F, 0x02),
|
||||
(0x60, 0x50),
|
||||
(0x61, 0x06),
|
||||
(0x62, 0x04),
|
||||
(0x63, 0x03),
|
||||
(0x64, 0x64),
|
||||
(0x65, 0x65),
|
||||
(0x66, 0x0B),
|
||||
(0x67, 0x73),
|
||||
(0x68, 0x07),
|
||||
(0x69, 0x06),
|
||||
(0x6A, 0x64),
|
||||
(0x6B, 0x08),
|
||||
(0x6C, 0x00),
|
||||
(0x6D, 0x32),
|
||||
(0x6E, 0x08),
|
||||
(0xE0, 0x04),
|
||||
(0x2C, 0x6B),
|
||||
(0x35, 0x08),
|
||||
(0x37, 0x00),
|
||||
(0xE0, 0x00),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
@@ -271,31 +271,24 @@ class ServerRegister {
|
||||
|
||||
// Formats a raw value into a string representation based on the value type for debugging
|
||||
std::string format_value(int64_t value) const {
|
||||
// max 44: float with %.1f can be up to 42 chars (3.4e38 → 39 integer digits + sign + decimal + 1 digit)
|
||||
// plus null terminator = 43, rounded to 44 for 4-byte alignment
|
||||
char buf[44];
|
||||
switch (this->value_type) {
|
||||
case SensorValueType::U_WORD:
|
||||
case SensorValueType::U_DWORD:
|
||||
case SensorValueType::U_DWORD_R:
|
||||
case SensorValueType::U_QWORD:
|
||||
case SensorValueType::U_QWORD_R:
|
||||
buf_append_printf(buf, sizeof(buf), 0, "%" PRIu64, static_cast<uint64_t>(value));
|
||||
return buf;
|
||||
return std::to_string(static_cast<uint64_t>(value));
|
||||
case SensorValueType::S_WORD:
|
||||
case SensorValueType::S_DWORD:
|
||||
case SensorValueType::S_DWORD_R:
|
||||
case SensorValueType::S_QWORD:
|
||||
case SensorValueType::S_QWORD_R:
|
||||
buf_append_printf(buf, sizeof(buf), 0, "%" PRId64, value);
|
||||
return buf;
|
||||
return std::to_string(value);
|
||||
case SensorValueType::FP32_R:
|
||||
case SensorValueType::FP32:
|
||||
buf_append_printf(buf, sizeof(buf), 0, "%.1f", bit_cast<float>(static_cast<uint32_t>(value)));
|
||||
return buf;
|
||||
return str_sprintf("%.1f", bit_cast<float>(static_cast<uint32_t>(value)));
|
||||
default:
|
||||
buf_append_printf(buf, sizeof(buf), 0, "%" PRId64, value);
|
||||
return buf;
|
||||
return std::to_string(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,20 +16,12 @@ void ModbusTextSensor::parse_and_publish(const std::vector<uint8_t> &data) {
|
||||
while ((items_left > 0) && index < data.size()) {
|
||||
uint8_t b = data[index];
|
||||
switch (this->encode_) {
|
||||
case RawEncoding::HEXBYTES: {
|
||||
// max 3: 2 hex digits + null
|
||||
char hex_buf[3];
|
||||
snprintf(hex_buf, sizeof(hex_buf), "%02x", b);
|
||||
output_str += hex_buf;
|
||||
case RawEncoding::HEXBYTES:
|
||||
output_str += str_snprintf("%02x", 2, b);
|
||||
break;
|
||||
}
|
||||
case RawEncoding::COMMA: {
|
||||
// max 5: optional ','(1) + uint8(3) + null, for both ",%d" and "%d"
|
||||
char dec_buf[5];
|
||||
snprintf(dec_buf, sizeof(dec_buf), index != this->offset ? ",%d" : "%d", b);
|
||||
output_str += dec_buf;
|
||||
case RawEncoding::COMMA:
|
||||
output_str += str_sprintf(index != this->offset ? ",%d" : "%d", b);
|
||||
break;
|
||||
}
|
||||
case RawEncoding::ANSI:
|
||||
if (b < 0x20)
|
||||
break;
|
||||
|
||||
@@ -154,7 +154,7 @@ void MPR121GPIOPin::digital_write(bool value) {
|
||||
}
|
||||
|
||||
size_t MPR121GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "ELE%u on MPR121", this->pin_);
|
||||
return snprintf(buffer, len, "ELE%u on MPR121", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace mpr121
|
||||
|
||||
@@ -43,7 +43,7 @@ void MQTTAlarmControlPanelComponent::setup() {
|
||||
|
||||
void MQTTAlarmControlPanelComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT alarm_control_panel '%s':", this->alarm_control_panel_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Supported Features: %" PRIu32 "\n"
|
||||
" Requires Code to Disarm: %s\n"
|
||||
|
||||
@@ -19,7 +19,7 @@ void MQTTBinarySensorComponent::setup() {
|
||||
|
||||
void MQTTBinarySensorComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Binary Sensor '%s':", this->binary_sensor_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
LOG_MQTT_COMPONENT(true, false)
|
||||
}
|
||||
MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor *binary_sensor)
|
||||
: binary_sensor_(binary_sensor) {
|
||||
|
||||
@@ -635,8 +635,7 @@ void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this
|
||||
const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; }
|
||||
void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix, const std::string &check_topic_prefix) {
|
||||
if (App.is_name_add_mac_suffix_enabled() && (topic_prefix == check_topic_prefix)) {
|
||||
char buf[ESPHOME_DEVICE_NAME_MAX_LEN + 1];
|
||||
this->topic_prefix_ = str_sanitize_to(buf, App.get_name().c_str());
|
||||
this->topic_prefix_ = str_sanitize(App.get_name());
|
||||
} else {
|
||||
this->topic_prefix_ = topic_prefix;
|
||||
}
|
||||
|
||||
@@ -27,23 +27,20 @@ inline char *append_char(char *p, char c) {
|
||||
// Max lengths for stack-based topic building.
|
||||
// These limits are enforced at Python config validation time in mqtt/__init__.py
|
||||
// using cv.Length() validators for topic_prefix and discovery_prefix.
|
||||
// MQTT_COMPONENT_TYPE_MAX_LEN, MQTT_SUFFIX_MAX_LEN, and MQTT_DEFAULT_TOPIC_MAX_LEN are in mqtt_component.h.
|
||||
// MQTT_COMPONENT_TYPE_MAX_LEN and MQTT_SUFFIX_MAX_LEN are defined in mqtt_component.h.
|
||||
// ESPHOME_DEVICE_NAME_MAX_LEN and OBJECT_ID_MAX_LEN are defined in entity_base.h.
|
||||
// This ensures the stack buffers below are always large enough.
|
||||
static constexpr size_t TOPIC_PREFIX_MAX_LEN = 64; // Validated in Python: cv.Length(max=64)
|
||||
static constexpr size_t DISCOVERY_PREFIX_MAX_LEN = 64; // Validated in Python: cv.Length(max=64)
|
||||
|
||||
// Stack buffer sizes - safe because all inputs are length-validated at config time
|
||||
// Format: prefix + "/" + type + "/" + object_id + "/" + suffix + null
|
||||
static constexpr size_t DEFAULT_TOPIC_MAX_LEN =
|
||||
TOPIC_PREFIX_MAX_LEN + 1 + MQTT_COMPONENT_TYPE_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 1 + MQTT_SUFFIX_MAX_LEN + 1;
|
||||
// Format: prefix + "/" + type + "/" + name + "/" + object_id + "/config" + null
|
||||
static constexpr size_t DISCOVERY_TOPIC_MAX_LEN = DISCOVERY_PREFIX_MAX_LEN + 1 + MQTT_COMPONENT_TYPE_MAX_LEN + 1 +
|
||||
ESPHOME_DEVICE_NAME_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 7 + 1;
|
||||
|
||||
// Function implementation of LOG_MQTT_COMPONENT macro to reduce code size
|
||||
void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic) {
|
||||
char buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (state_topic)
|
||||
ESP_LOGCONFIG(tag, " State Topic: '%s'", obj->get_state_topic_to_(buf).c_str());
|
||||
if (command_topic)
|
||||
ESP_LOGCONFIG(tag, " Command Topic: '%s'", obj->get_command_topic_to_(buf).c_str());
|
||||
}
|
||||
|
||||
void MQTTComponent::set_qos(uint8_t qos) { this->qos_ = qos; }
|
||||
|
||||
void MQTTComponent::set_subscribe_qos(uint8_t qos) { this->subscribe_qos_ = qos; }
|
||||
@@ -51,8 +48,7 @@ void MQTTComponent::set_subscribe_qos(uint8_t qos) { this->subscribe_qos_ = qos;
|
||||
void MQTTComponent::set_retain(bool retain) { this->retain_ = retain; }
|
||||
|
||||
std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discovery_info) const {
|
||||
char sanitized_name[ESPHOME_DEVICE_NAME_MAX_LEN + 1];
|
||||
str_sanitize_to(sanitized_name, App.get_name().c_str());
|
||||
std::string sanitized_name = str_sanitize(App.get_name());
|
||||
const char *comp_type = this->component_type();
|
||||
char object_id_buf[OBJECT_ID_MAX_LEN];
|
||||
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
|
||||
@@ -64,7 +60,7 @@ std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discove
|
||||
p = append_char(p, '/');
|
||||
p = append_str(p, comp_type, strlen(comp_type));
|
||||
p = append_char(p, '/');
|
||||
p = append_str(p, sanitized_name, strlen(sanitized_name));
|
||||
p = append_str(p, sanitized_name.data(), sanitized_name.size());
|
||||
p = append_char(p, '/');
|
||||
p = append_str(p, object_id.c_str(), object_id.size());
|
||||
p = append_str(p, "/config", 7);
|
||||
@@ -72,18 +68,19 @@ std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discove
|
||||
return std::string(buf, p - buf);
|
||||
}
|
||||
|
||||
StringRef MQTTComponent::get_default_topic_for_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf, const char *suffix,
|
||||
size_t suffix_len) const {
|
||||
std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) const {
|
||||
const std::string &topic_prefix = global_mqtt_client->get_topic_prefix();
|
||||
if (topic_prefix.empty()) {
|
||||
return StringRef(); // Empty topic_prefix means no default topic
|
||||
// If the topic_prefix is null, the default topic should be null
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *comp_type = this->component_type();
|
||||
char object_id_buf[OBJECT_ID_MAX_LEN];
|
||||
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
|
||||
|
||||
char *p = buf.data();
|
||||
char buf[DEFAULT_TOPIC_MAX_LEN];
|
||||
char *p = buf;
|
||||
|
||||
p = append_str(p, topic_prefix.data(), topic_prefix.size());
|
||||
p = append_char(p, '/');
|
||||
@@ -91,44 +88,21 @@ StringRef MQTTComponent::get_default_topic_for_to_(std::span<char, MQTT_DEFAULT_
|
||||
p = append_char(p, '/');
|
||||
p = append_str(p, object_id.c_str(), object_id.size());
|
||||
p = append_char(p, '/');
|
||||
p = append_str(p, suffix, suffix_len);
|
||||
*p = '\0';
|
||||
p = append_str(p, suffix.data(), suffix.size());
|
||||
|
||||
return StringRef(buf.data(), p - buf.data());
|
||||
}
|
||||
|
||||
std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) const {
|
||||
char buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
StringRef ref = this->get_default_topic_for_to_(buf, suffix.data(), suffix.size());
|
||||
return std::string(ref.c_str(), ref.size());
|
||||
}
|
||||
|
||||
StringRef MQTTComponent::get_state_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const {
|
||||
if (this->custom_state_topic_.has_value()) {
|
||||
// Returns ref to existing data for static/value, uses buf only for lambda case
|
||||
return this->custom_state_topic_.ref_or_copy_to(buf.data(), buf.size());
|
||||
}
|
||||
return this->get_default_topic_for_to_(buf, "state", 5);
|
||||
}
|
||||
|
||||
StringRef MQTTComponent::get_command_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const {
|
||||
if (this->custom_command_topic_.has_value()) {
|
||||
// Returns ref to existing data for static/value, uses buf only for lambda case
|
||||
return this->custom_command_topic_.ref_or_copy_to(buf.data(), buf.size());
|
||||
}
|
||||
return this->get_default_topic_for_to_(buf, "command", 7);
|
||||
return std::string(buf, p - buf);
|
||||
}
|
||||
|
||||
std::string MQTTComponent::get_state_topic_() const {
|
||||
char buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
StringRef ref = this->get_state_topic_to_(buf);
|
||||
return std::string(ref.c_str(), ref.size());
|
||||
if (this->custom_state_topic_.has_value())
|
||||
return this->custom_state_topic_.value();
|
||||
return this->get_default_topic_for_("state");
|
||||
}
|
||||
|
||||
std::string MQTTComponent::get_command_topic_() const {
|
||||
char buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
StringRef ref = this->get_command_topic_to_(buf);
|
||||
return std::string(ref.c_str(), ref.size());
|
||||
if (this->custom_command_topic_.has_value())
|
||||
return this->custom_command_topic_.value();
|
||||
return this->get_default_topic_for_("command");
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const std::string &payload) {
|
||||
@@ -193,14 +167,10 @@ bool MQTTComponent::send_discovery_() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.state_topic) {
|
||||
char state_topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
root[MQTT_STATE_TOPIC] = this->get_state_topic_to_(state_topic_buf);
|
||||
}
|
||||
if (config.command_topic) {
|
||||
char command_topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
root[MQTT_COMMAND_TOPIC] = this->get_command_topic_to_(command_topic_buf);
|
||||
}
|
||||
if (config.state_topic)
|
||||
root[MQTT_STATE_TOPIC] = this->get_state_topic_();
|
||||
if (config.command_topic)
|
||||
root[MQTT_COMMAND_TOPIC] = this->get_command_topic_();
|
||||
if (this->command_retain_)
|
||||
root[MQTT_COMMAND_RETAIN] = true;
|
||||
|
||||
@@ -219,7 +189,8 @@ bool MQTTComponent::send_discovery_() {
|
||||
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
|
||||
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
|
||||
char friendly_name_hash[9];
|
||||
snprintf(friendly_name_hash, sizeof(friendly_name_hash), "%08" PRIx32, fnv1_hash(this->friendly_name_()));
|
||||
sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name_()));
|
||||
friendly_name_hash[8] = 0; // ensure the hash-string ends with null
|
||||
// Format: mac-component_type-hash (e.g. "aabbccddeeff-sensor-12345678")
|
||||
// MAC (12) + "-" (1) + domain (max 20) + "-" (1) + hash (8) + null (1) = 43
|
||||
char unique_id[MAC_ADDRESS_BUFFER_SIZE + ESPHOME_DOMAIN_MAX_LEN + 11];
|
||||
@@ -317,9 +288,7 @@ void MQTTComponent::set_availability(std::string topic, std::string payload_avai
|
||||
}
|
||||
void MQTTComponent::disable_availability() { this->set_availability("", "", ""); }
|
||||
void MQTTComponent::call_setup() {
|
||||
// Cache is_internal result once during setup - topics don't change after this
|
||||
this->is_internal_ = this->compute_is_internal_();
|
||||
if (this->is_internal_)
|
||||
if (this->is_internal())
|
||||
return;
|
||||
|
||||
this->setup();
|
||||
@@ -375,28 +344,26 @@ StringRef MQTTComponent::get_default_object_id_to_(std::span<char, OBJECT_ID_MAX
|
||||
}
|
||||
StringRef MQTTComponent::get_icon_ref_() const { return this->get_entity()->get_icon_ref(); }
|
||||
bool MQTTComponent::is_disabled_by_default_() const { return this->get_entity()->is_disabled_by_default(); }
|
||||
bool MQTTComponent::compute_is_internal_() {
|
||||
bool MQTTComponent::is_internal() {
|
||||
if (this->custom_state_topic_.has_value()) {
|
||||
// If the custom state_topic is empty, return true as it is internal and should not publish
|
||||
// If the custom state_topic is null, return true as it is internal and should not publish
|
||||
// else, return false, as it is explicitly set to a topic, so it is not internal and should publish
|
||||
// Using is_empty() avoids heap allocation for non-lambda cases
|
||||
return this->custom_state_topic_.is_empty();
|
||||
return this->get_state_topic_().empty();
|
||||
}
|
||||
|
||||
if (this->custom_command_topic_.has_value()) {
|
||||
// If the custom command_topic is empty, return true as it is internal and should not publish
|
||||
// If the custom command_topic is null, return true as it is internal and should not publish
|
||||
// else, return false, as it is explicitly set to a topic, so it is not internal and should publish
|
||||
// Using is_empty() avoids heap allocation for non-lambda cases
|
||||
return this->custom_command_topic_.is_empty();
|
||||
return this->get_command_topic_().empty();
|
||||
}
|
||||
|
||||
// No custom topics have been set - check topic_prefix directly to avoid allocation
|
||||
if (global_mqtt_client->get_topic_prefix().empty()) {
|
||||
// If the default topic prefix is empty, then the component, by default, is internal and should not publish
|
||||
// No custom topics have been set
|
||||
if (this->get_default_topic_for_("").empty()) {
|
||||
// If the default topic prefix is null, then the component, by default, is internal and should not publish
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use ESPHome's component internal state if topic_prefix is not empty with no custom state_topic or command_topic
|
||||
// Use ESPHome's component internal state if topic_prefix is not null with no custom state_topic or command_topic
|
||||
return this->get_entity()->is_internal();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,22 +20,17 @@ struct SendDiscoveryConfig {
|
||||
bool command_topic{true}; ///< If the command topic should be included. Default to true.
|
||||
};
|
||||
|
||||
// Max lengths for stack-based topic building.
|
||||
// These limits are enforced at Python config validation time in mqtt/__init__.py
|
||||
// using cv.Length() validators for topic_prefix and discovery_prefix.
|
||||
// This ensures the stack buffers are always large enough.
|
||||
// Max lengths for stack-based topic building (must match mqtt_component.cpp)
|
||||
static constexpr size_t MQTT_COMPONENT_TYPE_MAX_LEN = 20;
|
||||
static constexpr size_t MQTT_SUFFIX_MAX_LEN = 32;
|
||||
static constexpr size_t MQTT_TOPIC_PREFIX_MAX_LEN = 64; // Validated in Python: cv.Length(max=64)
|
||||
// Stack buffer size - safe because all inputs are length-validated at config time
|
||||
// Format: prefix + "/" + type + "/" + object_id + "/" + suffix + null
|
||||
static constexpr size_t MQTT_DEFAULT_TOPIC_MAX_LEN =
|
||||
MQTT_TOPIC_PREFIX_MAX_LEN + 1 + MQTT_COMPONENT_TYPE_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 1 + MQTT_SUFFIX_MAX_LEN + 1;
|
||||
|
||||
class MQTTComponent; // Forward declaration
|
||||
void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic);
|
||||
|
||||
#define LOG_MQTT_COMPONENT(state_topic, command_topic) log_mqtt_component(TAG, this, state_topic, command_topic)
|
||||
#define LOG_MQTT_COMPONENT(state_topic, command_topic) \
|
||||
if (state_topic) { \
|
||||
ESP_LOGCONFIG(TAG, " State Topic: '%s'", this->get_state_topic_().c_str()); \
|
||||
} \
|
||||
if (command_topic) { \
|
||||
ESP_LOGCONFIG(TAG, " Command Topic: '%s'", this->get_command_topic_().c_str()); \
|
||||
}
|
||||
|
||||
// Macro to define component_type() with compile-time length verification
|
||||
// Usage: MQTT_COMPONENT_TYPE(MQTTSensorComponent, "sensor")
|
||||
@@ -79,8 +74,6 @@ void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, b
|
||||
* a clean separation.
|
||||
*/
|
||||
class MQTTComponent : public Component {
|
||||
friend void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic);
|
||||
|
||||
public:
|
||||
/// Constructs a MQTTComponent.
|
||||
explicit MQTTComponent();
|
||||
@@ -97,8 +90,7 @@ class MQTTComponent : public Component {
|
||||
|
||||
virtual bool send_initial_state() = 0;
|
||||
|
||||
/// Returns cached is_internal result (computed once during setup).
|
||||
bool is_internal() const { return this->is_internal_; }
|
||||
virtual bool is_internal();
|
||||
|
||||
/// Set QOS for state messages.
|
||||
void set_qos(uint8_t qos);
|
||||
@@ -186,16 +178,7 @@ class MQTTComponent : public Component {
|
||||
/// Helper method to get the discovery topic for this component.
|
||||
std::string get_discovery_topic_(const MQTTDiscoveryInfo &discovery_info) const;
|
||||
|
||||
/** Get this components state/command/... topic into a buffer.
|
||||
*
|
||||
* @param buf The buffer to write to (must be exactly MQTT_DEFAULT_TOPIC_MAX_LEN).
|
||||
* @param suffix The suffix/key such as "state" or "command".
|
||||
* @return StringRef pointing to the buffer with the topic.
|
||||
*/
|
||||
StringRef get_default_topic_for_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf, const char *suffix,
|
||||
size_t suffix_len) const;
|
||||
|
||||
/** Get this components state/command/... topic (allocates std::string).
|
||||
/** Get this components state/command/... topic.
|
||||
*
|
||||
* @param suffix The suffix/key such as "state" or "command".
|
||||
* @return The full topic.
|
||||
@@ -216,20 +199,10 @@ class MQTTComponent : public Component {
|
||||
/// Get whether the underlying Entity is disabled by default
|
||||
bool is_disabled_by_default_() const;
|
||||
|
||||
/// Get the MQTT state topic into a buffer (no heap allocation for non-lambda custom topics).
|
||||
/// @param buf Buffer of exactly MQTT_DEFAULT_TOPIC_MAX_LEN bytes.
|
||||
/// @return StringRef pointing to the topic in the buffer.
|
||||
StringRef get_state_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const;
|
||||
|
||||
/// Get the MQTT command topic into a buffer (no heap allocation for non-lambda custom topics).
|
||||
/// @param buf Buffer of exactly MQTT_DEFAULT_TOPIC_MAX_LEN bytes.
|
||||
/// @return StringRef pointing to the topic in the buffer.
|
||||
StringRef get_command_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const;
|
||||
|
||||
/// Get the MQTT topic that new states will be shared to (allocates std::string).
|
||||
/// Get the MQTT topic that new states will be shared to.
|
||||
std::string get_state_topic_() const;
|
||||
|
||||
/// Get the MQTT topic for listening to commands (allocates std::string).
|
||||
/// Get the MQTT topic for listening to commands.
|
||||
std::string get_command_topic_() const;
|
||||
|
||||
bool is_connected_() const;
|
||||
@@ -247,18 +220,12 @@ class MQTTComponent : public Component {
|
||||
|
||||
std::unique_ptr<Availability> availability_;
|
||||
|
||||
// Packed bitfields - QoS values are 0-2, bools are flags
|
||||
uint8_t qos_ : 2 {0};
|
||||
uint8_t subscribe_qos_ : 2 {0};
|
||||
bool command_retain_ : 1 {false};
|
||||
bool retain_ : 1 {true};
|
||||
bool discovery_enabled_ : 1 {true};
|
||||
bool resend_state_ : 1 {false};
|
||||
bool is_internal_ : 1 {false}; ///< Cached result of compute_is_internal_(), set during setup
|
||||
|
||||
/// Compute is_internal status based on topics and entity state.
|
||||
/// Called once during setup to cache the result.
|
||||
bool compute_is_internal_();
|
||||
bool command_retain_{false};
|
||||
bool retain_{true};
|
||||
uint8_t qos_{0};
|
||||
uint8_t subscribe_qos_{0};
|
||||
bool discovery_enabled_{true};
|
||||
bool resend_state_{false};
|
||||
};
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -51,7 +51,7 @@ void MQTTCoverComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT cover '%s':", this->cover_->get_name().c_str());
|
||||
auto traits = this->cover_->get_traits();
|
||||
bool has_command_topic = traits.get_supports_position() || !traits.get_supports_tilt();
|
||||
LOG_MQTT_COMPONENT(true, has_command_topic);
|
||||
LOG_MQTT_COMPONENT(true, has_command_topic)
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Position State Topic: '%s'\n"
|
||||
|
||||
@@ -36,7 +36,7 @@ void MQTTDateComponent::setup() {
|
||||
|
||||
void MQTTDateComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Date '%s':", this->date_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTDateComponent, "date")
|
||||
|
||||
@@ -47,7 +47,7 @@ void MQTTDateTimeComponent::setup() {
|
||||
|
||||
void MQTTDateTimeComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT DateTime '%s':", this->datetime_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTDateTimeComponent, "datetime")
|
||||
|
||||
@@ -90,7 +90,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery
|
||||
bool MQTTJSONLightComponent::send_initial_state() { return this->publish_state_(); }
|
||||
void MQTTJSONLightComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Light '%s':", this->state_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -30,7 +30,7 @@ void MQTTNumberComponent::setup() {
|
||||
|
||||
void MQTTNumberComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Number '%s':", this->number_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
LOG_MQTT_COMPONENT(true, false)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTNumberComponent, "number")
|
||||
|
||||
@@ -25,7 +25,7 @@ void MQTTSelectComponent::setup() {
|
||||
|
||||
void MQTTSelectComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Select '%s':", this->select_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
LOG_MQTT_COMPONENT(true, false)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTSelectComponent, "select")
|
||||
|
||||
@@ -28,7 +28,7 @@ void MQTTSensorComponent::dump_config() {
|
||||
if (this->get_expire_after() > 0) {
|
||||
ESP_LOGCONFIG(TAG, " Expire After: %" PRIu32 "s", this->get_expire_after() / 1000);
|
||||
}
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
LOG_MQTT_COMPONENT(true, false)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTSensorComponent, "sensor")
|
||||
|
||||
@@ -26,7 +26,7 @@ void MQTTTextComponent::setup() {
|
||||
|
||||
void MQTTTextComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT text '%s':", this->text_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTTextComponent, "text")
|
||||
|
||||
@@ -36,7 +36,7 @@ void MQTTTimeComponent::setup() {
|
||||
|
||||
void MQTTTimeComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Time '%s':", this->time_->get_name().c_str());
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
LOG_MQTT_COMPONENT(true, true)
|
||||
}
|
||||
|
||||
MQTT_COMPONENT_TYPE(MQTTTimeComponent, "time")
|
||||
|
||||
@@ -39,7 +39,7 @@ void MQTTValveComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT valve '%s':", this->valve_->get_name().c_str());
|
||||
auto traits = this->valve_->get_traits();
|
||||
bool has_command_topic = traits.get_supports_position();
|
||||
LOG_MQTT_COMPONENT(true, has_command_topic);
|
||||
LOG_MQTT_COMPONENT(true, has_command_topic)
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Position State Topic: '%s'\n"
|
||||
|
||||
@@ -43,14 +43,6 @@ namespace network {
|
||||
/// Buffer size for IP address string (IPv6 max: 39 chars + null)
|
||||
static constexpr size_t IP_ADDRESS_BUFFER_SIZE = 40;
|
||||
|
||||
/// Lowercase hex digits in IP address string (A-F -> a-f for IPv6 per RFC 5952)
|
||||
inline void lowercase_ip_str(char *buf) {
|
||||
for (char *p = buf; *p; ++p) {
|
||||
if (*p >= 'A' && *p <= 'F')
|
||||
*p += 32;
|
||||
}
|
||||
}
|
||||
|
||||
struct IPAddress {
|
||||
public:
|
||||
#ifdef USE_HOST
|
||||
@@ -60,15 +52,10 @@ struct IPAddress {
|
||||
}
|
||||
IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); }
|
||||
IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; }
|
||||
std::string str() const {
|
||||
char buf[IP_ADDRESS_BUFFER_SIZE];
|
||||
this->str_to(buf);
|
||||
return buf;
|
||||
}
|
||||
std::string str() const { return str_lower_case(inet_ntoa(ip_addr_)); }
|
||||
/// Write IP address to buffer. Buffer must be at least IP_ADDRESS_BUFFER_SIZE bytes.
|
||||
char *str_to(char *buf) const {
|
||||
inet_ntop(AF_INET, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE);
|
||||
return buf; // IPv4 only, no hex letters to lowercase
|
||||
return const_cast<char *>(inet_ntop(AF_INET, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE));
|
||||
}
|
||||
#else
|
||||
IPAddress() { ip_addr_set_zero(&ip_addr_); }
|
||||
@@ -147,18 +134,9 @@ struct IPAddress {
|
||||
bool is_ip4() const { return IP_IS_V4(&ip_addr_); }
|
||||
bool is_ip6() const { return IP_IS_V6(&ip_addr_); }
|
||||
bool is_multicast() const { return ip_addr_ismulticast(&ip_addr_); }
|
||||
std::string str() const {
|
||||
char buf[IP_ADDRESS_BUFFER_SIZE];
|
||||
this->str_to(buf);
|
||||
return buf;
|
||||
}
|
||||
std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); }
|
||||
/// Write IP address to buffer. Buffer must be at least IP_ADDRESS_BUFFER_SIZE bytes.
|
||||
/// Output is lowercased per RFC 5952 (IPv6 hex digits a-f).
|
||||
char *str_to(char *buf) const {
|
||||
ipaddr_ntoa_r(&ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE);
|
||||
lowercase_ip_str(buf);
|
||||
return buf;
|
||||
}
|
||||
char *str_to(char *buf) const { return ipaddr_ntoa_r(&ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE); }
|
||||
bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
||||
bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
||||
IPAddress &operator+=(uint8_t increase) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "nextion.h"
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
@@ -1284,9 +1283,8 @@ void Nextion::check_pending_waveform_() {
|
||||
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
|
||||
: 255; // ADDT command can only send 255
|
||||
|
||||
char command[24]; // "addt " + uint8 + "," + uint8 + "," + uint8 + null = max 17 chars
|
||||
buf_append_printf(command, sizeof(command), 0, "addt %u,%u,%zu", component->get_component_id(),
|
||||
component->get_wave_channel_id(), buffer_to_send);
|
||||
std::string command = "addt " + to_string(component->get_component_id()) + "," +
|
||||
to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send);
|
||||
if (!this->send_command_(command)) {
|
||||
delete nb; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->waveform_queue_.pop_front();
|
||||
|
||||
@@ -34,7 +34,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
}
|
||||
|
||||
char range_header[32];
|
||||
buf_append_printf(range_header, sizeof(range_header), 0, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
|
||||
sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
|
||||
ESP_LOGV(TAG, "Range: %s", range_header);
|
||||
http_client.addHeader("Range", range_header);
|
||||
int code = http_client.GET();
|
||||
|
||||
@@ -36,7 +36,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r
|
||||
}
|
||||
|
||||
char range_header[32];
|
||||
buf_append_printf(range_header, sizeof(range_header), 0, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
|
||||
sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
|
||||
ESP_LOGV(TAG, "Range: %s", range_header);
|
||||
esp_http_client_set_header(http_client, "Range", range_header);
|
||||
ESP_LOGV(TAG, "Open HTTP");
|
||||
|
||||
@@ -23,7 +23,7 @@ void NTC::process_(float value) {
|
||||
double v = this->a_ + this->b_ * lr + this->c_ * lr * lr * lr;
|
||||
auto temp = float(1.0 / v - 273.15);
|
||||
|
||||
ESP_LOGV(TAG, "'%s' - Temperature: %.1f°C", this->name_.c_str(), temp);
|
||||
ESP_LOGD(TAG, "'%s' - Temperature: %.1f°C", this->name_.c_str(), temp);
|
||||
this->publish_state(temp);
|
||||
}
|
||||
|
||||
|
||||
@@ -561,9 +561,8 @@ const char *OpenTherm::message_id_to_str(MessageId id) {
|
||||
}
|
||||
|
||||
void OpenTherm::debug_data(OpenthermData &data) {
|
||||
char type_buf[9], id_buf[9], hb_buf[9], lb_buf[9];
|
||||
ESP_LOGD(TAG, "%s %s %s %s", format_bin_to(type_buf, data.type), format_bin_to(id_buf, data.id),
|
||||
format_bin_to(hb_buf, data.valueHB), format_bin_to(lb_buf, data.valueLB));
|
||||
ESP_LOGD(TAG, "%s %s %s %s", format_bin(data.type).c_str(), format_bin(data.id).c_str(),
|
||||
format_bin(data.valueHB).c_str(), format_bin(data.valueLB).c_str());
|
||||
ESP_LOGD(TAG, "type: %s; id: %u; HB: %u; LB: %u; uint_16: %u; float: %f",
|
||||
this->message_type_to_str((MessageType) data.type), data.id, data.valueHB, data.valueLB, data.u16(),
|
||||
data.f88());
|
||||
|
||||
@@ -181,7 +181,7 @@ void PCA6416AGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this
|
||||
bool PCA6416AGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void PCA6416AGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t PCA6416AGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via PCA6416A", this->pin_);
|
||||
return snprintf(buffer, len, "%u via PCA6416A", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace pca6416a
|
||||
|
||||
@@ -130,7 +130,7 @@ void PCA9554GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this-
|
||||
bool PCA9554GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void PCA9554GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t PCA9554GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via PCA9554", this->pin_);
|
||||
return snprintf(buffer, len, "%u via PCA9554", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace pca9554
|
||||
|
||||
@@ -107,7 +107,7 @@ void PCF8574GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this-
|
||||
bool PCF8574GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void PCF8574GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t PCF8574GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via PCF8574", this->pin_);
|
||||
return snprintf(buffer, len, "%u via PCF8574", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace pcf8574
|
||||
|
||||
@@ -165,7 +165,7 @@ void PI4IOE5V6408GPIOPin::digital_write(bool value) {
|
||||
this->parent_->digital_write(this->pin_, value != this->inverted_);
|
||||
}
|
||||
size_t PI4IOE5V6408GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via PI4IOE5V6408", this->pin_);
|
||||
return snprintf(buffer, len, "%u via PI4IOE5V6408", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace pi4ioe5v6408
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace pipsolar {
|
||||
static const char *const TAG = "pipsolar.output";
|
||||
|
||||
void PipsolarOutput::write_state(float state) {
|
||||
char tmp[16];
|
||||
snprintf(tmp, sizeof(tmp), this->set_command_, state);
|
||||
char tmp[10];
|
||||
sprintf(tmp, this->set_command_.c_str(), state);
|
||||
|
||||
if (std::find(this->possible_values_.begin(), this->possible_values_.end(), state) != this->possible_values_.end()) {
|
||||
ESP_LOGD(TAG, "Will write: %s out of value %f / %02.0f", tmp, state, state);
|
||||
|
||||
@@ -15,15 +15,13 @@ class PipsolarOutput : public output::FloatOutput {
|
||||
public:
|
||||
PipsolarOutput() {}
|
||||
void set_parent(Pipsolar *parent) { this->parent_ = parent; }
|
||||
void set_set_command(const char *command) { this->set_command_ = command; }
|
||||
/// Prevent accidental use of std::string which would dangle
|
||||
void set_set_command(const std::string &command) = delete;
|
||||
void set_set_command(const std::string &command) { this->set_command_ = command; };
|
||||
void set_possible_values(std::vector<float> possible_values) { this->possible_values_ = std::move(possible_values); }
|
||||
void set_value(float value) { this->write_state(value); }
|
||||
void set_value(float value) { this->write_state(value); };
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
const char *set_command_{nullptr};
|
||||
std::string set_command_;
|
||||
Pipsolar *parent_;
|
||||
std::vector<float> possible_values_;
|
||||
};
|
||||
|
||||
@@ -9,9 +9,14 @@ static const char *const TAG = "pipsolar.switch";
|
||||
|
||||
void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); }
|
||||
void PipsolarSwitch::write_state(bool state) {
|
||||
const char *command = state ? this->on_command_ : this->off_command_;
|
||||
if (command != nullptr) {
|
||||
this->parent_->queue_command(command);
|
||||
if (state) {
|
||||
if (!this->on_command_.empty()) {
|
||||
this->parent_->queue_command(this->on_command_);
|
||||
}
|
||||
} else {
|
||||
if (!this->off_command_.empty()) {
|
||||
this->parent_->queue_command(this->off_command_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,15 @@ namespace pipsolar {
|
||||
class Pipsolar;
|
||||
class PipsolarSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void set_parent(Pipsolar *parent) { this->parent_ = parent; }
|
||||
void set_on_command(const char *command) { this->on_command_ = command; }
|
||||
void set_off_command(const char *command) { this->off_command_ = command; }
|
||||
/// Prevent accidental use of std::string which would dangle
|
||||
void set_on_command(const std::string &command) = delete;
|
||||
void set_off_command(const std::string &command) = delete;
|
||||
void set_parent(Pipsolar *parent) { this->parent_ = parent; };
|
||||
void set_on_command(const std::string &command) { this->on_command_ = command; };
|
||||
void set_off_command(const std::string &command) { this->off_command_ = command; };
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
const char *on_command_{nullptr};
|
||||
const char *off_command_{nullptr};
|
||||
std::string on_command_;
|
||||
std::string off_command_;
|
||||
Pipsolar *parent_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "rc522_spi.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
// Based on:
|
||||
@@ -71,7 +70,7 @@ void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
||||
index++;
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
buf_append_printf(cstrb, sizeof(cstrb), 0, " %x", values[0]);
|
||||
sprintf(cstrb, " %x", values[0]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
}
|
||||
@@ -79,7 +78,7 @@ void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
||||
values[index] = transfer_byte(address); // Read value and tell that we want to read the same address again.
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
buf_append_printf(cstrb, sizeof(cstrb), 0, " %x", values[index]);
|
||||
sprintf(cstrb, " %x", values[index]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
|
||||
@@ -89,7 +88,7 @@ void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read fro
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
buf = buf + " ";
|
||||
buf_append_printf(cstrb, sizeof(cstrb), 0, "%x", values[index]);
|
||||
sprintf(cstrb, "%x", values[index]);
|
||||
buf.append(cstrb);
|
||||
|
||||
ESP_LOGVV(TAG, "read_register_array_(%x, %d, , %d) -> %s", reg, count, rx_align, buf.c_str());
|
||||
@@ -128,7 +127,7 @@ void RC522Spi::pcd_write_register(PcdRegister reg, ///< The register to write t
|
||||
transfer_byte(values[index]);
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
buf_append_printf(cstrb, sizeof(cstrb), 0, " %x", values[index]);
|
||||
sprintf(cstrb, " %x", values[index]);
|
||||
buf.append(cstrb);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ optional<AEHAData> AEHAProtocol::decode(RemoteReceiveData src) {
|
||||
std::string AEHAProtocol::format_data_(const std::vector<uint8_t> &data) {
|
||||
std::string out;
|
||||
for (uint8_t byte : data) {
|
||||
char buf[8]; // "0x%02X," = 5 chars + null + margin
|
||||
snprintf(buf, sizeof(buf), "0x%02X,", byte);
|
||||
char buf[6];
|
||||
sprintf(buf, "0x%02X,", byte);
|
||||
out += buf;
|
||||
}
|
||||
out.pop_back();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "raw_protocol.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -9,30 +8,36 @@ static const char *const TAG = "remote.raw";
|
||||
|
||||
bool RawDumper::dump(RemoteReceiveData src) {
|
||||
char buffer[256];
|
||||
size_t pos = buf_append_printf(buffer, sizeof(buffer), 0, "Received Raw: ");
|
||||
uint32_t buffer_offset = 0;
|
||||
buffer_offset += sprintf(buffer, "Received Raw: ");
|
||||
|
||||
for (int32_t i = 0; i < src.size() - 1; i++) {
|
||||
const int32_t value = src[i];
|
||||
size_t prev_pos = pos;
|
||||
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
||||
int written;
|
||||
|
||||
if (i + 1 < src.size() - 1) {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32 ", ", value);
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32 ", ", value);
|
||||
} else {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32, value);
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32, value);
|
||||
}
|
||||
|
||||
if (pos >= sizeof(buffer) - 1) {
|
||||
// buffer full, flush and continue
|
||||
buffer[prev_pos] = '\0';
|
||||
if (written < 0 || written >= int(remaining_length)) {
|
||||
// write failed, flush...
|
||||
buffer[buffer_offset] = '\0';
|
||||
ESP_LOGI(TAG, "%s", buffer);
|
||||
buffer_offset = 0;
|
||||
written = sprintf(buffer, " ");
|
||||
if (i + 1 < src.size() - 1) {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32 ", ", value);
|
||||
written += sprintf(buffer + written, "%" PRId32 ", ", value);
|
||||
} else {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32, value);
|
||||
written += sprintf(buffer + written, "%" PRId32, value);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_offset += written;
|
||||
}
|
||||
if (pos != 0) {
|
||||
if (buffer_offset != 0) {
|
||||
ESP_LOGI(TAG, "%s", buffer);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "remote_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
@@ -158,8 +159,8 @@ void RemoteTransmitData::set_data_from_packed_sint32(const uint8_t *data, size_t
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteTransmitData::set_data_from_base64url(const std::string &base64url) {
|
||||
return base64_decode_int32_vector(base64url, this->data_);
|
||||
bool RemoteTransmitData::set_data_from_base85(const std::string &base85) {
|
||||
return base85_decode_int32_vector(base85, this->data_);
|
||||
}
|
||||
|
||||
/* RemoteTransmitterBase */
|
||||
@@ -168,31 +169,36 @@ void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
const auto &vec = this->temp_.get_data();
|
||||
char buffer[256];
|
||||
size_t pos = buf_append_printf(buffer, sizeof(buffer), 0,
|
||||
"Sending times=%" PRIu32 " wait=%" PRIu32 "ms: ", send_times, send_wait);
|
||||
uint32_t buffer_offset = 0;
|
||||
buffer_offset += sprintf(buffer, "Sending times=%" PRIu32 " wait=%" PRIu32 "ms: ", send_times, send_wait);
|
||||
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
const int32_t value = vec[i];
|
||||
size_t prev_pos = pos;
|
||||
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
||||
int written;
|
||||
|
||||
if (i + 1 < vec.size()) {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32 ", ", value);
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32 ", ", value);
|
||||
} else {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32, value);
|
||||
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32, value);
|
||||
}
|
||||
|
||||
if (pos >= sizeof(buffer) - 1) {
|
||||
// buffer full, flush and continue
|
||||
buffer[prev_pos] = '\0';
|
||||
if (written < 0 || written >= int(remaining_length)) {
|
||||
// write failed, flush...
|
||||
buffer[buffer_offset] = '\0';
|
||||
ESP_LOGVV(TAG, "%s", buffer);
|
||||
buffer_offset = 0;
|
||||
written = sprintf(buffer, " ");
|
||||
if (i + 1 < vec.size()) {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32 ", ", value);
|
||||
written += sprintf(buffer + written, "%" PRId32 ", ", value);
|
||||
} else {
|
||||
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32, value);
|
||||
written += sprintf(buffer + written, "%" PRId32, value);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_offset += written;
|
||||
}
|
||||
if (pos != 0) {
|
||||
if (buffer_offset != 0) {
|
||||
ESP_LOGVV(TAG, "%s", buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -36,11 +36,11 @@ class RemoteTransmitData {
|
||||
/// @param len Length of the buffer in bytes
|
||||
/// @param count Number of values (for reserve optimization)
|
||||
void set_data_from_packed_sint32(const uint8_t *data, size_t len, size_t count);
|
||||
/// Set data from base64url-encoded little-endian int32 values
|
||||
/// Base64url is URL-safe: uses '-' instead of '+', '_' instead of '/'
|
||||
/// @param base64url Base64url-encoded string of little-endian int32 values
|
||||
/// Set data from base85-encoded int32 values
|
||||
/// Decodes directly into internal buffer (zero heap allocations)
|
||||
/// @param base85 Base85-encoded string (5 chars per int32 value)
|
||||
/// @return true if successful, false if decode failed or invalid size
|
||||
bool set_data_from_base64url(const std::string &base64url);
|
||||
bool set_data_from_base85(const std::string &base85);
|
||||
void reset() {
|
||||
this->data_.clear();
|
||||
this->carrier_frequency_ = 0;
|
||||
|
||||
@@ -39,7 +39,7 @@ void ResistanceSensor::process_(float value) {
|
||||
}
|
||||
|
||||
res *= this->resistor_;
|
||||
ESP_LOGV(TAG, "'%s' - Resistance %.1fΩ", this->name_.c_str(), res);
|
||||
ESP_LOGD(TAG, "'%s' - Resistance %.1fΩ", this->name_.c_str(), res);
|
||||
this->publish_state(res);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,20 +8,17 @@ from esphome.const import (
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_INDEX,
|
||||
CONF_LAMBDA,
|
||||
CONF_MODE,
|
||||
CONF_MQTT_ID,
|
||||
CONF_ON_VALUE,
|
||||
CONF_OPERATION,
|
||||
CONF_OPTION,
|
||||
CONF_OPTIONS,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WEB_SERVER,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||
from esphome.cpp_generator import MockObjClass, TemplateArguments
|
||||
from esphome.cpp_types import global_ns
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
@@ -33,7 +30,7 @@ SelectPtr = Select.operator("ptr")
|
||||
# Triggers
|
||||
SelectStateTrigger = select_ns.class_(
|
||||
"SelectStateTrigger",
|
||||
automation.Trigger.template(cg.StringRef, cg.size_t),
|
||||
automation.Trigger.template(cg.std_string, cg.size_t),
|
||||
)
|
||||
|
||||
# Actions
|
||||
@@ -41,9 +38,6 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action)
|
||||
SelectSetIndexAction = select_ns.class_("SelectSetIndexAction", automation.Action)
|
||||
SelectOperationAction = select_ns.class_("SelectOperationAction", automation.Action)
|
||||
|
||||
# Conditions
|
||||
SelectIsCondition = select_ns.class_("SelectIsCondition", automation.Condition)
|
||||
|
||||
# Enums
|
||||
SelectOperation = select_ns.enum("SelectOperation")
|
||||
SELECT_OPERATION_OPTIONS = {
|
||||
@@ -100,7 +94,7 @@ async def setup_select_core_(var, config, *, options: list[str]):
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.StringRef, "x"), (cg.size_t, "i")], conf
|
||||
trigger, [(cg.std_string, "x"), (cg.size_t, "i")], conf
|
||||
)
|
||||
|
||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||
@@ -171,41 +165,6 @@ async def select_set_index_to_code(config, action_id, template_arg, args):
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"select.is",
|
||||
SelectIsCondition,
|
||||
OPERATION_BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(CONF_OPTIONS): cv.All(
|
||||
cv.ensure_list(cv.string_strict), cv.Length(min=1)
|
||||
),
|
||||
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||
}
|
||||
).add_extra(cv.has_exactly_one_key(CONF_OPTIONS, CONF_LAMBDA)),
|
||||
)
|
||||
async def select_is_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
if options := config.get(CONF_OPTIONS):
|
||||
# List of constant options
|
||||
# Create a constexpr and pass that with a template length
|
||||
arr_id = ID(
|
||||
f"{condition_id}_data",
|
||||
is_declaration=True,
|
||||
type=global_ns.namespace("constexpr char * const"),
|
||||
)
|
||||
arg = cg.static_const_array(arr_id, cg.ArrayInitializer(*options))
|
||||
template_arg = TemplateArguments(len(options), *template_arg)
|
||||
else:
|
||||
# Lambda
|
||||
arg = await cg.process_lambda(
|
||||
config[CONF_LAMBDA],
|
||||
[(global_ns.namespace("StringRef &").operator("const"), "current")] + args,
|
||||
return_type=cg.bool_,
|
||||
)
|
||||
template_arg = TemplateArguments(0, *template_arg)
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren, arg)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"select.operation",
|
||||
SelectOperationAction,
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
namespace esphome::select {
|
||||
|
||||
class SelectStateTrigger : public Trigger<StringRef, size_t> {
|
||||
class SelectStateTrigger : public Trigger<std::string, size_t> {
|
||||
public:
|
||||
explicit SelectStateTrigger(Select *parent) : parent_(parent) {
|
||||
parent->add_on_state_callback(
|
||||
[this](size_t index) { this->trigger(StringRef(this->parent_->option_at(index)), index); });
|
||||
[this](size_t index) { this->trigger(std::string(this->parent_->option_at(index)), index); });
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -66,34 +66,4 @@ template<typename... Ts> class SelectOperationAction : public Action<Ts...> {
|
||||
Select *select_;
|
||||
};
|
||||
|
||||
template<size_t N, typename... Ts> class SelectIsCondition : public Condition<Ts...> {
|
||||
public:
|
||||
SelectIsCondition(Select *parent, const char *const *option_list) : parent_(parent), option_list_(option_list) {}
|
||||
|
||||
bool check(const Ts &...x) override {
|
||||
auto current = this->parent_->current_option();
|
||||
for (size_t i = 0; i != N; i++) {
|
||||
if (current == this->option_list_[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
Select *parent_;
|
||||
const char *const *option_list_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SelectIsCondition<0, Ts...> : public Condition<Ts...> {
|
||||
public:
|
||||
SelectIsCondition(Select *parent, std::function<bool(const StringRef &, const Ts &...)> &&f)
|
||||
: parent_(parent), f_(f) {}
|
||||
|
||||
bool check(const Ts &...x) override { return this->f_(this->parent_->current_option(), x...); }
|
||||
|
||||
protected:
|
||||
Select *parent_;
|
||||
std::function<bool(const StringRef &, const Ts &...)> f_;
|
||||
};
|
||||
} // namespace esphome::select
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "sim800l.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
|
||||
@@ -51,8 +50,8 @@ void Sim800LComponent::update() {
|
||||
} else if (state_ == STATE_RECEIVED_SMS) {
|
||||
// Serial Buffer should have flushed.
|
||||
// Send cmd to delete received sms
|
||||
char delete_cmd[20]; // "AT+CMGD=" (8) + uint8_t (max 3) + null = 12 <= 20
|
||||
buf_append_printf(delete_cmd, sizeof(delete_cmd), 0, "AT+CMGD=%d", this->parse_index_);
|
||||
char delete_cmd[20];
|
||||
sprintf(delete_cmd, "AT+CMGD=%d", this->parse_index_);
|
||||
this->send_cmd_(delete_cmd);
|
||||
this->state_ = STATE_CHECK_SMS;
|
||||
this->expect_ack_ = true;
|
||||
|
||||
@@ -104,10 +104,7 @@ std::vector<ObisInfo> SmlFile::get_obis_info() {
|
||||
std::string bytes_repr(const BytesView &buffer) {
|
||||
std::string repr;
|
||||
for (auto const value : buffer) {
|
||||
// max 3: 2 hex digits + null
|
||||
char hex_buf[3];
|
||||
snprintf(hex_buf, sizeof(hex_buf), "%02x", static_cast<unsigned int>(value));
|
||||
repr += hex_buf;
|
||||
repr += str_sprintf("%02x", value & 0xff);
|
||||
}
|
||||
return repr;
|
||||
}
|
||||
@@ -149,11 +146,7 @@ ObisInfo::ObisInfo(const BytesView &server_id, const SmlNode &val_list_entry) :
|
||||
}
|
||||
|
||||
std::string ObisInfo::code_repr() const {
|
||||
// max 20: "255-255:255.255.255" (19 chars) + null
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3],
|
||||
this->code[4]);
|
||||
return buf;
|
||||
return str_sprintf("%d-%d:%d.%d.%d", this->code[0], this->code[1], this->code[2], this->code[3], this->code[4]);
|
||||
}
|
||||
|
||||
} // namespace sml
|
||||
|
||||
@@ -65,7 +65,7 @@ float SN74HC165Component::get_setup_priority() const { return setup_priority::IO
|
||||
bool SN74HC165GPIOPin::digital_read() { return this->parent_->digital_read_(this->pin_) != this->inverted_; }
|
||||
|
||||
size_t SN74HC165GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via SN74HC165", this->pin_);
|
||||
return snprintf(buffer, len, "%u via SN74HC165", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace sn74hc165
|
||||
|
||||
@@ -94,7 +94,7 @@ void SN74HC595GPIOPin::digital_write(bool value) {
|
||||
this->parent_->digital_write_(this->pin_, value != this->inverted_);
|
||||
}
|
||||
size_t SN74HC595GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via SN74HC595", this->pin_);
|
||||
return snprintf(buffer, len, "%u via SN74HC595", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace sn74hc595
|
||||
|
||||
@@ -107,9 +107,9 @@ std::unique_ptr<Socket> socket_ip_loop_monitored(int type, int protocol) {
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_address, uint16_t port) {
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
#if USE_NETWORK_IPV6
|
||||
if (strchr(ip_address, ':') != nullptr) {
|
||||
if (ip_address.find(':') != std::string::npos) {
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
@@ -121,14 +121,14 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_
|
||||
|
||||
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
// Use standard inet_pton for BSD sockets
|
||||
if (inet_pton(AF_INET6, ip_address, &server->sin6_addr) != 1) {
|
||||
if (inet_pton(AF_INET6, ip_address.c_str(), &server->sin6_addr) != 1) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// Use LWIP-specific functions
|
||||
ip6_addr_t ip6;
|
||||
inet6_aton(ip_address, &ip6);
|
||||
inet6_aton(ip_address.c_str(), &ip6);
|
||||
memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr));
|
||||
#endif
|
||||
return sizeof(sockaddr_in6);
|
||||
@@ -141,7 +141,7 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_
|
||||
auto *server = reinterpret_cast<sockaddr_in *>(addr);
|
||||
memset(server, 0, sizeof(sockaddr_in));
|
||||
server->sin_family = AF_INET;
|
||||
server->sin_addr.s_addr = inet_addr(ip_address);
|
||||
server->sin_addr.s_addr = inet_addr(ip_address.c_str());
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
@@ -87,17 +87,7 @@ std::unique_ptr<Socket> socket_loop_monitored(int domain, int type, int protocol
|
||||
std::unique_ptr<Socket> socket_ip_loop_monitored(int type, int protocol);
|
||||
|
||||
/// Set a sockaddr to the specified address and port for the IP version used by socket_ip().
|
||||
/// @param addr Destination sockaddr structure
|
||||
/// @param addrlen Size of the addr buffer
|
||||
/// @param ip_address Null-terminated IP address string (IPv4 or IPv6)
|
||||
/// @param port Port number in host byte order
|
||||
/// @return Size of the sockaddr structure used, or 0 on error
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_address, uint16_t port);
|
||||
|
||||
/// Convenience overload for std::string (backward compatible).
|
||||
inline socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
return set_sockaddr(addr, addrlen, ip_address.c_str(), port);
|
||||
}
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port);
|
||||
|
||||
/// Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
|
||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port);
|
||||
|
||||
@@ -7,14 +7,14 @@ DEPENDENCIES = ["network"]
|
||||
|
||||
status_ns = cg.esphome_ns.namespace("status")
|
||||
StatusBinarySensor = status_ns.class_(
|
||||
"StatusBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
|
||||
"StatusBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
|
||||
StatusBinarySensor,
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::status {
|
||||
namespace esphome {
|
||||
namespace status {
|
||||
|
||||
static const char *const TAG = "status";
|
||||
|
||||
void StatusBinarySensor::update() {
|
||||
void StatusBinarySensor::loop() {
|
||||
bool status = network::is_connected();
|
||||
#ifdef USE_MQTT
|
||||
if (mqtt::global_mqtt_client != nullptr) {
|
||||
@@ -32,4 +33,5 @@ void StatusBinarySensor::update() {
|
||||
void StatusBinarySensor::setup() { this->publish_initial_state(false); }
|
||||
void StatusBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Status Binary Sensor", this); }
|
||||
|
||||
} // namespace esphome::status
|
||||
} // namespace status
|
||||
} // namespace esphome
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome::status {
|
||||
namespace esphome {
|
||||
namespace status {
|
||||
|
||||
class StatusBinarySensor : public binary_sensor::BinarySensor, public PollingComponent {
|
||||
class StatusBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
void update() override;
|
||||
void loop() override;
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
@@ -15,4 +16,5 @@ class StatusBinarySensor : public binary_sensor::BinarySensor, public PollingCom
|
||||
bool is_status_binary_sensor() const override { return true; }
|
||||
};
|
||||
|
||||
} // namespace esphome::status
|
||||
} // namespace status
|
||||
} // namespace esphome
|
||||
|
||||
@@ -14,9 +14,7 @@ class SunTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
void set_parent(Sun *parent) { parent_ = parent; }
|
||||
void set_elevation(double elevation) { elevation_ = elevation; }
|
||||
void set_sunrise(bool sunrise) { sunrise_ = sunrise; }
|
||||
void set_format(const char *format) { this->format_ = format; }
|
||||
/// Prevent accidental use of std::string which would dangle
|
||||
void set_format(const std::string &format) = delete;
|
||||
void set_format(const std::string &format) { format_ = format; }
|
||||
|
||||
void update() override {
|
||||
optional<ESPTime> res;
|
||||
@@ -31,14 +29,14 @@ class SunTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||
}
|
||||
|
||||
char buf[ESPTime::STRFTIME_BUFFER_SIZE];
|
||||
size_t len = res->strftime_to(buf, this->format_);
|
||||
size_t len = res->strftime_to(buf, this->format_.c_str());
|
||||
this->publish_state(buf, len);
|
||||
}
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
const char *format_{nullptr};
|
||||
std::string format_{};
|
||||
Sun *parent_;
|
||||
double elevation_;
|
||||
bool sunrise_;
|
||||
|
||||
@@ -13,7 +13,7 @@ void SX1509GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->
|
||||
bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
size_t SX1509GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return buf_append_printf(buffer, len, 0, "%u via sx1509", this->pin_);
|
||||
return snprintf(buffer, len, "%u via sx1509", this->pin_);
|
||||
}
|
||||
|
||||
} // namespace sx1509
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user