Compare commits

..
Author SHA1 Message Date
J. Nick Koston c538c0fcf1 Merge remote-tracking branch 'origin/dev' into logger-boot-log-buffer
# Conflicts:
#	esphome/components/logger/logger.h
2026-10-21 07:25:34 -10:00
J. Nick Koston ea078bd9a4 Merge remote-tracking branch 'origin/dev' into logger-boot-log-buffer
# Conflicts:
#	esphome/components/logger/logger.cpp
2026-10-14 10:43:22 -10:00
J. Nick Koston 47f5c28925 Merge remote-tracking branch 'origin/dev' into logger-boot-log-buffer 2026-10-14 10:39:33 -10:00
J. Nick Koston 75aaeec28f Trailing underscore on the protected test helpers 2026-10-13 12:00:38 -10:00
J. Nick Koston 8b37d3a569 Inline the tag level schemas for the language schema builder and check boot_logs against logs 2026-10-13 11:02:53 -10:00
J. Nick Koston ead5824302 Reject boot_logs without a buffer and exercise per-tag boot levels on ESP8266 2026-10-13 10:48:07 -10:00
J. Nick Koston e411e19ab0 Share the LogBuffer colour code and tag lookup with the boot log replay 2026-10-13 10:45:53 -10:00
J. Nick Koston e57fca4f78 Guard the replay, warn on dropped lines, cap captured text, and register the action as an apply action 2026-10-13 10:33:49 -10:00
Omni Flux bbec20a66b [logger] Make boot log buffer log level configurable by tag
(cherry picked from commit fae4ecb708cf816902282c3b5cd6eff07dc02abf)
2026-10-13 10:24:15 -10:00
Omni Flux 9a353cc3e2 [logger] Make boot log buffer log level configurable
(cherry picked from commit 7f532b3021999bb75551368ca13966b729a89011)
2026-10-13 10:22:18 -10:00
J. Nick Koston 4b029993a4 Replay stored lines straight to the listeners with their tag, add host tests 2026-10-13 10:09:02 -10:00
J. Nick Koston 3a1bb239e9 Merge remote-tracking branch 'origin/dev' into logger-boot-log-buffer 2026-10-13 09:57:07 -10:00
J. Nick Koston e96553121c Merge remote-tracking branch 'upstream/dev' into HEAD 2026-09-17 08:49:38 -05:00
J. Nick Koston 9527929947 Merge remote-tracking branch 'origin/dev' into logger-boot-log-buffer
# Conflicts:
#	esphome/components/logger/logger.cpp
2026-09-14 11:01:54 -05:00
J. Nick Koston 38ab0805a0 [logger] Add optional boot log buffer replayed over the API 2026-07-17 23:02:43 -10:00
693 changed files with 3989 additions and 19755 deletions
-156
View File
@@ -1,156 +0,0 @@
---
name: display-test
description: Test an ESPHome display driver on a real panel with a camera. Use when a task asks to verify a display (LCD, e-paper, OLED, RLCD) on hardware, check orientation/rotation, partial updates, colours, contrast, or that text and shapes render correctly. Covers finding the USB port, flashing a test pattern, capturing the panel with any camera, and reading the photo.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---
# Display testing with a camera
Point a camera at the panel to get visual evidence that a driver renders correctly. Any camera
and capture tool works; this skill covers the common ones. `$SKILL` below is this directory
(`.agents/skills/display-test`).
## Ask before using a camera
A camera can show people and rooms, not only the panel. Before the first capture, ask the user
for permission and for which camera to use. List the cameras the host has (the commands below
show how), name the one you intend to use, and wait for the user to confirm it. Do not pick a
camera yourself, and do not fall back to another camera if the chosen one fails; ask again.
The permission covers this session and the camera the user named. Ask again for a different
camera or in a new session. If the user declines, stop and report that the display was not
checked visually.
## Capturing a frame
Grab a still image, skipping the first few frames so auto-exposure and white balance settle.
Read the JPEG with the Read tool; the image is shown visually.
### macOS
The bundled Swift tool uses AVFoundation and avoids the frame-grabbing problems some USB
cameras have with other tools:
```bash
mkdir -p /tmp/camsnap /tmp/display-test
swiftc -O "$SKILL/scripts/camsnap.swift" -o /tmp/camsnap/camsnap
/tmp/camsnap/camsnap <camera-name-substring> /tmp/display-test/after.jpg 30
```
The camera argument is a case-insensitive substring of the name AVFoundation reports. If it does
not match, the tool lists the cameras it found, so a run with a name that matches nothing lists
the cameras without capturing. Use a substring that matches only the camera the user approved.
It creates the output directory if needed and reports encoding or write errors on stderr. Other
options include `ffmpeg` with `avfoundation` input, `imagesnap`, or Photo Booth's still capture.
### Linux
Use `ffmpeg` with the v4l2 input, `fswebcam`, or `v4l2-ctl`. With `ffmpeg`, `-ss 2` after the
input discards the first two seconds of frames while exposure settles:
```bash
mkdir -p /tmp/display-test
ffmpeg -f v4l2 -i /dev/video0 -ss 2 -frames:v 1 -y /tmp/display-test/after.jpg
# or
fswebcam -d /dev/video0 --skip 10 /tmp/display-test/after.jpg
```
List devices with `v4l2-ctl --list-devices`.
### Windows
Use `ffmpeg` with the DirectShow input (`-ss 2` skips the first frames, as on Linux), or any
camera application that can save a still image:
```powershell
mkdir C:\temp -Force | Out-Null
ffmpeg -f dshow -list_devices true -i dummy # find the device name
ffmpeg -f dshow -i video="<device name>" -ss 2 -frames:v 1 -y C:\temp\after.jpg
```
### Camera caveats
- The board may sit at an angle. Do not trust absolute pixel positions or aspect ratios from the
photo; judge orientation, ordering and approximate spacing, using the corner markers as
references.
- Reflections or a glossy cover can brighten one side. Capture again if a region is unclear.
- A moving pattern is caught at an arbitrary phase. Take several captures, or use a static marker
per position.
## Board and port
Find the port; names change on every replug:
```bash
python3 "$SKILL/../hardware-test/scripts/ports.py"
```
Native-USB ESP32-S3/C3/C6/H2 boards show up as `USB JTAG/serial debug unit` and need
`logger: hardware_uart: USB_SERIAL_JTAG`.
## Test pattern
Write a scratch config outside the checkout. Use one `name:` per board and include:
- a white background, a black border and a filled square at the centre, drawn in the first
update (full-window check and scale);
- a 60x20 bar near each corner, one bar per later update with `auto_clear_enabled: false`, so
each bar is written by its own partial window. The bars are wider than tall, which shows a
90 or 270 degree rotation; the corners exercise the window alignment;
- a 20x20 square under the left end of the top-left bar, drawn in the same update, which marks
the top-left corner and shows a 180 degree rotation or a mirrored image.
Use `Color::BLACK` and `Color::WHITE` in lambdas. Do not use the old `COLOR_OFF`/`COLOR_ON`
constants; black must mean black.
A ready template is in `$SKILL/templates/display-pattern.yaml`. Substitute the display platform,
pins and (for a driver with fonts) a font. The marker pattern needs no font.
## Build, flash, capture
Run from the worktree under test so `python -m esphome` imports the checkout, not an installed
copy:
```bash
cd <worktree>
python3 -c "import esphome; print(esphome.__file__)" # must print the worktree path
PYTHONUNBUFFERED=1 python3 -m esphome compile /tmp/display-test/display-pattern.yaml
PYTHONUNBUFFERED=1 python3 -m esphome upload /tmp/display-test/display-pattern.yaml \
--device /dev/cu.usbmodemXXXX
sleep 6 # boot plus a few update intervals
<capture command> /tmp/display-test/after.jpg
```
The device path differs by OS and host; use the path printed by `ports.py`. If the USB console
wedges during flashing (esptool `No serial data received`), replug the board and retry. A board
powered only by a battery may need the USB cable for flashing; the panel keeps its image when
power is removed, which is useful for checking that an image is bistable.
## What to check
1. **Orientation**: the extra square sits under the bar in the expected top-left corner. If it is
in another corner, the image is rotated or mirrored. Test `rotation:` values with the same
pattern. The logical width and height swap at 90 and
270 degrees, so the 60x20 corner bars should then appear taller than wide on the physical
panel.
2. **Coverage**: the border touches all four edges, with no dead rows or columns at the edges.
3. **Partial updates**: with `auto_clear_enabled: false`, draw one marker per update. Each should
appear exactly at its requested position with no smearing, offset or mirrored placement.
Include all four corners.
4. **Text and contrast**: sharp glyphs, correct polarity (black text on white, not inverted), no
ghosting from the previous frame.
5. **Speed**: if the driver logs transfer sizes, check that a full refresh and a partial window
take the expected times.
Record the build commit, board, config, and the captured image path when reporting results.
## When a partial update looks wrong
Take a marker-only capture before assuming the mapping is wrong: the camera's perspective makes
absolute positions deceptive. If markers land in mirrored positions, the controller's address
order (`MADCTL` MX/MY/MV) or the RAM block mapping is inverted for the window; verify the
full-frame path separately, then fix the window computation.
## Add findings
Add new capture tools, panel quirks, or recovery steps here so the next session starts ahead.
@@ -1,71 +0,0 @@
// camsnap <camera name substring> <output.jpg> [frames to skip]
// macOS only: grabs one frame from an AVFoundation camera after letting exposure settle.
// See SKILL.md for capture tools on other systems.
import AVFoundation
import AppKit
import CoreImage
let args = CommandLine.arguments
guard args.count >= 3 else {
FileHandle.standardError.write("usage: camsnap <camera name> <out.jpg> [skip]\n".data(using: .utf8)!)
exit(2)
}
let wanted = args[1].lowercased()
let outPath = args[2]
let skip = args.count > 3 ? Int(args[3]) ?? 30 : 30
let discovery = AVCaptureDevice.DiscoverySession(
deviceTypes: [.external, .builtInWideAngleCamera], mediaType: .video, position: .unspecified)
guard let device = discovery.devices.first(where: { $0.localizedName.lowercased().contains(wanted) }) else {
let names = discovery.devices.map { $0.localizedName }.joined(separator: ", ")
FileHandle.standardError.write("no camera matching '\(wanted)'; found: \(names)\n".data(using: .utf8)!)
exit(1)
}
final class Grabber: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
var seen = 0
let skip: Int
let out: String
init(skip: Int, out: String) { self.skip = skip; self.out = out }
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection) {
seen += 1
guard seen > skip, let pixels = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let image = CIImage(cvPixelBuffer: pixels)
let rep = NSBitmapImageRep(ciImage: image)
guard let jpeg = rep.representation(using: .jpeg, properties: [.compressionFactor: 0.9]) else {
FileHandle.standardError.write("failed to encode \(out) as JPEG\n".data(using: .utf8)!)
exit(1)
}
let url = URL(fileURLWithPath: out)
try? FileManager.default.createDirectory(
at: url.deletingLastPathComponent(), withIntermediateDirectories: true)
do {
try jpeg.write(to: url)
} catch {
FileHandle.standardError.write("failed to write \(out): \(error)\n".data(using: .utf8)!)
exit(1)
}
print("saved \(out) after \(seen) frames")
exit(0)
}
}
let session = AVCaptureSession()
session.sessionPreset = .high
guard let input = try? AVCaptureDeviceInput(device: device), session.canAddInput(input) else {
FileHandle.standardError.write("cannot open \(device.localizedName)\n".data(using: .utf8)!)
exit(1)
}
session.addInput(input)
let output = AVCaptureVideoDataOutput()
output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
let grabber = Grabber(skip: skip, out: outPath)
output.setSampleBufferDelegate(grabber, queue: DispatchQueue(label: "camsnap"))
session.addOutput(output)
session.startRunning()
DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
FileHandle.standardError.write("timed out waiting for frames\n".data(using: .utf8)!)
exit(1)
}
RunLoop.main.run()
@@ -1,43 +0,0 @@
# Display test pattern: corner markers, centre reference and partial-update markers.
# Replace the display platform and pins with the driver under test.
esphome:
name: display-pattern
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
logger:
hardware_uart: USB_SERIAL_JTAG
level: DEBUG
spi:
clk_pin: GPIO11
mosi_pin: GPIO12
display:
- platform: st7305
id: test_display
cs_pin: GPIO40
dc_pin: GPIO5
reset_pin: GPIO41
update_interval: 1s
auto_clear_enabled: false
lambda: |-
static int frame = 0;
if (frame == 0) {
it.fill(Color::WHITE);
it.rectangle(0, 0, it.get_width(), it.get_height(), Color::BLACK);
it.filled_rectangle(it.get_width() / 2 - 10, it.get_height() / 2 - 10, 20, 20, Color::BLACK);
} else if (frame == 1) {
it.filled_rectangle(20, 20, 60, 20, Color::BLACK);
it.filled_rectangle(20, 50, 20, 20, Color::BLACK);
} else if (frame == 2) {
it.filled_rectangle(20, it.get_height() - 40, 60, 20, Color::BLACK);
} else if (frame == 3) {
it.filled_rectangle(it.get_width() - 80, 20, 60, 20, Color::BLACK);
} else if (frame == 4) {
it.filled_rectangle(it.get_width() - 80, it.get_height() - 40, 60, 20, Color::BLACK);
}
frame++;
+2 -8
View File
@@ -115,7 +115,7 @@ Per-topic scripts, each described in the matching `reference/recipes/<component>
(host driver, Zephyr driver probe), `rtsp_server/` (protocol client, raw rate reader, measurement set,
API image grab), `cse7766/` (API observer with ping), `zephyr_coredump/` (reader flow, decode with the
Zephyr tools), `libretiny_prefs/` (rtc preference harness header), `libretiny_crash/` (BK72xx crash probe header and
capture decoder), `api_outgoing/` (wake cycle and Wi-Fi drop timing readers), `ld2450/` (zone number driver, loopback harness driver with number and binary sensor states and TX frame summarizer), `api/` (raw plaintext native API client for user defined actions, store_yaml recovery client over Noise or plaintext), `http_request/` (update manifest server, update state driver), `modem/` (button and sensor driver over the plaintext API, also used for the BLE MIDI boards), `dfrobot_sen0395/` (API driver and case runner), `homeassistant_light/` (direct bulb driver, held-report case driver), `usb_audio/` (URL player), `speaker_media_player/` (API player driver, pipeline task case runner), `web_server/` (SSE client, request mix, SSE and HTTPD case runners), `mqtt/` (broker config with TLS, client-certificate and WebSocket listeners, test CA with server and client certificates, transport case runner, flash-and-drive runner, CLI TLS check without a board), `udp/` (datagram recorder, `udp.write` payload checker and case driver), `entity_icons/` (icon and device class readback over the API, MQTT discovery, web_server and dump_config, with a comparer).
capture decoder), `api_outgoing/` (wake cycle and Wi-Fi drop timing readers), `ld2450/` (zone number driver), `dfrobot_sen0395/` (API driver and case runner), `homeassistant_light/` (direct bulb driver, held-report case driver), `usb_audio/` (URL player), `speaker_media_player/` (API player driver, pipeline task case runner), `web_server/` (SSE client, request mix, SSE and HTTPD case runners), `mqtt/` (broker config with WebSocket listeners, test CA, transport case runner).
## Templates in `$SKILL/templates`
@@ -132,17 +132,11 @@ capture decoder), `api_outgoing/` (wake cycle and Wi-Fi drop timing readers), `l
`globals-storage-dut.yaml`, `usb-audio-waveshare-s3-eth-dut.yaml`, `usb-audio-sendspin-s3-eth-dut.yaml`,
`cast-pro-1-sendspin-control.yaml`, `aux-2-preprod-stock-dut.yaml`, `aux-2-preprod-usb-audio-dut.yaml`,
`dfrobot-sen0395-dut.yaml`, `aux-2-preprod-speaker-player-dut.yaml`, `web-server-sse-dut.yaml`,
`mqtt-transport-dut.yaml`, `mqtt-tls-dut.yaml`, `udp-write-dut.yaml`, `entity-icon-dut.yaml`,
and the directories `store_yaml/` (package with secrets one directory up, per-board files),
`http_request_update/` (manifest URL setter shapes, per-board files), `modem/` (modem only and modem plus
Wi-Fi boot checks) and `ble_midi/` (fake BLE MIDI instrument and the client).
`mqtt-transport-dut.yaml`.
## When something looks wrong
- A capture stopped early or a line is missing: check the timestamps before trusting it.
- A script that pipes into `tee` never ends although its work is done: a background command written as
`(cd dir && server & echo $!)` backgrounds the whole `cd && server` list, so a shell stays behind holding the
pipe open and `$!` is that shell, not the server. Write `(cd dir && exec server > out 2>&1 < /dev/null) &`.
- The client never joins the AP: another board may still run an AP with the same SSID, or the device
booted with saved credentials because the name was reused. One AP device at a time on the bench.
- Builds come out byte-identical between branches: the wrong tree was imported (rule 3). The reverse
@@ -7,9 +7,6 @@ Ports are not listed on purpose: they follow the USB hub port and change on ever
- Opening the port usually resets the board through DTR, but not always; `serial_capture.py --reset`
pulses RTS to capture from boot. 74880 baud shows the ROM boot messages (`rst cause`, `boot mode`).
- UART0 can be looped back onto itself (`USC0(0) |= (1 << UCLBE)` in an `on_boot` lambda) so a UART
component is testable without its device: frames written with `uart.write` arrive on RX. Log over the
API with `logger: baud_rate: 0`, since the serial port carries the component's bytes.
- `esphome logs` on some CH340s emits thousands of duplicated garbage lines right after the RTS reset;
a plain pyserial capture at 115200 is reliable.
- A failed esptool write can wedge the adapter (`termios` error 22, or an open that hangs) until replug.
@@ -4,27 +4,23 @@ Each flow names the pull requests it was used on, so their result tables show wh
One file per component, named after it:
- [`api.md`](api.md): noise handshake and OTA encryption on the ESP8266, outgoing connection wake timing on deep sleep and after a Wi-Fi drop, user defined action listing and execution with a raw plaintext client, store_yaml recovery over Noise or plaintext, user defined action listing and execution with a raw plaintext client
- [`ble_midi.md`](ble_midi.md): BLE MIDI client against a fake ESP32 instrument, every packet shape and the send actions
- [`api.md`](api.md): noise handshake and OTA encryption on the ESP8266, outgoing connection wake timing on deep sleep and after a Wi-Fi drop
- [`captive_portal.md`](captive_portal.md): fallback AP and /wifisave hammer
- [`cse7766.md`](cse7766.md): Sonoff S31 parser check over the API
- [`espnow.md`](espnow.md): ESP8266 broadcast receive
- [`hdmi_cec.md`](hdmi_cec.md): two-board wire
- [`http_request.md`](http_request.md): update manifest URL changed at run time, every setter shape, a swap under a running check
- [`ld2450.md`](ld2450.md): polygon zones, zone number round trip through the radar's readback, ESP8266 loopback harness without a radar
- [`ld2412.md`](ld2412.md): output pin level through the basic configuration command, held back writes
- [`ld2450.md`](ld2450.md): polygon zones, zone number round trip through the radar's readback
- [`libretiny.md`](libretiny.md): rtc preference emulation, BK72xx crash report on the next boot
- [`modem.md`](modem.md): PPPoS modem component boot check with no modem attached, loop blocking and disable timing
- [`mqtt.md`](mqtt.md): broker bench, QoS, reconnect, crash log over MQTT, WebSocket transports with a local and a public broker, TLS trust sources (own CA, client certificate, certificate bundle, no verification) with the CLI check
- [`mqtt.md`](mqtt.md): broker bench, QoS, reconnect, crash log over MQTT, WebSocket transports with a local and a public broker
- [`nrf52.md`](nrf52.md): dongle board support
- [`online_image.md`](online_image.md): stalled and malformed downloads
- [`openthread.md`](openthread.md): border router on the S3 host plus H2 RCP
- [`ota.md`](ota.md): overlap and lock, timeouts, deflate
- [`rtsp_server.md`](rtsp_server.md): camera streaming on the Waveshare ESP32-S3-ETH
- [`scheduler.md`](scheduler.md): loop wake A/B
- [`udp.md`](udp.md): datagrams, multicast, sockets, `udp.write` payload shapes
- [`udp.md`](udp.md): datagrams, multicast, sockets
- [`usb_cdc_acm.md`](usb_cdc_acm.md): nRF52840 dongle host driver
- [`wifi.md`](wifi.md): reboot_timeout with a fallback AP, DHCP lease cache, Easy Connect (DPP), WISP bridge with an ethernet upstream
- [`wifi.md`](wifi.md): reboot_timeout with a fallback AP, DHCP lease cache, Easy Connect (DPP)
- [`zephyr.md`](zephyr.md): preferences on the nRF52840 dongle
- [`zephyr_coredump.md`](zephyr_coredump.md): crash and decode on nRF52 boards
- [`zwave_proxy.md`](zwave_proxy.md): frame checksums on a live proxy
@@ -33,4 +29,4 @@ One file per component, named after it:
- [`usb_audio.md`](usb_audio.md): USB audio speaker and microphone, the usb_host FIFO split, and Music Assistant playback through sendspin
- [`dfrobot_sen0395.md`](dfrobot_sen0395.md): SEN0395 mmWave command flow, module quirks and overlong lines through its error reply
- [`speaker.md`](speaker.md): media player pipeline tasks on the Connect AUX-2 with the `speaker` platform player
- [`web_server.md`](web_server.md): state SSE stress and the HTTPD task stack on web_server_idf, entity icon and device class readback
- [`web_server.md`](web_server.md): state SSE stress and the HTTPD task stack on web_server_idf
@@ -28,31 +28,3 @@ connected first, and the loop-time maxima. For the loop cost of polling, repeat
so after a drop every dial is closed and HA's incoming reconnect wins 11 to 33 s later; the measurable effect is the
first dial landing in the loop the network flag flips.
Results: https://github.com/esphome/esphome/pull/21085#issuecomment-6254742791
## User defined action listing and execution without a client library (#18882)
`scripts/api/rawactions.py HOST list` speaks the plaintext native API over a raw socket and decodes every
`ListEntitiesServicesResponse` by field number (argument fields 1 name, 2 type, 3 description, 4 example, 5 optional,
6 default_value), so new wire fields show up without waiting for the client library. `rawactions.py HOST exec ACTION
k=v ...` sends `ExecuteServiceRequest` with `sint32` ints (field 5, not the legacy int32 field 2), fixed32 floats,
length delimited strings and `int[]` as repeated sint32 (field 7); `ALLOW_MISSING=1` leaves arguments out on purpose
(the device then drops the call). `templates/api-actions-dut.yaml` (D1 mini, three actions, optional variables with
defaults) and `templates/api-actions-strings-dut.yaml` (required strings) log what arrived. String arguments are
`StringRef`s that are not null terminated: copy them with `snprintf("%.*s", size, c_str())` into a buffer before
logging, and do not pass `%.*s` to `logger.log`, whose validator counts it as one pattern.
## store_yaml recovery over the native API (#16445)
`scripts/api/syrecover.py HOST KEY OUTDIR [ORIGDIR]` (KEY `-` for plaintext) builds the client library's
`APINoiseFrameHelper` around a stub `APIConnection` subclass (the compiled helper type checks the connection, and
`ConnectionParams` needs every field), sends Hello, Authentication (never answered without a password) and
`DeviceCapabilitiesRequest`, then `GetYamlRequest` (161), collects `GetYamlResponse` (162) frames by field number
(1 data, 2 done, 3 total_size, 4 encoding) until done, requests again, decompresses with `compression.zstd` and
unpacks the envelope with the tree's `unpack_envelope` (run with `PYTHONPATH` on that tree). Configs in
`templates/store_yaml/`: entry files in `boards/`, a package one directory above them with `!secret` and an inline
OTA password,
so the redaction and the `..` paths are exercised. Repetitive YAML compresses into one chunk even at 22 KB; a 6 KB
include of random base64 lines with `include_secrets: true` gives four chunks (1390 byte packets). Rough edge at
0bd3738430: `include_secrets: true` fails the build when a package in another directory uses `!secret` and has no
`secrets.yaml` beside it; a copy next to the package works around it.
Results: https://github.com/esphome/esphome/pull/16445#issuecomment-6335435397
@@ -1,19 +0,0 @@
# ble_midi
## BLE MIDI client against a fake instrument (#18508)
No MIDI hardware needed: `templates/ble_midi/midiperi.yaml` turns a second ESP32 (an S3 with ethernet, so the
API stays reachable while BLE is busy) into a BLE MIDI peripheral with `esp32_ble_server`: the MIDI service
advertised, the MIDI characteristic with read, write, write without response and notify, an `on_write` that logs
the bytes, buttons that notify hand built packets (channel messages, two messages in one packet, running status,
SysEx in one packet, split over two, oversize, cut short, clock, aftertouch, orphan data bytes, a 50 packet burst)
and a restart button for the disconnect test. `midicli.yaml` is the component test config on a classic devkit with
`wifi:`, `api:`, the peripheral's BLE address in `PERI_MAC` (the base MAC plus 2, printed by `esp32_ble` in
`dump_config`), `on_sysex` and `on_message` lambdas that print the bytes, debug sensors and out of range send
buttons. Drive both over the plaintext API with `scripts/modem/mdrive.py HOST LOGFILE press:NAME wait:S` 3 s
apart and compare the peripheral's `TX` and `RX` lines with the client's decoded lines. Gotcha:
`ble_server.characteristic.set_value` latches the value of its first play and re-applies it before every notify,
so the peripheral script sets the value from a lambda (`id(midi_io).set_value(std::vector<uint8_t>(pkt));
id(midi_io).notify();`). At VERBOSE the client logs four lines per message, so a burst shows an
`esp32_ble took a long time` of a couple of seconds that is the serial port, not the parser.
Results: https://github.com/esphome/esphome/pull/18508#issuecomment-6348272717
@@ -1,17 +0,0 @@
# http_request
## Update manifest URL changed at run time (#21373)
`scripts/http_request/manifest_server.py PORT [DELAY]` serves `stable.json`, `beta.json` and `slow.json` (answered
after DELAY s) with both chip families and logs every request. `templates/http_request_update/upd-common.yaml`
(secret `manifest_base` is the server URL) has the update entity on the stable manifest, buttons for every
`set_source_url` shape (`std::string` local, literal, temporary, `.c_str()` of a local, the current URL back in),
`Check Then Swap` (ESP32 task path: check the slow manifest and replace the URL in the same lambda), `Arm Swap In
Check` plus a `logger: on_message` lambda that replaces the URL from inside the inline check (ESP8266; the log line
from inside `on_message` is swallowed, judge by the next check's result), and a `Heap` button. `upd-base-common.yaml`
keeps only the shapes that compile on a tree with the bare pointer setter; `boards/upd8266.yaml` and `boards/upd32.yaml` are the
per-board files, one directory below the package their `../` include points at. Drive with `scripts/http_request/updrive.py HOST KEY LOGFILE press:NAME wait:S`, which prints every
update state (latest version, summary). The ESP8266 Arduino client gets `Error reading manifest: -1` on roughly a
quarter of the fetches from this Python server on every build, so count checks against the base before blaming a
PR. `http_request` on the ESP8266 needs `verify_ssl: false` even for plain http URLs.
Results: https://github.com/esphome/esphome/pull/21373#issuecomment-6337544933
@@ -1,13 +0,0 @@
# ld2412
## Output pin level and the basic configuration command (#21353)
`templates/ld2412-select-only-dut.yaml` (ESP32-S3, the module on GPIO43 and GPIO44 at 115200, only the output pin
level select, the module's OUT pin on GPIO38 as a binary sensor plus the presence sensor) with `uart: debug:
direction: BOTH`. Drive the select with `scripts/ld2450/lddrive.py HOST KEY LOGFILE sel:NAME=OPTION` and read the TX
dump for command `02` carrying the level byte, then the OUT pin binary sensor against presence: with level high the
pin follows presence, with level low it is inverted. A module that never answers the boot query (no `<<<` lines)
only shows the held back path: `Command 02 held back until the module reports its settings`, a query `12`, and after
2 s `Command 02 not sent, the module did not report its settings` with the select republished to the last known
level. With the full set of numbers configured a select change also needs every number to have a state, so wait
for the boot query answer before judging.
@@ -13,30 +13,3 @@ selects over the API and prints every LD2450 state update. After each set the hu
the radar and republishes all twelve coordinates from the answer, so set each coordinate of each zone to a distinct
value and check the readbacks; no target is needed. Readbacks can arrive late over Wi-Fi, so match them by content.
Results: https://github.com/esphome/esphome/pull/21146#issuecomment-6278026922
## ESP8266 loopback harness, no radar needed (#21349)
`templates/ld2450-loopback.yaml` (package; substitutions `name` and `baud`) runs the hub on UART0 (GPIO1 and GPIO3)
with `logger: baud_rate: 0` and sets the UART0 loopback bit in `on_boot` (`USC0(0) |= (1 << UCLBE)`), so everything
the hub or a `uart.write` action sends comes back on RX. Template buttons inject periodic frames (all four direction
strings, targets 2 and 3), query zone acks (zone type 0, 1 and 2 for the select readback) and call the name based
setters with a valid and a bogus name. `uart: debug: direction: TX` delimited on the command footer dumps every
command the hub sends. Drive it with `scripts/ld2450/lddrive.py HOST KEY LOGFILE press:NAME sel:NAME=OPTION wait:S`
(logs over the API at VERBOSE, 2 s between steps because the API log stream drops lines while `send_command_`
blocks), then `txsum.py LOGFILE` lists the TX frames per step; diff the summaries of the two builds. The hub reads its
own commands back and logs `Invalid status` for each, which is harmless. The boot readback of the baud rate select
comes from the UART setting, so a second build at `baud: "9600"` checks the flash table lookup.
Results: https://github.com/esphome/esphome/pull/21349#issuecomment-6332320026
## Frame splitter collisions (#21357)
The loopback template also carries the zone 1 numbers and seven collision buttons: a periodic frame with the
command footer inside target 1, zone acks with `55 CC` and with `04 03 02 01` inside the coordinates, a periodic
frame cut after 12 bytes followed by a zone ack, a frame missing its last byte followed by a complete one, a bogus
255 byte length then a frame, and garbage then a frame. Judge by the state updates plus the `Handling Periodic` and
`Handling Ack` lines. Through a real radar the set zone ack is sometimes corrupted by the radar itself (its tail
replaced by the periodic frame it was sending, for example `04 03 03 03 00 ... 55 CC`), so count readbacks over at
least 24 selects per build and read the raw bytes with `uart: debug: direction: RX` (`!extend` the package's uart
id) before blaming the parser; the API log stream drops lines under that dump volume, so some misses stay
unclassified.
Results: https://github.com/esphome/esphome/pull/21357#issuecomment-6333244886
@@ -1,18 +0,0 @@
# modem
## PPPoS modem component with no modem attached (#6721)
A boot check on a classic ESP32 devkit with the modem UART pins left open. `templates/modem/modem-common.yaml` is
the package (`api:`, debug `loop_time` and `free` sensors, uptime, buttons for `modem.enable`, `modem.disable` and
`modem.send_at`, a 20 s alive log); `modemo.yaml` is the modem only build with `reboot_timeout: 2min` and the
component test config's `on_boot` block at `priority: -100` (earlier, `modem.send_at` runs before the modem's
`setup()` and logs `Modem DCE not ready`); `modemw.yaml` adds `wifi:` with `reboot_timeout: 0s` so the API stays
reachable. Expect `Modem not responding at 115200 baud` once per pass with each pass blocking the loop about 1 s,
the reboot at the configured timeout (`SW_CPU_RESET`), and a `modem.disable` with no modem answering to block
the loop about 37 s (`Failed to leave data mode`, then the radio off timeout) without tripping the 30 s watchdog
floor because the component feeds it in between. Drive the Wi-Fi build with
`scripts/modem/mdrive.py HOST LOGFILE list press:NAME wait:S` (plaintext API; prints sensor and text sensor
states, saves the device log). Before 8cc8df4a1f a modem only config with `api:` failed validation because a
config dependent `AUTO_LOAD` added `network` after the dependency check; an explicit `network:` block was the
workaround. Results: https://github.com/esphome/esphome/pull/6721#issuecomment-6347395159 and
https://github.com/esphome/esphome/pull/6721#issuecomment-6348520416
@@ -20,7 +20,7 @@ the TLS listener's certificate, both run in the scratch directory so keys, logs
to `NAME/#`, sends a switch command and an `on_message` topic, presses the 3000 byte publish button over the API,
restarts the local broker when the last argument is `restart` (the PID in `mosquitto.pid`, written when the broker
was started with `mosquitto -c mosquitto-ws.conf & echo $! > mosquitto.pid` in the working directory next to the
certificates), checks every listener in the broker config answers again, and prints the broker's message counts with the device log. `mosquitto_sub` cannot speak
certificates), checks its three ports answer again, and prints the broker's message counts with the device log. `mosquitto_sub` cannot speak
WebSocket, so it subscribes on the plain listener of the same broker. Cases: `tcp` on 1883, `ws` on 9001, `wss` on
9002 with `certificate_authority` (the certificate names an address, so `skip_cert_cn_check: true`), `wss` to
test.mosquitto.org on 8081 with no CA for the certificate bundle, and two negatives that must never connect: a
@@ -29,25 +29,3 @@ different CA (`mbedtls_ssl_handshake returned -0x2700`) and the self-signed serv
default in the generated sdkconfig. The TLS session costs about 29 kB of internal heap while connected; the public
broker carries other devices' `esphome/discover` traffic, which is noise. Results:
https://github.com/esphome/esphome/pull/16056#issuecomment-6301034019
## TLS trust sources on ESP-IDF (#14318)
One build per trust source from `templates/mqtt-tls-dut.yaml`: broker only (port must resolve to 1883),
`certificate_authority` with no port (must resolve to 8883), `verify_ssl: false` with no CA, the CA plus
`client_certificate` and `client_certificate_key` on the 8884 listener that requires one, `transport: wss` with the CA,
`tls: true` to test.mosquitto.org on 8886 for the certificate bundle, and `tls: true` to the local self-signed broker as
the negative. `scripts/mqtt/mosquitto-ws.conf` now has the 8883 and 8884 listeners and `scripts/mqtt/make_test_ca.sh`
also writes `client.crt`/`client.key`. Before flashing, check each build's `src/esphome/core/defines.h` for
`USE_MQTT_TLS`, `USE_MQTT_CERT_BUNDLE` and `USE_MQTT_TLS_INSECURE` and its `sdkconfig.<name>` for
`CONFIG_MBEDTLS_CERTIFICATE_BUNDLE` and `CONFIG_ESP_TLS_INSECURE`: only the bundle builds may carry the bundle and only
the unverified build the insecure flags. `scripts/mqtt/flash_and_drive.sh TREE YAML PORT KEY BROKER_HOST
local|public|neg` runs one build end to end (flash, bounded capture, `cases.sh` with a restart for the local broker,
counts and the broker's per-listener connection lines) and prints the config dump's `TLS:` line (`CA certificate`,
`certificate bundle` or `no verification`). The CLI side (`esphome/mqtt.py`) is covered without a board by
`scripts/mqtt/cli_tls_check.sh TREE [neg] YAML...`, which runs `esphome clean-mqtt` under a timeout: status 124
after `Connected to MQTT broker!` is a pass, and with `neg` each config must instead fail with
`CERTIFICATE_VERIFY_FAILED` and never connect. Python's default SSL
context rejects a CA that lacks a `keyUsage` extension (`CA cert does not include key usage extension`) while mbedTLS
on the device accepts it, which is why the test CA carries `basicConstraints` and `keyUsage`. A broker kill shows as one
`Last esp-tls error: 0x8008` on every build, TLS or not. Results:
https://github.com/esphome/esphome/pull/14318#issuecomment-6311863827
@@ -9,18 +9,3 @@ listener. Broadcast to boards is lossy under power save, unicast is not. Flood a
reconnect on the Pico keeps IGMP membership. Set a long loop interval from `wifi.on_connect`
(`App.set_loop_interval(20000)`), not `on_boot`, or Wi-Fi connect crawls. The `logger.log` action
rejects `%.*s`; use a lambda.
## udp.write payload shapes (#21263, ApplyBytes)
`templates/udp-write-dut.yaml` puts every payload shape behind a template button (constant 4 bytes twice, 3, 9, 0,
128, 129 and 600 bytes, a 300 byte lambda, a simple value, a string) plus an `on_receive` that replies with the
received bytes reversed. `scripts/udp/write_cases.sh DEVICE_IP KEY` records the device's datagrams with
`udp_listen.py` on the test host, presses the buttons two seconds apart, sends one unicast datagram for the echo and
compares everything byte for byte with `check.py`; a missing datagram shifts every later one, so read the first
mismatch. Before flashing, check the generated `main.cpp`: on ESP8266 a constant payload is copied out of its
`PROGMEM` table into `uint8_t esphome__bytes[N]` up to the stack limit and into `std::make_unique<uint8_t[]>` beyond,
identical payloads share one table, a lambda is sent from its vector. An empty constant payload is generated as
`send_packet(nullptr, 0)`, and the raw lwIP UDP `sendto` used on ESP8266 rejected a null buffer with `EINVAL`
(`sendto() error 22` in the log, no datagram) until it accepted `nullptr` with a zero length; the old
`TemplatableBytes::visit` had passed a zero length stack buffer instead. Compare sizes against the stacked PR's own
head when the PR is stacked, not against dev. Results: https://github.com/esphome/esphome/pull/21263
@@ -29,32 +29,3 @@ clients. Entity URLs use the entity name, URL encoded, not an object id. The HTT
request of the boot; compare after the mix and after the SSE round, with two boots when the numbers are close.
Results: https://github.com/esphome/esphome/pull/21109#issuecomment-6300582328 and
https://github.com/esphome/esphome/pull/21038#issuecomment-6301310182
## Entity icon and device class readback (#21111)
Covers every path that reads an entity's icon or device class: the native API listing, MQTT discovery, the web_server
`/events` listing and REST endpoints, and `dump_config`. `templates/entity-icon-dut.yaml` (ESP8266 board with wifi,
encrypted api, ota, web_server and mqtt against a local broker) carries a sensor with a 63 character icon, one with no
icon or device class, an internal sensor, and binary_sensor, switch, button, text_sensor, number, select, event, cover
and text entities with icons and device classes. The per-run file supplies `name` and `broker_ip` through
substitutions. Flash the PR and base builds under different names, then from the scratch directory:
```sh
scripts/entity_icons/readback.sh DEVICE_IP KEY NAME BROKER_HOST cap-NAME.txt # once per build
scripts/entity_icons/compare.sh ic-base ic-pr # api, web, mqttn, dump all identical
```
`readback.sh` takes about 25 s once the device is on the network: 12 s of `mosquitto_sub` on `homeassistant/#` and
`NAME/#`, the API listing (`api_list.py`, one JSON line per entity with icon, device class, unit, options and event
types) and `web_dump.py` (6 s on `/events`, keeping only the listing rows that carry `domain`, then a GET of each
listed id with `?detail=all` and one retry, because the ESP8266 async server occasionally leaves a request unanswered
within 5 s). The web ids are `domain/Name`, not object ids, and `/event/<name>` has no GET handler (404 on every
build). `mqtt_norm.py` drops the `dev` block (compile time) and the `NAME/debug` topic (heap probe), so a clean run
differs in nothing. `dump_config` prints icons and device classes only for some domains; button, event and
binary_sensor icons never appear there.
Found with this flow on 2026-10-21: since #19577 every ESP8266 `web_server` build that lists a cover, climate, lock,
valve, update, alarm_control_panel or water_heater entity reboots with `Exception (3) LoadStoreErr` in
`json::linked(const char *)` (the ROM `strlen` on a flash address), fixed by #21316. The cover in the template keeps
that path covered; before #21316 is merged, apply its one line change to both trees or the web_server comparison
cannot run.
@@ -23,17 +23,3 @@ Only an Android phone configures: Settings, network details, Add device, Share W
QR scanner makes the phone an enrollee). Render the URI with the `qrcode` module and open the PNG.
`esp32: framework: log_level: DEBUG` plus `sdkconfig_options: CONFIG_WPA_DEBUG_PRINT: y` prints the
supplicant state machine.
## WISP bridge with an ethernet upstream (#7147)
`templates/wisp-ethernet-dut.yaml` (ESP32-S3 with a W5500, `wifi: ap:` with `ap_mode: WISP` and no station) and
`templates/wisp-client.yaml` (D1 mini joining the bridge, `http_request` to a web server on the wired LAN and to
`http://example.com/` every 15 s, `time: sntp`). Secrets: `wisp_psk` and `lan_http` (serve a text file on the host
with `python -m http.server 8787 --bind 0.0.0.0` from a scratch directory). Judge by the client's `GET status 200`
lines, the web server log showing the device's ethernet address as the source (NAT), the client's DHCP info (DNS1 is
the LAN resolver) and `SNTP synced`. The device logs `Starting AP` and `DHCP DNS server: <resolver>` on its USB
console; flashing over the air reboots it for repeat boots. Before the AP address and DHCP server moved ahead of the
AP start (the fix in that PR), the first AP start failed on every boot with `esp_netif_dhcps_option failed: 20484`
(DHCP server already started by the AP start event) and `Failed to enable NAT`, and the bridge only came up on the
retry 90 s later; a client that cannot find the SSID for the first minute on an older build is that. With the fix
the bridge is up 6 s after boot. Any change to the ESP-IDF AP start should also rerun the captive portal hammer.
@@ -1,211 +0,0 @@
"""Plaintext native API client that lists user actions with their raw argument fields and executes one.
usage: rawactions.py HOST list
rawactions.py HOST exec ACTION key=value [...] (every argument must be given; types from the listing)
Fields of ListEntitiesServicesArgument: 1 name, 2 type, 3 description, 4 example, 5 optional, 6 default_value.
"""
import os
import socket
import struct
import sys
HOST = sys.argv[1]
CMD = sys.argv[2]
ARGS = sys.argv[3:]
TYPE_NAMES = {
0: "bool",
1: "int",
2: "float",
3: "string",
4: "bool[]",
5: "int[]",
6: "float[]",
7: "string[]",
}
def varint(v: int) -> bytes:
out = bytearray()
while True:
b = v & 0x7F
v >>= 7
if v:
out.append(b | 0x80)
else:
out.append(b)
return bytes(out)
def svarint(v: int) -> bytes:
return varint((v << 1) ^ (v >> 63))
def decode_varint(buf: bytes, pos: int) -> tuple[int, int]:
value = shift = 0
while True:
b = buf[pos]
pos += 1
value |= (b & 0x7F) << shift
if not b & 0x80:
return value, pos
shift += 7
def fields(payload: bytes) -> list[tuple[int, int, object]]:
out = []
pos = 0
while pos < len(payload):
key, pos = decode_varint(payload, pos)
fnum, wt = key >> 3, key & 7
if wt == 0:
v, pos = decode_varint(payload, pos)
elif wt == 2:
ln, pos = decode_varint(payload, pos)
v = payload[pos : pos + ln]
pos += ln
elif wt == 5:
v = payload[pos : pos + 4] # fixed32, caller unpacks
pos += 4
else:
raise ValueError(f"wire type {wt}")
out.append((fnum, wt, v))
return out
def frame(mtype: int, payload: bytes) -> bytes:
return b"\x00" + varint(len(payload)) + varint(mtype) + payload
def field_bytes(fnum: int, data: bytes) -> bytes:
return varint((fnum << 3) | 2) + varint(len(data)) + data
def field_varint(fnum: int, v: int) -> bytes:
return varint((fnum << 3) | 0) + varint(v)
def recv_frames(
sock: socket.socket, until: set[int], limit: float = 5.0
) -> list[tuple[int, bytes]]:
sock.settimeout(limit)
buf = bytearray()
frames = []
while True:
try:
chunk = sock.recv(4096)
except TimeoutError:
return frames
if not chunk:
return frames
buf += chunk
while True:
if len(buf) < 3:
break
if buf[0] != 0:
raise ValueError("not plaintext")
try:
ln, p = decode_varint(buf, 1)
mt, p = decode_varint(buf, p)
except IndexError:
break
if len(buf) < p + ln:
break
frames.append((mt, bytes(buf[p : p + ln])))
del buf[: p + ln]
if mt in until:
return frames
def main() -> None:
sock = socket.create_connection((HOST, 6053), timeout=5)
sock.sendall(
frame(
1, field_bytes(1, b"rawactions") + field_varint(2, 1) + field_varint(3, 10)
)
) # HelloRequest
recv_frames(sock, {2})
sock.sendall(frame(3, b"")) # ConnectRequest, no password
recv_frames(sock, {4})
sock.sendall(frame(11, b"")) # ListEntitiesRequest
frames = recv_frames(sock, {19})
services = {}
for mt, payload in frames:
if mt != 41:
continue
name = key = None
args = []
for fnum, _, v in fields(payload):
if fnum == 1:
name = v.decode()
elif fnum == 2:
key = struct.unpack("<I", v)[0]
elif fnum == 3:
a = {
"name": None,
"type": 0,
"description": None,
"example": None,
"optional": None,
"default": None,
}
for af, _, av in fields(v):
if af == 1:
a["name"] = av.decode()
elif af == 2:
a["type"] = av
elif af == 3:
a["description"] = av.decode()
elif af == 4:
a["example"] = av.decode()
elif af == 5:
a["optional"] = bool(av)
elif af == 6:
a["default"] = av.decode()
else:
a[f"field{af}"] = av
args.append(a)
services[name] = (key, args)
if CMD == "list":
for name, (key, args) in services.items():
print(f"action {name} key={key}")
for a in args:
extra = {
k: v
for k, v in a.items()
if k not in ("name", "type") and v is not None
}
print(f" {a['name']}: {TYPE_NAMES.get(a['type'], a['type'])} {extra}")
return
name = ARGS[0]
key, args = services[name]
payload = varint((1 << 3) | 5) + struct.pack("<I", key)
given = dict(kv.split("=", 1) for kv in ARGS[1:])
for a in args:
if a["name"] not in given:
if os.environ.get("ALLOW_MISSING"):
continue # leave the argument out of the request on purpose
sys.exit(f"missing {a['name']}")
raw = given[a["name"]]
t = a["type"]
if t == 0:
av = field_varint(1, 1 if raw.lower() == "true" else 0)
elif t == 1:
av = varint((5 << 3) | 0) + svarint(int(raw)) # sint32 int_
elif t == 2:
av = varint((3 << 3) | 5) + struct.pack("<f", float(raw))
elif t == 3:
av = field_bytes(4, raw.encode())
elif t == 5:
av = b"".join(
varint((7 << 3) | 0) + svarint(int(x)) for x in raw.split(",") if x
) # repeated sint32 int_array
else:
sys.exit(f"type {t} not supported here")
payload += field_bytes(2, av)
sock.sendall(frame(42, payload)) # ExecuteServiceRequest
print(f"sent {name} with {given}")
sock.close()
main()
@@ -1,190 +0,0 @@
"""Pull the store_yaml blob off a device over the native API (plaintext or Noise) and unpack it.
usage: syrecover.py HOST KEY OUTDIR [ORIGDIR] (KEY of - means a plaintext API)
Prints the capability flag, chunk count, advertised and received sizes, encoding, the envelope's files, and
when ORIGDIR is given compares each recovered file with ORIGDIR/<path>.
Run with PYTHONPATH pointing at the esphome tree that has store_yaml (for unpack_envelope).
"""
import asyncio
from pathlib import Path
import sys
import time
from aioesphomeapi import api_pb2
from aioesphomeapi._frame_helper.noise import APINoiseFrameHelper
from aioesphomeapi._frame_helper.plain_text import APIPlaintextFrameHelper
from aioesphomeapi.connection import APIConnection, ConnectionParams
from aioesphomeapi.zeroconf import ZeroconfManager
from compression import zstd
from esphome.components.store_yaml import unpack_envelope
HOST, KEY, OUT = sys.argv[1], sys.argv[2], Path(sys.argv[3])
ORIG = Path(sys.argv[4]) if len(sys.argv) > 4 else None
HELLO, HELLO_RESP, CONNECT, CONNECT_RESP = 1, 2, 3, 4
CAPS_REQ, CAPS_RESP = 149, 150
GET_YAML_REQ, GET_YAML_RESP = 161, 162
def decode_varint(buf: bytes, pos: int) -> tuple[int, int]:
value = shift = 0
while True:
b = buf[pos]
pos += 1
value |= (b & 0x7F) << shift
if not b & 0x80:
return value, pos
shift += 7
def fields(payload: bytes) -> dict[int, list]:
out: dict[int, list] = {}
pos = 0
while pos < len(payload):
key, pos = decode_varint(payload, pos)
fnum, wt = key >> 3, key & 7
if wt == 0:
v, pos = decode_varint(payload, pos)
elif wt == 2:
ln, pos = decode_varint(payload, pos)
v = payload[pos : pos + ln]
pos += ln
elif wt == 5:
v = payload[pos : pos + 4]
pos += 4
else:
raise ValueError(f"wire type {wt}")
out.setdefault(fnum, []).append(v)
return out
async def main() -> None:
loop = asyncio.get_running_loop()
queue: asyncio.Queue[tuple[int, bytes]] = asyncio.Queue()
errors: list[Exception] = []
class Shim(APIConnection):
"""Only the two callbacks the frame helper uses; the real connection logic is bypassed."""
def process_packet(self, msg_type_proto, data) -> None:
queue.put_nowait((msg_type_proto, bytes(data)))
def report_fatal_error(self, err: Exception) -> None:
errors.append(err)
params = ConnectionParams(
addresses=[HOST],
port=6053,
password=None,
client_info="syrecover",
keepalive=15,
zeroconf_manager=ZeroconfManager(),
noise_psk=None if KEY == "-" else KEY,
expected_name=None,
expected_mac=None,
timezone=None,
provide_time=True,
)
shim = Shim(params, None, False, HOST)
if KEY == "-":
helper = APIPlaintextFrameHelper(
connection=shim, client_info="syrecover", log_name=HOST
)
else:
helper = APINoiseFrameHelper(
connection=shim,
noise_psk=KEY,
expected_name=None,
expected_mac=None,
client_info="syrecover",
log_name=HOST,
)
await loop.create_connection(lambda: helper, HOST, 6053)
await asyncio.wait_for(helper.ready_future, 10)
print(f"transport {'plaintext' if KEY == '-' else 'noise'}: handshake done")
async def expect(msg_type: int, timeout: float = 10) -> bytes:
while True:
t, d = await asyncio.wait_for(queue.get(), timeout)
if t == msg_type:
return d
hello = api_pb2.HelloRequest(
client_info="syrecover", api_version_major=1, api_version_minor=10
)
helper.write_packets([(HELLO, hello.SerializeToString())], False)
await expect(HELLO_RESP)
# Sent like the client library does; a device without a password never answers it
helper.write_packets(
[(CONNECT, api_pb2.AuthenticationRequest().SerializeToString())], False
)
helper.write_packets(
[(CAPS_REQ, api_pb2.DeviceCapabilitiesRequest().SerializeToString())], False
)
caps = fields(await expect(CAPS_RESP))
supported = None
if 7 in caps:
supported = fields(caps[7][0]).get(1) == [1]
print(f"capabilities: store_yaml field present={7 in caps} supported={supported}")
t0 = time.monotonic()
helper.write_packets([(GET_YAML_REQ, b"")], False)
chunks: list[bytes] = []
total = encoding = None
done = False
while not done:
f = fields(await expect(GET_YAML_RESP, 20))
if 1 in f:
chunks.append(f[1][0])
done = f.get(2) == [1]
if 3 in f:
total = f[3][0]
if 4 in f:
encoding = f[4][0].decode()
dt = time.monotonic() - t0
compressed = b"".join(chunks)
print(f"chunks {len(chunks)} sizes {[len(c) for c in chunks]}")
print(
f"received {len(compressed)} bytes, advertised {total}, encoding {encoding!r}, {dt:.2f} s"
)
# a second request right after: the device must answer again, not stay stuck
helper.write_packets([(GET_YAML_REQ, b"")], False)
again = 0
done = False
while not done:
f = fields(await expect(GET_YAML_RESP, 20))
again += len(f.get(1, [b""])[0])
done = f.get(2) == [1]
print(f"second request: {again} bytes")
helper.close()
if errors:
print("transport errors:", errors)
envelope = zstd.decompress(compressed)
files = unpack_envelope(envelope)
print(
f"envelope {len(envelope)} bytes, {len(files)} files, ratio {len(compressed) / len(envelope):.2f}"
)
OUT.mkdir(parents=True, exist_ok=True)
for rel, content in files.items():
target = OUT / rel.replace("../", "up_/")
if not target.resolve().is_relative_to(OUT.resolve()):
print("skipping a path outside the output directory:", rel)
continue
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(content)
note = ""
if ORIG is not None:
orig = (ORIG / rel).resolve()
if orig.exists():
note = (
"IDENTICAL to original"
if orig.read_bytes() == content
else f"DIFFERS from original ({orig.stat().st_size} bytes)"
)
else:
note = "no original"
print(f" {rel}: {len(content)} bytes {note}")
asyncio.run(main())
@@ -1,56 +0,0 @@
"""List every entity over the native API with its icon, device class and unit: one sorted JSON line each.
usage: api_list.py HOST KEY OUT
"""
import asyncio
import json
import pathlib
import sys
from aioesphomeapi import APIClient
HOST, KEY, OUT = sys.argv[1], sys.argv[2], sys.argv[3]
async def main() -> None:
c = APIClient(HOST, 6053, None, noise_psk=KEY)
await c.connect(login=True)
ents, _ = await c.list_entities_services()
rows = []
for e in ents:
row = {
"type": type(e).__name__,
"name": e.name,
"object_id": e.object_id,
"icon": e.icon,
}
for k in (
"device_class",
"unit_of_measurement",
"event_types",
"options",
"mode",
):
if hasattr(e, k):
v = getattr(e, k)
row[k] = (
list(v)
if isinstance(v, (list, tuple))
else (v.name if hasattr(v, "name") else v)
)
row["entity_category"] = (
e.entity_category.name
if hasattr(e.entity_category, "name")
else e.entity_category
)
row["disabled_by_default"] = e.disabled_by_default
rows.append(row)
rows.sort(key=lambda r: (r["type"], r["name"]))
with pathlib.Path(OUT).open("w", encoding="utf-8") as f:
f.writelines(json.dumps(r, sort_keys=True) + "\n" for r in rows)
print(f"{len(rows)} entities listed")
await c.disconnect()
asyncio.run(main())
@@ -1,13 +0,0 @@
#!/bin/sh
# compare.sh NAME_A NAME_B : diff the readback.sh outputs of two device names (api, web, mqttn and, when present, dump)
# after replacing each name with DEV. Exit 1 when any pair differs; the diffs stay in cmp-KIND.diff.
A=$1; B=$2; rc=0
[ -n "$A" ] && [ -n "$B" ] || { echo "usage: compare.sh NAME_A NAME_B"; exit 2; }
for k in api web mqttn dump; do
ext=jsonl; [ $k = dump ] && ext=txt
[ -f "$PWD/$k-$A.$ext" ] && [ -f "$PWD/$k-$B.$ext" ] || { echo "$k: missing"; [ $k = dump ] || rc=1; continue; }
sed "s/$A/DEV/g; s/$(echo "$A" | tr - _)/DEV/g" "$PWD/$k-$A.$ext" > "$PWD/cmp-$k-a.txt"
sed "s/$B/DEV/g; s/$(echo "$B" | tr - _)/DEV/g" "$PWD/$k-$B.$ext" > "$PWD/cmp-$k-b.txt"
if diff -u "$PWD/cmp-$k-a.txt" "$PWD/cmp-$k-b.txt" > "$PWD/cmp-$k.diff"; then echo "$k: identical ($(wc -l < "$PWD/cmp-$k-b.txt" | tr -d ' ') lines)"; else echo "$k: DIFFERS"; head -20 "$PWD/cmp-$k.diff"; rc=1; fi
done
exit $rc
@@ -1,37 +0,0 @@
"""Normalise mosquitto_sub output (topic TAB payload) for one device: drop other devices, replace the name, drop dev block.
usage: mqtt_norm.py IN NAME OUT
"""
import json
import pathlib
import sys
IN, NAME, OUT = sys.argv[1], sys.argv[2], sys.argv[3]
rows = {}
with pathlib.Path(IN).open(encoding="utf-8") as f:
for line in f:
topic, _, payload = line.rstrip("\n").partition("\t")
if (
f"/{NAME}/" not in topic and not topic.startswith(f"{NAME}/")
) or topic == f"{NAME}/debug":
continue
topic = topic.replace(NAME, "DEV")
try:
obj = json.loads(payload)
except ValueError:
rows[topic] = payload.replace(NAME, "DEV")
continue
if isinstance(obj, dict):
obj.pop("dev", None)
obj = {
k: (v.replace(NAME, "DEV") if isinstance(v, str) else v)
for k, v in obj.items()
}
rows[topic] = obj
with pathlib.Path(OUT).open("w", encoding="utf-8") as f:
f.writelines(
json.dumps({"topic": t, "payload": rows[t]}, sort_keys=True) + "\n"
for t in sorted(rows)
)
print(f"{len(rows)} topics for {NAME}")
@@ -1,18 +0,0 @@
#!/bin/sh
# readback.sh DEVICE_IP KEY NAME BROKER_HOST [CAPTURE] : record how a device built from templates/entity-icon-dut.yaml
# reports its entity icons and device classes: the native API listing (api-NAME.jsonl), the retained MQTT discovery
# payloads and states (mqtt-NAME.txt, normalised to mqttn-NAME.jsonl with the device name replaced and the debug topic
# dropped), the web_server /events listing plus every REST endpoint with ?detail=all (web-NAME.jsonl) and, when a
# serial CAPTURE is given, the dump_config Icon and Device Class lines (dump-NAME.txt, timestamps and line numbers
# removed). KEY as for api_button.py. Outputs go to the working directory; compare two names with compare.sh.
HERE=$(cd "$(dirname "$0")" && pwd); PY=${PYTHON:-python3}; DEV=$1; KEY=$2; N=$3; B=$4; CAP=$5; rc=0; TAB=$(printf '\t')
[ -n "$DEV" ] && [ -n "$KEY" ] && [ -n "$N" ] && [ -n "$B" ] || { echo "usage: readback.sh DEVICE_IP KEY NAME BROKER_HOST [CAPTURE]"; exit 2; }
timeout 12 mosquitto_sub -h "$B" -v -t 'homeassistant/#' -t "$N/#" -F "%t$TAB%p" > "$PWD/mqtt-$N.txt" 2> "$PWD/mqtt-$N.err"
$PY "$HERE/mqtt_norm.py" "$PWD/mqtt-$N.txt" "$N" "$PWD/mqttn-$N.jsonl" || rc=1
$PY "$HERE/api_list.py" "$DEV" "$KEY" "$PWD/api-$N.jsonl" || { echo "API listing failed"; rc=1; }
$PY "$HERE/web_dump.py" "$DEV" "$PWD/web-$N.jsonl" || { echo "web_server dump failed"; rc=1; }
if [ -n "$CAP" ]; then
LC_ALL=C grep -a '\]: Icon: \|\]: Device Class: ' "$CAP" | LC_ALL=C sed 's/\x1b\[[0-9;]*m//g; s/^\[[^]]*\]//; s/:[0-9]*\]:/]:/' | sort > "$PWD/dump-$N.txt"
echo "dump_config icon lines $(grep -c 'Icon:' "$PWD/dump-$N.txt"), device class lines $(grep -c 'Device Class:' "$PWD/dump-$N.txt")"
fi
exit $rc
@@ -1,99 +0,0 @@
"""Dump the web_server view of every entity: the /events listing (detail all) and each REST endpoint with ?detail=all.
usage: web_dump.py HOST OUT
"""
import json
import pathlib
import socket
import sys
import time
import urllib.parse
import urllib.request
HOST, OUT = sys.argv[1], sys.argv[2]
def sse_listing(seconds: float) -> list[dict]:
s = socket.create_connection((HOST, 80), timeout=5)
s.sendall(
b"GET /events HTTP/1.1\r\nHost: %s\r\nAccept: text/event-stream\r\n\r\n"
% HOST.encode()
)
buf = b""
deadline = time.monotonic() + seconds
s.settimeout(1.0)
while time.monotonic() < deadline:
try:
data = s.recv(65536)
except TimeoutError:
continue
if not data:
break
buf += data
s.close()
head, _, body = buf.partition(b"\r\n\r\n")
if b"chunked" in head.lower():
out = b""
while True:
i = body.find(b"\r\n")
if i < 0:
break
size = int(body[:i].split(b";")[0], 16)
out += body[i + 2 : i + 2 + size]
body = body[i + 2 + size + 2 :]
if size == 0:
break
body = out
rows = []
for block in body.decode(errors="replace").split("\n\n"):
ev, data = None, None
for line in block.split("\n"):
line = line.rstrip("\r")
if line.startswith("event:"):
ev = line[6:].strip()
elif line.startswith("data:"):
data = line[5:].strip()
if ev in ("state", "state_detail_all") and data:
try:
obj = json.loads(data)
except ValueError:
rows.append({"event": ev, "invalid": data})
continue
# bare state updates that land inside the window carry no domain; only the listing rows are kept
if "domain" in obj:
rows.append({"event": ev, **obj})
return rows
def get_json(url: str) -> dict:
"""One retry: the ESP8266 async server occasionally leaves a request unanswered."""
for attempt in (1, 2):
try:
with urllib.request.urlopen(url, timeout=5) as resp:
return json.loads(resp.read())
except TimeoutError:
if attempt == 2:
raise
print(f"retrying {url}", flush=True)
return {}
rows = sse_listing(6)
ids = sorted({r["id"] for r in rows if "id" in r})
rest = []
for ident in ids:
domain, _, obj = ident.partition("/")
url = f"http://{HOST}/{domain}/{urllib.parse.quote(obj)}?detail=all"
try:
rest.append({"url": url.replace(HOST, "HOST"), **get_json(url)})
except Exception as e: # noqa: BLE001
rest.append({"url": url.replace(HOST, "HOST"), "error": str(e)})
time.sleep(0.3)
with pathlib.Path(OUT).open("w", encoding="utf-8") as f:
f.writelines(
json.dumps(r, sort_keys=True) + "\n"
for r in sorted(rows, key=lambda r: (r.get("id", ""), r["event"]))
)
f.writelines(json.dumps(r, sort_keys=True) + "\n" for r in rest)
print(f"{len(rows)} sse rows, {len(ids)} ids, {len(rest)} rest rows")
@@ -1,58 +0,0 @@
"""Serve update manifests: stable.json, beta.json and slow.json (answered after a delay); logs every request."""
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import sys
import time
PORT = int(sys.argv[1])
DELAY = float(sys.argv[2]) if len(sys.argv) > 2 else 4.0
def manifest(name: str, version: str) -> bytes:
builds = [
{
"chipFamily": fam,
"ota": {
"path": f"/{name}.bin",
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"summary": f"{name} channel",
},
}
for fam in ("ESP8266", "ESP32")
]
return json.dumps(
{"name": f"Bench {name}", "version": version, "builds": builds}
).encode()
DOCS = {
"/stable.json": manifest("stable", "1.0.0"),
"/beta.json": manifest("beta", "2.0.0-beta1"),
"/slow.json": manifest("slow", "3.0.0-slow"),
}
class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None: # noqa: N802
body = DOCS.get(self.path)
print(
f"{time.strftime('%H:%M:%S')} {self.client_address[0]} GET {self.path} -> {404 if body is None else 200}",
flush=True,
)
if body is None:
self.send_error(404)
return
if self.path == "/slow.json":
time.sleep(DELAY)
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *args) -> None: # quiet default log
return
ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
@@ -1,129 +0,0 @@
"""Drive the http_request update harness over the API: press buttons, print every update state and save the log.
usage: updrive.py HOST KEY(- for plaintext) LOGFILE OP [OP ...]
OP: list | press:NAME | set:NAME=VALUE | sel:NAME=OPTION | wait:SECONDS
"""
import asyncio
import pathlib
import sys
import time
from aioesphomeapi import (
APIClient,
BinarySensorState,
ButtonInfo,
LogLevel,
NumberInfo,
NumberState,
SelectInfo,
SelectState,
SensorInfo,
SensorState,
TextSensorInfo,
TextSensorState,
UpdateState,
)
HOST, KEY, LOGFILE = sys.argv[1], sys.argv[2], sys.argv[3]
OPS = sys.argv[4:]
T0 = time.time()
def stamp() -> str:
return f"{time.time() - T0:7.3f}"
async def main() -> None:
with pathlib.Path(LOGFILE).open("w", encoding="utf-8") as log:
c = APIClient(HOST, 6053, None, noise_psk=None if KEY == "-" else KEY)
await c.connect(login=True)
info = await c.device_info()
print(
f"device {info.name} esphome {info.esphome_version} compiled {info.compilation_time}"
)
ents, _ = await c.list_entities_services()
by_key = {e.key: e for e in ents}
buttons = {e.name: e for e in ents if isinstance(e, ButtonInfo)}
selects = {e.name: e for e in ents if isinstance(e, SelectInfo)}
def on_state(s) -> None:
e = by_key.get(s.key)
if e is None:
return
if isinstance(s, TextSensorState):
print(
f"{stamp()} TXT {e.name} = {s.state!r}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, UpdateState):
print(
f"{stamp()} UPD {e.name} latest={s.latest_version!r} title={s.title!r} summary={s.release_summary!r} in_progress={s.in_progress}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, BinarySensorState):
print(
f"{stamp()} BIN {e.name} = {s.state}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, NumberState):
print(
f"{stamp()} NUM {e.name} = {s.state:.0f}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, SelectState):
print(
f"{stamp()} SEL {e.name} = {s.state!r}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, SensorState) and isinstance(e, SensorInfo):
print(f"{stamp()} SEN {e.name} = {s.state:.0f}", flush=True)
def on_log(msg) -> None:
log.write(f"{stamp()} {msg.message.decode(errors='replace')}\n")
c.subscribe_states(on_state)
c.subscribe_logs(on_log, log_level=LogLevel.LOG_LEVEL_VERBOSE)
await asyncio.sleep(1.5)
for op in OPS:
if op == "list":
for n in sorted(buttons):
print("button:", n)
for n in sorted(selects):
print("select:", n, selects[n].options)
for e in ents:
if isinstance(e, TextSensorInfo):
print("text_sensor:", e.name)
continue
kind, _, rest = op.partition(":")
if kind == "wait":
await asyncio.sleep(float(rest))
continue
if kind == "press":
if rest not in buttons:
sys.exit(f"no button named {rest!r}; have {sorted(buttons)}")
print(f"{stamp()} >>> press {rest}", flush=True)
log.write(f"{stamp()} >>> press {rest}\n")
c.button_command(buttons[rest].key)
elif kind == "set":
name, _, value = rest.partition("=")
numbers = {e.name: e for e in ents if isinstance(e, NumberInfo)}
if name not in numbers:
sys.exit(f"no number named {name!r}")
print(f"{stamp()} >>> set {name} = {value}", flush=True)
log.write(f"{stamp()} >>> set {name} = {value}\n")
c.number_command(numbers[name].key, float(value))
elif kind == "sel":
name, _, value = rest.partition("=")
if name not in selects:
sys.exit(f"no select named {name!r}; have {sorted(selects)}")
print(f"{stamp()} >>> select {name} = {value}", flush=True)
log.write(f"{stamp()} >>> select {name} = {value}\n")
c.select_command(selects[name].key, value)
else:
sys.exit(f"unknown op {op!r}")
await asyncio.sleep(2.0)
await asyncio.sleep(1)
await c.disconnect()
asyncio.run(main())
@@ -1,123 +0,0 @@
"""Drive an LD24xx harness over the API: press buttons, set selects and numbers, print state updates, save the log.
usage: lddrive.py HOST KEY LOGFILE OP [OP ...] (KEY of - means a plaintext API)
OP: list | press:NAME | sel:NAME=OPTION | set:NAME=VALUE | wait:SECONDS
Steps are 2 s apart: the API log stream drops lines while the hub blocks in send_command_.
"""
import asyncio
from pathlib import Path
import sys
import time
from aioesphomeapi import (
APIClient,
BinarySensorState,
ButtonInfo,
LogLevel,
NumberInfo,
NumberState,
SelectInfo,
SelectState,
SensorInfo,
SensorState,
TextSensorInfo,
TextSensorState,
)
HOST, KEY, LOGFILE = sys.argv[1], sys.argv[2], sys.argv[3]
OPS = sys.argv[4:]
T0 = time.time()
STEP_GAP_S = 2.0
def stamp() -> str:
return f"{time.time() - T0:7.3f}"
async def run(c: APIClient, log) -> None:
info = await c.device_info()
print(
f"device {info.name} esphome {info.esphome_version} compiled {info.compilation_time}"
)
ents, _ = await c.list_entities_services()
by_key = {e.key: e for e in ents}
buttons = {e.name: e for e in ents if isinstance(e, ButtonInfo)}
selects = {e.name: e for e in ents if isinstance(e, SelectInfo)}
numbers = {e.name: e for e in ents if isinstance(e, NumberInfo)}
def on_state(s) -> None:
e = by_key.get(s.key)
if e is None:
return
missing = " (missing)" if getattr(s, "missing_state", False) else ""
if isinstance(s, TextSensorState):
print(f"{stamp()} TXT {e.name} = {s.state!r}{missing}", flush=True)
elif isinstance(s, BinarySensorState):
print(f"{stamp()} BIN {e.name} = {s.state}{missing}", flush=True)
elif isinstance(s, NumberState):
print(f"{stamp()} NUM {e.name} = {s.state:.0f}{missing}", flush=True)
elif isinstance(s, SelectState):
print(f"{stamp()} SEL {e.name} = {s.state!r}{missing}", flush=True)
elif (
isinstance(s, SensorState)
and isinstance(e, SensorInfo)
and "Count" in e.name
):
print(f"{stamp()} CNT {e.name} = {s.state:.0f}", flush=True)
def on_log(msg) -> None:
log.write(f"{stamp()} {msg.message.decode(errors='replace')}\n")
c.subscribe_states(on_state)
c.subscribe_logs(on_log, log_level=LogLevel.LOG_LEVEL_VERBOSE)
await asyncio.sleep(1.5)
for op in OPS:
if op == "list":
for n in sorted(buttons):
print("button:", n)
for n in sorted(selects):
print("select:", n, selects[n].options)
for e in ents:
if isinstance(e, TextSensorInfo):
print("text_sensor:", e.name)
continue
kind, _, rest = op.partition(":")
if kind == "wait":
await asyncio.sleep(float(rest))
continue
if kind == "press":
if rest not in buttons:
sys.exit(f"no button named {rest!r}; have {sorted(buttons)}")
print(f"{stamp()} >>> press {rest}", flush=True)
log.write(f"{stamp()} >>> press {rest}\n")
c.button_command(buttons[rest].key)
elif kind == "set":
name, _, value = rest.partition("=")
if name not in numbers:
sys.exit(f"no number named {name!r}; have {sorted(numbers)}")
print(f"{stamp()} >>> set {name} = {value}", flush=True)
log.write(f"{stamp()} >>> set {name} = {value}\n")
c.number_command(numbers[name].key, float(value))
elif kind == "sel":
name, _, value = rest.partition("=")
if name not in selects:
sys.exit(f"no select named {name!r}; have {sorted(selects)}")
print(f"{stamp()} >>> select {name} = {value}", flush=True)
log.write(f"{stamp()} >>> select {name} = {value}\n")
c.select_command(selects[name].key, value)
else:
sys.exit(f"unknown op {op!r}")
await asyncio.sleep(STEP_GAP_S)
await asyncio.sleep(1)
async def main() -> None:
c = APIClient(HOST, 6053, None, noise_psk=None if KEY == "-" else KEY)
await c.connect(login=True)
with Path(LOGFILE).open("w", encoding="utf-8") as log:
await run(c, log)
await c.disconnect()
asyncio.run(main())
@@ -1,33 +0,0 @@
"""Summarize an lddrive.py log: for each step, the LD2450 TX frames, commands and publishes.
usage: txsum.py LOGFILE (diff the output of two builds)
"""
from pathlib import Path
import re
import sys
ANSI = re.compile(r"\x1b\[[0-9;]*m")
out: list[str] = []
with Path(sys.argv[1]).open(encoding="utf-8", errors="replace") as log:
for raw in log:
line = ANSI.sub("", raw.rstrip("\n"))
m = re.match(r"\s*[\d.]+ (.*)", line)
if not m:
continue
text = m.group(1)
if text.startswith(">>>"):
out.append(f"\n# {text[4:]}")
elif "[uart_debug" in text:
out.append(f" TX {text.split('>>> ')[1]}")
elif "[ld2450" in text and (
"Sending COMMAND" in text
or "zone type" in text.lower()
or "Query zone" in text
):
out.append(f" LOG {text.split(']: ', 1)[1]}")
elif "[select:" in text and ">>" in text:
out.append(f" SEL {text.split(']: ', 1)[1]}")
elif "[text_sensor:098]" in text:
out.append(f" TXT {text.split(']: ', 1)[1]}")
print("\n".join(out))
@@ -1,86 +0,0 @@
"""Drive the modem test build over the API: press buttons, print sensor states, save the device log.
usage: mdrive.py HOST LOGFILE OP [OP ...]
OP: list | press:NAME | wait:SECONDS
"""
import asyncio
import pathlib
import sys
import time
from aioesphomeapi import (
APIClient,
ButtonInfo,
LogLevel,
SensorInfo,
SensorState,
TextSensorInfo,
TextSensorState,
)
HOST, LOGFILE = sys.argv[1], sys.argv[2]
OPS = sys.argv[3:]
T0 = time.time()
def stamp() -> str:
return f"{time.time() - T0:7.3f}"
async def main() -> None:
with pathlib.Path(LOGFILE).open("w", encoding="utf-8") as log:
c = APIClient(HOST, 6053, None)
await c.connect(login=True)
info = await c.device_info()
print(
f"device {info.name} esphome {info.esphome_version} compiled {info.compilation_time}"
)
ents, _ = await c.list_entities_services()
by_key = {e.key: e for e in ents}
buttons = {e.name: e for e in ents if isinstance(e, ButtonInfo)}
def on_state(s) -> None:
e = by_key.get(s.key)
if e is None:
return
if isinstance(s, TextSensorState):
print(
f"{stamp()} TXT {e.name} = {s.state!r}{' (missing)' if s.missing_state else ''}",
flush=True,
)
elif isinstance(s, SensorState) and isinstance(e, SensorInfo):
print(f"{stamp()} SEN {e.name} = {s.state:.1f}", flush=True)
def on_log(msg) -> None:
log.write(f"{stamp()} {msg.message.decode(errors='replace')}\n")
log.flush()
c.subscribe_states(on_state)
c.subscribe_logs(on_log, log_level=LogLevel.LOG_LEVEL_DEBUG)
await asyncio.sleep(1.5)
for op in OPS:
if op == "list":
for n in sorted(buttons):
print("button:", n)
for e in ents:
if isinstance(e, (TextSensorInfo, SensorInfo)):
print("entity:", type(e).__name__, e.name)
continue
kind, _, rest = op.partition(":")
if kind == "wait":
await asyncio.sleep(float(rest))
continue
if kind == "press":
if rest not in buttons:
sys.exit(f"no button named {rest!r}; have {sorted(buttons)}")
print(f"{stamp()} >>> press {rest}", flush=True)
log.write(f"{stamp()} >>> press {rest}\n")
c.button_command(buttons[rest].key)
else:
sys.exit(f"unknown op {op!r}")
await asyncio.sleep(2.0)
await asyncio.sleep(1)
await c.disconnect()
asyncio.run(main())
@@ -24,10 +24,8 @@ if [ "$RESTART" = restart ]; then
pid=$(cat "$WORK/mosquitto.pid" 2>/dev/null); [ -n "$pid" ] || { echo "no mosquitto.pid in $WORK, cannot restart the broker"; exit 1; }
kill "$pid" || { echo "broker PID $pid is not running, nothing to restart"; exit 1; }; sleep 8
nc -z -w 3 "$B" 1883 && { echo "the old broker still answers on 1883 after the kill"; exit 1; }
# exec so the PID is the broker's own and no shell stays behind holding the caller's output pipe open
(cd "$WORK" && exec mosquitto -c mosquitto-ws.conf > mosquitto.out 2>&1 < /dev/null) &
echo $! > "$WORK/mosquitto.pid"; sleep 25
for port in $(awk '/^listener/ {print $2}' "$WORK/mosquitto-ws.conf"); do nc -z -w 3 "$B" "$port" || { echo "broker port $port not listening after the restart"; exit 1; }; done
(cd "$WORK" && mosquitto -c mosquitto-ws.conf > mosquitto.out 2>&1 & echo $! > "$WORK/mosquitto.pid"); sleep 25
for port in 1883 9001 9002; do nc -z -w 3 "$B" "$port" || { echo "broker port $port not listening after the restart"; exit 1; }; done
tail -n +$((n1+1)) "$LOG" | grep -a -q "MQTT_EVENT_CONNECTED\|\[mqtt:[0-9]*\]: Connected" || { echo "the device did not reconnect after the broker restart"; exit 1; }
else
echo "=== $N: no broker restart (pass 'restart' as the 7th argument for a local broker)"
@@ -1,24 +0,0 @@
#!/bin/sh
# cli_tls_check.sh TREE [neg] YAML... : run `esphome clean-mqtt` from the checkout TREE for each config, which goes
# through the same TLS setup in esphome/mqtt.py as `esphome logs` over MQTT, without needing a board. clean-mqtt never
# exits on its own, so it runs under a 25 s timeout: status 124 after "Connected to MQTT broker!" means the CLI
# connected. With neg every config must instead fail with CERTIFICATE_VERIFY_FAILED and never connect, so a negative
# that fails for another reason (bad YAML, broker down) is reported as a failure too. A relative YAML path is taken
# from the working directory, where the cleanmqtt-NAME.log outputs also go. Exit 1 when any config missed its result.
TREE=$1; shift; PY=${PYTHON:-python3}; NEG=0
[ "$1" = neg ] && { NEG=1; shift; }
[ -d "$TREE/esphome" ] && [ -n "$1" ] || { echo "usage: cli_tls_check.sh TREE [neg] YAML..."; exit 2; }
bad=0
for y in "$@"; do
case $y in /*) ;; *) y=$PWD/$y;; esac
n=$(basename "$y" .yaml); log=$PWD/cleanmqtt-$n.log
(cd "$TREE" && PYTHONUNBUFFERED=1 timeout 25 $PY -m esphome clean-mqtt "$y" > "$log" 2>&1); rc=$?
if [ "$NEG" = 0 ] && [ "$rc" = 124 ] && grep -q "Connected to MQTT broker" "$log"; then
echo "$n: connected"
elif [ "$NEG" = 1 ] && grep -q "CERTIFICATE_VERIFY_FAILED" "$log" && ! grep -q "Connected to MQTT broker" "$log"; then
echo "$n: rejected (CERTIFICATE_VERIFY_FAILED)"
else
echo "$n: FAILED (status $rc): $(grep -a -m1 -i 'cannot connect\|error' "$log" | cut -c1-160)"; bad=$((bad+1))
fi
done
[ "$bad" = 0 ]
@@ -1,53 +0,0 @@
#!/bin/sh
# flash_and_drive.sh TREE YAML PORT KEY BROKER_HOST MODE [SUB_PORT] : flash YAML from the checkout TREE over the serial
# PORT, capture the serial log (cap-NAME.txt in the working directory), wait for the first MQTT connect or TLS error,
# then drive the device with cases.sh (KEY as for cases.sh, - for a plaintext API). MODE local runs cases.sh with a
# broker restart (the working directory must hold mosquitto-ws.conf and mosquitto.pid), public runs it without a
# restart, neg only watches for 100 s (a build that must never connect). SUB_PORT is the plain listener cases.sh
# subscribes on (default 1883). Ends with connect, disconnect, handshake error and crash counts, the config dump's
# TLS line, the probe heap lines and the broker listener each connection used. Exit 1 when the capture did not start,
# the device reached no MQTT event within 90 s (local and public), it crashed, or the mode's own check failed.
TREE=$1; YAML=$2; PORT=$3; KEY=$4; B=$5; MODE=$6; SP=${7:-1883}
case $YAML in /*) ;; *) YAML=$PWD/$YAML;; esac
HERE=$(cd "$(dirname "$0")" && pwd); PY=${PYTHON:-python3}; N=$(basename "$YAML" .yaml); CAP=$PWD/cap-$N.txt; ULOG=$PWD/upload-$N.log
[ -d "$TREE/esphome" ] && [ -e "$PORT" ] && [ -n "$KEY" ] && [ -n "$B" ] || { echo "usage: flash_and_drive.sh TREE YAML PORT KEY BROKER_HOST local|public|neg [SUB_PORT]"; exit 2; }
case $MODE in local|public|neg) ;; *) echo "MODE must be local, public or neg"; exit 2;; esac
pkill -f "[s]erial_capture.py $PORT "; sleep 1
echo "=== $N: flash ($(date +%T))"
(cd "$TREE" && PYTHONUNBUFFERED=1 timeout 300 $PY -m esphome upload "$YAML" --device "$PORT" > "$ULOG" 2>&1) || { echo "upload failed"; tail -5 "$ULOG"; exit 1; }
SECS=200; [ "$MODE" = neg ] && SECS=100
# a stale capture from an earlier run must not satisfy the wait below
rm -f "$CAP"
$PY "$HERE/../serial_capture.py" "$PORT" $SECS "$CAP" > "$PWD/cap-$N.stderr" 2>&1 &
cap_pid=$!
i=0; until grep -a -q "MQTT_EVENT_CONNECTED\|\[mqtt:[0-9]*\]: Connected\|mbedtls_ssl_handshake returned\|Failed to verify" "$CAP" 2>/dev/null || [ $i -ge 45 ]; do sleep 2; i=$((i+1)); done
kill -0 $cap_pid 2>/dev/null && [ -f "$CAP" ] || { echo "serial capture is not running on $PORT"; tail -3 "$PWD/cap-$N.stderr"; exit 1; }
DEV=$(grep -a -o 'IP Address: [0-9.]*' "$CAP" | head -1 | awk '{print $3}')
if [ $i -ge 45 ] || [ -z "$DEV" ]; then
echo "=== $N: no MQTT event or no IP within 90 s ($(date +%T))"
[ "$MODE" = neg ] || { pkill -f "[s]erial_capture.py $PORT "; exit 1; }
else
echo "=== $N: first MQTT event after $((i*2)) s, device $DEV ($(date +%T))"
fi
grep -a 'ESPHome version\|\]: Transport\|\]: TLS:' "$CAP" | sed 's/\x1b\[[0-9;]*m//g' | cut -c1-120
rc=0
case $MODE in
local) PYTHON=$PY sh "$HERE/cases.sh" "$N" "$KEY" "$B" "$SP" "$CAP" "$DEV" restart; rc=$?;;
public) PYTHON=$PY sh "$HERE/cases.sh" "$N" "$KEY" "$B" "$SP" "$CAP" "$DEV"; rc=$?;;
neg) sleep 70;;
esac
wait $cap_pid
strip() { sed 's/\x1b\[[0-9;]*m//g'; }
# count the backend's own events so the result does not depend on on_connect/on_disconnect log lines in the config
connects=$(grep -a -c 'MQTT_EVENT_CONNECTED' "$CAP"); errors=$(grep -a -c 'mbedtls_ssl_handshake returned\|Failed to verify certificate' "$CAP")
crashes=$(grep -a -c 'Guru\|abort()\|Backtrace' "$CAP")
echo "=== $N: connects $connects, disconnects $(grep -a -c 'MQTT_EVENT_DISCONNECTED' "$CAP"), handshake errors $errors, crashes $crashes"
grep -a 'mbedtls_ssl_handshake returned\|Failed to verify\|esp-tls' "$CAP" | strip | sed 's/^\[[^]]*\]//' | sort | uniq -c | sort -rn | head -4 | cut -c1-150
grep -a '\[probe:' "$CAP" | strip | sed 's/.*\[probe:[0-9]*\]: //' | sed -n '1p;$p'
[ -f "$PWD/mosquitto.log" ] && [ -n "$DEV" ] && echo "broker listeners used by $DEV: $(grep "New connection from $DEV" "$PWD/mosquitto.log" | sed 's/.*on port //' | sort | uniq -c | tr '\n' ';')"
[ "$crashes" = 0 ] || { echo "device crashed $crashes time(s)"; exit 1; }
if [ "$MODE" = neg ]; then
[ "$connects" = 0 ] && [ "$errors" -gt 0 ] || { echo "negative case: expected no connect and a handshake error"; exit 1; }
else
[ "$rc" = 0 ] || { echo "cases.sh failed with status $rc"; exit 1; }
fi
@@ -1,21 +1,13 @@
#!/bin/sh
# make_test_ca.sh BROKER_IP : a throwaway CA, a server certificate for the broker address and a client certificate
# (30 days), written into the working directory; run it in the scratch directory where a copy of mosquitto-ws.conf
# lives. The device config takes ca.crt as certificate_authority (skip_cert_cn_check: true, the name check does not
# match an address) and client.crt/client.key as client_certificate/client_certificate_key for the listener that
# requires one; other-ca.crt is a second CA for the negative case. The CA carries basicConstraints and keyUsage
# because Python's default SSL context (esphome logs, esphome clean-mqtt) rejects a CA without them.
# make_test_ca.sh BROKER_IP : a throwaway CA and a server certificate for the broker address (30 days), written into
# the working directory; run it in the scratch directory where a copy of mosquitto-ws.conf lives. The device
# config takes ca.crt as certificate_authority (skip_cert_cn_check: true, the name check does not match an address);
# other-ca.crt is a second CA for the negative case.
set -e
IP=$1
[ -n "$IP" ] || { echo "usage: make_test_ca.sh BROKER_IP"; exit 2; }
openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt -days 30 -subj "/CN=hwtest-ca" \
-addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"
openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt -days 30 -subj "/CN=hwtest-ca"
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=hwtest-broker"
printf "subjectAltName=IP:%s\nextendedKeyUsage=serverAuth\n" "$IP" > san.ext
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 30 -extfile san.ext
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=hwtest-client"
printf "extendedKeyUsage=clientAuth\n" > client.ext
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 30 -extfile client.ext
openssl req -x509 -newkey rsa:2048 -nodes -keyout other-ca.key -out other-ca.crt -days 30 -subj "/CN=other-ca" \
-addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"
openssl verify -CAfile ca.crt server.crt client.crt
openssl req -x509 -newkey rsa:2048 -nodes -keyout other-ca.key -out other-ca.crt -days 30 -subj "/CN=other-ca"
openssl verify -CAfile ca.crt server.crt
@@ -1,20 +1,8 @@
# Copy next to the certificates in the scratch directory and start it there with
# `mosquitto -c mosquitto-ws.conf & echo $! > mosquitto.pid` (cases.sh restarts that PID): plain MQTT, MQTT over TLS,
# MQTT over TLS that requires a client certificate, MQTT over WebSocket, and MQTT over WebSocket with TLS
# (server.crt, server.key, ca.crt from make_test_ca.sh in the same directory). log_type all gives the listener port
# per connection.
# `mosquitto -c mosquitto-ws.conf & echo $! > mosquitto.pid` (cases.sh restarts that PID): plain MQTT, MQTT over WebSocket, and MQTT over WebSocket with TLS (server.crt and
# server.key from make_test_ca.sh in the same directory). log_type all gives the listener port per connection.
listener 1883
protocol mqtt
listener 8883
protocol mqtt
certfile server.crt
keyfile server.key
listener 8884
protocol mqtt
certfile server.crt
keyfile server.key
cafile ca.crt
require_certificate true
listener 9001
protocol websockets
listener 9002
@@ -1,40 +0,0 @@
"""Compare the listener log (JSON lines from udp_listen.py) against the udp-write-dut.yaml payload sequence.
usage: check.py LISTEN_JSONL ECHO_HEX (ECHO_HEX is the datagram sent to the device for the echo case)
"""
import json
import pathlib
import sys
with pathlib.Path(sys.argv[1]).open(encoding="utf-8") as f:
log = [json.loads(line) for line in f if line.strip()]
echo_in = bytes.fromhex(sys.argv[2])
small = bytes([0xAA, 0x55, 0x01, 0x02])
expected = [
("small 1/2", small),
("small 2/2", small),
("simple", bytes([1, 2, 3])),
("text", b"hello udp"),
("empty", b""),
("stack128", bytes((0x10 + i) & 0xFF for i in range(128))),
("heap129", bytes((0x20 + i) & 0xFF for i in range(129))),
("heap600", bytes((0x30 + i) & 0xFF for i in range(600))),
("lambda300", bytes((i * 7) & 0xFF for i in range(300))),
("echo", echo_in[::-1]),
]
got = [bytes.fromhex(e["hex"]) for e in log]
ok = 0
for i, (name, want) in enumerate(expected):
have = got[i] if i < len(got) else None
res = (
"OK"
if have == want
else f"MISMATCH got {None if have is None else have[:16].hex() + '... len ' + str(len(have))}"
)
ok += res == "OK"
print(f"{name:10s} {len(want):4d} B {res}")
print(
f"{ok}/{len(expected)} payloads byte-exact, {len(got)} datagrams received, sources {sorted({e['src'] for e in log})}"
)
sys.exit(0 if ok == len(expected) and len(got) == len(expected) else 1)
@@ -1,32 +0,0 @@
"""Log every datagram on PORT for SECONDS as one JSON line: time, source, length, hex payload."""
import json
import pathlib
import socket
import sys
import time
port, secs, out = int(sys.argv[1]), float(sys.argv[2]), sys.argv[3]
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", port))
s.settimeout(1.0)
end = time.monotonic() + secs
with pathlib.Path(out).open("w", encoding="utf-8") as f:
while time.monotonic() < end:
try:
data, (ip, sport) = s.recvfrom(65535)
except TimeoutError:
continue
f.write(
json.dumps(
{
"t": time.strftime("%H:%M:%S"),
"src": ip,
"len": len(data),
"hex": data.hex(),
}
)
+ "\n"
)
f.flush()
@@ -1,22 +0,0 @@
#!/bin/sh
# write_cases.sh DEVICE_IP KEY [PORT] : drive a device built from templates/udp-write-dut.yaml. Records every datagram
# the device sends to PORT (the udp component's port, default 18511) with udp_listen.py in the working directory,
# presses the eight payload buttons over the API two seconds apart (KEY as for api_button.py, - for a plaintext API),
# sends one 16 byte unicast datagram to the device for the echo case, then compares the recorded datagrams with
# check.py. The recorder runs until the driver stops it. Exit 1 when the recorder did not start or a payload is
# missing or differs; listen.jsonl stays in the working directory.
HERE=$(cd "$(dirname "$0")" && pwd); PY=${PYTHON:-python3}; DEV=$1; KEY=$2; PORT=${3:-18511}
[ -n "$DEV" ] && [ -n "$KEY" ] || { echo "usage: write_cases.sh DEVICE_IP KEY [PORT]"; exit 2; }
ECHO=0102030405060708090a0b0c0d0e0f10
rm -f "$PWD/listen.jsonl"
$PY "$HERE/udp_listen.py" "$PORT" 600 "$PWD/listen.jsonl" &
lst=$!
sleep 2
kill -0 $lst 2>/dev/null || { echo "udp_listen.py is not running on port $PORT"; exit 1; }
for b in Small Simple Text Empty Stack128 Heap129 Heap600 Lambda300; do
$PY "$HERE/../api_button.py" "$DEV" "$KEY" press "$b" > /dev/null || echo "press $b failed"; sleep 2
done
$PY -c "import socket; socket.socket(socket.AF_INET, socket.SOCK_DGRAM).sendto(bytes.fromhex('$ECHO'), ('$DEV', $PORT))"
sleep 5
kill $lst 2>/dev/null; wait $lst 2>/dev/null
$PY "$HERE/check.py" "$PWD/listen.jsonl" "$ECHO"
@@ -1,65 +0,0 @@
esphome:
name: ${name}
esp8266:
board: d1_mini
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
logger:
level: DEBUG
api:
reboot_timeout: 0s
actions:
- action: play_buzzer
description: Play a song or build one from parts
variables:
song_str:
type: string
description: Full RTTTL string
example: "scale:d=4,o=5,b=100:c,d,e"
required: false
notes:
type: string
required: false
octave:
type: int
default: 5
bpm:
type: int
default: 100
tempo:
type: float
default: 1.5
loud:
type: bool
default: true
label:
type: string
default: ""
then:
- lambda: |-
char s[40], n[40], l[40];
snprintf(s, sizeof(s), "%.*s", (int) song_str.size(), song_str.c_str());
snprintf(n, sizeof(n), "%.*s", (int) notes.size(), notes.c_str());
snprintf(l, sizeof(l), "%.*s", (int) label.size(), label.c_str());
ESP_LOGD("main", "play_buzzer song=%s(%d) notes=%s(%d) octave=%d bpm=%d tempo=%.1f loud=%d label=%s(%d)", s,
(int) song_str.size(), n, (int) notes.size(), octave, bpm, tempo, loud, l, (int) label.size());
- action: plain_action
variables:
value: int
then:
- logger.log:
format: "plain_action value=%d"
args: ["value"]
- action: list_action
variables:
values: int[]
then:
- logger.log:
format: "list_action n=%d"
args: ["values.size()"]
@@ -1,28 +0,0 @@
esphome:
name: ${name}
esp8266:
board: d1_mini
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
logger:
level: DEBUG
api:
reboot_timeout: 0s
actions:
- action: str_action
variables:
song_str: string
notes: string
octave: int
then:
- lambda: |-
char s[40], n[40];
snprintf(s, sizeof(s), "%.*s", (int) song_str.size(), song_str.c_str());
snprintf(n, sizeof(n), "%.*s", (int) notes.size(), notes.c_str());
ESP_LOGD("main", "str_action song=%s(%d) notes=%s(%d) octave=%d", s, (int) song_str.size(), n, (int) notes.size(), octave);
@@ -1,149 +0,0 @@
substitutions:
peri_mac: "${PERI_MAC}"
esphome:
name: midicli
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
level: VERBOSE
logs:
esp32_ble_tracker: DEBUG
ble_client: DEBUG
esp32_ble: DEBUG
api: DEBUG
scheduler: DEBUG
sensor: DEBUG
wifi: DEBUG
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
api:
reboot_timeout: 0s
esp32_ble_tracker:
ble_client:
- mac_address: ${peri_mac}
id: midi_client
ble_midi:
id: test_midi
ble_client_id: midi_client
max_sysex_size: 64
pair: false
on_connect:
- logger.log: "MIDI device connected"
- ble_midi.send_program_change:
id: test_midi
program: 0
on_disconnect:
- logger.log: "MIDI device disconnected"
on_note_on:
- logger.log:
format: "Note on %u velocity %u channel %u"
args: [note, velocity, channel]
on_note_off:
- logger.log:
format: "Note off %u channel %u"
args: [note, channel]
on_control_change:
- logger.log:
format: "CC %u = %u on channel %u"
args: [control_number, value, channel]
on_program_change:
- logger.log:
format: "Program %u on channel %u"
args: [program, channel]
on_pitch_bend:
- logger.log:
format: "Pitch bend %d on channel %u"
args: [value, channel]
on_sysex:
- lambda: |-
ESP_LOGI("midi", "SysEx of %u bytes: %s", (unsigned) data.size(), format_hex_pretty(data.data(), data.size()).c_str());
on_message:
- lambda: |-
ESP_LOGI("midi", "Raw message of %u bytes: %s", (unsigned) data.size(), format_hex_pretty(data.data(), data.size()).c_str());
sensor:
- platform: debug
free:
name: Heap Free
loop_time:
name: Loop Time
debug:
update_interval: 30s
button:
- platform: template
name: "MIDI send tests"
on_press:
- ble_midi.send_note_on:
id: test_midi
note: 60
- ble_midi.send_note_on:
id: test_midi
note: !lambda "return 60;"
velocity: !lambda "return 100;"
channel: !lambda "return 1;"
- ble_midi.send_note_off:
id: test_midi
note: 60
velocity: 0
channel: 1
- ble_midi.send_control_change:
id: test_midi
control_number: 7
value: 64
- ble_midi.send_pitch_bend:
id: test_midi
value: -8192
- ble_midi.send_sysex:
id: test_midi
payload: [0x7E, 0x7F, 0x06, 0x01]
- ble_midi.send_raw:
id: test_midi
data: !lambda "return std::vector<uint8_t>{0x90, 0x3C, 0x40};"
- if:
condition:
ble_midi.connected:
id: test_midi
then:
- logger.log: "Still connected"
else:
- logger.log: "Not connected"
- platform: template
name: "MIDI send out of range"
on_press:
- ble_midi.send_note_on:
id: test_midi
note: !lambda "return 200;"
- ble_midi.send_control_change:
id: test_midi
control_number: 1
value: 1
channel: !lambda "return 16;"
- ble_midi.send_pitch_bend:
id: test_midi
value: 8191
- ble_midi.send_sysex:
id: test_midi
payload: [0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7]
- ble_midi.send_raw:
id: test_midi
data: !lambda "return std::vector<uint8_t>(140, 0x00);"
- platform: template
name: "MIDI send long sysex"
on_press:
- ble_midi.send_sysex:
id: test_midi
payload: !lambda "return std::vector<uint8_t>(100, 0x11);"
@@ -1,177 +0,0 @@
esphome:
name: midiperi
esp32:
board: esp32-s3-devkitc-1
variant: esp32s3
flash_size: 16MB
framework:
type: esp-idf
logger:
hardware_uart: USB_SERIAL_JTAG
level: DEBUG
api:
reboot_timeout: 0s
ota:
- platform: esphome
ethernet:
type: W5500
clk_pin: GPIO13
mosi_pin: GPIO11
miso_pin: GPIO12
cs_pin: GPIO14
interrupt_pin: GPIO10
reset_pin: GPIO9
esp32_ble_server:
id: ble_server
manufacturer: FakeMIDI
on_connect:
- lambda: ESP_LOGI("peri", "central connected %d", id);
on_disconnect:
- lambda: ESP_LOGI("peri", "central disconnected %d", id);
services:
- uuid: 03B80E5A-EDE8-4B33-A751-6CE34EC4C700
advertise: true
characteristics:
- id: midi_io
uuid: 7772E5DB-3868-4112-A1A9-F2669D106BF3
read: true
write: true
write_no_response: true
notify: true
on_write:
- lambda: |-
ESP_LOGI("peri", "RX %u bytes: %s", (unsigned) x.size(), format_hex_pretty(x.data(), x.size()).c_str());
script:
- id: emit
parameters:
pkt: std::vector<uint8_t>
then:
- lambda: |-
ESP_LOGI("peri", "TX %u bytes: %s", (unsigned) pkt.size(), format_hex_pretty(pkt.data(), pkt.size()).c_str());
id(midi_io).set_value(std::vector<uint8_t>(pkt));
id(midi_io).notify();
button:
- platform: restart
name: P Restart
- platform: template
name: P Note On
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x90, 0x3C, 0x64};'
- platform: template
name: P Note Off
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x80, 0x3C, 0x00};'
- platform: template
name: P Control Change
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xB0, 0x07, 0x40};'
- platform: template
name: P Program Change
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xC5, 0x05};'
- platform: template
name: P Pitch Bend
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xE0, 0x7F, 0x7F};'
- platform: template
name: P Two Notes One Packet
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x90, 0x3C, 0x64, 0x81, 0x91, 0x3E, 0x50};'
- platform: template
name: P Running Status
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x90, 0x40, 0x64, 0x41, 0x64};'
- platform: template
name: P Sysex One Packet
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xF0, 0x7E, 0x7F, 0x06, 0x01, 0x80, 0xF7};'
- platform: template
name: P Sysex Split
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xF0, 0x41, 0x10, 0x42};'
- delay: 200ms
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x12, 0x00, 0x40, 0x80, 0xF7};'
- platform: template
name: P Sysex Oversize
on_press:
- script.execute:
id: emit
pkt: !lambda 'std::vector<uint8_t> v{0x80, 0x80, 0xF0}; for (int i = 0; i < 17; i++) v.push_back(i); return v;'
- delay: 200ms
- script.execute:
id: emit
pkt: !lambda 'std::vector<uint8_t> v{0x80}; for (int i = 0; i < 19; i++) v.push_back(0x20 + i); return v;'
- delay: 200ms
- script.execute:
id: emit
pkt: !lambda 'std::vector<uint8_t> v{0x80}; for (int i = 0; i < 19; i++) v.push_back(0x40 + i); return v;'
- delay: 200ms
- script.execute:
id: emit
pkt: !lambda 'std::vector<uint8_t> v{0x80}; for (int i = 0; i < 19; i++) v.push_back(0x60 + i); return v;'
- delay: 200ms
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xF7};'
- platform: template
name: P Sysex Cut Short
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xF0, 0x41, 0x10, 0x80, 0x90, 0x3C, 0x64};'
- platform: template
name: P Clock
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xF8};'
- platform: template
name: P Aftertouch
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0xD3, 0x45};'
- platform: template
name: P Orphan Data Byte
on_press:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x3C, 0x64};'
- platform: template
name: P Burst
on_press:
- repeat:
count: 50
then:
- script.execute:
id: emit
pkt: !lambda 'return {0x80, 0x80, 0x90, 0x30, 0x40, 0x80, 0x80, 0x30, 0x00};'
- delay: 20ms
@@ -1,148 +0,0 @@
# Entity icon and device class readback (esphome#21111): the per-run file supplies name and broker_ip through substitutions and includes this file as a package. Every readback path is covered: API listing, MQTT discovery, web_server JSON and dump_config. See reference/recipes/web_server.md.
esp8266:
board: d1_mini
logger:
level: DEBUG
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
ota:
- platform: esphome
web_server:
port: 80
mqtt:
broker: ${broker_ip}
reboot_timeout: 0s
interval:
- interval: 10s
then:
- lambda: |-
ESP_LOGI("probe", "free heap %u, max block %u", (unsigned) ESP.getFreeHeap(), (unsigned) ESP.getMaxFreeBlockSize());
esphome:
name: ${name}
sensor:
- platform: template
name: Temp
id: temp
device_class: temperature
unit_of_measurement: "°C"
icon: mdi:thermometer
accuracy_decimals: 1
lambda: return 21.5;
update_interval: 30s
- platform: template
name: Long Icon
id: long_icon
icon: mdi:this-icon-name-is-exactly-sixty-three-characters-long-abcde
device_class: humidity
unit_of_measurement: "%"
lambda: return 40.0;
update_interval: 30s
- platform: template
name: Plain
id: plain
lambda: return 1.0;
update_interval: 30s
- platform: template
name: Hidden
id: hidden
internal: true
icon: mdi:eye-off
device_class: power
lambda: return 2.0;
update_interval: 30s
binary_sensor:
- platform: template
name: Motion
id: motion
device_class: motion
icon: mdi:motion-sensor
lambda: return false;
switch:
- platform: template
name: Relay
id: relay
device_class: outlet
icon: mdi:power-plug
optimistic: true
button:
- platform: template
name: Reboot
id: reboot_btn
device_class: restart
icon: mdi:restart
on_press:
- logger.log: "Reboot pressed"
text_sensor:
- platform: template
name: Version
id: version
device_class: timestamp
icon: mdi:tag
lambda: return {"2026-10-21T00:00:00+00:00"};
update_interval: 30s
number:
- platform: template
name: Level
id: level
device_class: battery
icon: mdi:knob
unit_of_measurement: "%"
min_value: 0
max_value: 100
step: 1
optimistic: true
initial_value: 50
select:
- platform: template
name: Mode
id: mode
icon: mdi:format-list-bulleted
options: [a, b, c]
optimistic: true
initial_option: a
event:
- platform: template
name: Doorbell
id: doorbell
device_class: doorbell
icon: mdi:doorbell
event_types: [ring, knock]
cover:
- platform: template
name: Garage
id: garage
device_class: garage
icon: mdi:garage
optimistic: true
text:
- platform: template
name: Secret Note
id: note
mode: password
icon: mdi:form-textbox-password
optimistic: true
initial_value: "x"
@@ -1,10 +0,0 @@
esphome:
name: upd32
esp32:
board: esp32dev
framework:
type: esp-idf
packages:
common: !include ../upd-common.yaml
@@ -1,8 +0,0 @@
esphome:
name: upd8266
esp8266:
board: d1_mini
packages:
common: !include ../upd-common.yaml
@@ -1,52 +0,0 @@
substitutions:
base: !secret manifest_base
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
logger:
level: DEBUG
api:
reboot_timeout: 0s
http_request:
verify_ssl: false
timeout: 10s
ota:
- platform: http_request
id: ota_http
update:
- platform: http_request
id: upd
name: Firmware
source: ${base}/stable.json
update_interval: 1h
button:
- platform: template
name: Check
on_press:
- lambda: id(upd).check();
- platform: template
name: URL Slow Cstr
on_press:
- lambda: |-
std::string url = "${base}/slow.json";
id(upd).set_source_url(url.c_str());
- platform: template
name: URL Stable Literal
on_press:
- lambda: id(upd).set_source_url("${base}/stable.json");
- platform: template
name: Heap
on_press:
- lambda: |-
#ifdef USE_ESP8266
ESP_LOGI("harness", "heap free %u", ESP.getFreeHeap());
#else
ESP_LOGI("harness", "heap free %u", (unsigned) heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
#endif
@@ -1,95 +0,0 @@
substitutions:
base: !secret manifest_base
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
logger:
level: DEBUG
on_message:
- level: DEBUG
then:
- lambda: |-
// Inline check path: replace the URL from inside the check (ESP8266 runs the check on the main loop)
if (id(swap_in_check) && strstr(message, "Content-Length") != nullptr) {
id(swap_in_check) = false;
std::string url = "${base}/beta.json";
id(upd).set_source_url(url);
ESP_LOGI("harness", "URL replaced from inside the check");
}
api:
reboot_timeout: 0s
http_request:
verify_ssl: false
timeout: 10s
ota:
- platform: http_request
id: ota_http
update:
- platform: http_request
id: upd
name: Firmware
source: ${base}/stable.json
update_interval: 1h
globals:
- id: swap_in_check
type: bool
initial_value: "false"
button:
- platform: template
name: Check
on_press:
- lambda: id(upd).check();
- platform: template
name: URL Beta String
on_press:
- lambda: |-
std::string url = "${base}/beta.json";
id(upd).set_source_url(url);
- platform: template
name: URL Slow Cstr
on_press:
- lambda: |-
std::string url = "${base}/slow.json";
id(upd).set_source_url(url.c_str());
- platform: template
name: URL Stable Literal
on_press:
- lambda: id(upd).set_source_url("${base}/stable.json");
- platform: template
name: URL Temporary
on_press:
- lambda: id(upd).set_source_url(std::string("${base}/beta.json"));
- platform: template
name: Same URL Back
on_press:
- lambda: id(upd).set_source_url(id(upd).get_source_url());
- platform: template
name: Check Then Swap
on_press:
- lambda: |-
// ESP32: the check task reads slow.json while the URL is replaced under it
id(upd).check();
std::string url = "${base}/beta.json";
id(upd).set_source_url(url);
- platform: template
name: Arm Swap In Check
on_press:
- lambda: id(swap_in_check) = true;
- platform: template
name: Heap
on_press:
- lambda: |-
#ifdef USE_ESP8266
ESP_LOGI("harness", "heap free %u", ESP.getFreeHeap());
#else
ESP_LOGI("harness", "heap free %u", (unsigned) heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
#endif
@@ -1,57 +0,0 @@
substitutions:
name: ld2412sel
esphome:
name: ${name}
esp32:
variant: esp32s3
board: esp32-s3-devkitc-1
flash_size: 8MB
framework:
type: esp-idf
logger:
level: VERBOSE
api:
reboot_timeout: 0s
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
uart:
- id: uart_ld2412
tx_pin: GPIO43
rx_pin: GPIO44
baud_rate: 115200
debug:
direction: BOTH
dummy_receiver: false
after:
timeout: 10ms
bytes: 120
ld2412:
id: ld2412_radar
uart_id: uart_ld2412
select:
- platform: ld2412
ld2412_id: ld2412_radar
out_pin_level:
name: "LD2412 Hardware output pin level"
binary_sensor:
- platform: gpio
name: "LD2412 Out Pin"
pin: GPIO38
- platform: ld2412
ld2412_id: ld2412_radar
has_target:
name: "LD2412 Presence"
@@ -1,258 +0,0 @@
esphome:
name: ${name}
on_boot:
priority: -100
then:
- lambda: |-
// UART0 TX looped back into its own RX so injected frames reach the hub
USC0(0) |= (1 << UCLBE);
ESP_LOGI("harness", "UART0 loopback on");
esp8266:
board: d1_mini
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
reboot_timeout: 0s
logger:
baud_rate: 0
level: VERBOSE
uart:
id: radar_uart
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: ${baud}
debug:
direction: TX
dummy_receiver: false
after:
delimiter: [0x04, 0x03, 0x02, 0x01]
ld2450:
id: radar
uart_id: radar_uart
select:
- platform: ld2450
baud_rate:
name: Baud Rate
zone_type:
name: Zone Type
text_sensor:
- platform: ld2450
version:
name: Radar Version
target_1:
direction:
name: T1 Direction
target_2:
direction:
name: T2 Direction
target_3:
direction:
name: T3 Direction
sensor:
- platform: ld2450
target_count:
name: Target Count
button:
# periodic frames: header, 3 x (x, y, speed, resolution), footer
- platform: template
name: Frame Approach
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x14, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
- platform: template
name: Frame Away
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x14, 0x80, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
- platform: template
name: Frame Stationary
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x00, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
- platform: template
name: Frame None
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
- platform: template
name: Frame T2 T3
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF4, 0x81, 0x20, 0x83, 0x1E, 0x00, 0x64, 0x00,
0x2C, 0x01, 0xB0, 0x84, 0x0A, 0x80, 0x64, 0x00,
0x55, 0xCC]
# query zone acks: header, length, command C1, status 01, 00 00, zone type, 00, 24 coordinate bytes, footer
- platform: template
name: Zone Ack Disabled
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
- platform: template
name: Zone Ack Detection
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
- platform: template
name: Zone Ack Filter
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
# name based setters, the path lambdas use
- platform: template
name: Set Zone Filter By Name
on_press:
- lambda: id(radar).set_zone_type("Filter");
- platform: template
name: Set Zone Bogus By Name
on_press:
- lambda: id(radar).set_zone_type("Bogus");
- platform: template
name: Set Baud 57600 By Name
on_press:
- lambda: id(radar).set_baud_rate("57600");
- platform: template
name: Set Baud Bogus By Name
on_press:
- lambda: id(radar).set_baud_rate("Bogus");
# target 1 at x 772, y 258: bytes 04 03 02 01 are the command footer
- platform: template
name: Frame CmdFooter In Payload
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
# zone ack, type Detection, zone 1 x1 = 0xCC55: bytes 55 CC are the periodic footer
- platform: template
name: Zone Ack DataFooter In Payload
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x01, 0x00,
0x55, 0xCC, 0xC8, 0x00, 0xDC, 0x05, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
# zone ack, type Filter, zone 1 x1 = 0x0304 and y1 = 0x0102: bytes 04 03 02 01 are the command footer
- platform: template
name: Zone Ack CmdFooter In Payload
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x02, 0x00,
0x04, 0x03, 0x02, 0x01, 0xDC, 0x05, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
# a periodic frame cut after 12 bytes, then a complete zone ack (type Disabled, zeros)
- platform: template
name: Cut Frame Then Zone Ack
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xE8, 0x83, 0x14, 0x00, 0x64, 0x00,
0xFD, 0xFC, 0xFB, 0xFA, 0x1E, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x03, 0x02, 0x01]
# a periodic frame missing its last byte, then a complete one (target 1 moving away)
- platform: template
name: Cut Frame Then Frame
on_press:
- uart.write:
id: radar_uart
data: [0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x14, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55,
0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x14, 0x80, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
# a command header claiming a 255 byte payload, then a complete periodic frame (target 1 stationary)
- platform: template
name: Bogus Length Then Frame
on_press:
- uart.write:
id: radar_uart
data: [0xFD, 0xFC, 0xFB, 0xFA, 0xFF, 0x00,
0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x00, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
# five garbage bytes, then a complete periodic frame (target 1 approaching)
- platform: template
name: Garbage Then Frame
on_press:
- uart.write:
id: radar_uart
data: [0x01, 0x02, 0x03, 0x42, 0x99,
0xAA, 0xFF, 0x03, 0x00,
0x00, 0x00, 0xE8, 0x83, 0x14, 0x00, 0x64, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xCC]
number:
- platform: ld2450
presence_timeout:
name: Presence Timeout
zone_1:
x1:
name: Z1 X1
y1:
name: Z1 Y1
x2:
name: Z1 X2
y2:
name: Z1 Y2
@@ -1,65 +0,0 @@
esp32:
board: esp32dev
framework:
type: esp-idf
watchdog_timeout: 30s
logger:
level: DEBUG
api:
reboot_timeout: 0s
modem:
id: atmodem
rx_pin: GPIO26
tx_pin: GPIO27
baud_rate: 115200
model: SIM7600
apn: internet
pin_code: "0000"
init_at:
- ATI
urc:
name: Modem URC
reboot_timeout: ${modem_reboot_timeout}
on_connect:
logger.log: "modem connected"
on_disconnect:
logger.log: "modem disconnected"
on_sync:
logger.log: "modem synced"
sensor:
- platform: uptime
name: Uptime
update_interval: 30s
- platform: debug
free:
name: Heap Free
loop_time:
name: Loop Time
debug:
update_interval: 30s
button:
- platform: template
name: Modem Enable
on_press:
- modem.enable
- platform: template
name: Modem Disable
on_press:
- modem.disable
- platform: template
name: Modem Send AT
on_press:
- modem.send_at: "AT+CGSN"
interval:
- interval: 20s
then:
- lambda: |-
ESP_LOGI("alive", "uptime %u s enabled=%d connected=%d", (unsigned) (millis() / 1000),
id(atmodem).is_enabled(), id(atmodem).is_connected());
@@ -1,26 +0,0 @@
substitutions:
modem_reboot_timeout: 2min
esphome:
name: modemo
on_boot:
priority: -100
then:
- modem.disable
- modem.enable
- if:
condition:
modem.enabled:
then:
modem.send_at: "AT+CGSN"
else:
modem.enable:
- if:
condition:
not:
modem.connected:
then:
logger.log: modem not connected at boot
packages:
common: !include modem-common.yaml
@@ -1,12 +0,0 @@
substitutions:
modem_reboot_timeout: 0s
esphome:
name: modemw
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
packages:
common: !include modem-common.yaml
@@ -1,99 +0,0 @@
# MQTT TLS trust-source test (#14318, needs that PR's tree for tls and verify_ssl): one build per trust source, with
# the name and the mqtt block edited per file (see reference/recipes/mqtt.md). Secrets: broker_ip (the local broker
# from make_test_ca.sh), mqtt_ca, mqtt_client_cert and mqtt_client_key as block scalars from the same run.
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
level: VERBOSE
logs:
wifi: DEBUG
api: DEBUG
api.connection: DEBUG
scheduler: DEBUG
component: DEBUG
sensor: DEBUG
esp32: DEBUG
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
ota:
- platform: esphome
interval:
- interval: 10s
then:
- lambda: |-
ESP_LOGI("probe", "mqtt connected %d, internal free %u, largest block %u", id(mq).is_connected(),
(unsigned) heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
(unsigned) heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
sensor:
- platform: template
name: Counter
update_interval: 2s
accuracy_decimals: 0
lambda: |-
static uint32_t n = 0;
return n++;
switch:
- platform: template
name: Relay
id: relay
optimistic: true
on_turn_on:
- logger.log: "Relay ON"
on_turn_off:
- logger.log: "Relay OFF"
button:
- platform: template
name: Publish big
on_press:
- mqtt.publish:
topic: ${name}/big
payload: !lambda |-
return std::string(3000, 'q');
substitutions:
name: mqtt-dut
esphome:
name: ${name}
mqtt:
id: mq
broker: !secret broker_ip # test.mosquitto.org for the bundle run
# No port: it must resolve to 1883 without TLS options and to 8883 with them. Spell it out only for the
# client-certificate listener (8884), wss (9002) and the public bundle broker (8886).
# The next two lines are the own-CA build. Remove both for the broker-only build (skip_cert_cn_check alone turns
# TLS on), replace both with verify_ssl: false for the unverified build (the flag is rejected next to it), or with
# tls: true for the bundle builds (public broker, and the local one as the negative).
skip_cert_cn_check: true # with the local CA: the certificate names an address
certificate_authority: !secret mqtt_ca
# client_certificate: !secret mqtt_client_cert # with the CA, port 8884
# client_certificate_key: !secret mqtt_client_key
# transport: wss # with the CA, port 9002
reboot_timeout: 0s
discovery: false
on_message:
- topic: ${name}/cmd
then:
- logger.log:
format: "cmd payload: %s"
args: ["x.c_str()"]
on_connect:
- logger.log: "MQTT on_connect"
on_disconnect:
- logger.log: "MQTT on_disconnect"
@@ -1,15 +0,0 @@
esphome:
name: sy8266
esp8266:
board: d1_mini
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
packages:
common: !include ../sy-common.yaml
store_yaml:
@@ -1,16 +0,0 @@
esphome:
name: sy8266s
esp8266:
board: d1_mini
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
packages:
common: !include ../sy-common.yaml
store_yaml:
include_secrets: true
@@ -1,24 +0,0 @@
# shared package for the store_yaml hardware test
substitutions:
greeting: "hello from the package"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
logger:
level: DEBUG
ota:
- platform: esphome
password: recoverme123
sensor:
- platform: template
name: Package Sensor
lambda: return 42.0;
update_interval: 60s
packages:
extra: !include sy-extra.yaml
@@ -1,6 +0,0 @@
# second level include
text_sensor:
- platform: template
name: Greeting
lambda: return std::string("${greeting}");
update_interval: 60s
@@ -1,93 +0,0 @@
# udp.write payload shapes (#21263): every shape the action takes behind a template button, plus an on_receive that
# replies with the received bytes reversed through a lambda. Per run file: substitutions name and listener_ip (the test
# host that runs scripts/udp/udp_listen.py), then packages: common: !include this file. Drive it with
# scripts/udp/write_cases.sh DEVICE_IP KEY, which presses the buttons, sends the echo datagram and checks every payload.
esp8266:
board: d1_mini
logger:
level: VERBOSE
logs:
wifi: DEBUG
api: DEBUG
api.connection: DEBUG
scheduler: DEBUG
component: DEBUG
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
ota:
- platform: esphome
interval:
- interval: 10s
then:
- lambda: |-
ESP_LOGI("probe", "free heap %u, max block %u", (unsigned) ESP.getFreeHeap(), (unsigned) ESP.getMaxFreeBlockSize());
esphome:
name: ${name}
udp:
id: u
addresses:
- ${listener_ip}
on_receive:
- lambda: ESP_LOGI("echo", "received %u bytes", (unsigned) data.size());
- udp.write: !lambda |-
std::vector<uint8_t> v(data.rbegin(), data.rend());
return v;
button:
- platform: template
name: Small
on_press:
- logger.log: "case small"
- udp.write: [0xAA, 0x55, 0x01, 0x02]
- udp.write: [0xAA, 0x55, 0x01, 0x02]
- platform: template
name: Simple
on_press:
- logger.log: "case simple"
- udp.write: [1, 2, 3]
- platform: template
name: Text
on_press:
- logger.log: "case text"
- udp.write: "hello udp"
- platform: template
name: Empty
on_press:
- logger.log: "case empty"
- udp.write: []
- platform: template
name: Stack128
on_press:
- logger.log: "case stack128"
- udp.write: [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F]
- platform: template
name: Heap129
on_press:
- logger.log: "case heap129"
- udp.write: [0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0]
- platform: template
name: Heap600
on_press:
- logger.log: "case heap600"
- udp.write: [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87]
- platform: template
name: Lambda300
on_press:
- logger.log: "case lambda300"
- udp.write: !lambda |-
std::vector<uint8_t> v(300);
for (size_t i = 0; i < v.size(); i++) v[i] = (uint8_t) (i * 7);
return v;
@@ -1,57 +0,0 @@
substitutions:
name: wispcli
ap_ssid: wisp-bridge
esphome:
name: ${name}
esp8266:
board: d1_mini
logger:
level: DEBUG
wifi:
ssid: ${ap_ssid}
password: !secret wisp_psk
power_save_mode: none
reboot_timeout: 0s
http_request:
esp8266_disable_ssl_support: true
timeout: 8s
time:
- platform: sntp
id: sntp_time
on_time_sync:
- logger.log:
format: "SNTP synced"
level: INFO
interval:
- interval: 15s
then:
- http_request.get:
url: !secret lan_http
capture_response: true
on_response:
- logger.log:
format: "LAN GET status %d body %s"
args: ["response->status_code", "body.c_str()"]
level: INFO
on_error:
- logger.log:
format: "LAN GET failed"
level: WARN
- http_request.get:
url: http://example.com/
on_response:
- logger.log:
format: "WAN GET status %d"
args: ["response->status_code"]
level: INFO
on_error:
- logger.log:
format: "WAN GET failed"
level: WARN
@@ -1,40 +0,0 @@
substitutions:
name: wispeth
ap_ssid: wisp-bridge
esphome:
name: ${name}
esp32:
board: esp32-s3-devkitc-1
variant: esp32s3
flash_size: 16MB
framework:
type: esp-idf
logger:
hardware_uart: USB_SERIAL_JTAG
level: VERBOSE
api:
encryption:
key: !secret api_key
reboot_timeout: 0s
ota:
- platform: esphome
ethernet:
type: W5500
clk_pin: GPIO13
mosi_pin: GPIO11
miso_pin: GPIO12
cs_pin: GPIO14
interrupt_pin: GPIO10
reset_pin: GPIO9
wifi:
ap:
ssid: ${ap_ssid}
password: !secret wisp_psk
ap_mode: WISP
-8
View File
@@ -12,7 +12,6 @@ Checks: >-
-bugprone-multi-level-implicit-pointer-conversion,
-bugprone-narrowing-conversions,
-bugprone-tagged-union-member-count,
-bugprone-signed-bitwise,
-bugprone-signed-char-misuse,
-bugprone-switch-missing-default-case,
-cert-dcl50-cpp,
@@ -40,7 +39,6 @@ Checks: >-
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-explicit-constructor,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-macro-usage,
@@ -86,7 +84,6 @@ Checks: >-
-llvm-use-ranges,
-llvmlibc-*,
-misc-const-correctness,
-misc-explicit-constructor,
-misc-include-cleaner,
-misc-multiple-inheritance,
-misc-no-recursion,
@@ -116,7 +113,6 @@ Checks: >-
-objc-*,
-performance-enum-size,
-portability-avoid-pragma-once,
-portability-no-assembler,
-portability-template-virtual-member-function,
-readability-ambiguous-smartptr-reset-call,
-readability-avoid-nested-conditional-operator,
@@ -133,13 +129,9 @@ Checks: >-
-readability-named-parameter,
-readability-redundant-casting,
-readability-redundant-inline-specifier,
-readability-redundant-lambda-parameter-list,
-readability-redundant-member-init,
-readability-redundant-nested-if,
-readability-redundant-parentheses,
-readability-redundant-typename,
-readability-trailing-comma,
-readability-trivial-switch,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-readability-use-std-min-max,
-2
View File
@@ -2,6 +2,4 @@
omit =
esphome/components/*
esphome/analyze_memory/*
esphome/espidf/get_idf_tool_paths.py
esphome/espidf/get_idf_version.py
tests/integration/*
@@ -21,7 +21,6 @@ module.exports = {
'github-actions',
'by-code-owner',
'has-tests',
'api.proto',
'needs-tests',
'needs-docs',
'needs-developer-docs',
@@ -272,18 +272,6 @@ async function detectTests(changedFiles) {
return labels;
}
// Strategy: API protocol change detection
// aioesphomeapi carries copies of both files, so every change here needs a sync there
const API_PROTO_FILES = ['esphome/components/api/api.proto', 'esphome/components/api/api_options.proto'];
async function detectApiProto(changedFiles) {
const labels = new Set();
if (changedFiles.some(file => API_PROTO_FILES.includes(file))) {
labels.add('api.proto');
}
return labels;
}
// Strategy: PR Template Checkbox detection
async function detectPRTemplateCheckboxes(context) {
const labels = new Set();
@@ -472,7 +460,6 @@ module.exports = {
detectGitHubActionsChanges,
detectCodeOwner,
detectTests,
detectApiProto,
detectPRTemplateCheckboxes,
detectDeprecatedComponents,
detectMaintainerAccess,
-4
View File
@@ -10,7 +10,6 @@ const {
detectGitHubActionsChanges,
detectCodeOwner,
detectTests,
detectApiProto,
detectPRTemplateCheckboxes,
detectDeprecatedComponents,
detectMaintainerAccess,
@@ -115,7 +114,6 @@ module.exports = async ({ github, context }) => {
actionsLabels,
codeOwnerLabels,
testLabels,
apiProtoLabels,
checkboxLabels,
deprecatedResult,
maintainerAccess
@@ -130,7 +128,6 @@ module.exports = async ({ github, context }) => {
detectGitHubActionsChanges(changedFiles),
detectCodeOwner(github, context, changedFiles),
detectTests(changedFiles),
detectApiProto(changedFiles),
detectPRTemplateCheckboxes(context),
detectDeprecatedComponents(github, context, changedFiles),
detectMaintainerAccess(context)
@@ -159,7 +156,6 @@ module.exports = async ({ github, context }) => {
...actionsLabels,
...codeOwnerLabels,
...testLabels,
...apiProtoLabels,
...checkboxLabels,
...deprecatedLabels
]);
@@ -1,7 +1,6 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const {
detectApiProto,
detectMergeBranch,
detectNewPlatforms,
detectNewComponents,
@@ -376,27 +375,6 @@ describe('detectRequirements', () => {
});
});
// ---------------------------------------------------------------------------
// detectApiProto
// ---------------------------------------------------------------------------
describe('detectApiProto', () => {
it('labels a PR that changes api.proto', async () => {
const labels = await detectApiProto(['esphome/components/api/api.proto', 'esphome/components/api/api_pb2.cpp']);
assert.deepEqual(Array.from(labels), ['api.proto']);
});
it('labels a PR that changes only api_options.proto', async () => {
const labels = await detectApiProto(['esphome/components/api/api_options.proto']);
assert.deepEqual(Array.from(labels), ['api.proto']);
});
it('ignores the generated protocol files and other components', async () => {
const labels = await detectApiProto(['esphome/components/api/api_pb2.cpp', 'esphome/components/uart/api.proto']);
assert.equal(labels.size, 0);
});
});
// ---------------------------------------------------------------------------
// MANAGED_LABELS
// ---------------------------------------------------------------------------
@@ -409,10 +387,6 @@ describe('MANAGED_LABELS', () => {
it('includes needs-developer-docs so the workflow syncs it', () => {
assert.ok(MANAGED_LABELS.includes('needs-developer-docs'));
});
it('includes api.proto so the workflow removes it when the file drops out of the diff', () => {
assert.ok(MANAGED_LABELS.includes('api.proto'));
});
});
// ---------------------------------------------------------------------------
+1 -1
View File
@@ -38,7 +38,7 @@ repos:
- id: yamllint
exclude: ^(\.clang-format|\.clang-tidy)$
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v23.1.3
rev: v22.1.8
hooks:
- id: clang-format
types_or: [c, c++]
+6 -9
View File
@@ -205,16 +205,15 @@ file does, and it is the authority when they disagree. The most useful starting
for a table of structs copy the whole entry out and return the copy, never a pointer into flash.
On other platforms they are a plain `memcpy` and dereference, so no `#ifdef` is needed. Declare log tags with
`ESPHOME_LOG_TAG(TAG, "name")`, pass status text as `LOG_STR`, e.g.
`this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))`. An action that only forwards a byte payload is
registered with `automation.ApplyBytes` (see Actions below), which keeps a constant payload in flash;
`TEMPLATABLE_BYTES` is for a `play()` that does more with the bytes.
`this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))`, and give an action whose payload is a byte array
a `TEMPLATABLE_BYTES` field so a static code stays in flash.
* **Fixed sets of names** (status words, model names): a `PROGMEM_STRING_TABLE` (`esphome/core/progmem.h`),
logged with `get_log_str` and `LOG_STR_ARG`, copied out with `ESPHOME_strncpy_P` before `publish_state`.
A `dump_config` helper that picks a name returns `const LogString *` from `LOG_STR(...)`.
* **CRCs and bit tricks** come from `esphome/core/helpers.h` (`crc8`, `crc16`, `xor_bytes`, `sum16_bytes`,
`reverse_bits`, `encode_uint16`, `encode_value_le`, `sign_extend<BITS>`, `bcd_to_uint8`, `assign_bits`); pick
the helper's arguments instead of writing a loop. 7-byte BCD RTC calendars use `time::decode_bcd_calendar<LAYOUT>`
and `encode_bcd_calendar<LAYOUT>` with `EPSON_BCD_CALENDAR`, `DS_BCD_CALENDAR` or `PCF_BCD_CALENDAR`.
`reverse_bits`, `encode_uint16`, `encode_value_le`, `sign_extend<BITS>`, `bcd_to_uint8`); pick the helper's
arguments instead of writing a loop. Epson style 7-byte RTC calendars (one-hot weekday) use
`time::decode_bcd_calendar`.
* **Bus helpers:** UART drains with `read_available(on_byte)` and `discard_available()`, text lines with
`LineReader<N>`; I2C has `update_register`, `write_registers` (PROGMEM `I2CRegValue` table), `check_chip_id`
and `read_bytes_16_le`. `if (!this->status_comm_result(ok)) return;` sets or clears the warning.
@@ -605,9 +604,7 @@ file does, and it is the authority when they disagree. The most useful starting
)
```
The `ApplyField`, `ApplyCall` and `register_apply_action` docstrings in `esphome/automation.py` cover
the rest; `cover.control` and `cover.template.publish` are in-tree examples. An action whose input is a
byte payload passes `automation.ApplyBytes(CONF_DATA, "send({})")` in the same call; a constant payload
stays in flash and a lambda's vector is forwarded as data and size. `TEMPLATABLE_VALUE` with
the rest; `cover.control` and `cover.template.publish` are in-tree examples. `TEMPLATABLE_VALUE` with
`cg.templatable` stays for actions whose `play()` has real logic beyond forwarding values.
* **Conditions:**
-2
View File
@@ -695,12 +695,10 @@ esphome/components/usb_uart/* @clydebarrow
esphome/components/valve/* @esphome/core
esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81
esphome/components/veml6040/* @aalaei
esphome/components/veml7700/* @latonita
esphome/components/version/* @esphome/core
esphome/components/vevor_dtx/* @Bofr5555
esphome/components/vl53l1x/* @will-tm
esphome/components/vnc/* @MindFreeze
esphome/components/voice_assistant/* @jesserockz @kahrendt
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
-3
View File
@@ -11,9 +11,6 @@ coverage:
ignore:
- "esphome/components/**/*"
- "esphome/analyze_memory/**/*"
# Run as standalone scripts under the ESP-IDF Python, never imported by pytest
- "esphome/espidf/get_idf_tool_paths.py"
- "esphome/espidf/get_idf_version.py"
- "tests/integration/**/*"
comment:
+1 -1
View File
@@ -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.26.0
RUN uv pip install --no-cache-dir esphome-device-builder==1.25.0
RUN \
platformio settings set enable_telemetry No \
+16 -124
View File
@@ -24,7 +24,6 @@ from esphome.const import (
)
from esphome.core import CORE, ID, EsphomeError, HexInt, Lambda
from esphome.cpp_generator import (
CallExpression,
Expression,
LambdaExpression,
MockObj,
@@ -60,15 +59,10 @@ async def templatable_bytes(
if cg.is_template(value):
fn = await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
cg.add(set_template(fn))
elif len(value) > 0xFFFF:
raise EsphomeError(f"Byte payload is {len(value)} bytes; the maximum is 65535")
else:
cg.add(set_static(*_static_bytes(table_name, value)))
def _static_bytes(table_name: str, value: bytes | list[int]) -> tuple[MockObj, int]:
"""The shared PROGMEM table for a constant payload and its length."""
if (size := len(value)) > 0xFFFF:
raise EsphomeError(f"Byte payload is {size} bytes; the maximum is 65535")
return progmem_bytes(table_name, value), size
cg.add(set_static(progmem_bytes(table_name, value), len(value)))
def maybe_simple_id(*validators):
@@ -362,33 +356,6 @@ class ApplyField:
return ApplyCall(target, ((self.conf_key, self.type_, self.const_fn),))
@dataclass(frozen=True)
class ApplyBytes:
"""A byte payload key forwarded as ``target``, whose ``{}`` stands for ``data, len``.
A constant payload is a shared PROGMEM table passed as is; on ESP8266 it is first copied
out, to the stack up to ``stack`` bytes and to the heap beyond, unless ``progmem_target``
names a call that reads flash itself. A lambda's ``std::vector<uint8_t>`` is passed as
its data and size. The template may name the parent as ``{parent}``. An absent key
emits nothing.
"""
conf_key: str | tuple[str, ...]
target: str
stack: int = 64
progmem_target: str | None = None
def __post_init__(self) -> None:
for target in (self.target, self.progmem_target):
if target is None:
continue
fields = [
f for _, f, _, _ in string.Formatter().parse(target) if f is not None
]
if fields.count("") != 1 or any(f not in ("", "parent") for f in fields):
raise ValueError(f"apply bytes target {target!r} needs one {{}}")
def _config_lookup(config: ConfigType, key: str | tuple[str, ...]) -> Any:
if isinstance(key, str):
return config.get(key)
@@ -447,17 +414,9 @@ async def _apply_parent(config: ConfigType, id_key: str = CONF_ID) -> str:
def _apply_lambda_args(args: TemplateArgsType) -> TemplateArgsType:
# The generated function's parameters: a reference keeps its spelling, so a mutable
# one stays mutable; a value is taken by const reference, so a std::string is never copied.
# The generated function's parameters; a std::string arg is never copied.
return [
(
(
t
if str(cg.safe_exp(t)).rstrip().endswith("&")
else cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &")
),
arg,
)
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), arg)
for t, arg in args
]
@@ -529,7 +488,7 @@ def _apply_values(config: ConfigType, members: list[tuple[Any, Any, Any]]) -> li
def register_apply_action(
name: str,
schema: cv.Schema,
*fields: ApplyField | ApplyCall | ApplyBytes,
*fields: ApplyField | ApplyCall,
call: str | None = None,
id_key: str = CONF_ID,
) -> None:
@@ -545,7 +504,7 @@ def register_apply_action(
def apply_action_builder(
name: str,
schema: cv.Schema,
*fields: ApplyField | ApplyCall | ApplyBytes,
*fields: ApplyField | ApplyCall,
call: str | None = None,
id_key: str = CONF_ID,
) -> Callable[..., Awaitable[MockObj]]:
@@ -558,20 +517,17 @@ def apply_action_builder(
and ``apply_call.perform()`` is appended.
"""
# An action stores the value, so a std::string constant stays in flash on ESP8266.
statements_spec = [
(
c.target,
_default_std_string(c.members, flash_string),
)
for c in (f if isinstance(f, ApplyCall) else f.call() for f in fields)
]
_check_key_in_schema(name, schema, id_key)
statements_spec: list[ApplyBytes | tuple[str, list[tuple[Any, Any, Any]]]] = []
for f in fields:
if isinstance(f, ApplyBytes):
_check_key_in_schema(name, schema, f.conf_key)
statements_spec.append(f)
continue
c = f if isinstance(f, ApplyCall) else f.call()
members = _default_std_string(c.members, flash_string)
for _, members in statements_spec:
for conf_key, _, _ in members:
_check_key_in_schema(name, schema, conf_key)
statements_spec.append((c.target, members))
# Named like the generated function, so it cannot clash with an SDK symbol
table_name = f"esphome__{name.replace('.', '_')}__bytes"
async def builder(
config: ConfigType,
@@ -583,15 +539,7 @@ def apply_action_builder(
lambda_args = _apply_lambda_args(args)
receiver = "apply_call." if call else f"{parent}->"
statements: list[str] = []
for spec in statements_spec:
if isinstance(spec, ApplyBytes):
statements.extend(
await _render_bytes(
spec, config, receiver, parent, args, table_name
)
)
continue
target, members = spec
for target, members in statements_spec:
values = _apply_values(config, members)
if not _apply_call_active(members, values):
continue
@@ -612,62 +560,6 @@ def apply_action_builder(
return builder
async def _render_bytes(
spec: ApplyBytes,
config: ConfigType,
receiver: str,
parent: str,
args: TemplateArgsType,
table_name: str,
) -> list[str]:
"""The statements for one ``ApplyBytes``: what ``TemplatableBytes::visit`` does, inlined.
The payload lambda keeps the automation's own argument types, as ``TemplatableBytes``
gave it, and is always called, so a body that modifies an argument still compiles.
"""
value = _config_lookup(config, spec.conf_key)
if value is None:
return []
# The temporary carries the generated-name prefix so a trigger argument cannot shadow it
def send(target: str, data: str) -> str:
return f"{receiver}{target.format(data, parent=parent)};"
if isinstance(value, Lambda):
inner = await cg.process_lambda(
value, args, return_type=cg.std_vector.template(cg.uint8)
)
# Never reduced into the outer function, whose own parameters are const
call = CallExpression(inner, *[cg.MockObj(name) for _, name in args])
return [
"{",
f" const std::vector<uint8_t> esphome__bytes = {call};",
f" {send(spec.target, 'esphome__bytes.data(), esphome__bytes.size()')}",
"}",
]
table, size = _static_bytes(table_name, value)
# Qualified so a trigger argument cannot shadow a reused table; an empty payload is nullptr
table_args = f"::{table}, {size}" if size else f"{table}, {size}"
if CORE.is_esp8266 and spec.progmem_target is not None:
return [send(spec.progmem_target, table_args)]
if not CORE.is_esp8266:
return [send(spec.target, table_args)]
# A PROGMEM table must be read with aligned loads on ESP8266, so copy it out first;
# the length is known here, so the copy is an exact array, on the heap past ``stack``.
# An empty payload still gets a one byte array: the raw lwIP socket rejects a null buffer.
if size <= spec.stack:
buffer, data = f"uint8_t esphome__bytes[{max(size, 1)}];", "esphome__bytes"
else:
buffer, data = (
f"auto esphome__bytes = std::make_unique<uint8_t[]>({size});",
"esphome__bytes.get()",
)
statements = ["{", f" {buffer}"]
if size:
statements.append(f" esphome::progmem_memcpy({data}, {table_args});")
return [*statements, f" {send(spec.target, f'{data}, {size}')}", "}"]
def _apply_call_active(members: list[tuple[Any, Any, Any]], values: list[Any]) -> bool:
"""An ``ApplyCall`` is emitted unless it has keys and none of them is set."""
return not members or any(value is not None for value in values)
+1 -3
View File
@@ -111,9 +111,7 @@ class ToolOutput(RedirectText):
def _emit_line(self, line: str) -> None:
if self._progress and _PROGRESS.match(line):
if self._is_filtered(line):
self._drop_line(line)
else:
if not self._is_filtered(line):
text = _fit_terminal(line.strip("\r\n"))
self._write_color_replace(f"\r{text}\x1b[K")
self._on_progress_line = True
@@ -10,7 +10,7 @@ void AbsoluteHumidityComponent::setup() {
this->temperature_ = state;
this->enable_loop();
});
ESP_LOGD(TAG, " Added callback for temperature '%s'", LOG_STR_ARG(this->temperature_sensor_->get_log_name()));
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
// Get initial value
if (this->temperature_sensor_->has_state()) {
this->temperature_ = this->temperature_sensor_->get_state();
@@ -20,7 +20,7 @@ void AbsoluteHumidityComponent::setup() {
this->humidity_ = state;
this->enable_loop();
});
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", LOG_STR_ARG(this->humidity_sensor_->get_log_name()));
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", this->humidity_sensor_->get_name().c_str());
// Get initial value
if (this->humidity_sensor_->has_state()) {
this->humidity_ = this->humidity_sensor_->get_state();
@@ -49,8 +49,7 @@ void AbsoluteHumidityComponent::dump_config() {
"Sources\n"
" Temperature: '%s'\n"
" Relative Humidity: '%s'",
LOG_STR_ARG(this->temperature_sensor_->get_log_name()),
LOG_STR_ARG(this->humidity_sensor_->get_log_name()));
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
}
void AbsoluteHumidityComponent::loop() {
+1 -1
View File
@@ -66,7 +66,7 @@ template class Aggregator<uint32_t>;
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", LOG_STR_ARG(this->get_log_name()), value_v);
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
@@ -111,8 +111,6 @@ static const LogString *input_to_str(uint8_t input) {
return LOG_STR("VDD");
case NRF_SAADC_INPUT_VDDHDIV5:
return LOG_STR("VDDHDIV5");
default:
break;
}
return LOG_STR("undefined input");
}
@@ -13,7 +13,7 @@ float ADS1115Sensor::sample() {
void ADS1115Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", LOG_STR_ARG(this->get_log_name()), v);
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
@@ -21,7 +21,7 @@ float ADS1118Sensor::sample() {
void ADS1118Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", LOG_STR_ARG(this->get_log_name()), v);
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
-2
View File
@@ -261,8 +261,6 @@ void AirtonClimate::parse_state_frame_(const uint8_t *frame) {
case AIRTON_FAN_AUTO:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
default:
break;
}
this->target_temperature = (frame[3] & 0b00001111) + AIRTON_TEMP_MIN;
@@ -31,7 +31,7 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
this->last_update_ = millis();
if (state != this->current_state_) {
auto prev_state = this->current_state_;
ESP_LOGV(TAG, "'%s' >> %s (was %s)", LOG_STR_ARG(this->get_log_name()),
ESP_LOGV(TAG, "'%s' >> %s (was %s)", this->get_name().c_str(),
LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
this->current_state_ = state;
+13 -16
View File
@@ -30,9 +30,9 @@ void Am43Component::loop() {
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
ESP_LOGI(TAG, "[%s] Logging into AM43", LOG_STR_ARG(this->get_log_name()));
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", LOG_STR_ARG(this->get_log_name()), status);
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
} else {
this->logged_in_ = true;
}
@@ -50,7 +50,7 @@ CoverTraits Am43Component::get_traits() {
void Am43Component::control(const CoverCall &call) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
return;
}
if (call.get_stop()) {
@@ -59,7 +59,7 @@ void Am43Component::control(const CoverCall &call) {
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", LOG_STR_ARG(this->get_log_name()), status);
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
}
}
auto opt_pos = call.get_position();
@@ -73,8 +73,7 @@ void Am43Component::control(const CoverCall &call) {
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", LOG_STR_ARG(this->get_log_name()),
status);
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
}
}
}
@@ -90,9 +89,9 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", LOG_STR_ARG(this->get_log_name()));
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
} else {
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", LOG_STR_ARG(this->get_log_name()));
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", this->get_name().c_str());
}
break;
}
@@ -101,8 +100,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", LOG_STR_ARG(this->get_log_name()),
status);
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
break;
}
@@ -128,26 +126,25 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (this->decoder_->has_pin_response()) {
if (this->decoder_->pin_ok_) {
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", LOG_STR_ARG(this->get_log_name()));
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
auto *packet = this->encoder_->get_position_request();
auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_, packet->length, packet->data,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", LOG_STR_ARG(this->get_log_name()),
status);
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
}
} else {
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str());
}
}
if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_) {
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str());
}
if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_) {
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str());
}
break;
}
+3 -5
View File
@@ -67,9 +67,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.",
LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;
@@ -77,8 +76,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", LOG_STR_ARG(this->get_log_name()),
status);
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
break;
}
+34 -105
View File
@@ -1,7 +1,5 @@
from collections.abc import Callable
from ipaddress import IPv4Address, IPv6Address
import logging
import math
import re
from typing import Any
@@ -9,7 +7,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components.const import CONF_DESCRIPTION, CONF_HOST
from esphome.components.logger import request_log_listener
from esphome.components.network import require_ipv4, require_ipv6
from esphome.components.network import DOMAIN as NETWORK_DOMAIN
# ENCRYPTION_SCHEMA and validate_encryption_key are re-exported for external
# components and downstream consumers that import them from api
@@ -29,8 +27,8 @@ from esphome.const import (
CONF_CAPTURE_RESPONSE,
CONF_DATA,
CONF_DATA_TEMPLATE,
CONF_DEFAULT,
CONF_DELAY,
CONF_ENABLE_IPV6,
CONF_ENCRYPTION,
CONF_EVENT,
CONF_ID,
@@ -54,6 +52,7 @@ from esphome.const import (
)
from esphome.core import CORE, ID, CoroPriority, EsphomeError, coroutine_with_priority
from esphome.cpp_generator import Expression, MockObj, TemplateArgsType
import esphome.final_validate as fv
from esphome.helpers import fnv1_hash
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigFragmentType, ConfigType
@@ -139,7 +138,6 @@ SERVICE_ARG_FALLBACK_TYPES: dict[str, MockObj] = {
CONF_BATCH_DELAY = "batch_delay"
CONF_CUSTOM_SERVICES = "custom_services"
CONF_EXAMPLE = "example"
CONF_REQUIRED = "required"
CONF_HOMEASSISTANT_SERVICES = "homeassistant_services"
CONF_HOMEASSISTANT_STATES = "homeassistant_states"
CONF_LISTEN_BACKLOG = "listen_backlog"
@@ -251,66 +249,12 @@ def _validate_supports_response(value: Any) -> str:
return cv.enum(SUPPORTS_RESPONSE_OPTIONS, lower=True)(value)
def _not_boolean(value: Any) -> Any:
# bool is an int subclass, and "true" cannot be parsed as a number by the client
if isinstance(value, bool):
raise cv.Invalid("Expected a number, got a boolean")
return value
def _finite(value: float) -> float:
if not math.isfinite(value):
raise cv.Invalid("Expected a finite number")
return value
def _no_nul(value: str) -> str:
# The default is a C string in flash, so a NUL would cut it short
if "\0" in value:
raise cv.Invalid("A default cannot contain a NUL character")
return value
# The wire carries the default as text; the client parses it into ExecuteServiceArgument's
# types, so the values are kept within what int32 and float hold
FLOAT32_MAX = 3.4028235e38
_DEFAULT_VALUE_VALIDATORS: dict[str, Callable[[Any], Any]] = {
"bool": cv.boolean,
"int": cv.All(_not_boolean, cv.int_range(min=-(2**31), max=2**31 - 1)),
"float": cv.All(
_not_boolean,
cv.float_,
_finite,
cv.float_range(min=-FLOAT32_MAX, max=FLOAT32_MAX),
),
"string": cv.All(cv.string_strict, _no_nul),
}
assert _DEFAULT_VALUE_VALIDATORS.keys() == _SERVICE_ARG_SCALAR_TYPES.keys()
def _validate_variable_optionality(value: ConfigType) -> ConfigType:
"""Cross-validate required/default against the variable type."""
var_type = value[CONF_TYPE]
if _is_optional_var(value) and var_type not in _DEFAULT_VALUE_VALIDATORS:
raise cv.Invalid("Array variables cannot be optional")
if CONF_DEFAULT in value:
if value.get(CONF_REQUIRED) is True:
raise cv.Invalid("A variable with a default cannot set required: true")
value[CONF_DEFAULT] = _DEFAULT_VALUE_VALIDATORS[var_type](value[CONF_DEFAULT])
return value
VARIABLE_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_TYPE): cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
cv.Optional(CONF_DESCRIPTION): cv.string_strict,
cv.Optional(CONF_EXAMPLE): cv.string_strict,
cv.Optional(CONF_REQUIRED): cv.boolean,
cv.Optional(CONF_DEFAULT): cv.valid,
}
),
_validate_variable_optionality,
VARIABLE_SCHEMA = cv.Schema(
{
cv.Required(CONF_TYPE): cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
cv.Optional(CONF_DESCRIPTION): cv.string_strict,
cv.Optional(CONF_EXAMPLE): cv.string_strict,
}
)
# Accepts the plain `name: type` shorthand or the full mapping form
@@ -389,10 +333,7 @@ def _validate_outgoing_host(value: str) -> IPv4Address | IPv6Address:
"give the address without the '%' part"
)
if (mapped := address.ipv4_mapped) is not None:
address = mapped
# The build must carry the host's address family for the dial to work
require = require_ipv6 if isinstance(address, IPv6Address) else require_ipv4
require("api.outgoing_connection")(value)
return mapped
return address
@@ -522,28 +463,7 @@ def _has_action_metadata(actions: list[ConfigType]) -> bool:
)
def _is_optional_var(var_: ConfigType) -> bool:
return CONF_DEFAULT in var_ or var_.get(CONF_REQUIRED) is False
def _has_optional_args(actions: list[ConfigType]) -> bool:
return any(
_is_optional_var(var_)
for conf in actions
for var_ in conf[CONF_VARIABLES].values()
)
def _default_to_wire(value: Any) -> str:
"""Serialize a validated default for the wire; clients coerce by arg type."""
if isinstance(value, bool):
return "true" if value else "false"
return str(value)
def _action_strings(
conf: ConfigType, has_metadata: bool, has_optional: bool
) -> list[str | None]:
def _action_strings(conf: ConfigType, has_metadata: bool) -> list[str | None]:
"""Strings of one action in the table order UserServiceStatic (user_services.h) expects."""
# An empty description or example is treated as unset
strings: list[str | None] = [conf[CONF_ACTION]]
@@ -556,24 +476,36 @@ def _action_strings(
var_.get(CONF_DESCRIPTION) or None,
var_.get(CONF_EXAMPLE) or None,
]
if has_optional:
# The default slot doubles as the optional flag: nullptr for a required variable,
# "" for an optional one without a default (an empty string default is the same)
if not _is_optional_var(var_):
strings.append(None)
elif (default := var_.get(CONF_DEFAULT)) is None:
strings.append("")
else:
strings.append(_default_to_wire(default))
return strings
def _validate_outgoing_host_ipv6(config: ConfigType) -> ConfigType:
"""An IPv6 host can never be parsed, so never dialed, without IPv6."""
if (
(outgoing := config.get(CONF_OUTGOING_CONNECTION)) is None
or (host := outgoing.get(CONF_HOST)) is None
or host.version != 6
):
return config
network_conf = fv.full_config.get().get(NETWORK_DOMAIN) or {}
if not network_conf.get(CONF_ENABLE_IPV6):
raise cv.Invalid(
"outgoing_connection host is an IPv6 address but IPv6 is not "
"enabled; set 'network: enable_ipv6: true'",
path=[CONF_OUTGOING_CONNECTION, CONF_HOST],
)
return config
def _validate_wizard(config: ConfigType) -> ConfigType:
wizard.final_validate(config)
return config
FINAL_VALIDATE_SCHEMA = _validate_wizard
FINAL_VALIDATE_SCHEMA = cv.All(
_validate_outgoing_host_ipv6,
_validate_wizard,
)
def _add_action_strings(index: int, strings: list[str | None]) -> MockObj:
@@ -632,9 +564,6 @@ async def to_code(config: ConfigType) -> None:
has_metadata = _has_action_metadata(actions)
if has_metadata:
cg.add_define("USE_API_USER_DEFINED_ACTION_METADATA")
has_optional = _has_optional_args(actions)
if has_optional:
cg.add_define("USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS")
# Collect all triggers first, then register all at once with initializer_list
triggers: list[cg.MockObj] = []
for index, conf in enumerate(actions):
@@ -678,7 +607,7 @@ async def to_code(config: ConfigType) -> None:
native = SERVICE_ARG_NATIVE_TYPES[var_type]
service_template_args.append(native)
func_args.append((native, name))
strings = _action_strings(conf, has_metadata, has_optional)
strings = _action_strings(conf, has_metadata)
table = _add_action_strings(index, strings)
# Template args: supports_response mode, then user service arg types
templ = cg.TemplateArguments(supports_response, *service_template_args)
+62 -78
View File
@@ -227,17 +227,11 @@ message AreaInfo {
string name = 2 [(max_data_length) = 120, (force) = true];
}
enum DeviceRelation {
DEVICE_RELATION_VIA = 0;
DEVICE_RELATION_CHILD = 1;
}
message DeviceInfo {
uint32 device_id = 1;
// max_data_length matches core/config.FRIENDLY_NAME_MAX_LEN via DEVICE_SCHEMA
string name = 2 [(max_data_length) = 120, (force) = true];
uint32 area_id = 3;
DeviceRelation relation = 4;
}
enum SerialProxyPortType {
@@ -561,13 +555,13 @@ message ListEntitiesBinarySensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string device_class = 5 [(max_data_length) = 47, (progmem) = true];
string device_class = 5 [(max_data_length) = 47];
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 9;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
@@ -595,20 +589,18 @@ message ListEntitiesCoverResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
bool assumed_state = 5;
bool supports_position = 6;
bool supports_tilt = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
bool disabled_by_default = 9;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 11;
bool supports_stop = 12;
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
bool supports_stop_tilt = 14;
bool supports_toggle = 15;
}
// Deprecated in API version 1.1
@@ -670,8 +662,6 @@ message CoverCommandRequest {
float tilt = 7;
bool stop = 8;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
bool stop_tilt = 10;
bool toggle = 11;
}
// ==================== FAN ====================
@@ -683,7 +673,7 @@ message ListEntitiesFanResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_oscillation = 5;
@@ -691,7 +681,7 @@ message ListEntitiesFanResponse {
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "ConstVector<const char *, true>"];
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
@@ -771,7 +761,7 @@ message ListEntitiesLightResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"];
@@ -788,7 +778,7 @@ message ListEntitiesLightResponse {
float max_mireds = 10;
repeated string effects = 11 [(container_pointer_no_template) = "light::LightEffectNames"];
bool disabled_by_default = 13;
string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 15;
uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
}
@@ -876,14 +866,14 @@ message ListEntitiesSensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
string unit_of_measurement = 6 [(max_data_length) = 63];
int32 accuracy_decimals = 7;
bool force_update = 8;
string device_class = 9 [(max_data_length) = 47, (progmem) = true];
string device_class = 9 [(max_data_length) = 47];
SensorStateClass state_class = 10;
// Last reset type removed in 2021.9.0
// Deprecated in API version 1.5
@@ -917,14 +907,14 @@ message ListEntitiesSwitchResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool assumed_state = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
string device_class = 9 [(max_data_length) = 47, (progmem) = true];
string device_class = 9 [(max_data_length) = 47];
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message SwitchStateResponse {
@@ -962,13 +952,13 @@ message ListEntitiesTextSensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message TextSensorStateResponse {
@@ -1174,10 +1164,6 @@ message ListEntitiesServicesArgument {
ServiceArgType type = 2;
string description = 3 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA", (progmem) = true];
string example = 4 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA", (progmem) = true];
// Advisory: ExecuteServiceRequest still requires every argument
bool optional = 5 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS"];
// Always text: bool "true"/"false", numbers parseable as int/float; empty means no default
string default_value = 6 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS", (progmem) = true];
}
message ListEntitiesServicesResponse {
option (id) = 41;
@@ -1237,10 +1223,10 @@ message ListEntitiesCameraResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
bool disabled_by_default = 5;
string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 7;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
@@ -1329,7 +1315,7 @@ message ListEntitiesClimateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_current_temperature = 5; // Deprecated: use feature_flags
@@ -1349,7 +1335,7 @@ message ListEntitiesClimateResponse {
repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"];
repeated string supported_custom_presets = 17 [(container_pointer_no_template) = "ConstVector<const char *, true>"];
bool disabled_by_default = 18;
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
EntityCategory entity_category = 20;
float visual_current_temperature_step = 21;
bool supports_current_humidity = 22; // Deprecated: use feature_flags
@@ -1444,8 +1430,8 @@ message ListEntitiesWaterHeaterResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
@@ -1524,10 +1510,10 @@ message ListEntitiesNumberResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
float min_value = 6;
float max_value = 7;
float step = 8;
@@ -1535,7 +1521,7 @@ message ListEntitiesNumberResponse {
EntityCategory entity_category = 10;
string unit_of_measurement = 11 [(max_data_length) = 63];
NumberMode mode = 12;
string device_class = 13 [(max_data_length) = 47, (progmem) = true];
string device_class = 13 [(max_data_length) = 47];
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message NumberStateResponse {
@@ -1573,10 +1559,10 @@ message ListEntitiesSelectResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
repeated string options = 6 [(container_pointer_no_template) = "std::span<const char *const>"];
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
@@ -1617,10 +1603,10 @@ message ListEntitiesSirenResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
repeated string tones = 7 [(container_pointer_no_template) = "FixedVector<const char *>"];
bool supports_duration = 8;
@@ -1682,10 +1668,10 @@ message ListEntitiesLockResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool assumed_state = 8;
@@ -1731,13 +1717,13 @@ message ListEntitiesButtonResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message ButtonCommandRequest {
@@ -1789,8 +1775,6 @@ message MediaPlayerSupportedFormat {
uint32 num_channels = 3;
MediaPlayerFormatPurpose purpose = 4;
uint32 sample_bytes = 5;
// Constant bitrate in kbps for lossy formats; 0 means no preference
uint32 bitrate = 6;
}
message ListEntitiesMediaPlayerResponse {
option (id) = 63;
@@ -1800,10 +1784,10 @@ message ListEntitiesMediaPlayerResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
@@ -2392,9 +2376,9 @@ message ListEntitiesAlarmControlPanelResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 supported_features = 8;
@@ -2439,9 +2423,9 @@ message ListEntitiesTextResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
@@ -2487,10 +2471,10 @@ message ListEntitiesDateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
@@ -2534,10 +2518,10 @@ message ListEntitiesTimeResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
@@ -2581,13 +2565,13 @@ message ListEntitiesEventResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
repeated string event_types = 9 [(container_pointer_no_template) = "ConstVector<const char *, true>"];
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
@@ -2612,13 +2596,13 @@ message ListEntitiesValveResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
bool assumed_state = 9;
bool supports_position = 10;
@@ -2667,10 +2651,10 @@ message ListEntitiesDateTimeResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
@@ -2710,13 +2694,13 @@ message ListEntitiesUpdateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8 [(max_data_length) = 47, (progmem) = true];
string device_class = 8 [(max_data_length) = 47];
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message UpdateStateResponse {
@@ -2810,8 +2794,8 @@ message ListEntitiesInfraredResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
@@ -2879,8 +2863,8 @@ message ListEntitiesRadioFrequencyResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63, (progmem) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
+6 -13
View File
@@ -24,7 +24,6 @@
#endif
#include "esphome/components/network/util.h"
#include "esphome/core/application.h"
#include "esphome/core/device.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
@@ -486,12 +485,13 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
msg.key = entity->get_object_id_hash();
if (entity->has_own_name()) {
msg.name = entity->get_name_progmem();
msg.name = entity->get_name();
}
// Set common EntityBase properties
#ifdef USE_ENTITY_ICON
msg.icon = entity->get_icon_progmem();
char icon_buf[MAX_ICON_LENGTH];
msg.icon = StringRef(entity->get_icon_to(icon_buf));
#endif
msg.disabled_by_default = entity->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
@@ -502,11 +502,12 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
}
uint16_t APIConnection::fill_and_encode_entity_info_with_device_class(EntityBase *entity, InfoResponseProtoMessage &msg,
ProgmemStringRef &device_class_field,
StringRef &device_class_field,
CalculateSizeFn size_fn,
MessageEncodeFn encode_fn, APIConnection *conn,
uint32_t remaining_size) {
device_class_field = entity->get_device_class_progmem();
char dc_buf[MAX_DEVICE_CLASS_LENGTH];
device_class_field = StringRef(entity->get_device_class_to(dc_buf));
return fill_and_encode_entity_info(entity, msg, size_fn, encode_fn, conn, remaining_size);
}
@@ -554,8 +555,6 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
msg.supports_position = traits.get_supports_position();
msg.supports_tilt = traits.get_supports_tilt();
msg.supports_stop = traits.get_supports_stop();
msg.supports_stop_tilt = traits.get_supports_stop_tilt();
msg.supports_toggle = traits.get_supports_toggle();
return fill_and_encode_entity_info_with_device_class(cover, msg, msg.device_class, conn, remaining_size);
}
void APIConnection::on_cover_command_request(const CoverCommandRequest &msg) {
@@ -566,10 +565,6 @@ void APIConnection::on_cover_command_request(const CoverCommandRequest &msg) {
call.set_tilt(msg.tilt);
if (msg.stop)
call.set_command_stop();
if (msg.stop_tilt)
call.set_command_stop_tilt();
if (msg.toggle)
call.set_command_toggle();
call.perform();
}
#endif
@@ -1131,7 +1126,6 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
media_format.num_channels = supported_format.num_channels;
media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
media_format.sample_bytes = supported_format.sample_bytes;
media_format.bitrate = supported_format.bitrate;
}
return fill_and_encode_entity_info(media_player, msg, conn, remaining_size);
}
@@ -2063,7 +2057,6 @@ bool APIConnection::send_device_info_response_() {
device_info.device_id = device->get_device_id();
device_info.name = StringRef(device->get_name());
device_info.area_id = device->get_area_id();
device_info.relation = static_cast<enums::DeviceRelation>(device->get_relation());
}
#endif
#ifdef USE_AREAS
+5 -5
View File
@@ -472,15 +472,15 @@ class APIConnection final : public APIServerConnectionBase {
// Non-template core — fills device_class, then delegates to fill_and_encode_entity_info
static uint16_t fill_and_encode_entity_info_with_device_class(EntityBase *entity, InfoResponseProtoMessage &msg,
ProgmemStringRef &device_class_field,
CalculateSizeFn size_fn, MessageEncodeFn encode_fn,
APIConnection *conn, uint32_t remaining_size);
StringRef &device_class_field, CalculateSizeFn size_fn,
MessageEncodeFn encode_fn, APIConnection *conn,
uint32_t remaining_size);
// Thin template wrapper
template<typename T>
static uint16_t fill_and_encode_entity_info_with_device_class(EntityBase *entity, T &msg,
ProgmemStringRef &device_class_field,
APIConnection *conn, uint32_t remaining_size) {
StringRef &device_class_field, APIConnection *conn,
uint32_t remaining_size) {
return fill_and_encode_entity_info_with_device_class(entity, msg, device_class_field, &T::calc_size_msg,
&T::encode_msg, conn, remaining_size);
}
@@ -38,14 +38,12 @@ static bool peer_to_target(APIConnection *conn, SavedOutgoingTarget &target) {
const auto *bytes = reinterpret_cast<const uint8_t *>(&addr6->sin6_addr);
uint32_t prefix[3];
memcpy(prefix, bytes, sizeof(prefix));
#if USE_NETWORK_IPV4
// A dual-stack listener reports an IPv4 peer as ::ffff:a.b.c.d
if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == htonl(0xFFFFUL)) {
target.family = AF_INET;
memcpy(target.addr, bytes + sizeof(prefix), sizeof(struct in_addr));
return true;
}
#endif
// A link-local target is only reachable through the interface it came in
// on. Device platforms number interfaces from one; a host build can hand
// out an index too large to store, and a truncated one dials the wrong
@@ -59,7 +57,6 @@ static bool peer_to_target(APIConnection *conn, SavedOutgoingTarget &target) {
return true;
}
#endif
#if USE_NETWORK_IPV4
if (family != AF_INET) {
return false;
}
@@ -67,9 +64,6 @@ static bool peer_to_target(APIConnection *conn, SavedOutgoingTarget &target) {
target.family = AF_INET;
memcpy(target.addr, &addr4->sin_addr, sizeof(addr4->sin_addr));
return true;
#else
return false;
#endif
}
#endif
@@ -91,7 +85,6 @@ socklen_t OutgoingConnectionManager::target_sockaddr_(struct sockaddr_storage *a
return sizeof(*addr6);
}
#endif
#if USE_NETWORK_IPV4
if (this->saved_.family != AF_INET) {
return 0;
}
@@ -101,9 +94,6 @@ socklen_t OutgoingConnectionManager::target_sockaddr_(struct sockaddr_storage *a
addr4->sin_port = htons(API_OUTGOING_CONNECTION_PORT);
memcpy(&addr4->sin_addr, this->saved_.addr, sizeof(addr4->sin_addr));
return sizeof(*addr4);
#else
return 0;
#endif
#endif
}
+62 -210
View File
@@ -25,8 +25,6 @@ void HelloRequest::decode_field(void *self, uint32_t tag, const uint8_t *data, p
msg.outgoing_connection_target = value.as_bool();
break;
#endif
default:
break;
}
}
uint8_t *HelloResponse::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -53,8 +51,6 @@ void DisconnectRequest::decode_field(void *self, uint32_t tag, const uint8_t *da
case proto_tag(1, WIRE_TYPE_VARINT):
msg.reason = static_cast<enums::DisconnectReason>(value.as_varint());
break;
default:
break;
}
}
uint8_t *DisconnectRequest::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -89,7 +85,6 @@ uint8_t *DeviceInfo::encode_msg(const void *self, uint8_t *__restrict__ pos PROT
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.device_id);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 18, msg.name);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.area_id);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 4, static_cast<uint32_t>(msg.relation));
return pos;
}
uint32_t DeviceInfo::calc_size_msg(const void *self) {
@@ -98,7 +93,6 @@ uint32_t DeviceInfo::calc_size_msg(const void *self) {
size += ProtoSize::calc_uint32(1, msg.device_id);
size += 2 + msg.name.size();
size += ProtoSize::calc_uint32(1, msg.area_id);
size += msg.relation ? 2 : 0;
return size;
}
#endif
@@ -400,8 +394,6 @@ void WizardInputSetRequest::decode_field(void *self, uint32_t tag, const uint8_t
case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED):
msg.entity_id = StringRef(value.data(), value.size());
break;
default:
break;
}
}
#endif
@@ -411,12 +403,12 @@ uint8_t *ListEntitiesBinarySensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesBinarySensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.device_class);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.device_class);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.is_status_binary_sensor);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 9, static_cast<uint32_t>(msg.entity_category));
#ifdef USE_DEVICES
@@ -469,22 +461,20 @@ uint8_t *ListEntitiesCoverResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesCoverResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.assumed_state);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_position);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_tilt);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 11, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 12, msg.supports_stop);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 13, msg.device_id);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 14, msg.supports_stop_tilt);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 15, msg.supports_toggle);
return pos;
}
uint32_t ListEntitiesCoverResponse::calc_size_msg(const void *self) {
@@ -506,8 +496,6 @@ uint32_t ListEntitiesCoverResponse::calc_size_msg(const void *self) {
#ifdef USE_DEVICES
size += ProtoSize::calc_uint32(1, msg.device_id);
#endif
size += ProtoSize::calc_bool(1, msg.supports_stop_tilt);
size += ProtoSize::calc_bool(1, msg.supports_toggle);
return size;
}
uint8_t *CoverStateResponse::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -564,14 +552,6 @@ void CoverCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *
msg.device_id = value.as_varint();
break;
#endif
case proto_tag(10, WIRE_TYPE_VARINT):
msg.stop_tilt = value.as_bool();
break;
case proto_tag(11, WIRE_TYPE_VARINT):
msg.toggle = value.as_bool();
break;
default:
break;
}
}
#endif
@@ -580,14 +560,14 @@ uint8_t *ListEntitiesFanResponse::encode_msg(const void *self, uint8_t *__restri
const auto &msg = *static_cast<const ListEntitiesFanResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_oscillation);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_speed);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_direction);
pos = ProtoEncode::encode_int32(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.supported_speed_count);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 11, static_cast<uint32_t>(msg.entity_category));
for (const char *it : *msg.supported_preset_modes) {
@@ -692,8 +672,6 @@ void FanCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *da
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -702,7 +680,7 @@ uint8_t *ListEntitiesLightResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesLightResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
for (const auto &it : *msg.supported_color_modes) {
pos = ProtoEncode::encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, 12, static_cast<uint32_t>(it));
}
@@ -717,7 +695,7 @@ uint8_t *ListEntitiesLightResponse::encode_msg(const void *self, uint8_t *__rest
}
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 13, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 14, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 14, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 15, static_cast<uint32_t>(msg.entity_category));
#ifdef USE_DEVICES
@@ -900,8 +878,6 @@ void LightCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -910,14 +886,14 @@ uint8_t *ListEntitiesSensorResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.unit_of_measurement);
pos = ProtoEncode::encode_int32(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.accuracy_decimals);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.force_update);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_class);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 10, static_cast<uint32_t>(msg.state_class));
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 12, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 13, static_cast<uint32_t>(msg.entity_category));
@@ -978,14 +954,14 @@ uint8_t *ListEntitiesSwitchResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSwitchResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.assumed_state);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 8, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_class);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.device_id);
#endif
@@ -1045,8 +1021,6 @@ void SwitchCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -1056,13 +1030,13 @@ uint8_t *ListEntitiesTextSensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesTextSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_id);
#endif
@@ -1117,8 +1091,6 @@ void SubscribeLogsRequest::decode_field(void *self, uint32_t tag, const uint8_t
case proto_tag(2, WIRE_TYPE_VARINT):
msg.dump_config = value.as_bool();
break;
default:
break;
}
}
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
@@ -1148,8 +1120,6 @@ void NoiseEncryptionSetKeyRequest::decode_field(void *self, uint32_t tag, const
msg.key = value.data();
msg.key_len = value.size();
break;
default:
break;
}
}
uint8_t *NoiseEncryptionSetKeyResponse::encode_msg(const void *self,
@@ -1256,8 +1226,6 @@ void HomeassistantActionResponse::decode_field(void *self, uint32_t tag, const u
msg.response_data_len = value.size();
break;
#endif
default:
break;
}
}
#endif
@@ -1292,8 +1260,6 @@ void HomeAssistantStateResponse::decode_field(void *self, uint32_t tag, const ui
case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED):
msg.attribute = StringRef(value.data(), value.size());
break;
default:
break;
}
}
#endif
@@ -1319,8 +1285,6 @@ void DSTRule::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_
case proto_tag(6, WIRE_TYPE_VARINT):
msg.day_of_week = value.as_varint();
break;
default:
break;
}
}
void ParsedTimezone::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
@@ -1339,8 +1303,6 @@ void ParsedTimezone::decode_field(void *self, uint32_t tag, const uint8_t *data,
case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED):
value.decode_to_message(msg.dst_end);
break;
default:
break;
}
}
void GetTimeResponse::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
@@ -1354,8 +1316,6 @@ void GetTimeResponse::decode_field(void *self, uint32_t tag, const uint8_t *data
value.decode_to_message(msg.parsed_timezone);
msg.has_parsed_timezone = true;
break;
default:
break;
}
}
#ifdef USE_API_USER_DEFINED_ACTIONS
@@ -1369,12 +1329,6 @@ uint8_t *ListEntitiesServicesArgument::encode_msg(const void *self,
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.example);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.optional);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.default_value);
#endif
return pos;
}
@@ -1388,12 +1342,6 @@ uint32_t ListEntitiesServicesArgument::calc_size_msg(const void *self) {
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
size += ProtoSize::calc_length(1, msg.example.size());
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
size += ProtoSize::calc_bool(1, msg.optional);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
size += ProtoSize::calc_length(1, msg.default_value.size());
#endif
return size;
}
@@ -1458,8 +1406,6 @@ void ExecuteServiceArgument::decode_field(void *self, uint32_t tag, const uint8_
case proto_tag(9, WIRE_TYPE_LENGTH_DELIMITED):
msg.string_array.push_back(value.as_string());
break;
default:
break;
}
}
void ExecuteServiceArgument::decode(const uint8_t *buffer, size_t length) {
@@ -1494,8 +1440,6 @@ void ExecuteServiceRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.return_response = value.as_bool();
break;
#endif
default:
break;
}
}
void ExecuteServiceRequest::decode(const uint8_t *buffer, size_t length) {
@@ -1532,10 +1476,10 @@ uint8_t *ListEntitiesCameraResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesCameraResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
#ifdef USE_DEVICES
@@ -1590,8 +1534,6 @@ void CameraImageRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
case proto_tag(2, WIRE_TYPE_VARINT):
msg.stream = value.as_bool();
break;
default:
break;
}
}
#endif
@@ -1600,7 +1542,7 @@ uint8_t *ListEntitiesClimateResponse::encode_msg(const void *self, uint8_t *__re
const auto &msg = *static_cast<const ListEntitiesClimateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_current_temperature);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_two_point_target_temperature);
for (const auto &it : *msg.supported_modes) {
@@ -1633,7 +1575,7 @@ uint8_t *ListEntitiesClimateResponse::encode_msg(const void *self, uint8_t *__re
}
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 18, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 19, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 19, msg.icon);
#endif
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 20, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_float(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.visual_current_temperature_step);
@@ -1828,8 +1770,6 @@ void ClimateCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -1839,9 +1779,9 @@ uint8_t *ListEntitiesWaterHeaterResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesWaterHeaterResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 6, static_cast<uint32_t>(msg.entity_category));
@@ -1958,8 +1898,6 @@ void WaterHeaterCommandRequest::decode_field(void *self, uint32_t tag, const uin
case proto_tag(8, WIRE_TYPE_FIXED32):
msg.target_temperature_high = value.as_float();
break;
default:
break;
}
}
#endif
@@ -1968,9 +1906,9 @@ uint8_t *ListEntitiesNumberResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesNumberResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
if (uint32_t raw = float_to_raw(msg.min_value); raw != 0) [[likely]] {
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 53, raw);
@@ -1985,7 +1923,7 @@ uint8_t *ListEntitiesNumberResponse::encode_msg(const void *self, uint8_t *__res
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 10, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 11, msg.unit_of_measurement);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 12, static_cast<uint32_t>(msg.mode));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 13, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 13, msg.device_class);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 14, msg.device_id);
#endif
@@ -2051,8 +1989,6 @@ void NumberCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2061,9 +1997,9 @@ uint8_t *ListEntitiesSelectResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSelectResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
for (const char *it : *msg.options) {
pos = ProtoEncode::encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, 6, it, strlen(it));
@@ -2132,8 +2068,6 @@ void SelectCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2142,9 +2076,9 @@ uint8_t *ListEntitiesSirenResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesSirenResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
for (const char *it : *msg.tones) {
@@ -2236,8 +2170,6 @@ void SirenCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2246,9 +2178,9 @@ uint8_t *ListEntitiesLockResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesLockResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -2321,8 +2253,6 @@ void LockCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2331,13 +2261,13 @@ uint8_t *ListEntitiesButtonResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesButtonResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_id);
#endif
@@ -2372,8 +2302,6 @@ void ButtonCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2385,7 +2313,6 @@ uint8_t *MediaPlayerSupportedFormat::encode_msg(const void *self, uint8_t *__res
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.num_channels);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 4, static_cast<uint32_t>(msg.purpose));
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.sample_bytes);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.bitrate);
return pos;
}
uint32_t MediaPlayerSupportedFormat::calc_size_msg(const void *self) {
@@ -2396,7 +2323,6 @@ uint32_t MediaPlayerSupportedFormat::calc_size_msg(const void *self) {
size += ProtoSize::calc_uint32(1, msg.num_channels);
size += msg.purpose ? 2 : 0;
size += ProtoSize::calc_uint32(1, msg.sample_bytes);
size += ProtoSize::calc_uint32(1, msg.bitrate);
return size;
}
uint8_t *ListEntitiesMediaPlayerResponse::encode_msg(const void *self,
@@ -2404,9 +2330,9 @@ uint8_t *ListEntitiesMediaPlayerResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesMediaPlayerResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -2503,8 +2429,6 @@ void MediaPlayerCommandRequest::decode_field(void *self, uint32_t tag, const uin
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -2517,8 +2441,6 @@ void SubscribeBluetoothLEAdvertisementsRequest::decode_field(void *self, uint32_
case proto_tag(1, WIRE_TYPE_VARINT):
msg.flags = value.as_varint();
break;
default:
break;
}
}
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
@@ -2577,8 +2499,6 @@ void BluetoothDeviceRequest::decode_field(void *self, uint32_t tag, const uint8_
case proto_tag(4, WIRE_TYPE_VARINT):
msg.address_type = value.as_varint();
break;
default:
break;
}
}
uint8_t *BluetoothDeviceConnectionResponse::encode_msg(const void *self,
@@ -2607,8 +2527,6 @@ void BluetoothGATTGetServicesRequest::decode_field(void *self, uint32_t tag, con
case proto_tag(1, WIRE_TYPE_VARINT):
msg.address = value.as_varint();
break;
default:
break;
}
}
uint8_t *BluetoothGATTDescriptor::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -2735,8 +2653,6 @@ void BluetoothGATTReadRequest::decode_field(void *self, uint32_t tag, const uint
case proto_tag(2, WIRE_TYPE_VARINT):
msg.handle = value.as_varint();
break;
default:
break;
}
}
uint8_t *BluetoothGATTReadResponse::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -2772,8 +2688,6 @@ void BluetoothGATTWriteRequest::decode_field(void *self, uint32_t tag, const uin
msg.data = value.data();
msg.data_len = value.size();
break;
default:
break;
}
}
void BluetoothGATTReadDescriptorRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -2787,8 +2701,6 @@ void BluetoothGATTReadDescriptorRequest::decode_field(void *self, uint32_t tag,
case proto_tag(2, WIRE_TYPE_VARINT):
msg.handle = value.as_varint();
break;
default:
break;
}
}
void BluetoothGATTWriteDescriptorRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -2806,8 +2718,6 @@ void BluetoothGATTWriteDescriptorRequest::decode_field(void *self, uint32_t tag,
msg.data = value.data();
msg.data_len = value.size();
break;
default:
break;
}
}
void BluetoothGATTNotifyRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -2824,8 +2734,6 @@ void BluetoothGATTNotifyRequest::decode_field(void *self, uint32_t tag, const ui
case proto_tag(3, WIRE_TYPE_VARINT):
msg.enable = value.as_bool();
break;
default:
break;
}
}
uint8_t *BluetoothGATTNotifyDataResponse::encode_msg(const void *self,
@@ -2983,8 +2891,6 @@ void BluetoothScannerSetModeRequest::decode_field(void *self, uint32_t tag, cons
case proto_tag(1, WIRE_TYPE_VARINT):
msg.mode = static_cast<enums::BluetoothScannerMode>(value.as_varint());
break;
default:
break;
}
}
#endif
@@ -3000,8 +2906,6 @@ void SubscribeVoiceAssistantRequest::decode_field(void *self, uint32_t tag, cons
case proto_tag(2, WIRE_TYPE_VARINT):
msg.flags = value.as_varint();
break;
default:
break;
}
}
uint8_t *VoiceAssistantAudioSettings::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -3050,8 +2954,6 @@ void VoiceAssistantResponse::decode_field(void *self, uint32_t tag, const uint8_
case proto_tag(2, WIRE_TYPE_VARINT):
msg.error = value.as_bool();
break;
default:
break;
}
}
void VoiceAssistantEventData::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
@@ -3064,8 +2966,6 @@ void VoiceAssistantEventData::decode_field(void *self, uint32_t tag, const uint8
case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED):
msg.value = StringRef(value.data(), value.size());
break;
default:
break;
}
}
void VoiceAssistantEventResponse::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -3080,8 +2980,6 @@ void VoiceAssistantEventResponse::decode_field(void *self, uint32_t tag, const u
msg.data.emplace_back();
value.decode_to_message(msg.data.back());
break;
default:
break;
}
}
void VoiceAssistantAudio::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
@@ -3099,8 +2997,6 @@ void VoiceAssistantAudio::decode_field(void *self, uint32_t tag, const uint8_t *
msg.data2 = value.data();
msg.data2_len = value.size();
break;
default:
break;
}
}
uint8_t *VoiceAssistantAudio::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -3141,8 +3037,6 @@ void VoiceAssistantTimerEventResponse::decode_field(void *self, uint32_t tag, co
case proto_tag(6, WIRE_TYPE_VARINT):
msg.is_active = value.as_bool();
break;
default:
break;
}
}
void VoiceAssistantAnnounceRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -3162,8 +3056,6 @@ void VoiceAssistantAnnounceRequest::decode_field(void *self, uint32_t tag, const
case proto_tag(4, WIRE_TYPE_VARINT):
msg.start_conversation = value.as_bool();
break;
default:
break;
}
}
uint8_t *VoiceAssistantAnnounceFinished::encode_msg(const void *self,
@@ -3225,8 +3117,6 @@ void VoiceAssistantExternalWakeWord::decode_field(void *self, uint32_t tag, cons
case proto_tag(7, WIRE_TYPE_LENGTH_DELIMITED):
msg.url = StringRef(value.data(), value.size());
break;
default:
break;
}
}
void VoiceAssistantConfigurationRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -3238,8 +3128,6 @@ void VoiceAssistantConfigurationRequest::decode_field(void *self, uint32_t tag,
msg.external_wake_words.emplace_back();
value.decode_to_message(msg.external_wake_words.back());
break;
default:
break;
}
}
uint8_t *VoiceAssistantConfigurationResponse::encode_msg(const void *self,
@@ -3278,8 +3166,6 @@ void VoiceAssistantSetConfiguration::decode_field(void *self, uint32_t tag, cons
case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED):
msg.active_wake_words.push_back(value.as_string());
break;
default:
break;
}
}
#endif
@@ -3289,9 +3175,9 @@ uint8_t *ListEntitiesAlarmControlPanelResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesAlarmControlPanelResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -3361,8 +3247,6 @@ void AlarmControlPanelCommandRequest::decode_field(void *self, uint32_t tag, con
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3371,9 +3255,9 @@ uint8_t *ListEntitiesTextResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTextResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -3442,8 +3326,6 @@ void TextCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3452,9 +3334,9 @@ uint8_t *ListEntitiesDateResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesDateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -3525,8 +3407,6 @@ void DateCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3535,9 +3415,9 @@ uint8_t *ListEntitiesTimeResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -3608,8 +3488,6 @@ void TimeCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3618,13 +3496,13 @@ uint8_t *ListEntitiesEventResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesEventResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
for (const char *it : *msg.event_types) {
pos = ProtoEncode::encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, 9, it, strlen(it));
}
@@ -3680,13 +3558,13 @@ uint8_t *ListEntitiesValveResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesValveResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.assumed_state);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.supports_position);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 11, msg.supports_stop);
@@ -3759,8 +3637,6 @@ void ValveCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3770,9 +3646,9 @@ uint8_t *ListEntitiesDateTimeResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesDateTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
@@ -3835,8 +3711,6 @@ void DateTimeCommandRequest::decode_field(void *self, uint32_t tag, const uint8_
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3845,13 +3719,13 @@ uint8_t *ListEntitiesUpdateResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesUpdateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 7, static_cast<uint32_t>(msg.entity_category));
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 8, msg.device_class);
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.device_id);
#endif
@@ -3926,8 +3800,6 @@ void UpdateCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t
msg.device_id = value.as_varint();
break;
#endif
default:
break;
}
}
#endif
@@ -3940,8 +3812,6 @@ void ZWaveProxyFrame::decode_field(void *self, uint32_t tag, const uint8_t *data
msg.data = value.data();
msg.data_len = value.size();
break;
default:
break;
}
}
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
@@ -3968,8 +3838,6 @@ void ZWaveProxyRequest::decode_field(void *self, uint32_t tag, const uint8_t *da
msg.data = value.data();
msg.data_len = value.size();
break;
default:
break;
}
}
uint8_t *ZWaveProxyRequest::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -4005,9 +3873,9 @@ uint8_t *ListEntitiesInfraredResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesInfraredResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 6, static_cast<uint32_t>(msg.entity_category));
@@ -4065,8 +3933,6 @@ void InfraredRFTransmitRawTimingsRequest::decode_field(void *self, uint32_t tag,
case proto_tag(6, WIRE_TYPE_VARINT):
msg.modulation = value.as_varint();
break;
default:
break;
}
}
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
@@ -4123,9 +3989,9 @@ uint8_t *ListEntitiesRadioFrequencyResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesRadioFrequencyResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 6, static_cast<uint32_t>(msg.entity_category));
@@ -4183,8 +4049,6 @@ void SerialProxyConfigureRequest::decode_field(void *self, uint32_t tag, const u
case proto_tag(6, WIRE_TYPE_VARINT):
msg.data_size = value.as_varint();
break;
default:
break;
}
}
__attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
@@ -4213,8 +4077,6 @@ void SerialProxyWriteRequest::decode_field(void *self, uint32_t tag, const uint8
msg.data = value.data();
msg.data_len = value.size();
break;
default:
break;
}
}
void SerialProxySetModemPinsRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -4228,8 +4090,6 @@ void SerialProxySetModemPinsRequest::decode_field(void *self, uint32_t tag, cons
case proto_tag(2, WIRE_TYPE_VARINT):
msg.line_states = value.as_varint();
break;
default:
break;
}
}
void SerialProxyGetModemPinsRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
@@ -4240,8 +4100,6 @@ void SerialProxyGetModemPinsRequest::decode_field(void *self, uint32_t tag, cons
case proto_tag(1, WIRE_TYPE_VARINT):
msg.instance = value.as_varint();
break;
default:
break;
}
}
uint8_t *SerialProxyGetModemPinsResponse::encode_msg(const void *self,
@@ -4270,8 +4128,6 @@ void SerialProxyRequest::decode_field(void *self, uint32_t tag, const uint8_t *d
case proto_tag(2, WIRE_TYPE_VARINT):
msg.type = static_cast<enums::SerialProxyRequestType>(value.as_varint());
break;
default:
break;
}
}
uint8_t *SerialProxyRequestResponse::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -4302,8 +4158,6 @@ void SerialProxySetModeRequest::decode_field(void *self, uint32_t tag, const uin
case proto_tag(2, WIRE_TYPE_VARINT):
msg.mode = static_cast<enums::SerialProxyMode>(value.as_varint());
break;
default:
break;
}
}
uint8_t *UsbDeviceDescriptor::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
@@ -4372,8 +4226,6 @@ void BluetoothSetConnectionParamsRequest::decode_field(void *self, uint32_t tag,
case proto_tag(5, WIRE_TYPE_VARINT):
msg.timeout = value.as_varint();
break;
default:
break;
}
}
uint8_t *BluetoothSetConnectionParamsResponse::encode_msg(const void *self,
+14 -30
View File
@@ -19,10 +19,6 @@ enum DisconnectReason : uint32_t {
DISCONNECT_REASON_UNSPECIFIED = 0,
DISCONNECT_REASON_PROVISIONING_CLOSED = 1,
};
enum DeviceRelation : uint32_t {
DEVICE_RELATION_VIA = 0,
DEVICE_RELATION_CHILD = 1,
};
enum SerialProxyPortType : uint32_t {
SERIAL_PROXY_PORT_TYPE_TTL = 0,
SERIAL_PROXY_PORT_TYPE_RS232 = 1,
@@ -403,10 +399,10 @@ class InfoResponseProtoMessage : public ProtoMessage {
public:
StringRef object_id{};
uint32_t key{0};
ProgmemStringRef name{};
StringRef name{};
bool disabled_by_default{false};
#ifdef USE_ENTITY_ICON
ProgmemStringRef icon{};
StringRef icon{nullptr, 0}; // null until set, encode only
#endif
enums::EntityCategory entity_category{};
#ifdef USE_DEVICES
@@ -571,7 +567,6 @@ class DeviceInfo final : public ProtoMessage {
uint32_t device_id{0};
StringRef name{};
uint32_t area_id{0};
enums::DeviceRelation relation{};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -871,7 +866,7 @@ class ListEntitiesBinarySensorResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_binary_sensor_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
bool is_status_binary_sensor{false};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -911,17 +906,15 @@ class BinarySensorStateResponse final : public StateResponseProtoMessage {
class ListEntitiesCoverResponse final : public InfoResponseProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 13;
static constexpr uint8_t ESTIMATED_SIZE = 61;
static constexpr uint8_t ESTIMATED_SIZE = 57;
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_cover_response"); }
#endif
bool assumed_state{false};
bool supports_position{false};
bool supports_tilt{false};
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
bool supports_stop{false};
bool supports_stop_tilt{false};
bool supports_toggle{false};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -959,7 +952,7 @@ class CoverStateResponse final : public StateResponseProtoMessage {
class CoverCommandRequest final : public CommandProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 30;
static constexpr uint8_t ESTIMATED_SIZE = 29;
static constexpr uint8_t ESTIMATED_SIZE = 25;
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("cover_command_request"); }
#endif
@@ -968,8 +961,6 @@ class CoverCommandRequest final : public CommandProtoMessage {
bool has_tilt{false};
float tilt{0.0f};
bool stop{false};
bool stop_tilt{false};
bool toggle{false};
void decode(const uint8_t *buffer, size_t length) {
ProtoDecodableMessage::decode_fields(this, buffer, length, &decode_field);
}
@@ -1168,7 +1159,7 @@ class ListEntitiesSensorResponse final : public InfoResponseProtoMessage {
StringRef unit_of_measurement{nullptr, 0}; // null until set, encode only
int32_t accuracy_decimals{0};
bool force_update{false};
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
enums::SensorStateClass state_class{};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -1213,7 +1204,7 @@ class ListEntitiesSwitchResponse final : public InfoResponseProtoMessage {
const LogString *message_name() const override { return LOG_STR("list_entities_switch_response"); }
#endif
bool assumed_state{false};
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -1274,7 +1265,7 @@ class ListEntitiesTextSensorResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_text_sensor_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -1593,12 +1584,6 @@ class ListEntitiesServicesArgument final : public ProtoMessage {
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
ProgmemStringRef example{};
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
bool optional{false};
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
ProgmemStringRef default_value{};
#endif
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -1974,7 +1959,7 @@ class ListEntitiesNumberResponse final : public InfoResponseProtoMessage {
float step{0.0f};
StringRef unit_of_measurement{nullptr, 0}; // null until set, encode only
enums::NumberMode mode{};
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -2230,7 +2215,7 @@ class ListEntitiesButtonResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_button_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -2269,7 +2254,6 @@ class MediaPlayerSupportedFormat final : public ProtoMessage {
uint32_t num_channels{0};
enums::MediaPlayerFormatPurpose purpose{};
uint32_t sample_bytes{0};
uint32_t bitrate{0};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -3476,7 +3460,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_event_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
const ConstVector<const char *, true> *event_types{};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -3519,7 +3503,7 @@ class ListEntitiesValveResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_valve_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
bool assumed_state{false};
bool supports_position{false};
bool supports_stop{false};
@@ -3645,7 +3629,7 @@ class ListEntitiesUpdateResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_update_response"); }
#endif
ProgmemStringRef device_class{};
StringRef device_class{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
+62 -84
View File
@@ -154,16 +154,6 @@ template<> const char *proto_enum_to_string<enums::DisconnectReason>(enums::Disc
return ESPHOME_PSTR("UNKNOWN");
}
}
template<> const char *proto_enum_to_string<enums::DeviceRelation>(enums::DeviceRelation value) {
switch (value) {
case enums::DEVICE_RELATION_VIA:
return ESPHOME_PSTR("DEVICE_RELATION_VIA");
case enums::DEVICE_RELATION_CHILD:
return ESPHOME_PSTR("DEVICE_RELATION_CHILD");
default:
return ESPHOME_PSTR("UNKNOWN");
}
}
template<> const char *proto_enum_to_string<enums::SerialProxyPortType>(enums::SerialProxyPortType value) {
switch (value) {
case enums::SERIAL_PROXY_PORT_TYPE_TTL:
@@ -1015,7 +1005,6 @@ const char *DeviceInfo::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("area_id"), this->area_id);
dump_field(out, ESPHOME_PSTR("relation"), static_cast<enums::DeviceRelation>(this->relation));
return out.c_str();
}
#endif
@@ -1202,12 +1191,12 @@ const char *ListEntitiesBinarySensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesBinarySensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("is_status_binary_sensor"), this->is_status_binary_sensor);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
#ifdef USE_DEVICES
@@ -1231,22 +1220,20 @@ const char *ListEntitiesCoverResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCoverResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("assumed_state"), this->assumed_state);
dump_field(out, ESPHOME_PSTR("supports_position"), this->supports_position);
dump_field(out, ESPHOME_PSTR("supports_tilt"), this->supports_tilt);
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_field(out, ESPHOME_PSTR("supports_stop"), this->supports_stop);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
dump_field(out, ESPHOME_PSTR("supports_stop_tilt"), this->supports_stop_tilt);
dump_field(out, ESPHOME_PSTR("supports_toggle"), this->supports_toggle);
return out.c_str();
}
const char *CoverStateResponse::dump_to(DumpBuffer &out) const {
@@ -1271,8 +1258,6 @@ const char *CoverCommandRequest::dump_to(DumpBuffer &out) const {
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
dump_field(out, ESPHOME_PSTR("stop_tilt"), this->stop_tilt);
dump_field(out, ESPHOME_PSTR("toggle"), this->toggle);
return out.c_str();
}
#endif
@@ -1281,14 +1266,14 @@ const char *ListEntitiesFanResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesFanResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_oscillation"), this->supports_oscillation);
dump_field(out, ESPHOME_PSTR("supports_speed"), this->supports_speed);
dump_field(out, ESPHOME_PSTR("supports_direction"), this->supports_direction);
dump_field(out, ESPHOME_PSTR("supported_speed_count"), this->supported_speed_count);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
for (const auto &it : *this->supported_preset_modes) {
@@ -1336,7 +1321,7 @@ const char *ListEntitiesLightResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLightResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
for (const auto &it : *this->supported_color_modes) {
dump_field(out, ESPHOME_PSTR("supported_color_modes"), static_cast<enums::ColorMode>(it), 4);
}
@@ -1347,7 +1332,7 @@ const char *ListEntitiesLightResponse::dump_to(DumpBuffer &out) const {
}
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
#ifdef USE_DEVICES
@@ -1415,14 +1400,14 @@ const char *ListEntitiesSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("unit_of_measurement"), this->unit_of_measurement);
dump_field(out, ESPHOME_PSTR("accuracy_decimals"), this->accuracy_decimals);
dump_field(out, ESPHOME_PSTR("force_update"), this->force_update);
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("state_class"), static_cast<enums::SensorStateClass>(this->state_class));
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -1447,14 +1432,14 @@ const char *ListEntitiesSwitchResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSwitchResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("assumed_state"), this->assumed_state);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
@@ -1485,13 +1470,13 @@ const char *ListEntitiesTextSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
@@ -1643,12 +1628,6 @@ const char *ListEntitiesServicesArgument::dump_to(DumpBuffer &out) const {
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
dump_progmem_string_field(out, ESPHOME_PSTR("example"), this->example);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
dump_field(out, ESPHOME_PSTR("optional"), this->optional);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
dump_progmem_string_field(out, ESPHOME_PSTR("default_value"), this->default_value);
#endif
return out.c_str();
}
@@ -1722,10 +1701,10 @@ const char *ListEntitiesCameraResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCameraResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
#ifdef USE_DEVICES
@@ -1755,7 +1734,7 @@ const char *ListEntitiesClimateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesClimateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_current_temperature"), this->supports_current_temperature);
dump_field(out, ESPHOME_PSTR("supports_two_point_target_temperature"), this->supports_two_point_target_temperature);
for (const auto &it : *this->supported_modes) {
@@ -1782,7 +1761,7 @@ const char *ListEntitiesClimateResponse::dump_to(DumpBuffer &out) const {
}
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_field(out, ESPHOME_PSTR("visual_current_temperature_step"), this->visual_current_temperature_step);
@@ -1853,9 +1832,9 @@ const char *ListEntitiesWaterHeaterResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesWaterHeaterResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -1907,9 +1886,9 @@ const char *ListEntitiesNumberResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesNumberResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("min_value"), this->min_value);
dump_field(out, ESPHOME_PSTR("max_value"), this->max_value);
@@ -1918,7 +1897,7 @@ const char *ListEntitiesNumberResponse::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_field(out, ESPHOME_PSTR("unit_of_measurement"), this->unit_of_measurement);
dump_field(out, ESPHOME_PSTR("mode"), static_cast<enums::NumberMode>(this->mode));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
@@ -1949,9 +1928,9 @@ const char *ListEntitiesSelectResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSelectResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
for (const auto &it : *this->options) {
dump_field(out, ESPHOME_PSTR("options"), it, 4);
@@ -1988,9 +1967,9 @@ const char *ListEntitiesSirenResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSirenResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
for (const auto &it : *this->tones) {
@@ -2035,9 +2014,9 @@ const char *ListEntitiesLockResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLockResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2076,13 +2055,13 @@ const char *ListEntitiesButtonResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesButtonResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
@@ -2105,16 +2084,15 @@ const char *MediaPlayerSupportedFormat::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("num_channels"), this->num_channels);
dump_field(out, ESPHOME_PSTR("purpose"), static_cast<enums::MediaPlayerFormatPurpose>(this->purpose));
dump_field(out, ESPHOME_PSTR("sample_bytes"), this->sample_bytes);
dump_field(out, ESPHOME_PSTR("bitrate"), this->bitrate);
return out.c_str();
}
const char *ListEntitiesMediaPlayerResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesMediaPlayerResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2502,9 +2480,9 @@ const char *ListEntitiesAlarmControlPanelResponse::dump_to(DumpBuffer &out) cons
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesAlarmControlPanelResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2541,9 +2519,9 @@ const char *ListEntitiesTextResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2581,9 +2559,9 @@ const char *ListEntitiesDateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2621,9 +2599,9 @@ const char *ListEntitiesTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2661,13 +2639,13 @@ const char *ListEntitiesEventResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesEventResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
for (const auto &it : *this->event_types) {
dump_field(out, ESPHOME_PSTR("event_types"), it, 4);
}
@@ -2691,13 +2669,13 @@ const char *ListEntitiesValveResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesValveResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("assumed_state"), this->assumed_state);
dump_field(out, ESPHOME_PSTR("supports_position"), this->supports_position);
dump_field(out, ESPHOME_PSTR("supports_stop"), this->supports_stop);
@@ -2733,9 +2711,9 @@ const char *ListEntitiesDateTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2769,13 +2747,13 @@ const char *ListEntitiesUpdateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesUpdateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
dump_progmem_string_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
#ifdef USE_DEVICES
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
#endif
@@ -2832,9 +2810,9 @@ const char *ListEntitiesInfraredResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesInfraredResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
@@ -2890,9 +2868,9 @@ const char *ListEntitiesRadioFrequencyResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesRadioFrequencyResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_progmem_string_field(out, ESPHOME_PSTR("icon"), this->icon);
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
dump_field(out, ESPHOME_PSTR("entity_category"), static_cast<enums::EntityCategory>(this->entity_category));
+1 -15
View File
@@ -301,7 +301,7 @@ constexpr uint32_t VARINT_MAX_1_BYTE = 1 << 7; // 128
constexpr uint32_t VARINT_MAX_2_BYTE = 1 << 14; // 16384
/// Generated encode body: writes the fields at pos, returns the cursor past them.
using ProtoEncodeFn = uint8_t *(*)(const void *, uint8_t *PROTO_ENCODE_DEBUG_PARAM);
using ProtoEncodeFn = uint8_t *(*) (const void *, uint8_t *PROTO_ENCODE_DEBUG_PARAM);
/// Static encode helpers for the generated encode bodies. Each takes the write cursor by value and
/// returns it advanced, so outlined calls at -Os chain through the return register instead of a
@@ -409,10 +409,6 @@ class ProtoEncode {
/// Tag must be a single-byte varint (< 128). Always encodes (no zero check).
[[nodiscard]] static inline uint8_t *encode_short_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint8_t tag, const StringRef &ref) {
#ifdef USE_ESP8266
// memcpy_P reads RAM too, and sharing the progmem copy is smaller than an inline memcpy
return encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, tag, ProgmemStringRef(ref));
#else
#ifdef ESPHOME_DEBUG_API
assert(ref.size() < 128 && "encode_short_string_force: string exceeds max_data_length < 128");
#endif
@@ -421,7 +417,6 @@ class ProtoEncode {
pos[1] = static_cast<uint8_t>(ref.size());
std::memcpy(pos + 2, ref.c_str(), ref.size());
return pos + 2 + ref.size();
#endif
}
/// encode_short_string_force for a string that may be in flash (PROGMEM).
[[nodiscard]] static inline uint8_t *encode_short_progmem_string_force(
@@ -501,22 +496,13 @@ class ProtoEncode {
/// encode_progmem_bytes for a string field that may be in flash.
[[nodiscard]] static inline uint8_t *encode_progmem_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
#ifdef USE_ESP8266
return encode_progmem_bytes(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
#else
// In RAM here, so share the plain string encoder
return encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.ram_ref());
#endif
}
[[nodiscard]] static inline uint8_t *encode_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
#ifdef USE_ESP8266
return encode_progmem_bytes_force(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
#else
return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, ref.ram_ref());
#endif
}
[[nodiscard]] static inline uint8_t *encode_uint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, uint32_t value) {
-7
View File
@@ -31,13 +31,6 @@ ListEntitiesServicesResponse UserServiceStatic::encode_list_service_response_(
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
arg.description = this->str_(base + 1);
arg.example = this->str_(base + 2);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
// The default slot doubles as the optional flag: nullptr is required, "" optional without a default
const char *d = progmem_read_ptr(&this->strings_[base + USER_ACTION_ARG_BASE_STRINGS]);
arg.optional = d != nullptr;
if (d != nullptr)
arg.default_value = ProgmemStringRef(d, ESPHOME_strlen_P(d));
#endif
}
return msg;
+3 -10
View File
@@ -40,20 +40,13 @@ template<typename T> enums::ServiceArgType to_service_arg_type();
// All strings live in one PROGMEM pointer table emitted by codegen (see _action_strings in
// __init__.py), so each service costs a single pointer of RAM. Layout: the action name, then
// each argument name; with USE_API_USER_DEFINED_ACTION_METADATA the action description follows
// the name and every argument is (name, description, example); with
// USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS every argument ends with its default: nullptr for a
// required argument, "" for an optional one without a default. Unset is nullptr.
// the name and every argument is (name, description, example). Unset metadata is nullptr.
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
static constexpr size_t USER_ACTION_HEADER_STRINGS = 2;
static constexpr size_t USER_ACTION_ARG_BASE_STRINGS = 3;
static constexpr size_t USER_ACTION_ARG_STRINGS = 3;
#else
static constexpr size_t USER_ACTION_HEADER_STRINGS = 1;
static constexpr size_t USER_ACTION_ARG_BASE_STRINGS = 1;
#endif
#ifdef USE_API_USER_DEFINED_ACTION_OPTIONAL_ARGS
static constexpr size_t USER_ACTION_ARG_STRINGS = USER_ACTION_ARG_BASE_STRINGS + 1;
#else
static constexpr size_t USER_ACTION_ARG_STRINGS = USER_ACTION_ARG_BASE_STRINGS;
static constexpr size_t USER_ACTION_ARG_STRINGS = 1;
#endif
class UserServiceStatic : public UserServiceDescriptor {
public:
+2 -2
View File
@@ -30,10 +30,10 @@ void AQISensor::dump_config() {
ESP_LOGCONFIG(TAG, " Extended Range: %s",
this->extended_range_ ? LOG_STR_LITERAL("enabled") : LOG_STR_LITERAL("disabled"));
if (this->pm_2_5_sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", LOG_STR_ARG(this->pm_2_5_sensor_->get_log_name()));
ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", this->pm_2_5_sensor_->get_name().c_str());
}
if (this->pm_10_0_sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, " PM10 Sensor: '%s'", LOG_STR_ARG(this->pm_10_0_sensor_->get_log_name()));
ESP_LOGCONFIG(TAG, " PM10 Sensor: '%s'", this->pm_10_0_sensor_->get_name().c_str());
}
LOG_SENSOR(" ", "AQI", this);
}
@@ -23,6 +23,7 @@ AS5600Sensor = as5600_ns.class_("AS5600Sensor", sensor.Sensor, cg.PollingCompone
CONF_RAW_POSITION = "raw_position"
CONF_SLOW_FILTER = "slow_filter"
CONF_FAST_FILTER = "fast_filter"
CONF_PWM_FREQUENCY = "pwm_frequency"
CONF_BURN_COUNT = "burn_count"
CONF_START_POSITION = "start_position"
CONF_END_POSITION = "end_position"
+2 -2
View File
@@ -639,7 +639,7 @@ float ATM90E32Component::get_chip_temperature_() {
void ATM90E32Component::press_calibration_button(EntityBase *entity, void (ATM90E32Component::*action)(),
const LogString *hint, const LogString *hint2) {
ESP_LOGI(TAG, "%s", LOG_STR_ARG(entity->get_log_name()));
ESP_LOGI(TAG, "%s", entity->get_name().c_str());
if (hint != nullptr) {
ESP_LOGI(TAG, "%s", LOG_STR_ARG(hint));
}
@@ -1295,7 +1295,7 @@ void ATM90E32Component::check_phase_status() {
if (!status.empty()) {
status.pop_back(); // remove space
status.pop_back(); // remove semicolon
ESP_LOGW(TAG, "%s: %s", LOG_STR_ARG(sensor->get_log_name()), status.c_str());
ESP_LOGW(TAG, "%s: %s", sensor->get_name().c_str(), status.c_str());
sensor->publish_state(status);
} else {
sensor->publish_state("Okay");
-2
View File
@@ -168,8 +168,6 @@ bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
case BALLU_AUTO:
this->mode = climate::CLIMATE_MODE_AUTO;
break;
default:
break;
}
}
@@ -211,7 +211,7 @@ void BedJetClimate::control(const ClimateCall &call) {
} else if (fan_mode == CLIMATE_FAN_HIGH) {
result = this->parent_->set_fan_speed(75);
} else {
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", LOG_STR_ARG(this->get_log_name()),
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", this->get_name().c_str(),
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
return;
}
@@ -223,7 +223,7 @@ void BedJetClimate::control(const ClimateCall &call) {
auto fan_mode = call.get_custom_fan_mode();
auto fan_index = bedjet_fan_speed_to_step(fan_mode.c_str());
if (fan_index <= 19) {
ESP_LOGV(TAG, "[%s] Converted fan mode %.*s to bedjet fan step %d", LOG_STR_ARG(this->get_log_name()),
ESP_LOGV(TAG, "[%s] Converted fan mode %.*s to bedjet fan step %d", this->get_name().c_str(),
(int) fan_mode.size(), fan_mode.c_str(), fan_index);
bool result = this->parent_->set_fan_index(fan_index);
if (result) {
@@ -236,7 +236,7 @@ void BedJetClimate::control(const ClimateCall &call) {
void BedJetClimate::on_bedjet_state(bool is_ready) {}
void BedJetClimate::on_status(const BedjetStatusPacket *data) {
ESP_LOGV(TAG, "[%s] Handling on_status with data=%p", LOG_STR_ARG(this->get_log_name()), (void *) data);
ESP_LOGV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
auto converted_temp = bedjet_temp_to_c(data->target_temp_step);
if (converted_temp > 0)
@@ -310,11 +310,11 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
break;
default:
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", LOG_STR_ARG(this->get_log_name()), data->mode);
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", this->get_name().c_str(), data->mode);
break;
}
ESP_LOGV(TAG, "[%s] After on_status, new mode=%s", LOG_STR_ARG(this->get_log_name()),
ESP_LOGV(TAG, "[%s] After on_status, new mode=%s", this->get_name().c_str(),
LOG_STR_ARG(climate_mode_to_string(this->mode)));
// FIXME: compare new state to previous state.
this->publish_state();
@@ -351,11 +351,11 @@ bool BedJetClimate::update_status_() {
}
void BedJetClimate::update() {
ESP_LOGD(TAG, "[%s] update()", LOG_STR_ARG(this->get_log_name()));
ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
// TODO: if the hub component is already polling, do we also need to include polling?
// We're already going to get on_status() at the hub's polling interval.
auto result = this->update_status_();
ESP_LOGD(TAG, "[%s] update_status result=%s", LOG_STR_ARG(this->get_log_name()), result ? "true" : "false");
ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
}
} // namespace esphome::bedjet
+3 -3
View File
@@ -53,7 +53,7 @@ void BedJetFan::control(const fan::FanCall &call) {
}
void BedJetFan::on_status(const BedjetStatusPacket *data) {
ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", LOG_STR_ARG(this->get_log_name()), (void *) data);
ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
bool did_change = false;
bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT;
@@ -96,11 +96,11 @@ bool BedJetFan::update_status_() {
}
void BedJetFan::update() {
ESP_LOGD(TAG, "[%s] update()", LOG_STR_ARG(this->get_log_name()));
ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
// TODO: if the hub component is already polling, do we also need to include polling?
// We're already going to get on_status() at the hub's polling interval.
auto result = this->update_status_();
ESP_LOGD(TAG, "[%s] update_status result=%s", LOG_STR_ARG(this->get_log_name()), result ? "true" : "false");
ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
}
/** Resets states to defaults. */
+1 -1
View File
@@ -151,7 +151,7 @@ void BH1750Sensor::loop() {
break;
}
ESP_LOGV(TAG, "'%s': Illuminance=%.1flx", LOG_STR_ARG(this->get_log_name()), lx);
ESP_LOGV(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), lx);
this->status_clear_warning();
this->publish_state(lx);
this->state_ = IDLE;
@@ -36,7 +36,7 @@ bool BinarySensor::set_new_state(const optional<bool> &new_state) {
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
ControllerRegistry::notify_binary_sensor_update(this);
#endif
ESP_LOGV(TAG, "'%s' >> %s", LOG_STR_ARG(this->get_log_name()), ONOFFMAYBE(new_state));
ESP_LOGV(TAG, "'%s' >> %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
return true;
}
return false;
@@ -47,7 +47,7 @@ void BinarySensorMap::process_group_() {
}
} else if (this->last_mask_ != 0ULL) {
// no buttons are pressed and the states have changed since last run, so publish NAN
ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", LOG_STR_ARG(this->get_log_name()));
ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
this->publish_state(NAN);
}

Some files were not shown because too many files have changed in this diff Show More