Compare commits

...
Author SHA1 Message Date
J. Nick Koston 3500ecc1c1 Merge remote-tracking branch 'origin/dev' into declcfg-migrate
# Conflicts:
#	esphome/pins.py
2026-10-18 14:51:26 -10:00
J. Nick Koston c793ab30a7 [core][gpio_expander] Add a shared expander pin class and pin codegen helpers (#20866) 2026-10-18 14:48:35 -10:00
Clyde StubbsandClaude Opus 5.5 1753c45e53 [core] Move CONF_PROVIDER and CONF_REMOTE_ID to components/const (#21096)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:47:37 -10:00
J. Nick Koston 1767e4b0be [sensor] Add SensorWithDedup for child sensors that skip repeated values (#20925) 2026-10-18 14:47:19 -10:00
J. Nick Koston 5575dd8928 [core] Add final_validate.get_declaring_config (#20912) 2026-10-18 14:46:57 -10:00
J. Nick Koston 8e4592fa87 Merge branch 'declcfg' into declcfg-migrate 2026-10-18 14:21:18 -10:00
J. Nick Koston 76addfec20 Raise TypeError when an ID is not declared in a config block 2026-10-18 14:21:15 -10:00
J. Nick Koston 704dd4b445 [components] Report communication failures with status_comm_result in the remaining components (#21079) 2026-10-19 00:17:57 +00:00
J. Nick Koston c7073b9a0b Drop unused CONF_ID import in dfrobot_sen0609 2026-10-18 14:13:48 -10:00
pre-commit-ci-lite[bot] 480577e2f4 apply automatic formatting fixes 2026-10-19 00:12:56 +00:00
J. Nick Koston 3808c767fd Merge branch 'declcfg' into declcfg-migrate
# Conflicts:
#	esphome/components/display/__init__.py
2026-10-18 14:10:51 -10:00
J. Nick Koston d6a9292c41 Merge remote-tracking branch 'origin/dev' into declcfg 2026-10-18 14:10:43 -10:00
e6ad995c8e [i2s_audio] Add ESP32-S31 support (#20115)
Co-authored-by: cosmicDustOfLightLength <cosmicDustOfLightLength@example.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-19 00:07:52 +00:00
J. Nick Koston 84ce39e493 [components] Use the codegen helpers in the remaining components (#21078) 2026-10-18 14:05:37 -10:00
J. Nick Koston d9242520e3 [components] Decode bytes with the shared helpers (#21080) 2026-10-18 20:00:48 -04:00
J. Nick Koston 03bf2de7df [components] Decode BLE fields with the shared helpers (#21082) 2026-10-18 19:59:19 -04:00
J. Nick Koston a70d75e469 [components] Read UART bytes with read_available and discard_available (#21084) 2026-10-18 13:59:09 -10:00
J. Nick Koston 2dcb97774d [tests] Add an esp32-s31-idf base file and shared i2c and i2s_audio packages (#21086) 2026-10-18 19:54:48 -04:00
Jacopo Labardi be9741c6de [const] Share CONF_VALUE_TYPE across components (#21062) 2026-10-18 13:02:22 -10:00
ab-tools b1c138c612 [audio] Compute the remaining AudioStreamInfo conversions in 64 bits (#21092) 2026-10-18 12:12:50 -10:00
ab-tools 9526233821 [audio] Compute frames_to_microseconds() in 64 bits (#21089) 2026-10-18 11:39:37 -10:00
Bonne EgglestonandJ. Nick Koston 512e43793b [api] Poll the outgoing dial every loop (#21085)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 20:47:02 +00:00
603f16623f [usb_host] Size the FIFO split on ESP32-S2/S3 from periodic packet requests (#21081)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 20:44:05 +00:00
218e696654 [globals] Add RTC memory support (#13954)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 10:40:55 -10:00
Clyde Stubbs 3e68e0f5e1 [epaper_spi] Add the UC8151D controller and the Waveshare 2.13" B V3 (#21022) 2026-10-19 07:40:16 +11:00
dd456ea0b2 [homeassistant] Add the light platform (#11354)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 10:02:31 -10:00
Bonne Eggleston e54d66e620 [api] Poll the outgoing dial every loop on deep sleep builds (#21077) 2026-10-18 09:36:35 -10:00
J. Nick Koston a3bb17d2c6 [components] Create BLE and modbus devices with the new helpers (#21026) 2026-10-18 15:09:58 -04:00
71c0f46e35 [bme68x_bsec2] disable loop (#20994)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 08:33:17 -10:00
J. Nick Koston a153fcc8ee [bl0906][kamstrup_kmp][ufm01] Drop stale receive bytes with discard_available (#21071) 2026-10-18 14:30:10 -04:00
85724892cf [light] Add power estimation and current limiting for addressable LED strips (#15348)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-18 08:08:31 -10:00
J. Nick Koston deea006b39 [components] Set referenced components with cg.add_variable and register_parented (a-l) (#21021) 2026-10-18 17:43:05 +00:00
J. Nick Kostonandpre-commit-ci-lite[bot] 619728b686 [components] Drop stale UART receive bytes with discard_available (#21030)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-18 07:23:06 -10:00
J. Nick Koston c35d98b622 [core] Add progmem_string_table_find() (#20790) 2026-10-18 07:19:14 -10:00
J. Nick Koston b2d6c78499 [light] Add LightState::set_external_state() for outputs that mirror a light elsewhere (#20787) 2026-10-18 07:18:47 -10:00
J. Nick KostonandJonathan Swoboda a801e414a0 [components] Set referenced components with cg.add_variable and register_parented (m-z) (#21023)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-18 13:32:06 +00:00
J. Nick Koston 22a36de4ae [components] Decode little-endian I2C values with the new helpers (#20939) 2026-10-18 09:20:58 -04:00
J. Nick Koston eca95348ac [components] Assemble 24-bit readings with encode_uint24 (#21016) 2026-10-18 09:16:50 -04:00
J. Nick KostonandJonathan Swoboda ed9616d512 [components] Set pins with cg.add_pin (#21019)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-18 09:10:22 -04:00
J. Nick Koston 0cd1ae01f1 [components] Use cv.uint8_t and cv.uint16_t for full byte ranges (#21015) 2026-10-18 08:52:14 -04:00
J. Nick Koston 2a416fbce6 [core] Document that hub entities go in platforms in AGENTS.md (#21003) 2026-10-18 08:45:21 -04:00
J. Nick Koston 270d989481 [core] Add cg.add_pin and cg.add_variable for optional pins and ids (#21018) 2026-10-18 08:41:33 -04:00
J. Nick Koston 3665bcc4e3 [ble_device_base][ble_client][modbus] Add new_ble_device_component, new_ble_node_component and new_modbus_client_component (#21025) 2026-10-18 08:40:35 -04:00
J. Nick Koston 0922df9bb3 [uart] Add discard_available to drop stale receive bytes (#21029) 2026-10-18 08:39:02 -04:00
J. Nick Koston 54a28b3e3b [bl0906] Read stale receive bytes instead of spinning on flush() (#21035) 2026-10-18 08:37:48 -04:00
Michele PorelliandJ. Nick Koston 032a2b7200 [core] Let entities choose flash or RTC storage for their preferences (#21017)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-18 06:58:40 +00:00
J. Nick Koston 0bb78186f2 [components] Set display lambdas with display.add_writer (#21028) 2026-10-17 18:34:38 -10:00
J. Nick Koston 2fe1333cba [display] Add add_writer for display lambdas (#21027) 2026-10-17 18:34:38 -10:00
J. Nick Koston 265754c467 [components] Drop get_setup_priority overrides that return the default (#21014) 2026-10-17 17:43:16 -10:00
6580f4f314 [micro_wake_word] Allow configurable capture buffer duration (#20942)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 16:43:07 -10:00
J. Nick Koston 5ad63e3bb6 [http_request] Only treat a URL as secure when its scheme is https (#20867) 2026-10-18 02:38:07 +00:00
J. Nick Koston a8ab035b59 [lock] Filter on_lock and on_unlock with when (#20858) 2026-10-17 16:33:05 -10:00
J. Nick Koston 968d57c56b [core][time] Add BCD helpers and a shared RTC BCD calendar codec (#20860) 2026-10-17 16:32:13 -10:00
J. Nick Koston 1307faef06 [core] Add Component::status_comm_result (#20863) 2026-10-17 16:31:17 -10:00
ee117886b9 [vevor_dtx] Add Vevor weather station component (#17224)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 15:42:55 -10:00
1dbad7b951 [airton] Add support to climate for Airton IR A/C units (#7753)
Co-authored-by: Arjan <44190435+vingerha@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-18 01:37:58 +00:00
J. Nick Koston 5df6c65cc2 [api] Test api.connected without options and with a lambda (#20835) 2026-10-17 15:23:35 -10:00
J. Nick Koston af5efabdc6 [light] Test light.toggle with a transition length (#20832) 2026-10-17 15:23:22 -10:00
J. Nick Koston a2754f655c [dfrobot_sen0609][http_request] Use ESPHOME_strstr_P (#20825) 2026-10-17 15:23:06 -10:00
J. Nick Koston 34e6d3ec9c [pylontech] Restore the CONF_ID import (#21013) 2026-10-17 15:21:36 -10:00
J. Nick Koston 21362f8c92 [ethernet] Test the ethernet.connected condition (#20844) 2026-10-17 15:21:18 -10:00
J. Nick Koston 626d84bc64 [sensor][number] Test in_range with every bound combination (#20847) 2026-10-17 15:20:52 -10:00
J. Nick Koston 68fa7b9847 [wifi] Test the wifi.connected and wifi.enabled conditions (#20841) 2026-10-17 15:20:00 -10:00
J. Nick Koston 32dc3b8535 [http_request] Test ota.http_request.flash with every field (#20829) 2026-10-17 15:19:24 -10:00
J. Nick Koston 74bcec34ad [i2c] Add I2CDevice::update_register for register read-modify-write (#20854) 2026-10-17 15:18:36 -10:00
J. Nick Koston c1687aba4c [components] Let core report communication failures in dump_config (5/5) (#20874) 2026-10-17 15:16:21 -10:00
J. Nick Koston ec642f928f [i2c] Add little-endian and signed byte decoding helpers (#20938) 2026-10-17 15:15:36 -10:00
J. Nick Koston ec9a207f42 [components] Create UART devices with new_uart_component (#20919) 2026-10-17 15:15:21 -10:00
J. Nick Koston 3b7847d951 [components] Create SPI devices with new_spi_component (#20920) 2026-10-17 21:13:49 -04:00
J. Nick Koston 1197f22f00 [fusb302b] Expose the sensors as platforms (#21004) 2026-10-17 15:09:49 -10:00
J. Nick Koston e034662dc6 [uart_tcp] Expose the connection entities as platforms (#21008) 2026-10-17 15:09:32 -10:00
J. Nick Koston 42e849967a [bq76972] Expose the sensors as a sensor platform (#21006) 2026-10-17 15:09:14 -10:00
J. Nick Koston e6f1ed8d51 [tcp_uart] Expose the connection entities as platforms (#21009) 2026-10-17 15:07:42 -10:00
Igor StadnykandJ. Nick Koston bb9067acd7 [tfluna] Recover from transient runtime reconfiguration failures (#20992)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 15:07:16 -10:00
Michele Porelli 928cf7d696 [libretiny] Emulate rtc preference storage on the Beken BK7231T, BK7231N and BK7238 with flash and the reset reason (#21007) 2026-10-17 14:59:40 -10:00
89afb111ad [mt6701] Add MT6701 (MagnTek magnetic encoder) component (#17643)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 14:06:46 -10:00
Brandon HarveyandJ. Nick Koston d1ec52e95a [sen6x] Add device status binary sensors (#18784)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 23:34:15 +00:00
Bonne Egglestonandclydebarrow 818d43f742 [epaper_spi] Run the loop continuously between the steps of an update (#20554)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-18 10:25:36 +11:00
1a28a9220b [uart] Add a binary sensor that fires on a byte sequence (#14032)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 22:35:46 +00:00
Brandon HarveyandJ. Nick Koston 022733c46b [sen6x] Add PM number concentration sensors (#18782)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 12:16:45 -10:00
0f6fc503bd [sen6x] Add temperature compensation, acceleration and startup delay (#18781)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 11:01:14 -10:00
Bonne Egglestonandclydebarrow ae62960193 [epaper_spi] Add the UC8279 controller and the Xteink X4 Pro (#20545)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-18 07:51:07 +11:00
325c747dcd [sen6x] Add CO2 configuration options (#18780)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 10:41:13 -10:00
Brandon HarveyandJ. Nick Koston 5759c6b8e6 [deep_sleep] Honor nested wakeup_pin_mode on ESP32 single-pin configs (#16618)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 10:25:40 -10:00
c9ba6c355c [hoermann_hcp] Add an actuator error binary sensor (#20299)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 10:00:31 -10:00
Brandon HarveyandJ. Nick Koston d02da3c8de [wifi] Support hidden option in single-network shorthand (#18157)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 09:54:55 -10:00
Bascht74andJ. Nick Koston a576092f25 [remote_base] Send the 72-bit Jarolift TDEF group frame with transmit_keeloq (#20927)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 09:52:01 -10:00
Simonas Kazlauskas 792d650fb2 [remote_base] Add a protocol for 433MHz A-OK remotes (#20991) 2026-10-17 09:44:25 -10:00
f72857151a [pylontech] Add individual cell voltage and temperature sensors (#16852)
Co-authored-by: Michael <michael_e@gmx-topmail.com>
Co-authored-by: ritschys <117527311+ritschys@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 19:28:52 +00:00
5f8f075e76 [zephyr_coredump] Store a coredump in flash on nRF52 and print it to the next log reader (#13122)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 19:22:28 +00:00
Matias FernandezandJ. Nick Koston c2c8cb5eb3 [climate_ir_lg] Fix swing command to not include climate state (#17417)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 09:12:35 -10:00
J. Nick Koston 5c2b05d0eb [ci] Run the C++ unit tests on macOS without a sanitizer whose runtime aborts (#21000) 2026-10-17 09:00:51 -10:00
Igor StadnykandJ. Nick Koston e110613ef1 [core] Construct App once in the C++ unit test main (#21001)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 19:00:37 +00:00
luar123 4324b7d084 [zigbee] bump esp-zigbee-sdk to 2.0.5 (#20998) 2026-10-17 08:58:49 -10:00
37d6140193 [epaper_spi] Fix partial refresh and port waveshare 2.90inv2 (#18959)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: luar123 <49960470+luar123@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 08:55:37 -10:00
4f192130f8 [water_heater] Hand the component to the core team (#20999)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 18:51:06 +00:00
Hasan Khalilandpre-commit-ci-lite[bot] 4ef17cebdb [api] Pre-reserve Home Assistant state subscriptions vector (#20986)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-17 08:01:01 -10:00
35276a38bc [rtsp_server] Add an RTSP server that streams the esp32_camera picture (#17990)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 07:18:52 -10:00
J. Nick Koston 635df9c6ad [socket] Share the allow-list reject log and config lines through Ipv4Allow (#20983) 2026-10-17 06:51:29 -10:00
Jonathan Swoboda d16d8c111e [debug] Log uptime in dump_config (#20989) 2026-10-17 06:50:54 -10:00
Jonathan Swoboda 1ead61f11f [esp32] Remove trailing comment from ECDSA test signing key (#20988) 2026-10-17 06:50:11 -10:00
J. Nick Koston bd06d37d1a [components] Check I2C chip IDs with check_chip_id (#20943) 2026-10-17 06:49:46 -10:00
Jonathan Swoboda 7c6fac91ff [esp32] Bump recommended ESP-IDF to 5.5.6 (#20987) 2026-10-17 06:49:15 -10:00
J. Nick Koston 052a6096e6 [components] Log hex dumps with the ESP_LOG*_HEX macros (#20932) 2026-10-17 16:46:16 +00:00
J. Nick Koston 25b684f762 [pylontech] Parse each line as it arrives with one fixed buffer (#20959) 2026-10-17 06:44:41 -10:00
J. Nick Koston 62f8cc3436 [uart] Add read_available helpers to drain available bytes in chunks (#20947) 2026-10-17 06:41:51 -10:00
J. Nick Koston 34f9152d61 [i2c] Add write_registers for constant register tables (#20945) 2026-10-17 06:41:20 -10:00
luar123 73af6b0500 [core][scheduler] run process_to_add after component phase (#20993) 2026-10-17 06:40:39 -10:00
J. Nick Koston a384aae5b1 [button] Add factory reset, restart and query button schema presets (#20915) 2026-10-17 06:38:45 -10:00
J. Nick Koston 905267a9c3 [esp32_ble] Build ble.enabled, ble.enable and ble.disable with the apply helpers (#20839) 2026-10-17 06:38:25 -10:00
J. Nick Koston d08c845c65 [components] Create I2C devices with new_i2c_component (#20909) 2026-10-17 06:36:49 -10:00
J. Nick Koston 91dfb92c60 [esp32_ble] Test ble.enabled, ble.enable and ble.disable (#20838) 2026-10-17 09:22:29 -04:00
J. Nick Koston cef9418a0f [i2c][uart][spi] Add new_i2c_component, new_uart_component and new_spi_component (#20908) 2026-10-17 09:20:28 -04:00
J. Nick Koston c0a693dfce [components] Use xor_bytes and sum16_bytes for checksums (#20922) 2026-10-17 09:18:24 -04:00
Clyde Stubbs 1b4721d445 [core] Script improvements including choose (#20547) 2026-10-17 21:23:12 +11:00
f8d31c3e54 [dfrobot_sen0465] Add DFRobot SEN0465-SEN0476 electrochemical gas sensors (#20880)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-16 22:47:50 -10:00
eafcca8274 [mics_5524] Add datasheet model, diagnostics and advanced wiring (2/2) (#20521)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 22:47:28 -10:00
Remco van Essen 06b585d373 [i2s_audio] Fix speaker restart loop with full_duplex (#20984) 2026-10-16 22:05:21 -10:00
HugoPeters1024andJ. Nick Koston 885f2fe5d8 [kamstrup_kmp] Fix Kamstrup KMP reads with echoing optical heads (#20897)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 22:04:01 -10:00
J. Nick Koston 4e27b2fc12 [components] Collect UART lines with LineReader (#20956) 2026-10-16 19:54:11 -10:00
J. Nick Koston 375900bfcd [core] Add LineReader to collect lines in a fixed buffer (#20952) 2026-10-16 19:54:11 -10:00
Zhaoqi Xu 6d664d3fef [web_server] Report numbers without a state as NA (#20973) 2026-10-16 19:15:04 -10:00
J. Nick Koston 4dc2b5911b [core] Move CONF_DRIVE_STRENGTH to the shared component constants (#20975) 2026-10-16 18:21:04 -10:00
tomaszduda23 687466b6cd [logger][usb_cdc_acm][core] Add CDC uart to NRF52 (#11634) 2026-10-16 18:14:51 -10:00
J. Nick Koston dcdc042a4b [host] Define a no-op InterruptLock (#20974) 2026-10-16 17:53:33 -10:00
d2a1f7ee31 [zephyr] Fix preference log levels and report settings read/save errors (#20810)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 03:02:54 +00:00
J. Nick Koston e38ee35cd8 [core] Use get_declaring_config in ID matching 2026-10-16 10:58:44 -10:00
J. Nick Koston 81fecf2106 [core] Use get_declaring_config for parent config lookups 2026-10-16 10:48:34 -10:00
J. Nick Koston 76e5b07416 [core] Add final_validate.get_declaring_config helper 2026-10-16 10:42:44 -10:00
1208 changed files with 21985 additions and 5649 deletions

No files matched your search

+37 -6
View File
@@ -287,7 +287,8 @@ file does, and it is the authority when they disagree. The most useful starting
`from esphome.components.my_component import DOMAIN`. Keep it in alphabetical order with the other
metadata constants. CI (`lint_component_domain`) fails without it.
- `DEPENDENCIES`: List of required components
- `AUTO_LOAD`: Components to automatically load
- `AUTO_LOAD`: Components to automatically load. Not a way to put entities under the component's
own key; see "Entities of a hub" below.
- `CONFLICTS_WITH`: Incompatible components
- `CODEOWNERS`: GitHub usernames responsible for maintenance
- `MULTI_CONF`: Whether multiple instances are allowed
@@ -379,6 +380,26 @@ file does, and it is the authority when they disagree. The most useful starting
var = await switch.new_switch(config)
```
- **Entities of a hub:** a new component's entities go in platform files (`sensor.py`,
`binary_sensor.py`, ...) that take the hub's id, never under the hub's own key with their platforms
added to `AUTO_LOAD` to make that work. Auto loading a platform because the C++ uses its type is
fine. See https://developers.esphome.io/architecture/components/#exposing-multiple-entities.
```yaml
# Bad - entities under the hub's key
my_hub:
connected:
name: Connected
# Good - the hub, then one platform entry that holds its entities
my_hub:
id: hub1
binary_sensor:
- platform: my_hub
my_hub_id: hub1 # optional with a single hub
connected:
name: Connected
```
- **Optional child entities of a hub:** bind the config once with `sensor.sub_sensors(config)` (or
`sub_binary_sensors`, `sub_text_sensors`, `sub_buttons`, `sub_switches`, `sub_numbers`,
`sub_selects` in their domains), adding `parent=hub` for entities that derive from `Parented<T>`,
@@ -389,15 +410,25 @@ file does, and it is the authority when they disagree. The most useful starting
itself, `hub` for one fetched with `cg.get_variable`) so the calls fit on one line. Loops whose
setter also takes an index, such as `set_gate_threshold(x, n)`, stay as they are.
```python
# my_hub/sensor.py
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
await sensors(CONF_HUMIDITY, var.set_humidity_sensor)
buttons = button.sub_buttons(config, parent=var)
await buttons(CONF_RESTART, var.set_restart_button)
await sensors(CONF_TEMPERATURE, hub.set_temperature_sensor)
await sensors(CONF_HUMIDITY, hub.set_humidity_sensor)
# my_hub/button.py
async def to_code(config):
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
buttons = button.sub_buttons(config, parent=hub)
await buttons(CONF_RESTART, hub.set_restart_button)
```
- **Pins and referenced components:** set them with `await cg.add_pin(var.set_cs_pin, config[CONF_CS_PIN])`
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`.
* **Automations (Triggers, Actions, Conditions):**
Automations have three building blocks: **Triggers** (fire when something happens), **Actions** (do something), and **Conditions** (check if something is true).
+14 -1
View File
@@ -30,6 +30,9 @@ esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @ncareau
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban @precurse
esphome/components/airton/* @Procsiab
esphome/components/airton/select/* @Procsiab
esphome/components/airton/switch/* @Procsiab
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
esphome/components/alpha3/* @jan-hofmeier
esphome/components/am2315c/* @swoboda1337
@@ -157,6 +160,9 @@ esphome/components/delonghi/* @grob6000
esphome/components/dew_point/* @CFlix
esphome/components/dfplayer/* @glmnet
esphome/components/dfrobot_sen0395/* @niklasweber
esphome/components/dfrobot_sen0465_base/* @tzabbi
esphome/components/dfrobot_sen0465_i2c/* @tzabbi
esphome/components/dfrobot_sen0465_uart/* @tzabbi
esphome/components/dfrobot_sen0609/* @remcom
esphome/components/dht/* @OttoWinter
esphome/components/display_menu_base/* @numo68
@@ -273,6 +279,7 @@ esphome/components/hmac_sha256/* @dwmw2
esphome/components/hoermann_hcp/* @zweckj
esphome/components/homeassistant/* @esphome/core @OttoWinter
esphome/components/homeassistant/button/* @jesserockz
esphome/components/homeassistant/light/* @egormanga
esphome/components/homeassistant/number/* @landonr
esphome/components/homeassistant/select/* @jesserockz
esphome/components/homeassistant/switch/* @Links2004
@@ -425,6 +432,9 @@ esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta
esphome/components/msa3xx/* @latonita
esphome/components/mt6701/* @slimcdk
esphome/components/mt6701_i2c/* @slimcdk
esphome/components/mt6701_spi/* @slimcdk
esphome/components/nau7802/* @cujomalainey
esphome/components/network/* @esphome/core
esphome/components/nextion/* @edwardtfn @senexcrenshaw
@@ -509,6 +519,7 @@ esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rp2_ble_tracker/* @bdraco
esphome/components/rpi_dpi_rgb/* @clydebarrow
esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtsp_server/* @max246
esphome/components/rtttl/* @glmnet @ximex
esphome/components/runtime_image/* @clydebarrow @guillempages @kahrendt
esphome/components/runtime_stats/* @bdraco
@@ -681,10 +692,11 @@ esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81
esphome/components/veml7700/* @latonita
esphome/components/version/* @esphome/core
esphome/components/vevor_dtx/* @Bofr5555
esphome/components/voice_assistant/* @jesserockz @kahrendt
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
esphome/components/water_heater/* @dhoeben
esphome/components/water_heater/* @esphome/core
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/waveshare_io_ch32v003/* @latonita
esphome/components/web_server/ota/* @esphome/core
@@ -723,6 +735,7 @@ esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow
esphome/components/zephyr/* @tomaszduda23
esphome/components/zephyr_coredump/* @tomaszduda23
esphome/components/zephyr_mcumgr/ota/* @tomaszduda23
esphome/components/zephyr_pwm/* @wiomoc
esphome/components/zhlt01/* @cfeenstra1024
+126 -3
View File
@@ -14,6 +14,7 @@ from esphome.const import (
CONF_COUNT,
CONF_ELSE,
CONF_ID,
CONF_IF,
CONF_THEN,
CONF_TIME,
CONF_TIMEOUT,
@@ -34,6 +35,9 @@ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType, SafeExpType
from esphome.util import Registry
CONF_CHOOSE = "choose"
CONF_CHOICE_ID = "choice_id"
def progmem_bytes(name: str, data: bytes | list[int]) -> MockObj:
"""Shared PROGMEM table for constant bytes; equal payloads share one, empty is nullptr."""
@@ -840,10 +844,39 @@ async def delay_action_to_code(
return var
def _wrap_bare_if_condition(value: Any) -> Any:
"""Allow a single condition directly under `if:` in place of `condition: {...}`."""
if not isinstance(value, dict):
return value
# `any` and `all` are also registered conditions, but here they are the if action's own keys
bare = [
key
for key in value
if key in CONDITION_REGISTRY and key not in (CONF_ANY, CONF_ALL)
]
if not bare:
return value
if len(bare) > 1:
raise cv.Invalid(
f"Only one condition may be given directly under 'if', found {', '.join(bare)}. "
f"Use '{CONF_CONDITION}', '{CONF_ALL}' or '{CONF_ANY}' to combine conditions"
)
if other := [k for k in (CONF_CONDITION, CONF_ANY, CONF_ALL) if k in value]:
raise cv.Invalid(
f"'{bare[0]}' given directly under 'if' cannot be used with '{other[0]}'. "
f"Move it into '{other[0]}'"
)
key = bare[0]
result = {k: v for k, v in value.items() if k != key}
result[CONF_CONDITION] = {key: value[key]}
return result
@register_action(
"if",
IfAction,
cv.All(
_wrap_bare_if_condition,
{
cv.Exclusive(
CONF_CONDITION, CONF_CONDITION
@@ -870,14 +903,104 @@ async def if_action_to_code(
cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION))
condition = await build_condition(config[cond_conf], template_arg, args)
var = cg.new_Pvariable(action_id, if_template_arg, condition)
then_actions = else_actions = None
if CONF_THEN in config:
for action in await build_action_list(config[CONF_THEN], template_arg, args):
then_actions = await build_action_list(config[CONF_THEN], template_arg, args)
if has_else:
else_actions = await build_action_list(config[CONF_ELSE], template_arg, args)
_add_if_branches(var, then_actions, else_actions)
return var
def _add_if_branches(
var: MockObj,
then_actions: list[MockObj] | None,
else_actions: list[MockObj] | None,
) -> None:
"""Add the branches of an IfAction one action at a time; None leaves a branch out."""
if then_actions is not None:
for action in then_actions:
cg.add(var.add_then_action(action))
cg.add(var.finish_then())
if has_else:
for action in await build_action_list(config[CONF_ELSE], template_arg, args):
if else_actions is not None:
for action in else_actions:
cg.add(var.add_else_action(action))
cg.add(var.finish_else())
def _validate_choice(choice: ConfigType) -> ConfigType:
"""A choice is either `if` with `then`, or `else` on its own."""
if CONF_ELSE in choice:
for key in (CONF_IF, CONF_THEN):
if key in choice:
raise cv.Invalid(f"'{key}' cannot be used with 'else'", path=[key])
return choice
for key in (CONF_IF, CONF_THEN):
if key not in choice:
raise cv.Invalid(f"'{key}' is required", path=[key])
return choice
def _validate_choose_else(choices: list[ConfigType]) -> list[ConfigType]:
"""`else` may only be the last choice, and not the only one."""
for index, choice in enumerate(choices[:-1]):
if CONF_ELSE in choice:
raise cv.Invalid(
"Only the last choice may be 'else'", path=[index, CONF_ELSE]
)
if len(choices) == 1 and CONF_ELSE in choices[0]:
raise cv.Invalid(
"At least one choice must have 'if' and 'then'", path=[0, CONF_ELSE]
)
return choices
@register_action(
CONF_CHOOSE,
IfAction,
cv.All(
cv.ensure_list(
cv.All(
cv.Schema(
{
cv.GenerateID(CONF_CHOICE_ID): cv.declare_id(IfAction),
cv.Optional(CONF_IF): validate_potentially_and_condition,
cv.Optional(CONF_THEN): validate_action_list,
cv.Optional(CONF_ELSE): validate_action_list,
}
),
_validate_choice,
)
),
cv.Length(min=1),
_validate_choose_else,
),
synchronous=True,
)
async def choose_action_to_code(
config: list[ConfigType],
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
# Built as a chain of if/else actions: each choice's else branch holds the next choice,
# and the last choice's else branch holds the `else` actions, if any.
else_actions: list[MockObj] | None = None
if CONF_ELSE in config[-1]:
else_actions = await build_action_list(
config[-1][CONF_ELSE], template_arg, args
)
config = config[:-1]
var: MockObj | None = None
for index in reversed(range(len(config))):
choice = config[index]
if_template_arg = cg.TemplateArguments(else_actions is not None, *template_arg)
condition = await build_condition(choice[CONF_IF], template_arg, args)
choice_id = action_id if index == 0 else choice[CONF_CHOICE_ID]
var = cg.new_Pvariable(choice_id, if_template_arg, condition)
then_actions = await build_action_list(choice[CONF_THEN], template_arg, args)
_add_if_branches(var, then_actions, else_actions)
else_actions = [var]
return var
+3
View File
@@ -53,11 +53,14 @@ from esphome.cpp_generator import ( # noqa: F401
with_local_variable,
)
from esphome.cpp_helpers import ( # noqa: F401
add_pin,
add_variable,
build_registry_entry,
build_registry_list,
extract_registry_entry_config,
get_slot_count,
gpio_pin_expression,
new_bus_component,
past_safe_mode,
register_component,
register_parented,
+3 -5
View File
@@ -9,15 +9,13 @@ namespace esphome::a01nyub {
ESPHOME_LOG_TAG(TAG, "a01nyub.sensor");
void A01nyubComponent::loop() {
uint8_t data;
while (this->available() > 0) {
this->read_byte(&data);
this->read_available([this](uint8_t data) {
if (this->buffer_.empty() && (data != 0xff))
continue;
return;
buffer_.push_back(data);
if (this->buffer_.size() == 4)
this->check_buffer_();
}
});
}
void A01nyubComponent::check_buffer_() {
+3 -5
View File
@@ -9,15 +9,13 @@ namespace esphome::a02yyuw {
ESPHOME_LOG_TAG(TAG, "a02yyuw.sensor");
void A02yyuwComponent::loop() {
uint8_t data;
while (this->available() > 0) {
this->read_byte(&data);
this->read_available([this](uint8_t data) {
if (this->buffer_.empty() && (data != 0xff))
continue;
return;
buffer_.push_back(data);
if (this->buffer_.size() == 4)
this->check_buffer_();
}
});
}
void A02yyuwComponent::check_buffer_() {
+3 -8
View File
@@ -23,11 +23,6 @@ async def to_code(config: ConfigType) -> None:
await cg.register_component(var, config)
await stepper.register_stepper(var, config)
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
cg.add(var.set_step_pin(step_pin))
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
cg.add(var.set_dir_pin(dir_pin))
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
cg.add(var.set_sleep_pin(sleep_pin))
await cg.add_pin(var.set_step_pin, config[CONF_STEP_PIN])
await cg.add_pin(var.set_dir_pin, config[CONF_DIR_PIN])
await cg.add_pin(var.set_sleep_pin, config.get(CONF_SLEEP_PIN))
@@ -44,10 +44,7 @@ async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
temperature_sensor = await cg.get_variable(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(temperature_sensor))
humidity_sensor = await cg.get_variable(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(humidity_sensor))
await cg.add_variable(var.set_temperature_sensor, config[CONF_TEMPERATURE])
await cg.add_variable(var.set_humidity_sensor, config[CONF_HUMIDITY])
cg.add(var.set_equation(config[CONF_EQUATION]))
+2 -4
View File
@@ -68,10 +68,8 @@ async def to_code(config: ConfigType) -> None:
config[CONF_MIN_POWER] = 0.1
await output.register_output(var, config)
pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
cg.add(var.set_gate_pin(pin))
pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
cg.add(var.set_zero_cross_pin(pin))
await cg.add_pin(var.set_gate_pin, config[CONF_GATE_PIN])
await cg.add_pin(var.set_zero_cross_pin, config[CONF_ZERO_CROSS_PIN])
cg.add(var.set_zero_cross_interrupt_type(config[CONF_ZERO_CROSS_INTERRUPT_TYPE]))
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
cg.add(var.set_method(config[CONF_METHOD]))
@@ -68,32 +68,32 @@ void AdalightLightEffect::apply(light::AddressableLight &it, const Color &curren
blank_all_leds_(it);
}
if (this->available() > 0) {
ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
const size_t avail = this->available();
if (avail > 0) {
ESP_LOGV(TAG, "Frame: Available (size=%zu).", avail);
}
while (this->available() != 0) {
uint8_t data;
if (!this->read_byte(&data))
break;
this->frame_.push_back(data);
this->last_byte_ = now;
this->read_available(
[this, &it, now](uint8_t data) {
this->frame_.push_back(data);
this->last_byte_ = now;
switch (this->parse_frame_(it)) {
case INVALID:
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
reset_frame_(it);
break;
switch (this->parse_frame_(it)) {
case INVALID:
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
reset_frame_(it);
break;
case PARTIAL:
break;
case PARTIAL:
break;
case CONSUMED:
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
reset_frame_(it);
break;
}
}
case CONSUMED:
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
reset_frame_(it);
break;
}
},
avail);
}
AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
+1 -2
View File
@@ -164,8 +164,7 @@ async def to_code(config: ConfigType) -> None:
# Remove before 2027.2.0
cg.add(var.set_is_temperature())
elif not CORE.is_nrf52 or config[CONF_PIN][CONF_NUMBER] not in EXTRA_ADC:
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
await cg.add_pin(var.set_pin, config[CONF_PIN])
# Skip the setters when the config matches the C++ initializers.
if config[CONF_RAW]:
+1 -4
View File
@@ -1,7 +1,6 @@
import esphome.codegen as cg
from esphome.components import spi
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
DEPENDENCIES = ["spi"]
@@ -20,6 +19,4 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
await spi.new_spi_component(config)
@@ -56,8 +56,4 @@ async def to_code(config: ConfigType) -> None:
)
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
if lambda_config := config.get(CONF_LAMBDA):
lambda_ = await cg.process_lambda(
lambda_config, [(display.DisplayRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))
await display.add_writer(var, config)
+4 -13
View File
@@ -282,20 +282,11 @@ async def power_channel(config: ConfigType) -> MockObj:
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
if irq0_pin := config.get(CONF_IRQ0_PIN):
pin = await cg.gpio_pin_expression(irq0_pin)
cg.add(var.set_irq0_pin(pin))
pin = await cg.gpio_pin_expression(config[CONF_IRQ1_PIN])
cg.add(var.set_irq1_pin(pin))
if reset_pin := config.get(CONF_RESET_PIN):
pin = await cg.gpio_pin_expression(reset_pin)
cg.add(var.set_reset_pin(pin))
await cg.add_pin(var.set_irq0_pin, config.get(CONF_IRQ0_PIN))
await cg.add_pin(var.set_irq1_pin, config[CONF_IRQ1_PIN])
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
if frequency := config.get(CONF_FREQUENCY):
cg.add(var.set_frequency(frequency))
+1 -3
View File
@@ -169,9 +169,7 @@ ADE7953_CONFIG_SCHEMA = cv.Schema(
async def register_ade7953(var: MockObj, config: ConfigType) -> None:
await cg.register_component(var, config)
if irq_pin_config := config.get(CONF_IRQ_PIN):
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
cg.add(var.set_irq_pin(irq_pin))
await cg.add_pin(var.set_irq_pin, config.get(CONF_IRQ_PIN))
cg.add(var.set_pga_v(PGA_GAINS[config.get(CONF_VOLTAGE_PGA_GAIN)]))
cg.add(var.set_pga_ia(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_A)]))
+1 -4
View File
@@ -1,7 +1,6 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
DEPENDENCIES = ["i2c"]
@@ -27,8 +26,6 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
+1 -4
View File
@@ -1,7 +1,6 @@
import esphome.codegen as cg
from esphome.components import spi
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
CODEOWNERS = ["@solomondg1"]
@@ -26,6 +25,4 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
await spi.new_spi_component(config)
+1 -4
View File
@@ -4,7 +4,6 @@ from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ADDRESS,
CONF_ID,
CONF_MODE,
CONF_TVOC,
CONF_VALUE,
@@ -58,9 +57,7 @@ FINAL_VALIDATE_SCHEMA = i2c.final_validate_device_schema("ags10", max_frequency=
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_TVOC, var.set_tvoc)
+1 -4
View File
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
CONF_VARIANT,
DEVICE_CLASS_HUMIDITY,
@@ -49,9 +48,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_variant(config[CONF_VARIANT]))
sensors = sensor.sub_sensors(config)
+2 -4
View File
@@ -3,7 +3,7 @@ import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.audio_dac import AudioDac
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE
from esphome.const import CONF_MODE
from esphome.types import ConfigType
CODEOWNERS = ["@kbx81"]
@@ -41,6 +41,4 @@ automation.register_apply_action(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
await i2c.new_i2c_component(config)
@@ -1,4 +1,5 @@
#include "airthings_listener.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -12,10 +13,7 @@ bool AirthingsListener::parse_device(const ble_device_base::ESPBTDevice &device)
if (it.data.size() < 4)
continue;
uint32_t sn = it.data[0];
sn |= ((uint32_t) it.data[1] << 8);
sn |= ((uint32_t) it.data[2] << 16);
sn |= ((uint32_t) it.data[3] << 24);
uint32_t sn = encode_value_le<uint32_t>(it.data.data());
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str_to(addr_buf));
+2
View File
@@ -0,0 +1,2 @@
CODEOWNERS = ["@Procsiab"]
DOMAIN = "airton"
+319
View File
@@ -0,0 +1,319 @@
#include "airton.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::airton {
ESPHOME_LOG_TAG(TAG, "airton.climate");
// Distinct from the climate's own RESTORE_STATE_VERSION, which shares the entity's preference key
static constexpr uint32_t RESTORE_SETTINGS_VERSION = 0x2E86A1F3UL;
// From IRutils.h of IRremoteESP8266 library
static uint8_t checksum(const uint8_t *frame) {
return static_cast<uint8_t>(0x7F - sum_bytes(frame, AIRTON_STATE_FRAME_SIZE - 1)) ^ 0x2C;
}
void AirtonClimate::setup() {
climate_ir::ClimateIR::setup();
// The flap position is the one source of truth for the swing mode the climate state restored
this->settings_pref_ = this->make_entity_preference<AirtonSettings>(RESTORE_SETTINGS_VERSION);
this->settings_pref_.load(&this->settings_);
this->sync_swing_mode_();
this->publish_settings_();
this->publish_state();
}
void AirtonClimate::publish_settings_() {
#ifdef USE_SWITCH
if (this->sleep_mode_switch_ != nullptr)
this->sleep_mode_switch_->publish_state(this->settings_.sleep_state);
if (this->display_switch_ != nullptr)
this->display_switch_->publish_state(this->settings_.display_state);
#endif
#ifdef USE_SELECT
if (this->vertical_direction_select_ != nullptr)
this->vertical_direction_select_->publish_state(static_cast<size_t>(this->settings_.vertical_direction));
#endif
}
void AirtonClimate::save_settings_() { this->settings_pref_.save(&this->settings_); }
void AirtonClimate::sync_swing_mode_() {
this->swing_mode = this->settings_.vertical_direction == VerticalDirection::VERTICAL_DIRECTION_SWING
? climate::CLIMATE_SWING_VERTICAL
: climate::CLIMATE_SWING_OFF;
}
// The unit has no sleep mode while it only fans or picks the mode itself
bool AirtonClimate::sleep_mode_allowed_() const {
return this->mode != climate::CLIMATE_MODE_FAN_ONLY && this->mode != climate::CLIMATE_MODE_HEAT_COOL;
}
// Each setter stores and saves before it publishes: an automation on the entity runs inside
// publish_state() and may set the value again, which must not be undone afterwards
bool AirtonClimate::set_sleep_mode_state(bool state) {
state = state && this->sleep_mode_allowed_();
const bool changed = state != this->settings_.sleep_state;
if (changed) {
this->settings_.sleep_state = state;
this->save_settings_();
}
#ifdef USE_SWITCH
if (this->sleep_mode_switch_ != nullptr)
this->sleep_mode_switch_->publish_state(state);
#endif
return changed;
}
bool AirtonClimate::set_display_state(bool state) {
const bool changed = state != this->settings_.display_state;
if (changed) {
this->settings_.display_state = state;
this->save_settings_();
}
#ifdef USE_SWITCH
if (this->display_switch_ != nullptr)
this->display_switch_->publish_state(state);
#endif
return changed;
}
bool AirtonClimate::set_vertical_direction_state(VerticalDirection direction) {
const bool changed = direction != this->settings_.vertical_direction;
if (changed) {
this->settings_.vertical_direction = direction;
this->sync_swing_mode_();
this->save_settings_();
}
#ifdef USE_SELECT
if (this->vertical_direction_select_ != nullptr)
this->vertical_direction_select_->publish_state(static_cast<size_t>(direction));
#endif
return changed;
}
void AirtonClimate::send_settings() {
this->transmit_state();
this->publish_state();
}
void AirtonClimate::control(const climate::ClimateCall &call) {
// ClimateIR::control() sends and publishes the whole state afterwards
// Only a change of swing mode moves the flap, so a repeated swing-off keeps a fixed position
if (auto swing_mode = call.get_swing_mode(); swing_mode.has_value() && *swing_mode != this->swing_mode) {
this->set_vertical_direction_state(*swing_mode == climate::CLIMATE_SWING_VERTICAL
? VerticalDirection::VERTICAL_DIRECTION_SWING
: VerticalDirection::VERTICAL_DIRECTION_OFF);
}
ClimateIR::control(call);
}
void AirtonClimate::transmit_state() {
// Entering fan-only or automatic mode drops sleep mode, as the remote does
if (!this->sleep_mode_allowed_())
this->set_sleep_mode_state(false);
// Sampled valid state
// Power: On, Mode: 2 (Dry), Fan: 1 (Quiet), Temp: 20C, Swing(V): On, Econo: Off, Turbo: Off, Light: On, Health: On,
// Sleep: Off. 0x74C461041A11D3
// The turbo bit above the fan speed stays clear, as the remote sends it
uint8_t remote_state[AIRTON_STATE_FRAME_SIZE] = {
AIRTON_HEADER_BYTE0,
AIRTON_HEADER_BYTE1,
static_cast<uint8_t>(this->operation_mode_() | (this->fan_speed_() << 4)),
this->temperature_(),
static_cast<uint8_t>(this->settings_.vertical_direction),
this->operation_settings_(),
0,
};
remote_state[6] = checksum(remote_state);
ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X", remote_state[6], remote_state[5], remote_state[4],
remote_state[3], remote_state[2], remote_state[1], remote_state[0]);
auto transmit = this->transmitter_->transmit();
auto *data = transmit.get_data();
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->mark(AIRTON_BIT_MARK);
data->space(AIRTON_MESSAGE_SPACE);
transmit.perform();
}
uint8_t AirtonClimate::operation_mode_() {
uint8_t operating_mode = 0b1000; // First bit is for power state
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
operating_mode |= AIRTON_MODE_COOL;
break;
case climate::CLIMATE_MODE_DRY:
operating_mode |= AIRTON_MODE_DRY;
break;
case climate::CLIMATE_MODE_HEAT:
operating_mode |= AIRTON_MODE_HEAT;
break;
case climate::CLIMATE_MODE_HEAT_COOL:
operating_mode |= AIRTON_MODE_AUTO;
break;
case climate::CLIMATE_MODE_FAN_ONLY:
operating_mode |= AIRTON_MODE_FAN;
break;
case climate::CLIMATE_MODE_OFF:
default:
operating_mode = 0b0111 & this->previous_mode_; // Set previous mode with power state bit off
}
this->previous_mode_ = operating_mode;
return operating_mode;
}
uint8_t AirtonClimate::fan_speed_() {
uint8_t fan_speed;
switch (this->fan_mode.value_or(climate::CLIMATE_FAN_AUTO)) {
case climate::CLIMATE_FAN_LOW:
fan_speed = AIRTON_FAN_1;
break;
case climate::CLIMATE_FAN_MEDIUM:
fan_speed = AIRTON_FAN_3;
break;
case climate::CLIMATE_FAN_HIGH:
fan_speed = AIRTON_FAN_5;
break;
case climate::CLIMATE_FAN_AUTO:
default:
fan_speed = AIRTON_FAN_AUTO;
}
return fan_speed;
}
uint8_t AirtonClimate::temperature_() {
switch (this->mode) {
case climate::CLIMATE_MODE_HEAT_COOL:
return AIRTON_AUTO_MODE_TEMP - AIRTON_TEMP_MIN;
default:
return static_cast<uint8_t>(roundf(clamp<float>(this->target_temperature, AIRTON_TEMP_MIN, AIRTON_TEMP_MAX))) -
AIRTON_TEMP_MIN;
}
}
// The bits of this packet's byte have the following meanings (from MSB to LSB)
// Light, Health, Unknown, HeatOn, Unknown, NotAutoOn, Sleep, Econo
uint8_t AirtonClimate::operation_settings_() {
uint8_t settings = 0;
if (this->mode == climate::CLIMATE_MODE_HEAT) { // Set heating bit if on the corresponding mode
settings |= (1 << 4);
}
if (this->settings_.display_state) {
settings |= (1 << 7);
}
if (this->settings_.sleep_state) {
settings |= (1 << 1);
}
settings |= 0b01000000; // Health, as the remote always sends it
if (this->mode != climate::CLIMATE_MODE_HEAT_COOL) {
settings |= 0b00000100; // NotAutoOn: clear only while the unit picks the mode itself
}
return settings;
}
void AirtonClimate::parse_state_frame_(const uint8_t *frame) {
uint8_t mode = frame[2];
// A later "off" repeats the mode the remote last sent, as the remote itself does
this->previous_mode_ = mode & 0b00001111;
if (mode & 0b00001000) { // The power bit
switch (mode & 0b00000111) {
case AIRTON_MODE_COOL:
this->mode = climate::CLIMATE_MODE_COOL;
break;
case AIRTON_MODE_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
break;
case AIRTON_MODE_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
break;
case AIRTON_MODE_AUTO:
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
break;
case AIRTON_MODE_FAN:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
break;
}
} else {
this->mode = climate::CLIMATE_MODE_OFF;
}
uint8_t fan_mode = (frame[2] & 0b01110000) >> 4;
switch (fan_mode) {
case AIRTON_FAN_1:
case AIRTON_FAN_2:
this->fan_mode = climate::CLIMATE_FAN_LOW;
break;
case AIRTON_FAN_3:
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
break;
case AIRTON_FAN_4:
case AIRTON_FAN_5:
this->fan_mode = climate::CLIMATE_FAN_HIGH;
break;
case AIRTON_FAN_AUTO:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
}
this->target_temperature = (frame[3] & 0b00001111) + AIRTON_TEMP_MIN;
const uint8_t direction = frame[4] & 0b00001111;
if (direction <= static_cast<uint8_t>(VerticalDirection::VERTICAL_DIRECTION_DOWN)) {
this->set_vertical_direction_state(static_cast<VerticalDirection>(direction));
} else {
ESP_LOGV(TAG, "Unknown vertical direction %u", direction);
}
this->set_display_state((frame[5] & 0b10000000) != 0);
this->set_sleep_mode_state((frame[5] & 0b00000010) != 0);
this->publish_state();
}
bool AirtonClimate::on_receive(remote_base::RemoteReceiveData data) {
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 (remote_state[0] != AIRTON_HEADER_BYTE0 || remote_state[1] != AIRTON_HEADER_BYTE1) {
ESP_LOGV(TAG, "Wrong header contents: %02X %02X", remote_state[1], remote_state[0]);
return false;
}
const uint8_t expected = checksum(remote_state);
if (remote_state[AIRTON_STATE_FRAME_SIZE - 1] != expected) {
ESP_LOGV(TAG, "Checksum error: calculated %02X, received %02X", expected,
remote_state[AIRTON_STATE_FRAME_SIZE - 1]);
return false;
}
ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X", remote_state[6], remote_state[5], remote_state[4],
remote_state[3], remote_state[2], remote_state[1], remote_state[0]);
this->parse_state_frame_(remote_state);
return true;
}
} // namespace esphome::airton
+137
View File
@@ -0,0 +1,137 @@
#pragma once
#include "esphome/components/climate_ir/climate_ir.h"
#include "esphome/core/defines.h"
#include "esphome/core/preferences.h"
#ifdef USE_SWITCH
#include "esphome/components/switch/switch.h"
#endif
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
namespace esphome::airton {
// Values for Airton SMVH09B-2A2A3NH IR Controllers
static constexpr uint8_t AIRTON_TEMP_MIN = 16; // Celsius
static constexpr uint8_t AIRTON_TEMP_MAX = 31; // Celsius
static constexpr uint8_t AIRTON_AUTO_MODE_TEMP = 25; // The fixed setpoint the unit uses in auto mode
static constexpr uint8_t AIRTON_MODE_AUTO = 0b000;
static constexpr uint8_t AIRTON_MODE_COOL = 0b001;
static constexpr uint8_t AIRTON_MODE_HEAT = 0b100;
static constexpr uint8_t AIRTON_MODE_DRY = 0b010;
static constexpr uint8_t AIRTON_MODE_FAN = 0b011;
static constexpr uint8_t AIRTON_FAN_AUTO = 0b000;
static constexpr uint8_t AIRTON_FAN_1 = 0b001;
static constexpr uint8_t AIRTON_FAN_2 = 0b010;
static constexpr uint8_t AIRTON_FAN_3 = 0b011;
static constexpr uint8_t AIRTON_FAN_4 = 0b100;
static constexpr uint8_t AIRTON_FAN_5 = 0b101;
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 uint32_t AIRTON_MESSAGE_SPACE = 100000;
static constexpr uint8_t AIRTON_STATE_FRAME_SIZE = 7;
// Every frame starts with these two bytes
static constexpr uint8_t AIRTON_HEADER_BYTE0 = 0xD3;
static constexpr uint8_t AIRTON_HEADER_BYTE1 = 0x11;
// Position of the air outlet flap; the values are the nibble the frame carries and the index
// of the option in the vertical direction select
enum class VerticalDirection : uint8_t {
VERTICAL_DIRECTION_OFF = 0,
VERTICAL_DIRECTION_SWING = 1,
VERTICAL_DIRECTION_UP = 2,
VERTICAL_DIRECTION_MIDDLE_UP = 3,
VERTICAL_DIRECTION_MIDDLE = 4,
VERTICAL_DIRECTION_MIDDLE_DOWN = 5,
VERTICAL_DIRECTION_DOWN = 6,
};
// The on/off settings the switch platform exposes
enum class AirtonSetting : uint8_t {
AIRTON_SETTING_SLEEP_MODE,
AIRTON_SETTING_DISPLAY,
};
// The settings the unit keeps that the climate state does not carry; saved to flash when they change
struct AirtonSettings {
bool sleep_state;
bool display_state;
VerticalDirection vertical_direction;
};
class AirtonClimate : public climate_ir::ClimateIR {
public:
AirtonClimate()
: climate_ir::ClimateIR(AIRTON_TEMP_MIN, AIRTON_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
void setup() override;
void control(const climate::ClimateCall &call) override;
// The direction the settings start from until a saved one is restored in setup()
void set_default_vertical_direction(VerticalDirection direction) { this->settings_.vertical_direction = direction; }
// The setters update the state, publish their entity and save; they return whether anything changed.
// A change made here (switch, select, action) is then sent with send_settings(); a frame from the
// remote only reports what the unit already did.
bool set_sleep_mode_state(bool state);
bool set_display_state(bool state);
bool set_vertical_direction_state(VerticalDirection direction);
void send_settings();
void set_setting(AirtonSetting setting, bool state) {
const bool changed = setting == AirtonSetting::AIRTON_SETTING_DISPLAY ? this->set_display_state(state)
: this->set_sleep_mode_state(state);
if (changed)
this->send_settings();
}
#ifdef USE_SWITCH
void set_sleep_mode_switch(switch_::Switch *sw) { this->sleep_mode_switch_ = sw; }
void set_display_switch(switch_::Switch *sw) { this->display_switch_ = sw; }
#endif
#ifdef USE_SELECT
void set_vertical_direction_select(select::Select *sel) { this->vertical_direction_select_ = sel; }
#endif
protected:
void publish_settings_();
void save_settings_();
void sync_swing_mode_();
bool sleep_mode_allowed_() const;
void transmit_state() override;
uint8_t operation_mode_();
uint8_t fan_speed_();
uint8_t temperature_();
uint8_t operation_settings_();
bool on_receive(remote_base::RemoteReceiveData data) override;
void parse_state_frame_(const uint8_t *frame);
#ifdef USE_SWITCH
switch_::Switch *sleep_mode_switch_{nullptr};
switch_::Switch *display_switch_{nullptr};
#endif
#ifdef USE_SELECT
select::Select *vertical_direction_select_{nullptr};
#endif
ESPPreferenceObject settings_pref_;
// The unit's own defaults: display lit, no sleep mode, flap still
AirtonSettings settings_{false, true, VerticalDirection::VERTICAL_DIRECTION_OFF};
// The last mode sent, so turning off repeats it with the power bit cleared
uint8_t previous_mode_{0};
};
} // namespace esphome::airton
+71
View File
@@ -0,0 +1,71 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components import climate_ir
import esphome.config_validation as cv
from esphome.types import ConfigType
AUTO_LOAD = ["climate_ir"]
CODEOWNERS = ["@Procsiab"]
airton_ns = cg.esphome_ns.namespace("airton")
AirtonClimate = airton_ns.class_("AirtonClimate", climate_ir.ClimateIR)
CONF_AIRTON_ID = "airton_id"
CONF_SLEEP_MODE = "sleep_mode"
CONF_VERTICAL_DIRECTION = "vertical_direction"
VerticalDirection = airton_ns.enum("VerticalDirection", is_class=True)
# In the order of the enum, so the option index is the direction value
VERTICAL_DIRECTIONS = VerticalDirection.options(
"off",
"swing",
"up",
"middle-up",
"middle",
"middle-down",
"down",
prefix="VERTICAL_DIRECTION_",
)
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(AirtonClimate).extend(
{
cv.Optional(CONF_VERTICAL_DIRECTION, default="off"): cv.enum(
VERTICAL_DIRECTIONS
),
}
)
AIRTON_ACTION_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(AirtonClimate),
}
)
for _name, _call in (
(
"climate.airton.display_on",
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_DISPLAY, true)",
),
(
"climate.airton.display_off",
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_DISPLAY, false)",
),
(
"climate.airton.sleep_on",
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_SLEEP_MODE, true)",
),
(
"climate.airton.sleep_off",
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_SLEEP_MODE, false)",
),
):
automation.register_apply_action(
_name, AIRTON_ACTION_SCHEMA, automation.ApplyCall(_call)
)
async def to_code(config: ConfigType) -> None:
var = await climate_ir.new_climate_ir(config)
# The default until a saved setting is restored in setup(); off is the member's initial value
if (direction := config[CONF_VERTICAL_DIRECTION]) != "off":
cg.add(var.set_default_vertical_direction(direction))
@@ -0,0 +1,37 @@
import esphome.codegen as cg
from esphome.components import select
import esphome.config_validation as cv
from esphome.const import ENTITY_CATEGORY_CONFIG
from esphome.types import ConfigType
from ..climate import (
CONF_AIRTON_ID,
CONF_VERTICAL_DIRECTION,
VERTICAL_DIRECTIONS,
AirtonClimate,
airton_ns,
)
CODEOWNERS = ["@Procsiab"]
VerticalDirectionSelect = airton_ns.class_("VerticalDirectionSelect", select.Select)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
cv.Optional(CONF_VERTICAL_DIRECTION): select.select_schema(
VerticalDirectionSelect,
entity_category=ENTITY_CATEGORY_CONFIG,
),
}
)
async def to_code(config: ConfigType) -> None:
hub = await cg.get_variable(config[CONF_AIRTON_ID])
selects = select.sub_selects(config, parent=hub)
await selects(
CONF_VERTICAL_DIRECTION,
hub.set_vertical_direction_select,
options=list(VERTICAL_DIRECTIONS),
)
@@ -0,0 +1,17 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../airton.h"
namespace esphome::airton {
class VerticalDirectionSelect : public select::Select, public Parented<AirtonClimate> {
protected:
// The options are in the order of the enum, so the index is the direction; the parent publishes the state
void control(size_t index) override {
if (this->parent_->set_vertical_direction_state(static_cast<VerticalDirection>(index)))
this->parent_->send_settings();
}
};
} // namespace esphome::airton
@@ -0,0 +1,41 @@
import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import CONF_DISPLAY, ENTITY_CATEGORY_CONFIG
from esphome.types import ConfigType
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,
entity_category=ENTITY_CATEGORY_CONFIG,
default_restore_mode="DISABLED",
),
cv.Optional(CONF_SLEEP_MODE): switch.switch_schema(
AirtonSwitch,
entity_category=ENTITY_CATEGORY_CONFIG,
default_restore_mode="DISABLED",
),
}
)
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
)
await switches(
CONF_SLEEP_MODE,
hub.set_sleep_mode_switch,
AirtonSetting.AIRTON_SETTING_SLEEP_MODE,
)
@@ -0,0 +1,19 @@
#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
+1 -4
View File
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_CURRENT,
CONF_FLOW,
CONF_HEAD,
CONF_ID,
CONF_POWER,
CONF_SPEED,
CONF_VOLTAGE,
@@ -70,9 +69,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
var = await ble_client.new_ble_node_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_FLOW, var.set_flow_sensor)
+1 -1
View File
@@ -64,7 +64,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
uint32_t raw;
raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
humidity = raw * 9.5367431640625e-5;
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
raw = encode_uint24(data[3] & 0x0F, data[4], data[5]);
temperature = raw * 1.9073486328125e-4 - 50;
return crc8(data, 6, 0xFF, 0x31, true) == data[6];
}
+1 -4
View File
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
@@ -42,9 +41,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
+1 -6
View File
@@ -69,12 +69,7 @@ bool AM2320Component::read_data_(uint8_t *data) {
return false;
}
uint16_t checksum;
checksum = data[7] << 8;
checksum += data[6];
if (crc16(data, 6) != checksum) {
if (crc16(data, 6) != encode_value_le<uint16_t>(&data[6])) {
ESP_LOGW(TAG, "AM2320 Checksum invalid!");
return false;
}
+1 -4
View File
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
@@ -44,9 +43,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
+1 -1
View File
@@ -19,7 +19,7 @@ CONFIG_SCHEMA = (
cover.cover_schema(Am43Component)
.extend(
{
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
cv.Optional(CONF_PIN, default=8888): cv.uint16_t,
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
}
)
+1 -4
View File
@@ -3,7 +3,6 @@ from esphome.components import ble_client, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_ID,
CONF_ILLUMINANCE,
DEVICE_CLASS_BATTERY,
ENTITY_CATEGORY_DIAGNOSTIC,
@@ -44,9 +43,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
var = await ble_client.new_ble_node_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_BATTERY_LEVEL, var.set_battery)
@@ -37,8 +37,7 @@ async def to_code(config: ConfigType) -> None:
var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config)
sens = await cg.get_variable(config[CONF_SENSOR_ID])
cg.add(var.set_sensor(sens))
await cg.add_variable(var.set_sensor, config[CONF_SENSOR_ID])
if isinstance(config[CONF_THRESHOLD], dict):
lower = await cg.templatable(config[CONF_THRESHOLD][CONF_LOWER], [], cg.float_)
+8 -19
View File
@@ -28,15 +28,6 @@ static constexpr uint32_t APC1_NO_DATA_TIMEOUT_MS = 15000;
// Drop a partial frame when the bytes stop mid-way
static constexpr uint32_t APC1_RX_IDLE_TIMEOUT_MS = 200;
// Frames and commands end with the 16-bit sum of every byte before it
static uint16_t apc1_checksum(const uint8_t *data, size_t len) {
uint16_t sum = 0;
for (size_t i = 0; i < len; i++) {
sum += data[i];
}
return sum;
}
void APC1Component::setup() {
if (this->set_pin_ != nullptr) {
this->set_pin_->setup();
@@ -113,16 +104,14 @@ void APC1Component::loop() {
this->rx_index_ = 0;
}
while (this->available()) {
this->read_available([this, now](uint8_t byte) {
this->last_transmission_ = now;
uint8_t byte;
this->read_byte(&byte);
if (this->rx_index_ == 0) {
if (byte == APC1_HEADER_1) {
this->rx_buffer_[this->rx_index_++] = byte;
}
continue;
return;
}
if (this->rx_index_ == 1) {
@@ -131,12 +120,12 @@ void APC1Component::loop() {
} else if (byte != APC1_HEADER_1) {
this->rx_index_ = 0;
}
continue;
return;
}
this->rx_buffer_[this->rx_index_++] = byte;
if (this->rx_index_ < APC1_FRAME_HEADER_SIZE) {
continue;
return;
}
// The payload carries at least the checksum and must fit the buffer; checking it before the sum keeps a
@@ -145,19 +134,19 @@ void APC1Component::loop() {
if (payload_len < 2 || payload_len > this->rx_buffer_.size() - APC1_FRAME_HEADER_SIZE) {
ESP_LOGW(TAG, "Invalid frame length: %u", payload_len);
this->rx_index_ = 0;
continue;
return;
}
const uint8_t total_len = payload_len + APC1_FRAME_HEADER_SIZE;
if (this->rx_index_ == total_len) {
this->parse_frame_(total_len);
this->rx_index_ = 0;
}
}
});
}
void APC1Component::parse_frame_(uint16_t total_len) {
uint16_t expected_checksum = encode_uint16(this->rx_buffer_[total_len - 2], this->rx_buffer_[total_len - 1]);
uint16_t calculated_checksum = apc1_checksum(this->rx_buffer_.data(), total_len - 2);
uint16_t calculated_checksum = sum16_bytes(this->rx_buffer_.data(), total_len - 2);
if (expected_checksum != calculated_checksum) {
ESP_LOGW(TAG, "APC1 checksum mismatch: calculated 0x%04X != expected 0x%04X", calculated_checksum,
expected_checksum);
@@ -343,7 +332,7 @@ void APC1Component::send_command_(APC1Command cmd, uint16_t data) {
0,
0,
};
uint16_t checksum = apc1_checksum(buf.data(), 5);
uint16_t checksum = sum16_bytes(buf.data(), 5);
buf[5] = static_cast<uint8_t>((checksum >> 8) & 0xFF);
buf[6] = static_cast<uint8_t>(checksum & 0xFF);
this->write_array(buf);
+3 -11
View File
@@ -7,7 +7,6 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_ECO2,
CONF_HUMIDITY,
CONF_ID,
CONF_PM_0_3UM,
CONF_PM_0_5UM,
CONF_PM_1_0,
@@ -165,9 +164,7 @@ FINAL_VALIDATE_SCHEMA = final_validate
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = await uart.new_uart_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_PM_1_0, var.set_pm_1_0_sensor)
@@ -194,13 +191,8 @@ async def to_code(config: ConfigType) -> None:
await sensors(CONF_RS3, var.set_rs3_sensor)
await sensors(CONF_ERROR_CODE, var.set_error_code_sensor)
if (set_pin_config := config.get(CONF_SET_PIN)) is not None:
set_pin = await cg.gpio_pin_expression(set_pin_config)
cg.add(var.set_set_pin(set_pin))
if (reset_pin_config := config.get(CONF_RESET_PIN)) is not None:
reset_pin = await cg.gpio_pin_expression(reset_pin_config)
cg.add(var.set_reset_pin(reset_pin))
await cg.add_pin(var.set_set_pin, config.get(CONF_SET_PIN))
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
APC1_ACTION_SCHEMA = maybe_simple_id(
@@ -27,45 +27,36 @@ void APCProteousCover::setup() {
}
void APCProteousCover::loop() {
uint8_t data;
while (this->available() > 0) {
if (!this->read_byte(&data)) {
continue;
this->read_available([this](uint8_t data) {
const char *line = this->line_reader_.feed(data);
if (this->line_reader_.overflowed()) {
// Start over from this byte, so a frame right after unterminated noise is still read
ESP_LOGVV(TAG, "rx overflow, discarding");
this->line_reader_.restart_with(data);
} else if (line != nullptr && line[0] != '\0') {
ESP_LOGVV(TAG, "rx: '%s'", line);
this->parse_response_(line);
}
if (data == '\r') {
if (this->rx_len_ != 0) {
this->rx_buffer_[this->rx_len_] = '\0';
ESP_LOGVV(TAG, "rx: '%s'", this->rx_buffer_);
this->parse_response_();
this->rx_len_ = 0;
}
} else if (data != '\n') {
if (this->rx_len_ >= MAX_RESPONSE_LEN) {
ESP_LOGVV(TAG, "rx overflow, discarding");
this->rx_len_ = 0;
}
this->rx_buffer_[this->rx_len_++] = static_cast<char>(data);
}
}
});
}
void APCProteousCover::parse_response_() {
void APCProteousCover::parse_response_(const char *line) {
// Expected format: "?s=XX" or "?x=XX" where XX is a hex value
if (this->rx_len_ < 4 || this->rx_buffer_[0] != '?' || this->rx_buffer_[2] != '=') {
ESP_LOGV(TAG, "Invalid response: %s", this->rx_buffer_);
if (line[0] != '?' || line[1] == '\0' || line[2] != '=' || line[3] == '\0') {
ESP_LOGV(TAG, "Invalid response: %s", line);
return;
}
const char *hex_str = this->rx_buffer_ + 3;
const char *hex_str = line + 3;
char *end_ptr;
uint8_t value = static_cast<uint8_t>(strtol(hex_str, &end_ptr, 16));
if (end_ptr == hex_str) {
ESP_LOGW(TAG, "Failed to parse hex value: %s", this->rx_buffer_);
ESP_LOGW(TAG, "Failed to parse hex value: %s", line);
return;
}
bool state_changed = false;
char type = this->rx_buffer_[1];
char type = line[1];
if (type == 's') {
// bit 0 = operating, bit 1 = direction (0 = opening, 1 = closing)
CoverOperation new_operation = COVER_OPERATION_IDLE;
@@ -154,11 +145,10 @@ void APCProteousCover::update() {
return;
}
// The line is idle, so anything left in the buffer is an unterminated echo
if (this->rx_len_ != 0) {
this->rx_buffer_[this->rx_len_] = '\0';
ESP_LOGVV(TAG, "discarding stale rx '%s' before query", this->rx_buffer_);
this->rx_len_ = 0;
if (this->line_reader_.length() != 0) {
ESP_LOGVV(TAG, "discarding stale rx '%s' before query", this->line_reader_.partial());
}
this->line_reader_.reset();
const char *query = this->query_s_next_ ? QUERY_S : QUERY_X;
ESP_LOGVV(TAG, "tx: %s", query);
@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/line_reader.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/cover/cover.h"
@@ -17,7 +18,7 @@ class APCProteousCover : public cover::Cover, public PollingComponent, public ua
protected:
void control(const cover::CoverCall &call) override;
void parse_response_();
void parse_response_(const char *line);
void send_command_(const char *cmd);
void write_command_(const char *cmd, uint32_t now);
void stop_cmd_(uint32_t now);
@@ -43,8 +44,7 @@ class APCProteousCover : public cover::Cover, public PollingComponent, public ua
const char *pending_command_{nullptr};
uint32_t pending_command_time_{0};
uint32_t last_command_tx_{0};
char rx_buffer_[MAX_RESPONSE_LEN + 1];
uint8_t rx_len_{0};
LineReader<MAX_RESPONSE_LEN + 1, '\r', '\n'> line_reader_;
uint8_t command_retries_{0};
bool query_s_next_{true};
};
+2 -9
View File
@@ -52,16 +52,9 @@ enum { // APDS9306 registers
}
void APDS9306::setup() {
uint8_t id;
if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
// 0xB1 for APDS9306, 0xB3 for APDS9306-065; they differ only in bit 1
if (!this->check_chip_id(this, APDS9306_PART_ID, 0xB1, 0xFD))
return;
}
if (id != 0xB1 && id != 0xB3) { // 0xB1 for APDS9306 0xB3 for APDS9306-065
this->mark_failed(LOG_STR("APDS9306 has invalid id"));
return;
}
// ALS resolution and measurement, see datasheet or init.py for options
uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07);
+5 -12
View File
@@ -1,5 +1,6 @@
#include "apds9930.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <algorithm>
@@ -64,16 +65,8 @@ static constexpr uint16_t ALS_SATURATION = 1024 * (256 - APDS9930_ATIME_VALUE);
bool APDS9930Component::write_reg_(uint8_t reg, uint8_t value) { return this->write_byte(reg | APDS9930_CMD, value); }
void APDS9930Component::setup() {
uint8_t id;
if (!this->read_byte(APDS9930_ID | APDS9930_CMD, &id)) {
this->mark_failed(LOG_STR("Communication failed"));
if (!this->check_chip_id(this, APDS9930_ID | APDS9930_CMD, APDS9930_CHIP_ID))
return;
}
if (id != APDS9930_CHIP_ID) {
ESP_LOGE(TAG, "Wrong ID 0x%02X (expected 0x%02X)", id, APDS9930_CHIP_ID);
this->mark_failed(LOG_STR("Wrong chip ID"));
return;
}
const uint8_t control =
(this->led_drive_ & 0b11) << CONTROL_PDRIVE_SHIFT | CONTROL_PDIODE_CH1 << CONTROL_PDIODE_SHIFT |
@@ -119,8 +112,8 @@ void APDS9930Component::update() {
const uint8_t status = raw[0];
if (this->illuminance_sensor_ != nullptr && (status & APDS9930_AVALID) != 0) {
const uint16_t ch0 = encode_uint16(raw[2], raw[1]);
const uint16_t ch1 = encode_uint16(raw[4], raw[3]);
const uint16_t ch0 = encode_value_le<uint16_t>(&raw[1]);
const uint16_t ch1 = encode_value_le<uint16_t>(&raw[3]);
if (ch0 >= ALS_SATURATION || ch1 >= ALS_SATURATION) {
// A saturated channel would give a lux value that is too low, not too high
ESP_LOGW(TAG, "Light channel saturated, lower ambient_light_gain");
@@ -133,7 +126,7 @@ void APDS9930Component::update() {
}
if (this->proximity_sensor_ != nullptr && (status & APDS9930_PVALID) != 0) {
this->proximity_sensor_->publish_state(encode_uint16(raw[6], raw[5]));
this->proximity_sensor_->publish_state(encode_value_le<uint16_t>(&raw[5]));
}
}
+1 -4
View File
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
from esphome.components.const import UNIT_COUNTS
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_ILLUMINANCE,
DEVICE_CLASS_ILLUMINANCE,
STATE_CLASS_MEASUREMENT,
@@ -59,9 +58,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_led_drive(config[CONF_LED_DRIVE]))
cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
+1 -4
View File
@@ -1,7 +1,6 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
DEPENDENCIES = ["i2c"]
@@ -60,9 +59,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_led_drive(config[CONF_LED_DRIVE]))
cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
+6 -6
View File
@@ -162,13 +162,13 @@ void APDS9960::read_color_data_(uint8_t status) {
return;
}
uint8_t raw[8];
APDS9960_WARNING_CHECK(this->read_bytes(0x94, raw, 8), "Reading color values failed.");
uint16_t raw[4];
APDS9960_WARNING_CHECK(this->read_bytes_16_le(0x94, raw, 4), "Reading color values failed.");
uint16_t uint_clear = (uint16_t(raw[1]) << 8) | raw[0];
uint16_t uint_red = (uint16_t(raw[3]) << 8) | raw[2];
uint16_t uint_green = (uint16_t(raw[5]) << 8) | raw[4];
uint16_t uint_blue = (uint16_t(raw[7]) << 8) | raw[6];
uint16_t uint_clear = raw[0];
uint16_t uint_red = raw[1];
uint16_t uint_green = raw[2];
uint16_t uint_blue = raw[3];
float clear_perc = (uint_clear / float(UINT16_MAX)) * 100.0f;
float red_perc = (uint_red / float(UINT16_MAX)) * 100.0f;
@@ -152,10 +152,7 @@ void OutgoingConnectionManager::loop(APIServer *server) {
void OutgoingConnectionManager::try_dial_(APIServer *server, uint32_t now) {
if (!network::is_connected()) {
// Flips within seconds of boot; recheck fast so a deep sleep wake
// window is not spent waiting
this->schedule_wait_(now, NETWORK_RETRY_MS);
return;
return; // still WAITING with the wait elapsed, so the next loop rechecks
}
struct sockaddr_storage addr;
socklen_t addr_len = this->target_sockaddr_(&addr);
@@ -197,7 +194,6 @@ void OutgoingConnectionManager::try_dial_(APIServer *server, uint32_t now) {
}
this->state_ = DialState::DIAL_STATE_CONNECTING;
this->state_ts_ = now;
this->last_poll_ = now;
}
void OutgoingConnectionManager::poll_connect_(APIServer *server, uint32_t now) {
@@ -206,10 +202,6 @@ void OutgoingConnectionManager::poll_connect_(APIServer *server, uint32_t now) {
this->schedule_retry_(now);
return;
}
if (now - this->last_poll_ < CONNECT_POLL_INTERVAL_MS) {
return;
}
this->last_poll_ = now;
int err = 0;
switch (socket::poll_connect(*this->dial_socket_, err)) {
case socket::ConnectPollResult::CONNECT_POLL_RESULT_PENDING:
@@ -59,8 +59,6 @@ class OutgoingConnectionManager {
static constexpr uint32_t BACKOFF_MIN_MS = 5000;
static constexpr uint32_t BACKOFF_MAX_MS = 300000;
static constexpr uint32_t CONNECT_TIMEOUT_MS = 10000;
static constexpr uint32_t CONNECT_POLL_INTERVAL_MS = 250;
static constexpr uint32_t NETWORK_RETRY_MS = 500;
static constexpr uint32_t PRECONDITION_RETRY_MS = 5000;
// A deep sleep wake window is too short to spend on the delay, so those
// builds dial out as soon as the target is gone
@@ -104,7 +102,6 @@ class OutgoingConnectionManager {
uint32_t backoff_{BACKOFF_MIN_MS};
uint32_t wait_{BOOT_WAIT_MS};
uint32_t state_ts_{0};
uint32_t last_poll_{0};
// Byte-aligned types last
#ifndef API_OUTGOING_CONNECTION_HOST
+3
View File
@@ -65,6 +65,9 @@ void APIServer::socket_failed_(const LogString *msg) {
}
void APIServer::setup() {
#if defined(USE_API_HOMEASSISTANT_STATES) && defined(HOMEASSISTANT_STATE_SUBSCRIPTION_COUNT)
this->state_subs_.reserve(HOMEASSISTANT_STATE_SUBSCRIPTION_COUNT);
#endif
#ifdef USE_API_NOISE
// Always reserve the slot: flash preferences are positional on esp8266, so
// a yaml key build must keep the layout of a runtime key build
+9 -1
View File
@@ -208,6 +208,7 @@ WIZARD_SCHEMA = cv.All(
WIZARD_INPUT_DOMAINS = (
"binary_sensor",
"button",
"light",
"number",
"select",
"sensor",
@@ -216,7 +217,14 @@ WIZARD_INPUT_DOMAINS = (
"text_sensor",
)
# Platforms that act on one family of Home Assistant domains, which the input's filters must stay within
WIZARD_DOMAIN_LIMITED_PLATFORMS = ("button", "number", "select", "switch", "text")
WIZARD_DOMAIN_LIMITED_PLATFORMS = (
"button",
"light",
"number",
"select",
"switch",
"text",
)
def wizard_input_ids(api_config: ConfigType) -> set[str]:
+2 -5
View File
@@ -53,11 +53,8 @@ async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
pm_2_5_sensor = await cg.get_variable(config[CONF_PM_2_5])
cg.add(var.set_pm_2_5_sensor(pm_2_5_sensor))
pm_10_0_sensor = await cg.get_variable(config[CONF_PM_10_0])
cg.add(var.set_pm_10_0_sensor(pm_10_0_sensor))
await cg.add_variable(var.set_pm_2_5_sensor, config[CONF_PM_2_5])
await cg.add_variable(var.set_pm_10_0_sensor, config[CONF_PM_10_0])
cg.add(var.set_aqi_calculation_type(config[CONF_CALCULATION_TYPE]))
cg.add(var.set_extended_range(config.get(CONF_EXTENDED_RANGE, False)))
+3 -4
View File
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_CO2,
CONF_HUMIDITY,
CONF_ID,
CONF_MAC_ADDRESS,
CONF_PRESSURE,
CONF_RADON,
@@ -131,9 +130,9 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID], config[CONF_MAC_ADDRESS].as_hex)
await cg.register_component(var, config)
await ble_device_base.register_ble_device(var, config)
var = await ble_device_base.new_ble_device_component(
config, config[CONF_MAC_ADDRESS].as_hex
)
sensors = sensor.sub_sensors(config)
await sensors(CONF_CO2, var.set_co2_sensor)
+1 -2
View File
@@ -48,8 +48,7 @@ AS3935_SCHEMA = cv.Schema(
async def setup_as3935(var: MockObj, config: ConfigType) -> None:
await cg.register_component(var, config)
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin))
await cg.add_pin(var.set_irq_pin, config[CONF_IRQ_PIN])
cg.add(var.set_indoor(config[CONF_INDOOR]))
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))
+2 -7
View File
@@ -9,7 +9,6 @@ from esphome.const import (
CONF_DIR_PIN,
CONF_DIRECTION,
CONF_HYSTERESIS,
CONF_ID,
CONF_POWER_MODE,
CONF_RANGE,
CONF_WATCHDOG,
@@ -209,9 +208,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_direction(config[CONF_DIRECTION]))
cg.add(var.set_watchdog(config[CONF_WATCHDOG]))
@@ -221,9 +218,7 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_fast_filter(config[CONF_FAST_FILTER]))
cg.add(var.set_start_position(config[CONF_START_POSITION]))
if dir_pin_config := config.get(CONF_DIR_PIN):
pin = await cg.gpio_pin_expression(dir_pin_config)
cg.add(var.set_dir_pin(pin))
await cg.add_pin(var.set_dir_pin, config.get(CONF_DIR_PIN))
if (end_position_config := config.get(CONF_END_POSITION, None)) is not None:
cg.add(var.set_end_position(end_position_config))
+5 -13
View File
@@ -9,14 +9,8 @@ ESPHOME_LOG_TAG(TAG, "as7341");
void AS7341Component::setup() {
LOG_I2C_DEVICE(this);
// Verify device ID
uint8_t id;
this->read_byte(AS7341_ID, &id);
ESP_LOGCONFIG(TAG, " Read ID: 0x%X", id);
if ((id & 0xFC) != (AS7341_CHIP_ID << 2)) {
this->mark_failed(LOG_STR("Wrong chip ID"));
if (!this->check_chip_id(this, AS7341_ID, AS7341_CHIP_ID << 2, 0xFC))
return;
}
// Power on (enter IDLE state)
if (!this->enable_power(true)) {
@@ -101,16 +95,16 @@ uint8_t AS7341Component::get_atime() {
}
uint16_t AS7341Component::get_astep() {
uint16_t data;
this->read_byte_16(AS7341_ASTEP, &data);
return this->swap_bytes(data);
uint16_t data = 0;
this->read_byte_16_le(AS7341_ASTEP, &data);
return data;
}
bool AS7341Component::setup_gain(AS7341Gain gain) { return this->write_byte(AS7341_CFG1, gain); }
bool AS7341Component::setup_atime(uint8_t atime) { return this->write_byte(AS7341_ATIME, atime); }
bool AS7341Component::setup_astep(uint16_t astep) { return this->write_byte_16(AS7341_ASTEP, swap_bytes(astep)); }
bool AS7341Component::setup_astep(uint16_t astep) { return this->write_byte_16_le(AS7341_ASTEP, astep); }
bool AS7341Component::read_channels(uint16_t *data) {
this->set_smux_low_channels(true);
@@ -259,6 +253,4 @@ bool AS7341Component::clear_register_bit(uint8_t address, uint8_t bit_position)
return this->write_byte(address, data);
}
uint16_t AS7341Component::swap_bytes(uint16_t data) { return (data >> 8) | (data << 8); }
} // namespace esphome::as7341
-1
View File
@@ -118,7 +118,6 @@ class AS7341Component final : public PollingComponent, public i2c::I2CDevice {
bool write_register_bit(uint8_t address, bool value, uint8_t bit_position);
bool set_register_bit(uint8_t address, uint8_t bit_position);
bool clear_register_bit(uint8_t address, uint8_t bit_position);
uint16_t swap_bytes(uint16_t data);
protected:
sensor::Sensor *f1_{nullptr};
+2 -5
View File
@@ -5,7 +5,6 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_CLEAR,
CONF_GAIN,
CONF_ID,
DEVICE_CLASS_EMPTY,
ICON_BRIGHTNESS_5,
STATE_CLASS_MEASUREMENT,
@@ -74,7 +73,7 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_CLEAR): SENSOR_SCHEMA,
cv.Optional(CONF_NIR): SENSOR_SCHEMA,
cv.Optional(CONF_GAIN, default="X8"): cv.enum(GAIN_OPTIONS),
cv.Optional(CONF_ATIME, default=29): cv.int_range(min=0, max=255),
cv.Optional(CONF_ATIME, default=29): cv.uint8_t,
cv.Optional(CONF_ASTEP, default=599): cv.int_range(min=0, max=65534),
}
)
@@ -97,9 +96,7 @@ SENSORS = {
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_atime(config[CONF_ATIME]))
+2 -5
View File
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_CALIBRATION,
CONF_GAIN,
CONF_GLASS_ATTENUATION_FACTOR,
CONF_ID,
CONF_TYPE,
CONF_UPDATE_INTERVAL,
)
@@ -128,7 +127,7 @@ def _schema(
{
cv.GenerateID(): cv.declare_id(AS734XComponent),
cv.Optional(CONF_GAIN, default="X8"): cv.enum(gains),
cv.Optional(CONF_ATIME, default=29): cv.int_range(min=0, max=255),
cv.Optional(CONF_ATIME, default=29): cv.uint8_t,
cv.Optional(CONF_ASTEP, default=599): cv.int_range(min=0, max=65534),
cv.Optional(CONF_CALIBRATION, default={}): _calibration_schema(bands),
cv.Optional(CONF_NORMALIZE_BASIC_COUNTS, default="NONE"): cv.enum(
@@ -166,9 +165,7 @@ def _per_band(
async def to_code(config: ConfigType) -> None:
chip = config[CONF_TYPE]
cg.add_define("USE_AS7341" if chip == "AS7341" else "USE_AS7343")
var = cg.new_Pvariable(config[CONF_ID], cg.RawExpression(f"&as734x::{chip}_CHIP"))
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config, cg.RawExpression(f"&as734x::{chip}_CHIP"))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_atime(config[CONF_ATIME]))
cg.add(var.set_astep(config[CONF_ASTEP]))
+2 -2
View File
@@ -277,7 +277,7 @@ bool AS734XComponent::update_bits_(uint8_t reg, uint8_t mask, bool value) {
bool AS734XComponent::write_exposure_() {
const ChipInfo *chip = this->chip_;
if (!this->select_bank_(REG_ATIME) || !this->write_byte(REG_ATIME, this->atime_) ||
!this->write_byte_16(pgm(chip->astep_reg), byteswap(this->astep_)) ||
!this->write_byte_16_le(pgm(chip->astep_reg), this->astep_) ||
!this->write_byte(pgm(chip->cfg1_reg), this->gain_)) {
return false;
}
@@ -459,7 +459,7 @@ void AS734XComponent::read_step_() {
if (frame[0] & ASTATUS_ASAT) {
ESP_LOGV(TAG, "Step %u saturated", this->step_);
}
auto slot_value = [&frame](uint8_t slot) { return encode_uint16(frame[2 + 2 * slot], frame[1 + 2 * slot]); };
auto slot_value = [&frame](uint8_t slot) { return encode_value_le<uint16_t>(&frame[1 + 2 * slot]); };
const uint8_t *readout = pgm(this->chip_->readout) + this->step_ * NUM_BANDS;
const uint8_t extra_clear = pgm(this->chip_->extra_clear_slot);
for (uint8_t band = 0; band < NUM_BANDS; band++) {
+1 -2
View File
@@ -108,8 +108,7 @@ CONFIG_SCHEMA = cv.Schema(
def _final_validate(config: ConfigType) -> ConfigType:
full = fv.full_config.get()
hub = full.get_config_for_path(full.get_path_for_id(config[CONF_AS734X_ID])[:-1])
hub = fv.get_declaring_config(config[CONF_AS734X_ID])
if hub[CONF_TYPE] != "AS7341":
return config
for key in _BAND_KEYS:
+1 -3
View File
@@ -73,9 +73,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
await i2c.new_i2c_component(config)
# Actions
@@ -1,4 +1,5 @@
#include "atc_mithermometer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::atc_mithermometer {
@@ -89,7 +90,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
}
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
const int16_t temperature = encode_value<int16_t>(&data[6]);
result.temperature = temperature / 10.0f;
// humidity, 1 byte, 8-bit unsigned integer, 1.0 %
@@ -99,7 +100,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
result.battery_level = data[9];
// battery, 2 bytes, 16-bit unsigned integer, 0.001 V
const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
const int16_t battery_voltage = encode_value<int16_t>(&data[10]);
result.battery_voltage = battery_voltage / 1.0e3f;
return true;
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY,
CONF_ID,
CONF_MAC_ADDRESS,
CONF_SIGNAL_STRENGTH,
CONF_TEMPERATURE,
@@ -79,9 +78,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_device_base.register_ble_device(var, config)
var = await ble_device_base.new_ble_device_component(config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
+1 -4
View File
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_CURRENT,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_FREQUENCY,
CONF_ID,
CONF_LINE_FREQUENCY,
CONF_POWER,
CONF_POWER_FACTOR,
@@ -125,9 +124,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
var = await spi.new_spi_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_VOLTAGE, var.set_voltage_sensor)
+1 -4
View File
@@ -117,12 +117,9 @@ void ATM90E32Component::loop() {
}
void ATM90E32Component::update() {
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
this->status_set_warning();
if (!this->status_comm_result(this->read16_(ATM90E32_REGISTER_METEREN) == 1, nullptr))
return;
}
this->set_publish_interval_flag_(true);
this->status_clear_warning();
#ifdef USE_TEXT_SENSOR
this->check_phase_status();
+3 -2
View File
@@ -23,14 +23,15 @@ AudioStreamInfo::AudioStreamInfo(uint8_t bits_per_sample, uint8_t channels, uint
}
uint32_t AudioStreamInfo::frames_to_microseconds(uint32_t frames) const {
return (frames * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_;
// In 64 bits: frames * 1000000 overflows 32 bits above 4294 frames, about 90 ms at 48 kHz.
return (static_cast<uint64_t>(frames) * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_;
}
uint32_t AudioStreamInfo::frames_to_milliseconds_with_remainder(uint32_t *total_frames) const {
uint32_t unprocessable_frames = *total_frames % (this->sample_rate_ / this->ms_sample_rate_gcd_);
uint32_t frames_for_ms_calculation = *total_frames - unprocessable_frames;
uint32_t playback_ms = (frames_for_ms_calculation * 1000) / this->sample_rate_;
uint32_t playback_ms = (static_cast<uint64_t>(frames_for_ms_calculation) * 1000) / this->sample_rate_;
*total_frames = unprocessable_frames;
return playback_ms;
}
+6 -4
View File
@@ -34,7 +34,7 @@ class AudioStreamInfo {
/// @return Duration in milliseconds that will store `bytes` bytes of audio. May round down for certain sample rates
/// or values of `bytes`.
uint32_t bytes_to_ms(size_t bytes) const {
return bytes * 1000 / (this->sample_rate_ * this->bytes_per_sample_ * this->channels_);
return static_cast<uint64_t>(bytes) * 1000 / (this->sample_rate_ * this->bytes_per_sample_ * this->channels_);
}
/// @brief Convert bytes to frames.
@@ -60,18 +60,20 @@ class AudioStreamInfo {
/// @brief Converts duration to frames.
/// @param ms Duration in milliseconds
/// @return Audio frames that will store `ms` milliseconds of audio. May round down for certain sample rates.
uint32_t ms_to_frames(uint32_t ms) const { return (ms * this->sample_rate_) / 1000; }
uint32_t ms_to_frames(uint32_t ms) const { return (static_cast<uint64_t>(ms) * this->sample_rate_) / 1000; }
/// @brief Converts duration to samples.
/// @param ms Duration in milliseconds
/// @return Audio samples that will store `ms` milliseconds of audio. May round down for certain sample rates.
uint32_t ms_to_samples(uint32_t ms) const { return (ms * this->channels_ * this->sample_rate_) / 1000; }
uint32_t ms_to_samples(uint32_t ms) const {
return (static_cast<uint64_t>(ms) * this->channels_ * this->sample_rate_) / 1000;
}
/// @brief Converts duration to bytes. May round down for certain sample rates.
/// @param ms Duration in milliseconds
/// @return Bytes that will store `ms` milliseconds of audio. May round down for certain sample rates.
size_t ms_to_bytes(uint32_t ms) const {
return (ms * this->bytes_per_sample_ * this->channels_ * this->sample_rate_) / 1000;
return (static_cast<uint64_t>(ms) * this->bytes_per_sample_ * this->channels_ * this->sample_rate_) / 1000;
}
/// @brief Computes the duration, in microseconds, the given amount of frames represents.
@@ -31,7 +31,5 @@ async def to_code(config: ConfigType) -> None:
await touchscreen.register_touchscreen(var, config)
await i2c.register_i2c_device(var, config)
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
if reset_pin := config.get(CONF_RESET_PIN):
cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
await cg.add_pin(var.set_interrupt_pin, config.get(CONF_INTERRUPT_PIN))
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
+7 -6
View File
@@ -1,4 +1,5 @@
#include "b_parasite.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::b_parasite {
@@ -62,29 +63,29 @@ bool BParasite::parse_device(const ble_device_base::ESPBTDevice &device) {
}
// Battery voltage in millivolts.
uint16_t battery_millivolt = data[2] << 8 | data[3];
uint16_t battery_millivolt = encode_value<uint16_t>(&data[2]);
float battery_voltage = battery_millivolt / 1000.0f;
// Temperature in 1000 * Celsius (protocol v1) or 100 * Celsius (protocol v2).
float temp_celsius;
if (protocol_version == 1) {
uint16_t temp_millicelsius = data[4] << 8 | data[5];
uint16_t temp_millicelsius = encode_value<uint16_t>(&data[4]);
temp_celsius = temp_millicelsius / 1000.0f;
} else {
int16_t temp_centicelsius = data[4] << 8 | data[5];
int16_t temp_centicelsius = encode_value<int16_t>(&data[4]);
temp_celsius = temp_centicelsius / 100.0f;
}
// Relative air humidity in the range [0, 2^16).
uint16_t humidity = data[6] << 8 | data[7];
uint16_t humidity = encode_value<uint16_t>(&data[6]);
float humidity_percent = (100.0f * humidity) / (1 << 16);
// Relative soil moisture in [0 - 2^16).
uint16_t soil_moisture = data[8] << 8 | data[9];
uint16_t soil_moisture = encode_value<uint16_t>(&data[8]);
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
// Ambient light in lux.
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
float illuminance = has_illuminance ? encode_value<uint16_t>(&data[16]) : 0.0f;
if (battery_voltage_ != nullptr) {
battery_voltage_->publish_state(battery_voltage);
+1 -4
View File
@@ -4,7 +4,6 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY,
CONF_ID,
CONF_ILLUMINANCE,
CONF_MAC_ADDRESS,
CONF_MOISTURE,
@@ -76,9 +75,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_device_base.register_ble_device(var, config)
var = await ble_device_base.new_ble_device_component(config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
+2 -6
View File
@@ -46,12 +46,8 @@ async def to_code(config: ConfigType) -> None:
var = await climate.new_climate(config)
await cg.register_component(var, config)
sens = await cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens))
if CONF_HUMIDITY_SENSOR in config:
sens = await cg.get_variable(config[CONF_HUMIDITY_SENSOR])
cg.add(var.set_humidity_sensor(sens))
await cg.add_variable(var.set_sensor, config[CONF_SENSOR])
await cg.add_variable(var.set_humidity_sensor, config.get(CONF_HUMIDITY_SENSOR))
normal_config = BangBangClimateTargetTempConfig(
config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
+3 -7
View File
@@ -1,7 +1,7 @@
import esphome.codegen as cg
from esphome.components import ble_client, time
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_RECEIVE_TIMEOUT, CONF_TIME_ID
from esphome.const import CONF_RECEIVE_TIMEOUT, CONF_TIME_ID
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType
@@ -41,11 +41,7 @@ async def register_bedjet_child(var: MockObj, config: ConfigType) -> None:
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if time_id := config.get(CONF_TIME_ID):
time_ = await cg.get_variable(time_id)
cg.add(var.set_time_id(time_))
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))
+1 -4
View File
@@ -6,7 +6,6 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_GAIN,
CONF_GLASS_ATTENUATION_FACTOR,
CONF_ID,
CONF_INTEGRATION_TIME,
)
from esphome.types import ConfigType
@@ -62,9 +61,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_adc_gain(config[CONF_GAIN]))
cg.add(var.set_measurement_time(config[CONF_INTEGRATION_TIME]))
+7 -15
View File
@@ -63,15 +63,8 @@ static float calculate_cct(uint16_t red, uint16_t green, uint16_t blue, bool low
}
void BH1745Component::setup() {
uint8_t manufacturer_id;
if (!this->read_byte(REG_MANUFACTURER_ID, &manufacturer_id)) {
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
if (!this->check_chip_id(this, REG_MANUFACTURER_ID, MANUFACTURER_ID))
return;
}
if (manufacturer_id != MANUFACTURER_ID) {
this->mark_failed(LOG_STR("Wrong manufacturer ID"));
return;
}
uint8_t system_control;
if (!this->read_byte(REG_SYSTEM_CONTROL, &system_control) ||
(system_control & SYSTEM_CONTROL_PART_ID_MASK) != PART_ID) {
@@ -140,18 +133,17 @@ void BH1745Component::update() {
if ((mode_control2 & MODE_CONTROL2_VALID) == 0)
return;
uint8_t buffer[BH1745_CHANNELS * 2];
if (!this->read_bytes(REG_RED_DATA_LSB, buffer, sizeof(buffer))) {
uint16_t buffer[BH1745_CHANNELS];
if (!this->read_bytes_16_le(REG_RED_DATA_LSB, buffer, BH1745_CHANNELS)) {
this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return;
}
this->status_clear_warning();
// Each channel is sent low byte first.
const uint16_t red = encode_uint16(buffer[1], buffer[0]);
const uint16_t green = encode_uint16(buffer[3], buffer[2]);
const uint16_t blue = encode_uint16(buffer[5], buffer[4]);
const uint16_t clear = encode_uint16(buffer[7], buffer[6]);
const uint16_t red = buffer[0];
const uint16_t green = buffer[1];
const uint16_t blue = buffer[2];
const uint16_t clear = buffer[3];
ESP_LOGV(TAG, "Red: %u, Green: %u, Blue: %u, Clear: %u", red, green, blue, clear);
if (this->red_counts_sensor_ != nullptr)
+2 -1
View File
@@ -1,3 +1,4 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "bh1900nux.h"
@@ -36,7 +37,7 @@ void BH1900NUXSensor::update() {
// Combined raw value, unsigned and unaligned 16 bit
// Temperature is represented in just 12 bits, shift needed
int16_t raw_temperature_register_value = encode_uint16(temperature_raw[0], temperature_raw[1]);
int16_t raw_temperature_register_value = encode_value<int16_t>(temperature_raw);
raw_temperature_register_value >>= 4;
float temperature_value = raw_temperature_register_value * SENSOR_RESOLUTION; // Apply sensor resolution
+3 -10
View File
@@ -25,13 +25,6 @@ async def to_code(config: ConfigType) -> None:
var = await fan.new_fan(config)
await cg.register_component(var, config)
output_ = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(output_))
if oscillation_output_id := config.get(CONF_OSCILLATION_OUTPUT):
oscillation_output = await cg.get_variable(oscillation_output_id)
cg.add(var.set_oscillating(oscillation_output))
if direction_output_id := config.get(CONF_DIRECTION_OUTPUT):
direction_output = await cg.get_variable(direction_output_id)
cg.add(var.set_direction(direction_output))
await cg.add_variable(var.set_output, config[CONF_OUTPUT])
await cg.add_variable(var.set_oscillating, config.get(CONF_OSCILLATION_OUTPUT))
await cg.add_variable(var.set_direction, config.get(CONF_DIRECTION_OUTPUT))
+1 -2
View File
@@ -20,5 +20,4 @@ async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await light.register_light(var, config)
out = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(out))
await cg.add_variable(var.set_output, config[CONF_OUTPUT])
@@ -144,8 +144,7 @@ async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
parent = await cg.get_variable(config[CONF_BK72XX_BLE_ID])
cg.add(var.set_parent(parent))
await cg.register_parented(var, config[CONF_BK72XX_BLE_ID])
# The tracker registers itself as a controller scan listener in setup();
# request the codegen-sized StaticVector slot for it.
bk72xx_ble.request_scan_listener_slot()
+5 -8
View File
@@ -1,13 +1,14 @@
#include "bl0906.h"
#include "constants.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::bl0906 {
ESPHOME_LOG_TAG(TAG, "bl0906");
constexpr uint32_t to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
constexpr uint32_t to_uint32_t(ube24_t input) { return encode_uint24(input.h, input.m, input.l); }
constexpr int32_t to_int32_t(sbe24_t input) {
return static_cast<int32_t>(encode_uint32((uint8_t) input.h, input.m, input.l, 0)) >> 8;
@@ -19,8 +20,7 @@ constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data)
}
void BL0906::loop() {
while (this->available())
this->flush();
this->discard_available();
if (this->current_stage_ == STAGE_IDLE) {
// Woken up between cycles to drain the action queue. Go back to sleep.
@@ -91,8 +91,7 @@ void BL0906::advance_stage_() {
}
void BL0906::setup() {
while (this->available())
this->flush();
this->discard_available();
this->write_array(USR_WRPROT_WITABLE, sizeof(USR_WRPROT_WITABLE));
// Calibration (1: register address; 2: value before calibration; 3: value after calibration)
this->bias_correction_(BL0906_RMSOS_1, 0.01600, 0); // Calibration current_1
@@ -133,9 +132,7 @@ void BL0906::handle_actions_() {
}
}
while (this->available()) {
this->read();
}
this->discard_available();
this->action_queue_.clear();
}
+1 -3
View File
@@ -147,9 +147,7 @@ automation.register_apply_action(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = await uart.new_uart_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_FREQUENCY, var.set_frequency_sensor)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
+1 -4
View File
@@ -2,7 +2,6 @@ import esphome.codegen as cg
from esphome.components import sensor, uart
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_INTERNAL_TEMPERATURE,
CONF_MODE,
CONF_VOLTAGE,
@@ -112,9 +111,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = await uart.new_uart_component(config)
if (mode := config[CONF_MODE]) != DEFAULT_MODE:
cg.add(var.set_mode(mode))
+1 -4
View File
@@ -5,7 +5,6 @@ from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_EXTERNAL_TEMPERATURE,
CONF_ID,
CONF_INTERNAL_TEMPERATURE,
CONF_POWER,
CONF_REFERENCE_VOLTAGE,
@@ -227,9 +226,7 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = await uart.new_uart_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_VOLTAGE, var.set_voltage_sensor)
+1 -4
View File
@@ -6,7 +6,6 @@ from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_FREQUENCY,
CONF_ID,
CONF_LINE_FREQUENCY,
CONF_POWER,
CONF_RESET,
@@ -97,9 +96,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
var = await uart.new_uart_component(config)
sensors = sensor.sub_sensors(config)
await sensors(CONF_VOLTAGE, var.set_voltage_sensor)
+10
View File
@@ -1,3 +1,6 @@
from collections.abc import Coroutine
from typing import Any
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
@@ -171,6 +174,13 @@ async def register_ble_node(var, config):
cg.add(parent.register_ble_node(var))
def new_ble_node_component(
config: ConfigType, *args: Any
) -> Coroutine[Any, Any, cg.MockObj]:
"""Construct, register and attach to the BLE client; args go to the constructor."""
return cg.new_bus_component(register_ble_node, config, *args)
BLE_WRITE_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
@@ -24,8 +24,9 @@ AES-CCM decryption for encrypted advertisements is provided portably in
ble_aes_ccm.h.
"""
from collections.abc import Callable
from collections.abc import Callable, Coroutine
import re
from typing import Any
import esphome.codegen as cg
from esphome.components.const import CONF_WINDOW
@@ -182,6 +183,13 @@ async def register_ble_device(var: cg.MockObj, config: ConfigType) -> cg.MockObj
return var
def new_ble_device_component(
config: ConfigType, *args: Any
) -> Coroutine[Any, Any, cg.MockObj]:
"""Construct, register and attach to the BLE hub; args go to the constructor."""
return cg.new_bus_component(register_ble_device, config, *args)
# ---- shared validation / codegen helpers (platform-neutral) ----
@@ -493,13 +493,13 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x19: // Appearance
if (ad_data_len >= 2)
this->appearance_ = static_cast<uint16_t>(ad_data[0]) | (static_cast<uint16_t>(ad_data[1]) << 8);
this->appearance_ = encode_value_le<uint16_t>(ad_data);
break;
case 0x02: // Incomplete List of 16-bit Service UUIDs
case 0x03: // Complete List of 16-bit Service UUIDs
for (uint8_t i = 0; (i + 1) < ad_data_len; i += 2) {
uint16_t uuid = (static_cast<uint16_t>(ad_data[i + 1]) << 8) | ad_data[i];
uint16_t uuid = encode_value_le<uint16_t>(&ad_data[i]);
this->service_uuids_.push_back(ESPBTUUID::from_uint16(uuid));
}
break;
@@ -507,9 +507,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x04: // Incomplete List of 32-bit Service UUIDs
case 0x05: // Complete List of 32-bit Service UUIDs
for (uint8_t i = 0; (i + 3) < ad_data_len; i += 4) {
uint32_t uuid = (static_cast<uint32_t>(ad_data[i + 3]) << 24) |
(static_cast<uint32_t>(ad_data[i + 2]) << 16) | (static_cast<uint32_t>(ad_data[i + 1]) << 8) |
ad_data[i];
uint32_t uuid = encode_value_le<uint32_t>(&ad_data[i]);
this->service_uuids_.push_back(ESPBTUUID::from_uint32(uuid));
}
break;
@@ -522,7 +520,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0xFF: // Manufacturer Specific Data
if (ad_data_len >= 2) {
uint16_t company_id = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
uint16_t company_id = encode_value_le<uint16_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint16(company_id);
sd.data.assign(ad_data + 2, ad_data + ad_data_len);
@@ -532,7 +530,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x16: // Service Data — 16-bit UUID
if (ad_data_len >= 2) {
uint16_t uuid = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
uint16_t uuid = encode_value_le<uint16_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint16(uuid);
sd.data.assign(ad_data + 2, ad_data + ad_data_len);
@@ -542,8 +540,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x20: // Service Data — 32-bit UUID
if (ad_data_len >= 4) {
uint32_t uuid = (static_cast<uint32_t>(ad_data[3]) << 24) | (static_cast<uint32_t>(ad_data[2]) << 16) |
(static_cast<uint32_t>(ad_data[1]) << 8) | ad_data[0];
uint32_t uuid = encode_value_le<uint32_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint32(uuid);
sd.data.assign(ad_data + 4, ad_data + ad_data_len);
@@ -4,6 +4,7 @@
#pragma once
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/components/ble_device_base/ble_client_state.h"
#include "esphome/components/ble_device_base/ble_device.h"
@@ -124,10 +125,8 @@ inline void fill_128bit_uuid_array(std::array<uint64_t, 2> &out, const ble_devic
if (uuid.type() == ESPBTUUID::Type::UUID128) {
const uint8_t *u = uuid.uuid128();
// out[0] = bytes 8-15 (big-endian), out[1] = bytes 0-7 (big-endian)
out[0] = ((uint64_t) u[15] << 56) | ((uint64_t) u[14] << 48) | ((uint64_t) u[13] << 40) | ((uint64_t) u[12] << 32) |
((uint64_t) u[11] << 24) | ((uint64_t) u[10] << 16) | ((uint64_t) u[9] << 8) | ((uint64_t) u[8]);
out[1] = ((uint64_t) u[7] << 56) | ((uint64_t) u[6] << 48) | ((uint64_t) u[5] << 40) | ((uint64_t) u[4] << 32) |
((uint64_t) u[3] << 24) | ((uint64_t) u[2] << 16) | ((uint64_t) u[1] << 8) | ((uint64_t) u[0]);
out[0] = encode_value_le<uint64_t>(&u[8]);
out[1] = encode_value_le<uint64_t>(u);
return;
}
// 16/32-bit UUID inserted into the Bluetooth base UUID:
@@ -372,8 +372,7 @@ async def _to_code_esp32(config: ConfigType) -> None:
await cg.register_component(var, config)
cg.add(var.set_active(config[CONF_ACTIVE]))
tracker = await cg.get_variable(config[esp32_ble_tracker.CONF_ESP32_BLE_ID])
cg.add(var.set_ble_hub(tracker))
await cg.add_variable(var.set_ble_hub, config[esp32_ble_tracker.CONF_ESP32_BLE_ID])
# Compiles the scanner-state push slot into the tracker and the matching
# registration into the proxy; the other hubs are polled instead.
@@ -390,8 +389,7 @@ async def _to_code_ble_hub(config: ConfigType) -> None:
await cg.register_component(var, config)
cg.add(var.set_active(config[CONF_ACTIVE]))
hub = await cg.get_variable(config[ble_device_base.CONF_BLE_HUB_ID])
cg.add(var.set_ble_hub(hub))
await cg.add_variable(var.set_ble_hub, config[ble_device_base.CONF_BLE_HUB_ID])
await _connections_to_code(var, config)
+11 -21
View File
@@ -2,6 +2,7 @@
#include <cinttypes>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::bm8563 {
@@ -67,28 +68,17 @@ void BM8563::read_time() {
time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp);
}
uint8_t BM8563::bcd2_to_byte_(uint8_t value) {
const uint8_t tmp = ((value & 0xF0) >> 0x4) * 10;
return tmp + (value & 0x0F);
}
uint8_t BM8563::byte_to_bcd2_(uint8_t value) {
const uint8_t bcdhigh = value / 10;
value -= bcdhigh * 10;
return (bcdhigh << 4) | value;
}
void BM8563::get_time_(ESPTime &time) {
uint8_t buf[3] = {0};
this->read_register(TIME_FIRST_REG, buf, 3);
time.second = this->bcd2_to_byte_(buf[0] & 0x7f);
time.minute = this->bcd2_to_byte_(buf[1] & 0x7f);
time.hour = this->bcd2_to_byte_(buf[2] & 0x3f);
time.second = bcd_to_uint8(buf[0] & 0x7f);
time.minute = bcd_to_uint8(buf[1] & 0x7f);
time.hour = bcd_to_uint8(buf[2] & 0x3f);
}
void BM8563::set_time_(const ESPTime &time) {
uint8_t buf[3] = {this->byte_to_bcd2_(time.second), this->byte_to_bcd2_(time.minute), this->byte_to_bcd2_(time.hour)};
uint8_t buf[3] = {uint8_to_bcd(time.second), uint8_to_bcd(time.minute), uint8_to_bcd(time.hour)};
this->write_register_(TIME_FIRST_REG, buf, 3);
}
@@ -96,12 +86,12 @@ void BM8563::get_date_(ESPTime &time) {
uint8_t buf[4] = {0};
this->read_register(DATE_FIRST_REG, buf, sizeof(buf));
time.day_of_month = this->bcd2_to_byte_(buf[0] & 0x3f);
time.day_of_month = bcd_to_uint8(buf[0] & 0x3f);
// The chip counts weekdays 0-6 from Sunday (not BCD), ESPTime counts 1-7
time.day_of_week = (buf[1] & 0x07) + 1;
time.month = this->bcd2_to_byte_(buf[2] & 0x1f);
time.month = bcd_to_uint8(buf[2] & 0x1f);
uint8_t year_byte = this->bcd2_to_byte_(buf[3] & 0xff);
uint8_t year_byte = bcd_to_uint8(buf[3] & 0xff);
if (buf[2] & 0x80) {
time.year = 1900 + year_byte;
@@ -112,10 +102,10 @@ void BM8563::get_date_(ESPTime &time) {
void BM8563::set_date_(const ESPTime &time) {
uint8_t buf[4] = {
this->byte_to_bcd2_(time.day_of_month),
uint8_to_bcd(time.day_of_month),
static_cast<uint8_t>(time.day_of_week - 1),
this->byte_to_bcd2_(time.month),
this->byte_to_bcd2_(time.year % 100),
uint8_to_bcd(time.month),
uint8_to_bcd(time.year % 100),
};
if (time.year < 2000) {
-3
View File
@@ -29,9 +29,6 @@ class BM8563 final : public time::RealTimeClock, public i2c::I2CDevice {
void write_byte_(uint8_t reg, uint8_t value);
void write_register_(uint8_t reg, const uint8_t *data, size_t len);
optional<uint8_t> read_register_(uint8_t reg);
uint8_t bcd2_to_byte_(uint8_t value);
uint8_t byte_to_bcd2_(uint8_t value);
};
} // namespace esphome::bm8563
+2 -4
View File
@@ -2,7 +2,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components import i2c, time
import esphome.config_validation as cv
from esphome.const import CONF_DURATION, CONF_ID
from esphome.const import CONF_DURATION
from esphome.types import ConfigType
DEPENDENCIES = ["i2c"]
@@ -50,7 +50,5 @@ automation.register_apply_action(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
await time.register_time(var, config)
@@ -236,7 +236,7 @@ void BME280Component::update() {
});
}
float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
int32_t adc = encode_uint24(data[3], data[4], data[5]);
adc >>= 4;
if (adc == 0x80000) {
// temperature was disabled
@@ -256,7 +256,7 @@ float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
}
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
int32_t adc = encode_uint24(data[0], data[1], data[2]);
adc >>= 4;
if (adc == 0x80000) {
// pressure was disabled
@@ -335,8 +335,8 @@ uint8_t BME280Component::read_u8_(uint8_t a_register) {
}
uint16_t BME280Component::read_u16_le_(uint8_t a_register) {
uint16_t data = 0;
this->read_byte_16(a_register, &data);
return (data >> 8) | (data << 8);
this->read_byte_16_le(a_register, &data);
return data;
}
int16_t BME280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
+1 -1
View File
@@ -91,7 +91,7 @@ class BME280Component : public PollingComponent {
virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0;
virtual bool write_byte(uint8_t a_register, uint8_t data) = 0;
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0;
virtual bool read_byte_16_le(uint8_t a_register, uint16_t *data) = 0;
BME280CalibrationData calibration_;
BME280Oversampling temperature_oversampling_{BME280_OVERSAMPLING_16X};
+2 -2
View File
@@ -16,8 +16,8 @@ bool BME280I2CComponent::write_byte(uint8_t a_register, uint8_t data) {
bool BME280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
return I2CDevice::read_bytes(a_register, data, len);
};
bool BME280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
return I2CDevice::read_byte_16(a_register, data);
bool BME280I2CComponent::read_byte_16_le(uint8_t a_register, uint16_t *data) {
return I2CDevice::read_byte_16_le(a_register, data);
};
void BME280I2CComponent::dump_config() {
+1 -1
View File
@@ -12,7 +12,7 @@ class BME280I2CComponent final : public esphome::bme280_base::BME280Component, p
bool read_byte(uint8_t a_register, uint8_t *data) override;
bool write_byte(uint8_t a_register, uint8_t data) override;
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
bool read_byte_16(uint8_t a_register, uint16_t *data) override;
bool read_byte_16_le(uint8_t a_register, uint16_t *data) override;
void dump_config() override;
};
+4 -3
View File
@@ -3,6 +3,7 @@
#include "bme280_spi.h"
#include <esphome/components/bme280_base/bme280_base.h>
#include "esphome/core/helpers.h"
namespace esphome::bme280_spi {
@@ -51,11 +52,11 @@ bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t le
return true;
}
bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
bool BME280SPIComponent::read_byte_16_le(uint8_t a_register, uint16_t *data) {
this->enable();
this->transfer_byte(set_bit(a_register, 7));
((uint8_t *) data)[1] = this->transfer_byte(0);
((uint8_t *) data)[0] = this->transfer_byte(0);
const uint8_t lsb = this->transfer_byte(0);
*data = encode_uint16(this->transfer_byte(0), lsb);
this->disable();
return true;
}
+1 -1
View File
@@ -12,7 +12,7 @@ class BME280SPIComponent final : public esphome::bme280_base::BME280Component,
bool read_byte(uint8_t a_register, uint8_t *data) override;
bool write_byte(uint8_t a_register, uint8_t data) override;
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
bool read_byte_16(uint8_t a_register, uint16_t *data) override;
bool read_byte_16_le(uint8_t a_register, uint16_t *data) override;
};
} // namespace esphome::bme280_spi
+11 -12
View File
@@ -1,5 +1,6 @@
#include "bme680.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::bme680 {
@@ -92,8 +93,8 @@ bool BME680Component::setup_registers_() {
return false;
}
this->calibration_.t1 = coeff2[9] << 8 | coeff2[8];
this->calibration_.t2 = coeff1[2] << 8 | coeff1[1];
this->calibration_.t1 = encode_value_le<uint16_t>(&coeff2[8]);
this->calibration_.t2 = encode_value_le<int16_t>(&coeff1[1]);
this->calibration_.t3 = coeff1[3];
this->calibration_.h1 = coeff2[2] << 4 | (coeff2[1] & 0x0F);
@@ -104,19 +105,19 @@ bool BME680Component::setup_registers_() {
this->calibration_.h6 = coeff2[6];
this->calibration_.h7 = coeff2[7];
this->calibration_.p1 = coeff1[6] << 8 | coeff1[5];
this->calibration_.p2 = coeff1[8] << 8 | coeff1[7];
this->calibration_.p1 = encode_value_le<uint16_t>(&coeff1[5]);
this->calibration_.p2 = encode_value_le<int16_t>(&coeff1[7]);
this->calibration_.p3 = coeff1[9];
this->calibration_.p4 = coeff1[12] << 8 | coeff1[11];
this->calibration_.p5 = coeff1[14] << 8 | coeff1[13];
this->calibration_.p4 = encode_value_le<int16_t>(&coeff1[11]);
this->calibration_.p5 = encode_value_le<int16_t>(&coeff1[13]);
this->calibration_.p6 = coeff1[16];
this->calibration_.p7 = coeff1[15];
this->calibration_.p8 = coeff1[20] << 8 | coeff1[19];
this->calibration_.p9 = coeff1[22] << 8 | coeff1[21];
this->calibration_.p8 = encode_value_le<int16_t>(&coeff1[19]);
this->calibration_.p9 = encode_value_le<int16_t>(&coeff1[21]);
this->calibration_.p10 = coeff1[23];
this->calibration_.gh1 = coeff2[14];
this->calibration_.gh2 = coeff2[12] << 8 | coeff2[13];
this->calibration_.gh2 = encode_value<int16_t>(&coeff2[12]);
this->calibration_.gh3 = coeff2[15];
uint8_t temp_var = 0;
@@ -283,7 +284,7 @@ uint8_t BME680Component::calc_heater_duration_(uint16_t duration) {
}
void BME680Component::read_data_() {
uint8_t data[15];
if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) {
if (!this->status_comm_result(this->read_bytes(BME680_REGISTER_FIELD0, data, 15))) {
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(NAN);
if (this->pressure_sensor_ != nullptr)
@@ -293,10 +294,8 @@ void BME680Component::read_data_() {
if (this->gas_resistance_sensor_ != nullptr)
this->gas_resistance_sensor_->publish_state(NAN);
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return;
}
this->status_clear_warning();
uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4);
uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4);
+1 -4
View File
@@ -7,7 +7,6 @@ from esphome.const import (
CONF_GAS_RESISTANCE,
CONF_HEATER,
CONF_HUMIDITY,
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
@@ -115,9 +114,7 @@ CONFIG_SCHEMA = (
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
sensors = sensor.sub_sensors(config)
if await sensors(CONF_TEMPERATURE, var.set_temperature_sensor):
+1 -3
View File
@@ -73,9 +73,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
var = await i2c.new_i2c_component(config)
cg.add(var.set_device_id(str(config[CONF_ID])))
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
Loaded 100 of 1208 files, more files were not shown because too many files have changed in this diff. Show more