mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 22:56:19 +00:00
Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbc5669efa | ||
|
|
718b04c15a | ||
|
|
24a8634a4b | ||
|
|
fc6664d737 | ||
|
|
388e411469 | ||
|
|
a8ebdcb8f2 | ||
|
|
f5f1c48f51 | ||
|
|
7c55de311f | ||
|
|
307faa6c38 | ||
|
|
5f2adcf9b3 | ||
|
|
7738464f0b | ||
|
|
5a86e26f68 | ||
|
|
786b47d8c2 | ||
|
|
83092ea05c | ||
|
|
629afd38f6 | ||
|
|
3ffc3a9610 | ||
|
|
9de7bd7461 | ||
|
|
5df922e0df | ||
|
|
b571d2a5ab | ||
|
|
231a2897c0 | ||
|
|
2223b14794 | ||
|
|
3d340f4d90 | ||
|
|
2f99466f3a | ||
|
|
f05e15522c | ||
|
|
f37dad683b | ||
|
|
a0fb14bf54 | ||
|
|
d4443be0c1 | ||
|
|
0dfb573e18 | ||
|
|
572fc033cd | ||
|
|
4062f0a323 | ||
|
|
cd40fb1c68 | ||
|
|
7afe7750cd | ||
|
|
e08bdf8cca | ||
|
|
b3172ecae8 | ||
|
|
2b4d9c0af9 | ||
|
|
3a10d2c187 | ||
|
|
05e2c6b133 | ||
|
|
2797349c75 | ||
|
|
95a01ac2ab | ||
|
|
ea01c909b7 | ||
|
|
d3655597ea |
@@ -465,7 +465,7 @@ jobs:
|
||||
echo "binary=$BINARY" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run CodSpeed benchmarks
|
||||
uses: CodSpeedHQ/action@f99becdce5e5d51fd556489ebef684f4ecfd6286 # v4.18.5
|
||||
uses: CodSpeedHQ/action@9f3a37ece7abc84992501a7fcd54d1704f3458fa # v4.18.4
|
||||
with:
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
@@ -504,15 +504,6 @@ jobs:
|
||||
options: --environment nrf52-tidy --grep USE_ZEPHYR --grep USE_NRF52
|
||||
cache_sdk_nrf: true
|
||||
ignore_errors: false
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for RP2
|
||||
options: --environment rp2-tidy --grep USE_RP2
|
||||
pio_cache_key: tidyrp2
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for LibreTiny
|
||||
environments: bk72xx-tidy ln882h-tidy rtl87xxb-tidy rtl87xxc-tidy
|
||||
options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X
|
||||
pio_cache_key: tidylibretiny
|
||||
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
@@ -576,21 +567,10 @@ jobs:
|
||||
. venv/bin/activate
|
||||
if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then
|
||||
echo "Running FULL clang-tidy scan (reason: ${{ steps.check_full_scan.outputs.reason }})"
|
||||
changed=""
|
||||
script/clang-tidy --all-headers --fix ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||
else
|
||||
echo "Running clang-tidy on changed files only"
|
||||
changed="--changed"
|
||||
fi
|
||||
if [ -n "${{ matrix.environments }}" ]; then
|
||||
rc=0
|
||||
for env in ${{ matrix.environments }}; do
|
||||
echo "::group::clang-tidy $env"
|
||||
script/clang-tidy --all-headers --fix $changed --environment "$env" ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }} || rc=1
|
||||
echo "::endgroup::"
|
||||
done
|
||||
exit $rc
|
||||
else
|
||||
script/clang-tidy --all-headers --fix $changed ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||
script/clang-tidy --all-headers --fix --changed ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||
fi
|
||||
env:
|
||||
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||
@@ -764,8 +744,7 @@ jobs:
|
||||
include:
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for ESP32 S3
|
||||
# yamllint disable-line rule:line-length
|
||||
options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC
|
||||
options: --environment esp32s3-idf-tidy --grep USE_ESP32_VARIANT_ESP32S3
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for ESP32 P4
|
||||
# P4 has no native Wi-Fi/BLE; those run over the hosted co-processor,
|
||||
@@ -775,7 +754,7 @@ jobs:
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for ESP32 C6
|
||||
# yamllint disable-line rule:line-length
|
||||
options: --environment esp32c6-idf-tidy --grep SOC_LP_I2C_SUPPORTED --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE
|
||||
options: --environment esp32c6-idf-tidy --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE
|
||||
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
||||
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -84,6 +84,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
||||
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -14,4 +14,4 @@ jobs:
|
||||
permissions:
|
||||
issues: write # issues.lock on closed issues
|
||||
pull-requests: write # issues.lock on closed pull requests
|
||||
uses: esphome/workflows/.github/workflows/lock.yml@9f6577fd37b5cf773ab1b9be929714a0dcd15661 # 2026.7.0
|
||||
uses: esphome/workflows/.github/workflows/lock.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Stale
|
||||
uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||
uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||
with:
|
||||
debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch
|
||||
remove-stale-when-updated: true
|
||||
|
||||
@@ -145,7 +145,6 @@ esphome/components/dlms_meter/* @latonita @PolarGoose @SimonFischer04 @Tomer27cz
|
||||
esphome/components/dps310/* @kbx81
|
||||
esphome/components/ds1307/* @badbadc0ffee
|
||||
esphome/components/ds2484/* @mrk-its
|
||||
esphome/components/ds248x/* @tomwellnitz
|
||||
esphome/components/dsmr/* @glmnet @PolarGoose
|
||||
esphome/components/duty_time/* @dudanov
|
||||
esphome/components/ee895/* @Stock-M
|
||||
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 2026.8.0-dev
|
||||
PROJECT_NUMBER = 2026.7.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ RUN \
|
||||
-r /requirements.txt
|
||||
|
||||
# Install the ESPHome Device Builder dashboard.
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.6.4
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.6.8
|
||||
|
||||
RUN \
|
||||
platformio settings set enable_telemetry No \
|
||||
|
||||
+34
-47
@@ -28,13 +28,13 @@ from esphome.const import (
|
||||
ALLOWED_NAME_CHARS,
|
||||
ARGUMENT_HELP_DEVICE,
|
||||
CONF_API,
|
||||
CONF_AUTH,
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BROKER,
|
||||
CONF_DEASSERT_RTS_DTR,
|
||||
CONF_DISABLED,
|
||||
CONF_ESPHOME,
|
||||
CONF_LEVEL,
|
||||
CONF_LOG,
|
||||
CONF_LOG_TOPIC,
|
||||
CONF_LOGGER,
|
||||
CONF_MDNS,
|
||||
@@ -48,7 +48,7 @@ from esphome.const import (
|
||||
CONF_PORT,
|
||||
CONF_SUBSTITUTIONS,
|
||||
CONF_TOPIC,
|
||||
CONF_VERSION,
|
||||
CONF_USERNAME,
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WIFI,
|
||||
ENV_NOGITIGNORE,
|
||||
@@ -317,12 +317,9 @@ def choose_upload_log_host(
|
||||
]
|
||||
resolved.append(choose_prompt(options, purpose=purpose))
|
||||
elif device == "OTA":
|
||||
# Logs can stream over a network transport via the native API
|
||||
# or the web_server HTTP SSE feed.
|
||||
network_logging = has_api() or has_web_server_logging()
|
||||
# ensure IP adresses are used first
|
||||
if is_ip_address(CORE.address) and (
|
||||
(purpose == Purpose.LOGGING and network_logging)
|
||||
(purpose == Purpose.LOGGING and has_api())
|
||||
or (purpose == Purpose.UPLOADING and has_ota())
|
||||
):
|
||||
resolved.extend(_resolve_with_cache(CORE.address, purpose))
|
||||
@@ -334,11 +331,7 @@ def choose_upload_log_host(
|
||||
if has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
|
||||
if (
|
||||
network_logging
|
||||
and has_non_ip_address()
|
||||
and has_resolvable_address()
|
||||
):
|
||||
if has_api() and has_non_ip_address() and has_resolvable_address():
|
||||
resolved.extend(_ota_hostnames_for_default(purpose))
|
||||
|
||||
elif purpose == Purpose.UPLOADING:
|
||||
@@ -400,7 +393,7 @@ def choose_upload_log_host(
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
||||
|
||||
if has_api() or has_web_server_logging():
|
||||
if has_api():
|
||||
add_ota_options()
|
||||
|
||||
elif purpose == Purpose.UPLOADING and has_ota():
|
||||
@@ -493,21 +486,6 @@ def has_web_server_ota() -> bool:
|
||||
)
|
||||
|
||||
|
||||
def has_web_server_logging() -> bool:
|
||||
"""Check if logs can be streamed over the web_server HTTP SSE endpoint.
|
||||
|
||||
The ``web_server`` component exposes a ``/events`` Server-Sent Events
|
||||
stream that carries ``event: log`` frames. This requires version 2+ (the
|
||||
v1 UI has no ``/events`` endpoint) and the ``log`` option enabled (default).
|
||||
"""
|
||||
web_conf = CORE.config.get(CONF_WEB_SERVER)
|
||||
if web_conf is None:
|
||||
return False
|
||||
if web_conf.get(CONF_VERSION, 2) == 1:
|
||||
return False
|
||||
return web_conf.get(CONF_LOG, True)
|
||||
|
||||
|
||||
def has_mqtt_ip_lookup() -> bool:
|
||||
"""Check if MQTT is available and IP lookup is supported."""
|
||||
from esphome.components.mqtt import CONF_DISCOVER_IP
|
||||
@@ -730,7 +708,6 @@ def _wrap_to_code(name, comp, yaml_util):
|
||||
|
||||
@functools.wraps(comp.to_code)
|
||||
async def wrapped(conf):
|
||||
cg.add(cg.ComponentMarker(name))
|
||||
cg.add(cg.LineComment(f"{name}:"))
|
||||
if comp.config_schema is not None:
|
||||
conf_str = yaml_util.dump(conf)
|
||||
@@ -799,6 +776,13 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
|
||||
|
||||
check_placeholder_credentials(config)
|
||||
|
||||
# Keep this here, NOT in codegen: config-hash and --only-generate must keep
|
||||
# working on machines that cannot run the toolchain.
|
||||
if CORE.is_esp8266:
|
||||
from esphome.components.esp8266 import check_rosetta
|
||||
|
||||
check_rosetta()
|
||||
|
||||
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
|
||||
# If you change this format, update the regex in that script as well
|
||||
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
|
||||
@@ -1314,23 +1298,25 @@ def _upload_via_native_api(
|
||||
def _upload_via_web_server(
|
||||
config: ConfigType, network_devices: list[str], binary: Path
|
||||
) -> tuple[int, str | None]:
|
||||
web_conf = config.get(CONF_WEB_SERVER)
|
||||
if not web_conf:
|
||||
raise EsphomeError(
|
||||
f"Cannot upload via web_server OTA: the {CONF_WEB_SERVER} component "
|
||||
f"is not configured."
|
||||
)
|
||||
|
||||
remote_port = int(web_conf[CONF_PORT])
|
||||
auth = web_conf.get(CONF_AUTH) or {}
|
||||
username = auth.get(CONF_USERNAME)
|
||||
password = auth.get(CONF_PASSWORD)
|
||||
|
||||
from esphome import web_server_ota
|
||||
from esphome.web_server_helpers import get_web_server_connection
|
||||
|
||||
remote_port, username, password = get_web_server_connection(config)
|
||||
return web_server_ota.run_ota(
|
||||
network_devices, remote_port, username, password, binary
|
||||
)
|
||||
|
||||
|
||||
def _show_logs_via_web_server(config: ConfigType, network_devices: list[str]) -> int:
|
||||
from esphome import web_server_logs
|
||||
from esphome.web_server_helpers import get_web_server_connection
|
||||
|
||||
port, username, password = get_web_server_connection(config)
|
||||
return web_server_logs.run_logs(network_devices, port, username, password)
|
||||
|
||||
|
||||
# Layout of esp_partition_info_t on flash. Each entry is 32 bytes, leading with a
|
||||
# 16-bit little-endian magic. ESP-IDF defines ESP_PARTITION_MAGIC = 0x50AA (stored as
|
||||
# bytes 0xAA, 0x50) for partition entries and ESP_PARTITION_MAGIC_MD5 = 0xEBEB for the
|
||||
@@ -1459,13 +1445,6 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
||||
config, args.topic, args.username, args.password, args.client_id
|
||||
)
|
||||
|
||||
# Fall back to the web_server HTTP SSE log stream for devices that have
|
||||
# web_server: but no api: (the logging counterpart to web_server OTA).
|
||||
if has_web_server_logging() and (
|
||||
network_devices := _resolve_network_devices(devices, config, args)
|
||||
):
|
||||
return _show_logs_via_web_server(config, network_devices)
|
||||
|
||||
raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
|
||||
|
||||
|
||||
@@ -1538,10 +1517,18 @@ def _redact_with_legacy_fallback(output: str) -> str:
|
||||
m = _LEGACY_REDACTION_RE.search(line)
|
||||
if m is None:
|
||||
continue
|
||||
key = m.group("key")
|
||||
if not in_substitutions:
|
||||
unmarked.add(m.group("key"))
|
||||
# Public keys (e.g. wireguard's peer_public_key) are not secret;
|
||||
# redacting them and telling maintainers to mark them cv.sensitive
|
||||
# would be wrong on both counts. Substitution keys are user-named
|
||||
# with no schema behind them, so anything secret-shaped there
|
||||
# (public or not) stays conservatively redacted.
|
||||
if "public" in key.split("_"):
|
||||
continue
|
||||
unmarked.add(key)
|
||||
lines[i] = (
|
||||
f"{line[: m.start()]}{m.group('key')}: "
|
||||
f"{line[: m.start()]}{key}: "
|
||||
f"\\033[8m{m.group('val')}\\033[28m{line[m.end() :]}"
|
||||
)
|
||||
output = "\n".join(lines)
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
import heapq
|
||||
import json
|
||||
from operator import itemgetter
|
||||
from pathlib import Path
|
||||
import sys
|
||||
@@ -21,7 +20,6 @@ from . import (
|
||||
RAM_SECTIONS,
|
||||
MemoryAnalyzer,
|
||||
)
|
||||
from .toolchain import find_elf_path, find_idedata_path, idedata_candidates
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import ComponentMemory
|
||||
@@ -43,7 +41,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
|
||||
# Symbol size threshold for detailed analysis
|
||||
SYMBOL_SIZE_THRESHOLD: int = (
|
||||
10 # Show symbols larger than this in detailed analysis
|
||||
100 # Show symbols larger than this in detailed analysis
|
||||
)
|
||||
# Lower threshold for RAM symbols (RAM is more constrained)
|
||||
RAM_SYMBOL_SIZE_THRESHOLD: int = 24
|
||||
@@ -761,25 +759,45 @@ def main():
|
||||
print(f"Error: {build_path} is not a directory", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
elf_path = find_elf_path(build_path)
|
||||
if not elf_path:
|
||||
print(f"Error: no firmware ELF found in {build_dir}", file=sys.stderr)
|
||||
# Find firmware.elf
|
||||
elf_file = None
|
||||
for elf_candidate in [
|
||||
build_path / "firmware.elf",
|
||||
build_path / ".pioenvs" / build_path.name / "firmware.elf",
|
||||
]:
|
||||
if elf_candidate.exists():
|
||||
elf_file = str(elf_candidate)
|
||||
break
|
||||
|
||||
if not elf_file:
|
||||
print(f"Error: firmware.elf not found in {build_dir}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elf_file = str(elf_path)
|
||||
|
||||
# Find idedata.json - check current directory first, then home
|
||||
device_name = build_path.name
|
||||
idedata_candidates = [
|
||||
Path.cwd() / ".esphome" / "idedata" / f"{device_name}.json",
|
||||
Path.home() / ".esphome" / "idedata" / f"{device_name}.json",
|
||||
]
|
||||
|
||||
idedata = None
|
||||
if idedata_path := find_idedata_path(build_path):
|
||||
for idedata_path in idedata_candidates:
|
||||
if not idedata_path.exists():
|
||||
continue
|
||||
try:
|
||||
with idedata_path.open(encoding="utf-8") as f:
|
||||
raw_data = json.load(f)
|
||||
idedata = IDEData(raw_data)
|
||||
print(f"Loaded idedata from: {idedata_path}", file=sys.stderr)
|
||||
break
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
print(f"Warning: Failed to load idedata: {e}", file=sys.stderr)
|
||||
|
||||
if not idedata:
|
||||
searched = "\n ".join(str(p) for p in idedata_candidates(build_path))
|
||||
print(f"Warning: idedata not found, searched:\n {searched}", file=sys.stderr)
|
||||
print(
|
||||
f"Warning: idedata not found (searched {idedata_candidates[0]} and {idedata_candidates[1]})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
analyzer = MemoryAnalyzerCLI(elf_file, idedata=idedata)
|
||||
analyzer.analyze()
|
||||
|
||||
@@ -23,78 +23,6 @@ TOOLCHAIN_PREFIXES = [
|
||||
]
|
||||
|
||||
|
||||
def find_elf_path(build_path: Path) -> Path | None:
|
||||
"""Locate the firmware ELF inside an ESPHome build directory.
|
||||
|
||||
The layout depends on the toolchain that produced the build, so try each
|
||||
known one in turn.
|
||||
|
||||
Args:
|
||||
build_path: Path to an ESPHome build directory
|
||||
|
||||
Returns:
|
||||
Path to the ELF file, or None if no known layout matches
|
||||
"""
|
||||
name = build_path.name
|
||||
for candidate in (
|
||||
# Native ESP-IDF: idf.py writes build/<name>.elf, which ESPHome copies
|
||||
# to build/firmware.elf (see espidf.toolchain.create_elf_copy)
|
||||
build_path / "build" / "firmware.elf",
|
||||
# PlatformIO
|
||||
build_path / "firmware.elf",
|
||||
build_path / ".pioenvs" / name / "firmware.elf",
|
||||
# LibreTiny uses raw_firmware.elf
|
||||
build_path / "raw_firmware.elf",
|
||||
build_path / ".pioenvs" / name / "raw_firmware.elf",
|
||||
# Zephyr (nRF52); the SDK nests the artifacts one level deeper from 2.9.2
|
||||
build_path / ".pioenvs" / name / "zephyr" / "zephyr" / "zephyr.elf",
|
||||
build_path / ".pioenvs" / name / "zephyr" / "zephyr.elf",
|
||||
):
|
||||
if candidate.is_file():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def idedata_candidates(build_path: Path) -> list[Path]:
|
||||
"""Return the idedata locations searched for a build directory, in order.
|
||||
|
||||
Exposed so a caller reporting "not found" can name the paths it tried
|
||||
without keeping its own copy of the list.
|
||||
|
||||
Args:
|
||||
build_path: Path to an ESPHome build directory
|
||||
|
||||
Returns:
|
||||
The candidate idedata JSON paths, most specific first
|
||||
"""
|
||||
name = build_path.name
|
||||
return [
|
||||
# In .pioenvs for test builds
|
||||
build_path / ".pioenvs" / name / "idedata.json",
|
||||
# Both toolchains cache it in the data dir, which holds this build dir:
|
||||
# <data_dir>/idedata/<name>.json next to <data_dir>/build/<name>
|
||||
build_path.parent.parent / "idedata" / f"{name}.json",
|
||||
# Regular builds, invoked from the config dir or from anywhere
|
||||
Path.cwd() / ".esphome" / "idedata" / f"{name}.json",
|
||||
Path.home() / ".esphome" / "idedata" / f"{name}.json",
|
||||
]
|
||||
|
||||
|
||||
def find_idedata_path(build_path: Path) -> Path | None:
|
||||
"""Locate the idedata JSON belonging to an ESPHome build directory.
|
||||
|
||||
Args:
|
||||
build_path: Path to an ESPHome build directory
|
||||
|
||||
Returns:
|
||||
Path to the idedata JSON, or None if it was not found
|
||||
"""
|
||||
for candidate in idedata_candidates(build_path):
|
||||
if candidate.is_file():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def _find_in_platformio_packages(tool_name: str) -> str | None:
|
||||
"""Search for a tool in PlatformIO package directories.
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
# pylint: disable=unused-import
|
||||
from esphome.cpp_generator import ( # noqa: F401
|
||||
ArrayInitializer,
|
||||
ComponentMarker,
|
||||
Expression,
|
||||
FlashStringLiteral,
|
||||
LineComment,
|
||||
|
||||
@@ -1746,6 +1746,12 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) {
|
||||
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu16 ".%" PRIu16, this->helper_->get_client_name(),
|
||||
this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_);
|
||||
|
||||
// TODO: Remove before 2026.8.0 (one version after get_object_id backward compat removal)
|
||||
if (!this->client_supports_api_version(1, 14)) {
|
||||
ESP_LOGW(TAG, "'%s' using outdated API %" PRIu16 ".%" PRIu16 ", update to 1.14+", this->helper_->get_client_name(),
|
||||
this->client_api_version_major_, this->client_api_version_minor_);
|
||||
}
|
||||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 14;
|
||||
|
||||
@@ -18,7 +18,7 @@ with warnings.catch_warnings():
|
||||
import contextlib
|
||||
|
||||
from esphome.const import CONF_KEY, CONF_PORT, __version__
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.util import safe_print
|
||||
|
||||
from . import CONF_ENCRYPTION
|
||||
@@ -36,17 +36,15 @@ class _LogLineProcessor:
|
||||
"""Feeds incoming log lines to the stack-trace decoder.
|
||||
|
||||
Two responsibilities beyond just calling the decoder:
|
||||
1. Catch everything the decoder can raise. aioesphomeapi isolates
|
||||
exceptions raised by log handlers, so an escaping one no longer
|
||||
kills the session, but it does log a full traceback per line. A
|
||||
crash dump carries a PC line plus one per backtrace frame, so the
|
||||
tracebacks bury the dump the user is trying to read. Decoding is a
|
||||
diagnostic nicety; nothing it raises is worth that noise.
|
||||
2. Disable decoding after the first failure. _decode_pc shells out to
|
||||
the toolchain to resolve addr2line, which is expensive; a single
|
||||
crash dump can contain many PC/BT lines and we don't want to retry
|
||||
the failing subprocess for each one. This only works if every
|
||||
failure is caught, which is why 1 is not narrowed to EsphomeError.
|
||||
1. Catch EsphomeError. on_log runs inside an asyncio protocol
|
||||
callback; if an exception escapes, the loop tears the transport
|
||||
down with "Fatal error: protocol.data_received() call failed."
|
||||
and ReconnectLogic immediately reconnects, the device replays
|
||||
the same crash trace, and we loop forever.
|
||||
2. Disable decoding after the first failure. _decode_pc shells out
|
||||
to PlatformIO via _run_idedata, which is expensive; a single
|
||||
crash dump can contain many PC/BT lines and we don't want to
|
||||
retry the failing subprocess for each one.
|
||||
"""
|
||||
|
||||
def __init__(self, config: dict[str, Any], platform_handler: Any | None) -> None:
|
||||
@@ -63,13 +61,12 @@ class _LogLineProcessor:
|
||||
self.backtrace_state = self._platform_handler(
|
||||
self._config, raw_line, self.backtrace_state
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 # pylint: disable=broad-except
|
||||
except EsphomeError as exc:
|
||||
self._decode_enabled = False
|
||||
self.backtrace_state = False
|
||||
# _run_idedata raises EsphomeError with no message; fall back
|
||||
# to a generic explanation when str(exc) is empty.
|
||||
detail = str(exc) or "build artifacts not found locally"
|
||||
_LOGGER.debug("Stack-trace decoding failed", exc_info=True)
|
||||
_LOGGER.warning(
|
||||
"Crash trace decoding unavailable: %s. "
|
||||
"Run 'esphome compile' for this device to enable PC decoding.",
|
||||
|
||||
@@ -7,7 +7,6 @@ AQICalculatorType = aqi_ns.enum("AQICalculatorType")
|
||||
|
||||
CONF_AQI = "aqi"
|
||||
CONF_CALCULATION_TYPE = "calculation_type"
|
||||
CONF_EXTENDED_RANGE = "extended_range"
|
||||
|
||||
AQI_CALCULATION_TYPE = {
|
||||
"CAQI": AQICalculatorType.CAQI_TYPE,
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace esphome::aqi {
|
||||
|
||||
class AbstractAQICalculator {
|
||||
public:
|
||||
virtual uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool extended_range) = 0;
|
||||
virtual uint16_t get_aqi(float pm2_5_value, float pm10_0_value) = 0;
|
||||
};
|
||||
|
||||
} // namespace esphome::aqi
|
||||
|
||||
@@ -11,12 +11,10 @@ namespace esphome::aqi {
|
||||
|
||||
class AQICalculator : public AbstractAQICalculator {
|
||||
public:
|
||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool extended_range) override {
|
||||
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID, extended_range);
|
||||
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID, extended_range);
|
||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
||||
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
||||
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
||||
float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f});
|
||||
// extended_range lets the index run past the standard maximum, so clamp to the sensor's range.
|
||||
aqi = std::min(aqi, static_cast<float>(std::numeric_limits<uint16_t>::max()));
|
||||
return static_cast<uint16_t>(std::lround(aqi));
|
||||
}
|
||||
|
||||
@@ -32,7 +30,7 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
{35.5f, 55.5f},
|
||||
{55.5f, 125.5f},
|
||||
{125.5f, 225.5f},
|
||||
{225.5f, 500.4f} // EPA 2024: AQI 301-500 maps to PM2.5 225.5-500.4 ug/m3
|
||||
{225.5f, std::numeric_limits<float>::max()}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
@@ -43,11 +41,11 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
{155.0f, 255.0f},
|
||||
{255.0f, 355.0f},
|
||||
{355.0f, 425.0f},
|
||||
{425.0f, 604.0f} // EPA: AQI 301-500 maps to PM10 425-604 ug/m3 (top of the 401-500 band)
|
||||
{425.0f, std::numeric_limits<float>::max()}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
static float calculate_index(float value, const float array[NUM_LEVELS][2], bool extended_range) {
|
||||
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
|
||||
int grid_index = get_grid_index(value, array);
|
||||
if (grid_index == -1) {
|
||||
return -1.0f;
|
||||
@@ -57,22 +55,14 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
float conc_lo = array[grid_index][0];
|
||||
float conc_hi = array[grid_index][1];
|
||||
|
||||
float index = (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
|
||||
// Concentrations above the highest breakpoint run the linear fit past aqi_hi. By default we
|
||||
// clamp to the standard maximum; with extended_range we keep the extrapolated "over-range"
|
||||
// value so heavy pollution reports numbers beyond what the standard defines.
|
||||
if (grid_index == NUM_LEVELS - 1 && !extended_range && index > aqi_hi) {
|
||||
return aqi_hi;
|
||||
}
|
||||
return index;
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
}
|
||||
|
||||
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
|
||||
for (int i = 0; i < NUM_LEVELS; i++) {
|
||||
// The top band is open-ended: any value at or above its lower breakpoint falls into it,
|
||||
// and calculate_index() decides whether to clamp or extrapolate.
|
||||
const bool in_range = (value >= array[i][0]) && (i == NUM_LEVELS - 1 || value < array[i][1]);
|
||||
const bool in_range =
|
||||
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
|
||||
: (value < array[i][1])); // others exclusive on hi
|
||||
if (in_range) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ void AQISensor::setup() {
|
||||
void AQISensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AQI Sensor:");
|
||||
ESP_LOGCONFIG(TAG, " Calculation Type: %s", this->aqi_calc_type_ == AQI_TYPE ? "AQI" : "CAQI");
|
||||
ESP_LOGCONFIG(TAG, " Extended Range: %s", this->extended_range_ ? "enabled" : "disabled");
|
||||
if (this->pm_2_5_sensor_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", this->pm_2_5_sensor_->get_name().c_str());
|
||||
}
|
||||
@@ -45,7 +44,7 @@ void AQISensor::calculate_aqi_() {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t aqi = calculator->get_aqi(this->pm_2_5_value_, this->pm_10_0_value_, this->extended_range_);
|
||||
uint16_t aqi = calculator->get_aqi(this->pm_2_5_value_, this->pm_10_0_value_);
|
||||
this->publish_state(aqi);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ class AQISensor final : public sensor::Sensor, public Component {
|
||||
void set_pm_2_5_sensor(sensor::Sensor *sensor) { this->pm_2_5_sensor_ = sensor; }
|
||||
void set_pm_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; }
|
||||
void set_aqi_calculation_type(AQICalculatorType type) { this->aqi_calc_type_ = type; }
|
||||
void set_extended_range(bool extended_range) { this->extended_range_ = extended_range; }
|
||||
|
||||
protected:
|
||||
void calculate_aqi_();
|
||||
@@ -22,7 +21,6 @@ class AQISensor final : public sensor::Sensor, public Component {
|
||||
sensor::Sensor *pm_2_5_sensor_{nullptr};
|
||||
sensor::Sensor *pm_10_0_sensor_{nullptr};
|
||||
AQICalculatorType aqi_calc_type_{AQI_TYPE};
|
||||
bool extended_range_{false};
|
||||
AQICalculatorFactory aqi_calculator_factory_;
|
||||
|
||||
float pm_2_5_value_{NAN};
|
||||
|
||||
@@ -9,28 +9,25 @@ namespace esphome::aqi {
|
||||
|
||||
class CAQICalculator : public AbstractAQICalculator {
|
||||
public:
|
||||
// The CAQI (CITEAIR) scale defines no maximum: its top "Very high" class is simply ">100". We
|
||||
// therefore always extrapolate the top band past 100 without limit, so the extended_range flag
|
||||
// (which lifts the AQI calculator's fixed 500 cap) has no meaning here and is ignored.
|
||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool /*extended_range*/) override {
|
||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
||||
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
||||
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
||||
float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f});
|
||||
aqi = std::min(aqi, static_cast<float>(std::numeric_limits<uint16_t>::max()));
|
||||
return static_cast<uint16_t>(std::lround(aqi));
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr int NUM_LEVELS = 4;
|
||||
static constexpr int NUM_LEVELS = 5;
|
||||
|
||||
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}};
|
||||
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}, {101, 400}};
|
||||
|
||||
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
|
||||
// clang-format off
|
||||
{0.0f, 15.1f},
|
||||
{15.1f, 30.1f},
|
||||
{30.1f, 55.1f},
|
||||
{55.1f, 110.1f}
|
||||
{55.1f, 110.1f},
|
||||
{110.1f, std::numeric_limits<float>::max()}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
@@ -39,7 +36,8 @@ class CAQICalculator : public AbstractAQICalculator {
|
||||
{0.0f, 25.1f},
|
||||
{25.1f, 50.1f},
|
||||
{50.1f, 90.1f},
|
||||
{90.1f, 180.1f}
|
||||
{90.1f, 180.1f},
|
||||
{180.1f, std::numeric_limits<float>::max()}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
@@ -54,15 +52,14 @@ class CAQICalculator : public AbstractAQICalculator {
|
||||
float conc_lo = array[grid_index][0];
|
||||
float conc_hi = array[grid_index][1];
|
||||
|
||||
// The top band is open-ended (see get_grid_index), so for concentrations above the last
|
||||
// breakpoint this linear fit extrapolates past 100 unbounded, matching CAQI's open ">100" class.
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
}
|
||||
|
||||
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
|
||||
for (int i = 0; i < NUM_LEVELS; i++) {
|
||||
// The top band is open-ended: any value at or above its lower breakpoint falls into it.
|
||||
const bool in_range = (value >= array[i][0]) && (i == NUM_LEVELS - 1 || value < array[i][1]);
|
||||
const bool in_range =
|
||||
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
|
||||
: (value < array[i][1])); // others exclusive on hi
|
||||
if (in_range) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -8,25 +8,14 @@ from esphome.const import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
from . import AQI_CALCULATION_TYPE, CONF_CALCULATION_TYPE, CONF_EXTENDED_RANGE, aqi_ns
|
||||
from . import AQI_CALCULATION_TYPE, CONF_CALCULATION_TYPE, aqi_ns
|
||||
|
||||
CODEOWNERS = ["@jasstrong"]
|
||||
DEPENDENCIES = ["sensor"]
|
||||
|
||||
AQISensor = aqi_ns.class_("AQISensor", sensor.Sensor, cg.Component)
|
||||
|
||||
|
||||
def _validate_extended_range(config):
|
||||
if CONF_EXTENDED_RANGE in config and config[CONF_CALCULATION_TYPE] == "CAQI":
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_EXTENDED_RANGE}' is not supported with 'calculation_type: CAQI'. "
|
||||
"CAQI has no maximum value by specification, so it is always reported unbounded.",
|
||||
[CONF_EXTENDED_RANGE],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
AQISensor,
|
||||
accuracy_decimals=0,
|
||||
@@ -40,11 +29,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Required(CONF_CALCULATION_TYPE): cv.enum(
|
||||
AQI_CALCULATION_TYPE, upper=True
|
||||
),
|
||||
cv.Optional(CONF_EXTENDED_RANGE): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
_validate_extended_range,
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
@@ -59,4 +46,3 @@ async def to_code(config):
|
||||
cg.add(var.set_pm_10_0_sensor(pm_10_0_sensor))
|
||||
|
||||
cg.add(var.set_aqi_calculation_type(config[CONF_CALCULATION_TYPE]))
|
||||
cg.add(var.set_extended_range(config.get(CONF_EXTENDED_RANGE, False)))
|
||||
|
||||
@@ -37,15 +37,15 @@ namespace esphome::beken_spi_led_strip {
|
||||
|
||||
static const char *const TAG = "beken_spi_led_strip";
|
||||
|
||||
struct SpiData {
|
||||
struct spi_data_t {
|
||||
SemaphoreHandle_t dma_tx_semaphore;
|
||||
volatile bool tx_in_progress;
|
||||
bool first_run;
|
||||
};
|
||||
|
||||
static SpiData *spi_data = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static spi_data_t *spi_data = nullptr;
|
||||
|
||||
static void set_spi_ctrl_register(uint32_t bit, bool val) {
|
||||
static void set_spi_ctrl_register(unsigned long bit, bool val) {
|
||||
uint32_t value = REG_READ(SPI_CTRL);
|
||||
if (val == 0) {
|
||||
value &= ~bit;
|
||||
@@ -55,7 +55,7 @@ static void set_spi_ctrl_register(uint32_t bit, bool val) {
|
||||
REG_WRITE(SPI_CTRL, value);
|
||||
}
|
||||
|
||||
static void set_spi_config_register(uint32_t bit, bool val) {
|
||||
static void set_spi_config_register(unsigned long bit, bool val) {
|
||||
uint32_t value = REG_READ(SPI_CONFIG);
|
||||
if (val == 0) {
|
||||
value &= ~bit;
|
||||
@@ -67,7 +67,7 @@ static void set_spi_config_register(uint32_t bit, bool val) {
|
||||
|
||||
void spi_dma_tx_enable(bool enable) {
|
||||
GDMA_CFG_ST en_cfg;
|
||||
set_spi_config_register(SPI_TX_EN, enable);
|
||||
set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
|
||||
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
en_cfg.param = enable ? 1 : 0;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
|
||||
@@ -110,13 +110,13 @@ static void spi_set_clock(uint32_t max_hz) {
|
||||
param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
|
||||
param |= (div << SPI_CKR_POSI);
|
||||
REG_WRITE(SPI_CTRL, param);
|
||||
ESP_LOGD(TAG, "target frequency: %" PRIu32 ", actual frequency: %d", max_hz, source_clk / 2 / div);
|
||||
ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
|
||||
}
|
||||
|
||||
void spi_dma_tx_finish_callback(unsigned int param) {
|
||||
spi_data->tx_in_progress = false;
|
||||
xSemaphoreGive(spi_data->dma_tx_semaphore);
|
||||
spi_dma_tx_enable(false);
|
||||
spi_dma_tx_enable(0);
|
||||
}
|
||||
|
||||
void BekenSPILEDStripLightOutput::setup() {
|
||||
@@ -161,7 +161,7 @@ void BekenSPILEDStripLightOutput::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
spi_data = (SpiData *) calloc(1, sizeof(SpiData)); // NOLINT(cppcoreguidelines-no-malloc)
|
||||
spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
|
||||
if (spi_data == nullptr) {
|
||||
ESP_LOGE(TAG, "Cannot allocate spi_data!");
|
||||
this->mark_failed();
|
||||
@@ -177,20 +177,20 @@ void BekenSPILEDStripLightOutput::setup() {
|
||||
|
||||
spi_data->first_run = true;
|
||||
|
||||
set_spi_ctrl_register(MSTEN, false);
|
||||
set_spi_ctrl_register(BIT_WDTH, false);
|
||||
set_spi_ctrl_register(MSTEN, 0);
|
||||
set_spi_ctrl_register(BIT_WDTH, 0);
|
||||
spi_set_clock(this->spi_frequency_);
|
||||
set_spi_ctrl_register(CKPOL, false);
|
||||
set_spi_ctrl_register(CKPHA, false);
|
||||
set_spi_ctrl_register(MSTEN, true);
|
||||
set_spi_ctrl_register(SPIEN, true);
|
||||
set_spi_ctrl_register(CKPOL, 0);
|
||||
set_spi_ctrl_register(CKPHA, 0);
|
||||
set_spi_ctrl_register(MSTEN, 1);
|
||||
set_spi_ctrl_register(SPIEN, 1);
|
||||
|
||||
set_spi_ctrl_register(TXINT_EN, false);
|
||||
set_spi_ctrl_register(RXINT_EN, false);
|
||||
set_spi_config_register(SPI_TX_FINISH_EN, true);
|
||||
set_spi_config_register(SPI_RX_FINISH_EN, true);
|
||||
set_spi_ctrl_register(RXOVR_EN, false);
|
||||
set_spi_ctrl_register(TXOVR_EN, false);
|
||||
set_spi_ctrl_register(TXINT_EN, 0);
|
||||
set_spi_ctrl_register(RXINT_EN, 0);
|
||||
set_spi_config_register(SPI_TX_FINISH_EN, 1);
|
||||
set_spi_config_register(SPI_RX_FINISH_EN, 1);
|
||||
set_spi_ctrl_register(RXOVR_EN, 0);
|
||||
set_spi_ctrl_register(TXOVR_EN, 0);
|
||||
|
||||
value = REG_READ(SPI_CTRL);
|
||||
value &= ~CTRL_NSSMD_3;
|
||||
@@ -199,7 +199,7 @@ void BekenSPILEDStripLightOutput::setup() {
|
||||
|
||||
value = GFUNC_MODE_SPI_DMA;
|
||||
sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
|
||||
set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, false);
|
||||
set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
|
||||
|
||||
GDMA_CFG_ST en_cfg;
|
||||
GDMACFG_TPYES_ST init_cfg;
|
||||
@@ -210,7 +210,7 @@ void BekenSPILEDStripLightOutput::setup() {
|
||||
init_cfg.dstptr_incr = 0;
|
||||
init_cfg.srcptr_incr = 1;
|
||||
init_cfg.src_start_addr = this->dma_buf_;
|
||||
init_cfg.dst_start_addr = (void *) SPI_DAT; // NOLINT(performance-no-int-to-ptr) SPI_DMA_REG4_TXFIFO
|
||||
init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO
|
||||
init_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
init_cfg.prio = 0; // 10
|
||||
init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
|
||||
@@ -230,7 +230,7 @@ void BekenSPILEDStripLightOutput::setup() {
|
||||
en_cfg.param = 0;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
|
||||
|
||||
spi_dma_tx_enable(false);
|
||||
spi_dma_tx_enable(0);
|
||||
|
||||
value = REG_READ(SPI_CONFIG);
|
||||
value &= ~(0xFFF << 8);
|
||||
@@ -247,8 +247,7 @@ void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uin
|
||||
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
|
||||
// protect from refreshing too often
|
||||
uint32_t now = micros();
|
||||
if (this->max_refresh_rate_.has_value() && *this->max_refresh_rate_ != 0 &&
|
||||
(now - this->last_refresh_) < *this->max_refresh_rate_) {
|
||||
if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
|
||||
// try again next loop iteration, so that this change won't get lost
|
||||
this->schedule_show();
|
||||
return;
|
||||
@@ -294,7 +293,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
|
||||
}
|
||||
|
||||
spi_data->first_run = false;
|
||||
spi_dma_tx_enable(true);
|
||||
spi_dma_tx_enable(1);
|
||||
|
||||
this->status_clear_warning();
|
||||
}
|
||||
@@ -377,7 +376,7 @@ void BekenSPILEDStripLightOutput::dump_config() {
|
||||
" RGB Order: %s\n"
|
||||
" Max refresh rate: %" PRIu32 "\n"
|
||||
" Number of LEDs: %u",
|
||||
rgb_order, this->max_refresh_rate_.value_or(0), this->num_leds_);
|
||||
rgb_order, *this->max_refresh_rate_, this->num_leds_);
|
||||
}
|
||||
|
||||
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
@@ -245,9 +245,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
||||
service_resp.characteristics.init(total_char_count);
|
||||
uint16_t char_offset = 0;
|
||||
esp_gattc_char_elem_t char_result;
|
||||
// Bound by total_char_count: the vector is sized for it, and a malicious peripheral
|
||||
// can make enumeration return more entries than the count query reported
|
||||
while (char_offset < total_char_count) { // characteristics
|
||||
while (true) { // characteristics
|
||||
uint16_t char_count = 1;
|
||||
esp_gatt_status_t char_status =
|
||||
esp_ble_gattc_get_all_char(this->gattc_if_, this->conn_id_, service_result.start_handle,
|
||||
@@ -289,7 +287,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
||||
characteristic_resp.descriptors.init(total_desc_count);
|
||||
uint16_t desc_offset = 0;
|
||||
esp_gattc_descr_elem_t desc_result;
|
||||
while (desc_offset < total_desc_count) { // descriptors
|
||||
while (true) { // descriptors
|
||||
uint16_t desc_count = 1;
|
||||
esp_gatt_status_t desc_status = esp_ble_gattc_get_all_descr(
|
||||
this->gattc_if_, this->conn_id_, char_result.char_handle, &desc_result, &desc_count, desc_offset);
|
||||
|
||||
@@ -21,7 +21,6 @@ MULTI_CONF = True
|
||||
|
||||
ns = cg.esphome_ns.namespace("cc1101")
|
||||
CC1101Component = ns.class_("CC1101Component", cg.Component, spi.SPIDevice)
|
||||
CC1101Listener = ns.class_("CC1101Listener")
|
||||
|
||||
# Config keys
|
||||
CONF_RX_ATTENUATION = "rx_attenuation"
|
||||
@@ -49,15 +48,6 @@ CONF_FILTER_LENGTH_FSK_MSK = "filter_length_fsk_msk"
|
||||
CONF_FILTER_LENGTH_ASK_OOK = "filter_length_ask_ook"
|
||||
CONF_FREEZE = "freeze"
|
||||
CONF_HYST_LEVEL = "hyst_level"
|
||||
CONF_FOC_BS_CS_GATE = "foc_bs_cs_gate"
|
||||
CONF_FOC_LIMIT = "foc_limit"
|
||||
CONF_FOC_PRE_K = "foc_pre_k"
|
||||
CONF_FOC_POST_K = "foc_post_k"
|
||||
CONF_BS_LIMIT = "bs_limit"
|
||||
CONF_BS_PRE_KI = "bs_pre_ki"
|
||||
CONF_BS_PRE_KP = "bs_pre_kp"
|
||||
CONF_BS_POST_KI = "bs_post_ki"
|
||||
CONF_BS_POST_KP = "bs_post_kp"
|
||||
|
||||
# Packet mode config keys
|
||||
CONF_PACKET_MODE = "packet_mode"
|
||||
@@ -171,64 +161,6 @@ HYST_LEVEL = {
|
||||
"High": HystLevel.HYST_LEVEL_HIGH,
|
||||
}
|
||||
|
||||
FocLimit = ns.enum("FocLimit", True)
|
||||
FOC_LIMIT = {
|
||||
"Disabled": FocLimit.FOC_LIMIT_DISABLED,
|
||||
"BW/8": FocLimit.FOC_LIMIT_BW_8,
|
||||
"BW/4": FocLimit.FOC_LIMIT_BW_4,
|
||||
"BW/2": FocLimit.FOC_LIMIT_BW_2,
|
||||
}
|
||||
|
||||
FocPreK = ns.enum("FocPreK", True)
|
||||
FOC_PRE_K = {
|
||||
"K": FocPreK.FOC_PRE_K_K,
|
||||
"2K": FocPreK.FOC_PRE_K_2K,
|
||||
"3K": FocPreK.FOC_PRE_K_3K,
|
||||
"4K": FocPreK.FOC_PRE_K_4K,
|
||||
}
|
||||
|
||||
FocPostK = ns.enum("FocPostK", True)
|
||||
FOC_POST_K = {
|
||||
"Same": FocPostK.FOC_POST_K_SAME,
|
||||
"K/2": FocPostK.FOC_POST_K_K_2,
|
||||
}
|
||||
|
||||
BsLimit = ns.enum("BsLimit", True)
|
||||
BS_LIMIT = {
|
||||
"Disabled": BsLimit.BS_LIMIT_DISABLED,
|
||||
"3.125%": BsLimit.BS_LIMIT_3P125_PERCENT,
|
||||
"6.25%": BsLimit.BS_LIMIT_6P25_PERCENT,
|
||||
"12.5%": BsLimit.BS_LIMIT_12P5_PERCENT,
|
||||
}
|
||||
|
||||
BsPreKi = ns.enum("BsPreKi", True)
|
||||
BS_PRE_KI = {
|
||||
"KI": BsPreKi.BS_PRE_KI_KI,
|
||||
"2KI": BsPreKi.BS_PRE_KI_2KI,
|
||||
"3KI": BsPreKi.BS_PRE_KI_3KI,
|
||||
"4KI": BsPreKi.BS_PRE_KI_4KI,
|
||||
}
|
||||
|
||||
BsPreKp = ns.enum("BsPreKp", True)
|
||||
BS_PRE_KP = {
|
||||
"KP": BsPreKp.BS_PRE_KP_KP,
|
||||
"2KP": BsPreKp.BS_PRE_KP_2KP,
|
||||
"3KP": BsPreKp.BS_PRE_KP_3KP,
|
||||
"4KP": BsPreKp.BS_PRE_KP_4KP,
|
||||
}
|
||||
|
||||
BsPostKi = ns.enum("BsPostKi", True)
|
||||
BS_POST_KI = {
|
||||
"Same": BsPostKi.BS_POST_KI_SAME,
|
||||
"KI/2": BsPostKi.BS_POST_KI_KI_2,
|
||||
}
|
||||
|
||||
BsPostKp = ns.enum("BsPostKp", True)
|
||||
BS_POST_KP = {
|
||||
"Same": BsPostKp.BS_POST_KP_SAME,
|
||||
"KP": BsPostKp.BS_POST_KP_KP,
|
||||
}
|
||||
|
||||
# Optional settings to generate setter calls for
|
||||
CONFIG_MAP = {
|
||||
cv.Optional(CONF_OUTPUT_POWER, default=10): cv.float_range(min=-30.0, max=11.0),
|
||||
@@ -282,15 +214,6 @@ CONFIG_MAP = {
|
||||
cv.Optional(CONF_FREEZE): cv.enum(FREEZE, upper=False),
|
||||
cv.Optional(CONF_WAIT_TIME, default="32"): cv.enum(WAIT_TIME, upper=False),
|
||||
cv.Optional(CONF_HYST_LEVEL): cv.enum(HYST_LEVEL, upper=False),
|
||||
cv.Optional(CONF_FOC_BS_CS_GATE): cv.boolean,
|
||||
cv.Optional(CONF_FOC_LIMIT): cv.enum(FOC_LIMIT, upper=False),
|
||||
cv.Optional(CONF_FOC_PRE_K): cv.enum(FOC_PRE_K, upper=False),
|
||||
cv.Optional(CONF_FOC_POST_K): cv.enum(FOC_POST_K, upper=False),
|
||||
cv.Optional(CONF_BS_LIMIT): cv.enum(BS_LIMIT, upper=False),
|
||||
cv.Optional(CONF_BS_PRE_KI): cv.enum(BS_PRE_KI, upper=False),
|
||||
cv.Optional(CONF_BS_PRE_KP): cv.enum(BS_PRE_KP, upper=False),
|
||||
cv.Optional(CONF_BS_POST_KI): cv.enum(BS_POST_KI, upper=False),
|
||||
cv.Optional(CONF_BS_POST_KP): cv.enum(BS_POST_KP, upper=False),
|
||||
cv.Optional(CONF_PACKET_MODE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PACKET_LENGTH): cv.uint8_t,
|
||||
cv.Optional(CONF_CRC_ENABLE, default=False): cv.boolean,
|
||||
|
||||
@@ -672,69 +672,6 @@ void CC1101Component::set_hyst_level(HystLevel value) {
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_foc_bs_cs_gate(bool value) {
|
||||
this->state_.FOC_BS_CS_GATE = value ? 1 : 0;
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::FOCCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_foc_limit(FocLimit value) {
|
||||
this->state_.FOC_LIMIT = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::FOCCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_foc_pre_k(FocPreK value) {
|
||||
this->state_.FOC_PRE_K = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::FOCCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_foc_post_k(FocPostK value) {
|
||||
this->state_.FOC_POST_K = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::FOCCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_bs_limit(BsLimit value) {
|
||||
this->state_.BS_LIMIT = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::BSCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_bs_pre_ki(BsPreKi value) {
|
||||
this->state_.BS_PRE_KI = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::BSCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_bs_pre_kp(BsPreKp value) {
|
||||
this->state_.BS_PRE_KP = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::BSCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_bs_post_ki(BsPostKi value) {
|
||||
this->state_.BS_POST_KI = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::BSCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_bs_post_kp(BsPostKp value) {
|
||||
this->state_.BS_POST_KP = static_cast<uint8_t>(value);
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::BSCFG);
|
||||
}
|
||||
}
|
||||
|
||||
void CC1101Component::set_packet_mode(bool value) {
|
||||
this->state_.PKT_FORMAT =
|
||||
static_cast<uint8_t>(value ? PacketFormat::PACKET_FORMAT_FIFO : PacketFormat::PACKET_FORMAT_ASYNC_SERIAL);
|
||||
|
||||
@@ -71,17 +71,6 @@ class CC1101Component final : public Component,
|
||||
void set_wait_time(WaitTime value);
|
||||
void set_hyst_level(HystLevel value);
|
||||
|
||||
// Frequency offset compensation and bit synchronization settings
|
||||
void set_foc_bs_cs_gate(bool value);
|
||||
void set_foc_limit(FocLimit value);
|
||||
void set_foc_pre_k(FocPreK value);
|
||||
void set_foc_post_k(FocPostK value);
|
||||
void set_bs_limit(BsLimit value);
|
||||
void set_bs_pre_ki(BsPreKi value);
|
||||
void set_bs_pre_kp(BsPreKp value);
|
||||
void set_bs_post_ki(BsPostKi value);
|
||||
void set_bs_post_kp(BsPostKp value);
|
||||
|
||||
// Packet mode settings
|
||||
void set_packet_mode(bool value);
|
||||
void set_packet_length(uint8_t value);
|
||||
|
||||
@@ -231,56 +231,6 @@ enum class HystLevel : uint8_t {
|
||||
HYST_LEVEL_HIGH,
|
||||
};
|
||||
|
||||
enum class FocLimit : uint8_t {
|
||||
FOC_LIMIT_DISABLED,
|
||||
FOC_LIMIT_BW_8,
|
||||
FOC_LIMIT_BW_4,
|
||||
FOC_LIMIT_BW_2,
|
||||
};
|
||||
|
||||
enum class FocPreK : uint8_t {
|
||||
FOC_PRE_K_K,
|
||||
FOC_PRE_K_2K,
|
||||
FOC_PRE_K_3K,
|
||||
FOC_PRE_K_4K,
|
||||
};
|
||||
|
||||
enum class FocPostK : uint8_t {
|
||||
FOC_POST_K_SAME,
|
||||
FOC_POST_K_K_2,
|
||||
};
|
||||
|
||||
enum class BsLimit : uint8_t {
|
||||
BS_LIMIT_DISABLED,
|
||||
BS_LIMIT_3P125_PERCENT,
|
||||
BS_LIMIT_6P25_PERCENT,
|
||||
BS_LIMIT_12P5_PERCENT,
|
||||
};
|
||||
|
||||
enum class BsPreKi : uint8_t {
|
||||
BS_PRE_KI_KI,
|
||||
BS_PRE_KI_2KI,
|
||||
BS_PRE_KI_3KI,
|
||||
BS_PRE_KI_4KI,
|
||||
};
|
||||
|
||||
enum class BsPreKp : uint8_t {
|
||||
BS_PRE_KP_KP,
|
||||
BS_PRE_KP_2KP,
|
||||
BS_PRE_KP_3KP,
|
||||
BS_PRE_KP_4KP,
|
||||
};
|
||||
|
||||
enum class BsPostKi : uint8_t {
|
||||
BS_POST_KI_SAME,
|
||||
BS_POST_KI_KI_2,
|
||||
};
|
||||
|
||||
enum class BsPostKp : uint8_t {
|
||||
BS_POST_KP_SAME,
|
||||
BS_POST_KP_KP,
|
||||
};
|
||||
|
||||
enum class PacketFormat : uint8_t {
|
||||
PACKET_FORMAT_FIFO,
|
||||
PACKET_FORMAT_SYNC_SERIAL,
|
||||
|
||||
@@ -28,7 +28,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
ESP_LOGD(TAG,
|
||||
"LibreTiny debug info:\n"
|
||||
" Version: %s\n"
|
||||
" Chip: %s (%04x) @ %" PRIu32 " MHz\n"
|
||||
" Chip: %s (%04x) @ %u MHz\n"
|
||||
" Chip ID: 0x%06" PRIX32 "\n"
|
||||
" Board: %s\n"
|
||||
" Flash: %" PRIu32 " KiB\n"
|
||||
@@ -38,7 +38,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
lt_get_board_code(), flash_kib, ram_kib, reset_reason);
|
||||
|
||||
pos = buf_append_str(buf, size, pos, "|Version: ");
|
||||
pos = buf_append_str(buf, size, pos, <_BANNER_STR[10]);
|
||||
pos = buf_append_str(buf, size, pos, LT_BANNER_STR + 10);
|
||||
pos = buf_append_str(buf, size, pos, "|Reset Reason: ");
|
||||
pos = buf_append_str(buf, size, pos, reset_reason);
|
||||
pos = buf_append_str(buf, size, pos, "|Chip Name: ");
|
||||
|
||||
@@ -74,7 +74,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
|
||||
char *buf = buffer.data();
|
||||
|
||||
uint32_t cpu_freq = RP2040::f_cpu();
|
||||
uint32_t cpu_freq = ::rp2040.f_cpu();
|
||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq);
|
||||
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq);
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ from esphome.const import (
|
||||
CONF_SECOND,
|
||||
CONF_SLEEP_DURATION,
|
||||
CONF_TIME_ID,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WAKEUP_PIN,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
@@ -235,15 +234,6 @@ EXT1_WAKEUP_MODES = {
|
||||
}
|
||||
WakeupCauseToRunDuration = deep_sleep_ns.struct("WakeupCauseToRunDuration")
|
||||
|
||||
WakeupCause = deep_sleep_ns.enum("WakeupCause")
|
||||
WakeTrigger = deep_sleep_ns.class_(
|
||||
"WakeTrigger", automation.Trigger.template(WakeupCause), cg.Component
|
||||
)
|
||||
Ext1WakeTrigger = deep_sleep_ns.class_(
|
||||
"Ext1WakeTrigger", automation.Trigger.template(), cg.Component
|
||||
)
|
||||
|
||||
CONF_ON_WAKE = "on_wake"
|
||||
CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode"
|
||||
CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup"
|
||||
CONF_TOUCH_WAKEUP = "touch_wakeup"
|
||||
@@ -266,22 +256,6 @@ WAKEUP_PIN_SCHEMA = cv.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
EXT1_WAKEUP_PIN_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_PIN): cv.All(
|
||||
pins.internal_gpio_input_pin_schema, validate_pin_number_esp32
|
||||
),
|
||||
cv.Optional(CONF_ON_WAKE): automation.validate_automation(
|
||||
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Ext1WakeTrigger)}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
# Entries that are not in the {pin: ..., on_wake: ...} form are treated as a
|
||||
# bare pin config (the original syntax, e.g. a plain "GPIO5" or {number: 5}).
|
||||
validate_ext1_wakeup_pin = cv.maybe_simple_value(EXT1_WAKEUP_PIN_SCHEMA, key=CONF_PIN)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -308,7 +282,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_PINS): cv.ensure_list(
|
||||
validate_ext1_wakeup_pin,
|
||||
pins.internal_gpio_input_pin_schema,
|
||||
validate_pin_number_esp32,
|
||||
),
|
||||
cv.Required(CONF_MODE): cv.All(
|
||||
cv.enum(EXT1_WAKEUP_MODES, upper=True),
|
||||
@@ -317,12 +292,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_ON_WAKE): cv.All(
|
||||
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]),
|
||||
automation.validate_automation(
|
||||
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger)}
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_TOUCH_WAKEUP): cv.All(
|
||||
cv.only_on_esp32,
|
||||
esp32.only_on_variant(
|
||||
@@ -354,7 +323,7 @@ async def to_code(config):
|
||||
if CONF_WAKEUP_PIN in config:
|
||||
pins_as_list = config.get(CONF_WAKEUP_PIN, [])
|
||||
if CORE.is_bk72xx:
|
||||
cg.add(var.init_wakeup_pins(len(pins_as_list)))
|
||||
cg.add(var.init_wakeup_pins_(len(pins_as_list)))
|
||||
for item in pins_as_list:
|
||||
cg.add(
|
||||
var.add_wakeup_pin(
|
||||
@@ -393,27 +362,16 @@ async def to_code(config):
|
||||
)
|
||||
cg.add(var.set_run_duration(wakeup_cause_to_run_duration))
|
||||
|
||||
if (ext1_conf := config.get(CONF_ESP32_EXT1_WAKEUP)) is not None:
|
||||
if CONF_ESP32_EXT1_WAKEUP in config:
|
||||
conf = config[CONF_ESP32_EXT1_WAKEUP]
|
||||
mask = 0
|
||||
for pin_conf in ext1_conf[CONF_PINS]:
|
||||
number = pin_conf[CONF_PIN][CONF_NUMBER]
|
||||
mask |= 1 << number
|
||||
for wake_conf in pin_conf.get(CONF_ON_WAKE, []):
|
||||
trigger = cg.new_Pvariable(wake_conf[CONF_TRIGGER_ID], number)
|
||||
await cg.register_component(trigger, wake_conf)
|
||||
await automation.build_automation(trigger, [], wake_conf)
|
||||
cg.add_define("USE_DEEP_SLEEP_ON_WAKE")
|
||||
for pin in conf[CONF_PINS]:
|
||||
mask |= 1 << pin[CONF_NUMBER]
|
||||
struct = cg.StructInitializer(
|
||||
Ext1Wakeup, ("mask", mask), ("wakeup_mode", ext1_conf[CONF_MODE])
|
||||
Ext1Wakeup, ("mask", mask), ("wakeup_mode", conf[CONF_MODE])
|
||||
)
|
||||
cg.add(var.set_ext1_wakeup(struct))
|
||||
|
||||
for wake_conf in config.get(CONF_ON_WAKE, []):
|
||||
trigger = cg.new_Pvariable(wake_conf[CONF_TRIGGER_ID])
|
||||
await cg.register_component(trigger, wake_conf)
|
||||
await automation.build_automation(trigger, [(WakeupCause, "cause")], wake_conf)
|
||||
cg.add_define("USE_DEEP_SLEEP_ON_WAKE")
|
||||
|
||||
if CONF_TOUCH_WAKEUP in config:
|
||||
cg.add(var.set_touch_wakeup(config[CONF_TOUCH_WAKEUP]))
|
||||
if CORE.using_zephyr and "zigbee" not in CORE.loaded_integrations:
|
||||
|
||||
@@ -7,21 +7,6 @@ namespace esphome::deep_sleep {
|
||||
|
||||
static const char *const TAG = "deep_sleep.bk72xx";
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
WakeupCause get_wakeup_cause() {
|
||||
switch (lt_get_reboot_reason()) {
|
||||
case REBOOT_REASON_SLEEP_GPIO:
|
||||
return WAKEUP_CAUSE_GPIO;
|
||||
case REBOOT_REASON_SLEEP_RTC:
|
||||
return WAKEUP_CAUSE_TIMER;
|
||||
case REBOOT_REASON_SLEEP_USB:
|
||||
return WAKEUP_CAUSE_UNKNOWN;
|
||||
default:
|
||||
return WAKEUP_CAUSE_NONE;
|
||||
}
|
||||
}
|
||||
#endif // USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const { return this->run_duration_; }
|
||||
|
||||
void DeepSleepComponent::dump_config_platform_() {
|
||||
@@ -30,15 +15,15 @@ void DeepSleepComponent::dump_config_platform_() {
|
||||
}
|
||||
}
|
||||
|
||||
bool DeepSleepComponent::pin_prevents_sleep_(WakeUpPinItem &pin_item) const {
|
||||
return (pin_item.wakeup_pin_mode == WAKEUP_PIN_MODE_KEEP_AWAKE && pin_item.wakeup_pin != nullptr &&
|
||||
!this->sleep_duration_.has_value() && (pin_item.wakeup_level == get_real_pin_state_(*pin_item.wakeup_pin)));
|
||||
bool DeepSleepComponent::pin_prevents_sleep_(WakeUpPinItem &pinItem) const {
|
||||
return (pinItem.wakeup_pin_mode == WAKEUP_PIN_MODE_KEEP_AWAKE && pinItem.wakeup_pin != nullptr &&
|
||||
!this->sleep_duration_.has_value() && (pinItem.wakeup_level == get_real_pin_state_(*pinItem.wakeup_pin)));
|
||||
}
|
||||
|
||||
bool DeepSleepComponent::prepare_to_sleep_() {
|
||||
if (!this->wakeup_pins_.empty()) {
|
||||
if (wakeup_pins_.size() > 0) {
|
||||
for (WakeUpPinItem &item : this->wakeup_pins_) {
|
||||
if (this->pin_prevents_sleep_(item)) {
|
||||
if (pin_prevents_sleep_(item)) {
|
||||
// Defer deep sleep until inactive
|
||||
if (!this->next_enter_deep_sleep_) {
|
||||
this->status_set_warning();
|
||||
@@ -59,7 +44,7 @@ void DeepSleepComponent::deep_sleep_() {
|
||||
item.wakeup_level = !item.wakeup_level;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Wake-up on P%u %s (%" PRId32 ")", item.wakeup_pin->get_pin(), item.wakeup_level ? "HIGH" : "LOW",
|
||||
ESP_LOGI(TAG, "Wake-up on P%u %s (%d)", item.wakeup_pin->get_pin(), item.wakeup_level ? "HIGH" : "LOW",
|
||||
static_cast<int32_t>(item.wakeup_pin_mode));
|
||||
}
|
||||
|
||||
|
||||
@@ -60,65 +60,6 @@ struct WakeupCauseToRunDuration {
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
/// Why the device woke from deep sleep. Passed to on_wake automations.
|
||||
enum WakeupCause : uint8_t {
|
||||
/// The device did not wake from deep sleep (for example a cold boot, reset or OTA restart).
|
||||
WAKEUP_CAUSE_NONE = 0,
|
||||
/// The device woke from deep sleep, but the source could not be identified.
|
||||
WAKEUP_CAUSE_UNKNOWN,
|
||||
/// The device was woken by the sleep timer.
|
||||
WAKEUP_CAUSE_TIMER,
|
||||
/// The device was woken by a GPIO pin (wakeup_pin or esp32_ext1_wakeup).
|
||||
WAKEUP_CAUSE_GPIO,
|
||||
/// The device was woken by a touch pad.
|
||||
WAKEUP_CAUSE_TOUCH,
|
||||
};
|
||||
|
||||
/// Return why the device woke from deep sleep. Implemented per platform.
|
||||
WakeupCause get_wakeup_cause();
|
||||
|
||||
/** Setup priority of on_wake triggers.
|
||||
*
|
||||
* Between restoring global variables (setup_priority::HARDWARE, 800) and on_boot automations at
|
||||
* their default priority (600), so on_wake automations can update state (e.g. globals) that
|
||||
* on_boot automations then use.
|
||||
*/
|
||||
inline constexpr float ON_WAKE_TRIGGER_SETUP_PRIORITY = 700.0f;
|
||||
|
||||
/// Fires once on boot when the device woke from deep sleep, with the wakeup cause.
|
||||
class WakeTrigger : public Trigger<WakeupCause>, public Component {
|
||||
public:
|
||||
void setup() override {
|
||||
const WakeupCause cause = get_wakeup_cause();
|
||||
if (cause != WAKEUP_CAUSE_NONE) {
|
||||
this->trigger(cause);
|
||||
}
|
||||
}
|
||||
float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
|
||||
};
|
||||
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
/// Fires once on boot when the device was woken from deep sleep by the given ext1 pin.
|
||||
class Ext1WakeTrigger : public Trigger<>, public Component {
|
||||
public:
|
||||
explicit Ext1WakeTrigger(uint8_t pin) : pin_(pin) {}
|
||||
void setup() override {
|
||||
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1 &&
|
||||
(esp_sleep_get_ext1_wakeup_status() & (1ULL << this->pin_))) {
|
||||
this->trigger();
|
||||
}
|
||||
}
|
||||
float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
|
||||
|
||||
protected:
|
||||
uint8_t pin_;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
template<typename... Ts> class EnterDeepSleepAction;
|
||||
|
||||
template<typename... Ts> class PreventDeepSleepAction;
|
||||
@@ -143,7 +84,7 @@ class DeepSleepComponent final : public Component {
|
||||
#endif // USE_ESP32
|
||||
|
||||
#if defined(USE_BK72XX)
|
||||
void init_wakeup_pins(size_t capacity) { this->wakeup_pins_.init(capacity); }
|
||||
void init_wakeup_pins_(size_t capacity) { this->wakeup_pins_.init(capacity); }
|
||||
void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode) {
|
||||
this->wakeup_pins_.emplace_back(WakeUpPinItem{wakeup_pin, wakeup_pin_mode, !wakeup_pin->is_inverted()});
|
||||
}
|
||||
@@ -191,7 +132,7 @@ class DeepSleepComponent final : public Component {
|
||||
bool should_teardown_();
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
bool pin_prevents_sleep_(WakeUpPinItem &pin_item) const;
|
||||
bool pin_prevents_sleep_(WakeUpPinItem &pinItem) const;
|
||||
bool get_real_pin_state_(InternalGPIOPin &pin) const { return (pin.digital_read() ^ pin.is_inverted()); }
|
||||
#endif // USE_BK72XX
|
||||
|
||||
|
||||
@@ -30,25 +30,6 @@ namespace esphome::deep_sleep {
|
||||
|
||||
static const char *const TAG = "deep_sleep";
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
WakeupCause get_wakeup_cause() {
|
||||
switch (esp_sleep_get_wakeup_cause()) {
|
||||
case ESP_SLEEP_WAKEUP_EXT0:
|
||||
case ESP_SLEEP_WAKEUP_EXT1:
|
||||
case ESP_SLEEP_WAKEUP_GPIO:
|
||||
return WAKEUP_CAUSE_GPIO;
|
||||
case ESP_SLEEP_WAKEUP_TIMER:
|
||||
return WAKEUP_CAUSE_TIMER;
|
||||
case ESP_SLEEP_WAKEUP_TOUCHPAD:
|
||||
return WAKEUP_CAUSE_TOUCH;
|
||||
case ESP_SLEEP_WAKEUP_UNDEFINED:
|
||||
return WAKEUP_CAUSE_NONE;
|
||||
default:
|
||||
return WAKEUP_CAUSE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
#endif // USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
|
||||
if (this->wakeup_cause_to_run_duration_.has_value()) {
|
||||
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
|
||||
|
||||
@@ -3,27 +3,10 @@
|
||||
|
||||
#include <Esp.h>
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
extern "C" {
|
||||
#include <user_interface.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace esphome::deep_sleep {
|
||||
|
||||
static const char *const TAG = "deep_sleep";
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
WakeupCause get_wakeup_cause() {
|
||||
// The ESP8266 can only wake from deep sleep through the RTC timer (via GPIO16 -> RST).
|
||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
||||
if (ESP.getResetInfoPtr()->reason == REASON_DEEP_SLEEP_AWAKE) {
|
||||
return WAKEUP_CAUSE_TIMER;
|
||||
}
|
||||
return WAKEUP_CAUSE_NONE;
|
||||
}
|
||||
#endif // USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const { return this->run_duration_; }
|
||||
|
||||
void DeepSleepComponent::dump_config_platform_() {}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_SLEEP_PIN, CONF_TYPE
|
||||
|
||||
CODEOWNERS = ["@tomwellnitz"]
|
||||
MULTI_CONF = True
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_DS248X_ID = "ds248x_id"
|
||||
CONF_BUS_SLEEP = "bus_sleep"
|
||||
CONF_HUB_SLEEP = "hub_sleep"
|
||||
CONF_ACTIVE_PULLUP = "active_pullup"
|
||||
|
||||
CONF_RESET_LOW_TIME = "reset_low_time"
|
||||
CONF_MASTER_SAMPLE_TIME = "master_sample_time"
|
||||
CONF_WRITE_0_LOW_TIME = "write_0_low_time"
|
||||
CONF_RECOVERY_TIME = "recovery_time"
|
||||
CONF_ACTIVE_PULLUP_RESISTANCE = "active_pullup_resistance"
|
||||
|
||||
TYPE_DS2482_100 = "ds2482-100"
|
||||
TYPE_DS2482_101 = "ds2482-101"
|
||||
TYPE_DS2482_800 = "ds2482-800"
|
||||
TYPE_DS2484 = "ds2484"
|
||||
|
||||
CHANNEL_COUNTS = {
|
||||
TYPE_DS2482_100: 1,
|
||||
TYPE_DS2482_101: 1,
|
||||
TYPE_DS2482_800: 8,
|
||||
TYPE_DS2484: 1,
|
||||
}
|
||||
|
||||
ds248x_ns = cg.esphome_ns.namespace("ds248x")
|
||||
DS248xComponent = ds248x_ns.class_("DS248xComponent", cg.Component, i2c.I2CDevice)
|
||||
|
||||
|
||||
def _component_schema(*extras):
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DS248xComponent),
|
||||
cv.Optional(CONF_ACTIVE_PULLUP, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
for extra in extras:
|
||||
schema = schema.extend(extra)
|
||||
return schema.extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x18))
|
||||
|
||||
|
||||
SLEEP_SCHEMA = {
|
||||
cv.Optional(CONF_SLEEP_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_BUS_SLEEP, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HUB_SLEEP, default=False): cv.boolean,
|
||||
}
|
||||
|
||||
DS2484_SCHEMA = {
|
||||
cv.Optional(CONF_RESET_LOW_TIME): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_MASTER_SAMPLE_TIME): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_WRITE_0_LOW_TIME): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_RECOVERY_TIME): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_ACTIVE_PULLUP_RESISTANCE): cv.enum(
|
||||
{
|
||||
# DS2484 Table 7: value codes 0-5 map to 500 ohm, 6-15 map to 1000 ohm.
|
||||
"500ohm": 0,
|
||||
"1000ohm": 6,
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
TYPE_DS2482_100: _component_schema(),
|
||||
TYPE_DS2482_101: _component_schema(SLEEP_SCHEMA),
|
||||
TYPE_DS2482_800: _component_schema(),
|
||||
TYPE_DS2484: _component_schema(SLEEP_SCHEMA, DS2484_SCHEMA),
|
||||
},
|
||||
key=CONF_TYPE,
|
||||
lower=True,
|
||||
)
|
||||
|
||||
|
||||
def get_channel_count(config):
|
||||
return CHANNEL_COUNTS[config[CONF_TYPE]]
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_active_pullup(config[CONF_ACTIVE_PULLUP]))
|
||||
cg.add(var.set_channel_count(get_channel_count(config)))
|
||||
|
||||
if CONF_BUS_SLEEP in config:
|
||||
cg.add(var.set_bus_sleep(config[CONF_BUS_SLEEP]))
|
||||
if CONF_HUB_SLEEP in config:
|
||||
cg.add(var.set_hub_sleep(config[CONF_HUB_SLEEP]))
|
||||
|
||||
if CONF_RESET_LOW_TIME in config:
|
||||
cg.add(var.set_val_trstl(config[CONF_RESET_LOW_TIME]))
|
||||
if CONF_MASTER_SAMPLE_TIME in config:
|
||||
cg.add(var.set_val_tmsp(config[CONF_MASTER_SAMPLE_TIME]))
|
||||
if CONF_WRITE_0_LOW_TIME in config:
|
||||
cg.add(var.set_val_tw0l(config[CONF_WRITE_0_LOW_TIME]))
|
||||
if CONF_RECOVERY_TIME in config:
|
||||
cg.add(var.set_val_trec0(config[CONF_RECOVERY_TIME]))
|
||||
if CONF_ACTIVE_PULLUP_RESISTANCE in config:
|
||||
cg.add(var.set_val_rwpu(config[CONF_ACTIVE_PULLUP_RESISTANCE]))
|
||||
|
||||
if CONF_SLEEP_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||
cg.add(var.set_sleep_pin(pin))
|
||||
@@ -1,320 +0,0 @@
|
||||
#include "ds248x.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
|
||||
static const char *const TAG = "ds248x";
|
||||
|
||||
void DS248xComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up DS248x...");
|
||||
|
||||
// Wake up device if sleep pin is configured
|
||||
if (this->sleep_pin_) {
|
||||
this->sleep_pin_->setup();
|
||||
this->sleep_pin_->pin_mode(esphome::gpio::FLAG_OUTPUT);
|
||||
this->sleep_pin_->digital_write(true); // Wake up
|
||||
delay(1); // DS2482-101 Datasheet: tOSCWUP = 100μs (using 10x margin)
|
||||
}
|
||||
|
||||
// Probe device
|
||||
ESP_LOGD(TAG, "Probing DS248x...");
|
||||
uint8_t status = 0;
|
||||
if (this->read(&status, 1) == i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Device responded! Status: 0x%02x", status);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Device did not respond. Trying reset anyway...");
|
||||
}
|
||||
|
||||
if (!this->device_reset_()) {
|
||||
ESP_LOGW(TAG, "DS248x reset failed during setup!");
|
||||
}
|
||||
|
||||
// Configure device
|
||||
if (!this->device_configure_()) {
|
||||
ESP_LOGE(TAG, "DS248x configuration failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset to Channel 0
|
||||
this->select_channel(0);
|
||||
|
||||
ESP_LOGI(TAG, "DS248x initialized successfully.");
|
||||
}
|
||||
|
||||
void DS248xComponent::on_shutdown() {
|
||||
if (this->sleep_pin_ && (this->hub_sleep_ || this->bus_sleep_)) {
|
||||
this->sleep_pin_->digital_write(false); // Sleep
|
||||
}
|
||||
}
|
||||
|
||||
void DS248xComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "DS248x:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
ESP_LOGCONFIG(TAG, " Channel Count: %d", this->channel_count_);
|
||||
ESP_LOGCONFIG(TAG, " Active Pullup: %s", YESNO(this->active_pullup_));
|
||||
if (this->ds2484_mode_) {
|
||||
ESP_LOGCONFIG(TAG, " DS2484 Mode: enabled");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Internal Helpers ---
|
||||
|
||||
// Datasheet command durations are sub-2ms; allow a little margin before forcing recovery.
|
||||
static constexpr uint32_t BUSY_TIMEOUT_MS = 5;
|
||||
|
||||
bool DS248xComponent::set_read_pointer_(uint8_t ptr) { return this->write_byte(DS248X_COMMAND_SETREADPTR, ptr); }
|
||||
|
||||
bool DS248xComponent::wait_busy_() {
|
||||
uint32_t start = millis();
|
||||
do {
|
||||
uint8_t status;
|
||||
if (this->read(&status, 1) == i2c::ERROR_OK && !(status & DS248X_STATUS_BUSY))
|
||||
return true;
|
||||
delayMicroseconds(100);
|
||||
} while (millis() - start < BUSY_TIMEOUT_MS);
|
||||
ESP_LOGW(TAG, "DS248x busy timeout");
|
||||
bool recovered = this->device_reset_() && this->device_configure_();
|
||||
this->current_channel_ = -1;
|
||||
if (!recovered) {
|
||||
ESP_LOGE(TAG, "DS248x recovery failed after busy timeout");
|
||||
this->mark_failed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DS248xComponent::device_reset_() {
|
||||
ESP_LOGD(TAG, "Resetting device...");
|
||||
uint8_t cmd = DS248X_COMMAND_RESET;
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
uint8_t status;
|
||||
if (this->read(&status, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
if (!(status & DS248X_STATUS_RST)) {
|
||||
ESP_LOGW(TAG, "Device reset failed (RST bit not set)");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->current_channel_ = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DS248xComponent::device_configure_() {
|
||||
ESP_LOGD(TAG, "Configuring device...");
|
||||
|
||||
if (!this->write_config_()) {
|
||||
ESP_LOGW(TAG, "Config write/verify failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Configured successfully");
|
||||
|
||||
// DS2484 Configuration
|
||||
if (this->ds2484_mode_) {
|
||||
if (this->ds2484_trstl_ != DS2484_PARAM_UNSET &&
|
||||
!this->configure_ds2484_port_(DS2484_PORT_PARAM_TRSTL, this->ds2484_trstl_))
|
||||
return false;
|
||||
if (this->ds2484_tmsp_ != DS2484_PARAM_UNSET &&
|
||||
!this->configure_ds2484_port_(DS2484_PORT_PARAM_TMSP, this->ds2484_tmsp_))
|
||||
return false;
|
||||
if (this->ds2484_tw0l_ != DS2484_PARAM_UNSET &&
|
||||
!this->configure_ds2484_port_(DS2484_PORT_PARAM_TW0L, this->ds2484_tw0l_))
|
||||
return false;
|
||||
if (this->ds2484_trec0_ != DS2484_PARAM_UNSET &&
|
||||
!this->configure_ds2484_port_(DS2484_PORT_PARAM_TREC0, this->ds2484_trec0_))
|
||||
return false;
|
||||
if (this->ds2484_rwpu_ != DS2484_PARAM_UNSET &&
|
||||
!this->configure_ds2484_port_(DS2484_PORT_PARAM_RWPU, this->ds2484_rwpu_))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DS248xComponent::configure_ds2484_port_(uint8_t param, uint8_t val) {
|
||||
uint8_t cmd = DS2484_COMMAND_ADJUSTPORT;
|
||||
// Control Byte format (DS2484 Table 6): P[2:0] in bits 7:5, OD in bit 4, VAL[3:0] in bits 3:0
|
||||
uint8_t data = ((param & 0x07) << 5) | (val & 0x0F);
|
||||
|
||||
// The DS2484 always acknowledges the Adjust 1-Wire Port control byte (datasheet "Adjust
|
||||
// 1-Wire Port"), so a successful write confirms the update. We deliberately do not read
|
||||
// back to verify: a single read of the Port Configuration register always returns the
|
||||
// fixed 8-byte report starting at Byte 1 (tRSTL standard speed), not the parameter that
|
||||
// was just written, so a per-parameter readback comparison would spuriously fail for
|
||||
// tMSP/tW0L/tREC0/RWPU.
|
||||
if (!this->write_byte(cmd, data)) {
|
||||
ESP_LOGW(TAG, "DS2484 port config failed (param %d)", param);
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->set_read_pointer_(DS248X_POINTER_STATUS);
|
||||
}
|
||||
|
||||
bool DS248xComponent::write_config_() {
|
||||
uint8_t config = 0;
|
||||
if (this->active_pullup_)
|
||||
config |= DS248X_CONFIG_ACTIVE_PULLUP;
|
||||
|
||||
// The DS248x only accepts the config byte if the upper nibble is the one's-complement of the lower nibble.
|
||||
uint8_t config_byte = (config & 0x0F) | ((~config & 0x0F) << 4);
|
||||
|
||||
if (!this->write_byte(DS248X_COMMAND_WRITECONFIG, config_byte)) {
|
||||
ESP_LOGW(TAG, "Failed to write config byte");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_CONFIG)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t read_config;
|
||||
if (this->read(&read_config, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Failed to read back config byte");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((read_config & 0x0F) != (config_byte & 0x0F)) {
|
||||
ESP_LOGW(TAG, "Config mismatch! Wrote 0x%02x, Read 0x%02x", config_byte, read_config);
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->set_read_pointer_(DS248X_POINTER_STATUS);
|
||||
}
|
||||
|
||||
// --- Channel Selection ---
|
||||
|
||||
// Channel select codes: write code -> expected read code
|
||||
static constexpr uint8_t CHANNEL_WRITE_CODES[8] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87};
|
||||
static constexpr uint8_t CHANNEL_READ_CODES[8] = {0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87};
|
||||
|
||||
bool DS248xComponent::select_channel(uint8_t channel) {
|
||||
if (this->channel_count_ <= 1)
|
||||
return true;
|
||||
if (channel >= this->channel_count_)
|
||||
return false;
|
||||
|
||||
if (this->current_channel_ == channel)
|
||||
return true;
|
||||
|
||||
if (!this->write_byte(DS248X_COMMAND_CHANNELSELECT, CHANNEL_WRITE_CODES[channel])) {
|
||||
this->current_channel_ = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t read_code;
|
||||
if (this->read(&read_code, 1) != i2c::ERROR_OK) {
|
||||
this->current_channel_ = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (read_code != CHANNEL_READ_CODES[channel]) {
|
||||
ESP_LOGW(TAG, "Channel select failed! Expected 0x%02x, got 0x%02x", CHANNEL_READ_CODES[channel], read_code);
|
||||
this->current_channel_ = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
|
||||
return false;
|
||||
|
||||
this->current_channel_ = channel;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- 1-Wire Bus Operations ---
|
||||
|
||||
bool DS248xComponent::ow_reset(bool &presence) {
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
|
||||
return false;
|
||||
|
||||
uint8_t cmd = DS248X_COMMAND_RESETWIRE;
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
if (!this->wait_busy_()) {
|
||||
ESP_LOGW(TAG, "ow_reset: wait busy failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t status;
|
||||
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "ow_reset: read status failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status & DS248X_STATUS_SD) {
|
||||
ESP_LOGW(TAG, "Short detected on 1-Wire bus!");
|
||||
return false;
|
||||
}
|
||||
|
||||
presence = (status & DS248X_STATUS_PPD);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DS248xComponent::ow_write_byte(uint8_t byte) {
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
|
||||
return false;
|
||||
|
||||
if (!this->wait_busy_()) {
|
||||
ESP_LOGW(TAG, "Device busy before writing byte 0x%02x", byte);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cmd[2] = {DS248X_COMMAND_WRITEBYTE, byte};
|
||||
if (this->write(cmd, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "I2C write failed for byte 0x%02x", byte);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->wait_busy_()) {
|
||||
ESP_LOGW(TAG, "Timeout waiting for write byte to complete!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DS248xComponent::ow_read_byte(uint8_t &byte) {
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
|
||||
return false;
|
||||
|
||||
uint8_t cmd = DS248X_COMMAND_READBYTE;
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
if (!this->wait_busy_())
|
||||
return false;
|
||||
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_DATA))
|
||||
return false;
|
||||
|
||||
if (this->read(&byte, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DS248xComponent::search_triplet(bool search_direction, uint8_t &status) {
|
||||
if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
|
||||
return false;
|
||||
|
||||
// DS248x Datasheet: 1-Wire Triplet command requires 2 bytes:
|
||||
// Byte 1: Command code 0x78
|
||||
// Byte 2: Direction byte (bit 7 = V, search direction if discrepancy)
|
||||
uint8_t buffer[2] = {DS248X_COMMAND_TRIPLET, static_cast<uint8_t>(search_direction ? 0x80 : 0x00)};
|
||||
if (this->write(buffer, 2) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
if (!this->wait_busy_())
|
||||
return false;
|
||||
|
||||
if (this->read(&status, 1) != i2c::ERROR_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome::ds248x
|
||||
@@ -1,133 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// DS248x I2C-to-1-Wire Bridge Family
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-100.pdf
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-800.pdf
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2484.pdf
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
|
||||
// DS248x I2C Commands
|
||||
static constexpr uint8_t DS248X_COMMAND_RESET = 0xF0;
|
||||
static constexpr uint8_t DS248X_COMMAND_SETREADPTR = 0xE1;
|
||||
static constexpr uint8_t DS248X_COMMAND_WRITECONFIG = 0xD2;
|
||||
static constexpr uint8_t DS248X_COMMAND_CHANNELSELECT = 0xC3;
|
||||
static constexpr uint8_t DS248X_COMMAND_RESETWIRE = 0xB4;
|
||||
static constexpr uint8_t DS248X_COMMAND_WRITEBYTE = 0xA5;
|
||||
static constexpr uint8_t DS248X_COMMAND_READBYTE = 0x96;
|
||||
static constexpr uint8_t DS248X_COMMAND_TRIPLET = 0x78;
|
||||
static constexpr uint8_t DS2484_COMMAND_ADJUSTPORT = 0xC3;
|
||||
|
||||
// DS2484 "Adjust 1-Wire Port" parameter codes (datasheet Table 6, control byte P[2:0])
|
||||
static constexpr uint8_t DS2484_PORT_PARAM_TRSTL = 0x0;
|
||||
static constexpr uint8_t DS2484_PORT_PARAM_TMSP = 0x1;
|
||||
static constexpr uint8_t DS2484_PORT_PARAM_TW0L = 0x2;
|
||||
static constexpr uint8_t DS2484_PORT_PARAM_TREC0 = 0x3;
|
||||
static constexpr uint8_t DS2484_PORT_PARAM_RWPU = 0x4;
|
||||
|
||||
// DS248x Status Register Bits
|
||||
static constexpr uint8_t DS248X_STATUS_BUSY = 0x01;
|
||||
static constexpr uint8_t DS248X_STATUS_PPD = 0x02;
|
||||
static constexpr uint8_t DS248X_STATUS_SD = 0x04;
|
||||
static constexpr uint8_t DS248X_STATUS_RST = 0x10;
|
||||
static constexpr uint8_t DS248X_STATUS_SBR = 0x20;
|
||||
static constexpr uint8_t DS248X_STATUS_TSB = 0x40;
|
||||
static constexpr uint8_t DS248X_STATUS_DIR = 0x80;
|
||||
|
||||
// DS248x Register Pointers
|
||||
static constexpr uint8_t DS248X_POINTER_STATUS = 0xF0;
|
||||
static constexpr uint8_t DS248X_POINTER_DATA = 0xE1;
|
||||
static constexpr uint8_t DS248X_POINTER_CONFIG = 0xC3;
|
||||
|
||||
// DS248x Configuration Bits
|
||||
static constexpr uint8_t DS248X_CONFIG_ACTIVE_PULLUP = 0x01;
|
||||
|
||||
/**
|
||||
* @brief DS248x I2C-to-1-Wire Bridge Component.
|
||||
*
|
||||
* This component manages the DS248x chip (DS2482-100, DS2482-800, DS2484).
|
||||
* It provides low-level 1-Wire bus operations via I2C.
|
||||
*
|
||||
* Usage: Configure DS248xOneWireBus instances for each channel.
|
||||
* These buses implement the one_wire::OneWireBus interface for compatibility
|
||||
* with all existing 1-Wire device components (dallas_temp, etc.).
|
||||
*/
|
||||
class DS248xComponent : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void on_shutdown() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void set_sleep_pin(InternalGPIOPin *pin) { this->sleep_pin_ = pin; }
|
||||
void set_bus_sleep(bool enabled) { this->bus_sleep_ = enabled; }
|
||||
void set_hub_sleep(bool enabled) { this->hub_sleep_ = enabled; }
|
||||
void set_channel_count(uint8_t count) { this->channel_count_ = count; }
|
||||
void set_active_pullup(bool enabled) { this->active_pullup_ = enabled; }
|
||||
|
||||
// DS2484 Timing Parameters
|
||||
void set_val_trstl(uint8_t val) {
|
||||
this->ds2484_trstl_ = val;
|
||||
this->ds2484_mode_ = true;
|
||||
}
|
||||
void set_val_tmsp(uint8_t val) {
|
||||
this->ds2484_tmsp_ = val;
|
||||
this->ds2484_mode_ = true;
|
||||
}
|
||||
void set_val_tw0l(uint8_t val) {
|
||||
this->ds2484_tw0l_ = val;
|
||||
this->ds2484_mode_ = true;
|
||||
}
|
||||
void set_val_trec0(uint8_t val) {
|
||||
this->ds2484_trec0_ = val;
|
||||
this->ds2484_mode_ = true;
|
||||
}
|
||||
void set_val_rwpu(uint8_t val) {
|
||||
this->ds2484_rwpu_ = val;
|
||||
this->ds2484_mode_ = true;
|
||||
}
|
||||
|
||||
/// Get the channel count (1 for DS2482-100/DS2484, 8 for DS2482-800)
|
||||
uint8_t get_channel_count() const { return this->channel_count_; }
|
||||
|
||||
// --- Core 1-Wire API (used by DS248xOneWireBus) ---
|
||||
bool select_channel(uint8_t channel);
|
||||
bool ow_reset(bool &presence);
|
||||
bool ow_write_byte(uint8_t byte);
|
||||
bool ow_read_byte(uint8_t &byte);
|
||||
|
||||
// --- Search support (used by DS248xOneWireBus) ---
|
||||
bool search_triplet(bool search_direction, uint8_t &status);
|
||||
|
||||
protected:
|
||||
InternalGPIOPin *sleep_pin_{nullptr};
|
||||
uint8_t channel_count_ = 1;
|
||||
bool bus_sleep_{false};
|
||||
bool hub_sleep_{false};
|
||||
bool active_pullup_ = false;
|
||||
|
||||
// DS2484 Config
|
||||
bool ds2484_mode_ = false;
|
||||
static constexpr uint8_t DS2484_PARAM_UNSET = 0xFF;
|
||||
uint8_t ds2484_trstl_{DS2484_PARAM_UNSET};
|
||||
uint8_t ds2484_tmsp_{DS2484_PARAM_UNSET};
|
||||
uint8_t ds2484_tw0l_{DS2484_PARAM_UNSET};
|
||||
uint8_t ds2484_trec0_{DS2484_PARAM_UNSET};
|
||||
uint8_t ds2484_rwpu_{DS2484_PARAM_UNSET};
|
||||
|
||||
int8_t current_channel_{-1};
|
||||
|
||||
// Internal helpers
|
||||
bool set_read_pointer_(uint8_t ptr);
|
||||
bool wait_busy_();
|
||||
bool device_reset_();
|
||||
bool device_configure_();
|
||||
bool configure_ds2484_port_(uint8_t param, uint8_t val);
|
||||
bool write_config_();
|
||||
};
|
||||
|
||||
} // namespace esphome::ds248x
|
||||
@@ -1,171 +0,0 @@
|
||||
#include "ds248x_one_wire_bus.h"
|
||||
#include "ds248x.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
|
||||
static const char *const TAG = "ds248x.one_wire";
|
||||
|
||||
void DS248xOneWireBus::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up DS248x 1-Wire Bus (Channel %d)...", this->channel_);
|
||||
|
||||
// Parent setup happens in DS248xComponent::setup()
|
||||
// We just need to scan for devices on this channel
|
||||
if (!this->ensure_channel_()) {
|
||||
ESP_LOGE(TAG, "Failed to select channel %d during setup", this->channel_);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform device search on this channel
|
||||
this->search();
|
||||
|
||||
ESP_LOGCONFIG(TAG, "Found %zu devices on channel %d", this->devices_.size(), this->channel_);
|
||||
}
|
||||
|
||||
void DS248xOneWireBus::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "DS248x 1-Wire Bus (Channel %d):", this->channel_);
|
||||
this->dump_devices_(TAG);
|
||||
}
|
||||
|
||||
bool DS248xOneWireBus::ensure_channel_() {
|
||||
if (this->parent_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Parent not set!");
|
||||
return false;
|
||||
}
|
||||
return this->parent_->select_channel(this->channel_);
|
||||
}
|
||||
|
||||
int DS248xOneWireBus::reset_int() {
|
||||
if (!this->ensure_channel_()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool presence = false;
|
||||
if (!this->parent_->ow_reset(presence)) {
|
||||
return -1;
|
||||
}
|
||||
return presence ? 1 : 0;
|
||||
}
|
||||
|
||||
void DS248xOneWireBus::write8(uint8_t val) {
|
||||
if (!this->ensure_channel_()) {
|
||||
return;
|
||||
}
|
||||
if (!this->parent_->ow_write_byte(val)) {
|
||||
ESP_LOGE(TAG, "Failed to write byte 0x%02X on channel %d", val, this->channel_);
|
||||
}
|
||||
}
|
||||
|
||||
void DS248xOneWireBus::write64(uint64_t val) {
|
||||
if (!this->ensure_channel_()) {
|
||||
return;
|
||||
}
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
uint8_t byte = static_cast<uint8_t>(val >> (i * 8));
|
||||
if (!this->parent_->ow_write_byte(byte)) {
|
||||
ESP_LOGE(TAG, "Failed to write byte %d/8 (0x%02X) on channel %d - aborting write64", i + 1, byte, this->channel_);
|
||||
return; // Stop writing to prevent sending corrupted data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DS248xOneWireBus::read8() {
|
||||
if (!this->ensure_channel_()) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t value = 0;
|
||||
if (!this->parent_->ow_read_byte(value)) {
|
||||
ESP_LOGE(TAG, "Failed to read byte on channel %d", this->channel_);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t DS248xOneWireBus::read64() {
|
||||
if (!this->ensure_channel_()) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t value = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
uint8_t byte = 0;
|
||||
if (!this->parent_->ow_read_byte(byte)) {
|
||||
ESP_LOGE(TAG, "Failed to read byte %d/8 on channel %d - returning partial data", i + 1, this->channel_);
|
||||
return value; // Return partial data to avoid blocking, caller should validate
|
||||
}
|
||||
value |= (static_cast<uint64_t>(byte) << (i * 8));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void DS248xOneWireBus::reset_search() {
|
||||
this->search_last_discrepancy_ = 0;
|
||||
this->search_last_device_flag_ = false;
|
||||
this->search_address_ = 0;
|
||||
}
|
||||
|
||||
uint64_t DS248xOneWireBus::search_int() {
|
||||
if (!this->ensure_channel_()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->search_last_device_flag_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t last_zero = 0;
|
||||
uint64_t address = this->search_address_;
|
||||
|
||||
// Iterate through all 64 bits
|
||||
for (uint8_t bit_number = 1; bit_number <= 64; bit_number++) {
|
||||
uint64_t bit_mask = 1ULL << (bit_number - 1);
|
||||
|
||||
// Determine search direction
|
||||
bool search_direction;
|
||||
if (bit_number < this->search_last_discrepancy_) {
|
||||
search_direction = (address & bit_mask) != 0;
|
||||
} else {
|
||||
search_direction = (bit_number == this->search_last_discrepancy_);
|
||||
}
|
||||
|
||||
// Perform triplet operation
|
||||
uint8_t status = 0;
|
||||
if (!this->parent_->search_triplet(search_direction, status)) {
|
||||
ESP_LOGW(TAG, "1-Wire triplet failed at bit %d on channel %d - aborting search", bit_number, this->channel_);
|
||||
this->reset_search();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool id_bit = (status & DS248X_STATUS_SBR) != 0;
|
||||
bool cmp_id_bit = (status & DS248X_STATUS_TSB) != 0;
|
||||
bool dir_taken = (status & DS248X_STATUS_DIR) != 0;
|
||||
|
||||
if (id_bit && cmp_id_bit) {
|
||||
// No devices participating
|
||||
this->reset_search();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!id_bit && !cmp_id_bit && !dir_taken) {
|
||||
// Discrepancy, went 0 - record position
|
||||
last_zero = bit_number;
|
||||
}
|
||||
|
||||
// Update address based on direction taken
|
||||
if (dir_taken) {
|
||||
address |= bit_mask;
|
||||
} else {
|
||||
address &= ~bit_mask;
|
||||
}
|
||||
}
|
||||
|
||||
// Search successful
|
||||
this->search_last_discrepancy_ = last_zero;
|
||||
if (last_zero == 0) {
|
||||
this->search_last_device_flag_ = true;
|
||||
}
|
||||
this->search_address_ = address;
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
} // namespace esphome::ds248x
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/one_wire/one_wire_bus.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
|
||||
class DS248xComponent;
|
||||
|
||||
/**
|
||||
* @brief OneWireBus implementation for DS248x I2C-to-1-Wire bridges.
|
||||
*
|
||||
* This class wraps the DS248xComponent to provide the one_wire::OneWireBus interface,
|
||||
* enabling compatibility with all existing 1-Wire device components (dallas_temp, etc.).
|
||||
*
|
||||
* For DS2482-800, multiple instances of this class can be created (one per channel).
|
||||
* For DS2482-100/DS2484, a single instance is used.
|
||||
*/
|
||||
class DS248xOneWireBus : public one_wire::OneWireBus, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS - 1.0f; }
|
||||
|
||||
/// Set the parent DS248x component
|
||||
void set_parent(DS248xComponent *parent) { this->parent_ = parent; }
|
||||
|
||||
/// Set the 1-Wire channel (0-7, only relevant for DS2482-800)
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
|
||||
/// Get the channel number
|
||||
uint8_t get_channel() const { return this->channel_; }
|
||||
|
||||
// OneWireBus interface implementation
|
||||
int reset_int() override;
|
||||
void write8(uint8_t val) override;
|
||||
void write64(uint64_t val) override;
|
||||
uint8_t read8() override;
|
||||
uint64_t read64() override;
|
||||
|
||||
protected:
|
||||
void reset_search() override;
|
||||
uint64_t search_int() override;
|
||||
|
||||
/// Select the channel on the DS248x before any 1-Wire operation
|
||||
bool ensure_channel_();
|
||||
|
||||
DS248xComponent *parent_{nullptr};
|
||||
uint8_t channel_{0};
|
||||
|
||||
// Search state
|
||||
uint64_t search_address_{0};
|
||||
uint8_t search_last_discrepancy_{0};
|
||||
bool search_last_device_flag_{false};
|
||||
};
|
||||
|
||||
} // namespace esphome::ds248x
|
||||
@@ -1,56 +0,0 @@
|
||||
"""DS248x 1-Wire Bus Platform.
|
||||
|
||||
This platform creates one_wire bus instances backed by a DS248x I2C-to-1-Wire bridge.
|
||||
It supports DS2482-100/101 (single channel), DS2482-800 (8 channels), and DS2484 (single channel).
|
||||
|
||||
For multi-channel devices (DS2482-800), create one platform entry per channel.
|
||||
Each entry becomes a separate one_wire bus that can be used by dallas_temp and other 1-Wire devices.
|
||||
"""
|
||||
|
||||
from esphome import final_validate as fv
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.one_wire import OneWireBus
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
|
||||
from . import CONF_DS248X_ID, DS248xComponent, ds248x_ns, get_channel_count
|
||||
|
||||
CODEOWNERS = ["@tomwellnitz"]
|
||||
DEPENDENCIES = ["ds248x"]
|
||||
|
||||
DS248xOneWireBus = ds248x_ns.class_("DS248xOneWireBus", OneWireBus, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DS248xOneWireBus),
|
||||
cv.GenerateID(CONF_DS248X_ID): cv.use_id(DS248xComponent),
|
||||
cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=7),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def _final_validate(config):
|
||||
"""Validate that the channel is within the parent's channel count."""
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_DS248X_ID])[:-1]
|
||||
parent_config = fconf.get_config_for_path(path)
|
||||
channel_count = get_channel_count(parent_config)
|
||||
channel = config[CONF_CHANNEL]
|
||||
|
||||
if channel >= channel_count:
|
||||
raise cv.Invalid(
|
||||
f"Channel {channel} is invalid for DS248x with {channel_count} channel(s). "
|
||||
f"Valid range: 0-{channel_count - 1}"
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[CONF_DS248X_ID])
|
||||
cg.add(var.set_parent(parent))
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
@@ -14,9 +14,10 @@ void EPaperMono::refresh_screen(bool partial) {
|
||||
}
|
||||
|
||||
void EPaperMono::deep_sleep() {
|
||||
// Deep sleep loses RAM so cannot be used with partial update
|
||||
if (!this->is_using_partial_update_()) {
|
||||
ESP_LOGV(TAG, "Deep sleep");
|
||||
ESP_LOGV(TAG, "Deep sleep");
|
||||
if (this->is_using_partial_update_()) {
|
||||
this->cmd_data(0x10, {0x00}); // sleep in power on mode
|
||||
} else {
|
||||
this->cmd_data(0x10, {0x03}); // deep sleep
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3061,23 +3061,19 @@ def copy_files():
|
||||
|
||||
|
||||
def _decode_pc(config, addr):
|
||||
# Convert toolchain-resolution errors (e.g. missing build dir / cmake
|
||||
# cache) into EsphomeError. The api log processor stops decoding on any
|
||||
# exception, so this is about the message it reports rather than about
|
||||
# catching it at all: EsphomeError carries an explanation worth showing
|
||||
# the user, where a raw OSError repr does not.
|
||||
# _decode_pc runs from the api log processor's asyncio callback, which
|
||||
# only catches EsphomeError. Any other exception escaping here tears down
|
||||
# the protocol and triggers an infinite reconnect/replay loop. Convert
|
||||
# toolchain-resolution errors (e.g. missing build dir / cmake cache) into
|
||||
# EsphomeError so the caller can disable decoding cleanly.
|
||||
if CORE.using_toolchain_esp_idf:
|
||||
from esphome.espidf import toolchain as idf_toolchain
|
||||
|
||||
try:
|
||||
addr2line_path = idf_toolchain.get_addr2line_path()
|
||||
firmware_elf_path = idf_toolchain.get_elf_path()
|
||||
except (RuntimeError, OSError) as err:
|
||||
# OSError covers a missing build directory or a cmake that isn't
|
||||
# on PATH; both surface from the subprocess call, not as RuntimeError.
|
||||
except RuntimeError as err:
|
||||
raise EsphomeError(f"ESP-IDF toolchain not available: {err}") from err
|
||||
if not firmware_elf_path.is_file():
|
||||
raise EsphomeError(f"Firmware ELF not found: {firmware_elf_path}")
|
||||
else:
|
||||
from esphome.platformio import toolchain
|
||||
|
||||
|
||||
@@ -181,6 +181,12 @@ const char *ESPBTUUID::to_str(std::span<char, UUID_STR_LEN> output) const {
|
||||
return output.data();
|
||||
}
|
||||
}
|
||||
std::string ESPBTUUID::to_string() const {
|
||||
char buf[UUID_STR_LEN];
|
||||
this->to_str(buf);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
} // namespace esphome::esp32_ble
|
||||
|
||||
#endif // USE_ESP32_BLE_UUID
|
||||
|
||||
@@ -46,6 +46,9 @@ class ESPBTUUID {
|
||||
|
||||
esp_bt_uuid_t get_uuid() const;
|
||||
|
||||
// Remove before 2026.8.0
|
||||
ESPDEPRECATED("Use to_str() instead. Removed in 2026.8.0", "2026.2.0")
|
||||
std::string to_string() const; // NOLINT
|
||||
const char *to_str(std::span<char, UUID_STR_LEN> output) const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -77,10 +77,6 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
if (this->handle_ != param->write.handle)
|
||||
break;
|
||||
if (param->write.len > this->value_.attr_max_len) {
|
||||
ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", param->write.len, this->value_.attr_max_len);
|
||||
break;
|
||||
}
|
||||
this->value_.attr_len = param->write.len;
|
||||
memcpy(this->value_.attr_value, param->write.value, param->write.len);
|
||||
if (this->on_write_callback_) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
@@ -20,9 +21,15 @@ from esphome.const import (
|
||||
PLATFORM_ESP8266,
|
||||
ThreadModel,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
CoroPriority,
|
||||
EsphomeError,
|
||||
Lambda,
|
||||
coroutine_with_priority,
|
||||
)
|
||||
from esphome.core.config import BOARD_MAX_LENGTH
|
||||
from esphome.helpers import copy_file_if_changed
|
||||
from esphome.helpers import IS_MACOS, copy_file_if_changed
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .boards import BOARDS, ESP8266_LD_SCRIPTS
|
||||
@@ -237,6 +244,32 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def check_rosetta() -> None:
|
||||
"""Fail fast when the x86_64 ESP8266 toolchain cannot run on this Mac.
|
||||
|
||||
There is no native arm64 build of the xtensa-lx106 toolchain; on Apple
|
||||
Silicon it runs under Rosetta 2, which macOS updates can remove.
|
||||
"""
|
||||
if not IS_MACOS or platform.machine() != "arm64":
|
||||
return
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["/usr/bin/arch", "-x86_64", "/usr/bin/true"],
|
||||
capture_output=True,
|
||||
close_fds=False,
|
||||
check=False,
|
||||
)
|
||||
except OSError:
|
||||
return # arch(1) unavailable; let the build proceed
|
||||
if result.returncode != 0:
|
||||
raise EsphomeError(
|
||||
"ESP8266 builds on Apple Silicon Macs use an Intel (x86_64) "
|
||||
"compiler that requires Rosetta 2, which is not installed on "
|
||||
"this system. Install it with:\n"
|
||||
" softwareupdate --install-rosetta --agree-to-license"
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.PLATFORM)
|
||||
async def to_code(config):
|
||||
cg.add(esp8266_ns.setup_preferences())
|
||||
@@ -278,16 +311,7 @@ async def to_code(config):
|
||||
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[
|
||||
f"platformio/framework-arduinoespressif8266@{conf[CONF_SOURCE]}",
|
||||
# Override the ancient tool-esptoolpy ~1.30000.0 pinned by
|
||||
# platform-espressif8266 4.2.1 with the same 5.x build the
|
||||
# pioarduino ESP32 platform uses, so both platforms share the
|
||||
# same installed package and stop reinstalling on every switch.
|
||||
# The 0.0.1 path component is pioarduino's stable registry
|
||||
# release tag (not the tool version); the tool itself is 5.2.0.
|
||||
"pioarduino/tool-esptoolpy@https://github.com/pioarduino/registry/releases/download/0.0.1/esptoolpy-v5.2.0.zip",
|
||||
],
|
||||
[f"platformio/framework-arduinoespressif8266@{conf[CONF_SOURCE]}"],
|
||||
)
|
||||
|
||||
# Default for platformio is LWIP2_LOW_MEMORY with:
|
||||
|
||||
@@ -112,10 +112,8 @@ enum class EthernetComponentState : uint8_t {
|
||||
|
||||
// Platform-neutral duplex/speed types
|
||||
#ifndef USE_ESP32
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
enum eth_duplex_t { ETH_DUPLEX_HALF, ETH_DUPLEX_FULL };
|
||||
enum eth_speed_t { ETH_SPEED_10M, ETH_SPEED_100M };
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
#endif
|
||||
|
||||
class EthernetComponent final : public Component {
|
||||
|
||||
@@ -187,18 +187,17 @@ void EthernetComponent::loop() {
|
||||
}
|
||||
|
||||
void EthernetComponent::dump_config() {
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
const char *type_str = "W5500";
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
const char *type_str = "W5100";
|
||||
#elif defined(USE_ETHERNET_W6100)
|
||||
const char *type_str = "W6100";
|
||||
#elif defined(USE_ETHERNET_W6300)
|
||||
const char *type_str = "W6300";
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
const char *type_str = "ENC28J60";
|
||||
#else
|
||||
const char *type_str = "Unknown";
|
||||
#if defined(USE_ETHERNET_W5500)
|
||||
type_str = "W5500";
|
||||
#elif defined(USE_ETHERNET_W5100)
|
||||
type_str = "W5100";
|
||||
#elif defined(USE_ETHERNET_W6100)
|
||||
type_str = "W6100";
|
||||
#elif defined(USE_ETHERNET_W6300)
|
||||
type_str = "W6300";
|
||||
#elif defined(USE_ETHERNET_ENC28J60)
|
||||
type_str = "ENC28J60";
|
||||
#endif
|
||||
#if defined(USE_ETHERNET_W6300)
|
||||
// W6300 uses PIO QSPI with hardcoded pins — SPI pin fields are not used
|
||||
|
||||
@@ -83,7 +83,7 @@ void FanCall::validate_() {
|
||||
auto traits = this->parent_.get_traits();
|
||||
|
||||
if (this->speed_.has_value()) {
|
||||
this->speed_ = clamp(*this->speed_, 1, static_cast<int>(traits.supported_speed_count()));
|
||||
this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
|
||||
|
||||
// https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
|
||||
// "Manually setting a speed must disable any set preset mode"
|
||||
|
||||
@@ -14,7 +14,7 @@ class FanTraits {
|
||||
|
||||
public:
|
||||
FanTraits() = default;
|
||||
FanTraits(bool oscillation, bool speed, bool direction, uint8_t speed_count)
|
||||
FanTraits(bool oscillation, bool speed, bool direction, int speed_count)
|
||||
: oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {}
|
||||
|
||||
/// Return if this fan supports oscillation.
|
||||
@@ -26,9 +26,9 @@ class FanTraits {
|
||||
/// Set whether this fan supports speed levels.
|
||||
void set_speed(bool speed) { this->speed_ = speed; }
|
||||
/// Return how many speed levels the fan has
|
||||
uint8_t supported_speed_count() const { return this->speed_count_; }
|
||||
int supported_speed_count() const { return this->speed_count_; }
|
||||
/// Set how many speed levels this fan has.
|
||||
void set_supported_speed_count(uint8_t speed_count) { this->speed_count_ = speed_count; }
|
||||
void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; }
|
||||
/// Return if this fan supports changing direction
|
||||
bool supports_direction() const { return this->direction_; }
|
||||
/// Set whether this fan supports changing direction
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "fastled_light.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
@@ -132,7 +132,7 @@ void HaierClimateBase::save_settings() {
|
||||
}
|
||||
|
||||
bool HaierClimateBase::get_display_state() const {
|
||||
return (this->display_status_ == SwitchState::SWITCH_ON) || (this->display_status_ == SwitchState::PENDING_ON);
|
||||
return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON);
|
||||
}
|
||||
|
||||
void HaierClimateBase::set_display_state(bool state) {
|
||||
@@ -144,7 +144,7 @@ void HaierClimateBase::set_display_state(bool state) {
|
||||
}
|
||||
|
||||
bool HaierClimateBase::get_health_mode() const {
|
||||
return (this->health_mode_ == SwitchState::SWITCH_ON) || (this->health_mode_ == SwitchState::PENDING_ON);
|
||||
return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON);
|
||||
}
|
||||
|
||||
void HaierClimateBase::set_health_mode(bool state) {
|
||||
|
||||
@@ -147,8 +147,8 @@ class HaierClimateBase : public esphome::Component,
|
||||
esphome::optional<haier_protocol::HaierMessage> message;
|
||||
};
|
||||
enum class SwitchState {
|
||||
SWITCH_OFF = 0b00,
|
||||
SWITCH_ON = 0b01,
|
||||
OFF = 0b00,
|
||||
ON = 0b01,
|
||||
PENDING_OFF = 0b10,
|
||||
PENDING_ON = 0b11,
|
||||
};
|
||||
@@ -157,8 +157,8 @@ class HaierClimateBase : public esphome::Component,
|
||||
esphome::optional<PendingAction> action_request_;
|
||||
uint8_t fan_mode_speed_;
|
||||
uint8_t other_modes_fan_speed_;
|
||||
SwitchState display_status_{SwitchState::SWITCH_ON};
|
||||
SwitchState health_mode_{SwitchState::SWITCH_OFF};
|
||||
SwitchState display_status_{SwitchState::ON};
|
||||
SwitchState health_mode_{SwitchState::OFF};
|
||||
bool force_send_control_;
|
||||
bool forced_request_status_;
|
||||
bool reset_protocol_request_;
|
||||
|
||||
@@ -50,7 +50,7 @@ void HonClimate::set_quiet_mode_state(bool state) {
|
||||
this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||
this->force_send_control_ = true;
|
||||
} else {
|
||||
this->quiet_mode_state_ = state ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->quiet_mode_state_ = state ? SwitchState::ON : SwitchState::OFF;
|
||||
}
|
||||
this->settings_.quiet_mode_state = state;
|
||||
#ifdef USE_SWITCH
|
||||
@@ -63,7 +63,7 @@ void HonClimate::set_quiet_mode_state(bool state) {
|
||||
}
|
||||
|
||||
bool HonClimate::get_quiet_mode_state() const {
|
||||
return (this->quiet_mode_state_ == SwitchState::SWITCH_ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
|
||||
return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
|
||||
}
|
||||
|
||||
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
||||
@@ -513,7 +513,7 @@ void HonClimate::initialization() {
|
||||
}
|
||||
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
||||
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
||||
this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::ON : SwitchState::OFF;
|
||||
}
|
||||
|
||||
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||
@@ -939,14 +939,14 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||
// AC just turned on from remote need to turn off display
|
||||
this->force_send_control_ = true;
|
||||
} else if ((((uint8_t) this->display_status_) & 0b10) == 0) {
|
||||
this->display_status_ = disp_status ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Health mode
|
||||
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||
bool old_health_mode = this->get_health_mode();
|
||||
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||
}
|
||||
{
|
||||
@@ -1008,7 +1008,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||
// In proper mode and not in pending state
|
||||
bool new_quiet_mode = packet.control.quiet_mode != 0;
|
||||
if (new_quiet_mode != this->get_quiet_mode_state()) {
|
||||
this->quiet_mode_state_ = new_quiet_mode ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
|
||||
this->settings_.quiet_mode_state = new_quiet_mode;
|
||||
#ifdef USE_SWITCH
|
||||
if (this->quiet_mode_switch_ != nullptr) {
|
||||
|
||||
@@ -197,7 +197,7 @@ class HonClimate final : public HaierClimateBase {
|
||||
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
||||
HonSettings settings_{};
|
||||
ESPPreferenceObject hon_rtc_;
|
||||
SwitchState quiet_mode_state_{SwitchState::SWITCH_OFF};
|
||||
SwitchState quiet_mode_state_{SwitchState::OFF};
|
||||
};
|
||||
|
||||
} // namespace esphome::haier
|
||||
|
||||
@@ -464,14 +464,14 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
|
||||
// AC just turned on from remote need to turn off display
|
||||
this->force_send_control_ = true;
|
||||
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||
this->display_status_ = disp_status ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Health mode
|
||||
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||
bool old_health_mode = this->get_health_mode();
|
||||
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF;
|
||||
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||
}
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
||||
DECAY_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1, max=255),
|
||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ enum DecayMode {
|
||||
|
||||
class HBridgeFan final : public Component, public fan::Fan {
|
||||
public:
|
||||
HBridgeFan(uint8_t speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
||||
HBridgeFan(int speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
||||
|
||||
void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; }
|
||||
void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; }
|
||||
@@ -35,7 +35,7 @@ class HBridgeFan final : public Component, public fan::Fan {
|
||||
output::FloatOutput *pin_b_;
|
||||
output::FloatOutput *enable_{nullptr};
|
||||
output::BinaryOutput *oscillating_{nullptr};
|
||||
uint8_t speed_count_{};
|
||||
int speed_count_{};
|
||||
DecayMode decay_mode_{DECAY_MODE_SLOW};
|
||||
fan::FanTraits traits_;
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ void HeatpumpIRClimate::transmit_state() {
|
||||
swing_h_cmd = HDIR_SWING;
|
||||
}
|
||||
|
||||
switch (this->fan_mode.value_or(climate::CLIMATE_FAN_ON)) {
|
||||
switch (this->fan_mode.value_or(climate::CLIMATE_FAN_AUTO)) {
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
fan_speed_cmd = FAN_2;
|
||||
break;
|
||||
|
||||
@@ -242,7 +242,7 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[1] != HlkFm22xResult::SUCCEEDED) {
|
||||
if (data[1] != HlkFm22xResult::SUCCESS) {
|
||||
ESP_LOGE(TAG, "Command <0x%.2X> failed. Error: 0x%.2X", data[0], data[1]);
|
||||
switch (expected) {
|
||||
case HlkFm22xCommand::ENROLL:
|
||||
|
||||
@@ -41,7 +41,7 @@ enum HlkFm22xNoteType {
|
||||
};
|
||||
|
||||
enum HlkFm22xResult {
|
||||
SUCCEEDED = 0x00,
|
||||
SUCCESS = 0x00,
|
||||
REJECTED = 0x01,
|
||||
ABORTED = 0x02,
|
||||
FAILED4_CAMERA = 0x04,
|
||||
|
||||
@@ -61,7 +61,7 @@ void HM3301Component::update() {
|
||||
int16_t aqi_value = -1;
|
||||
if (this->aqi_sensor_ != nullptr && pm_2_5_value != -1 && pm_10_0_value != -1) {
|
||||
aqi::AbstractAQICalculator *calculator = this->aqi_calculator_factory_.get_calculator(this->aqi_calc_type_);
|
||||
aqi_value = calculator->get_aqi(pm_2_5_value, pm_10_0_value, /*extended_range=*/false);
|
||||
aqi_value = calculator->get_aqi(pm_2_5_value, pm_10_0_value);
|
||||
}
|
||||
|
||||
if (pm_1_0_value != -1) {
|
||||
|
||||
@@ -488,10 +488,10 @@ template<typename... Ts> class HttpRequestSendAction final : public Action<Ts...
|
||||
body = this->body_.value(x...);
|
||||
}
|
||||
if (!this->json_.empty()) {
|
||||
body = json::build_json([this, x...](JsonObject root) { this->encode_json_(x..., root); });
|
||||
body = json::build_json([this, x...](JsonObject root) mutable { this->encode_json_(x..., root); });
|
||||
}
|
||||
if (this->json_func_ != nullptr) {
|
||||
body = json::build_json([this, x...](JsonObject root) { this->json_func_(x..., root); });
|
||||
body = json::build_json([this, x...](JsonObject root) mutable { this->json_func_(x..., root); });
|
||||
}
|
||||
std::vector<Header> request_headers;
|
||||
request_headers.reserve(this->request_headers_.size());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "http_request_arduino.h"
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY)
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "http_request.h"
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY)
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#if defined(USE_RP2)
|
||||
#include <HTTPClient.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY)
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#include "i2c_bus_arduino.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY)
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#include <Wire.h>
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
@@ -118,7 +118,7 @@ void IDFI2CBus::dump_config() {
|
||||
if (s.second) {
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,4 +42,4 @@ async def setup_improv_core(var: MockObj, config: ConfigType, component: str):
|
||||
cg.add(var.set_next_url(_process_next_url(next_url)))
|
||||
cg.add_define(f"USE_{component.upper()}_NEXT_URL")
|
||||
|
||||
cg.add_library("improv/Improv", "1.2.6")
|
||||
cg.add_library("improv/Improv", "1.2.4")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/improv_base/improv_base.h"
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
@@ -66,53 +65,7 @@ class ImprovSerialComponent final : public Component, public improv_base::Improv
|
||||
std::vector<uint8_t> build_rpc_settings_response_(improv::Command command);
|
||||
std::vector<uint8_t> build_version_info_();
|
||||
|
||||
ESPHOME_ALWAYS_INLINE optional<uint8_t> read_byte_() {
|
||||
optional<uint8_t> byte;
|
||||
uint8_t data = 0;
|
||||
#ifdef USE_ESP32
|
||||
switch (this->uart_selection_) {
|
||||
case logger::UART_SELECTION_UART0:
|
||||
case logger::UART_SELECTION_UART1:
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
|
||||
!defined(USE_ESP32_VARIANT_ESP32C61) && !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
case logger::UART_SELECTION_UART2:
|
||||
#endif
|
||||
if (this->uart_num_ >= 0) {
|
||||
size_t available;
|
||||
uart_get_buffered_data_len(this->uart_num_, &available);
|
||||
if (available) {
|
||||
uart_read_bytes(this->uart_num_, &data, 1, 0);
|
||||
byte = data;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
|
||||
case logger::UART_SELECTION_USB_CDC:
|
||||
if (esp_usb_console_available_for_read()) {
|
||||
esp_usb_console_read_buf((char *) &data, 1);
|
||||
byte = data;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_LOGGER_USB_SERIAL_JTAG
|
||||
case logger::UART_SELECTION_USB_SERIAL_JTAG: {
|
||||
if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) {
|
||||
byte = data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#elif defined(USE_ARDUINO)
|
||||
if (this->hw_serial_->available()) {
|
||||
this->hw_serial_->readBytes(&data, 1);
|
||||
byte = data;
|
||||
}
|
||||
#endif
|
||||
return byte;
|
||||
}
|
||||
optional<uint8_t> read_byte_();
|
||||
void write_data_(const uint8_t *data = nullptr, size_t size = 0);
|
||||
|
||||
uint8_t tx_header_[TX_BUFFER_SIZE] = {
|
||||
@@ -132,7 +85,6 @@ class ImprovSerialComponent final : public Component, public improv_base::Improv
|
||||
|
||||
#ifdef USE_ESP32
|
||||
uart_port_t uart_num_;
|
||||
logger::UARTSelection uart_selection_{logger::UART_SELECTION_UART0};
|
||||
#elif defined(USE_ARDUINO)
|
||||
Stream *hw_serial_{nullptr};
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@ static const char *const TAG = "kuntze";
|
||||
static const uint8_t CMD_READ_REG = 0x03;
|
||||
static const uint16_t REGISTER[] = {4136, 4160, 4680, 6000, 4688, 4728, 5832};
|
||||
|
||||
// Maximum bytes to log for Modbus responses (2 registers = 4 bytes, plus byte count = 5 bytes)
|
||||
// Maximum bytes to log for Modbus responses (2 registers = 4, plus count = 5)
|
||||
static constexpr size_t KUNTZE_MAX_LOG_BYTES = 8;
|
||||
|
||||
void Kuntze::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
|
||||
namespace esphome::ld24xx {
|
||||
|
||||
/// Parse a little-endian 16-bit unsigned value from two bytes.
|
||||
static constexpr uint16_t two_byte_to_uint16(uint8_t low, uint8_t high) { return ((uint16_t) high << 8) | low; }
|
||||
|
||||
// Helper to find index of value in constexpr array
|
||||
template<size_t N> optional<size_t> find_index(const uint32_t (&arr)[N], uint32_t value) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
|
||||
@@ -44,7 +44,7 @@ void arch_init() {
|
||||
|
||||
void arch_restart() {
|
||||
lt_reboot();
|
||||
while (true) {
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
// it is callable from Thumb code via interworking. The MRS CPSR instruction
|
||||
// is ARM-only and user code here may be built in Thumb, so in_isr_context()
|
||||
// defers to this port helper on BK72xx instead of reading CPSR inline.
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
extern "C" uint32_t platform_is_in_interrupt_context(void);
|
||||
#endif
|
||||
|
||||
@@ -60,11 +59,9 @@ extern "C" void delayMicroseconds(unsigned int us);
|
||||
// Forward decls from libretiny's <lt_api.h> family for the inline arch_*
|
||||
// wrappers below. Pulling the full header would drag in the rest of the
|
||||
// LibreTiny C API.
|
||||
// NOLINTBEGIN(readability-redundant-declaration)
|
||||
extern "C" void lt_wdt_feed(void);
|
||||
extern "C" uint32_t lt_cpu_get_cycle_count(void);
|
||||
extern "C" uint32_t lt_cpu_get_freq(void);
|
||||
// NOLINTEND(readability-redundant-declaration)
|
||||
|
||||
namespace esphome::libretiny {}
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ void LTComponent::dump_config() {
|
||||
"LibreTiny:\n"
|
||||
" Version: %s\n"
|
||||
" Loglevel: %u",
|
||||
<_BANNER_STR[10], LT_LOGLEVEL);
|
||||
LT_BANNER_STR + 10, LT_LOGLEVEL);
|
||||
#if defined(__OPTIMIZE_SIZE__) && __OPTIMIZE_LEVEL__ > 0 && __OPTIMIZE_LEVEL__ <= 3
|
||||
ESP_LOGCONFIG(TAG, " Optimization: -Os, SDK: -O" STRINGIFY_MACRO(__OPTIMIZE_LEVEL__));
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->version_ != nullptr) {
|
||||
this->version_->publish_state(<_BANNER_STR[10]);
|
||||
this->version_->publish_state(LT_BANNER_STR + 10);
|
||||
}
|
||||
#endif // USE_TEXT_SENSOR
|
||||
}
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
#include <cstdint>
|
||||
|
||||
// Forward declare FlashDB types to avoid pulling in flashdb.h
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
struct fdb_kvdb;
|
||||
struct fdb_blob;
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
||||
namespace esphome::libretiny {
|
||||
|
||||
|
||||
@@ -219,14 +219,11 @@ LightColorValues LightCall::validate_() {
|
||||
this->set_flag_(FLAG_HAS_STATE);
|
||||
}
|
||||
|
||||
// Make sure a turn-on makes the light visible: if the resulting brightness would be zero
|
||||
// (e.g. restored from a brightness=0 turn-off), reset it to full brightness.
|
||||
if (this->has_state() && this->state_ && (color_mode & ColorCapability::BRIGHTNESS)) {
|
||||
float brightness = this->has_brightness() ? this->brightness_ : this->parent_->remote_values.get_brightness();
|
||||
if (brightness == 0.0f) {
|
||||
this->brightness_ = 1.0f;
|
||||
this->set_flag_(FLAG_HAS_BRIGHTNESS);
|
||||
}
|
||||
// Make sure a simple (no specific brightness) turn-on makes the light visible
|
||||
if (this->has_state() && this->state_ && (color_mode & ColorCapability::BRIGHTNESS) && !this->has_brightness() &&
|
||||
this->parent_->remote_values.get_brightness() == 0.0f) {
|
||||
this->brightness_ = 1.0f;
|
||||
this->set_flag_(FLAG_HAS_BRIGHTNESS);
|
||||
}
|
||||
|
||||
// Set color brightness to 100% if currently zero and a color is set.
|
||||
|
||||
@@ -71,6 +71,14 @@ void LightState::setup() {
|
||||
break;
|
||||
}
|
||||
|
||||
// A light coming up on boot must never end up on-but-invisible: if the resolved restore
|
||||
// state is on but its brightness is zero (e.g. a stale/persisted value from before a
|
||||
// forced-on restore mode, or an inverted restore flipping a dim-to-0 off state to on),
|
||||
// reset it to full brightness.
|
||||
if (recovered.state && recovered.brightness == 0.0f) {
|
||||
recovered.brightness = 1.0f;
|
||||
}
|
||||
|
||||
call.set_color_mode_if_supported(recovered.color_mode);
|
||||
call.set_state(recovered.state);
|
||||
call.set_brightness_if_supported(recovered.brightness);
|
||||
|
||||
@@ -20,7 +20,7 @@ TaskLogBuffer::~TaskLogBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
size_t TaskLogBuffer::available_contiguous_space_() const {
|
||||
size_t TaskLogBuffer::available_contiguous_space() const {
|
||||
if (this->head_ >= this->tail_) {
|
||||
// head is ahead of or equal to tail
|
||||
// Available space is from head to end, plus from start to tail
|
||||
@@ -81,7 +81,7 @@ void TaskLogBuffer::release_message_main_loop() {
|
||||
this->tail_ = 0;
|
||||
}
|
||||
|
||||
this->message_count_ = this->message_count_ - 1;
|
||||
this->message_count_--;
|
||||
this->current_message_size_ = 0;
|
||||
|
||||
xSemaphoreGive(this->mutex_);
|
||||
@@ -117,7 +117,7 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin
|
||||
}
|
||||
|
||||
// Check if we have enough contiguous space
|
||||
size_t contiguous = this->available_contiguous_space_();
|
||||
size_t contiguous = this->available_contiguous_space();
|
||||
|
||||
if (contiguous < total_size) {
|
||||
// Not enough contiguous space at end
|
||||
@@ -128,9 +128,9 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin
|
||||
}
|
||||
|
||||
// Need at least enough space to safely write padding marker (level field is at end of struct)
|
||||
constexpr size_t padding_marker_min_space = offsetof(LogMessage, level) + 1;
|
||||
constexpr size_t PADDING_MARKER_MIN_SPACE = offsetof(LogMessage, level) + 1;
|
||||
|
||||
if (space_at_start >= total_size && this->head_ > 0 && contiguous >= padding_marker_min_space) {
|
||||
if (space_at_start >= total_size && this->head_ > 0 && contiguous >= PADDING_MARKER_MIN_SPACE) {
|
||||
// Add padding marker (set level field to indicate this is padding, not a real message)
|
||||
LogMessage *padding = reinterpret_cast<LogMessage *>(this->storage_ + this->head_);
|
||||
padding->level = PADDING_MARKER_LEVEL;
|
||||
@@ -180,7 +180,7 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin
|
||||
this->head_ = 0;
|
||||
}
|
||||
|
||||
this->message_count_ = this->message_count_ + 1;
|
||||
this->message_count_++;
|
||||
|
||||
xSemaphoreGive(this->mutex_);
|
||||
return true;
|
||||
|
||||
@@ -84,7 +84,7 @@ class TaskLogBuffer {
|
||||
static inline size_t message_total_size(size_t text_length) { return sizeof(LogMessage) + text_length + 1; }
|
||||
|
||||
// Calculate available contiguous space at write position
|
||||
size_t available_contiguous_space_() const;
|
||||
size_t available_contiguous_space() const;
|
||||
|
||||
uint8_t storage_[ESPHOME_TASK_LOG_BUFFER_SIZE]; // Embedded in Logger (no separate heap allocation)
|
||||
size_t head_{0}; // Write position
|
||||
|
||||
@@ -27,8 +27,8 @@ static void register_libretiny(MDNSComponent *, StaticVector<MDNSService, MDNS_S
|
||||
while (*service_type == '_') {
|
||||
service_type++;
|
||||
}
|
||||
uint16_t port = service.port.value();
|
||||
MDNS.addService(service_type, proto, port);
|
||||
uint16_t port_ = service.port.value();
|
||||
MDNS.addService(service_type, proto, port_);
|
||||
for (const auto &record : service.txt_records) {
|
||||
MDNS.addServiceTxt(service_type, proto, MDNS_STR_ARG(record.key), MDNS_STR_ARG(record.value));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import hashlib
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from esphome import automation, external_files, git
|
||||
@@ -9,6 +10,7 @@ from esphome.automation import register_action, register_condition
|
||||
from esphome.bundle import add_bundle_file
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32, microphone, ota, psram
|
||||
from esphome.components.http_request import validate_url
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FILE,
|
||||
@@ -209,33 +211,13 @@ def _validate_manifest_version(manifest_data):
|
||||
raise cv.Invalid("Invalid manifest file, missing 'version' key")
|
||||
|
||||
|
||||
def _process_http_source(config):
|
||||
url = config[CONF_URL]
|
||||
path = _compute_local_file_path(config)
|
||||
|
||||
json_path = path / "manifest.json"
|
||||
|
||||
json_contents = external_files.download_content(url, json_path)
|
||||
|
||||
manifest_data = json.loads(json_contents)
|
||||
if not isinstance(manifest_data, dict):
|
||||
raise cv.Invalid("Manifest file must contain a JSON object")
|
||||
|
||||
model = manifest_data[CONF_MODEL]
|
||||
model_url = urljoin(url, model)
|
||||
|
||||
model_path = path / model
|
||||
|
||||
external_files.download_content(str(model_url), model_path)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
HTTP_SCHEMA = cv.All(
|
||||
HTTP_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_URL): cv.url,
|
||||
},
|
||||
_process_http_source,
|
||||
# validate_url only accepts http(s); the shorthand validator relies
|
||||
# on this branch rejecting git shorthands ("github://...") so they
|
||||
# fall through to the git branch.
|
||||
cv.Required(CONF_URL): validate_url,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -280,6 +262,13 @@ LOCAL_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
# Bare model names in the official model repository ("okay_nabu"). Must not
|
||||
# overlap with local paths, http(s) urls, or git shorthands
|
||||
# ("github://user/repo/file.json@ref"), which the shorthand validator tries
|
||||
# next; anything containing "/", ":" or "@" is not a model name.
|
||||
_MODEL_NAME_RE = re.compile(r"[A-Za-z0-9_.-]+")
|
||||
|
||||
|
||||
def _validate_source_model_name(value):
|
||||
if not isinstance(value, str):
|
||||
raise cv.Invalid("Model name must be a string")
|
||||
@@ -287,6 +276,9 @@ def _validate_source_model_name(value):
|
||||
if value.endswith(".json"):
|
||||
raise cv.Invalid("Model name must not end with .json")
|
||||
|
||||
if not _MODEL_NAME_RE.fullmatch(value):
|
||||
raise cv.Invalid("Model name may only contain letters, numbers, . _ -")
|
||||
|
||||
return MODEL_SOURCE_SCHEMA(
|
||||
{
|
||||
CONF_TYPE: TYPE_HTTP,
|
||||
@@ -376,6 +368,58 @@ def _maybe_empty_vad_schema(value):
|
||||
return VAD_MODEL_SCHEMA(value)
|
||||
|
||||
|
||||
def _download_http_models(config: ConfigType) -> ConfigType:
|
||||
"""Download every http-sourced manifest and model file in two concurrent
|
||||
batches (all manifests, then all model files).
|
||||
|
||||
The model file's URL only becomes known once its manifest has been
|
||||
fetched and parsed, so the two stages cannot be merged into one batch.
|
||||
"""
|
||||
model_parameters = [*config[CONF_MODELS]]
|
||||
if vad := config.get(CONF_VAD):
|
||||
model_parameters.append(vad)
|
||||
# Keyed by cache path so a URL referenced twice is fetched and parsed once
|
||||
http_models: dict[Path, str] = {
|
||||
_compute_local_file_path(model_config): model_config[CONF_URL]
|
||||
for parameters in model_parameters
|
||||
if (model_config := parameters.get(CONF_MODEL)) is not None
|
||||
and model_config.get(CONF_TYPE) == TYPE_HTTP
|
||||
}
|
||||
if not http_models:
|
||||
return config
|
||||
|
||||
external_files.download_content_many(
|
||||
((url, path / "manifest.json") for path, url in http_models.items()),
|
||||
description="wake word manifest(s)",
|
||||
)
|
||||
|
||||
model_files: list[tuple[str, Path]] = []
|
||||
errors: list[cv.Invalid] = []
|
||||
for path, url in http_models.items():
|
||||
try:
|
||||
manifest_data = json.loads((path / "manifest.json").read_bytes())
|
||||
except (OSError, ValueError) as e:
|
||||
errors.append(cv.Invalid(f"Invalid manifest file at {url}: {e}"))
|
||||
continue
|
||||
if not isinstance(manifest_data, dict):
|
||||
errors.append(
|
||||
cv.Invalid(f"Manifest file at {url} must contain a JSON object")
|
||||
)
|
||||
continue
|
||||
model = manifest_data.get(CONF_MODEL)
|
||||
if not isinstance(model, str):
|
||||
errors.append(
|
||||
cv.Invalid(f"Manifest file at {url} is missing the 'model' key")
|
||||
)
|
||||
continue
|
||||
model_files.append((urljoin(url, model), path / model))
|
||||
if errors:
|
||||
raise cv.MultipleInvalid(errors)
|
||||
|
||||
external_files.download_content_many(model_files, description="wake word model(s)")
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -409,6 +453,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_on_esp32,
|
||||
_download_http_models,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "ac_adapter.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/AirConditioner/AirConditioner.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "air_conditioner.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/AirConditioner/AirConditioner.h>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/ApplianceBase.h>
|
||||
|
||||
@@ -25,8 +25,6 @@ from esphome.const import (
|
||||
ICON_POWER,
|
||||
ICON_THERMOMETER,
|
||||
ICON_WATER_PERCENT,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
@@ -154,12 +152,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
cv.only_on(
|
||||
[
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
# Actions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY)
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
#include "esphome/components/remote_base/midea_protocol.h"
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ void MideaIR::transmit_state() {
|
||||
data.set_fahrenheit(this->fahrenheit_);
|
||||
data.set_temp(this->target_temperature);
|
||||
data.set_mode(this->mode);
|
||||
data.set_fan_mode(this->fan_mode.value_or(ClimateFanMode::CLIMATE_FAN_ON));
|
||||
data.set_fan_mode(this->fan_mode.value_or(ClimateFanMode::CLIMATE_FAN_AUTO));
|
||||
data.set_sleep_preset(this->preset == climate::CLIMATE_PRESET_SLEEP);
|
||||
data.fix();
|
||||
this->transmit_(data);
|
||||
|
||||
@@ -318,232 +318,4 @@ DsiDriverChip(
|
||||
(0xE0, 0x00),
|
||||
]
|
||||
)
|
||||
|
||||
# JC8012P4A1 V2 Driver Configuration (jd9365)
|
||||
# Some units of this model have a different LCD panel but still use the same JD9365 driver chip.
|
||||
# Using parameters from esp_lcd_jd9365.h and the working full init sequence
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# * Resolution: 800x1280
|
||||
# * PCLK Frequency: 70 MHz
|
||||
# * DSI Lane Bit Rate: 1.5 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=10, vsync_front_porch=20)
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
DsiDriverChip(
|
||||
"JC8012P4A1-V2",
|
||||
width=800,
|
||||
height=1280,
|
||||
hsync_back_porch=20,
|
||||
hsync_pulse_width=20,
|
||||
hsync_front_porch=40,
|
||||
vsync_back_porch=10,
|
||||
vsync_pulse_width=4,
|
||||
vsync_front_porch=20,
|
||||
pclk_frequency="70MHz",
|
||||
lane_bit_rate="1500Mbps",
|
||||
color_order="RGB",
|
||||
reset_pin=27,
|
||||
initsequence=[
|
||||
(0xE0, 0x00),
|
||||
(0xE1, 0x93),
|
||||
(0xE2, 0x65),
|
||||
(0xE3, 0xF8),
|
||||
(0x80, 0x01),
|
||||
(0xE0, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x01, 0x44),
|
||||
(0x03, 0x10),
|
||||
(0x04, 0x38),
|
||||
(0x0C, 0x74),
|
||||
(0x17, 0x00),
|
||||
(0x18, 0xAF),
|
||||
(0x19, 0x00),
|
||||
(0x1A, 0x00),
|
||||
(0x1B, 0xAF),
|
||||
(0x1C, 0x00),
|
||||
(0x35, 0x26),
|
||||
(0x37, 0x09),
|
||||
(0x38, 0x04),
|
||||
(0x39, 0x00),
|
||||
(0x3A, 0x01),
|
||||
(0x3C, 0x78),
|
||||
(0x3D, 0xFF),
|
||||
(0x3E, 0xFF),
|
||||
(0x3F, 0x7F),
|
||||
(0x40, 0x06),
|
||||
(0x41, 0xA0),
|
||||
(0x42, 0x81),
|
||||
(0x43, 0x1E),
|
||||
(0x44, 0x0D),
|
||||
(0x45, 0x28),
|
||||
(0x55, 0x02),
|
||||
(0x57, 0x69),
|
||||
(0x59, 0x0A),
|
||||
(0x5A, 0x2A),
|
||||
(0x5B, 0x17),
|
||||
(0x5D, 0x7F),
|
||||
(0x5E, 0x6B),
|
||||
(0x5F, 0x5C),
|
||||
(0x60, 0x50),
|
||||
(0x61, 0x4C),
|
||||
(0x62, 0x3E),
|
||||
(0x63, 0x41),
|
||||
(0x64, 0x2B),
|
||||
(0x65, 0x43),
|
||||
(0x66, 0x42),
|
||||
(0x67, 0x43),
|
||||
(0x68, 0x62),
|
||||
(0x69, 0x52),
|
||||
(0x6A, 0x5A),
|
||||
(0x6B, 0x4C),
|
||||
(0x6C, 0x48),
|
||||
(0x6D, 0x3A),
|
||||
(0x6E, 0x28),
|
||||
(0x6F, 0x10),
|
||||
(0x70, 0x7F),
|
||||
(0x71, 0x6B),
|
||||
(0x72, 0x5C),
|
||||
(0x73, 0x50),
|
||||
(0x74, 0x4C),
|
||||
(0x75, 0x3E),
|
||||
(0x76, 0x41),
|
||||
(0x77, 0x2B),
|
||||
(0x78, 0x43),
|
||||
(0x79, 0x42),
|
||||
(0x7A, 0x43),
|
||||
(0x7B, 0x62),
|
||||
(0x7C, 0x52),
|
||||
(0x7D, 0x5A),
|
||||
(0x7E, 0x4C),
|
||||
(0x7F, 0x48),
|
||||
(0x80, 0x3A),
|
||||
(0x81, 0x28),
|
||||
(0x82, 0x10),
|
||||
(0xE0, 0x02),
|
||||
(0x00, 0x42),
|
||||
(0x01, 0x42),
|
||||
(0x02, 0x40),
|
||||
(0x03, 0x40),
|
||||
(0x04, 0x5E),
|
||||
(0x05, 0x5E),
|
||||
(0x06, 0x5F),
|
||||
(0x07, 0x5F),
|
||||
(0x08, 0x5F),
|
||||
(0x09, 0x57),
|
||||
(0x0A, 0x57),
|
||||
(0x0B, 0x77),
|
||||
(0x0C, 0x77),
|
||||
(0x0D, 0x47),
|
||||
(0x0E, 0x47),
|
||||
(0x0F, 0x45),
|
||||
(0x10, 0x45),
|
||||
(0x11, 0x4B),
|
||||
(0x12, 0x4B),
|
||||
(0x13, 0x49),
|
||||
(0x14, 0x49),
|
||||
(0x15, 0x5F),
|
||||
(0x16, 0x41),
|
||||
(0x17, 0x41),
|
||||
(0x18, 0x40),
|
||||
(0x19, 0x40),
|
||||
(0x1A, 0x5E),
|
||||
(0x1B, 0x5E),
|
||||
(0x1C, 0x5F),
|
||||
(0x1D, 0x5F),
|
||||
(0x1E, 0x5F),
|
||||
(0x1F, 0x57),
|
||||
(0x20, 0x57),
|
||||
(0x21, 0x77),
|
||||
(0x22, 0x77),
|
||||
(0x23, 0x46),
|
||||
(0x24, 0x46),
|
||||
(0x25, 0x44),
|
||||
(0x26, 0x44),
|
||||
(0x27, 0x4A),
|
||||
(0x28, 0x4A),
|
||||
(0x29, 0x48),
|
||||
(0x2A, 0x48),
|
||||
(0x2B, 0x5F),
|
||||
(0x2C, 0x01),
|
||||
(0x2D, 0x01),
|
||||
(0x2E, 0x00),
|
||||
(0x2F, 0x00),
|
||||
(0x30, 0x1F),
|
||||
(0x31, 0x1F),
|
||||
(0x32, 0x1E),
|
||||
(0x33, 0x1E),
|
||||
(0x34, 0x1F),
|
||||
(0x35, 0x17),
|
||||
(0x36, 0x17),
|
||||
(0x37, 0x37),
|
||||
(0x38, 0x37),
|
||||
(0x39, 0x08),
|
||||
(0x3A, 0x08),
|
||||
(0x3B, 0x0A),
|
||||
(0x3C, 0x0A),
|
||||
(0x3D, 0x04),
|
||||
(0x3E, 0x04),
|
||||
(0x3F, 0x06),
|
||||
(0x40, 0x06),
|
||||
(0x41, 0x1F),
|
||||
(0x42, 0x02),
|
||||
(0x43, 0x02),
|
||||
(0x44, 0x00),
|
||||
(0x45, 0x00),
|
||||
(0x46, 0x1F),
|
||||
(0x47, 0x1F),
|
||||
(0x48, 0x1E),
|
||||
(0x49, 0x1E),
|
||||
(0x4A, 0x1F),
|
||||
(0x4B, 0x17),
|
||||
(0x4C, 0x17),
|
||||
(0x4D, 0x37),
|
||||
(0x4E, 0x37),
|
||||
(0x4F, 0x09),
|
||||
(0x50, 0x09),
|
||||
(0x51, 0x0B),
|
||||
(0x52, 0x0B),
|
||||
(0x53, 0x05),
|
||||
(0x54, 0x05),
|
||||
(0x55, 0x07),
|
||||
(0x56, 0x07),
|
||||
(0x57, 0x1F),
|
||||
(0x58, 0x40),
|
||||
(0x5B, 0x30),
|
||||
(0x5C, 0x00),
|
||||
(0x5D, 0x34),
|
||||
(0x5E, 0x05),
|
||||
(0x5F, 0x02),
|
||||
(0x63, 0x00),
|
||||
(0x64, 0x6A),
|
||||
(0x67, 0x73),
|
||||
(0x68, 0x07),
|
||||
(0x69, 0x08),
|
||||
(0x6A, 0x6A),
|
||||
(0x6B, 0x08),
|
||||
(0x6C, 0x00),
|
||||
(0x6D, 0x00),
|
||||
(0x6E, 0x00),
|
||||
(0x6F, 0x88),
|
||||
(0x75, 0xFF),
|
||||
(0x77, 0xDD),
|
||||
(0x78, 0x2C),
|
||||
(0x79, 0x15),
|
||||
(0x7A, 0x17),
|
||||
(0x7D, 0x14),
|
||||
(0x7E, 0x82),
|
||||
(0xE0, 0x04),
|
||||
(0x00, 0x0E),
|
||||
(0x02, 0xB3),
|
||||
(0x09, 0x60),
|
||||
(0x0E, 0x48),
|
||||
(0x37, 0x58),
|
||||
(0x2B, 0x0F),
|
||||
(0xE0, 0x05),
|
||||
(0x15, 0x1D),
|
||||
(0xE0, 0x00),
|
||||
(0xE6, 0x02),
|
||||
(0xE7, 0x0C)
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#ifndef USE_BK72XX
|
||||
|
||||
#include "sensor_mlx90393.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -272,5 +270,3 @@ void MLX90393Cls::verify_settings_timeout_(MLX90393Setting stage) {
|
||||
}
|
||||
|
||||
} // namespace esphome::mlx90393
|
||||
|
||||
#endif // USE_BK72XX
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef USE_BK72XX
|
||||
|
||||
#include <MLX90393.h>
|
||||
#include <MLX90393Hal.h>
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
@@ -78,5 +76,3 @@ class MLX90393Cls final : public PollingComponent, public i2c::I2CDevice, public
|
||||
};
|
||||
|
||||
} // namespace esphome::mlx90393
|
||||
|
||||
#endif // USE_BK72XX
|
||||
|
||||
@@ -71,6 +71,15 @@ struct IPAddress {
|
||||
bool is_ip4() const { return false; }
|
||||
bool is_ip6() const { return this->is_set(); }
|
||||
bool is_multicast() const { return net_ipv6_is_addr_mcast(&ip_addr_); }
|
||||
// Remove before 2026.8.0
|
||||
ESPDEPRECATED(
|
||||
"str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0",
|
||||
"2026.2.0")
|
||||
std::string str() const {
|
||||
char buf[IP_ADDRESS_BUFFER_SIZE];
|
||||
this->str_to(buf);
|
||||
return buf;
|
||||
}
|
||||
char *str_to(char *buf) const {
|
||||
if (inet_ntop(AF_INET6, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE) == nullptr)
|
||||
buf[0] = '\0';
|
||||
@@ -86,6 +95,15 @@ 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; }
|
||||
// Remove before 2026.8.0
|
||||
ESPDEPRECATED(
|
||||
"str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0",
|
||||
"2026.2.0")
|
||||
std::string str() const {
|
||||
char buf[IP_ADDRESS_BUFFER_SIZE];
|
||||
this->str_to(buf);
|
||||
return buf;
|
||||
}
|
||||
/// 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);
|
||||
@@ -168,6 +186,15 @@ 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_); }
|
||||
// Remove before 2026.8.0
|
||||
ESPDEPRECATED(
|
||||
"str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0",
|
||||
"2026.2.0")
|
||||
std::string str() const {
|
||||
char buf[IP_ADDRESS_BUFFER_SIZE];
|
||||
this->str_to(buf);
|
||||
return buf;
|
||||
}
|
||||
/// 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 {
|
||||
|
||||
@@ -19,6 +19,10 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
},
|
||||
"nextion_upload_arduino.cpp": {
|
||||
PlatformFramework.ESP8266_ARDUINO,
|
||||
PlatformFramework.RP2_ARDUINO,
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,14 +4,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, esp32, uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BRIGHTNESS,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_ON_TOUCH,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
)
|
||||
from esphome.const import CONF_BRIGHTNESS, CONF_ID, CONF_LAMBDA, CONF_ON_TOUCH
|
||||
from esphome.core import CORE, TimePeriod
|
||||
|
||||
from . import ( # noqa: F401 pylint: disable=unused-import
|
||||
@@ -142,17 +135,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(
|
||||
CONF_TFT_UPLOAD_WATCHDOG_TIMEOUT
|
||||
): cv.positive_time_period_milliseconds,
|
||||
# TFT upload needs an HTTP client and runtime UART reconfiguration,
|
||||
# neither of which is implemented for the RP2 or host platforms.
|
||||
cv.Optional(CONF_TFT_URL): cv.All(
|
||||
cv.url,
|
||||
cv.only_on(
|
||||
[
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
]
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_TFT_URL): cv.url,
|
||||
cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.Any(
|
||||
0, cv.int_range(min=3, max=65535)
|
||||
),
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
#elif defined(USE_ESP8266)
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#elif defined(USE_LIBRETINY)
|
||||
#include <HTTPClient.h>
|
||||
#endif // USE_ESP32 vs USE_ESP8266 vs USE_LIBRETINY
|
||||
#endif // USE_ESP32 vs USE_ESP8266
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
|
||||
namespace esphome::nextion {
|
||||
@@ -1566,7 +1564,7 @@ class Nextion final : public NextionBase, public PollingComponent, public uart::
|
||||
* @return position of last byte transferred, -1 for failure.
|
||||
*/
|
||||
int upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start);
|
||||
#elif defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||
#elif defined(USE_ARDUINO)
|
||||
/**
|
||||
* will request chunk_size chunks from the web server
|
||||
* and send each to the nextion
|
||||
@@ -1575,7 +1573,7 @@ class Nextion final : public NextionBase, public PollingComponent, public uart::
|
||||
* @return position of last byte transferred, -1 for failure.
|
||||
*/
|
||||
int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start);
|
||||
#endif // USE_ESP32 vs USE_ESP8266/USE_LIBRETINY
|
||||
#endif // USE_ESP32 vs USE_ARDUINO
|
||||
|
||||
/**
|
||||
* Ends the upload process, restart Nextion and, if successful,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "nextion.h"
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
#ifdef USE_ESP8266
|
||||
#ifndef USE_ESP32
|
||||
|
||||
#include <cinttypes>
|
||||
#include "esphome/components/network/util.h"
|
||||
@@ -209,6 +209,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
http_client.setTimeout(this->tft_upload_http_timeout_);
|
||||
|
||||
bool begin_status = false;
|
||||
#ifdef USE_ESP8266
|
||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
|
||||
http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
|
||||
@@ -218,6 +219,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
http_client.setRedirectLimit(3);
|
||||
#endif
|
||||
begin_status = http_client.begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
||||
#endif // USE_ESP8266
|
||||
if (!begin_status) {
|
||||
this->connection_state_.is_updating_ = false;
|
||||
ESP_LOGD(TAG, "Connection failed");
|
||||
@@ -354,6 +356,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
return upload_end_(true);
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
WiFiClient *Nextion::get_wifi_client_() {
|
||||
if (this->tft_url_.starts_with("https:")) {
|
||||
if (this->wifi_client_secure_ == nullptr) {
|
||||
@@ -371,8 +374,9 @@ WiFiClient *Nextion::get_wifi_client_() {
|
||||
}
|
||||
return this->wifi_client_;
|
||||
}
|
||||
#endif // USE_ESP8266
|
||||
|
||||
} // namespace esphome::nextion
|
||||
|
||||
#endif // USE_ESP8266
|
||||
#endif // NOT USE_ESP32
|
||||
#endif // USE_NEXTION_TFT_UPLOAD
|
||||
|
||||
@@ -27,11 +27,11 @@ uint8_t parse_u8_lb(OpenthermData &data) { return data.valueLB; }
|
||||
uint8_t parse_u8_hb(OpenthermData &data) { return data.valueHB; }
|
||||
int8_t parse_s8_lb(OpenthermData &data) { return (int8_t) data.valueLB; }
|
||||
int8_t parse_s8_hb(OpenthermData &data) { return (int8_t) data.valueHB; }
|
||||
uint16_t parse_u16(OpenthermData &data) { return data.get_u16(); }
|
||||
uint16_t parse_u16(OpenthermData &data) { return data.u16(); }
|
||||
uint16_t parse_u8_lb_60(OpenthermData &data) { return data.valueLB * 60; }
|
||||
uint16_t parse_u8_hb_60(OpenthermData &data) { return data.valueHB * 60; }
|
||||
int16_t parse_s16(OpenthermData &data) { return data.get_s16(); }
|
||||
float parse_f88(OpenthermData &data) { return data.get_f88(); }
|
||||
int16_t parse_s16(OpenthermData &data) { return data.s16(); }
|
||||
float parse_f88(OpenthermData &data) { return data.f88(); }
|
||||
|
||||
void write_flag8_lb_0(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 0, value); }
|
||||
void write_flag8_lb_1(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 1, value); }
|
||||
@@ -53,9 +53,9 @@ void write_u8_lb(const uint8_t value, OpenthermData &data) { data.valueLB = valu
|
||||
void write_u8_hb(const uint8_t value, OpenthermData &data) { data.valueHB = value; }
|
||||
void write_s8_lb(const int8_t value, OpenthermData &data) { data.valueLB = (uint8_t) value; }
|
||||
void write_s8_hb(const int8_t value, OpenthermData &data) { data.valueHB = (uint8_t) value; }
|
||||
void write_u16(const uint16_t value, OpenthermData &data) { data.set_u16(value); }
|
||||
void write_s16(const int16_t value, OpenthermData &data) { data.set_s16(value); }
|
||||
void write_f88(const float value, OpenthermData &data) { data.set_f88(value); }
|
||||
void write_u16(const uint16_t value, OpenthermData &data) { data.u16(value); }
|
||||
void write_s16(const int16_t value, OpenthermData &data) { data.s16(value); }
|
||||
void write_f88(const float value, OpenthermData &data) { data.f88(value); }
|
||||
|
||||
} // namespace message_data
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user