mirror of
https://github.com/esphome/esphome.git
synced 2026-10-22 13:56:10 +00:00
Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46bc9d6454 | ||
|
|
65a2f185d6 | ||
|
|
c0cbf9cdfd | ||
|
|
89eb58edce | ||
|
|
442bd4975e | ||
|
|
808075bf93 | ||
|
|
a2fbedeac3 | ||
|
|
8b951fa620 | ||
|
|
b18a5b09e7 | ||
|
|
6c4a42c069 | ||
|
|
18120cc4a8 | ||
|
|
7719179dcd | ||
|
|
74e1057268 | ||
|
|
6304411341 | ||
|
|
fd902a646a | ||
|
|
09b580e4ea | ||
|
|
89536632dc | ||
|
|
3da581233a | ||
|
|
ff1c448ecc | ||
|
|
1430428f70 | ||
|
|
91f7b0d147 | ||
|
|
5bccdab01d | ||
|
|
68df9248ed | ||
|
|
0a501b7ef5 | ||
|
|
b65395174c | ||
|
|
da0fed0517 |
@@ -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), `homeassistant_light/` (direct bulb driver), `usb_audio/` (URL player).
|
||||
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).
|
||||
|
||||
## Templates in `$SKILL/templates`
|
||||
|
||||
@@ -130,7 +130,8 @@ capture decoder), `api_outgoing/` (wake cycle and Wi-Fi drop timing readers), `h
|
||||
`bk72xx-rtc-prefs-dut.yaml`, `bk72xx-crash-probe-dut.yaml`, `dut-ap-only-wipe-once-bk72xx.yaml`,
|
||||
`homeassistant-light-dut.yaml`, `deep-sleep-outgoing-dut.yaml`, `outgoing-wifi-drop-dut.yaml`,
|
||||
`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`.
|
||||
`cast-pro-1-sendspin-control.yaml`, `aux-2-preprod-stock-dut.yaml`, `aux-2-preprod-usb-audio-dut.yaml`,
|
||||
`dfrobot-sen0395-dut.yaml`.
|
||||
|
||||
## When something looks wrong
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ One file per component, named after it:
|
||||
- [`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
|
||||
- [`ld2450.md`](ld2450.md): polygon zones
|
||||
- [`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
|
||||
- [`mqtt.md`](mqtt.md): broker bench, QoS, reconnect, crash log over MQTT
|
||||
- [`nrf52.md`](nrf52.md): dongle board support
|
||||
@@ -25,5 +25,6 @@ One file per component, named after it:
|
||||
- [`zephyr_coredump.md`](zephyr_coredump.md): crash and decode on nRF52 boards
|
||||
- [`zwave_proxy.md`](zwave_proxy.md): frame checksums on a live proxy
|
||||
- [`globals.md`](globals.md): restoring globals with storage rtc and flash across every reset kind
|
||||
- [`homeassistant.md`](homeassistant.md): light platform mirrored from a local Home Assistant
|
||||
- [`homeassistant.md`](homeassistant.md): light platform mirrored from a local Home Assistant, held reports versus later calls
|
||||
- [`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
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# dfrobot_sen0395
|
||||
|
||||
## DFRobot SEN0395 mmWave on an ESP32 DevKit V1 (#20906, #20979)
|
||||
|
||||
Wiring: V to VIN (5 V, the module wants 3.6 to 5 V), G to GND, sensor TX to GPIO16 (RX2), sensor RX to GPIO17
|
||||
(TX2), IO2 to GPIO18 (presence output, a gpio binary sensor), I1 unconnected. `templates/dfrobot-sen0395-dut.yaml`
|
||||
has the hub, both detection binary sensors, the four switches, settings buttons and junk-line buttons.
|
||||
`scripts/dfrobot_sen0395/ctl.py HOST KEY press:NAME sw:NAME=on|off wait:S` drives it and prints binary sensor and
|
||||
switch updates; `cases.sh DEV_IP KEY CAPTURE LABEL` runs the whole flow against a running serial capture. Flash by
|
||||
USB, `serial_capture.py PORT 540 CAPTURE --reset`, then the cases; compare the `Sending command` and `Message:` lines
|
||||
and the counts of `Done`, `Error`, prompts and frames between builds. The module echoes every command, so the echoed
|
||||
lines are the bytes the component sent and can be compared between builds as a set.
|
||||
|
||||
Module facts: it answers `Done` or `Error` and then the `leapMMW:/>` prompt without a line end; a junk line gets
|
||||
`<line> is not recognized as a CLI command` (36 chars longer than the line, so 220 and 240 byte junk gives 256 and
|
||||
276 char replies); input lines of 256 bytes or more make the module assert (`..\User_code\main.c line:232`) and
|
||||
restart, after which the sensor is stopped; a factory reset restarts the module about 7 s later; `sensorStop` on a
|
||||
stopped sensor answers `sensor stopped already` then `Error`; a settings press takes 5 to 6 s (stop, change, save,
|
||||
start). Free heap on the ESP32 is Wi-Fi noise (192 to 198 kB) and says nothing about the component.
|
||||
Results: https://github.com/esphome/esphome/pull/20979#issuecomment-6279129146 and
|
||||
https://github.com/esphome/esphome/pull/20906#issuecomment-6279129604
|
||||
@@ -33,3 +33,15 @@ connected but not yet subscribed is dropped by the api server (`client has not s
|
||||
fallback to an off report the mirror keeps the unsent brightness; a mirror that never saw its light on since boot has
|
||||
no levels to restore. Results: https://github.com/esphome/esphome/pull/11354#issuecomment-6253630336 and
|
||||
https://github.com/esphome/esphome/pull/11354#issuecomment-6254161409
|
||||
|
||||
## Held report versus a later call (#21144)
|
||||
|
||||
A report that arrives during a transition is held and applied when the transition ends, unless a call came after
|
||||
it. `scripts/homeassistant_light/heldcase.py DEV_IP KEY BULB_IP same|many [BULB_KEY]` (a key of `-` is plaintext) keeps one API connection to the mirror
|
||||
device and one to the bulb so the steps land inside a 3 s fade: press "A 50 fade 3s", 0.7 s later set the bulb to
|
||||
90% directly (the report is held), 1.4 s in press the same fade again (or "A many calls", 16 calls in one loop
|
||||
pass). Pass: the mirror and the bulb end at the second call's target. Only `same` reproduces the bug: a build
|
||||
without the fix logs `Applying the report that arrived during the transition` and the bulb stays at 90%. `many`
|
||||
ends at 100% on both builds because the last call's target differs from the held report, so it is a regression
|
||||
check only, not a reproduction. Separate button presses are too slow (about 1.5 s each) to land inside the fade.
|
||||
The mirrors are not API entities, so read their state from the "Log state" button's line. Results: https://github.com/esphome/esphome/pull/21144#issuecomment-6278257695
|
||||
|
||||
@@ -5,3 +5,11 @@
|
||||
To make a target "leave" without anyone moving, set the radar's Zone-1 numbers over the target and
|
||||
switch the zone type to `Filter` (the count drops to 0 within 0.5 s); set it back to `Disabled` after.
|
||||
Writing those numbers blocks the ld2450 loop about 50 ms.
|
||||
|
||||
## Zone numbers round trip (#21146)
|
||||
|
||||
`scripts/ld2450/zonedrive.py HOST KEY(- for plaintext) set:NAME=VALUE sel:NAME=OPTION wait:S list` sets numbers and
|
||||
selects over the API and prints every LD2450 state update. After each set the hub sends the set zone command, queries
|
||||
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
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
# cases.sh DEV_IP KEY CAPTURE LABEL : the SEN0395 flow (settings, switches, junk lines, factory reset, reset) against
|
||||
# templates/dfrobot-sen0395-dut.yaml, with a serial capture of the device running into CAPTURE
|
||||
D=$1; K=$2; LOG=$3; LABEL=$4; PY=${PYTHON:-python3}; CTL="$(dirname "$0")/ctl.py"
|
||||
show() { tail -n +$(($1+1)) "$LOG" | sed 's/\x1b\[[0-9;]*m//g' | grep -a 'dfrobot\|Detected\|\[W\]\|\[E\]\|sen\]' | grep -a -v 'JYBSS\|took a long time' | cut -c1-200; }
|
||||
step() { n0=$(wc -l < "$LOG"); echo "=== $LABEL: $1 ($(date +%T))"; shift; $PY "$CTL" "$D" "$K" "$@" || { rc=$?; echo "driver failed ($rc), aborting"; exit $rc; }; echo "--- device log:"; show "$n0"; }
|
||||
frames() { echo "frames: $(tail -n +$(($1+1)) "$LOG" | grep -a -c 'JYBSS,1') present, $(tail -n +$(($1+1)) "$LOG" | grep -a -c 'JYBSS,0') absent"; }
|
||||
n0=$(wc -l < "$LOG"); echo "=== $LABEL: 20 s of frames ($(date +%T))"; sleep 20; frames "$n0"
|
||||
step "range 0 to 3 m" "press:Range 0 to 3m" wait:12
|
||||
step "range 0 to 9 m" "press:Range 0 to 9m" wait:12
|
||||
step "latency 1 s / 2 s" "press:Latency 1s 2s" wait:12
|
||||
step "latency 0 / 0" "press:Latency 0 0" wait:12
|
||||
step "sensitivity 7" "press:Sensitivity 7" wait:12
|
||||
step "LED off then on" "sw:LED=off" wait:10 "sw:LED=on" wait:10
|
||||
step "sensor active off then on" "sw:Sensor active=off" wait:10 "sw:Sensor active=on" wait:10
|
||||
step "presence via UART off then on" "sw:Presence via UART=off" wait:10 "sw:Presence via UART=on" wait:10
|
||||
step "start after boot off then on" "sw:Start after boot=off" wait:10 "sw:Start after boot=on" wait:10
|
||||
for n in 100 160 200 220 240 256; do step "junk line of $n bytes" "press:Junk $n" wait:7; done
|
||||
step "junk 220 then a command" "press:Junk 220" wait:6 "press:Range 0 to 9m" wait:12
|
||||
step "factory reset" "press:Factory reset" wait:15
|
||||
step "reset" "press:Reset" wait:15
|
||||
n0=$(wc -l < "$LOG"); echo "=== $LABEL: 20 s of frames after ($(date +%T))"; sleep 20; frames "$n0"
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Drive the SEN0395 test device over the API and print its binary sensor and switch updates.
|
||||
usage: ctl.py HOST KEY(- for plaintext) OP [OP ...] OP: press:NAME | sw:NAME=on|off | wait:SECONDS
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import time
|
||||
|
||||
from aioesphomeapi import (
|
||||
APIClient,
|
||||
BinarySensorState,
|
||||
ButtonInfo,
|
||||
SwitchInfo,
|
||||
SwitchState,
|
||||
)
|
||||
|
||||
HOST, KEY = sys.argv[1], sys.argv[2]
|
||||
OPS = sys.argv[3:]
|
||||
T0 = time.time()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
c = APIClient(HOST, 6053, None, noise_psk=None if KEY == "-" else KEY)
|
||||
await c.connect(login=True)
|
||||
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)}
|
||||
switches = {e.name: e for e in ents if isinstance(e, SwitchInfo)}
|
||||
|
||||
def on_state(s) -> None:
|
||||
e = by_key.get(s.key)
|
||||
if isinstance(s, BinarySensorState):
|
||||
print(
|
||||
f"{time.time() - T0:7.3f} BIN {e.name} = {'ON' if s.state else 'off'}"
|
||||
)
|
||||
elif isinstance(s, SwitchState):
|
||||
print(
|
||||
f"{time.time() - T0:7.3f} SW {e.name} = {'on' if s.state else 'off'}"
|
||||
)
|
||||
|
||||
c.subscribe_states(on_state)
|
||||
await asyncio.sleep(1.0)
|
||||
for op in OPS:
|
||||
kind, _, rest = op.partition(":")
|
||||
if kind == "wait":
|
||||
await asyncio.sleep(float(rest))
|
||||
elif kind == "press":
|
||||
if rest not in buttons:
|
||||
sys.exit(f"no button {rest!r}; have {sorted(buttons)}")
|
||||
print(f"{time.time() - T0:7.3f} >>> press {rest}")
|
||||
c.button_command(buttons[rest].key)
|
||||
elif kind == "sw":
|
||||
name, _, val = rest.partition("=")
|
||||
if name not in switches:
|
||||
sys.exit(f"no switch {name!r}; have {sorted(switches)}")
|
||||
print(f"{time.time() - T0:7.3f} >>> switch {name} {val}")
|
||||
c.switch_command(switches[name].key, val == "on")
|
||||
else:
|
||||
sys.exit(f"unknown op {op!r}")
|
||||
await c.disconnect()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Held-report cases for esphome#21144 with persistent API connections, so the steps land inside the 3 s fade.
|
||||
usage: heldcase.py DEV_IP KEY BULB_IP same|many [BULB_KEY] (KEY or BULB_KEY of - means plaintext)
|
||||
t0 press "A 50 fade 3s" on the mirror device (3 s fade to 50%)
|
||||
t0+0.7 set the bulb directly to 90% (Home Assistant reports it to the mirror during the fade: held)
|
||||
t0+1.4 same: press "A 50 fade 3s" again (same target as the running fade)
|
||||
many: press "A many calls" (16 calls in one loop pass, the last one 100% with a 1 s fade)
|
||||
t0+6.5 read the bulb, press Log state so the mirror prints its state
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import time
|
||||
|
||||
from aioesphomeapi import APIClient, ButtonInfo, LightInfo, LightState
|
||||
|
||||
DEV, KEY, BULB, CASE = sys.argv[1:5]
|
||||
BULB_KEY = sys.argv[5] if len(sys.argv) > 5 else "-"
|
||||
if CASE not in ("same", "many"):
|
||||
sys.exit(f"case must be same or many, got {CASE!r}")
|
||||
T0 = time.time()
|
||||
|
||||
|
||||
def stamp() -> str:
|
||||
return f"{time.time() - T0:6.3f}"
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
dev = APIClient(DEV, 6053, None, noise_psk=None if KEY == "-" else KEY)
|
||||
bulb = APIClient(BULB, 6053, None, noise_psk=None if BULB_KEY == "-" else BULB_KEY)
|
||||
await dev.connect(login=True)
|
||||
await bulb.connect(login=True)
|
||||
dents, _ = await dev.list_entities_services()
|
||||
bents, _ = await bulb.list_entities_services()
|
||||
buttons = {e.name: e for e in dents if isinstance(e, ButtonInfo)}
|
||||
blight = next(e for e in bents if isinstance(e, LightInfo))
|
||||
states = {}
|
||||
|
||||
def on_bulb(s) -> None:
|
||||
if isinstance(s, LightState) and s.key == blight.key:
|
||||
states["bulb"] = s
|
||||
print(f"{stamp()} bulb state: on={s.state} bri={s.brightness:.2f}")
|
||||
|
||||
bulb.subscribe_states(on_bulb)
|
||||
await asyncio.sleep(1.0)
|
||||
print(f"{stamp()} >>> bulb to 100% (precondition)")
|
||||
bulb.light_command(blight.key, state=True, brightness=1.0)
|
||||
await asyncio.sleep(4.0)
|
||||
t = time.time()
|
||||
print(f"{stamp()} >>> t0 press A 50 fade 3s")
|
||||
dev.button_command(buttons["A 50 fade 3s"].key)
|
||||
await asyncio.sleep(0.7 - (time.time() - t))
|
||||
print(f"{stamp()} >>> bulb direct to 90%")
|
||||
bulb.light_command(blight.key, state=True, brightness=0.9)
|
||||
await asyncio.sleep(1.4 - (time.time() - t))
|
||||
name = "A 50 fade 3s" if CASE == "same" else "A many calls"
|
||||
print(f"{stamp()} >>> press {name}")
|
||||
dev.button_command(buttons[name].key)
|
||||
await asyncio.sleep(6.5 - (time.time() - t))
|
||||
print(f"{stamp()} >>> press Log state (the mirror's state is in the device log)")
|
||||
dev.button_command(buttons["Log state"].key)
|
||||
await asyncio.sleep(1.0)
|
||||
b = states.get("bulb")
|
||||
print(
|
||||
f"{stamp()} RESULT bulb "
|
||||
+ (f"bri={b.brightness:.2f}" if b is not None else "no state")
|
||||
)
|
||||
await dev.disconnect()
|
||||
await bulb.disconnect()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
"""Drive LD2450 numbers and selects over the API while printing every LD2450 state update.
|
||||
usage: zonedrive.py HOST KEY(- for plaintext) OP [OP ...]
|
||||
OP: set:NAME=VALUE | sel:NAME=OPTION | wait:SECONDS | list
|
||||
Every state update of an entity whose name contains "LD2450" is printed with a timestamp.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import time
|
||||
|
||||
from aioesphomeapi import (
|
||||
APIClient,
|
||||
NumberInfo,
|
||||
NumberState,
|
||||
SelectInfo,
|
||||
SelectState,
|
||||
SensorInfo,
|
||||
SensorState,
|
||||
)
|
||||
|
||||
HOST, KEY = 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:
|
||||
c = APIClient(HOST, 6053, None, noise_psk=None if KEY == "-" else KEY)
|
||||
await c.connect(login=True)
|
||||
ents, _ = await c.list_entities_services()
|
||||
by_key = {e.key: e for e in ents}
|
||||
numbers = {e.name: e for e in ents if isinstance(e, NumberInfo)}
|
||||
selects = {e.name: e for e in ents if isinstance(e, SelectInfo)}
|
||||
sensors = {e.name: e for e in ents if isinstance(e, SensorInfo)}
|
||||
|
||||
def on_state(s) -> None:
|
||||
e = by_key.get(s.key)
|
||||
if e is None or "LD2450" not in e.name:
|
||||
return
|
||||
if isinstance(s, NumberState):
|
||||
print(
|
||||
f"{stamp()} NUM {e.name} = {s.state:.0f}{' (missing)' if s.missing_state else ''}"
|
||||
)
|
||||
elif isinstance(s, SelectState):
|
||||
print(f"{stamp()} SEL {e.name} = {s.state}")
|
||||
elif isinstance(s, SensorState) and "Count" in e.name:
|
||||
print(f"{stamp()} CNT {e.name} = {s.state:.0f}")
|
||||
|
||||
c.subscribe_states(on_state)
|
||||
await asyncio.sleep(1.5)
|
||||
for op in OPS:
|
||||
if op == "list":
|
||||
for n in sorted(numbers):
|
||||
print("number:", n)
|
||||
for n in sorted(selects):
|
||||
print("select:", n, selects[n].options)
|
||||
for n in sorted(sensors):
|
||||
if "Count" in n:
|
||||
print("sensor:", n)
|
||||
continue
|
||||
kind, _, rest = op.partition(":")
|
||||
if kind == "wait":
|
||||
await asyncio.sleep(float(rest))
|
||||
continue
|
||||
name, _, value = rest.partition("=")
|
||||
if kind == "set":
|
||||
if name not in numbers:
|
||||
sys.exit(f"no number named {name!r}; have {sorted(numbers)}")
|
||||
print(f"{stamp()} >>> set {name} = {value}")
|
||||
c.number_command(numbers[name].key, float(value))
|
||||
elif kind == "sel":
|
||||
if name not in selects:
|
||||
sys.exit(f"no select named {name!r}; have {sorted(selects)}")
|
||||
print(f"{stamp()} >>> select {name} = {value}")
|
||||
c.select_command(selects[name].key, value)
|
||||
else:
|
||||
sys.exit(f"unknown op {op!r}")
|
||||
await asyncio.sleep(0.3)
|
||||
await c.disconnect()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,186 @@
|
||||
# DFRobot SEN0395 on an ESP32 DevKit V1: V to VIN (5 V), G to GND, sensor TX to GPIO16 (RX2), sensor RX to GPIO17 (TX2),
|
||||
# IO2 to GPIO18, I1 unconnected. Settings buttons, the four switches and junk-line buttons whose echo and
|
||||
# "is not recognized" reply come back as lines of known length (220 and 240 bytes give replies over 252 chars).
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
logs:
|
||||
api: DEBUG
|
||||
api.connection: DEBUG
|
||||
api.service: DEBUG
|
||||
wifi: DEBUG
|
||||
mdns: DEBUG
|
||||
scheduler: DEBUG
|
||||
component: DEBUG
|
||||
esp32: DEBUG
|
||||
uart: DEBUG
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_key
|
||||
reboot_timeout: 0s
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
|
||||
uart:
|
||||
id: mmwave_uart
|
||||
tx_pin: GPIO17
|
||||
rx_pin: GPIO16
|
||||
baud_rate: 115200
|
||||
|
||||
dfrobot_sen0395:
|
||||
id: mmwave
|
||||
uart_id: mmwave_uart
|
||||
|
||||
binary_sensor:
|
||||
- platform: dfrobot_sen0395
|
||||
dfrobot_sen0395_id: mmwave
|
||||
name: Detected via UART
|
||||
id: uart_detected
|
||||
- platform: gpio
|
||||
name: Detected via GPIO
|
||||
device_class: motion
|
||||
pin:
|
||||
number: GPIO18
|
||||
mode: INPUT_PULLDOWN
|
||||
|
||||
switch:
|
||||
- platform: dfrobot_sen0395
|
||||
dfrobot_sen0395_id: mmwave
|
||||
type: sensor_active
|
||||
name: Sensor active
|
||||
- platform: dfrobot_sen0395
|
||||
dfrobot_sen0395_id: mmwave
|
||||
type: presence_via_uart
|
||||
name: Presence via UART
|
||||
- platform: dfrobot_sen0395
|
||||
dfrobot_sen0395_id: mmwave
|
||||
type: turn_on_led
|
||||
name: LED
|
||||
- platform: dfrobot_sen0395
|
||||
dfrobot_sen0395_id: mmwave
|
||||
type: start_after_boot
|
||||
name: Start after boot
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: Range 0 to 3m
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
detection_segments:
|
||||
- [0cm, 3m]
|
||||
- platform: template
|
||||
name: Range 0 to 9m
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
detection_segments:
|
||||
- [0cm, 9m]
|
||||
- platform: template
|
||||
name: Latency 1s 2s
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
output_latency:
|
||||
delay_after_detect: 1s
|
||||
delay_after_disappear: 2s
|
||||
- platform: template
|
||||
name: Latency 0 0
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
output_latency:
|
||||
delay_after_detect: 0s
|
||||
delay_after_disappear: 0s
|
||||
- platform: template
|
||||
name: Sensitivity 7
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
sensitivity: 7
|
||||
- platform: template
|
||||
name: Factory reset
|
||||
on_press:
|
||||
- dfrobot_sen0395.settings:
|
||||
id: mmwave
|
||||
factory_reset: true
|
||||
- platform: template
|
||||
name: Reset
|
||||
on_press:
|
||||
- dfrobot_sen0395.reset:
|
||||
id: mmwave
|
||||
- platform: template
|
||||
name: Junk 100
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(100, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
- platform: template
|
||||
name: Junk 160
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(160, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
- platform: template
|
||||
name: Junk 200
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(200, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
- platform: template
|
||||
name: Junk 220
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(220, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
- platform: template
|
||||
name: Junk 240
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(240, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
- platform: template
|
||||
name: Junk 256
|
||||
on_press:
|
||||
- uart.write:
|
||||
id: mmwave_uart
|
||||
data: !lambda |-
|
||||
std::vector<uint8_t> v(256, 'x');
|
||||
v.push_back('\r');
|
||||
v.push_back('\n');
|
||||
return v;
|
||||
|
||||
interval:
|
||||
- interval: 30s
|
||||
then:
|
||||
- lambda: 'ESP_LOGI("sen", "alive uart_detected=%d heap=%u", id(uart_detected).state, (unsigned) esp_get_free_heap_size());'
|
||||
@@ -124,6 +124,20 @@ button:
|
||||
name: B off
|
||||
on_press:
|
||||
- light.turn_off: bulb_b
|
||||
- platform: template
|
||||
name: A 50 fade 3s
|
||||
on_press:
|
||||
- light.turn_on:
|
||||
id: bulb_a
|
||||
brightness: 50%
|
||||
transition_length: 3s
|
||||
- platform: template
|
||||
name: A many calls
|
||||
on_press:
|
||||
- lambda: |-
|
||||
for (int i = 0; i < 16; i++) {
|
||||
id(bulb_a).make_call().set_brightness(i == 15 ? 1.0f : 0.6f).set_transition_length(1000).perform();
|
||||
}
|
||||
- platform: template
|
||||
name: Log state
|
||||
on_press:
|
||||
|
||||
@@ -80,13 +80,15 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
```python
|
||||
# Bad - looks up CONF_BLAH twice
|
||||
if CONF_BLAH in config:
|
||||
cg.add(var.set_blah(config[CONF_BLAH]))
|
||||
cg.add(var.set_blah_ms(config[CONF_BLAH].total_milliseconds))
|
||||
|
||||
# Good - single lookup, value bound inline
|
||||
if (blah := config.get(CONF_BLAH)) is not None:
|
||||
cg.add(var.set_blah(blah))
|
||||
cg.add(var.set_blah_ms(blah.total_milliseconds))
|
||||
```
|
||||
The same applies to `while` loops and comprehensions where it avoids recomputing a value. Don't contort code to use it — reach for `:=` only when it genuinely cuts repetition or an extra assignment line.
|
||||
When the setter takes the value unchanged, use `cg.add_optional(var.set_blah, config.get(CONF_BLAH))`
|
||||
instead; it is not awaited, and it skips only `None`, so `0` and `False` are still set.
|
||||
|
||||
* **C++ Field Visibility:**
|
||||
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
|
||||
@@ -208,8 +210,14 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
* **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`, `reverse_bits`, `encode_uint16`);
|
||||
pick the helper's polynomial and bit-order arguments instead of writing a CRC loop.
|
||||
* **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`); 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.
|
||||
A child sensor that skips repeated values is `SUB_SENSOR_WITH_DEDUP(name, type)`.
|
||||
* **An unset float is `NAN`**, not `optional<float>`; the optional costs 4 to 8 bytes of padding per member.
|
||||
* **`PollingComponent` first runs `update()`** after a random share of the interval; call `this->update()` at the
|
||||
end of `setup()` when the entities need a state at boot.
|
||||
@@ -429,6 +437,22 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
and `await cg.add_variable(var.set_bus, config[CONF_BUS_ID])`; both skip a `None` value, so pass
|
||||
`config.get(KEY)` for an optional key, and return what they created. Use `cg.register_parented` for `set_parent`.
|
||||
|
||||
- **Hub forwarding entities need no C++ class:** a button, switch, number or select that only calls its hub
|
||||
uses `button.ApplyButton` (`switch.ApplySwitch`, `number.ApplyNumber`, `select.ApplySelect`) in its schema
|
||||
and `apply=` on the `sub_*` call. The body is a call on the hub with `entity` and `state`, `value` or
|
||||
`index`/`option` in scope; on a switch, number or select `publish="before"` or `"after"` publishes the state.
|
||||
```python
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches(CONF_BLUETOOTH, None, apply="set_bluetooth(state)", publish="after")
|
||||
```
|
||||
|
||||
- **Other codegen helpers:** `uart.new_uart_component(config, *ctor_args)` (and `i2c.new_i2c_component`,
|
||||
`spi.new_spi_component`, `modbus.new_modbus_client_component`, `ble_client.new_ble_node_component`)
|
||||
constructs, registers and attaches in one call; `display.add_writer(var, config)` sets the lambda;
|
||||
`MyEnum.options("on", prefix="MY_ENUM_")` builds an enum dict; `button.restart_button_schema(cls)` (and
|
||||
`factory_reset_`, `query_`) are button presets; `fv.get_declaring_config(id)` finds a hub's config in
|
||||
final validation.
|
||||
|
||||
* **Automations (Triggers, Actions, Conditions):**
|
||||
|
||||
Automations have three building blocks: **Triggers** (fire when something happens), **Actions** (do something), and **Conditions** (check if something is true).
|
||||
|
||||
@@ -601,6 +601,8 @@ esphome/components/ssd1331_base/* @kbx81
|
||||
esphome/components/ssd1331_spi/* @kbx81
|
||||
esphome/components/ssd1351_base/* @kbx81
|
||||
esphome/components/ssd1351_spi/* @kbx81
|
||||
esphome/components/st25r/* @JohnMcLear
|
||||
esphome/components/st25r_spi/* @JohnMcLear
|
||||
esphome/components/st7123/* @miniskipper
|
||||
esphome/components/st7305/* @clydebarrow
|
||||
esphome/components/st7567_base/* @latonita
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ RUN \
|
||||
-r /requirements.txt
|
||||
|
||||
# Install the ESPHome Device Builder dashboard.
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.24.0
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.25.0
|
||||
|
||||
RUN \
|
||||
platformio settings set enable_telemetry No \
|
||||
|
||||
@@ -53,6 +53,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
||||
with_local_variable,
|
||||
)
|
||||
from esphome.cpp_helpers import ( # noqa: F401
|
||||
add_optional,
|
||||
add_pin,
|
||||
add_variable,
|
||||
build_registry_entry,
|
||||
|
||||
@@ -136,13 +136,7 @@ void AirtonClimate::transmit_state() {
|
||||
data->set_carrier_frequency(AIRTON_IR_FREQUENCY);
|
||||
data->mark(AIRTON_HEADER_MARK);
|
||||
data->space(AIRTON_HEADER_SPACE);
|
||||
for (uint8_t payload_byte : remote_state) {
|
||||
for (uint8_t payload_bit_cursor = 0; payload_bit_cursor < 8; payload_bit_cursor++) {
|
||||
data->mark(AIRTON_BIT_MARK);
|
||||
bool bit = payload_byte & (1 << payload_bit_cursor);
|
||||
data->space(bit ? AIRTON_ONE_SPACE : AIRTON_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<AIRTON_BIT_TIMINGS>(remote_state, AIRTON_STATE_FRAME_SIZE);
|
||||
|
||||
data->mark(AIRTON_BIT_MARK);
|
||||
data->space(AIRTON_MESSAGE_SPACE);
|
||||
@@ -284,20 +278,14 @@ void AirtonClimate::parse_state_frame_(const uint8_t *frame) {
|
||||
}
|
||||
|
||||
bool AirtonClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t remote_state[AIRTON_STATE_FRAME_SIZE] = {};
|
||||
uint8_t remote_state[AIRTON_STATE_FRAME_SIZE];
|
||||
if (!data.expect_item(AIRTON_HEADER_MARK, AIRTON_HEADER_SPACE)) {
|
||||
ESP_LOGV(TAG, "Wrong header encoding detected!");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < AIRTON_STATE_FRAME_SIZE; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (data.expect_item(AIRTON_BIT_MARK, AIRTON_ONE_SPACE)) {
|
||||
remote_state[i] |= 1 << j;
|
||||
} else if (!data.expect_item(AIRTON_BIT_MARK, AIRTON_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Wrong modulation encoding for: Byte %d, bit %d", i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!data.expect_bytes<AIRTON_BIT_TIMINGS>(remote_state, AIRTON_STATE_FRAME_SIZE)) {
|
||||
ESP_LOGV(TAG, "Wrong modulation encoding");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remote_state[0] != AIRTON_HEADER_BYTE0 || remote_state[1] != AIRTON_HEADER_BYTE1) {
|
||||
|
||||
@@ -36,8 +36,8 @@ static constexpr uint32_t AIRTON_IR_FREQUENCY = 38000;
|
||||
static constexpr uint32_t AIRTON_HEADER_MARK = 6630;
|
||||
static constexpr uint32_t AIRTON_HEADER_SPACE = 3350;
|
||||
static constexpr uint32_t AIRTON_BIT_MARK = 400;
|
||||
static constexpr uint32_t AIRTON_ONE_SPACE = 1260;
|
||||
static constexpr uint32_t AIRTON_ZERO_SPACE = 430;
|
||||
static constexpr remote_base::BitTimings AIRTON_BIT_TIMINGS{
|
||||
.bit_mark = AIRTON_BIT_MARK, .one_space = 1260, .zero_space = 430};
|
||||
static constexpr uint32_t AIRTON_MESSAGE_SPACE = 100000;
|
||||
|
||||
static constexpr uint8_t AIRTON_STATE_FRAME_SIZE = 7;
|
||||
|
||||
@@ -8,19 +8,18 @@ from ..climate import CONF_AIRTON_ID, CONF_SLEEP_MODE, AirtonClimate, airton_ns
|
||||
|
||||
CODEOWNERS = ["@Procsiab"]
|
||||
|
||||
AirtonSwitch = airton_ns.class_("AirtonSwitch", switch.Switch)
|
||||
AirtonSetting = airton_ns.enum("AirtonSetting", is_class=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
|
||||
cv.Optional(CONF_DISPLAY): switch.switch_schema(
|
||||
AirtonSwitch,
|
||||
switch.ApplySwitch,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
default_restore_mode="DISABLED",
|
||||
),
|
||||
cv.Optional(CONF_SLEEP_MODE): switch.switch_schema(
|
||||
AirtonSwitch,
|
||||
switch.ApplySwitch,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
default_restore_mode="DISABLED",
|
||||
),
|
||||
@@ -32,10 +31,12 @@ async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_AIRTON_ID])
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches(
|
||||
CONF_DISPLAY, hub.set_display_switch, AirtonSetting.AIRTON_SETTING_DISPLAY
|
||||
CONF_DISPLAY,
|
||||
hub.set_display_switch,
|
||||
apply=f"set_setting({AirtonSetting.AIRTON_SETTING_DISPLAY}, state)",
|
||||
)
|
||||
await switches(
|
||||
CONF_SLEEP_MODE,
|
||||
hub.set_sleep_mode_switch,
|
||||
AirtonSetting.AIRTON_SETTING_SLEEP_MODE,
|
||||
apply=f"set_setting({AirtonSetting.AIRTON_SETTING_SLEEP_MODE}, state)",
|
||||
)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../airton.h"
|
||||
|
||||
namespace esphome::airton {
|
||||
|
||||
// One of the unit's on/off settings; the parent sends the state and publishes it back
|
||||
class AirtonSwitch : public switch_::Switch, public Parented<AirtonClimate> {
|
||||
public:
|
||||
explicit AirtonSwitch(AirtonSetting setting) : setting_(setting) {}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override { this->parent_->set_setting(this->setting_, state); }
|
||||
|
||||
AirtonSetting setting_;
|
||||
};
|
||||
|
||||
} // namespace esphome::airton
|
||||
@@ -8,8 +8,8 @@ ESPHOME_LOG_TAG(TAG, "ballu.climate");
|
||||
const uint16_t BALLU_HEADER_MARK = 9000;
|
||||
const uint16_t BALLU_HEADER_SPACE = 4500;
|
||||
const uint16_t BALLU_BIT_MARK = 575;
|
||||
const uint16_t BALLU_ONE_SPACE = 1675;
|
||||
const uint16_t BALLU_ZERO_SPACE = 550;
|
||||
static constexpr remote_base::BitTimings BALLU_BIT_TIMINGS{
|
||||
.bit_mark = BALLU_BIT_MARK, .one_space = 1675, .zero_space = 550};
|
||||
|
||||
const uint32_t BALLU_CARRIER_FREQUENCY = 38000;
|
||||
|
||||
@@ -104,13 +104,7 @@ void BalluClimate::transmit_state() {
|
||||
data->mark(BALLU_HEADER_MARK);
|
||||
data->space(BALLU_HEADER_SPACE);
|
||||
// Data
|
||||
for (uint8_t i : remote_state) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
data->mark(BALLU_BIT_MARK);
|
||||
bool bit = i & (1 << j);
|
||||
data->space(bit ? BALLU_ONE_SPACE : BALLU_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<BALLU_BIT_TIMINGS>(remote_state, BALLU_STATE_LENGTH);
|
||||
// Footer
|
||||
data->mark(BALLU_BIT_MARK);
|
||||
|
||||
@@ -124,21 +118,10 @@ bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
|
||||
// Read all bytes.
|
||||
for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
|
||||
// Read bit
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) {
|
||||
remote_state[i] |= 1 << j;
|
||||
|
||||
} else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]);
|
||||
uint8_t remote_state[BALLU_STATE_LENGTH];
|
||||
if (!data.expect_bytes<BALLU_BIT_TIMINGS>(remote_state, BALLU_STATE_LENGTH)) {
|
||||
ESP_LOGV(TAG, "Data fail");
|
||||
return false;
|
||||
}
|
||||
// Validate footer
|
||||
if (!data.expect_mark(BALLU_BIT_MARK)) {
|
||||
|
||||
@@ -43,5 +43,4 @@ async def register_bedjet_child(var: MockObj, config: ConfigType) -> None:
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = await ble_client.new_ble_node_component(config)
|
||||
await cg.add_variable(var.set_time_id, config.get(CONF_TIME_ID))
|
||||
if (receive_timeout := config.get(CONF_RECEIVE_TIMEOUT)) is not None:
|
||||
cg.add(var.set_status_timeout(receive_timeout))
|
||||
cg.add_optional(var.set_status_timeout, config.get(CONF_RECEIVE_TIMEOUT))
|
||||
|
||||
@@ -111,8 +111,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
cg.add_optional(var.set_max_refresh_rate, config.get(CONF_MAX_REFRESH_RATE))
|
||||
|
||||
chipset = CHIPSETS[config[CONF_CHIPSET]]
|
||||
cg.add(
|
||||
|
||||
@@ -122,6 +122,7 @@ float BL0940::calculate_energy_reference_() {
|
||||
float BL0940::calculate_calibration_value_(float state) { return (100 + state) / 100; }
|
||||
|
||||
void BL0940::reset_calibration() {
|
||||
ESP_LOGI(TAG, "Resetting calibration defaults...");
|
||||
#ifdef USE_NUMBER
|
||||
if (this->current_calibration_number_ != nullptr && this->current_cal_ != 1) {
|
||||
this->current_calibration_number_->make_call().set_value(0).perform();
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG, ICON_RESTART
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_BL0940_ID, bl0940_ns
|
||||
from .. import CONF_BL0940_ID
|
||||
from ..sensor import BL0940
|
||||
|
||||
CalibrationResetButton = bl0940_ns.class_(
|
||||
"CalibrationResetButton", button.Button, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
button.button_schema(
|
||||
CalibrationResetButton,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART,
|
||||
)
|
||||
.extend({cv.GenerateID(CONF_BL0940_ID): cv.use_id(BL0940)})
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
CONFIG_SCHEMA = button.button_schema(
|
||||
button.ApplyButton,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART,
|
||||
).extend({cv.GenerateID(CONF_BL0940_ID): cv.use_id(BL0940)})
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = await button.new_button(config)
|
||||
await cg.register_component(var, config)
|
||||
await cg.register_parented(var, config[CONF_BL0940_ID])
|
||||
await button.sub_buttons(config, parent=config[CONF_BL0940_ID]).create(
|
||||
config, None, apply="reset_calibration()"
|
||||
)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "calibration_reset_button.h"
|
||||
#include "../bl0940.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome::bl0940 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "bl0940.button.calibration_reset");
|
||||
|
||||
void CalibrationResetButton::dump_config() { LOG_BUTTON("", "Calibration Reset Button", this); }
|
||||
|
||||
void CalibrationResetButton::press_action() {
|
||||
ESP_LOGI(TAG, "Resetting calibration defaults...");
|
||||
this->parent_->reset_calibration();
|
||||
}
|
||||
|
||||
} // namespace esphome::bl0940
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome::bl0940 {
|
||||
|
||||
class BL0940; // Forward declaration of BL0940 class
|
||||
|
||||
class CalibrationResetButton final : public button::Button, public Component, public Parented<BL0940> {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::bl0940
|
||||
@@ -83,8 +83,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
ibeacon_uuid = ble_device_base.as_reversed_hex_array(ibeacon_uuid)
|
||||
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
||||
|
||||
if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
|
||||
cg.add(var.set_ibeacon_major(ibeacon_major))
|
||||
cg.add_optional(var.set_ibeacon_major, config.get(CONF_IBEACON_MAJOR))
|
||||
|
||||
if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
|
||||
cg.add(var.set_ibeacon_minor(ibeacon_minor))
|
||||
cg.add_optional(var.set_ibeacon_minor, config.get(CONF_IBEACON_MINOR))
|
||||
|
||||
@@ -79,8 +79,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
ibeacon_uuid = ble_device_base.as_reversed_hex_array(ibeacon_uuid)
|
||||
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
||||
|
||||
if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
|
||||
cg.add(var.set_ibeacon_major(ibeacon_major))
|
||||
cg.add_optional(var.set_ibeacon_major, config.get(CONF_IBEACON_MAJOR))
|
||||
|
||||
if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
|
||||
cg.add(var.set_ibeacon_minor(ibeacon_minor))
|
||||
cg.add_optional(var.set_ibeacon_minor, config.get(CONF_IBEACON_MINOR))
|
||||
|
||||
@@ -69,8 +69,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
text_sensors = text_sensor.sub_text_sensors(config)
|
||||
await text_sensors(CONF_VERSION, var.set_version)
|
||||
|
||||
if (baseline := config.get(CONF_BASELINE)) is not None:
|
||||
cg.add(var.set_baseline(baseline))
|
||||
cg.add_optional(var.set_baseline, config.get(CONF_BASELINE))
|
||||
|
||||
await cg.add_variable(var.set_temperature, config.get(CONF_TEMPERATURE))
|
||||
await cg.add_variable(var.set_humidity, config.get(CONF_HUMIDITY))
|
||||
|
||||
@@ -65,6 +65,7 @@ CONF_REMOTE_ID = "remote_id"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
CONF_ROLE = "role"
|
||||
CONF_ROWS = "rows"
|
||||
CONF_RST_PIN = "rst_pin"
|
||||
CONF_SCAN_PARAMETERS = "scan_parameters"
|
||||
CONF_SERIAL_NUMBER = "serial_number"
|
||||
CONF_SHA256 = "sha256"
|
||||
|
||||
@@ -117,8 +117,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
|
||||
cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK]))
|
||||
if (max_duration := config.get(CONF_MAX_DURATION)) is not None:
|
||||
cg.add(var.set_max_duration(max_duration))
|
||||
cg.add_optional(var.set_max_duration, config.get(CONF_MAX_DURATION))
|
||||
cg.add(var.set_malfunction_detection(config[CONF_MALFUNCTION_DETECTION]))
|
||||
if malfunction_action := config.get(CONF_MALFUNCTION_ACTION):
|
||||
await automation.build_automation(
|
||||
|
||||
@@ -9,6 +9,14 @@ ESPHOME_LOG_TAG(TAG, "daikin.climate");
|
||||
|
||||
static constexpr uint32_t BOOST_TIMEOUT_ID = 0;
|
||||
|
||||
static constexpr uint8_t DAIKIN_HEADER_BYTE0 = 0x11;
|
||||
static constexpr uint8_t DAIKIN_HEADER_BYTE1 = 0xDA;
|
||||
static constexpr uint8_t DAIKIN_HEADER_BYTE2 = 0x27;
|
||||
static constexpr uint8_t DAIKIN_HEADER_BYTE3 = 0x00;
|
||||
static constexpr uint8_t DAIKIN_FRAME_TYPE_INDEX = 4;
|
||||
static constexpr uint8_t DAIKIN_FRAME_HEADER_SIZE = DAIKIN_FRAME_TYPE_INDEX + 1;
|
||||
static constexpr uint8_t DAIKIN_STATE_FRAME_OFFSET = 2 * DAIKIN_COMFORT_FRAME_SIZE;
|
||||
|
||||
void DaikinClimate::setup() {
|
||||
climate_ir::ClimateIR::setup();
|
||||
// A boost preset restored from flash gets its timer too
|
||||
@@ -42,37 +50,19 @@ void DaikinClimate::transmit_state() {
|
||||
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_state, DAIKIN_COMFORT_FRAME_SIZE);
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(DAIKIN_MESSAGE_SPACE);
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (int i = 8; i < 16; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_state + DAIKIN_COMFORT_FRAME_SIZE, DAIKIN_COMFORT_FRAME_SIZE);
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(DAIKIN_MESSAGE_SPACE);
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (int i = 16; i < 35; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_state + DAIKIN_STATE_FRAME_OFFSET, DAIKIN_STATE_FRAME_SIZE);
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(0);
|
||||
|
||||
@@ -284,46 +274,24 @@ bool DaikinClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
}
|
||||
|
||||
bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t frame[DAIKIN_STATE_FRAME_SIZE] = {};
|
||||
uint8_t frame[DAIKIN_STATE_FRAME_SIZE];
|
||||
if (!data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Frame header, then the frame type that sets the frame size
|
||||
if (!data.expect_bytes<DAIKIN_BIT_TIMINGS>(frame, DAIKIN_FRAME_HEADER_SIZE) || frame[0] != DAIKIN_HEADER_BYTE0 ||
|
||||
frame[1] != DAIKIN_HEADER_BYTE1 || frame[2] != DAIKIN_HEADER_BYTE2 || frame[3] != DAIKIN_HEADER_BYTE3) {
|
||||
return false;
|
||||
}
|
||||
uint8_t frame_size = DAIKIN_STATE_FRAME_SIZE;
|
||||
for (uint8_t pos = 0; pos < frame_size; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
frame[pos] = byte;
|
||||
if (pos == 0) {
|
||||
// frame header
|
||||
if (byte != 0x11)
|
||||
return false;
|
||||
} else if (pos == 1) {
|
||||
// frame header
|
||||
if (byte != 0xDA)
|
||||
return false;
|
||||
} else if (pos == 2) {
|
||||
// frame header
|
||||
if (byte != 0x27)
|
||||
return false;
|
||||
} else if (pos == 3) { // NOLINT(bugprone-branch-clone)
|
||||
// frame header
|
||||
if (byte != 0x00)
|
||||
return false;
|
||||
} else if (pos == 4) {
|
||||
// frame type
|
||||
if (byte == DAIKIN_FRAME_TYPE_COMFORT) {
|
||||
frame_size = DAIKIN_COMFORT_FRAME_SIZE;
|
||||
} else if (byte != DAIKIN_FRAME_TYPE_STATE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (frame[DAIKIN_FRAME_TYPE_INDEX] == DAIKIN_FRAME_TYPE_COMFORT) {
|
||||
frame_size = DAIKIN_COMFORT_FRAME_SIZE;
|
||||
} else if (frame[DAIKIN_FRAME_TYPE_INDEX] != DAIKIN_FRAME_TYPE_STATE) {
|
||||
return false;
|
||||
}
|
||||
if (!data.expect_bytes<DAIKIN_BIT_TIMINGS>(frame + DAIKIN_FRAME_HEADER_SIZE, frame_size - DAIKIN_FRAME_HEADER_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
if (frame[frame_size - 1] != sum_bytes(frame, frame_size - 1)) {
|
||||
return false;
|
||||
|
||||
@@ -45,6 +45,8 @@ const uint32_t DAIKIN_HEADER_SPACE = 1760;
|
||||
const uint32_t DAIKIN_BIT_MARK = 520;
|
||||
const uint32_t DAIKIN_ONE_SPACE = 1370;
|
||||
const uint32_t DAIKIN_ZERO_SPACE = 360;
|
||||
static constexpr remote_base::BitTimings DAIKIN_BIT_TIMINGS{
|
||||
.bit_mark = DAIKIN_BIT_MARK, .one_space = DAIKIN_ONE_SPACE, .zero_space = DAIKIN_ZERO_SPACE};
|
||||
const uint32_t DAIKIN_MESSAGE_SPACE = 32300;
|
||||
|
||||
// Frame types (byte 4) and sizes
|
||||
|
||||
@@ -39,13 +39,7 @@ void DaikinArcClimate::transmit_query_() {
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (uint8_t i : remote_header) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = i & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_header, sizeof(remote_header));
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(0);
|
||||
|
||||
@@ -119,26 +113,14 @@ void DaikinArcClimate::transmit_state() {
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (uint8_t i : remote_header) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = i & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_header, sizeof(remote_header));
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(DAIKIN_MESSAGE_SPACE);
|
||||
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (uint8_t i : remote_state) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = i & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_state, sizeof(remote_state));
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(0);
|
||||
|
||||
@@ -339,7 +321,7 @@ bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
}
|
||||
|
||||
bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {};
|
||||
uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE];
|
||||
|
||||
bool valid_daikin_frame = false;
|
||||
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||
@@ -418,16 +400,11 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
}
|
||||
|
||||
for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
|
||||
ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
|
||||
return false;
|
||||
}
|
||||
if (!data.expect_bytes<DAIKIN_BIT_TIMINGS>(&state_frame[pos], 1)) {
|
||||
ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
|
||||
return false;
|
||||
}
|
||||
state_frame[pos] = byte;
|
||||
const uint8_t byte = state_frame[pos];
|
||||
if (pos == 0) {
|
||||
// frame header
|
||||
if (byte != 0x11) {
|
||||
|
||||
@@ -36,6 +36,8 @@ const uint32_t DAIKIN_HEADER_SPACE = 1760;
|
||||
const uint32_t DAIKIN_BIT_MARK = 400;
|
||||
const uint32_t DAIKIN_ONE_SPACE = 1300;
|
||||
const uint32_t DAIKIN_ZERO_SPACE = 480;
|
||||
static constexpr remote_base::BitTimings DAIKIN_BIT_TIMINGS{
|
||||
.bit_mark = DAIKIN_BIT_MARK, .one_space = DAIKIN_ONE_SPACE, .zero_space = DAIKIN_ZERO_SPACE};
|
||||
const uint32_t DAIKIN_MESSAGE_SPACE = 35000;
|
||||
|
||||
const uint8_t DAIKIN_DBG_TOLERANCE = 25;
|
||||
|
||||
@@ -6,6 +6,12 @@ namespace esphome::daikin_brc {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "daikin_brc.climate");
|
||||
|
||||
static constexpr uint8_t DAIKIN_BRC_HEADER_BYTE0 = 0x11;
|
||||
static constexpr uint8_t DAIKIN_BRC_HEADER_BYTE1 = 0xDA;
|
||||
static constexpr uint8_t DAIKIN_BRC_HEADER_BYTE2 = 0x17;
|
||||
static constexpr uint8_t DAIKIN_BRC_HEADER_BYTE3 = 0x18;
|
||||
static constexpr uint8_t DAIKIN_BRC_FRAME_TYPE_STATE = 0x00;
|
||||
|
||||
void DaikinBrcClimate::control(const climate::ClimateCall &call) {
|
||||
this->mode_button_ = 0x00;
|
||||
if (call.get_mode().has_value()) {
|
||||
@@ -42,25 +48,14 @@ void DaikinBrcClimate::transmit_state() {
|
||||
|
||||
data->mark(DAIKIN_BRC_HEADER_MARK);
|
||||
data->space(DAIKIN_BRC_HEADER_SPACE);
|
||||
for (int i = 0; i < DAIKIN_BRC_PREAMBLE_SIZE; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BRC_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_BRC_ONE_SPACE : DAIKIN_BRC_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BRC_BIT_TIMINGS>(remote_state, DAIKIN_BRC_PREAMBLE_SIZE);
|
||||
data->mark(DAIKIN_BRC_BIT_MARK);
|
||||
data->space(DAIKIN_BRC_MESSAGE_SPACE);
|
||||
data->mark(DAIKIN_BRC_HEADER_MARK);
|
||||
data->space(DAIKIN_BRC_HEADER_SPACE);
|
||||
|
||||
for (int i = DAIKIN_BRC_PREAMBLE_SIZE; i < DAIKIN_BRC_TRANSMIT_FRAME_SIZE; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BRC_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_BRC_ONE_SPACE : DAIKIN_BRC_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DAIKIN_BRC_BIT_TIMINGS>(remote_state + DAIKIN_BRC_PREAMBLE_SIZE,
|
||||
DAIKIN_BRC_TRANSMIT_FRAME_SIZE - DAIKIN_BRC_PREAMBLE_SIZE);
|
||||
|
||||
data->mark(DAIKIN_BRC_BIT_MARK);
|
||||
data->space(0);
|
||||
@@ -232,41 +227,16 @@ bool DaikinBrcClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
}
|
||||
|
||||
bool DaikinBrcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t state_frame[DAIKIN_BRC_STATE_FRAME_SIZE] = {};
|
||||
uint8_t state_frame[DAIKIN_BRC_STATE_FRAME_SIZE];
|
||||
if (!data.expect_item(DAIKIN_BRC_HEADER_MARK, DAIKIN_BRC_HEADER_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t pos = 0; pos < DAIKIN_BRC_STATE_FRAME_SIZE; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(DAIKIN_BRC_BIT_MARK, DAIKIN_BRC_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(DAIKIN_BRC_BIT_MARK, DAIKIN_BRC_ZERO_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state_frame[pos] = byte;
|
||||
if (pos == 0) {
|
||||
// frame header
|
||||
if (byte != 0x11)
|
||||
return false;
|
||||
} else if (pos == 1) {
|
||||
// frame header
|
||||
if (byte != 0xDA)
|
||||
return false;
|
||||
} else if (pos == 2) {
|
||||
// frame header
|
||||
if (byte != 0x17)
|
||||
return false;
|
||||
} else if (pos == 3) {
|
||||
// frame header, with this unit's address in the low 3 bits
|
||||
if (byte != (0x18 | this->sub_unit_))
|
||||
return false;
|
||||
} else if (pos == 4) {
|
||||
// frame type
|
||||
if (byte != 0x00)
|
||||
return false;
|
||||
}
|
||||
// Frame header, with this unit's address in the low 3 bits of byte 3, then the frame type
|
||||
if (!data.expect_bytes<DAIKIN_BRC_BIT_TIMINGS>(state_frame, DAIKIN_BRC_STATE_FRAME_SIZE) ||
|
||||
state_frame[0] != DAIKIN_BRC_HEADER_BYTE0 || state_frame[1] != DAIKIN_BRC_HEADER_BYTE1 ||
|
||||
state_frame[2] != DAIKIN_BRC_HEADER_BYTE2 || state_frame[3] != (DAIKIN_BRC_HEADER_BYTE3 | this->sub_unit_) ||
|
||||
state_frame[4] != DAIKIN_BRC_FRAME_TYPE_STATE) {
|
||||
return false;
|
||||
}
|
||||
return this->parse_state_frame_(state_frame);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ const uint32_t DAIKIN_BRC_IR_FREQUENCY = 38000;
|
||||
const uint32_t DAIKIN_BRC_HEADER_MARK = 5070;
|
||||
const uint32_t DAIKIN_BRC_HEADER_SPACE = 2140;
|
||||
const uint32_t DAIKIN_BRC_BIT_MARK = 370;
|
||||
const uint32_t DAIKIN_BRC_ONE_SPACE = 1780;
|
||||
const uint32_t DAIKIN_BRC_ZERO_SPACE = 710;
|
||||
static constexpr remote_base::BitTimings DAIKIN_BRC_BIT_TIMINGS{
|
||||
.bit_mark = DAIKIN_BRC_BIT_MARK, .one_space = 1780, .zero_space = 710};
|
||||
const uint32_t DAIKIN_BRC_MESSAGE_SPACE = 29410;
|
||||
|
||||
const uint8_t DAIKIN_BRC_IR_DRY_FAN_TEMP_F = 72; // Dry/Fan mode is always 17 Celsius.
|
||||
|
||||
@@ -364,8 +364,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
cg.add(var.set_sleep_duration(config[CONF_SLEEP_DURATION]))
|
||||
cg.add_optional(var.set_sleep_duration, config.get(CONF_SLEEP_DURATION))
|
||||
if (wakeup_pins := config.get(CONF_WAKEUP_PIN)) is not None:
|
||||
if CORE.is_bk72xx:
|
||||
cg.add(var.init_wakeup_pins(len(wakeup_pins)))
|
||||
@@ -380,8 +379,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
else:
|
||||
await cg.add_pin(var.set_wakeup_pin, wakeup_pins[0][CONF_PIN])
|
||||
if CONF_WAKEUP_PIN_MODE in config:
|
||||
cg.add(var.set_wakeup_pin_mode(config[CONF_WAKEUP_PIN_MODE]))
|
||||
cg.add_optional(var.set_wakeup_pin_mode, config.get(CONF_WAKEUP_PIN_MODE))
|
||||
if CONF_RUN_DURATION in config:
|
||||
run_duration_config = config[CONF_RUN_DURATION]
|
||||
if not isinstance(run_duration_config, dict):
|
||||
@@ -427,8 +425,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
await automation.build_automation(trigger, [(WakeupCause, "cause")], wake_conf)
|
||||
cg.add_define("USE_DEEP_SLEEP_ON_WAKE")
|
||||
|
||||
if CONF_TOUCH_WAKEUP in config:
|
||||
cg.add(var.set_touch_wakeup(config[CONF_TOUCH_WAKEUP]))
|
||||
cg.add_optional(var.set_touch_wakeup, config.get(CONF_TOUCH_WAKEUP))
|
||||
if CORE.using_zephyr:
|
||||
# Devices are suspended when CPU is entering a low power state
|
||||
# https://github.com/nrfconnect/sdk-zephyr/blob/v3.7.99-ncs2-2/doc/services/pm/device.rst#system-managed-device-power-management
|
||||
|
||||
@@ -23,13 +23,7 @@ void DelonghiClimate::transmit_state() {
|
||||
|
||||
data->mark(DELONGHI_HEADER_MARK);
|
||||
data->space(DELONGHI_HEADER_SPACE);
|
||||
for (unsigned char b : remote_state) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DELONGHI_BIT_MARK);
|
||||
bool bit = b & mask;
|
||||
data->space(bit ? DELONGHI_ONE_SPACE : DELONGHI_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<DELONGHI_BIT_TIMINGS>(remote_state, DELONGHI_STATE_FRAME_SIZE);
|
||||
data->mark(DELONGHI_BIT_MARK);
|
||||
data->space(0);
|
||||
|
||||
@@ -155,27 +149,13 @@ bool DelonghiClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
}
|
||||
|
||||
bool DelonghiClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t state_frame[DELONGHI_STATE_FRAME_SIZE] = {};
|
||||
uint8_t state_frame[DELONGHI_STATE_FRAME_SIZE];
|
||||
if (!data.expect_item(DELONGHI_HEADER_MARK, DELONGHI_HEADER_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t pos = 0; pos < DELONGHI_STATE_FRAME_SIZE; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(DELONGHI_BIT_MARK, DELONGHI_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(DELONGHI_BIT_MARK, DELONGHI_ZERO_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state_frame[pos] = byte;
|
||||
if (pos == 0) {
|
||||
// frame header
|
||||
if (byte != DELONGHI_ADDRESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data.expect_bytes<DELONGHI_BIT_TIMINGS>(state_frame, DELONGHI_STATE_FRAME_SIZE) ||
|
||||
state_frame[0] != DELONGHI_ADDRESS)
|
||||
return false;
|
||||
return this->parse_state_frame_(state_frame);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ const uint32_t DELONGHI_IR_FREQUENCY = 38000;
|
||||
const uint32_t DELONGHI_HEADER_MARK = 9000;
|
||||
const uint32_t DELONGHI_HEADER_SPACE = 4500;
|
||||
const uint32_t DELONGHI_BIT_MARK = 465;
|
||||
const uint32_t DELONGHI_ONE_SPACE = 1750;
|
||||
const uint32_t DELONGHI_ZERO_SPACE = 670;
|
||||
static constexpr remote_base::BitTimings DELONGHI_BIT_TIMINGS{
|
||||
.bit_mark = DELONGHI_BIT_MARK, .one_space = 1750, .zero_space = 670};
|
||||
|
||||
// State Frame size
|
||||
const uint8_t DELONGHI_STATE_FRAME_SIZE = 8;
|
||||
|
||||
@@ -5,15 +5,13 @@ import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, ENTITY_CATEGORY_CONFIG, ICON_SCALE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_DFROBOT_SEN0609_ID, DFRobotSEN0609Component, dfrobot_sen0609_ns
|
||||
|
||||
OutPinLevelSelect = dfrobot_sen0609_ns.class_("OutPinLevelSelect", select.Select)
|
||||
from .. import CONF_DFROBOT_SEN0609_ID, DFRobotSEN0609Component
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_DFROBOT_SEN0609_ID): cv.use_id(DFRobotSEN0609Component),
|
||||
cv.Optional(CONF_OUT_PIN_LEVEL): select.select_schema(
|
||||
OutPinLevelSelect,
|
||||
select.ApplySelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
),
|
||||
@@ -25,5 +23,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
# The level the OUT pin has while a target is present; the index is the module's value
|
||||
await selects(
|
||||
CONF_OUT_PIN_LEVEL, hub.set_out_pin_level_select, options=["low", "high"]
|
||||
CONF_OUT_PIN_LEVEL,
|
||||
hub.set_out_pin_level_select,
|
||||
options=["low", "high"],
|
||||
apply="control_out_pin_level(index)",
|
||||
)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../dfrobot_sen0609.h"
|
||||
|
||||
namespace esphome::dfrobot_sen0609 {
|
||||
|
||||
class OutPinLevelSelect final : public select::Select, public Parented<DFRobotSEN0609Component> {
|
||||
public:
|
||||
// User provided, not "= default": `new(p) OutPinLevelSelect()` would zero-fill .bss that is already zero.
|
||||
OutPinLevelSelect() {}
|
||||
|
||||
protected:
|
||||
void control(size_t index) override { this->parent_->control_out_pin_level(index); }
|
||||
};
|
||||
|
||||
} // namespace esphome::dfrobot_sen0609
|
||||
@@ -18,7 +18,6 @@ from .. import (
|
||||
final_validate_mode,
|
||||
)
|
||||
|
||||
DFRobotSEN0609Switch = dfrobot_sen0609_ns.class_("DFRobotSEN0609Switch", switch.Switch)
|
||||
SwitchSetting = dfrobot_sen0609_ns.enum("SwitchSetting")
|
||||
|
||||
CONF_MICRO_MOTION = "micro_motion"
|
||||
@@ -29,7 +28,7 @@ CONF_RUN_LED = "run_led"
|
||||
def _led() -> cv.Schema:
|
||||
# The module forgets the LED state at power off, so it is restored from here
|
||||
return switch.switch_schema(
|
||||
DFRobotSEN0609Switch,
|
||||
switch.ApplySwitch,
|
||||
default_restore_mode="RESTORE_DEFAULT_ON",
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
@@ -41,7 +40,7 @@ CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_DFROBOT_SEN0609_ID): cv.use_id(DFRobotSEN0609Component),
|
||||
cv.Optional(CONF_MICRO_MOTION): switch.switch_schema(
|
||||
DFRobotSEN0609Switch,
|
||||
switch.ApplySwitch,
|
||||
device_class=DEVICE_CLASS_SWITCH,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
@@ -64,5 +63,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
(CONF_OUT_LED, SwitchSetting.SWITCH_OUT_LED),
|
||||
):
|
||||
await switches(
|
||||
key, lambda s, setting=setting: hub.set_switch(setting, s), setting
|
||||
key,
|
||||
lambda s, setting=setting: hub.set_switch(setting, s),
|
||||
apply=f"control_switch({setting}, state)",
|
||||
)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../dfrobot_sen0609.h"
|
||||
|
||||
namespace esphome::dfrobot_sen0609 {
|
||||
|
||||
class DFRobotSEN0609Switch final : public switch_::Switch, public Parented<DFRobotSEN0609Component> {
|
||||
public:
|
||||
explicit DFRobotSEN0609Switch(SwitchSetting setting) : setting_(setting) {}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override { this->parent_->control_switch(this->setting_, state); }
|
||||
|
||||
SwitchSetting setting_;
|
||||
};
|
||||
|
||||
} // namespace esphome::dfrobot_sen0609
|
||||
@@ -277,8 +277,7 @@ async def menu_item_to_code(menu, config, parent):
|
||||
if CONF_ITEMS in config:
|
||||
for c in config[CONF_ITEMS]:
|
||||
await menu_item_to_code(menu, c, item)
|
||||
if CONF_IMMEDIATE_EDIT in config:
|
||||
cg.add(item.set_immediate_edit(config[CONF_IMMEDIATE_EDIT]))
|
||||
cg.add_optional(item.set_immediate_edit, config.get(CONF_IMMEDIATE_EDIT))
|
||||
if config[CONF_TYPE] == CONF_SELECT:
|
||||
await cg.add_variable(item.set_select_variable, config[CONF_SELECT])
|
||||
if config[CONF_TYPE] == CONF_NUMBER:
|
||||
|
||||
@@ -91,20 +91,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_active_pullup(config[CONF_ACTIVE_PULLUP]))
|
||||
cg.add(var.set_channel_count(get_channel_count(config)))
|
||||
|
||||
if CONF_BUS_SLEEP in config:
|
||||
cg.add(var.set_bus_sleep(config[CONF_BUS_SLEEP]))
|
||||
if CONF_HUB_SLEEP in config:
|
||||
cg.add(var.set_hub_sleep(config[CONF_HUB_SLEEP]))
|
||||
cg.add_optional(var.set_bus_sleep, config.get(CONF_BUS_SLEEP))
|
||||
cg.add_optional(var.set_hub_sleep, config.get(CONF_HUB_SLEEP))
|
||||
|
||||
if CONF_RESET_LOW_TIME in config:
|
||||
cg.add(var.set_val_trstl(config[CONF_RESET_LOW_TIME]))
|
||||
if CONF_MASTER_SAMPLE_TIME in config:
|
||||
cg.add(var.set_val_tmsp(config[CONF_MASTER_SAMPLE_TIME]))
|
||||
if CONF_WRITE_0_LOW_TIME in config:
|
||||
cg.add(var.set_val_tw0l(config[CONF_WRITE_0_LOW_TIME]))
|
||||
if CONF_RECOVERY_TIME in config:
|
||||
cg.add(var.set_val_trec0(config[CONF_RECOVERY_TIME]))
|
||||
if CONF_ACTIVE_PULLUP_RESISTANCE in config:
|
||||
cg.add(var.set_val_rwpu(config[CONF_ACTIVE_PULLUP_RESISTANCE]))
|
||||
cg.add_optional(var.set_val_trstl, config.get(CONF_RESET_LOW_TIME))
|
||||
cg.add_optional(var.set_val_tmsp, config.get(CONF_MASTER_SAMPLE_TIME))
|
||||
cg.add_optional(var.set_val_tw0l, config.get(CONF_WRITE_0_LOW_TIME))
|
||||
cg.add_optional(var.set_val_trec0, config.get(CONF_RECOVERY_TIME))
|
||||
cg.add_optional(var.set_val_rwpu, config.get(CONF_ACTIVE_PULLUP_RESISTANCE))
|
||||
|
||||
await cg.add_pin(var.set_sleep_pin, config.get(CONF_SLEEP_PIN))
|
||||
|
||||
@@ -55,5 +55,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]
|
||||
)
|
||||
|
||||
if CONF_MAX_DURATION in config:
|
||||
cg.add(var.set_max_duration(config[CONF_MAX_DURATION]))
|
||||
cg.add_optional(var.set_max_duration, config.get(CONF_MAX_DURATION))
|
||||
|
||||
@@ -252,8 +252,7 @@ async def to_code(config):
|
||||
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
|
||||
cg.add(var.set_enable_pins(enable))
|
||||
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
if CONF_RESET_DURATION in config:
|
||||
cg.add(var.set_reset_duration(config[CONF_RESET_DURATION]))
|
||||
cg.add_optional(var.set_reset_duration, config.get(CONF_RESET_DURATION))
|
||||
if transform := config.get(CONF_TRANSFORM):
|
||||
transform[CONF_SWAP_XY] = False
|
||||
else:
|
||||
|
||||
@@ -265,8 +265,7 @@ async def esp32_pin_to_code(config):
|
||||
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||
if config[CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
if CONF_DRIVE_STRENGTH in config:
|
||||
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
|
||||
cg.add_optional(var.set_drive_strength, config.get(CONF_DRIVE_STRENGTH))
|
||||
flags = pins.gpio_flags_expr(config[CONF_MODE])
|
||||
if config[CONF_HOLD_STATE]:
|
||||
flags = flags | cg.gpio_Flags.FLAG_HOLD
|
||||
|
||||
@@ -535,16 +535,12 @@ async def to_code(config: ConfigType) -> None:
|
||||
):
|
||||
cg.add_define("ESPHOME_ESP32_BLE_EXTENDED_AUTH_PARAMS", None)
|
||||
|
||||
if CONF_AUTH_REQ_MODE in config:
|
||||
cg.add(var.set_auth_req(config[CONF_AUTH_REQ_MODE]))
|
||||
if CONF_MAX_KEY_SIZE in config:
|
||||
cg.add(var.set_max_key_size(config[CONF_MAX_KEY_SIZE]))
|
||||
if CONF_MIN_KEY_SIZE in config:
|
||||
cg.add(var.set_min_key_size(config[CONF_MIN_KEY_SIZE]))
|
||||
cg.add_optional(var.set_auth_req, config.get(CONF_AUTH_REQ_MODE))
|
||||
cg.add_optional(var.set_max_key_size, config.get(CONF_MAX_KEY_SIZE))
|
||||
cg.add_optional(var.set_min_key_size, config.get(CONF_MIN_KEY_SIZE))
|
||||
|
||||
cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME]))
|
||||
if (name := config.get(CONF_NAME)) is not None:
|
||||
cg.add(var.set_name(name))
|
||||
cg.add_optional(var.set_name, config.get(CONF_NAME))
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Define max connections for use in C++ code (e.g., ble_server.h)
|
||||
|
||||
@@ -83,8 +83,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_measured_power(config[CONF_MEASURED_POWER]))
|
||||
|
||||
# TX power control only available on native Bluetooth (not ESP-Hosted)
|
||||
if CONF_TX_POWER in config:
|
||||
cg.add(var.set_tx_power(config[CONF_TX_POWER]))
|
||||
cg.add_optional(var.set_tx_power, config.get(CONF_TX_POWER))
|
||||
|
||||
cg.add_define("USE_ESP32_BLE_ADVERTISING")
|
||||
|
||||
|
||||
@@ -647,8 +647,7 @@ async def to_code(config):
|
||||
)
|
||||
)
|
||||
)
|
||||
if CONF_MANUFACTURER_DATA in config:
|
||||
cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
|
||||
cg.add_optional(var.set_manufacturer_data, config.get(CONF_MANUFACTURER_DATA))
|
||||
for service_config in config[CONF_SERVICES]:
|
||||
# Calculate the optimal number of handles based on the number of characteristics and descriptors
|
||||
num_handles = calculate_num_handles(service_config)
|
||||
|
||||
@@ -157,10 +157,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_rx(config[CONF_RX_PIN]))
|
||||
cg.add(var.set_tx(config[CONF_TX_PIN]))
|
||||
cg.add(var.set_mode(config[CONF_MODE]))
|
||||
if (rx_queue_len := config.get(CONF_RX_QUEUE_LEN)) is not None:
|
||||
cg.add(var.set_rx_queue_len(rx_queue_len))
|
||||
if (tx_queue_len := config.get(CONF_TX_QUEUE_LEN)) is not None:
|
||||
cg.add(var.set_tx_queue_len(tx_queue_len))
|
||||
cg.add_optional(var.set_rx_queue_len, config.get(CONF_RX_QUEUE_LEN))
|
||||
cg.add_optional(var.set_tx_queue_len, config.get(CONF_TX_QUEUE_LEN))
|
||||
|
||||
if CONF_TX_ENQUEUE_TIMEOUT in config:
|
||||
tx_enqueue_timeout_ms = config[CONF_TX_ENQUEUE_TIMEOUT].total_milliseconds
|
||||
|
||||
@@ -170,10 +170,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
await power_management.register_power_management(var, config)
|
||||
add_idf_sdkconfig_option("CONFIG_PM_ENABLE", True)
|
||||
|
||||
if (max_mhz := config.get(CONF_MAX_FREQUENCY)) is not None:
|
||||
cg.add(var.set_max_freq_mhz(max_mhz))
|
||||
if (min_mhz := config.get(CONF_MIN_FREQUENCY)) is not None:
|
||||
cg.add(var.set_min_freq_mhz(min_mhz))
|
||||
cg.add_optional(var.set_max_freq_mhz, config.get(CONF_MAX_FREQUENCY))
|
||||
cg.add_optional(var.set_min_freq_mhz, config.get(CONF_MIN_FREQUENCY))
|
||||
if config[CONF_PROFILING]:
|
||||
add_idf_sdkconfig_option("CONFIG_PM_PROFILING", True)
|
||||
if config[CONF_TRACE]:
|
||||
|
||||
@@ -137,8 +137,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
if config[CONF_PIN][CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
cg.add_optional(var.set_max_refresh_rate, config.get(CONF_MAX_REFRESH_RATE))
|
||||
|
||||
if CONF_CHIPSET in config:
|
||||
chipset = CHIPSETS[config[CONF_CHIPSET]]
|
||||
@@ -169,5 +168,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
cg.add(var.set_use_psram(config[CONF_USE_PSRAM]))
|
||||
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
|
||||
if CONF_USE_DMA in config:
|
||||
cg.add(var.set_use_dma(config[CONF_USE_DMA]))
|
||||
cg.add_optional(var.set_use_dma, config.get(CONF_USE_DMA))
|
||||
|
||||
@@ -366,20 +366,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
|
||||
|
||||
if variant in (VARIANT_ESP32S2, VARIANT_ESP32S3, VARIANT_ESP32P4):
|
||||
if CONF_FILTER_MODE in config:
|
||||
cg.add(touch.set_filter_mode(config[CONF_FILTER_MODE]))
|
||||
if CONF_DEBOUNCE_COUNT in config:
|
||||
cg.add(touch.set_debounce_count(config[CONF_DEBOUNCE_COUNT]))
|
||||
if CONF_NOISE_THRESHOLD in config:
|
||||
cg.add(touch.set_noise_threshold(config[CONF_NOISE_THRESHOLD]))
|
||||
if CONF_JITTER_STEP in config:
|
||||
cg.add(touch.set_jitter_step(config[CONF_JITTER_STEP]))
|
||||
if CONF_SMOOTH_MODE in config:
|
||||
cg.add(touch.set_smooth_level(config[CONF_SMOOTH_MODE]))
|
||||
if CONF_DENOISE_GRADE in config:
|
||||
cg.add(touch.set_denoise_grade(config[CONF_DENOISE_GRADE]))
|
||||
if CONF_DENOISE_CAP_LEVEL in config:
|
||||
cg.add(touch.set_denoise_cap(config[CONF_DENOISE_CAP_LEVEL]))
|
||||
cg.add_optional(touch.set_filter_mode, config.get(CONF_FILTER_MODE))
|
||||
cg.add_optional(touch.set_debounce_count, config.get(CONF_DEBOUNCE_COUNT))
|
||||
cg.add_optional(touch.set_noise_threshold, config.get(CONF_NOISE_THRESHOLD))
|
||||
cg.add_optional(touch.set_jitter_step, config.get(CONF_JITTER_STEP))
|
||||
cg.add_optional(touch.set_smooth_level, config.get(CONF_SMOOTH_MODE))
|
||||
cg.add_optional(touch.set_denoise_grade, config.get(CONF_DENOISE_GRADE))
|
||||
cg.add_optional(touch.set_denoise_cap, config.get(CONF_DENOISE_CAP_LEVEL))
|
||||
if CONF_WATERPROOF_GUARD_RING in config:
|
||||
cg.add(
|
||||
touch.set_waterproof_guard_ring_pad(config[CONF_WATERPROOF_GUARD_RING])
|
||||
|
||||
@@ -151,10 +151,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_traffic_generator_target_ip(target_ip))
|
||||
if (group := config.get(CONF_CSI_TRAFFIC_MULTICAST_GROUP)) is not None:
|
||||
cg.add(var.set_csi_traffic_multicast_group(group))
|
||||
if (on_hits := config.get(CONF_MOTION_ON_HITS)) is not None:
|
||||
cg.add(var.set_motion_on_hits(on_hits))
|
||||
if (off_hits := config.get(CONF_MOTION_OFF_HITS)) is not None:
|
||||
cg.add(var.set_motion_off_hits(off_hits))
|
||||
cg.add_optional(var.set_motion_on_hits, config.get(CONF_MOTION_ON_HITS))
|
||||
cg.add_optional(var.set_motion_off_hits, config.get(CONF_MOTION_OFF_HITS))
|
||||
if esp32.get_esp32_variant() == VARIANT_ESP32C5:
|
||||
band = CORE.config[WIFI_DOMAIN].get(wifi.CONF_BAND_MODE, "AUTO")
|
||||
cg.add(
|
||||
|
||||
@@ -724,8 +724,7 @@ async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
|
||||
cg.add(var.set_polling_interval(config[CONF_POLLING_INTERVAL]))
|
||||
if _is_framework_spi_polling_mode_supported():
|
||||
cg.add_define("USE_ETHERNET_SPI_POLLING_SUPPORT")
|
||||
if CONF_RESET_PIN in config:
|
||||
cg.add(var.set_reset_pin(config[CONF_RESET_PIN]))
|
||||
cg.add_optional(var.set_reset_pin, config.get(CONF_RESET_PIN))
|
||||
cg.add(var.set_clock_speed(config[CONF_CLOCK_SPEED]))
|
||||
|
||||
cg.add_define("USE_ETHERNET_SPI")
|
||||
@@ -746,8 +745,7 @@ async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
|
||||
elif config[CONF_TYPE] in ("GENERIC", "YT8531"):
|
||||
# RGMII data pins come from the IDF default config; set MDC/MDIO + PHY addr.
|
||||
_add_mdio_setters(var, config)
|
||||
if CONF_POWER_PIN in config:
|
||||
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
||||
cg.add_optional(var.set_power_pin, config.get(CONF_POWER_PIN))
|
||||
_add_phy_registers(var, config)
|
||||
else:
|
||||
_add_mdio_setters(var, config)
|
||||
@@ -755,8 +753,7 @@ async def _to_code_esp32(var: cg.MockObj, config: ConfigType) -> None:
|
||||
cg.add(var.set_clk_mode(clk_mode))
|
||||
if (clk_pin := config[CONF_CLK][CONF_PIN]) != DEFAULT_CLK_PIN:
|
||||
cg.add(var.set_clk_pin(clk_pin))
|
||||
if CONF_POWER_PIN in config:
|
||||
cg.add(var.set_power_pin(config[CONF_POWER_PIN]))
|
||||
cg.add_optional(var.set_power_pin, config.get(CONF_POWER_PIN))
|
||||
_add_phy_registers(var, config)
|
||||
|
||||
# Register Ethernet with the esp32 sdkconfig reconciler. It disables the
|
||||
@@ -782,10 +779,8 @@ async def _to_code_rp2040(var: cg.MockObj, config: ConfigType) -> None:
|
||||
cg.add(var.set_miso_pin(config[CONF_MISO_PIN]))
|
||||
cg.add(var.set_mosi_pin(config[CONF_MOSI_PIN]))
|
||||
cg.add(var.set_cs_pin(config[CONF_CS_PIN]))
|
||||
if CONF_INTERRUPT_PIN in config:
|
||||
cg.add(var.set_interrupt_pin(config[CONF_INTERRUPT_PIN]))
|
||||
if CONF_RESET_PIN in config:
|
||||
cg.add(var.set_reset_pin(config[CONF_RESET_PIN]))
|
||||
cg.add_optional(var.set_interrupt_pin, config.get(CONF_INTERRUPT_PIN))
|
||||
cg.add_optional(var.set_reset_pin, config.get(CONF_RESET_PIN))
|
||||
|
||||
cg.add_define("USE_ETHERNET_SPI")
|
||||
cg.add_library(_RP2_SPI_LIBRARIES[config[CONF_TYPE]], None)
|
||||
|
||||
@@ -83,8 +83,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
if (time_constant := config.get(CONF_TIME_CONSTANT)) is not None:
|
||||
cg.add(var.set_time_constant(time_constant))
|
||||
if (weighting := config.get(CONF_TIME_WEIGHTING)) is not None:
|
||||
cg.add(var.set_time_weighting(weighting))
|
||||
cg.add_optional(var.set_time_weighting, config.get(CONF_TIME_WEIGHTING))
|
||||
else:
|
||||
cg.add(var.set_alpha(config.get(CONF_ALPHA, DEFAULT_ALPHA)))
|
||||
cg.add(var.set_restore(config[CONF_RESTORE]))
|
||||
|
||||
@@ -41,8 +41,7 @@ async def new_fastled_light(config: ConfigType) -> MockObj:
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
cg.add_optional(var.set_max_refresh_rate, config.get(CONF_MAX_REFRESH_RATE))
|
||||
|
||||
if CORE.is_esp32:
|
||||
from esphome.components.esp32 import add_idf_component
|
||||
|
||||
@@ -147,8 +147,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
|
||||
# OTHER
|
||||
if CONF_MAX_DURATION in config:
|
||||
cg.add(var.set_max_duration(config[CONF_MAX_DURATION]))
|
||||
cg.add_optional(var.set_max_duration, config.get(CONF_MAX_DURATION))
|
||||
|
||||
cg.add(var.set_has_built_in_endstop(config[CONF_HAS_BUILT_IN_ENDSTOP]))
|
||||
|
||||
|
||||
@@ -66,9 +66,8 @@ constexpr uint8_t FUJITSU_GENERAL_FAN_SILENT = 0x04;
|
||||
constexpr uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
|
||||
constexpr uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
|
||||
|
||||
constexpr uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
|
||||
constexpr uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
|
||||
constexpr uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
|
||||
static constexpr remote_base::BitTimings FUJITSU_GENERAL_BIT_TIMINGS{
|
||||
.bit_mark = 420, .one_space = 1200, .zero_space = 420};
|
||||
|
||||
constexpr uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
|
||||
constexpr uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
|
||||
@@ -207,14 +206,7 @@ void FujitsuGeneralClimate::transmit_(uint8_t const *message, uint8_t length) {
|
||||
data->space(FUJITSU_GENERAL_HEADER_SPACE);
|
||||
|
||||
// Data
|
||||
for (uint8_t i = 0; i < length; ++i) {
|
||||
const uint8_t byte = message[i];
|
||||
for (uint8_t mask = 0b00000001; mask > 0; mask <<= 1) { // write from right to left
|
||||
data->mark(FUJITSU_GENERAL_BIT_MARK);
|
||||
bool bit = byte & mask;
|
||||
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(message, length);
|
||||
|
||||
// Footer
|
||||
data->mark(FUJITSU_GENERAL_TRL_MARK);
|
||||
@@ -284,19 +276,12 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
|
||||
uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH];
|
||||
|
||||
// Read header
|
||||
for (uint8_t byte = 0; byte < FUJITSU_GENERAL_COMMON_LENGTH; ++byte) {
|
||||
// Read bit
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
|
||||
recv_message[byte] |= 1 << bit; // read from right to left
|
||||
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!data.expect_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(recv_message, FUJITSU_GENERAL_COMMON_LENGTH)) {
|
||||
ESP_LOGV(TAG, "Header bytes fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t recv_message_type = recv_message[FUJITSU_GENERAL_MESSAGE_TYPE_BYTE];
|
||||
@@ -319,15 +304,10 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
}
|
||||
|
||||
// Read message body
|
||||
for (uint8_t byte = FUJITSU_GENERAL_COMMON_LENGTH; byte < recv_message_length; ++byte) {
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
|
||||
recv_message[byte] |= 1 << bit; // read from right to left
|
||||
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!data.expect_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(recv_message + FUJITSU_GENERAL_COMMON_LENGTH,
|
||||
recv_message_length - FUJITSU_GENERAL_COMMON_LENGTH)) {
|
||||
ESP_LOGV(TAG, "Body bytes fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t byte = 0; byte < recv_message_length; ++byte) {
|
||||
|
||||
@@ -151,21 +151,14 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Graph options
|
||||
if CONF_X_GRID in config:
|
||||
cg.add(var.set_grid_x(config[CONF_X_GRID]))
|
||||
if CONF_Y_GRID in config:
|
||||
cg.add(var.set_grid_y(config[CONF_Y_GRID]))
|
||||
if CONF_BORDER in config:
|
||||
cg.add(var.set_border(config[CONF_BORDER]))
|
||||
cg.add_optional(var.set_grid_x, config.get(CONF_X_GRID))
|
||||
cg.add_optional(var.set_grid_y, config.get(CONF_Y_GRID))
|
||||
cg.add_optional(var.set_border, config.get(CONF_BORDER))
|
||||
# Axis related options
|
||||
if CONF_MIN_VALUE in config:
|
||||
cg.add(var.set_min_value(config[CONF_MIN_VALUE]))
|
||||
if CONF_MAX_VALUE in config:
|
||||
cg.add(var.set_max_value(config[CONF_MAX_VALUE]))
|
||||
if CONF_MIN_RANGE in config:
|
||||
cg.add(var.set_min_range(config[CONF_MIN_RANGE]))
|
||||
if CONF_MAX_RANGE in config:
|
||||
cg.add(var.set_max_range(config[CONF_MAX_RANGE]))
|
||||
cg.add_optional(var.set_min_value, config.get(CONF_MIN_VALUE))
|
||||
cg.add_optional(var.set_max_value, config.get(CONF_MAX_VALUE))
|
||||
cg.add_optional(var.set_min_range, config.get(CONF_MIN_RANGE))
|
||||
cg.add_optional(var.set_max_range, config.get(CONF_MAX_RANGE))
|
||||
# Trace options
|
||||
for trace in config[CONF_TRACES]:
|
||||
tr = cg.new_Pvariable(trace[CONF_ID], GraphTrace())
|
||||
|
||||
@@ -373,20 +373,14 @@ async def to_code(config):
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL]))
|
||||
if CONF_CONTROL_METHOD in config:
|
||||
cg.add(var.set_control_method(config[CONF_CONTROL_METHOD]))
|
||||
if CONF_BEEPER in config:
|
||||
cg.add(var.set_beeper_state(config[CONF_BEEPER]))
|
||||
if CONF_DISPLAY in config:
|
||||
cg.add(var.set_display_state(config[CONF_DISPLAY]))
|
||||
if CONF_SUPPORTED_MODES in config:
|
||||
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
|
||||
cg.add_optional(var.set_control_method, config.get(CONF_CONTROL_METHOD))
|
||||
cg.add_optional(var.set_beeper_state, config.get(CONF_BEEPER))
|
||||
cg.add_optional(var.set_display_state, config.get(CONF_DISPLAY))
|
||||
cg.add_optional(var.set_supported_modes, config.get(CONF_SUPPORTED_MODES))
|
||||
if CONF_SUPPORTED_SWING_MODES in config:
|
||||
cg.add(var.set_supported_swing_modes(config[CONF_SUPPORTED_SWING_MODES]))
|
||||
if CONF_SUPPORTED_PRESETS in config:
|
||||
cg.add(var.set_supported_presets(config[CONF_SUPPORTED_PRESETS]))
|
||||
if CONF_ANSWER_TIMEOUT in config:
|
||||
cg.add(var.set_answer_timeout(config[CONF_ANSWER_TIMEOUT]))
|
||||
cg.add_optional(var.set_supported_presets, config.get(CONF_SUPPORTED_PRESETS))
|
||||
cg.add_optional(var.set_answer_timeout, config.get(CONF_ANSWER_TIMEOUT))
|
||||
if CONF_ALTERNATIVE_SWING_CONTROL in config:
|
||||
cg.add(
|
||||
var.set_alternative_swing_control(config[CONF_ALTERNATIVE_SWING_CONTROL])
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_LIGHT
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from ..climate import HaierTundra, haier_tundra_ns
|
||||
from ..climate import HaierTundra
|
||||
|
||||
CODEOWNERS = ["@fauxpark"]
|
||||
|
||||
HaierTundraButton = haier_tundra_ns.class_(
|
||||
"HaierTundraButton", button.Button, cg.Component
|
||||
)
|
||||
ButtonKind = haier_tundra_ns.enum("ButtonKind", is_class=True)
|
||||
|
||||
ICON_LED_OUTLINE = "mdi:led-outline"
|
||||
ICON_SPRAY_BOTTLE = "mdi:spray-bottle"
|
||||
|
||||
@@ -23,21 +17,16 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_HAIER_TUNDRA_ID): cv.use_id(HaierTundra),
|
||||
cv.Optional(CONF_LIGHT): button.button_schema(
|
||||
HaierTundraButton, icon=ICON_LED_OUTLINE
|
||||
button.ApplyButton, icon=ICON_LED_OUTLINE
|
||||
),
|
||||
cv.Optional(CONF_SELF_CLEAN): button.button_schema(
|
||||
HaierTundraButton, icon=ICON_SPRAY_BOTTLE
|
||||
button.ApplyButton, icon=ICON_SPRAY_BOTTLE
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_HAIER_TUNDRA_ID])
|
||||
for key, kind in (
|
||||
(CONF_LIGHT, ButtonKind.BUTTON_KIND_LIGHT),
|
||||
(CONF_SELF_CLEAN, ButtonKind.BUTTON_KIND_SELF_CLEAN),
|
||||
):
|
||||
if (conf := config.get(key)) is not None:
|
||||
var = await button.new_button(conf, hub, kind)
|
||||
await cg.register_component(var, conf)
|
||||
buttons = button.sub_buttons(config, parent=config[CONF_HAIER_TUNDRA_ID])
|
||||
await buttons(CONF_LIGHT, None, apply="toggle_light()")
|
||||
await buttons(CONF_SELF_CLEAN, None, apply="start_self_clean()")
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "haier_tundra_button.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::haier_tundra {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "haier_tundra.button");
|
||||
|
||||
void HaierTundraButton::dump_config() { LOG_BUTTON("", "Haier Tundra Button", this); }
|
||||
|
||||
void HaierTundraButton::press_action() {
|
||||
if (this->kind_ == ButtonKind::BUTTON_KIND_LIGHT) {
|
||||
this->parent_->toggle_light();
|
||||
} else {
|
||||
this->parent_->start_self_clean();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::haier_tundra
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "esphome/components/haier_tundra/haier_tundra.h"
|
||||
|
||||
namespace esphome::haier_tundra {
|
||||
|
||||
enum class ButtonKind : uint8_t {
|
||||
BUTTON_KIND_LIGHT,
|
||||
BUTTON_KIND_SELF_CLEAN,
|
||||
};
|
||||
|
||||
class HaierTundraButton final : public button::Button, public Component, public Parented<HaierTundra> {
|
||||
public:
|
||||
HaierTundraButton(HaierTundra *parent, ButtonKind kind) : Parented(parent), kind_(kind) {}
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
||||
ButtonKind kind_;
|
||||
};
|
||||
|
||||
} // namespace esphome::haier_tundra
|
||||
@@ -60,5 +60,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.add_variable(var.set_pin_b, config[CONF_PIN_B])
|
||||
await cg.add_variable(var.set_enable_pin, config.get(CONF_ENABLE_PIN))
|
||||
|
||||
if CONF_PRESET_MODES in config:
|
||||
cg.add(var.set_preset_modes(config[CONF_PRESET_MODES]))
|
||||
cg.add_optional(var.set_preset_modes, config.get(CONF_PRESET_MODES))
|
||||
|
||||
@@ -15,24 +15,15 @@ static constexpr uint8_t MODE_MASK = 0x07;
|
||||
static constexpr uint8_t TEMP_MASK = 0x0F;
|
||||
static constexpr uint8_t TEMPERATURE_BASE = 17;
|
||||
static constexpr uint8_t TEMPERATURE_SHIFT = 4;
|
||||
static constexpr remote_base::BitTimings MITSUBISHI_HEAVY_BIT_TIMINGS{.bit_mark = MITSUBISHI_HEAVY_BIT_MARK,
|
||||
.one_space = MITSUBISHI_HEAVY_ONE_SPACE,
|
||||
.zero_space = MITSUBISHI_HEAVY_ZERO_SPACE};
|
||||
|
||||
static bool read_raw_bytes(remote_base::RemoteReceiveData &data, uint8_t frame[MITSUBISHI_HEAVY_FRAME_LENGTH]) {
|
||||
if (!data.expect_item(MITSUBISHI_HEAVY_HDR_MARK, MITSUBISHI_HEAVY_HDR_SPACE))
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < MITSUBISHI_HEAVY_FRAME_LENGTH; i++) {
|
||||
uint8_t byte = 0;
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ZERO_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
frame[i] = byte;
|
||||
}
|
||||
|
||||
return true;
|
||||
return data.expect_bytes<MITSUBISHI_HEAVY_BIT_TIMINGS>(frame, MITSUBISHI_HEAVY_FRAME_LENGTH);
|
||||
}
|
||||
|
||||
static bool validate_prefix(const uint8_t frame[MITSUBISHI_HEAVY_FRAME_LENGTH]) {
|
||||
|
||||
@@ -224,13 +224,7 @@ void HitachiClimate::transmit_state() {
|
||||
// Header
|
||||
data->item(HITACHI_AC424_HDR_MARK, HITACHI_AC424_HDR_SPACE);
|
||||
// Data
|
||||
for (uint8_t i : remote_state_) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
data->mark(HITACHI_AC424_BIT_MARK);
|
||||
bool bit = i & (1 << j);
|
||||
data->space(bit ? HITACHI_AC424_ONE_SPACE : HITACHI_AC424_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<HITACHI_AC424_BIT_TIMINGS>(remote_state_, HITACHI_AC424_STATE_LENGTH);
|
||||
// Footer
|
||||
data->item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_MIN_GAP);
|
||||
}
|
||||
@@ -320,18 +314,10 @@ bool HitachiClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t recv_state[HITACHI_AC424_STATE_LENGTH] = {0};
|
||||
// Read all bytes.
|
||||
for (uint8_t pos = 0; pos < HITACHI_AC424_STATE_LENGTH; pos++) {
|
||||
// Read bit
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_ONE_SPACE)) {
|
||||
recv_state[pos] |= 1 << bit;
|
||||
} else if (!data.expect_item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_ZERO_SPACE)) {
|
||||
ESP_LOGVV(TAG, "Byte %d bit %d fail", pos, bit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t recv_state[HITACHI_AC424_STATE_LENGTH];
|
||||
if (!data.expect_bytes<HITACHI_AC424_BIT_TIMINGS>(recv_state, HITACHI_AC424_STATE_LENGTH)) {
|
||||
ESP_LOGVV(TAG, "Data fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate footer
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace esphome::hitachi_ac424 {
|
||||
const uint16_t HITACHI_AC424_HDR_MARK = 3416; // ac
|
||||
const uint16_t HITACHI_AC424_HDR_SPACE = 1604; // ac
|
||||
const uint16_t HITACHI_AC424_BIT_MARK = 463;
|
||||
const uint16_t HITACHI_AC424_ONE_SPACE = 1208;
|
||||
const uint16_t HITACHI_AC424_ZERO_SPACE = 372;
|
||||
static constexpr remote_base::BitTimings HITACHI_AC424_BIT_TIMINGS{
|
||||
.bit_mark = HITACHI_AC424_BIT_MARK, .one_space = 1208, .zero_space = 372};
|
||||
const uint32_t HITACHI_AC424_MIN_GAP = 100000; // just a guess.
|
||||
const uint16_t HITACHI_AC424_FREQ = 38000; // Hz.
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ def setup_home_assistant_entity(
|
||||
cg.add(var.set_entity_id(cg.RawExpression(buffer)))
|
||||
else:
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
||||
if CONF_ATTRIBUTE in config:
|
||||
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
|
||||
cg.add_optional(var.set_attribute, config.get(CONF_ATTRIBUTE))
|
||||
cg.add_define("USE_API_HOMEASSISTANT_STATES")
|
||||
if state_subscription_count > 0:
|
||||
data = _get_data()
|
||||
|
||||
@@ -156,10 +156,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_sensor(sens, i))
|
||||
|
||||
cg.add(var.set_model(config[CONF_MODEL]))
|
||||
if CONF_RESOLUTION in config:
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add_optional(var.set_resolution, config.get(CONF_RESOLUTION))
|
||||
|
||||
cg.add(var.set_request_temperature(CONF_TEMPERATURE in config))
|
||||
|
||||
if CONF_DISABLE_LED in config:
|
||||
cg.add(var.set_disable_led(config[CONF_DISABLE_LED]))
|
||||
cg.add_optional(var.set_disable_led, config.get(CONF_DISABLE_LED))
|
||||
|
||||
@@ -333,11 +333,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_sda_pin(config[CONF_SDA]))
|
||||
if CONF_SDA_PULLUP_ENABLED in config:
|
||||
cg.add(var.set_sda_pullup_enabled(config[CONF_SDA_PULLUP_ENABLED]))
|
||||
cg.add_optional(var.set_sda_pullup_enabled, config.get(CONF_SDA_PULLUP_ENABLED))
|
||||
cg.add(var.set_scl_pin(config[CONF_SCL]))
|
||||
if CONF_SCL_PULLUP_ENABLED in config:
|
||||
cg.add(var.set_scl_pullup_enabled(config[CONF_SCL_PULLUP_ENABLED]))
|
||||
cg.add_optional(var.set_scl_pullup_enabled, config.get(CONF_SCL_PULLUP_ENABLED))
|
||||
|
||||
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
||||
cg.add(var.set_scan(config[CONF_SCAN]))
|
||||
@@ -350,11 +348,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_sda_pin(config[CONF_SDA]))
|
||||
if CONF_SDA_PULLUP_ENABLED in config:
|
||||
cg.add(var.set_sda_pullup_enabled(config[CONF_SDA_PULLUP_ENABLED]))
|
||||
cg.add_optional(var.set_sda_pullup_enabled, config.get(CONF_SDA_PULLUP_ENABLED))
|
||||
cg.add(var.set_scl_pin(config[CONF_SCL]))
|
||||
if CONF_SCL_PULLUP_ENABLED in config:
|
||||
cg.add(var.set_scl_pullup_enabled(config[CONF_SCL_PULLUP_ENABLED]))
|
||||
cg.add_optional(var.set_scl_pullup_enabled, config.get(CONF_SCL_PULLUP_ENABLED))
|
||||
|
||||
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
||||
cg.add(var.set_scan(config[CONF_SCAN]))
|
||||
|
||||
@@ -370,12 +370,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
# Helps avoid callbacks being skipped due to processor load
|
||||
add_idf_sdkconfig_option("CONFIG_I2S_ISR_IRAM_SAFE", True)
|
||||
|
||||
if CONF_I2S_LRCLK_PIN in config:
|
||||
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
|
||||
if CONF_I2S_BCLK_PIN in config:
|
||||
cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
|
||||
if CONF_I2S_MCLK_PIN in config:
|
||||
cg.add(var.set_mclk_pin(config[CONF_I2S_MCLK_PIN]))
|
||||
cg.add_optional(var.set_lrclk_pin, config.get(CONF_I2S_LRCLK_PIN))
|
||||
cg.add_optional(var.set_bclk_pin, config.get(CONF_I2S_BCLK_PIN))
|
||||
cg.add_optional(var.set_mclk_pin, config.get(CONF_I2S_MCLK_PIN))
|
||||
if config[CONF_FULL_DUPLEX]:
|
||||
cg.add_define("USE_I2S_AUDIO_FULL_DUPLEX")
|
||||
|
||||
|
||||
@@ -84,5 +84,4 @@ async def to_code(config: dict[str, Any]) -> None:
|
||||
await remote_base.attach_receiver(var, config, CONF_REMOTE_RECEIVER_ID)
|
||||
|
||||
# Set receiver demodulation frequency if specified (metadata only, no hardware effect)
|
||||
if CONF_RECEIVER_FREQUENCY in config:
|
||||
cg.add(var.set_receiver_frequency(config[CONF_RECEIVER_FREQUENCY]))
|
||||
cg.add_optional(var.set_receiver_frequency, config.get(CONF_RECEIVER_FREQUENCY))
|
||||
|
||||
@@ -376,22 +376,19 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
)
|
||||
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
if (reset_duration := config.get(CONF_RESET_DURATION)) is not None:
|
||||
cg.add(var.set_reset_duration(reset_duration))
|
||||
cg.add_optional(var.set_reset_duration, config.get(CONF_RESET_DURATION))
|
||||
if config.get(CONF_INVERT_COLORS):
|
||||
cg.add(var.set_invert_colors(True))
|
||||
if config.get(CONF_SLEEP_WHEN_DONE):
|
||||
cg.add(var.set_sleep_when_done(True))
|
||||
cg.add(var.set_vcom(config[CONF_VCOM]))
|
||||
cg.add(var.set_vcom_register(config[CONF_VCOM_REGISTER]))
|
||||
if CONF_FORCE_TEMPERATURE in config:
|
||||
cg.add(var.set_force_temperature(config[CONF_FORCE_TEMPERATURE]))
|
||||
cg.add_optional(var.set_force_temperature, config.get(CONF_FORCE_TEMPERATURE))
|
||||
if config.get(CONF_USE_LEGACY_DPY_AREA):
|
||||
cg.add(var.set_use_legacy_dpy_area(True))
|
||||
cg.add(var.set_grayscale(config[CONF_GRAYSCALE]))
|
||||
cg.add(var.set_dithering(config[CONF_DITHERING]))
|
||||
if (mode := config.get(CONF_UPDATE_MODE)) is not None:
|
||||
cg.add(var.set_update_mode(mode))
|
||||
cg.add_optional(var.set_update_mode, config.get(CONF_UPDATE_MODE))
|
||||
|
||||
transform = config.get(
|
||||
CONF_TRANSFORM,
|
||||
|
||||
@@ -94,22 +94,14 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
for source_conf in config.get(CONF_SOURCE_ID, ()):
|
||||
await cg.add_variable(var.add_provider, source_conf)
|
||||
if CONF_MIN_LENGTH in config:
|
||||
cg.add(var.set_min_length(config[CONF_MIN_LENGTH]))
|
||||
if CONF_MAX_LENGTH in config:
|
||||
cg.add(var.set_max_length(config[CONF_MAX_LENGTH]))
|
||||
if CONF_START_KEYS in config:
|
||||
cg.add(var.set_start_keys(config[CONF_START_KEYS]))
|
||||
if CONF_END_KEYS in config:
|
||||
cg.add(var.set_end_keys(config[CONF_END_KEYS]))
|
||||
if CONF_END_KEY_REQUIRED in config:
|
||||
cg.add(var.set_end_key_required(config[CONF_END_KEY_REQUIRED]))
|
||||
if CONF_BACK_KEYS in config:
|
||||
cg.add(var.set_back_keys(config[CONF_BACK_KEYS]))
|
||||
if CONF_CLEAR_KEYS in config:
|
||||
cg.add(var.set_clear_keys(config[CONF_CLEAR_KEYS]))
|
||||
if CONF_ALLOWED_KEYS in config:
|
||||
cg.add(var.set_allowed_keys(config[CONF_ALLOWED_KEYS]))
|
||||
cg.add_optional(var.set_min_length, config.get(CONF_MIN_LENGTH))
|
||||
cg.add_optional(var.set_max_length, config.get(CONF_MAX_LENGTH))
|
||||
cg.add_optional(var.set_start_keys, config.get(CONF_START_KEYS))
|
||||
cg.add_optional(var.set_end_keys, config.get(CONF_END_KEYS))
|
||||
cg.add_optional(var.set_end_key_required, config.get(CONF_END_KEY_REQUIRED))
|
||||
cg.add_optional(var.set_back_keys, config.get(CONF_BACK_KEYS))
|
||||
cg.add_optional(var.set_clear_keys, config.get(CONF_CLEAR_KEYS))
|
||||
cg.add_optional(var.set_allowed_keys, config.get(CONF_ALLOWED_KEYS))
|
||||
|
||||
for trigger_name, args in TRIGGER_TYPES.items():
|
||||
arglist: TemplateArgsType = [(arg.type, arg.name) for arg in args]
|
||||
|
||||
@@ -917,6 +917,17 @@ void LD2420Component::init_gate_config_numbers() {
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::set_gate_threshold_value(number::Number *n, float value) {
|
||||
for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
|
||||
const bool move = this->gate_move_threshold_numbers_[gate] == n;
|
||||
if (!move && this->gate_still_threshold_numbers_[gate] != n)
|
||||
continue;
|
||||
const uint8_t target = this->is_gate_select() ? this->get_gate_select_value() : gate;
|
||||
(move ? this->new_config.move_thresh : this->new_config.still_thresh)[target] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::refresh_gate_config_numbers() {
|
||||
this->gate_timeout_number_->publish_state(this->new_config.timeout);
|
||||
this->min_gate_distance_number_->publish_state(this->new_config.min_gate);
|
||||
|
||||
@@ -89,6 +89,8 @@ class LD2420Component final : public Component, public uart::UARTDevice {
|
||||
void publish_gate_still_threshold(uint8_t gate) {
|
||||
this->gate_still_threshold_numbers_[0]->publish_state(this->new_config.still_thresh[gate]);
|
||||
};
|
||||
// Stores a threshold number's value for its gate, or for the selected gate with gate_select
|
||||
void set_gate_threshold_value(number::Number *n, float value);
|
||||
void init_gate_config_numbers();
|
||||
void refresh_gate_config_numbers();
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from functools import partial
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MOVE_THRESHOLD,
|
||||
CONF_STILL_THRESHOLD,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
@@ -16,22 +17,9 @@ from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
|
||||
LD2420TimeoutNumber = ld2420_ns.class_("LD2420TimeoutNumber", number.Number)
|
||||
LD2420MoveSensFactorNumber = ld2420_ns.class_(
|
||||
"LD2420MoveSensFactorNumber", number.Number
|
||||
)
|
||||
LD2420StillSensFactorNumber = ld2420_ns.class_(
|
||||
"LD2420StillSensFactorNumber", number.Number
|
||||
)
|
||||
LD2420MinDistanceNumber = ld2420_ns.class_("LD2420MinDistanceNumber", number.Number)
|
||||
LD2420MaxDistanceNumber = ld2420_ns.class_("LD2420MaxDistanceNumber", number.Number)
|
||||
LD2420GateSelectNumber = ld2420_ns.class_("LD2420GateSelectNumber", number.Number)
|
||||
LD2420MoveThresholdNumbers = ld2420_ns.class_(
|
||||
"LD2420MoveThresholdNumbers", number.Number
|
||||
)
|
||||
LD2420StillThresholdNumbers = ld2420_ns.class_(
|
||||
"LD2420StillThresholdNumbers", number.Number
|
||||
)
|
||||
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
|
||||
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
|
||||
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
|
||||
@@ -46,7 +34,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Inclusive(CONF_PRESENCE_TIMEOUT, TIMEOUT_GROUP): number.number_schema(
|
||||
LD2420TimeoutNumber,
|
||||
number.ApplyNumber,
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_TIMELAPSE,
|
||||
@@ -70,23 +58,23 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_STILL_THRESHOLD, GATE_GROUP): number.number_schema(
|
||||
LD2420StillThresholdNumbers,
|
||||
number.ApplyNumber,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_MOVE_THRESHOLD, GATE_GROUP): number.number_schema(
|
||||
LD2420MoveThresholdNumbers,
|
||||
number.ApplyNumber,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Optional(CONF_GATE_MOVE_SENSITIVITY): number.number_schema(
|
||||
LD2420MoveSensFactorNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
),
|
||||
cv.Optional(CONF_GATE_STILL_SENSITIVITY): number.number_schema(
|
||||
LD2420StillSensFactorNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
@@ -98,12 +86,12 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
cv.Optional(f"gate_{x}"): (
|
||||
{
|
||||
cv.Required(CONF_MOVE_THRESHOLD): number.number_schema(
|
||||
LD2420MoveThresholdNumbers,
|
||||
number.ApplyNumber,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Required(CONF_STILL_THRESHOLD): number.number_schema(
|
||||
LD2420StillThresholdNumbers,
|
||||
number.ApplyNumber,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
@@ -123,6 +111,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
min_value=0,
|
||||
max_value=255,
|
||||
step=5,
|
||||
apply="new_config.timeout = value",
|
||||
publish="before",
|
||||
)
|
||||
await numbers(
|
||||
CONF_MIN_GATE_DISTANCE,
|
||||
@@ -144,6 +134,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
min_value=0.05,
|
||||
max_value=1,
|
||||
step=0.025,
|
||||
apply="gate_move_sensitivity_factor = value",
|
||||
publish="before",
|
||||
)
|
||||
await numbers(
|
||||
CONF_GATE_STILL_SENSITIVITY,
|
||||
@@ -151,7 +143,16 @@ async def to_code(config: ConfigType) -> None:
|
||||
min_value=0.05,
|
||||
max_value=1,
|
||||
step=0.025,
|
||||
apply="gate_still_sensitivity_factor = value",
|
||||
publish="before",
|
||||
)
|
||||
threshold = {
|
||||
"min_value": 0,
|
||||
"max_value": 65535,
|
||||
"step": 25,
|
||||
"apply": "set_gate_threshold_value(entity, value)",
|
||||
"publish": "before",
|
||||
}
|
||||
if config.get(CONF_GATE_SELECT):
|
||||
await numbers(
|
||||
CONF_GATE_SELECT,
|
||||
@@ -160,35 +161,27 @@ async def to_code(config: ConfigType) -> None:
|
||||
max_value=15,
|
||||
step=1,
|
||||
)
|
||||
if gate_still_threshold := config.get(CONF_STILL_THRESHOLD):
|
||||
n = cg.new_Pvariable(gate_still_threshold[CONF_ID])
|
||||
await number.register_number(
|
||||
n, gate_still_threshold, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, hub)
|
||||
cg.add(hub.set_gate_still_threshold_numbers(0, n))
|
||||
if gate_move_threshold := config.get(CONF_MOVE_THRESHOLD):
|
||||
n = cg.new_Pvariable(gate_move_threshold[CONF_ID])
|
||||
await number.register_number(
|
||||
n, gate_move_threshold, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, hub)
|
||||
cg.add(hub.set_gate_move_threshold_numbers(0, n))
|
||||
# With gate_select one pair of numbers sits at index 0 and edits the selected gate
|
||||
await numbers(
|
||||
CONF_STILL_THRESHOLD,
|
||||
partial(hub.set_gate_still_threshold_numbers, 0),
|
||||
**threshold,
|
||||
)
|
||||
await numbers(
|
||||
CONF_MOVE_THRESHOLD,
|
||||
partial(hub.set_gate_move_threshold_numbers, 0),
|
||||
**threshold,
|
||||
)
|
||||
else:
|
||||
for x in range(16):
|
||||
if gate_conf := config.get(f"gate_{x}"):
|
||||
move_config = gate_conf[CONF_MOVE_THRESHOLD]
|
||||
n = cg.new_Pvariable(move_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, move_config, min_value=0, max_value=65535, step=25
|
||||
await numbers.create(
|
||||
gate_conf[CONF_MOVE_THRESHOLD],
|
||||
partial(hub.set_gate_move_threshold_numbers, x),
|
||||
**threshold,
|
||||
)
|
||||
await cg.register_parented(n, hub)
|
||||
cg.add(hub.set_gate_move_threshold_numbers(x, n))
|
||||
|
||||
still_config = gate_conf[CONF_STILL_THRESHOLD]
|
||||
n = cg.new_Pvariable(still_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, still_config, min_value=0, max_value=65535, step=25
|
||||
await numbers.create(
|
||||
gate_conf[CONF_STILL_THRESHOLD],
|
||||
partial(hub.set_gate_still_threshold_numbers, x),
|
||||
**threshold,
|
||||
)
|
||||
await cg.register_parented(n, hub)
|
||||
cg.add(hub.set_gate_still_threshold_numbers(x, n))
|
||||
|
||||
@@ -6,11 +6,6 @@ ESPHOME_LOG_TAG(TAG, "ld2420.number");
|
||||
|
||||
namespace esphome::ld2420 {
|
||||
|
||||
void LD2420TimeoutNumber::control(float timeout) {
|
||||
this->publish_state(timeout);
|
||||
this->parent_->new_config.timeout = timeout;
|
||||
}
|
||||
|
||||
void LD2420MinDistanceNumber::control(float min_gate) {
|
||||
if ((uint16_t) min_gate > this->parent_->new_config.max_gate) {
|
||||
min_gate = this->parent_->get_min_gate_distance_value();
|
||||
@@ -36,36 +31,4 @@ void LD2420GateSelectNumber::control(float gate_select) {
|
||||
this->parent_->publish_gate_still_threshold(gate);
|
||||
}
|
||||
|
||||
void LD2420MoveSensFactorNumber::control(float move_factor) {
|
||||
this->publish_state(move_factor);
|
||||
this->parent_->gate_move_sensitivity_factor = move_factor;
|
||||
}
|
||||
|
||||
void LD2420StillSensFactorNumber::control(float still_factor) {
|
||||
this->publish_state(still_factor);
|
||||
this->parent_->gate_still_sensitivity_factor = still_factor;
|
||||
}
|
||||
|
||||
LD2420MoveThresholdNumbers::LD2420MoveThresholdNumbers(uint8_t gate) : gate_(gate) {}
|
||||
|
||||
void LD2420MoveThresholdNumbers::control(float move_threshold) {
|
||||
this->publish_state(move_threshold);
|
||||
if (!this->parent_->is_gate_select()) {
|
||||
this->parent_->new_config.move_thresh[this->gate_] = move_threshold;
|
||||
} else {
|
||||
this->parent_->new_config.move_thresh[this->parent_->get_gate_select_value()] = move_threshold;
|
||||
}
|
||||
}
|
||||
|
||||
LD2420StillThresholdNumbers::LD2420StillThresholdNumbers(uint8_t gate) : gate_(gate) {}
|
||||
|
||||
void LD2420StillThresholdNumbers::control(float still_threshold) {
|
||||
this->publish_state(still_threshold);
|
||||
if (!this->parent_->is_gate_select()) {
|
||||
this->parent_->new_config.still_thresh[this->gate_] = still_threshold;
|
||||
} else {
|
||||
this->parent_->new_config.still_thresh[this->parent_->get_gate_select_value()] = still_threshold;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2420
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
|
||||
namespace esphome::ld2420 {
|
||||
|
||||
class LD2420TimeoutNumber final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420TimeoutNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float timeout) override;
|
||||
};
|
||||
|
||||
class LD2420MinDistanceNumber final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MinDistanceNumber() = default;
|
||||
@@ -37,40 +29,4 @@ class LD2420GateSelectNumber final : public number::Number, public Parented<LD24
|
||||
void control(float gate_select) override;
|
||||
};
|
||||
|
||||
class LD2420MoveSensFactorNumber final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MoveSensFactorNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float move_factor) override;
|
||||
};
|
||||
|
||||
class LD2420StillSensFactorNumber final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420StillSensFactorNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float still_factor) override;
|
||||
};
|
||||
|
||||
class LD2420StillThresholdNumbers final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420StillThresholdNumbers() = default;
|
||||
LD2420StillThresholdNumbers(uint8_t gate);
|
||||
|
||||
protected:
|
||||
uint8_t gate_;
|
||||
void control(float still_threshold) override;
|
||||
};
|
||||
|
||||
class LD2420MoveThresholdNumbers final : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MoveThresholdNumbers() = default;
|
||||
LD2420MoveThresholdNumbers(uint8_t gate);
|
||||
|
||||
protected:
|
||||
uint8_t gate_;
|
||||
void control(float move_threshold) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2420
|
||||
|
||||
@@ -957,7 +957,13 @@ void LD2450Component::set_direction_text_sensor(uint8_t target, text_sensor::Tex
|
||||
|
||||
// Send Zone coordinates data to LD2450
|
||||
#ifdef USE_NUMBER
|
||||
void LD2450Component::set_zone_coordinate(uint8_t zone) {
|
||||
void LD2450Component::set_zone_coordinate(number::Number *n) {
|
||||
uint8_t zone = 0;
|
||||
while (zone < MAX_ZONES && this->zone_numbers_[zone].x1 != n && this->zone_numbers_[zone].y1 != n &&
|
||||
this->zone_numbers_[zone].x2 != n && this->zone_numbers_[zone].y2 != n)
|
||||
zone++;
|
||||
if (zone == MAX_ZONES)
|
||||
return;
|
||||
number::Number *x1sens = this->zone_numbers_[zone].x1;
|
||||
number::Number *y1sens = this->zone_numbers_[zone].y1;
|
||||
number::Number *x2sens = this->zone_numbers_[zone].x2;
|
||||
|
||||
@@ -128,7 +128,7 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
void set_direction_text_sensor(uint8_t target, text_sensor::TextSensor *s);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void set_zone_coordinate(uint8_t zone);
|
||||
void set_zone_coordinate(number::Number *n);
|
||||
void set_zone_numbers(uint8_t zone, number::Number *x1, number::Number *y1, number::Number *x2, number::Number *y2);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
|
||||
@@ -25,7 +25,6 @@ ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE = "mdi:arrow-top-left-bold-box-outline"
|
||||
MAX_ZONES = 3
|
||||
|
||||
PresenceTimeoutNumber = ld2450_ns.class_("PresenceTimeoutNumber", number.Number)
|
||||
ZoneCoordinateNumber = ld2450_ns.class_("ZoneCoordinateNumber", number.Number)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
@@ -45,28 +44,28 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
cv.Optional(f"zone_{n + 1}"): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_X1): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_TOP_LEFT_BOLD_BOX_OUTLINE,
|
||||
),
|
||||
cv.Required(CONF_Y1): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_TOP_LEFT,
|
||||
),
|
||||
cv.Required(CONF_X2): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_ARROW_BOTTOM_RIGHT_BOLD_BOX_OUTLINE,
|
||||
),
|
||||
cv.Required(CONF_Y2): number.number_schema(
|
||||
ZoneCoordinateNumber,
|
||||
number.ApplyNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
unit_of_measurement=UNIT_MILLIMETER,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
@@ -91,32 +90,21 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
for zone_num in range(MAX_ZONES):
|
||||
if zone_conf := config.get(f"zone_{zone_num + 1}"):
|
||||
zone_x1_config = zone_conf.get(CONF_X1)
|
||||
x1 = cg.new_Pvariable(zone_x1_config[CONF_ID], zone_num)
|
||||
await number.register_number(
|
||||
x1, zone_x1_config, min_value=-4860, max_value=4860, step=1
|
||||
)
|
||||
await cg.register_parented(x1, hub)
|
||||
|
||||
zone_y1_config = zone_conf.get(CONF_Y1)
|
||||
y1 = cg.new_Pvariable(zone_y1_config[CONF_ID], zone_num)
|
||||
await number.register_number(
|
||||
y1, zone_y1_config, min_value=0, max_value=7560, step=1
|
||||
)
|
||||
await cg.register_parented(y1, hub)
|
||||
|
||||
zone_x2_config = zone_conf.get(CONF_X2)
|
||||
x2 = cg.new_Pvariable(zone_x2_config[CONF_ID], zone_num)
|
||||
await number.register_number(
|
||||
x2, zone_x2_config, min_value=-4860, max_value=4860, step=1
|
||||
)
|
||||
await cg.register_parented(x2, hub)
|
||||
|
||||
zone_y2_config = zone_conf.get(CONF_Y2)
|
||||
y2 = cg.new_Pvariable(zone_y2_config[CONF_ID], zone_num)
|
||||
await number.register_number(
|
||||
y2, zone_y2_config, min_value=0, max_value=7560, step=1
|
||||
)
|
||||
await cg.register_parented(y2, hub)
|
||||
|
||||
x1, y1, x2, y2 = [
|
||||
await numbers.create(
|
||||
zone_conf[key],
|
||||
None,
|
||||
min_value=lo,
|
||||
max_value=hi,
|
||||
step=1,
|
||||
apply="set_zone_coordinate(entity)",
|
||||
publish="before",
|
||||
)
|
||||
for key, lo, hi in (
|
||||
(CONF_X1, -4860, 4860),
|
||||
(CONF_Y1, 0, 7560),
|
||||
(CONF_X2, -4860, 4860),
|
||||
(CONF_Y2, 0, 7560),
|
||||
)
|
||||
]
|
||||
cg.add(hub.set_zone_numbers(zone_num, x1, y1, x2, y2))
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "zone_coordinate_number.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
ZoneCoordinateNumber::ZoneCoordinateNumber(uint8_t zone) : zone_(zone) {}
|
||||
|
||||
void ZoneCoordinateNumber::control(float value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_zone_coordinate(this->zone_);
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../ld2450.h"
|
||||
|
||||
namespace esphome::ld2450 {
|
||||
|
||||
class ZoneCoordinateNumber : public number::Number, public Parented<LD2450Component> {
|
||||
public:
|
||||
ZoneCoordinateNumber(uint8_t zone);
|
||||
|
||||
protected:
|
||||
uint8_t zone_;
|
||||
void control(float value) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
@@ -67,11 +67,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID], gpio)
|
||||
await cg.register_component(var, config)
|
||||
await output.register_output(var, config)
|
||||
if CONF_CHANNEL in config:
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
cg.add_optional(var.set_channel, config.get(CONF_CHANNEL))
|
||||
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
|
||||
if CONF_PHASE_ANGLE in config:
|
||||
cg.add(var.set_phase_angle(config[CONF_PHASE_ANGLE]))
|
||||
cg.add_optional(var.set_phase_angle, config.get(CONF_PHASE_ANGLE))
|
||||
|
||||
|
||||
automation.register_apply_action(
|
||||
|
||||
@@ -85,10 +85,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
cg.add(var.set_keys(table))
|
||||
cg.add(var.set_debounce_time(config[CONF_DEBOUNCE_TIME]))
|
||||
if CONF_HAS_DIODES in config:
|
||||
cg.add(var.set_has_diodes(config[CONF_HAS_DIODES]))
|
||||
if CONF_HAS_PULLDOWNS in config:
|
||||
cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS]))
|
||||
cg.add_optional(var.set_has_diodes, config.get(CONF_HAS_DIODES))
|
||||
cg.add_optional(var.set_has_pulldowns, config.get(CONF_HAS_PULLDOWNS))
|
||||
for conf in config.get(CONF_ON_KEY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
cg.add(var.register_key_trigger(trigger))
|
||||
|
||||
@@ -88,8 +88,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
cg.add(var.set_warmup_seconds(config[CONF_WARMUP_TIME]))
|
||||
|
||||
if CONF_DETECTION_RANGE in config:
|
||||
cg.add(var.set_detection_range(config[CONF_DETECTION_RANGE]))
|
||||
cg.add_optional(var.set_detection_range, config.get(CONF_DETECTION_RANGE))
|
||||
|
||||
|
||||
NO_ARGS_ACTION_SCHEMA = maybe_simple_id(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType
|
||||
@@ -9,12 +8,9 @@ from .. import (
|
||||
CONF_MICRONOVA_ID,
|
||||
MICRONOVA_ADDRESS_SCHEMA,
|
||||
MicroNova,
|
||||
micronova_ns,
|
||||
register_micronova_writer,
|
||||
)
|
||||
|
||||
MicroNovaButton = micronova_ns.class_("MicroNovaButton", button.Button, cg.Component)
|
||||
|
||||
CONF_CUSTOM_BUTTON = "custom_button"
|
||||
CONF_MEMORY_DATA = "memory_data"
|
||||
|
||||
@@ -22,7 +18,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_MICRONOVA_ID): cv.use_id(MicroNova),
|
||||
cv.Optional(CONF_CUSTOM_BUTTON): button.button_schema(
|
||||
MicroNovaButton,
|
||||
button.ApplyButton,
|
||||
)
|
||||
.extend(
|
||||
MICRONOVA_ADDRESS_SCHEMA(
|
||||
@@ -35,11 +31,13 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
mv = await cg.get_variable(config[CONF_MICRONOVA_ID])
|
||||
|
||||
if custom_button_config := config.get(CONF_CUSTOM_BUTTON):
|
||||
if conf := config.get(CONF_CUSTOM_BUTTON):
|
||||
register_micronova_writer()
|
||||
bt = await button.new_button(custom_button_config, mv)
|
||||
cg.add(bt.set_memory_location(custom_button_config[CONF_MEMORY_LOCATION]))
|
||||
cg.add(bt.set_memory_address(custom_button_config[CONF_MEMORY_ADDRESS]))
|
||||
cg.add(bt.set_memory_data(custom_button_config[CONF_MEMORY_DATA]))
|
||||
location = conf[CONF_MEMORY_LOCATION]
|
||||
address = conf[CONF_MEMORY_ADDRESS]
|
||||
data = conf[CONF_MEMORY_DATA]
|
||||
await button.sub_buttons(config, parent=config[CONF_MICRONOVA_ID]).create(
|
||||
conf,
|
||||
None,
|
||||
apply=f"queue_write_command(0x{location:02X}, 0x{address:02X}, 0x{data:02X})",
|
||||
)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#include "micronova_button.h"
|
||||
|
||||
namespace esphome::micronova {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "micronova.button");
|
||||
|
||||
void MicroNovaButton::dump_config() {
|
||||
LOG_BUTTON("", "Micronova button", this);
|
||||
this->dump_base_config();
|
||||
}
|
||||
|
||||
void MicroNovaButton::press_action() {
|
||||
this->micronova_->queue_write_command(this->memory_location_, this->memory_address_, this->memory_data_);
|
||||
}
|
||||
|
||||
} // namespace esphome::micronova
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/micronova/micronova.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaButton final : public Component, public button::Button, public MicroNovaBaseListener {
|
||||
public:
|
||||
MicroNovaButton(MicroNova *m) : MicroNovaBaseListener(m) {}
|
||||
void dump_config() override;
|
||||
|
||||
void set_memory_data(uint8_t f) { this->memory_data_ = f; }
|
||||
uint8_t get_memory_data() { return this->memory_data_; }
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
||||
uint8_t memory_data_ = 0;
|
||||
};
|
||||
|
||||
} // namespace esphome::micronova
|
||||
@@ -427,8 +427,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
# Only a build with a datasheet model links the power law and its libm
|
||||
cg.add_define("USE_MICS_5524_DATASHEET")
|
||||
cg.add(var.set_vcc(config[CONF_VCC]))
|
||||
if (rl := config.get(CONF_RL)) is not None:
|
||||
cg.add(var.set_rl(rl))
|
||||
cg.add_optional(var.set_rl, config.get(CONF_RL))
|
||||
cg.add(var.set_voltage_multiplier(_voltage_multiplier(config)))
|
||||
cg.add(var.set_samples(config[CONF_SAMPLE_COUNT]))
|
||||
cg.add(var.set_warmup_time(config[CONF_WARMUP_TIME]))
|
||||
|
||||
@@ -225,16 +225,12 @@ async def to_code(config):
|
||||
cg.add(var.set_beeper_feedback(config[CONF_BEEPER]))
|
||||
cg.add(var.set_autoconf(config[CONF_AUTOCONF]))
|
||||
cg.add(var.set_display_control(config[CONF_DISPLAY_CONTROL]))
|
||||
if CONF_SUPPORTED_MODES in config:
|
||||
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
|
||||
cg.add_optional(var.set_supported_modes, config.get(CONF_SUPPORTED_MODES))
|
||||
if CONF_SUPPORTED_SWING_MODES in config:
|
||||
cg.add(var.set_supported_swing_modes(config[CONF_SUPPORTED_SWING_MODES]))
|
||||
if CONF_SUPPORTED_PRESETS in config:
|
||||
cg.add(var.set_supported_presets(config[CONF_SUPPORTED_PRESETS]))
|
||||
if CONF_CUSTOM_PRESETS in config:
|
||||
cg.add(var.set_custom_presets(config[CONF_CUSTOM_PRESETS]))
|
||||
if CONF_CUSTOM_FAN_MODES in config:
|
||||
cg.add(var.set_custom_fan_modes(config[CONF_CUSTOM_FAN_MODES]))
|
||||
cg.add_optional(var.set_supported_presets, config.get(CONF_SUPPORTED_PRESETS))
|
||||
cg.add_optional(var.set_custom_presets, config.get(CONF_CUSTOM_PRESETS))
|
||||
cg.add_optional(var.set_custom_fan_modes, config.get(CONF_CUSTOM_FAN_MODES))
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_OUTDOOR_TEMPERATURE, var.set_outdoor_temperature_sensor)
|
||||
await sensors(CONF_POWER_USAGE, var.set_power_sensor)
|
||||
|
||||
@@ -40,8 +40,8 @@ const uint8_t MITSUBISHI_NIGHTMODE = 0xC1;
|
||||
|
||||
// Pulse parameters in usec
|
||||
const uint16_t MITSUBISHI_BIT_MARK = 430;
|
||||
const uint16_t MITSUBISHI_ONE_SPACE = 1250;
|
||||
const uint16_t MITSUBISHI_ZERO_SPACE = 390;
|
||||
static constexpr remote_base::BitTimings MITSUBISHI_BIT_TIMINGS{
|
||||
.bit_mark = MITSUBISHI_BIT_MARK, .one_space = 1250, .zero_space = 390};
|
||||
const uint16_t MITSUBISHI_HEADER_MARK = 3500;
|
||||
const uint16_t MITSUBISHI_HEADER_SPACE = 1700;
|
||||
const uint16_t MITSUBISHI_MIN_GAP = 17500;
|
||||
@@ -102,8 +102,8 @@ void MitsubishiClimate::transmit_state() {
|
||||
// Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
|
||||
// Byte 16: Left vane control (for specific models). Constants match right vane controls.
|
||||
// Byte 17: Checksum: SUM[Byte0...Byte16]
|
||||
uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t remote_state[MITSUBISHI_FRAME_SIZE] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
@@ -259,13 +259,7 @@ void MitsubishiClimate::transmit_state() {
|
||||
data->mark(MITSUBISHI_HEADER_MARK);
|
||||
data->space(MITSUBISHI_HEADER_SPACE);
|
||||
// Data
|
||||
for (uint8_t i : remote_state) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
data->mark(MITSUBISHI_BIT_MARK);
|
||||
bool bit = i & (1 << j);
|
||||
data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->item_bytes<MITSUBISHI_BIT_TIMINGS>(remote_state, sizeof(remote_state));
|
||||
// Footer
|
||||
if (r == 0) {
|
||||
data->mark(MITSUBISHI_BIT_MARK);
|
||||
@@ -280,33 +274,21 @@ void MitsubishiClimate::transmit_state() {
|
||||
bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; }
|
||||
|
||||
bool MitsubishiClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t state_frame[18] = {};
|
||||
uint8_t state_frame[MITSUBISHI_FRAME_SIZE];
|
||||
|
||||
if (!data.expect_item(MITSUBISHI_HEADER_MARK, MITSUBISHI_HEADER_SPACE)) {
|
||||
ESP_LOGV(TAG, "Header fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t pos = 0; pos < 18; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(MITSUBISHI_BIT_MARK, MITSUBISHI_ONE_SPACE)) {
|
||||
byte |= 1 << bit;
|
||||
} else if (!data.expect_item(MITSUBISHI_BIT_MARK, MITSUBISHI_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state_frame[pos] = byte;
|
||||
|
||||
// Check Header && Footer. Byte 16 only ever carries the left vane position (bits 3,4,5).
|
||||
if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) ||
|
||||
(pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) ||
|
||||
(pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) ||
|
||||
(pos == 16 && (byte & ~MITSUBISHI_VERTICAL_VANE_MASK) != 0)) {
|
||||
ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value");
|
||||
return false;
|
||||
}
|
||||
// Check Header && Footer. Byte 16 only ever carries the left vane position (bits 3,4,5).
|
||||
if (!data.expect_bytes<MITSUBISHI_BIT_TIMINGS>(state_frame, sizeof(state_frame)) ||
|
||||
state_frame[0] != MITSUBISHI_BYTE00 || state_frame[1] != MITSUBISHI_BYTE01 ||
|
||||
state_frame[2] != MITSUBISHI_BYTE02 || state_frame[3] != MITSUBISHI_BYTE03 ||
|
||||
state_frame[4] != MITSUBISHI_BYTE04 || state_frame[13] != MITSUBISHI_BYTE13 ||
|
||||
(state_frame[16] & ~MITSUBISHI_VERTICAL_VANE_MASK) != 0) {
|
||||
ESP_LOGV(TAG, "Bit decode or bytes 0,1,2,3,4,13 or 16 fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
// On/Off and Mode
|
||||
|
||||
@@ -84,5 +84,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
await sensors(CONF_FIELD_STRENGTH_Y, var.set_y_sensor)
|
||||
await sensors(CONF_FIELD_STRENGTH_Z, var.set_z_sensor)
|
||||
await sensors(CONF_HEADING, var.set_heading_sensor)
|
||||
if CONF_AUTO_SET_RESET in config:
|
||||
cg.add(var.set_auto_set_reset(config[CONF_AUTO_SET_RESET]))
|
||||
cg.add_optional(var.set_auto_set_reset, config.get(CONF_AUTO_SET_RESET))
|
||||
|
||||
@@ -451,8 +451,7 @@ async def add_modbus_base_properties(
|
||||
lambda_param_type: cg.MockObj = cg.float_,
|
||||
lambda_return_type: Any = float,
|
||||
) -> None:
|
||||
if CONF_CUSTOM_PDU in config:
|
||||
cg.add(var.set_custom_pdu(config[CONF_CUSTOM_PDU]))
|
||||
cg.add_optional(var.set_custom_pdu, config.get(CONF_CUSTOM_PDU))
|
||||
|
||||
if config[CONF_RESPONSE_SIZE] > 0:
|
||||
cg.add(var.set_register_size(config[CONF_RESPONSE_SIZE]))
|
||||
|
||||
@@ -122,8 +122,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_tank_empty(CONF_SUPPORTED_TANKS_MAP[t][0]))
|
||||
cg.add(var.set_tank_full(CONF_SUPPORTED_TANKS_MAP[t][1]))
|
||||
|
||||
if CONF_PROPANE_BUTANE_MIX in config:
|
||||
cg.add(var.set_propane_butane_mix(config[CONF_PROPANE_BUTANE_MIX]))
|
||||
cg.add_optional(var.set_propane_butane_mix, config.get(CONF_PROPANE_BUTANE_MIX))
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_TEMPERATURE, var.set_temperature)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user